This commit is contained in:
2026-05-05 20:49:45 +02:00
parent f2c227e08f
commit 348e76660c
28 changed files with 3279 additions and 210 deletions

View File

@@ -151,6 +151,27 @@
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2DiagnosticsHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2DiagnosticsCollapse" aria-expanded="false" aria-controls="demoPipeline2DiagnosticsCollapse">
Diagnostics locaux
</button>
</h2>
<div id="demoPipeline2DiagnosticsCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2DiagnosticsHeading" data-bs-parent="#demoPipeline2LeftAccordion">
<div class="accordion-body">
<p class="small text-body-secondary mb-3">
Analyse les données déjà persistées : transactions, decoded events, trades, candles, tokens, pools et pairs.
</p>
<div class="d-flex gap-2">
<button id="demoPipeline2DiagnoseLocalPipelineButton" type="button" class="btn btn-outline-primary">
Diagnose local pipeline
</button>
</div>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm">
<h2 class="accordion-header" id="demoPipeline2CandlesControlHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2CandlesControlCollapse" aria-expanded="false" aria-controls="demoPipeline2CandlesControlCollapse">
@@ -215,6 +236,19 @@
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2LocalDiagnosticsHeading">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2LocalDiagnosticsCollapse" aria-expanded="false" aria-controls="demoPipeline2LocalDiagnosticsCollapse">
Local pipeline diagnostics
</button>
</h2>
<div id="demoPipeline2LocalDiagnosticsCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2LocalDiagnosticsHeading" data-bs-parent="#demoPipeline2ContentAccordion">
<div class="accordion-body">
<textarea id="demoPipeline2LocalDiagnosticsTextarea" class="form-control font-monospace" rows="18" readonly spellcheck="false"></textarea>
</div>
</div>
</div>
<div class="accordion-item border-0 shadow-sm mb-3">
<h2 class="accordion-header" id="demoPipeline2ChartHeading">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ChartCollapse" aria-expanded="true" aria-controls="demoPipeline2ChartCollapse">

View File

@@ -0,0 +1,34 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local decoded-event diagnostics summary for the UI.
*/
export type KbDemoPipeline2LocalDecodedEventDiagnosticSummary = {
/**
* Protocol name.
*/
protocolName: string,
/**
* Event kind.
*/
eventKind: string,
/**
* Event category.
*/
eventCategory: string | null,
/**
* Trade candidate flag.
*/
tradeCandidate: boolean | null,
/**
* Candle candidate flag.
*/
candleCandidate: boolean | null,
/**
* Event count.
*/
eventCount: number,
/**
* Linked trade-event count.
*/
tradeEventCount: number, };

View File

@@ -0,0 +1,38 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local DEX diagnostics summary for the UI.
*/
export type KbDemoPipeline2LocalDexDiagnosticSummary = {
/**
* DEX code.
*/
dexCode: string,
/**
* Pool count.
*/
poolCount: number,
/**
* Pair count.
*/
pairCount: number,
/**
* Decoded event count.
*/
decodedEventCount: number,
/**
* Decoded trade candidate count.
*/
decodedTradeCandidateCount: number,
/**
* Decoded candle candidate count.
*/
decodedCandleCandidateCount: number,
/**
* Trade event count.
*/
tradeEventCount: number,
/**
* Pair candle count.
*/
pairCandleCount: number, };

View File

@@ -0,0 +1,19 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { KbDemoPipeline2LocalPipelineDiagnosticSummary } from "./KbDemoPipeline2LocalPipelineDiagnosticSummary";
/**
* Local diagnostics payload returned to the UI.
*/
export type KbDemoPipeline2LocalDiagnosticsPayload = {
/**
* Open database URL.
*/
databaseUrl: string,
/**
* Pretty JSON diagnostics summary.
*/
summaryJson: string,
/**
* Structured diagnostics summary.
*/
summary: KbDemoPipeline2LocalPipelineDiagnosticSummary, };

View File

@@ -0,0 +1,34 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local duplicate decoded-event trade diagnostic sample for the UI.
*/
export type KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample = {
/**
* Decoded event id.
*/
decodedEventId: number,
/**
* Protocol name.
*/
protocolName: string | null,
/**
* Event kind.
*/
eventKind: string | null,
/**
* Pool account.
*/
poolAccount: string | null,
/**
* Trade event count.
*/
tradeEventCount: number,
/**
* Trade event ids.
*/
tradeEventIds: string | null,
/**
* Signatures.
*/
signatures: string | null, };

View File

@@ -0,0 +1,50 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local missing-trade-event diagnostic sample for the UI.
*/
export type KbDemoPipeline2LocalMissingTradeEventDiagnosticSample = {
/**
* Decoded event id.
*/
decodedEventId: number,
/**
* Chain transaction id.
*/
transactionId: number | null,
/**
* Transaction signature.
*/
signature: string | null,
/**
* Protocol name.
*/
protocolName: string,
/**
* Event kind.
*/
eventKind: string,
/**
* Pool account.
*/
poolAccount: string | null,
/**
* Whether the source transaction failed.
*/
transactionFailed: boolean,
/**
* Diagnostic reason explaining why no trade event was linked.
*/
reason: string,
/**
* Whether payload has an explicit base amount.
*/
hasBaseAmountPayload: boolean,
/**
* Whether payload has an explicit quote amount.
*/
hasQuoteAmountPayload: boolean,
/**
* Whether payload has an explicit price.
*/
hasPricePayload: boolean, };

View File

@@ -0,0 +1,38 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local multi-trade signature/pair diagnostic sample for the UI.
*/
export type KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample = {
/**
* Transaction signature.
*/
signature: string,
/**
* Pair id.
*/
pairId: number,
/**
* Pool address.
*/
poolAddress: string | null,
/**
* DEX code.
*/
dexCode: string | null,
/**
* Trade event count.
*/
tradeEventCount: number,
/**
* Distinct decoded event count.
*/
decodedEventCount: number,
/**
* Trade event ids.
*/
tradeEventIds: string | null,
/**
* Decoded event ids.
*/
decodedEventIds: string | null, };

View File

