From fcae75fa7c7d5dbd3bc4a7cb993881a7a8358874 Mon Sep 17 00:00:00 2001 From: redshift <213178690+1ftredsh@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:11:19 +0200 Subject: [PATCH] provider manage instnace is now persisted across calls. --- src/daemon/http/index.ts | 4 ++ src/daemon/index.ts | 4 ++ src/daemon/sse.ts | 98 ---------------------------------------- 3 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 src/daemon/sse.ts diff --git a/src/daemon/http/index.ts b/src/daemon/http/index.ts index 834406e..3ce26c1 100644 --- a/src/daemon/http/index.ts +++ b/src/daemon/http/index.ts @@ -3,6 +3,7 @@ import { type IncomingMessage, type ServerResponse } from "http"; import { routeRequestsToNodeResponse, InsufficientBalanceError, + ProviderManager, } from "@routstr/sdk"; import type { UsageTrackingDriver } from "@routstr/sdk"; import { logger } from "../../utils/logger"; @@ -37,6 +38,7 @@ type DaemonDeps = { ensureProvidersBootstrapped: () => Promise; getRoutstr21Models: (forceRefresh?: boolean) => Promise; mode?: ClientMode; + providerManager: ProviderManager; }; /** @@ -277,6 +279,7 @@ export function createDaemonRequestHandler(deps: { getRoutstr21Models: (forceRefresh?: boolean) => Promise; mode?: "xcashu" | "apikeys"; usageTrackingDriver: UsageTrackingDriver; + providerManager: ProviderManager; }) { return async function handler(req: IncomingMessage, res: ServerResponse) { const host = req.headers.host || "localhost"; @@ -1193,6 +1196,7 @@ export function createDaemonRequestHandler(deps: { mode: deps.mode, usageTrackingDriver: deps.usageTrackingDriver, sdkStore: deps.store, + providerManager: deps.providerManager, res, }); return; diff --git a/src/daemon/index.ts b/src/daemon/index.ts index 8f24d4c..e919bec 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -2,6 +2,7 @@ import { createServer } from "http"; import { existsSync } from "fs"; import { ModelManager, + ProviderManager, createDiscoveryAdapterFromStore, createProviderRegistryFromStore, createStorageAdapterFromStore, @@ -46,6 +47,8 @@ async function main(): Promise { const providerRegistry = createProviderRegistryFromStore(store); const storageAdapter = createStorageAdapterFromStore(store); const modelManager = new ModelManager(discoveryAdapter); + // Create shared ProviderManager for consistent failure tracking across all requests + const providerManager = new ProviderManager(providerRegistry, store); const { ensureProvidersBootstrapped, getRoutstr21Models } = createModelService(modelManager); @@ -72,6 +75,7 @@ async function main(): Promise { getRoutstr21Models, mode: config.mode || "apikeys", usageTrackingDriver, + providerManager, }), ); diff --git a/src/daemon/sse.ts b/src/daemon/sse.ts deleted file mode 100644 index 112345b..0000000 --- a/src/daemon/sse.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Transform } from "stream"; -import type { UsageData } from "./types"; - -export function createSSEParserTransform( - onUsage: (usage: UsageData) => void, - onResponseId?: (responseId: string) => void, -): Transform { - let buffer = ""; - - const maybeCaptureUsageFromJson = (jsonText: string): void => { - try { - const data = JSON.parse(jsonText) as any; - const responseId = data.id; - if (typeof responseId === "string" && responseId.trim().length > 0) { - onResponseId?.(responseId.trim()); - } - - if (data.usage) { - const usageCost = data.usage.cost; - const cost = - typeof usageCost === "number" - ? usageCost - : usageCost?.total_usd ?? - data.metadata?.routstr?.cost?.total_usd ?? - 0; - const msats = - data.metadata?.routstr?.cost?.total_msats ?? - (typeof data.usage.cost_sats === "number" - ? data.usage.cost_sats * 1000 - : 0); - onUsage({ - promptTokens: data.usage.prompt_tokens ?? 0, - completionTokens: data.usage.completion_tokens ?? 0, - totalTokens: data.usage.total_tokens ?? 0, - cost, - satsCost: msats / 1000, - }); - } - } catch { - // Ignore non-JSON lines/events. - } - }; - - const processLine = (self: Transform, line: string): void => { - const trimmed = line.trim(); - if (!trimmed) { - return; - } - - if (trimmed === "data: [DONE]" || trimmed === "[DONE]") { - self.push("data: [DONE]\n\n"); - return; - } - - if (trimmed.startsWith("data:")) { - const dataStr = trimmed.startsWith("data: ") - ? trimmed.slice(6) - : trimmed.slice(5).trimStart(); - if (dataStr === "[DONE]") { - self.push("data: [DONE]\n\n"); - return; - } - maybeCaptureUsageFromJson(dataStr); - self.push(`data: ${dataStr}\n\n`); - return; - } - - if (trimmed.startsWith("{")) { - maybeCaptureUsageFromJson(trimmed); - self.push(`data: ${trimmed}\n\n`); - return; - } - - self.push(line + "\n"); - }; - - return new Transform({ - transform(chunk, encoding, callback) { - buffer += chunk.toString(); - - const lines = buffer.split(/\r?\n/); - buffer = lines.pop() || ""; - - for (const line of lines) { - processLine(this, line); - } - - callback(); - }, - flush(callback) { - if (buffer.trim()) { - processLine(this, buffer); - } - buffer = ""; - callback(); - }, - }); -}