mirror of
https://github.com/Routstr/routstrd.git
synced 2026-08-09 03:44:38 +00:00
Merge pull request #47 from Routstr/syncing-sdk
Sync daemon with SDK discovery adapter API
This commit is contained in:
@@ -37,7 +37,6 @@ type DaemonDeps = {
|
||||
walletClient: CocodClient;
|
||||
walletAdapter: any;
|
||||
storageAdapter: any;
|
||||
providerRegistry: any;
|
||||
discoveryAdapter: any;
|
||||
modelManager: any;
|
||||
ensureProvidersBootstrapped: () => Promise<void>;
|
||||
@@ -292,7 +291,6 @@ export function createDaemonRequestHandler(deps: {
|
||||
walletClient: CocodClient;
|
||||
walletAdapter: any;
|
||||
storageAdapter: any;
|
||||
providerRegistry: any;
|
||||
discoveryAdapter: any;
|
||||
modelManager: any;
|
||||
ensureProvidersBootstrapped: () => Promise<void>;
|
||||
@@ -1268,7 +1266,6 @@ export function createDaemonRequestHandler(deps: {
|
||||
headers: incomingHeaders,
|
||||
walletAdapter: deps.walletAdapter,
|
||||
storageAdapter: deps.storageAdapter,
|
||||
providerRegistry: deps.providerRegistry,
|
||||
discoveryAdapter: deps.discoveryAdapter,
|
||||
modelManager: deps.modelManager,
|
||||
debugLevel: "DEBUG",
|
||||
|
||||
@@ -6,15 +6,27 @@ import { getUsageSummary, __resetUsageSummaryCacheForTest } from "./usage-summar
|
||||
|
||||
// ─── Test fixtures ────────────────────────────────────────────────────────────
|
||||
//
|
||||
// All timestamps fall within 30 days of 2026-06-02 (today as of this writing).
|
||||
// All timestamps are computed relative to "now" so the fixtures never age
|
||||
// out of getUsageSummary's 30-day rolling window. We anchor to a recent UTC
|
||||
// midnight ~10 days ago and keep the same hour-of-day structure the
|
||||
// tz-bucketing assertions depend on.
|
||||
//
|
||||
// tz = 300 (UTC-5 / EST). After shifting by -300min, UTC dates become:
|
||||
// d1: 2026-05-20T10:00Z → shifted 2026-05-20T05:00Z → local day "2026-05-20", hour 05
|
||||
// d2: 2026-05-21T03:00Z → shifted 2026-05-20T22:00Z → local day "2026-05-20", hour 22
|
||||
// d3: 2026-05-21T06:00Z → shifted 2026-05-21T01:00Z → local day "2026-05-21", hour 01
|
||||
// d1: <anchor> 10:00Z → local <anchor> 05:00 → local day "<anchor>"
|
||||
// d2: <anchor+1d> 03:00Z → local <anchor> 22:00 → local day "<anchor>"
|
||||
// d3: <anchor+1d> 06:00Z → local <anchor+1d> 01:00 → local day "<anchor+1d>"
|
||||
|
||||
const D1 = new Date("2026-05-20T10:00:00Z").getTime(); // local 2026-05-20, hour 05
|
||||
const D2 = new Date("2026-05-21T03:00:00Z").getTime(); // local 2026-05-20, hour 22
|
||||
const D3 = new Date("2026-05-21T06:00:00Z").getTime(); // local 2026-05-21, hour 01
|
||||
const _BASE_UTC_MIDNIGHT = Math.floor(Date.now() / 86_400_000) * 86_400_000;
|
||||
const _ANCHOR_MS = _BASE_UTC_MIDNIGHT - 10 * 86_400_000; // 10 days ago UTC
|
||||
const D1 = _ANCHOR_MS + 10 * 3_600_000; // 10:00Z → local day <anchor>
|
||||
const D2 = _ANCHOR_MS + 1 * 86_400_000 + 3 * 3_600_000; // 03:00Z next day → local day <anchor>
|
||||
const D3 = _ANCHOR_MS + 1 * 86_400_000 + 6 * 3_600_000; // 06:00Z next day → local day <anchor+1d>
|
||||
|
||||
/** Compute the local-day date string the same way the SDK's day grouping does. */
|
||||
function localDayDateStr(ts: number, tzOffsetMinutes: number): string {
|
||||
const d = new Date(ts - tzOffsetMinutes * 60_000);
|
||||
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
const BASE_ENTRY: Omit<UsageTrackingEntry, "id" | "timestamp" | "modelId" | "client" | "totalTokens"> = {
|
||||
baseUrl: "https://api.openai.com/",
|
||||
@@ -152,23 +164,24 @@ describe("getUsageSummary", () => {
|
||||
const summary = await getUsageSummary(driver, CLIENTS, TZ);
|
||||
const { days } = summary;
|
||||
|
||||
// D1 (10:00Z) → local 2026-05-20, D2 (03:00Z) → local 2026-05-20, D3 (06:00Z) → local 2026-05-21
|
||||
// So: day 2026-05-20 has e1+e2, day 2026-05-21 has e3+e4
|
||||
// D1 (10:00Z) and D2 (next day 03:00Z) both bucket to local day <anchor>;
|
||||
// D3 (next day 06:00Z) buckets to local day <anchor+1d>.
|
||||
// So: day <anchor> has e1+e2, day <anchor+1d> has e3+e4
|
||||
expect(days.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
// Most-recent-first: 2026-05-21 first
|
||||
expect(days[0]!.date).toBe("2026-05-21");
|
||||
// Most-recent-first: <anchor+1d> first
|
||||
expect(days[0]!.date).toBe(localDayDateStr(D3, TZ));
|
||||
expect(days[0]!.requests).toBe(2); // e3, e4
|
||||
expect(days[0]!.satsCost).toBe(8 + 50);
|
||||
|
||||
expect(days[1]!.date).toBe("2026-05-20");
|
||||
expect(days[1]!.date).toBe(localDayDateStr(D1, TZ));
|
||||
expect(days[1]!.requests).toBe(2); // e1, e2
|
||||
expect(days[1]!.satsCost).toBe(10 + 20);
|
||||
});
|
||||
|
||||
it("returns empty hoursToday (test entries are not today)", async () => {
|
||||
const summary = await getUsageSummary(driver, CLIENTS, TZ);
|
||||
// Test entries are in May 2026, not today (June 2026)
|
||||
// Test entries are ~10 days ago, not today
|
||||
expect(summary.hoursToday).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
||||
+13
-5
@@ -39,7 +39,6 @@ import {
|
||||
createBunSqliteDriver,
|
||||
createBunSqliteUsageTrackingDriver,
|
||||
createShardedDiscoveryAdapter,
|
||||
createProviderRegistryFromDiscoveryAdapter,
|
||||
} from "@routstr/sdk/storage/bun";
|
||||
import { createWalletAdapter } from "./wallet";
|
||||
import type { AutoRefillConfig } from "./wallet/auto-refill";
|
||||
@@ -82,7 +81,6 @@ async function main(): Promise<void> {
|
||||
});
|
||||
|
||||
const discoveryAdapter = await createShardedDiscoveryAdapter({ driver: sqliteDriver });
|
||||
const providerRegistry = createProviderRegistryFromDiscoveryAdapter(discoveryAdapter, daemonSdkLogger);
|
||||
const storageAdapter = createStorageAdapterFromStore(store);
|
||||
const modelManager = new ModelManager(discoveryAdapter, {
|
||||
logger: daemonSdkLogger,
|
||||
@@ -91,7 +89,7 @@ async function main(): Promise<void> {
|
||||
nostrRelays: config.relays,
|
||||
});
|
||||
// Create shared ProviderManager for consistent failure tracking across all requests
|
||||
const providerManager = new ProviderManager(providerRegistry, store, daemonSdkLogger);
|
||||
const providerManager = new ProviderManager(discoveryAdapter, store, daemonSdkLogger);
|
||||
const { ensureProvidersBootstrapped, getRoutstr21Models, getModelProviders, refreshProvidersAndModels } =
|
||||
createModelService(modelManager, store);
|
||||
|
||||
@@ -123,7 +121,7 @@ async function main(): Promise<void> {
|
||||
const refundClient = new RoutstrClient(
|
||||
walletAdapter,
|
||||
storageAdapter,
|
||||
providerRegistry,
|
||||
discoveryAdapter,
|
||||
"min",
|
||||
"apikeys",
|
||||
{ logger: daemonSdkLogger },
|
||||
@@ -139,7 +137,6 @@ async function main(): Promise<void> {
|
||||
walletClient,
|
||||
walletAdapter,
|
||||
storageAdapter,
|
||||
providerRegistry,
|
||||
discoveryAdapter,
|
||||
modelManager,
|
||||
ensureProvidersBootstrapped,
|
||||
@@ -176,6 +173,13 @@ async function main(): Promise<void> {
|
||||
);
|
||||
|
||||
refreshInterval = setInterval(async () => {
|
||||
logger.log("Running scheduled Nostr event refresh...");
|
||||
try {
|
||||
await modelManager.refreshNostrEvents();
|
||||
} catch (error) {
|
||||
logger.error("Scheduled Nostr event refresh failed:", error);
|
||||
}
|
||||
|
||||
logger.log("Running scheduled model refresh...");
|
||||
try {
|
||||
await refreshModelsAndIntegrations(getRoutstr21Models, updatedConfig, "Scheduled");
|
||||
@@ -268,6 +272,10 @@ async function main(): Promise<void> {
|
||||
// Start the recurring model refresh job after initial bootstrap
|
||||
void ensureProvidersBootstrapped()
|
||||
.then(async () => {
|
||||
// Catch up on any Nostr events published since last run
|
||||
logger.log("Running initial Nostr event refresh...");
|
||||
await modelManager.refreshNostrEvents();
|
||||
|
||||
startModelRefreshJob();
|
||||
startRefundJob();
|
||||
// Run an immediate refresh to populate models right away
|
||||
|
||||
+13
-6
@@ -278,18 +278,25 @@ export function renderToday(stats: UsageStats, width: number): string {
|
||||
const _now = new Date();
|
||||
const todayDateStr = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
|
||||
|
||||
let todayStats: { date: string; requests: number; satsCost: number; promptTokens: number; completionTokens: number; totalTokens: number };
|
||||
let recentDays: Array<{ date: string; requests: number; satsCost: number; totalTokens: number; promptTokens: number; completionTokens: number }>;
|
||||
let hourlyMap: Map<number, { requests: number; satsCost: number }>;
|
||||
let todayStats: { date: string; requests: number; satsCost: number; promptTokens: number; completionTokens: number; totalTokens: number } = {
|
||||
date: todayDateStr,
|
||||
requests: 0,
|
||||
satsCost: 0,
|
||||
promptTokens: 0,
|
||||
completionTokens: 0,
|
||||
totalTokens: 0,
|
||||
};
|
||||
let recentDays: Array<{ date: string; requests: number; satsCost: number; totalTokens: number; promptTokens: number; completionTokens: number }> = [];
|
||||
let hourlyMap: Map<number, { requests: number; satsCost: number }> = new Map();
|
||||
|
||||
if (stats.summary) {
|
||||
// Use server-aggregated data
|
||||
const { days, hoursToday } = stats.summary;
|
||||
// days[0] is most-recent-first; check if it's today
|
||||
const todayDayStat = days[0]?.date === todayDateStr ? days[0] : undefined;
|
||||
todayStats = todayDayStat
|
||||
? { date: todayDayStat.date, requests: todayDayStat.requests, satsCost: todayDayStat.satsCost, promptTokens: todayDayStat.promptTokens, completionTokens: todayDayStat.completionTokens, totalTokens: todayDayStat.totalTokens }
|
||||
: { date: todayDateStr, requests: 0, satsCost: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
||||
if (todayDayStat) {
|
||||
todayStats = { date: todayDayStat.date, requests: todayDayStat.requests, satsCost: todayDayStat.satsCost, promptTokens: todayDayStat.promptTokens, completionTokens: todayDayStat.completionTokens, totalTokens: todayDayStat.totalTokens };
|
||||
}
|
||||
// Exclude today by date (it has its own box) rather than by position —
|
||||
// days[0] is only today when there was activity today.
|
||||
recentDays = days.filter((d) => d.date !== todayDateStr).slice(0, 6);
|
||||
|
||||
Reference in New Issue
Block a user