@routstr/sdk 0.3.10 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +19 -6
  2. package/dist/browser.d.mts +2 -2
  3. package/dist/browser.d.ts +2 -2
  4. package/dist/browser.js +201 -66
  5. package/dist/browser.js.map +1 -1
  6. package/dist/browser.mjs +198 -67
  7. package/dist/browser.mjs.map +1 -1
  8. package/dist/bun.d.mts +5 -5
  9. package/dist/bun.d.ts +5 -5
  10. package/dist/bun.js +275 -66
  11. package/dist/bun.js.map +1 -1
  12. package/dist/bun.mjs +272 -67
  13. package/dist/bun.mjs.map +1 -1
  14. package/dist/{bunSqlite-BMTseLIz.d.ts → bunSqlite-BmXWNc25.d.ts} +1 -1
  15. package/dist/{bunSqlite-D6AreVE2.d.mts → bunSqlite-Bro9efsl.d.mts} +1 -1
  16. package/dist/client/index.d.mts +31 -10
  17. package/dist/client/index.d.ts +31 -10
  18. package/dist/client/index.js +185 -36
  19. package/dist/client/index.js.map +1 -1
  20. package/dist/client/index.mjs +182 -37
  21. package/dist/client/index.mjs.map +1 -1
  22. package/dist/discovery/index.d.mts +3 -3
  23. package/dist/discovery/index.d.ts +3 -3
  24. package/dist/discovery/index.js +12 -20
  25. package/dist/discovery/index.js.map +1 -1
  26. package/dist/discovery/index.mjs +12 -20
  27. package/dist/discovery/index.mjs.map +1 -1
  28. package/dist/index.d.mts +8 -6
  29. package/dist/index.d.ts +8 -6
  30. package/dist/index.js +201 -66
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +198 -67
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/node.d.mts +2 -2
  35. package/dist/node.d.ts +2 -2
  36. package/dist/node.js +276 -66
  37. package/dist/node.js.map +1 -1
  38. package/dist/node.mjs +273 -67
  39. package/dist/node.mjs.map +1 -1
  40. package/dist/storage/bun.d.mts +4 -4
  41. package/dist/storage/bun.d.ts +4 -4
  42. package/dist/storage/bun.js +173 -0
  43. package/dist/storage/bun.js.map +1 -1
  44. package/dist/storage/bun.mjs +173 -0
  45. package/dist/storage/bun.mjs.map +1 -1
  46. package/dist/storage/index.d.mts +2 -2
  47. package/dist/storage/index.d.ts +2 -2
  48. package/dist/storage/index.js +99 -0
  49. package/dist/storage/index.js.map +1 -1
  50. package/dist/storage/index.mjs +99 -0
  51. package/dist/storage/index.mjs.map +1 -1
  52. package/dist/storage/node.d.mts +2 -2
  53. package/dist/storage/node.d.ts +2 -2
  54. package/dist/storage/node.js +174 -0
  55. package/dist/storage/node.js.map +1 -1
  56. package/dist/storage/node.mjs +174 -0
  57. package/dist/storage/node.mjs.map +1 -1
  58. package/dist/{store-C8MZlfuz.d.ts → store-CAQLSbEj.d.ts} +38 -1
  59. package/dist/{store-BiuM2V9N.d.mts → store-CuXwe5Rg.d.mts} +38 -1
  60. package/dist/wallet/index.js +38 -24
  61. package/dist/wallet/index.js.map +1 -1
  62. package/dist/wallet/index.mjs +38 -24
  63. package/dist/wallet/index.mjs.map +1 -1
  64. package/package.json +7 -3
@@ -1,4 +1,4 @@
1
- import { a as StorageDriver, U as UsageTrackingDriver } from './store-C8MZlfuz.js';
1
+ import { a as StorageDriver, c as UsageTrackingDriver } from './store-CAQLSbEj.js';
2
2
  import { S as SdkLogger } from './types-_21yYFZG.js';
3
3
 