@@ -0,0 +1,66 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local pair diagnostics summary for the UI.
*/
export type KbDemoPipeline2LocalPairDiagnosticSummary = {
/**
* Pair id.
*/
pairId: number,
/**
* Pool address.
*/
poolAddress: string,
/**
* DEX code.
*/
dexCode: string,
/**
* Base mint.
*/
baseMint: string,
/**
* Base symbol.
*/
baseSymbol: string | null,
/**
* Quote mint.
*/
quoteMint: string,
/**
* Quote symbol.
*/
quoteSymbol: string | null,
/**
* Pair symbol.
*/
pairSymbol: string | null,
/**
* Decoded event count.
*/
decodedEventCount: number,
/**
* Decoded trade candidate count.
*/
decodedTradeCandidateCount: number,
/**
* Decoded candle candidate count.
*/
decodedCandleCandidateCount: number,
/**
* Trade event count.
*/
tradeEventCount: number,
/**
* Invalid trade event count.
*/
invalidTradeEventCount: number,
/**
* Pair candle count.
*/
pairCandleCount: number,
/**
* Last known price.
*/
lastPriceQuotePerBase: number | null, };

View File

@@ -0,0 +1,54 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Local pair gap diagnostic sample for the UI.
*/
export type KbDemoPipeline2LocalPairGapDiagnosticSample = {
/**
* Pair id.
*/
pairId: number,
/**
* Pool address.
*/
poolAddress: string,
/**
* DEX code.
*/
dexCode: string,
/**
* Base mint.
*/
baseMint: string,
/**
* Base symbol.
*/
baseSymbol: string | null,
/**
* Quote mint.
*/
quoteMint: string,
/**
* Quote symbol.
*/
quoteSymbol: string | null,
/**
* Pair symbol.
*/
pairSymbol: string | null,
/**
* Decoded event count.
*/
decodedEventCount: number,
/**
* Decoded trade candidate count.
*/
decodedTradeCandidateCount: number,
/**
* Trade event count.
*/
tradeEventCount: number,
/**
* Pair candle count.
*/
pairCandleCount: number, };

View File

@@ -0,0 +1,150 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { KbDemoPipeline2LocalDecodedEventDiagnosticSummary } from "./KbDemoPipeline2LocalDecodedEventDiagnosticSummary";
import type { KbDemoPipeline2LocalDexDiagnosticSummary } from "./KbDemoPipeline2LocalDexDiagnosticSummary";
import type { KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample } from "./KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample";
import type { KbDemoPipeline2LocalMissingTradeEventDiagnosticSample } from "./KbDemoPipeline2LocalMissingTradeEventDiagnosticSample";
import type { KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample } from "./KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample";
import type { KbDemoPipeline2LocalPairDiagnosticSummary } from "./KbDemoPipeline2LocalPairDiagnosticSummary";
import type { KbDemoPipeline2LocalPairGapDiagnosticSample } from "./KbDemoPipeline2LocalPairGapDiagnosticSample";
/**
* Local pipeline diagnostics summary for the UI.
*/
export type KbDemoPipeline2LocalPipelineDiagnosticSummary = {
/**
* Total persisted chain transactions.
*/
transactionCount: number,
/**
* Total successful chain transactions.
*/
okTransactionCount: number,
/**
* Total failed chain transactions.
*/
failedTransactionCount: number,
/**
* Total decoded DEX events.
*/
decodedEventCount: number,
/**
* Total decoded DEX trade candidates.
*/
decodedTradeCandidateCount: number,
/**
* Total decoded DEX candle candidates.
*/
decodedCandleCandidateCount: number,
/**
* Whether the local persisted pipeline has no blocking diagnostic issue.
*/
diagnosticsClean: boolean,
/**
* Number of blocking diagnostic issues.
*/
blockingIssueCount: number,
/**
* Total trade candidates without trade event, including ignored failed transactions.
*/
missingTradeEventCount: number,
/**
* Explicit alias for decoded trade candidates without linked trade event.
*/
decodedTradeCandidateWithoutTradeEventCount: number,
/**
* Trade candidates without linked trade event on successful transactions.
*/
decodedTradeCandidateWithoutTradeEventOnOkTransactionCount: number,
/**
* Trade candidates without linked trade event on failed transactions.
*/
decodedTradeCandidateWithoutTradeEventOnFailedTransactionCount: number,
/**
* Trade candidates without linked trade event and without explicit base/quote payload amounts.
* Actionable missing trade events on successful transactions.
*/
actionableMissingTradeEventCount: number,
/**
* Ignored missing trade events caused by failed transactions.
*/
ignoredFailedTransactionTradeCandidateCount: number, decodedTradeCandidateWithoutAmountPayloadCount: number,
/**
* Total persisted trade events.
*/
tradeEventCount: number,
/**
* Total invalid trade events.
*/
invalidTradeEventCount: number,
/**
* Total persisted pair candles.
*/
pairCandleCount: number,
/**
* Real duplicate trade rows grouped by decoded event id.
*/
duplicateDecodedEventTradeCount: number,
/**
* Multi-trade groups sharing the same signature and pair id.
*/
multiTradeSignaturePairCount: number,
/**
* Total duplicate candle buckets.
*/
duplicateCandleBucketCount: number,
/**
* Total known tokens.
*/
tokenCount: number,
/**
* Total tokens missing symbol or name.
*/
tokenMetadataMissingCount: number,
/**
* Total known pools.
*/
poolCount: number,
/**
* Total known pairs.
*/
pairCount: number,
/**
* Total pairs without trade.
*/
pairWithoutTradeCount: number,
/**
* Total pairs without candle.
*/
pairWithoutCandleCount: number,
/**
* Diagnostics grouped by DEX.
*/
dexSummaries: Array<KbDemoPipeline2LocalDexDiagnosticSummary>,
/**
* Diagnostics grouped by pair.
*/
pairSummaries: Array<KbDemoPipeline2LocalPairDiagnosticSummary>,
/**
* Diagnostics grouped by decoded event kind.
*/
decodedEventSummaries: Array<KbDemoPipeline2LocalDecodedEventDiagnosticSummary>,
/**
* Samples of decoded trade candidates without linked trade event.
*/
missingTradeEventSamples: Array<KbDemoPipeline2LocalMissingTradeEventDiagnosticSample>,
/**
* Samples of duplicated trade rows by decoded event id.
*/
duplicateDecodedEventTradeSamples: Array<KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample>,
/**
* Samples of multi-trade signature/pair groups.
*/
multiTradeSignaturePairSamples: Array<KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample>,
/**
* Samples of pairs without trade.
*/
pairWithoutTradeSamples: Array<KbDemoPipeline2LocalPairGapDiagnosticSample>,
/**
* Samples of pairs without candle.
*/
pairWithoutCandleSamples: Array<KbDemoPipeline2LocalPairGapDiagnosticSample>, };

View File