4
4
  declare function createBunSqliteDriver(dbPath: string, options?: {
@@ -1,4 +1,4 @@
1
- import { a as StorageDriver, U as UsageTrackingDriver } from './store-BiuM2V9N.mjs';
1
+ import { a as StorageDriver, c as UsageTrackingDriver } from './store-CuXwe5Rg.mjs';
2
2
  import { S as SdkLogger } from './types-_21yYFZG.mjs';
3
3
 
4
4
  declare function createBunSqliteDriver(dbPath: string, options?: {
@@ -1,6 +1,6 @@
1
- import { e as Model, S as SdkLogger, m as StreamingResult, M as Message, n as TransactionHistory } from '../types-_21yYFZG.mjs';
1
+ import { e as Model, S as SdkLogger, m as StreamingResult, U as UsageStats, M as Message, n as TransactionHistory } from '../types-_21yYFZG.mjs';
2
2
  import { P as ProviderRegistry, W as WalletAdapter, S as StorageAdapter, a as StreamingCallbacks } from '../interfaces-Csn8Uq04.mjs';
3
- import { S as SdkStore, U as UsageTrackingDriver } from '../store-BiuM2V9N.mjs';
3
+ import { S as SdkStore, c as UsageTrackingDriver } from '../store-CuXwe5Rg.mjs';
4
4
  import { CashuSpender, BalanceManager } from '../wallet/index.mjs';
5
5
  import { Transform } from 'stream';
6
6
  import 'zustand/vanilla';
@@ -181,6 +181,23 @@ interface RouteRequestParams {
181
181
  modelId?: string;
182
182
  clientApiKey?: string;
183
183
  }
184
+ interface RequestResponseLogRequestInput {
185
+ method: string;
186
+ url: string;
187
+ path: string;
188
+ baseUrl: string;
189
+ headers: Record<string, string>;
190
+ body?: unknown;
191
+ rawBody?: string;
192
+ }
193
+ interface RequestResponseLogSink {
194
+ logRequest?(input: RequestResponseLogRequestInput): string | undefined | Promise<string | undefined>;
195
+ logResponseStart?(id: string | undefined, response: Response): void | Promise<void>;
196
+ logResponseChunk?(id: string | undefined, sequence: number, text: string): void | Promise<void>;
197
+ logResponseEnd?(id: string | undefined): void | Promise<void>;
198
+ logResponseError?(id: string | undefined, error: unknown): void | Promise<void>;
199
+ logResponseBody?(id: string | undefined, response: Response): void | Promise<void>;
200
+ }
184
201
  interface RoutstrClientConfig {
185
202
  usageTrackingDriver?: UsageTrackingDriver;
186
203
  sdkStore?: SdkStore;
@@ -188,6 +205,8 @@ interface RoutstrClientConfig {
188
205
  providerManager?: ProviderManager;
189
206
  /** Optional: injectable logger (defaults to consoleLogger) */
190
207
  logger?: SdkLogger;
208
+ /** Optional: raw request/response logging callbacks supplied by the runtime/app. */
209
+ requestResponseLogSink?: RequestResponseLogSink;
191
210
  }
192
211
  declare class RoutstrClient {
193
212
  private walletAdapter;
@@ -202,6 +221,7 @@ declare class RoutstrClient {
202
221
  private usageTrackingDriver?;
203
222
  private sdkStore?;
204
223
  private logger;
224
+ private requestResponseLogSink?;
205
225
  constructor(walletAdapter: WalletAdapter, storageAdapter: StorageAdapter, providerRegistry: ProviderRegistry, alertLevel: AlertLevel, mode?: RoutstrClientMode, options?: RoutstrClientConfig);
206
226
  /**
207
227
  * Get the current client mode
@@ -349,6 +369,10 @@ interface UsageTrackingData {
349
369
  cacheCreationMsats?: number;
350
370
  remainingBalanceMsats?: number;
351
371
  }
372
+ declare function extractUsageFromResponseBody(body: unknown, fallbackSatsCost?: number): UsageTrackingData | null;
373
+ declare function extractResponseId(body: unknown): string | undefined;
374
+ declare function extractUsageFromSSEJson(parsed: any, fallbackSatsCost?: number): UsageTrackingData | null;
375
+ declare function toUsageStats(usage: UsageTrackingData | null | undefined): UsageStats | undefined;
352
376
 
353
377
  /**
354
378
  * Inspect a Web `ReadableStream<Uint8Array>` of SSE bytes for `usage` and
@@ -363,7 +387,10 @@ interface UsageTrackingData {
363
387
  * The returned Promise resolves with the final captured values once the
364
388
  * stream ends (or is cancelled / errors out).
365
389
  */
366
- declare function inspectSSEWebStream(stream: ReadableStream<Uint8Array>, onUsage: (usage: UsageTrackingData) => void, onResponseId?: (responseId: string) => void): Promise<{
390
+ declare function inspectSSEWebStream(stream: ReadableStream<Uint8Array>, onUsage: (usage: UsageTrackingData) => void, onResponseId?: (responseId: string) => void, options?: {
391
+ /** Called with each raw chunk read from the tee'd inspection branch. */
392
+ onRawChunk?: (chunk: Uint8Array, sequence: number, text: string) => void | Promise<void>;
393
+ }): Promise<{
367
394
  capturedUsage?: UsageTrackingData;
368
395
  capturedResponseId?: string;
369
396
  }>;
@@ -409,16 +436,10 @@ interface FetchAIResponseClient {
409
436
  mintUrl: string;
410
437
  modelId?: string;
411
438
  }): Promise<Response>;
412
- getProviderManager(): {
413
- getModelForProvider(baseUrl: string, modelId: string): Promise<{
414
- id: string;
415
- } | null>;
416
- };
417
439
  getMode(): RoutstrClientMode;
418
440
  }
419
441
  interface FetchAIResponseDeps {
420
442
  client: FetchAIResponseClient;
421
- providerRegistry: ProviderRegistry;
422
443
  alertLevel: AlertLevel;
423
444
  logger: SdkLogger;
424
445
  getPendingCashuTokenAmount?: () => number;
@@ -430,4 +451,4 @@ interface FetchAIResponseDeps {
430
451
  */
431
452
  declare function fetchAIResponse(options: FetchOptions, callbacks: StreamingCallbacks, deps: FetchAIResponseDeps): Promise<void>;
432
453
 
433
- export { type AlertLevel, type DebugLevel, type FetchAIResponseDeps, type ModelProviderPrice, ProviderManager, type RouteRequestParams, RoutstrClient, type RoutstrClientConfig, type RoutstrClientMode, type StreamCallbacks, StreamProcessor, createSSEParserTransform, fetchAIResponse, inspectSSEWebStream };
454
+ export { type AlertLevel, type DebugLevel, type FetchAIResponseDeps, type ModelProviderPrice, ProviderManager, type RequestResponseLogRequestInput, type RequestResponseLogSink, type RouteRequestParams, RoutstrClient, type RoutstrClientConfig, type RoutstrClientMode, type StreamCallbacks, StreamProcessor, type UsageTrackingData, createSSEParserTransform, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, inspectSSEWebStream, toUsageStats };
@@ -1,6 +1,6 @@
1
- import { e as Model, S as SdkLogger, m as StreamingResult, M as Message, n as TransactionHistory } from '../types-_21yYFZG.js';
1
+ import { e as Model, S as SdkLogger, m as StreamingResult, U as UsageStats, M as Message, n as TransactionHistory } from '../types-_21yYFZG.js';
2
2
  import { P as ProviderRegistry, W as WalletAdapter, S as StorageAdapter, a as StreamingCallbacks } from '../interfaces-C-DYd9Jy.js';
3
- import { S as SdkStore, U as UsageTrackingDriver } from '../store-C8MZlfuz.js';
3
+ import { S as SdkStore, c as UsageTrackingDriver } from '../store-CAQLSbEj.js';
4
4
  import { CashuSpender, BalanceManager } from '../wallet/index.js';
5
5
  import { Transform } from 'stream';
6
6
  import 'zustand/vanilla';
@@ -181,6 +181,23 @@ interface RouteRequestParams {
181
181
  modelId?: string;
182
182
  clientApiKey?: string;
183
183
  }
184
+ interface RequestResponseLogRequestInput {
185
+ method: string;
186
+ url: string;
187
+ path: string;
188
+ baseUrl: string;
189
+ headers: Record<string, string>;
190
+ body?: unknown;
191
+ rawBody?: string;
192
+ }
193
+ interface RequestResponseLogSink {
194
+ logRequest?(input: RequestResponseLogRequestInput): string | undefined | Promise<string | undefined>;
195
+ logResponseStart?(id: string | undefined, response: Response): void | Promise<void>;
196
+ logResponseChunk?(id: string | undefined, sequence: number, text: string): void | Promise<void>;
197
+ logResponseEnd?(id: string | undefined): void | Promise<void>;
198
+ logResponseError?(id: string | undefined, error: unknown): void | Promise<void>;
199
+ logResponseBody?(id: string | undefined, response: Response): void | Promise<void>;
200
+ }
184
201
  interface RoutstrClientConfig {
185
202
  usageTrackingDriver?: UsageTrackingDriver;
186
203
  sdkStore?: SdkStore;
@@ -188,6 +205,8 @@ interface RoutstrClientConfig {
188
205
  providerManager?: ProviderManager;
189
206
  /** Optional: injectable logger (defaults to consoleLogger) */
190
207
  logger?: SdkLogger;
208
+ /** Optional: raw request/response logging callbacks supplied by the runtime/app. */
209
+ requestResponseLogSink?: RequestResponseLogSink;
191
210
  }
192
211
  declare class RoutstrClient {
193
212
  private walletAdapter;
@@ -202,6 +221,7 @@ declare class RoutstrClient {
202
221
  private usageTrackingDriver?;
203
222
  private sdkStore?;
204
223
  private logger;
224
+ private requestResponseLogSink?;
205
225
  constructor(walletAdapter: WalletAdapter, storageAdapter: StorageAdapter, providerRegistry: ProviderRegistry, alertLevel: AlertLevel, mode?: RoutstrClientMode, options?: RoutstrClientConfig);
206
226
  /**
207
227
  * Get the current client mode
@@ -349,6 +369,10 @@ interface UsageTrackingData {
349
369
  cacheCreationMsats?: number;
350
370
  remainingBalanceMsats?: number;
351
371
  }
372
+ declare function extractUsageFromResponseBody(body: unknown, fallbackSatsCost?: number): UsageTrackingData | null;
373
+ declare function extractResponseId(body: unknown): string | undefined;
374
+ declare function extractUsageFromSSEJson(parsed: any, fallbackSatsCost?: number): UsageTrackingData | null;
375
+ declare function toUsageStats(usage: UsageTrackingData | null | undefined): UsageStats | undefined;
352
376
 
353
377
  /**
354
378
  * Inspect a Web `ReadableStream<Uint8Array>` of SSE bytes for `usage` and
@@ -363,7 +387,10 @@ interface UsageTrackingData {
363
387
  * The returned Promise resolves with the final captured values once the
364
388
  * stream ends (or is cancelled / errors out).
365
389
  */
366
- declare function inspectSSEWebStream(stream: ReadableStream<Uint8Array>, onUsage: (usage: UsageTrackingData) => void, onResponseId?: (responseId: string) => void): Promise<{
390
+ declare function inspectSSEWebStream(stream: ReadableStream<Uint8Array>, onUsage: (usage: UsageTrackingData) => void, onResponseId?: (responseId: string) => void, options?: {
391
+ /** Called with each raw chunk read from the tee'd inspection branch. */
392
+ onRawChunk?: (chunk: Uint8Array, sequence: number, text: string) => void | Promise<void>;
393
+ }): Promise<{
367
394
  capturedUsage?: UsageTrackingData;
368
395
  capturedResponseId?: string;
369
396
  }>;
@@ -409,16 +436,10 @@ interface FetchAIResponseClient {
409
436
  mintUrl: string;
410
437
  modelId?: string;
411
438
  }): Promise<Response>;
412
- getProviderManager(): {
413
- getModelForProvider(baseUrl: string, modelId: string): Promise<{
414
- id: string;
415
- } | null>;
416
- };
417
439
  getMode(): RoutstrClientMode;
418
440
  }
419
441
  interface FetchAIResponseDeps {
420
442
  client: FetchAIResponseClient;
421
- providerRegistry: ProviderRegistry;
422
443
  alertLevel: AlertLevel;
423
444
  logger: SdkLogger;
424
445
  getPendingCashuTokenAmount?: () => number;
@@ -430,4 +451,4 @@ interface FetchAIResponseDeps {
430
451
  */
431
452
  declare function fetchAIResponse(options: FetchOptions, callbacks: StreamingCallbacks, deps: FetchAIResponseDeps): Promise<void>;
432
453
 
433
- export { type AlertLevel, type DebugLevel, type FetchAIResponseDeps, type ModelProviderPrice, ProviderManager, type RouteRequestParams, RoutstrClient, type RoutstrClientConfig, type RoutstrClientMode, type StreamCallbacks, StreamProcessor, createSSEParserTransform, fetchAIResponse, inspectSSEWebStream };
454
+ export { type AlertLevel, type DebugLevel, type FetchAIResponseDeps, type ModelProviderPrice, ProviderManager, type RequestResponseLogRequestInput, type RequestResponseLogSink, type RouteRequestParams, RoutstrClient, type RoutstrClientConfig, type RoutstrClientMode, type StreamCallbacks, StreamProcessor, type UsageTrackingData, createSSEParserTransform, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, inspectSSEWebStream, toUsageStats };
@@ -1061,8 +1061,8 @@ var BalanceManager = class _BalanceManager {
1061
1061
  const refundableProviderBalance = Object.entries(
1062
1062
  balanceState.providerBalances
1063
1063
  ).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
1064
- if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 2) {
1065
- await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount);
1064
+ if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 3) {
1065
+ await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, adjustedAmount);
1066
1066
  return this.createProviderToken({
1067
1067
  ...options,
1068
1068
  retryCount: retryCount + 1
@@ -1201,33 +1201,47 @@ var BalanceManager = class _BalanceManager {
1201
1201
  }
1202
1202
  return candidates;
1203
1203
  }
1204
- async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
1204
+ async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, requiredAmount) {
1205
1205
  const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
1206
1206
  const forceRefund = retryCount >= 2;
1207
- const apiKeysToRefund = apiKeyDistribution.filter(
1208
- (apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
1209
- );
1210
- const apiKeyRefundResults = await Promise.allSettled(
1211
- apiKeysToRefund.map(async (apiKeyEntry) => {
1212
- const fullApiKeyEntry = this.storageAdapter.getApiKey(
1213
- apiKeyEntry.baseUrl
1214
- );
1215
- if (!fullApiKeyEntry) {
1216
- return { baseUrl: apiKeyEntry.baseUrl, success: false };
1217
- }
1218
- const result = await this.refundApiKey({
1207
+ const candidates = apiKeyDistribution.filter((apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0).map((apiKey) => {
1208
+ const full = this.storageAdapter.getApiKey(apiKey.baseUrl);
1209
+ return {
1210
+ baseUrl: apiKey.baseUrl,
1211
+ amount: apiKey.amount,
1212
+ lastUsed: full?.lastUsed ?? 0,
1213
+ key: full?.key
1214
+ };
1215
+ }).filter((c) => c.key != null).sort((a, b) => a.lastUsed - b.lastUsed);
1216
+ if (candidates.length === 0) return;
1217
+ if (forceRefund) {
1218
+ for (const candidate of candidates) {
1219
+ await this.refundApiKey({
1219
1220
  mintUrl,
1220
- baseUrl: apiKeyEntry.baseUrl,
1221
- apiKey: fullApiKeyEntry.key,
1222
- forceRefund
1221
+ baseUrl: candidate.baseUrl,
1222
+ apiKey: candidate.key,
1223
+ forceRefund: true
1223
1224
  });
1224
- return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
1225
- })
1226
- );
1227
- for (const result of apiKeyRefundResults) {
1228
- if (result.status === "fulfilled" && result.value.success) {
1229
- this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
1225
+ const newState = await this.getBalanceState();
1226
+ const newAvailable = (newState.mintBalances[mintUrl] || 0) + (newState.providerBalances[baseUrl] || 0);
1227
+ if (newAvailable >= requiredAmount) {
1228
+ this.logger.log(
1229
+ `_refundOtherProvidersForTopUp: freed enough balance (${newAvailable} >= ${requiredAmount}), stopping early`
1230
+ );
1231
+ return;
1232
+ }
1230
1233
  }
1234
+ } else {
1235
+ await Promise.allSettled(
1236
+ candidates.map(
1237
+ (candidate) => this.refundApiKey({
1238
+ mintUrl,
1239
+ baseUrl: candidate.baseUrl,
1240
+ apiKey: candidate.key,
1241
+ forceRefund: false
1242
+ })
1243
+ )
1244
+ );
1231
1245
  }
1232
1246
  }
1233
1247
  /**
@@ -2052,6 +2066,91 @@ var SDK_STORAGE_KEYS = {
2052
2066
  PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
2053
2067
  };
2054
2068
 
2069
+ // storage/usageTracking/aggregate.ts
2070
+ var pad2 = (n) => String(n).padStart(2, "0");
2071
+ var jsGroupKey = (entry, groupBy, tzOffsetMinutes) => {
2072
+ switch (groupBy) {
2073
+ case "modelId":
2074
+ return entry.modelId ?? null;
2075
+ case "baseUrl":
2076
+ return entry.baseUrl ?? null;
2077
+ case "client":
2078
+ return entry.client ?? null;
2079
+ case "sessionId":
2080
+ return entry.sessionId ?? null;
2081
+ case "provider":
2082
+ return entry.provider ?? null;
2083
+ case "day": {
2084
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
2085
+ return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
2086
+ }
2087
+ case "hour": {
2088
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
2089
+ return pad2(d.getUTCHours());
2090
+ }
2091
+ }
2092
+ };
2093
+ var reduceAggregate = (entries, options = {}) => {
2094
+ const emptyRow = (group) => ({
2095
+ group,
2096
+ requests: 0,
2097
+ promptTokens: 0,
2098
+ completionTokens: 0,
2099
+ totalTokens: 0,
2100
+ cost: 0,
2101
+ satsCost: 0,
2102
+ baseMsats: 0,
2103
+ inputMsats: 0,
2104
+ outputMsats: 0,
2105
+ totalMsats: 0,
2106
+ totalUsd: 0,
2107
+ cacheReadInputTokens: 0,
2108
+ cacheCreationInputTokens: 0,
2109
+ cacheReadMsats: 0,
2110
+ cacheCreationMsats: 0
2111
+ });
2112
+ const accumulate = (row, entry) => {
2113
+ row.requests += 1;
2114
+ row.promptTokens += entry.promptTokens;
2115
+ row.completionTokens += entry.completionTokens;
2116
+ row.totalTokens += entry.totalTokens;
2117
+ row.cost += entry.cost;
2118
+ row.satsCost += entry.satsCost;
2119
+ row.baseMsats += entry.baseMsats ?? 0;
2120
+ row.inputMsats += entry.inputMsats ?? 0;
2121
+ row.outputMsats += entry.outputMsats ?? 0;
2122
+ row.totalMsats += entry.totalMsats ?? 0;
2123
+ row.totalUsd += entry.totalUsd ?? 0;
2124
+ row.cacheReadInputTokens += entry.cacheReadInputTokens ?? 0;
2125
+ row.cacheCreationInputTokens += entry.cacheCreationInputTokens ?? 0;
2126
+ row.cacheReadMsats += entry.cacheReadMsats ?? 0;
2127
+ row.cacheCreationMsats += entry.cacheCreationMsats ?? 0;
2128
+ };
2129
+ if (!options.groupBy) {
2130
+ const total = emptyRow(null);
2131
+ for (const entry of entries) accumulate(total, entry);
2132
+ return [total];
2133
+ }
2134
+ const tz = options.tzOffsetMinutes ?? 0;
2135
+ const groups = /* @__PURE__ */ new Map();
2136
+ for (const entry of entries) {
2137
+ const key = jsGroupKey(entry, options.groupBy, tz);
2138
+ let row = groups.get(key);
2139
+ if (!row) {
2140
+ row = emptyRow(key);
2141
+ groups.set(key, row);
2142
+ }
2143
+ accumulate(row, entry);
2144
+ }
2145
+ const rows = [...groups.values()];
2146
+ if (options.groupBy === "day" || options.groupBy === "hour") {
2147
+ rows.sort((a, b) => (a.group ?? "").localeCompare(b.group ?? ""));
2148
+ } else {
2149
+ rows.sort((a, b) => b.satsCost - a.satsCost);
2150
+ }
2151
+ return rows;
2152
+ };
2153
+
2055
2154
  // storage/usageTracking/indexedDB.ts
2056
2155
  var DEFAULT_DB_NAME = "routstr-sdk";
2057
2156
  var DEFAULT_STORE_NAME = "usage_tracking";
@@ -2114,6 +2213,9 @@ var matchesFilters = (entry, options = {}) => {
2114
2213
  if (options.client && entry.client !== options.client) {
2115
2214
  return false;
2116
2215
  }
2216
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
2217
+ return false;
2218
+ }
2117
2219
  if (options.provider && entry.provider !== options.provider) {
2118
2220
  return false;
2119
2221
  }
@@ -2212,6 +2314,10 @@ var createIndexedDBUsageTrackingDriver = (options = {}) => {
2212
2314
  const results = await this.list(options2);
2213
2315
  return results.length;
2214
2316
  },
2317
+ async aggregate(options2 = {}) {
2318
+ const entries = await this.list(options2);
2319
+ return reduceAggregate(entries, options2);
2320
+ },
2215
2321
  async deleteOlderThan(timestamp) {
2216
2322
  await ensureMigrated();
2217
2323
  const db = await getDb();
@@ -2269,6 +2375,9 @@ var matchesFilters2 = (entry, options = {}) => {
2269
2375
  if (options.client && entry.client !== options.client) {
2270
2376
  return false;
2271
2377
  }
2378
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
2379
+ return false;
2380
+ }
2272
2381
  if (options.provider && entry.provider !== options.provider) {
2273
2382
  return false;
2274
2383
  }
@@ -2301,6 +2410,10 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
2301
2410
  async count(options = {}) {
2302
2411
  return (await this.list(options)).length;
2303
2412
  },
2413
+ async aggregate(options = {}) {
2414
+ const entries = [...store.values()].filter((entry) => matchesFilters2(entry, options));
2415
+ return reduceAggregate(entries, options);
2416
+ },
2304
2417
  async deleteOlderThan(timestamp) {
2305
2418
  let deleted = 0;
2306
2419
  for (const [id, entry] of store.entries()) {
@@ -2923,13 +3036,14 @@ function hasUsageChanged(previous, next) {
2923
3036
  function isInspectionComplete(responseIdCaptured, usage) {
2924
3037
  return responseIdCaptured && !!usage && usage.totalTokens > 0 && typeof usage.totalMsats === "number" && !!usage.provider;
2925
3038
  }
2926
- async function inspectSSEWebStream(stream, onUsage, onResponseId) {
3039
+ async function inspectSSEWebStream(stream, onUsage, onResponseId, options) {
2927
3040
  const reader = stream.getReader();
2928
3041
  const decoder = new TextDecoder("utf-8");
2929
3042
  let buffer = "";
2930
3043
  let capturedUsage = null;
2931
3044
  let capturedResponseId;
2932
3045
  let responseIdCaptured = false;
3046
+ let rawChunkSequence = 0;
2933
3047
  const inspectDataPayload = (jsonText) => {
2934
3048
  const trimmed = jsonText.trim();
2935
3049
  if (!trimmed || trimmed === "[DONE]") {
@@ -3000,7 +3114,9 @@ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
3000
3114
  const { value, done } = await reader.read();
3001
3115
  if (done) break;
3002
3116
  if (value && value.byteLength > 0) {
3003
- buffer += decoder.decode(value, { stream: true });
3117
+ const text = decoder.decode(value, { stream: true });
3118
+ void options?.onRawChunk?.(value, rawChunkSequence++, text);
3119
+ buffer += text;
3004
3120
  drainBufferedEvents();
3005
3121
  }
3006
3122
  }
@@ -3145,6 +3261,7 @@ var RoutstrClient = class {
3145
3261
  this.mode = mode;
3146
3262
  this.usageTrackingDriver = options.usageTrackingDriver;
3147
3263
  this.sdkStore = options.sdkStore;
3264
+ this.requestResponseLogSink = options.requestResponseLogSink;
3148
3265
  this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore, this.logger);
3149
3266
  }
3150
3267
  walletAdapter;
@@ -3159,6 +3276,7 @@ var RoutstrClient = class {
3159
3276
  usageTrackingDriver;
3160
3277
  sdkStore;
3161
3278
  logger;
3279
+ requestResponseLogSink;
3162
3280
  /**
3163
3281
  * Get the current client mode
3164
3282
  */
@@ -3349,6 +3467,7 @@ var RoutstrClient = class {
3349
3467
  let usagePromise = Promise.resolve({});
3350
3468
  if (contentType.includes("text/event-stream") && response.body) {
3351
3469
  const [clientStream, inspectStream] = response.body.tee();
3470
+ const requestResponseLogId = response.requestResponseLogId;
3352
3471
  processedResponse = new Response(clientStream, {
3353
3472
  status: response.status,
3354
3473
  statusText: response.statusText,
@@ -3356,6 +3475,7 @@ var RoutstrClient = class {
3356
3475
  });
3357
3476
  processedResponse.baseUrl = response.baseUrl;
3358
3477
  processedResponse.token = response.token;
3478
+ processedResponse.requestResponseLogId = requestResponseLogId;
3359
3479
  usagePromise = inspectSSEWebStream(
3360
3480
  inspectStream,
3361
3481
  (usage) => {
@@ -3365,8 +3485,23 @@ var RoutstrClient = class {
3365
3485
  (responseId) => {
3366
3486
  capturedResponseId = responseId;
3367
3487
  processedResponse.requestId = responseId;
3488
+ },
3489
+ {
3490
+ onRawChunk: (_chunk, sequence, text) => {
3491
+ void this.requestResponseLogSink?.logResponseChunk?.(
3492
+ requestResponseLogId,
3493
+ sequence,
3494
+ text
3495
+ );
3496
+ }
3368
3497
  }
3369
- );
3498
+ ).then(async (result) => {
3499
+ await this.requestResponseLogSink?.logResponseEnd?.(requestResponseLogId);
3500
+ return result;
3501
+ }).catch(async (error) => {
3502
+ await this.requestResponseLogSink?.logResponseError?.(requestResponseLogId, error);
3503
+ throw error;
3504
+ });
3370
3505
  processedResponse.usagePromise = usagePromise;
3371
3506
  }
3372
3507
  return {
@@ -3400,16 +3535,30 @@ var RoutstrClient = class {
3400
3535
  const { path, method, body, baseUrl, token, headers } = params;
3401
3536
  try {
3402
3537
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
3538
+ const requestBodyText = body === void 0 || method === "GET" ? void 0 : JSON.stringify(body);
3539
+ const requestLogId = await this.requestResponseLogSink?.logRequest?.({
3540
+ method,
3541
+ url,
3542
+ path,
3543
+ baseUrl,
3544
+ headers,
3545
+ body,
3546
+ rawBody: requestBodyText
3547
+ });
3403
3548
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
3404
3549
  const response = await fetch(url, {
3405
3550
  method,
3406
3551
  headers,
3407
- body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
3552
+ body: requestBodyText
3408
3553
  });
3409
3554
  if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
3410
3555
  response.baseUrl = baseUrl;
3411
3556
  response.token = token;
3557
+ response.requestResponseLogId = requestLogId;
3558
+ await this.requestResponseLogSink?.logResponseStart?.(requestLogId, response);
3559
+ const contentType = response.headers.get("content-type") || "";
3412
3560
  if (!response.ok) {
3561
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
3413
3562
  const requestId = response.headers.get("x-routstr-request-id") || void 0;
3414
3563
  let bodyText;
3415
3564
  try {
@@ -3427,6 +3576,9 @@ var RoutstrClient = class {
3427
3576
  params.retryCount ?? 0
3428
3577
  );
3429
3578
  }
3579
+ if (!contentType.includes("text/event-stream")) {
3580
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
3581
+ }
3430
3582
  return response;
3431
3583
  } catch (error) {
3432
3584
  if (isNetworkErrorMessage(error?.message || "")) {
@@ -4316,15 +4468,8 @@ async function fetchAIResponse(options, callbacks, deps) {
4316
4468
  const apiMessages = await convertMessages(messageHistory);
4317
4469
  callbacks.onPaymentProcessing?.(true);
4318
4470
  callbacks.onTokenCreated?.(deps.getPendingCashuTokenAmount?.() ?? 0);
4319
- const providerInfo = await deps.providerRegistry.getProviderInfo(baseUrl);
4320
- const providerVersion = providerInfo?.version ?? "";
4321
- let modelIdForRequest = selectedModel.id;
4322
- if (/^0\.1\./.test(providerVersion)) {
4323
- const newModel = await deps.client.getProviderManager().getModelForProvider(baseUrl, selectedModel.id);
4324
- modelIdForRequest = newModel?.id ?? selectedModel.id;
4325
- }
4326
4471
  const body = {
4327
- model: modelIdForRequest,
4472
+ model: selectedModel.id,
4328
4473
  messages: apiMessages,
4329
4474
  stream: true
4330
4475
  };
@@ -4442,7 +4587,11 @@ exports.ProviderManager = ProviderManager;
4442
4587
  exports.RoutstrClient = RoutstrClient;
4443
4588
  exports.StreamProcessor = StreamProcessor;
4444
4589
  exports.createSSEParserTransform = createSSEParserTransform;
4590
+ exports.extractResponseId = extractResponseId;
4591
+ exports.extractUsageFromResponseBody = extractUsageFromResponseBody;
4592
+ exports.extractUsageFromSSEJson = extractUsageFromSSEJson;
4445
4593
  exports.fetchAIResponse = fetchAIResponse;
4446
4594
  exports.inspectSSEWebStream = inspectSSEWebStream;
4595
+ exports.toUsageStats = toUsageStats;
4447
4596
  //# sourceMappingURL=index.js.map
4448
4597
  //# sourceMappingURL=index.js.map