@@ -13,6 +13,7 @@ import type { KbDemoPipeline2BackfillPoolRequest } from "./bindings/KbDemoPipeli
import type { KbDemoPipeline2BackfillPayload } from "./bindings/KbDemoPipeline2BackfillPayload.ts";
import type { KbDemoPipeline2PairCandlesRequest } from "./bindings/KbDemoPipeline2PairCandlesRequest.ts";
import type { KbDemoPipeline2PairCandlesPayload } from "./bindings/KbDemoPipeline2PairCandlesPayload.ts";
import type { KbDemoPipeline2LocalDiagnosticsPayload } from "./bindings/KbDemoPipeline2LocalDiagnosticsPayload.ts";
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
@@ -50,8 +51,8 @@ interface KbLocalPipelineReplayResult {
decodedEventCount: number;
detectionCount: number;
tradeEventCount: number;
pairCandleCount: number;
analyticSignalCount: number;
pairCandleUpsertCount: number;
analyticSignalUpsertCount: number;
tokenMetadataUpdatedCount: number;
pairSymbolUpdatedCount: number;
globalErrorCount: number;
@@ -349,6 +350,7 @@ document.addEventListener("DOMContentLoaded", async () => {
const replayMetadataCheckbox = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataCheckbox");
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
const pairSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2PairSelect");
const timeframeSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2TimeframeSelect");
@@ -359,6 +361,7 @@ document.addEventListener("DOMContentLoaded", async () => {
const backfillSummaryTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2BackfillSummaryTextarea");
const chartElement = document.querySelector<HTMLDivElement>("#demoPipeline2Chart");
const chartMeta = document.querySelector<HTMLDivElement>("#demoPipeline2ChartMeta");
const localDiagnosticsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalDiagnosticsTextarea");
const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ClearLogButton");
const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LogTextarea");
@@ -380,12 +383,14 @@ document.addEventListener("DOMContentLoaded", async () => {
!replayMetadataCheckbox ||
!replayMetadataLimitInput ||
!replayLocalPipelineButton ||
!diagnoseLocalPipelineButton ||
!pairSelect ||
!timeframeSelect ||
!customTimeframeInput ||
!preferMaterializedInput ||
!loadCandlesButton ||
!backfillSummaryTextarea ||
!localDiagnosticsTextarea ||
!chartElement ||
!chartMeta ||
!clearLogButton ||
@@ -405,6 +410,7 @@ document.addEventListener("DOMContentLoaded", async () => {
const safeChartMeta = chartMeta;
const safeLogTextarea = logTextarea;
const safeLocalDiagnosticsTextarea = localDiagnosticsTextarea;
const chart = echarts.init(safeChartElement);
setEmptyChart(chart, safeChartMeta, "Aucune candle disponible.");
@@ -581,7 +587,7 @@ document.addEventListener("DOMContentLoaded", async () => {
appendLogLine(
logTextarea,
`[ui] local pipeline replay completed: ${result.replayedTransactionCount.toString()} replayed, ${result.tradeEventCount.toString()} trades, ${result.pairCandleCount.toString()} candles`,
`[ui] local pipeline replay completed: ${result.replayedTransactionCount.toString()} replayed, ${result.tradeEventCount.toString()} trades, ${result.pairCandleUpsertCount.toString()} candle upserts`,
);
await refreshCatalog();
@@ -590,6 +596,29 @@ document.addEventListener("DOMContentLoaded", async () => {
}
});
diagnoseLocalPipelineButton.addEventListener("click", async () => {
appendLogLine(logTextarea, "[ui] diagnosing local pipeline");
diagnoseLocalPipelineButton.disabled = true;
try {
const payload = await invoke<KbDemoPipeline2LocalDiagnosticsPayload>(
"demo_pipeline2_diagnose_local_pipeline",
);
safeLocalDiagnosticsTextarea.value = payload.summaryJson;
appendLogLine(
logTextarea,
`[ui] local pipeline diagnostics completed: ${payload.summary.decodedEventCount.toString()} decoded, ${payload.summary.tradeEventCount.toString()} trades, ${payload.summary.pairCandleCount.toString()} candles`,
);
} catch (error) {
appendLogLine(logTextarea, `[ui] local pipeline diagnostics error: ${String(error)}`);
} finally {
diagnoseLocalPipelineButton.disabled = false;
}
});
loadCandlesButton.addEventListener("click", async () => {
const pairIdText = pairSelect.value.trim();
if (pairIdText === "") {

View File

@@ -1,7 +1,7 @@
{
"name": "kb-app",
"private": true,
"version": "0.7.24",
"version": "0.7.26",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -10,13 +10,370 @@
use tauri::Manager;
use ts_rs::TS;
/// One token item for the local catalog.
/// Local diagnostics payload returned to the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2TokenItem.ts"
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalDiagnosticsPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalDiagnosticsPayload {
/// Open database URL.
pub database_url: std::string::String,
/// Pretty JSON diagnostics summary.
pub summary_json: std::string::String,
/// Structured diagnostics summary.
pub summary: KbDemoPipeline2LocalPipelineDiagnosticSummary,
}
/// Local pipeline diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalPipelineDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalPipelineDiagnosticSummary {
/// Total persisted chain transactions.
#[ts(type = "number")]
pub transaction_count: i64,
/// Total successful chain transactions.
#[ts(type = "number")]
pub ok_transaction_count: i64,
/// Total failed chain transactions.
#[ts(type = "number")]
pub failed_transaction_count: i64,
/// Total decoded DEX events.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Total decoded DEX trade candidates.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Total decoded DEX candle candidates.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Whether the local persisted pipeline has no blocking diagnostic issue.
pub diagnostics_clean: bool,
/// Number of blocking diagnostic issues.
#[ts(type = "number")]
pub blocking_issue_count: i64,
/// Total trade candidates without trade event, including ignored failed transactions.
#[ts(type = "number")]
pub missing_trade_event_count: i64,
/// Explicit alias for decoded trade candidates without linked trade event.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_count: i64,
/// Trade candidates without linked trade event on successful transactions.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_on_ok_transaction_count: i64,
/// Trade candidates without linked trade event on failed transactions.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_on_failed_transaction_count: i64,
/// Trade candidates without linked trade event and without explicit base/quote payload amounts.
/// Actionable missing trade events on successful transactions.
#[ts(type = "number")]
pub actionable_missing_trade_event_count: i64,
/// Ignored missing trade events caused by failed transactions.
#[ts(type = "number")]
pub ignored_failed_transaction_trade_candidate_count: i64,
#[ts(type = "number")]
pub decoded_trade_candidate_without_amount_payload_count: i64,
/// Total persisted trade events.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Total invalid trade events.
#[ts(type = "number")]
pub invalid_trade_event_count: i64,
/// Total persisted pair candles.
#[ts(type = "number")]
pub pair_candle_count: i64,
/// Real duplicate trade rows grouped by decoded event id.
#[ts(type = "number")]
pub duplicate_decoded_event_trade_count: i64,
/// Multi-trade groups sharing the same signature and pair id.
#[ts(type = "number")]
pub multi_trade_signature_pair_count: i64,
/// Total duplicate candle buckets.
#[ts(type = "number")]
pub duplicate_candle_bucket_count: i64,
/// Total known tokens.
#[ts(type = "number")]
pub token_count: i64,
/// Total tokens missing symbol or name.
#[ts(type = "number")]
pub token_metadata_missing_count: i64,
/// Total known pools.
#[ts(type = "number")]
pub pool_count: i64,
/// Total known pairs.
#[ts(type = "number")]
pub pair_count: i64,
/// Total pairs without trade.
#[ts(type = "number")]
pub pair_without_trade_count: i64,
/// Total pairs without candle.
#[ts(type = "number")]
pub pair_without_candle_count: i64,
/// Diagnostics grouped by DEX.
pub dex_summaries: std::vec::Vec<KbDemoPipeline2LocalDexDiagnosticSummary>,
/// Diagnostics grouped by pair.
pub pair_summaries: std::vec::Vec<KbDemoPipeline2LocalPairDiagnosticSummary>,
/// Diagnostics grouped by decoded event kind.
pub decoded_event_summaries: std::vec::Vec<KbDemoPipeline2LocalDecodedEventDiagnosticSummary>,
/// Samples of decoded trade candidates without linked trade event.
pub missing_trade_event_samples:
std::vec::Vec<KbDemoPipeline2LocalMissingTradeEventDiagnosticSample>,
/// Samples of duplicated trade rows by decoded event id.
pub duplicate_decoded_event_trade_samples:
std::vec::Vec<KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample>,
/// Samples of multi-trade signature/pair groups.
pub multi_trade_signature_pair_samples:
std::vec::Vec<KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample>,
/// Samples of pairs without trade.
pub pair_without_trade_samples: std::vec::Vec<KbDemoPipeline2LocalPairGapDiagnosticSample>,
/// Samples of pairs without candle.
pub pair_without_candle_samples: std::vec::Vec<KbDemoPipeline2LocalPairGapDiagnosticSample>,
}
/// Local DEX diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalDexDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalDexDiagnosticSummary {
/// DEX code.
pub dex_code: std::string::String,
/// Pool count.
#[ts(type = "number")]
pub pool_count: i64,
/// Pair count.
#[ts(type = "number")]
pub pair_count: i64,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Decoded candle candidate count.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
}
/// Local pair diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalPairDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalPairDiagnosticSummary {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// DEX code.
pub dex_code: std::string::String,
/// Base mint.
pub base_mint: std::string::String,
/// Base symbol.
pub base_symbol: std::option::Option<std::string::String>,
/// Quote mint.
pub quote_mint: std::string::String,
/// Quote symbol.
pub quote_symbol: std::option::Option<std::string::String>,
/// Pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Decoded candle candidate count.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Invalid trade event count.
#[ts(type = "number")]
pub invalid_trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
/// Last known price.
#[ts(type = "number | null")]
pub last_price_quote_per_base: std::option::Option<f64>,
}
/// Local decoded-event diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalDecodedEventDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalDecodedEventDiagnosticSummary {
/// Protocol name.
pub protocol_name: std::string::String,
/// Event kind.
pub event_kind: std::string::String,
/// Event category.
pub event_category: std::option::Option<std::string::String>,
/// Trade candidate flag.
pub trade_candidate: std::option::Option<bool>,
/// Candle candidate flag.
pub candle_candidate: std::option::Option<bool>,
/// Event count.
#[ts(type = "number")]
pub event_count: i64,
/// Linked trade-event count.
#[ts(type = "number")]
pub trade_event_count: i64,
}
/// Local missing-trade-event diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalMissingTradeEventDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalMissingTradeEventDiagnosticSample {
/// Decoded event id.
#[ts(type = "number")]
pub decoded_event_id: i64,
/// Chain transaction id.
#[ts(type = "number | null")]
pub transaction_id: std::option::Option<i64>,
/// Transaction signature.
pub signature: std::option::Option<std::string::String>,
/// Protocol name.
pub protocol_name: std::string::String,
/// Event kind.
pub event_kind: std::string::String,
/// Pool account.
pub pool_account: std::option::Option<std::string::String>,
/// Whether the source transaction failed.
pub transaction_failed: bool,
/// Diagnostic reason explaining why no trade event was linked.
pub reason: std::string::String,
/// Whether payload has an explicit base amount.
pub has_base_amount_payload: bool,
/// Whether payload has an explicit quote amount.
pub has_quote_amount_payload: bool,
/// Whether payload has an explicit price.
pub has_price_payload: bool,
}
/// Local duplicate decoded-event trade diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
/// Decoded event id.
#[ts(type = "number")]
pub decoded_event_id: i64,
/// Protocol name.
pub protocol_name: std::option::Option<std::string::String>,
/// Event kind.
pub event_kind: std::option::Option<std::string::String>,
/// Pool account.
pub pool_account: std::option::Option<std::string::String>,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Trade event ids.
pub trade_event_ids: std::option::Option<std::string::String>,
/// Signatures.
pub signatures: std::option::Option<std::string::String>,
}
/// Local multi-trade signature/pair diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
/// Transaction signature.
pub signature: std::string::String,
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::option::Option<std::string::String>,
/// DEX code.
pub dex_code: std::option::Option<std::string::String>,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Distinct decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Trade event ids.
pub trade_event_ids: std::option::Option<std::string::String>,
/// Decoded event ids.
pub decoded_event_ids: std::option::Option<std::string::String>,
}
/// Local pair gap diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2LocalPairGapDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2LocalPairGapDiagnosticSample {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// DEX code.
pub dex_code: std::string::String,
/// Base mint.
pub base_mint: std::string::String,
/// Base symbol.
pub base_symbol: std::option::Option<std::string::String>,
/// Quote mint.
pub quote_mint: std::string::String,
/// Quote symbol.
pub quote_symbol: std::option::Option<std::string::String>,
/// Pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
}
/// One token item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipeline2TokenItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2TokenItem {
/// Token mint.
pub mint: std::string::String,
@@ -28,10 +385,7 @@ pub(crate) struct KbDemoPipeline2TokenItem {
/// One pool item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2PoolItem.ts"
)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipeline2PoolItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2PoolItem {
/// Pool address.
@@ -45,10 +399,7 @@ pub(crate) struct KbDemoPipeline2PoolItem {
/// One pair item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2PairItem.ts"
)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipeline2PairItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2PairItem {
/// Internal pair id.
@@ -70,10 +421,7 @@ pub(crate) struct KbDemoPipeline2PairItem {
/// Full local catalog payload.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2CatalogPayload.ts"
)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipeline2CatalogPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2CatalogPayload {
/// Open database URL.
@@ -122,10 +470,7 @@ pub(crate) struct KbDemoPipeline2BackfillPoolRequest {
/// Shared backfill response payload.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/KbDemoPipeline2BackfillPayload.ts"
)]
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoPipeline2BackfillPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct KbDemoPipeline2BackfillPayload {
/// Object key used by the backfill.
@@ -176,6 +521,35 @@ pub(crate) struct KbDemoPipeline2PairCandlesPayload {
pub candles_json: std::string::String,
}
/// Runs local pipeline diagnostics from persisted data only.
#[tauri::command]
pub(crate) async fn demo_pipeline2_diagnose_local_pipeline(
state: tauri::State<'_, crate::KbAppState>,
) -> Result<KbDemoPipeline2LocalDiagnosticsPayload, std::string::String> {
let database = state.database.clone();
let service = kb_lib::KbLocalPipelineDiagnosticsService::new(database.clone());
let summary_result = service.diagnose().await;
let summary = match summary_result {
Ok(summary) => summary,
Err(error) => {
return Err(format!("local pipeline diagnostics failed: {}", error));
},
};
let ui_summary = kb_demo_pipeline2_map_local_diagnostics_summary(summary);
let summary_json_result = serde_json::to_string_pretty(&ui_summary);
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => {
return Err(format!("cannot serialize local pipeline diagnostics: {}", error));
},
};
Ok(KbDemoPipeline2LocalDiagnosticsPayload {
database_url: database.database_url().to_string(),
summary_json,
summary: ui_summary,
})
}
/// Opens the `Demo Pipeline 2` window.
#[tauri::command]
pub(crate) fn open_demo_pipeline2_window(
@@ -203,9 +577,9 @@ pub(crate) fn open_demo_pipeline2_window(
Ok(window) => window,
Err(error) => {
return Err(format!("cannot create demo_pipeline2 window: {error:?}"));
}
},
}
}
},
};
let show_result = demo_window.show();
if let Err(error) = show_result {
@@ -261,7 +635,7 @@ pub(crate) async fn demo_pipeline2_backfill_token_mint(
"cannot backfill token mint '{}' with role '{}': {}",
token_mint, http_role, error
));
}
},
};
let summary_json_result = serde_json::to_string_pretty(&backfill);
let summary_json = match summary_json_result {
@@ -271,7 +645,7 @@ pub(crate) async fn demo_pipeline2_backfill_token_mint(
"cannot serialize token backfill result for '{}': {}",
token_mint, error
));
}
},
};
let catalog = kb_demo_pipeline2_build_catalog(database).await?;
Ok(KbDemoPipeline2BackfillPayload {
@@ -311,7 +685,7 @@ pub(crate) async fn demo_pipeline2_backfill_pool_address(
"cannot backfill pool address '{}' with role '{}': {}",
pool_address, http_role, error
));
}
},
};
let summary_json_result = serde_json::to_string_pretty(&backfill);
let summary_json = match summary_json_result {
@@ -321,7 +695,7 @@ pub(crate) async fn demo_pipeline2_backfill_pool_address(
"cannot serialize pool backfill result for '{}': {}",
pool_address, error
));
}
},
};
let catalog = kb_demo_pipeline2_build_catalog(database).await?;
Ok(KbDemoPipeline2BackfillPayload {
@@ -362,7 +736,7 @@ pub(crate) async fn demo_pipeline2_get_pair_candles(
"cannot load candles for pair '{}' timeframe '{}': {}",
request.pair_id, request.timeframe_seconds, error
));
}
},
};
let candles_json_result = serde_json::to_string_pretty(&candles);
let candles_json = match candles_json_result {
@@ -372,7 +746,7 @@ pub(crate) async fn demo_pipeline2_get_pair_candles(
"cannot serialize candles for pair '{}' timeframe '{}': {}",
request.pair_id, request.timeframe_seconds, error
));
}
},
};
Ok(KbDemoPipeline2PairCandlesPayload {
pair_id: request.pair_id,
@@ -389,7 +763,7 @@ async fn kb_demo_pipeline2_build_catalog(
Ok(dexes) => dexes,
Err(error) => {
return Err(format!("cannot list DEXes: {}", error));
}
},
};
let mut dex_code_by_id = std::collections::BTreeMap::<i64, std::string::String>::new();
for dex in dexes {
@@ -402,9 +776,8 @@ async fn kb_demo_pipeline2_build_catalog(
Ok(db_tokens) => db_tokens,
Err(error) => {
return Err(format!("cannot list tokens: {}", error));
}
},
};
let mut tokens = std::vec::Vec::<KbDemoPipeline2TokenItem>::new();
for token in db_tokens {
tokens.push(KbDemoPipeline2TokenItem {
@@ -418,14 +791,14 @@ async fn kb_demo_pipeline2_build_catalog(
Ok(pools) => pools,
Err(error) => {
return Err(format!("cannot list pools: {}", error));
}
},
};
let pairs_result = kb_lib::list_pairs(database.as_ref()).await;
let pairs = match pairs_result {
Ok(pairs) => pairs,
Err(error) => {
return Err(format!("cannot list pairs: {}", error));
}
},
};
let mut pair_by_pool_id = std::collections::BTreeMap::<i64, kb_lib::KbPairDto>::new();
for pair in &pairs {
@@ -445,7 +818,7 @@ async fn kb_demo_pipeline2_build_catalog(
Ok(all_pools) => all_pools,
Err(error) => {
return Err(format!("cannot reload pools for pair catalog: {}", error));
}
},
};
let mut found_address = std::string::String::new();
for pool in all_pools {
@@ -465,11 +838,8 @@ async fn kb_demo_pipeline2_build_catalog(
let pair_metric_option = match pair_metric_result {
Ok(pair_metric_option) => pair_metric_option,
Err(error) => {
return Err(format!(
"cannot fetch pair metric for pair '{}': {}",
pair_id, error
));
}
return Err(format!("cannot fetch pair metric for pair '{}': {}", pair_id, error));
},
};
let trade_count = pair_metric_option.as_ref().map(|metric| metric.trade_count);
let last_price_quote_per_base =
@@ -537,18 +907,215 @@ pub(crate) async fn demo_pipeline2_replay_local_pipeline(
}
}
fn kb_demo_pipeline2_map_local_diagnostics_summary(
summary: kb_lib::KbLocalPipelineDiagnosticSummaryDto,
) -> KbDemoPipeline2LocalPipelineDiagnosticSummary {
let mut dex_summaries = std::vec::Vec::new();
for dex_summary in summary.dex_summaries {
dex_summaries.push(kb_demo_pipeline2_map_local_dex_diagnostic_summary(dex_summary));
}
let mut pair_summaries = std::vec::Vec::new();
for pair_summary in summary.pair_summaries {
pair_summaries.push(kb_demo_pipeline2_map_local_pair_diagnostic_summary(pair_summary));
}
let mut decoded_event_summaries = std::vec::Vec::new();
for decoded_event_summary in summary.decoded_event_summaries {
decoded_event_summaries.push(kb_demo_pipeline2_map_local_decoded_event_diagnostic_summary(
decoded_event_summary,
));
}
let mut missing_trade_event_samples = std::vec::Vec::new();
for sample in summary.missing_trade_event_samples {
missing_trade_event_samples.push(kb_demo_pipeline2_map_missing_trade_event_sample(sample));
}
let mut duplicate_decoded_event_trade_samples = std::vec::Vec::new();
for sample in summary.duplicate_decoded_event_trade_samples {
duplicate_decoded_event_trade_samples
.push(kb_demo_pipeline2_map_duplicate_decoded_event_trade_sample(sample));
}
let mut multi_trade_signature_pair_samples = std::vec::Vec::new();
for sample in summary.multi_trade_signature_pair_samples {
multi_trade_signature_pair_samples
.push(kb_demo_pipeline2_map_multi_trade_signature_pair_sample(sample));
}
let mut pair_without_trade_samples = std::vec::Vec::new();
for sample in summary.pair_without_trade_samples {
pair_without_trade_samples.push(kb_demo_pipeline2_map_pair_gap_sample(sample));
}
let mut pair_without_candle_samples = std::vec::Vec::new();
for sample in summary.pair_without_candle_samples {
pair_without_candle_samples.push(kb_demo_pipeline2_map_pair_gap_sample(sample));
}
KbDemoPipeline2LocalPipelineDiagnosticSummary {
transaction_count: summary.transaction_count,
ok_transaction_count: summary.ok_transaction_count,
failed_transaction_count: summary.failed_transaction_count,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
diagnostics_clean: summary.diagnostics_clean,
blocking_issue_count: summary.blocking_issue_count,
missing_trade_event_count: summary.missing_trade_event_count,
decoded_trade_candidate_without_trade_event_count: summary
.decoded_trade_candidate_without_trade_event_count,
decoded_trade_candidate_without_trade_event_on_ok_transaction_count: summary
.decoded_trade_candidate_without_trade_event_on_ok_transaction_count,
decoded_trade_candidate_without_trade_event_on_failed_transaction_count: summary
.decoded_trade_candidate_without_trade_event_on_failed_transaction_count,
actionable_missing_trade_event_count: summary.actionable_missing_trade_event_count,
ignored_failed_transaction_trade_candidate_count: summary
.ignored_failed_transaction_trade_candidate_count,
decoded_trade_candidate_without_amount_payload_count: summary
.decoded_trade_candidate_without_amount_payload_count,
trade_event_count: summary.trade_event_count,
invalid_trade_event_count: summary.invalid_trade_event_count,
pair_candle_count: summary.pair_candle_count,
duplicate_decoded_event_trade_count: summary.duplicate_decoded_event_trade_count,
multi_trade_signature_pair_count: summary.multi_trade_signature_pair_count,
duplicate_candle_bucket_count: summary.duplicate_candle_bucket_count,
token_count: summary.token_count,
token_metadata_missing_count: summary.token_metadata_missing_count,
pool_count: summary.pool_count,
pair_count: summary.pair_count,
pair_without_trade_count: summary.pair_without_trade_count,
pair_without_candle_count: summary.pair_without_candle_count,
dex_summaries,
pair_summaries,
decoded_event_summaries,
missing_trade_event_samples,
duplicate_decoded_event_trade_samples,
multi_trade_signature_pair_samples,
pair_without_trade_samples,
pair_without_candle_samples,
}
}
fn kb_demo_pipeline2_map_local_dex_diagnostic_summary(
summary: kb_lib::KbLocalDexDiagnosticSummaryDto,
) -> KbDemoPipeline2LocalDexDiagnosticSummary {
KbDemoPipeline2LocalDexDiagnosticSummary {
dex_code: summary.dex_code,
pool_count: summary.pool_count,
pair_count: summary.pair_count,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
trade_event_count: summary.trade_event_count,
pair_candle_count: summary.pair_candle_count,
}
}
fn kb_demo_pipeline2_map_local_pair_diagnostic_summary(
summary: kb_lib::KbLocalPairDiagnosticSummaryDto,
) -> KbDemoPipeline2LocalPairDiagnosticSummary {
KbDemoPipeline2LocalPairDiagnosticSummary {
pair_id: summary.pair_id,
pool_address: summary.pool_address,
dex_code: summary.dex_code,
base_mint: summary.base_mint,
base_symbol: summary.base_symbol,
quote_mint: summary.quote_mint,
quote_symbol: summary.quote_symbol,
pair_symbol: summary.pair_symbol,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
trade_event_count: summary.trade_event_count,
invalid_trade_event_count: summary.invalid_trade_event_count,
pair_candle_count: summary.pair_candle_count,
last_price_quote_per_base: summary.last_price_quote_per_base,
}
}
fn kb_demo_pipeline2_map_local_decoded_event_diagnostic_summary(
summary: kb_lib::KbLocalDecodedEventDiagnosticSummaryDto,
) -> KbDemoPipeline2LocalDecodedEventDiagnosticSummary {
KbDemoPipeline2LocalDecodedEventDiagnosticSummary {
protocol_name: summary.protocol_name,
event_kind: summary.event_kind,
event_category: summary.event_category,
trade_candidate: summary.trade_candidate,
candle_candidate: summary.candle_candidate,
event_count: summary.event_count,
trade_event_count: summary.trade_event_count,
}
}
fn kb_demo_pipeline2_map_missing_trade_event_sample(
sample: kb_lib::KbLocalMissingTradeEventDiagnosticSampleDto,
) -> KbDemoPipeline2LocalMissingTradeEventDiagnosticSample {
return KbDemoPipeline2LocalMissingTradeEventDiagnosticSample {
decoded_event_id: sample.decoded_event_id,
transaction_id: sample.transaction_id,
signature: sample.signature,
protocol_name: sample.protocol_name,
event_kind: sample.event_kind,
pool_account: sample.pool_account,
transaction_failed: sample.transaction_failed,
reason: sample.reason,
has_base_amount_payload: sample.has_base_amount_payload,
has_quote_amount_payload: sample.has_quote_amount_payload,
has_price_payload: sample.has_price_payload,
};
}
fn kb_demo_pipeline2_map_duplicate_decoded_event_trade_sample(
sample: kb_lib::KbLocalDuplicateDecodedEventTradeDiagnosticSampleDto,
) -> KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
return KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
decoded_event_id: sample.decoded_event_id,
protocol_name: sample.protocol_name,
event_kind: sample.event_kind,
pool_account: sample.pool_account,
trade_event_count: sample.trade_event_count,
trade_event_ids: sample.trade_event_ids,
signatures: sample.signatures,
};
}
fn kb_demo_pipeline2_map_multi_trade_signature_pair_sample(
sample: kb_lib::KbLocalMultiTradeSignaturePairDiagnosticSampleDto,
) -> KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
return KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
signature: sample.signature,
pair_id: sample.pair_id,
pool_address: sample.pool_address,
dex_code: sample.dex_code,
trade_event_count: sample.trade_event_count,
decoded_event_count: sample.decoded_event_count,
trade_event_ids: sample.trade_event_ids,
decoded_event_ids: sample.decoded_event_ids,
};
}
fn kb_demo_pipeline2_map_pair_gap_sample(
sample: kb_lib::KbLocalPairGapDiagnosticSampleDto,
) -> KbDemoPipeline2LocalPairGapDiagnosticSample {
return KbDemoPipeline2LocalPairGapDiagnosticSample {
pair_id: sample.pair_id,
pool_address: sample.pool_address,
dex_code: sample.dex_code,
base_mint: sample.base_mint,
base_symbol: sample.base_symbol,
quote_mint: sample.quote_mint,
quote_symbol: sample.quote_symbol,
pair_symbol: sample.pair_symbol,
decoded_event_count: sample.decoded_event_count,
decoded_trade_candidate_count: sample.decoded_trade_candidate_count,
trade_event_count: sample.trade_event_count,
pair_candle_count: sample.pair_candle_count,
};
}
fn kb_demo_pipeline2_normalize_http_role(
role: std::option::Option<std::string::String>,
) -> std::string::String {
match role {
Some(role) => {
let trimmed = role.trim().to_string();
if trimmed.is_empty() {
"history_backfill".to_string()
} else {
trimmed
}
}
if trimmed.is_empty() { "history_backfill".to_string() } else { trimmed }
},
None => "history_backfill".to_string(),
}
}

View File

@@ -61,7 +61,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
error
);
return Err(error);
}
},
};
let prepare_result = config.prepare_filesystem();
if let Err(error) = prepare_result {
@@ -74,7 +74,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
Err(error) => {
eprintln!("kb_app tracing initialization error: {error}");
return Err(error);
}
},
};
tracing::info!(
app_name = %config.app.name,
@@ -92,7 +92,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
Err(error) => {
tracing::error!("cannot create http endpoint pool: {}", error);
panic!("cannot create http endpoint pool: {}", error);
}
},
};
let ws_manager_result = kb_lib::WsManager::from_config(&config);
let ws_manager = match ws_manager_result {
@@ -100,7 +100,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
Err(error) => {
tracing::error!("cannot create websocket manager: {}", error);
panic!("cannot create websocket manager: {}", error);
}
},
};
let app_state = KbAppState {
config: config.clone(),
@@ -151,6 +151,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
crate::demo_pipeline2::demo_pipeline2_backfill_pool_address,
crate::demo_pipeline2::demo_pipeline2_get_pair_candles,
crate::demo_pipeline2::demo_pipeline2_replay_local_pipeline,
crate::demo_pipeline2::demo_pipeline2_diagnose_local_pipeline,
]);
tauri_builder = tauri_builder.plugin(tracing_builder.build::<tauri::Wry>());
tauri_builder = tauri_builder.setup(|app| {
@@ -162,7 +163,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
None => {
tracing::error!("splash window not found");
return;
}
},
};
let main_window_option = app_handle.get_webview_window("main");
let main_window = match main_window_option {
@@ -170,7 +171,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
None => {
tracing::error!("main window not found");
return;
}
},
};
let is_debug = cfg!(debug_assertions);
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
@@ -178,12 +179,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
emit_splash_order(&splash_window, "add_log", Some("Start Fade-In"), None);
}
emit_splash_order(&splash_window, "fadein", None, None);
emit_splash_order(
&splash_window,
"add_msg",
Some("Initialisation..."),
Some("info"),
);
emit_splash_order(&splash_window, "add_msg", Some("Initialisation..."), Some("info"));
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
emit_splash_order(
&splash_window,
@@ -273,20 +269,14 @@ async fn start_ws_clients(
}
kb_emit_app_log(
&app_handle,
&format!(
"[app] starting {} websocket client(s)",
enabled_endpoints.len()
),
&format!("[app] starting {} websocket client(s)", enabled_endpoints.len()),
);
let mut started_clients: std::vec::Vec<kb_lib::WsClient> = std::vec::Vec::new();
let mut relay_tasks: std::vec::Vec<tauri::async_runtime::JoinHandle<()>> = std::vec::Vec::new();
for endpoint in enabled_endpoints {
kb_emit_app_log(
&app_handle,
&format!(
"[app] preparing websocket endpoint '{}' ({})",
endpoint.name, endpoint.url
),
&format!("[app] preparing websocket endpoint '{}' ({})", endpoint.name, endpoint.url),
);
let client_result = kb_lib::WsClient::new(endpoint.clone());
let client = match client_result {
@@ -297,7 +287,7 @@ async fn start_ws_clients(
"cannot create websocket client for endpoint '{}': {}",
endpoint.name, error
));
}
},
};
let mut event_receiver = client.subscribe_events();
let relay_app_handle = app_handle.clone();
@@ -308,7 +298,7 @@ async fn start_ws_clients(
Ok(event) => {
let line = kb_format_ws_event(&event);
kb_emit_app_log(&relay_app_handle, &line);
}
},
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
kb_emit_app_log(
&relay_app_handle,
@@ -317,10 +307,10 @@ async fn start_ws_clients(
skipped
),
);
}
},
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
break;
}
},
}
}
});
@@ -349,10 +339,7 @@ async fn start_ws_clients(
let runtime_guard = state.ws_runtime.lock().await;
runtime_guard.clients.len()
};
kb_emit_app_log(
&app_handle,
&format!("[app] {} websocket client(s) started", started_count),
);
kb_emit_app_log(&app_handle, &format!("[app] {} websocket client(s) started", started_count));
Ok(started_count)
}
@@ -372,10 +359,7 @@ async fn stop_ws_clients(
kb_emit_app_log(&app_handle, "[app] websocket clients are already stopped");
return Ok(0);
}
kb_emit_app_log(
&app_handle,
&format!("[app] stopping {} websocket client(s)", clients.len()),
);
kb_emit_app_log(&app_handle, &format!("[app] stopping {} websocket client(s)", clients.len()));
let stopped_count = clients.len();
for client in &clients {
let disconnect_result = client.disconnect().await;
@@ -393,10 +377,7 @@ async fn stop_ws_clients(
for relay_task in relay_tasks.drain(..) {
relay_task.abort();
}
kb_emit_app_log(
&app_handle,
&format!("[app] {} websocket client(s) stopped", stopped_count),
);
kb_emit_app_log(&app_handle, &format!("[app] {} websocket client(s) stopped", stopped_count));
Ok(stopped_count)
}
@@ -409,34 +390,25 @@ fn kb_emit_app_log(app_handle: &tauri::AppHandle, message: &str) {
fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
match event {
kb_lib::WsEvent::Connected {
endpoint_name,
endpoint_url,
} => {
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
format!("[ws:{endpoint_name}] connected to {endpoint_url}")
}
kb_lib::WsEvent::TextMessage {
endpoint_name,
text,
} => {
},
kb_lib::WsEvent::TextMessage { endpoint_name, text } => {
format!("[ws:{endpoint_name}] text: {text}")
}
kb_lib::WsEvent::JsonRpcMessage {
endpoint_name,
message,
} => match message {
},
kb_lib::WsEvent::JsonRpcMessage { endpoint_name, message } => match message {
kb_lib::KbJsonRpcWsIncomingMessage::SuccessResponse(response) => {
format!(
"[ws:{endpoint_name}] json-rpc success id={} result={}",
response.id, response.result
)
}
},
kb_lib::KbJsonRpcWsIncomingMessage::ErrorResponse(response) => {
format!(
"[ws:{endpoint_name}] json-rpc error id={} code={} message={}",
response.id, response.error.code, response.error.message
)
}
},
kb_lib::KbJsonRpcWsIncomingMessage::Notification(notification) => {
format!(
"[ws:{endpoint_name}] json-rpc notification method={} subscription={} result={}",
@@ -444,22 +416,12 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
notification.params.subscription,
notification.params.result
)
}
},
},
kb_lib::WsEvent::JsonRpcParseError {
endpoint_name,
text,
error,
} => {
format!(
"[ws:{endpoint_name}] json-rpc parse error: {} | raw={}",
error, text
)
}
kb_lib::WsEvent::SubscriptionRegistered {
endpoint_name,
subscription,
} => {
kb_lib::WsEvent::JsonRpcParseError { endpoint_name, text, error } => {
format!("[ws:{endpoint_name}] json-rpc parse error: {} | raw={}", error, text)
},
kb_lib::WsEvent::SubscriptionRegistered { endpoint_name, subscription } => {
format!(
"[ws:{endpoint_name}] subscription registered subscribe_method={} unsubscribe_method={} notification_method={} request_id={} subscription_id={}",
subscription.subscribe_method,
@@ -468,7 +430,7 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
subscription.request_id,
subscription.subscription_id
)
}
},
kb_lib::WsEvent::SubscriptionNotification {
endpoint_name,
subscription,
@@ -483,16 +445,13 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
subscription.subscription_id,
notification.params.result
)
}
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription {
endpoint_name,
notification,
} => {
},
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription { endpoint_name, notification } => {
format!(
"[ws:{endpoint_name}] untracked notification method={} subscription={} result={}",
notification.method, notification.params.subscription, notification.params.result
)
}
},
kb_lib::WsEvent::SubscriptionUnregistered {
endpoint_name,
subscription_id,
@@ -503,44 +462,25 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
"[ws:{endpoint_name}] subscription unregistered subscription_id={} unsubscribe_method={} was_active={}",
subscription_id, unsubscribe_method, was_active
)
}
kb_lib::WsEvent::BinaryMessage {
endpoint_name,
data,
} => {
},
kb_lib::WsEvent::BinaryMessage { endpoint_name, data } => {
format!("[ws:{endpoint_name}] binary message ({} bytes)", data.len())
}
kb_lib::WsEvent::Ping {
endpoint_name,
data,
} => {
},
kb_lib::WsEvent::Ping { endpoint_name, data } => {
format!("[ws:{endpoint_name}] ping ({} bytes)", data.len())
}
kb_lib::WsEvent::Pong {
endpoint_name,
data,
} => {
},
kb_lib::WsEvent::Pong { endpoint_name, data } => {
format!("[ws:{endpoint_name}] pong ({} bytes)", data.len())
}
kb_lib::WsEvent::CloseReceived {
endpoint_name,
code,
reason,
} => {
format!(
"[ws:{endpoint_name}] close received code={:?} reason={:?}",
code, reason
)
}
},
kb_lib::WsEvent::CloseReceived { endpoint_name, code, reason } => {
format!("[ws:{endpoint_name}] close received code={:?} reason={:?}", code, reason)
},
kb_lib::WsEvent::Disconnected { endpoint_name } => {
format!("[ws:{endpoint_name}] disconnected")
}
kb_lib::WsEvent::Error {
endpoint_name,
error,
} => {
},
kb_lib::WsEvent::Error { endpoint_name, error } => {
format!("[ws:{endpoint_name}] error: {error}")
}
},
}
}

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "kb-bapp",
"version": "0.7.24",
"version": "0.7.26",
"identifier": "com.sasedev.kb-app",
"build": {
"beforeDevCommand": "npm run dev",