@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
package/dist/node.d.mts CHANGED
@@ -5,10 +5,10 @@ import { ModelManager, ModelManagerConfig } from './discovery/index.mjs';
5
5
  export { MintDiscovery } from './discovery/index.mjs';
6
6
  export { A as ApiKeyEntry, C as ChildKeyEntry, P as ProviderRegistry, R as RoutstrClientOptions, S as StorageAdapter, a as StreamingCallbacks, W as WalletAdapter, X as XCashuTokenEntry } from './interfaces-Csn8Uq04.mjs';
7
7
  export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, SpendOptions, TopUpOptions } from './wallet/index.mjs';
8
- export { AlertLevel, DebugLevel, FetchAIResponseDeps, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientConfig, RoutstrClientMode, StreamCallbacks, StreamProcessor, createSSEParserTransform, fetchAIResponse, inspectSSEWebStream } from './client/index.mjs';
8
+ export { AlertLevel, DebugLevel, FetchAIResponseDeps, ModelProviderPrice, ProviderManager, RequestResponseLogRequestInput, RequestResponseLogSink, RouteRequestParams, RoutstrClient, RoutstrClientConfig, RoutstrClientMode, StreamCallbacks, StreamProcessor, UsageTrackingData, createSSEParserTransform, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, inspectSSEWebStream, toUsageStats } from './client/index.mjs';
9
9
  export { SDK_STORAGE_KEYS, ShardedDiscoveryAdapterOptions, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromDiscoveryAdapter, createShardedDiscoveryAdapter, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, localStorageDriver, setDefaultUsageTrackingDriver } from './storage/index.mjs';
10
10
  export { SqliteDriverOptions, SqliteUsageTrackingDriverOptions, createSqliteDriver, createSqliteUsageTrackingDriver } from './storage/node.mjs';
11
- export { L as ListUsageTrackingOptions, S as SdkStore, a as StorageDriver, U as UsageTrackingDriver, b as UsageTrackingEntry, c as createDiscoveryAdapterFromStore, d as createProviderRegistryFromStore, e as createSdkStore, f as createStorageAdapterFromStore } from './store-BiuM2V9N.mjs';
11
+ export { A as AggregateUsageOptions, L as ListUsageTrackingOptions, S as SdkStore, a as StorageDriver, U as UsageAggregateRow, b as UsageGroupBy, c as UsageTrackingDriver, d as UsageTrackingEntry, e as createDiscoveryAdapterFromStore, f as createProviderRegistryFromStore, g as createSdkStore, h as createStorageAdapterFromStore } from './store-CuXwe5Rg.mjs';
12
12
  import 'applesauce-core';
13
13
  import 'stream';
14
14
  import 'zustand/vanilla';
package/dist/node.d.ts CHANGED
@@ -5,10 +5,10 @@ import { ModelManager, ModelManagerConfig } from './discovery/index.js';
5
5
  export { MintDiscovery } from './discovery/index.js';
6
6
  export { A as ApiKeyEntry, C as ChildKeyEntry, P as ProviderRegistry, R as RoutstrClientOptions, S as StorageAdapter, a as StreamingCallbacks, W as WalletAdapter, X as XCashuTokenEntry } from './interfaces-C-DYd9Jy.js';
7
7
  export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, SpendOptions, TopUpOptions } from './wallet/index.js';
8
- export { AlertLevel, DebugLevel, FetchAIResponseDeps, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientConfig, RoutstrClientMode, StreamCallbacks, StreamProcessor, createSSEParserTransform, fetchAIResponse, inspectSSEWebStream } from './client/index.js';
8
+ export { AlertLevel, DebugLevel, FetchAIResponseDeps, ModelProviderPrice, ProviderManager, RequestResponseLogRequestInput, RequestResponseLogSink, RouteRequestParams, RoutstrClient, RoutstrClientConfig, RoutstrClientMode, StreamCallbacks, StreamProcessor, UsageTrackingData, createSSEParserTransform, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, inspectSSEWebStream, toUsageStats } from './client/index.js';
9
9
  export { SDK_STORAGE_KEYS, ShardedDiscoveryAdapterOptions, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromDiscoveryAdapter, createShardedDiscoveryAdapter, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, localStorageDriver, setDefaultUsageTrackingDriver } from './storage/index.js';
10
10
  export { SqliteDriverOptions, SqliteUsageTrackingDriverOptions, createSqliteDriver, createSqliteUsageTrackingDriver } from './storage/node.js';
11
- export { L as ListUsageTrackingOptions, S as SdkStore, a as StorageDriver, U as UsageTrackingDriver, b as UsageTrackingEntry, c as createDiscoveryAdapterFromStore, d as createProviderRegistryFromStore, e as createSdkStore, f as createStorageAdapterFromStore } from './store-C8MZlfuz.js';
11
+ export { A as AggregateUsageOptions, L as ListUsageTrackingOptions, S as SdkStore, a as StorageDriver, U as UsageAggregateRow, b as UsageGroupBy, c as UsageTrackingDriver, d as UsageTrackingEntry, e as createDiscoveryAdapterFromStore, f as createProviderRegistryFromStore, g as createSdkStore, h as createStorageAdapterFromStore } from './store-CAQLSbEj.js';
12
12
  import 'applesauce-core';
13
13
  import 'stream';
14
14
  import 'zustand/vanilla';
package/dist/node.js CHANGED
@@ -139,6 +139,11 @@ var MintDiscoveryError = class extends Error {
139
139
  }
140
140
  baseUrl;
141
141
  };
142
+ var DEFAULT_NOSTR_RELAYS = [
143
+ "wss://relay.damus.io",
144
+ "wss://nos.lol",
145
+ "wss://relay.routstr.com"
146
+ ];
142
147
  var ModelManager = class _ModelManager {
143
148
  constructor(adapter, config = {}) {
144
149
  this.adapter = adapter;
@@ -332,11 +337,11 @@ var ModelManager = class _ModelManager {
332
337
  return this.bootstrapFromHttp(torMode, forceRefresh);
333
338
  }
334
339
  /**
335
- * Resolve Nostr relay URLs for a given use case.
336
- * Returns user-configured relays if set, otherwise the provided defaults.
340
+ * Resolve Nostr relay URLs.
341
+ * Returns user-configured relays if set, otherwise the shared defaults.
337
342
  */
338
- getNostrRelays(defaults) {
339
- return this.nostrRelays && this.nostrRelays.length > 0 ? this.nostrRelays : defaults;
343
+ getNostrRelays() {
344
+ return this.nostrRelays && this.nostrRelays.length > 0 ? this.nostrRelays : DEFAULT_NOSTR_RELAYS;
340
345
  }
341
346
  /**
342
347
  * Bootstrap providers from Nostr network (kind 38421)
@@ -345,11 +350,7 @@ var ModelManager = class _ModelManager {
345
350
  * @returns Array of provider base URLs
346
351
  */
347
352
  async bootstrapFromNostr(kind, torMode, forceRefresh = false) {
348
- const relays = this.getNostrRelays([
349
- "wss://relay.primal.net",
350
- "wss://nos.lol",
351
- "wss://relay.damus.io"
352
- ]);
353
+ const relays = this.getNostrRelays();
353
354
  const cached = await this.getCachedNostrEvents(
354
355
  { kinds: [kind] },
355
356
  this.cacheTTL,
@@ -532,12 +533,7 @@ var ModelManager = class _ModelManager {
532
533
  );
533
534
  let sessionEvents = cached;
534
535
  if (cached.length === 0) {
535
- const lgtmRelays = this.getNostrRelays([
536
- "wss://relay.primal.net",
537
- "wss://nos.lol",
538
- "wss://relay.damus.io",
539
- "wss://relay.routstr.com"
540
- ]);
536
+ const lgtmRelays = this.getNostrRelays();
541
537
  const pool = new applesauceRelay.RelayPool();
542
538
  const timeoutMs = 5e3;
543
539
  await new Promise((resolve) => {
@@ -783,11 +779,7 @@ var ModelManager = class _ModelManager {
783
779
  return cachedModels;
784
780
  }
785
781
  }
786
- const relays = this.getNostrRelays([
787
- "wss://relay.damus.io",
788
- "wss://nos.lol",
789
- "wss://relay.routstr.com"
790
- ]);
782
+ const relays = this.getNostrRelays();
791
783
  const cached = await this.getCachedNostrEvents(
792
784
  { kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrPubkey] },
793
785
  this.cacheTTL,
@@ -1999,8 +1991,8 @@ var BalanceManager = class _BalanceManager {
1999
1991
  const refundableProviderBalance = Object.entries(
2000
1992
  balanceState.providerBalances
2001
1993
  ).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
2002
- if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 2) {
2003
- await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount);
1994
+ if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 3) {
1995
+ await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, adjustedAmount);
2004
1996
  return this.createProviderToken({
2005
1997
  ...options,
2006
1998
  retryCount: retryCount + 1
@@ -2139,33 +2131,47 @@ var BalanceManager = class _BalanceManager {
2139
2131
  }
2140
2132
  return candidates;
2141
2133
  }
2142
- async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
2134
+ async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, requiredAmount) {
2143
2135
  const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
2144
2136
  const forceRefund = retryCount >= 2;
2145
- const apiKeysToRefund = apiKeyDistribution.filter(
2146
- (apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
2147
- );
2148
- const apiKeyRefundResults = await Promise.allSettled(
2149
- apiKeysToRefund.map(async (apiKeyEntry) => {
2150
- const fullApiKeyEntry = this.storageAdapter.getApiKey(
2151
- apiKeyEntry.baseUrl
2152
- );
2153
- if (!fullApiKeyEntry) {
2154
- return { baseUrl: apiKeyEntry.baseUrl, success: false };
2155
- }
2156
- const result = await this.refundApiKey({
2137
+ const candidates = apiKeyDistribution.filter((apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0).map((apiKey) => {
2138
+ const full = this.storageAdapter.getApiKey(apiKey.baseUrl);
2139
+ return {
2140
+ baseUrl: apiKey.baseUrl,
2141
+ amount: apiKey.amount,
2142
+ lastUsed: full?.lastUsed ?? 0,
2143
+ key: full?.key
2144
+ };
2145
+ }).filter((c) => c.key != null).sort((a, b) => a.lastUsed - b.lastUsed);
2146
+ if (candidates.length === 0) return;
2147
+ if (forceRefund) {
2148
+ for (const candidate of candidates) {
2149
+ await this.refundApiKey({
2157
2150
  mintUrl,
2158
- baseUrl: apiKeyEntry.baseUrl,
2159
- apiKey: fullApiKeyEntry.key,
2160
- forceRefund
2151
+ baseUrl: candidate.baseUrl,
2152
+ apiKey: candidate.key,
2153
+ forceRefund: true
2161
2154
  });
2162
- return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
2163
- })
2164
- );
2165
- for (const result of apiKeyRefundResults) {
2166
- if (result.status === "fulfilled" && result.value.success) {
2167
- this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
2155
+ const newState = await this.getBalanceState();
2156
+ const newAvailable = (newState.mintBalances[mintUrl] || 0) + (newState.providerBalances[baseUrl] || 0);
2157
+ if (newAvailable >= requiredAmount) {
2158
+ this.logger.log(
2159
+ `_refundOtherProvidersForTopUp: freed enough balance (${newAvailable} >= ${requiredAmount}), stopping early`
2160
+ );
2161
+ return;
2162
+ }
2168
2163
  }
2164
+ } else {
2165
+ await Promise.allSettled(
2166
+ candidates.map(
2167
+ (candidate) => this.refundApiKey({
2168
+ mintUrl,
2169
+ baseUrl: candidate.baseUrl,
2170
+ apiKey: candidate.key,
2171
+ forceRefund: false
2172
+ })
2173
+ )
2174
+ );
2169
2175
  }
2170
2176
  }
2171
2177
  /**
@@ -3162,6 +3168,149 @@ var SDK_STORAGE_KEYS = {
3162
3168
  PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
3163
3169
  };
3164
3170
 
3171
+ // storage/usageTracking/aggregate.ts
3172
+ var pad2 = (n) => String(n).padStart(2, "0");
3173
+ var aggregateColumns = "COUNT(*) AS requests, COALESCE(SUM(prompt_tokens), 0) AS promptTokens, COALESCE(SUM(completion_tokens), 0) AS completionTokens, COALESCE(SUM(total_tokens), 0) AS totalTokens, COALESCE(SUM(cost), 0) AS cost, COALESCE(SUM(sats_cost), 0) AS satsCost, COALESCE(SUM(base_msats), 0) AS baseMsats, COALESCE(SUM(input_msats), 0) AS inputMsats, COALESCE(SUM(output_msats), 0) AS outputMsats, COALESCE(SUM(total_msats), 0) AS totalMsats, COALESCE(SUM(total_usd), 0) AS totalUsd, COALESCE(SUM(cache_read_input_tokens), 0) AS cacheReadInputTokens, COALESCE(SUM(cache_creation_input_tokens), 0) AS cacheCreationInputTokens, COALESCE(SUM(cache_read_msats), 0) AS cacheReadMsats, COALESCE(SUM(cache_creation_msats), 0) AS cacheCreationMsats";
3174
+ var sqlGroupExpr = (groupBy) => {
3175
+ switch (groupBy) {
3176
+ case "modelId":
3177
+ return { expr: "model_id", usesTz: false };
3178
+ case "baseUrl":
3179
+ return { expr: "base_url", usesTz: false };
3180
+ case "client":
3181
+ return { expr: "client", usesTz: false };
3182
+ case "sessionId":
3183
+ return { expr: "session_id", usesTz: false };
3184
+ case "provider":
3185
+ return { expr: "provider", usesTz: false };
3186
+ case "day":
3187
+ return {
3188
+ expr: "strftime('%Y-%m-%d', (timestamp - ? * 60000) / 1000, 'unixepoch')",
3189
+ usesTz: true
3190
+ };
3191
+ case "hour":
3192
+ return {
3193
+ expr: "strftime('%H', (timestamp - ? * 60000) / 1000, 'unixepoch')",
3194
+ usesTz: true
3195
+ };
3196
+ }
3197
+ };
3198
+ var buildAggregateSql = (tableName, where, options = {}) => {
3199
+ if (!options.groupBy) {
3200
+ return {
3201
+ sql: `SELECT NULL AS grp, ${aggregateColumns} FROM ${tableName} ${where.sql}`,
3202
+ params: where.params
3203
+ };
3204
+ }
3205
+ const { expr, usesTz } = sqlGroupExpr(options.groupBy);
3206
+ const tzParams = usesTz ? [options.tzOffsetMinutes ?? 0] : [];
3207
+ const orderBy = options.groupBy === "day" || options.groupBy === "hour" ? "ORDER BY grp ASC" : "ORDER BY satsCost DESC";
3208
+ return {
3209
+ sql: `SELECT ${expr} AS grp, ${aggregateColumns} FROM ${tableName} ${where.sql} GROUP BY grp ${orderBy}`,
3210
+ params: [...tzParams, ...where.params]
3211
+ };
3212
+ };
3213
+ var mapAggregateRow = (row) => ({
3214
+ group: row.grp == null ? null : String(row.grp),
3215
+ requests: Number(row.requests ?? 0),
3216
+ promptTokens: Number(row.promptTokens ?? 0),
3217
+ completionTokens: Number(row.completionTokens ?? 0),
3218
+ totalTokens: Number(row.totalTokens ?? 0),
3219
+ cost: Number(row.cost ?? 0),
3220
+ satsCost: Number(row.satsCost ?? 0),
3221
+ baseMsats: Number(row.baseMsats ?? 0),
3222
+ inputMsats: Number(row.inputMsats ?? 0),
3223
+ outputMsats: Number(row.outputMsats ?? 0),
3224
+ totalMsats: Number(row.totalMsats ?? 0),
3225
+ totalUsd: Number(row.totalUsd ?? 0),
3226
+ cacheReadInputTokens: Number(row.cacheReadInputTokens ?? 0),
3227
+ cacheCreationInputTokens: Number(row.cacheCreationInputTokens ?? 0),
3228
+ cacheReadMsats: Number(row.cacheReadMsats ?? 0),
3229
+ cacheCreationMsats: Number(row.cacheCreationMsats ?? 0)
3230
+ });
3231
+ var jsGroupKey = (entry, groupBy, tzOffsetMinutes) => {
3232
+ switch (groupBy) {
3233
+ case "modelId":
3234
+ return entry.modelId ?? null;
3235
+ case "baseUrl":
3236
+ return entry.baseUrl ?? null;
3237
+ case "client":
3238
+ return entry.client ?? null;
3239
+ case "sessionId":
3240
+ return entry.sessionId ?? null;
3241
+ case "provider":
3242
+ return entry.provider ?? null;
3243
+ case "day": {
3244
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
3245
+ return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
3246
+ }
3247
+ case "hour": {
3248
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
3249
+ return pad2(d.getUTCHours());
3250
+ }
3251
+ }
3252
+ };
3253
+ var reduceAggregate = (entries, options = {}) => {
3254
+ const emptyRow = (group) => ({
3255
+ group,
3256
+ requests: 0,
3257
+ promptTokens: 0,
3258
+ completionTokens: 0,
3259
+ totalTokens: 0,
3260
+ cost: 0,
3261
+ satsCost: 0,
3262
+ baseMsats: 0,
3263
+ inputMsats: 0,
3264
+ outputMsats: 0,
3265
+ totalMsats: 0,
3266
+ totalUsd: 0,
3267
+ cacheReadInputTokens: 0,
3268
+ cacheCreationInputTokens: 0,
3269
+ cacheReadMsats: 0,
3270
+ cacheCreationMsats: 0
3271
+ });
3272
+ const accumulate = (row, entry) => {
3273
+ row.requests += 1;
3274
+ row.promptTokens += entry.promptTokens;
3275
+ row.completionTokens += entry.completionTokens;
3276
+ row.totalTokens += entry.totalTokens;
3277
+ row.cost += entry.cost;
3278
+ row.satsCost += entry.satsCost;
3279
+ row.baseMsats += entry.baseMsats ?? 0;
3280
+ row.inputMsats += entry.inputMsats ?? 0;
3281
+ row.outputMsats += entry.outputMsats ?? 0;
3282
+ row.totalMsats += entry.totalMsats ?? 0;
3283
+ row.totalUsd += entry.totalUsd ?? 0;
3284
+ row.cacheReadInputTokens += entry.cacheReadInputTokens ?? 0;
3285
+ row.cacheCreationInputTokens += entry.cacheCreationInputTokens ?? 0;
3286
+ row.cacheReadMsats += entry.cacheReadMsats ?? 0;
3287
+ row.cacheCreationMsats += entry.cacheCreationMsats ?? 0;
3288
+ };
3289
+ if (!options.groupBy) {
3290
+ const total = emptyRow(null);
3291
+ for (const entry of entries) accumulate(total, entry);
3292
+ return [total];
3293
+ }
3294
+ const tz = options.tzOffsetMinutes ?? 0;
3295
+ const groups = /* @__PURE__ */ new Map();
3296
+ for (const entry of entries) {
3297
+ const key = jsGroupKey(entry, options.groupBy, tz);
3298
+ let row = groups.get(key);
3299
+ if (!row) {
3300
+ row = emptyRow(key);
3301
+ groups.set(key, row);
3302
+ }
3303
+ accumulate(row, entry);
3304
+ }
3305
+ const rows = [...groups.values()];
3306
+ if (options.groupBy === "day" || options.groupBy === "hour") {
3307
+ rows.sort((a, b) => (a.group ?? "").localeCompare(b.group ?? ""));
3308
+ } else {
3309
+ rows.sort((a, b) => b.satsCost - a.satsCost);
3310
+ }
3311
+ return rows;
3312
+ };
3313
+
3165
3314
  // storage/usageTracking/indexedDB.ts
3166
3315
  var DEFAULT_DB_NAME = "routstr-sdk";
3167
3316
  var DEFAULT_STORE_NAME = "usage_tracking";
@@ -3224,6 +3373,9 @@ var matchesFilters = (entry, options = {}) => {
3224
3373
  if (options.client && entry.client !== options.client) {
3225
3374
  return false;
3226
3375
  }
3376
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
3377
+ return false;
3378
+ }
3227
3379
  if (options.provider && entry.provider !== options.provider) {
3228
3380
  return false;
3229
3381
  }
@@ -3322,6 +3474,10 @@ var createIndexedDBUsageTrackingDriver = (options = {}) => {
3322
3474
  const results = await this.list(options2);
3323
3475
  return results.length;
3324
3476
  },
3477
+ async aggregate(options2 = {}) {
3478
+ const entries = await this.list(options2);
3479
+ return reduceAggregate(entries, options2);
3480
+ },
3325
3481
  async deleteOlderThan(timestamp) {
3326
3482
  await ensureMigrated();
3327
3483
  const db = await getDb();
@@ -3379,6 +3535,9 @@ var matchesFilters2 = (entry, options = {}) => {
3379
3535
  if (options.client && entry.client !== options.client) {
3380
3536
  return false;
3381
3537
  }
3538
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
3539
+ return false;
3540
+ }
3382
3541
  if (options.provider && entry.provider !== options.provider) {
3383
3542
  return false;
3384
3543
  }
@@ -3411,6 +3570,10 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
3411
3570
  async count(options = {}) {
3412
3571
  return (await this.list(options)).length;
3413
3572
  },
3573
+ async aggregate(options = {}) {
3574
+ const entries = [...store.values()].filter((entry) => matchesFilters2(entry, options));
3575
+ return reduceAggregate(entries, options);
3576
+ },
3414
3577
  async deleteOlderThan(timestamp) {
3415
3578
  let deleted = 0;
3416
3579
  for (const [id, entry] of store.entries()) {
@@ -4562,13 +4725,14 @@ function hasUsageChanged(previous, next) {
4562
4725
  function isInspectionComplete(responseIdCaptured, usage) {
4563
4726
  return responseIdCaptured && !!usage && usage.totalTokens > 0 && typeof usage.totalMsats === "number" && !!usage.provider;
4564
4727
  }
4565
- async function inspectSSEWebStream(stream, onUsage, onResponseId) {
4728
+ async function inspectSSEWebStream(stream, onUsage, onResponseId, options) {
4566
4729
  const reader = stream.getReader();
4567
4730
  const decoder = new TextDecoder("utf-8");
4568
4731
  let buffer = "";
4569
4732
  let capturedUsage = null;
4570
4733
  let capturedResponseId;
4571
4734
  let responseIdCaptured = false;
4735
+ let rawChunkSequence = 0;
4572
4736
  const inspectDataPayload = (jsonText) => {
4573
4737
  const trimmed = jsonText.trim();
4574
4738
  if (!trimmed || trimmed === "[DONE]") {
@@ -4639,7 +4803,9 @@ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
4639
4803
  const { value, done } = await reader.read();
4640
4804
  if (done) break;
4641
4805
  if (value && value.byteLength > 0) {
4642
- buffer += decoder.decode(value, { stream: true });
4806
+ const text = decoder.decode(value, { stream: true });
4807
+ void options?.onRawChunk?.(value, rawChunkSequence++, text);
4808
+ buffer += text;
4643
4809
  drainBufferedEvents();
4644
4810
  }
4645
4811
  }
@@ -4784,6 +4950,7 @@ var RoutstrClient = class {
4784
4950
  this.mode = mode;
4785
4951
  this.usageTrackingDriver = options.usageTrackingDriver;
4786
4952
  this.sdkStore = options.sdkStore;
4953
+ this.requestResponseLogSink = options.requestResponseLogSink;
4787
4954
  this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore, this.logger);
4788
4955
  }
4789
4956
  walletAdapter;
@@ -4798,6 +4965,7 @@ var RoutstrClient = class {
4798
4965
  usageTrackingDriver;
4799
4966
  sdkStore;
4800
4967
  logger;
4968
+ requestResponseLogSink;
4801
4969
  /**
4802
4970
  * Get the current client mode
4803
4971
  */
@@ -4988,6 +5156,7 @@ var RoutstrClient = class {
4988
5156
  let usagePromise = Promise.resolve({});
4989
5157
  if (contentType.includes("text/event-stream") && response.body) {
4990
5158
  const [clientStream, inspectStream] = response.body.tee();
5159
+ const requestResponseLogId = response.requestResponseLogId;
4991
5160
  processedResponse = new Response(clientStream, {
4992
5161
  status: response.status,
4993
5162
  statusText: response.statusText,
@@ -4995,6 +5164,7 @@ var RoutstrClient = class {
4995
5164
  });
4996
5165
  processedResponse.baseUrl = response.baseUrl;
4997
5166
  processedResponse.token = response.token;
5167
+ processedResponse.requestResponseLogId = requestResponseLogId;
4998
5168
  usagePromise = inspectSSEWebStream(
4999
5169
  inspectStream,
5000
5170
  (usage) => {
@@ -5004,8 +5174,23 @@ var RoutstrClient = class {
5004
5174
  (responseId) => {
5005
5175
  capturedResponseId = responseId;
5006
5176
  processedResponse.requestId = responseId;
5177
+ },
5178
+ {
5179
+ onRawChunk: (_chunk, sequence, text) => {
5180
+ void this.requestResponseLogSink?.logResponseChunk?.(
5181
+ requestResponseLogId,
5182
+ sequence,
5183
+ text
5184
+ );
5185
+ }
5007
5186
  }
5008
- );
5187
+ ).then(async (result) => {
5188
+ await this.requestResponseLogSink?.logResponseEnd?.(requestResponseLogId);
5189
+ return result;
5190
+ }).catch(async (error) => {
5191
+ await this.requestResponseLogSink?.logResponseError?.(requestResponseLogId, error);
5192
+ throw error;
5193
+ });
5009
5194
  processedResponse.usagePromise = usagePromise;
5010
5195
  }
5011
5196
  return {
@@ -5039,16 +5224,30 @@ var RoutstrClient = class {
5039
5224
  const { path, method, body, baseUrl, token, headers } = params;
5040
5225
  try {
5041
5226
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
5227
+ const requestBodyText = body === void 0 || method === "GET" ? void 0 : JSON.stringify(body);
5228
+ const requestLogId = await this.requestResponseLogSink?.logRequest?.({
5229
+ method,
5230
+ url,
5231
+ path,
5232
+ baseUrl,
5233
+ headers,
5234
+ body,
5235
+ rawBody: requestBodyText
5236
+ });
5042
5237
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
5043
5238
  const response = await fetch(url, {
5044
5239
  method,
5045
5240
  headers,
5046
- body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
5241
+ body: requestBodyText
5047
5242
  });
5048
5243
  if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
5049
5244
  response.baseUrl = baseUrl;
5050
5245
  response.token = token;
5246
+ response.requestResponseLogId = requestLogId;
5247
+ await this.requestResponseLogSink?.logResponseStart?.(requestLogId, response);
5248
+ const contentType = response.headers.get("content-type") || "";
5051
5249
  if (!response.ok) {
5250
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
5052
5251
  const requestId = response.headers.get("x-routstr-request-id") || void 0;
5053
5252
  let bodyText;
5054
5253
  try {
@@ -5066,6 +5265,9 @@ var RoutstrClient = class {
5066
5265
  params.retryCount ?? 0
5067
5266
  );
5068
5267
  }
5268
+ if (!contentType.includes("text/event-stream")) {
5269
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
5270
+ }
5069
5271
  return response;
5070
5272
  } catch (error) {
5071
5273
  if (isNetworkErrorMessage(error?.message || "")) {
@@ -5955,15 +6157,8 @@ async function fetchAIResponse(options, callbacks, deps) {
5955
6157
  const apiMessages = await convertMessages(messageHistory);
5956
6158
  callbacks.onPaymentProcessing?.(true);
5957
6159
  callbacks.onTokenCreated?.(deps.getPendingCashuTokenAmount?.() ?? 0);
5958
- const providerInfo = await deps.providerRegistry.getProviderInfo(baseUrl);
5959
- const providerVersion = providerInfo?.version ?? "";
5960
- let modelIdForRequest = selectedModel.id;
5961
- if (/^0\.1\./.test(providerVersion)) {
5962
- const newModel = await deps.client.getProviderManager().getModelForProvider(baseUrl, selectedModel.id);
5963
- modelIdForRequest = newModel?.id ?? selectedModel.id;
5964
- }
5965
6160
  const body = {
5966
- model: modelIdForRequest,
6161
+ model: selectedModel.id,
5967
6162
  messages: apiMessages,
5968
6163
  stream: true
5969
6164
  };
@@ -6098,7 +6293,8 @@ async function resolveRouteRequestContext(options) {
6098
6293
  usageTrackingDriver,
6099
6294
  sdkStore,
6100
6295
  providerManager: providedProviderManager,
6101
- logger
6296
+ logger,
6297
+ requestResponseLogSink
6102
6298
  } = options;
6103
6299
  let modelManager;
6104
6300
  let providers;
@@ -6147,15 +6343,8 @@ async function resolveRouteRequestContext(options) {
6147
6343
  baseUrl = cheapest.baseUrl;
6148
6344
  selectedModel = cheapest.model;
6149
6345
  }
6150
- const balances = await walletAdapter.getBalances();
6151
- const totalBalance = Object.values(balances).reduce((sum, v) => sum + v, 0);
6152
- if (totalBalance <= 0) {
6153
- throw new Error(
6154
- "Wallet balance is empty. Add a mint and fund it before making requests."
6155
- );
6156
- }
6157
6346
  const providerMints = providerRegistry.getProviderMints(baseUrl);
6158
- const mintUrl = walletAdapter.getActiveMintUrl() || providerMints[0] || Object.keys(balances)[0];
6347
+ const mintUrl = walletAdapter.getActiveMintUrl() || providerMints[0] || Object.keys(await walletAdapter.getBalances())[0];
6159
6348
  if (!mintUrl) {
6160
6349
  throw new Error("No mint configured in wallet");
6161
6350
  }
@@ -6165,7 +6354,7 @@ async function resolveRouteRequestContext(options) {
6165
6354
  providerRegistry,
6166
6355
  "min",
6167
6356
  mode,
6168
- { usageTrackingDriver, sdkStore, providerManager, logger }
6357
+ { usageTrackingDriver, sdkStore, providerManager, logger, requestResponseLogSink }
6169
6358
  );
6170
6359
  const maxTokens = extractMaxTokens(requestBody);
6171
6360
  const stream = extractStream(requestBody);
@@ -6259,6 +6448,8 @@ var createSqliteDriver = (options = {}) => {
6259
6448
  const initDb = async () => {
6260
6449
  if (!db) {
6261
6450
  db = await loadDatabase(dbPath);
6451
+ db.exec("PRAGMA journal_mode = WAL");
6452
+ db.exec("PRAGMA busy_timeout = 5000");
6262
6453
  db.exec(
6263
6454
  `CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
6264
6455
  );
@@ -6377,6 +6568,11 @@ var buildWhereClause = (options = {}) => {
6377
6568
  clauses.push("client = ?");
6378
6569
  params.push(options.client);
6379
6570
  }
6571
+ if (options.clients && options.clients.length > 0) {
6572
+ const placeholders = options.clients.map(() => "?").join(", ");
6573
+ clauses.push(`client IN (${placeholders})`);
6574
+ params.push(...options.clients);
6575
+ }
6380
6576
  if (options.provider) {
6381
6577
  clauses.push("provider = ?");
6382
6578
  params.push(options.provider);
@@ -6396,6 +6592,8 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
6396
6592
  const initDb = async () => {
6397
6593
  if (!db) {
6398
6594
  db = await loadDatabase2(dbPath);
6595
+ db.exec("PRAGMA journal_mode = WAL");
6596
+ db.exec("PRAGMA busy_timeout = 5000");
6399
6597
  db.exec(`
6400
6598
  CREATE TABLE IF NOT EXISTS ${tableName} (
6401
6599
  id TEXT PRIMARY KEY,
@@ -6566,6 +6764,14 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
6566
6764
  const row = stmt.get(...params);
6567
6765
  return Number(row?.count ?? 0);
6568
6766
  },
6767
+ async aggregate(options2 = {}) {
6768
+ await ensureInit();
6769
+ await ensureMigrated();
6770
+ const where = buildWhereClause(options2);
6771
+ const { sql, params } = buildAggregateSql(tableName, where, options2);
6772
+ const rows = db.prepare(sql).all(...params);
6773
+ return rows.map(mapAggregateRow);
6774
+ },
6569
6775
  async deleteOlderThan(timestamp) {
6570
6776
  await ensureInit();
6571
6777
  await ensureMigrated();
@@ -6630,6 +6836,9 @@ exports.createShardedDiscoveryAdapter = createShardedDiscoveryAdapter;
6630
6836
  exports.createSqliteDriver = createSqliteDriver;
6631
6837
  exports.createSqliteUsageTrackingDriver = createSqliteUsageTrackingDriver;
6632
6838
  exports.createStorageAdapterFromStore = createStorageAdapterFromStore;
6839
+ exports.extractResponseId = extractResponseId;
6840
+ exports.extractUsageFromResponseBody = extractUsageFromResponseBody;
6841
+ exports.extractUsageFromSSEJson = extractUsageFromSSEJson;
6633
6842
  exports.fetchAIResponse = fetchAIResponse;
6634
6843
  exports.filterBaseUrlsForTor = filterBaseUrlsForTor;
6635
6844
  exports.getDefaultDiscoveryAdapter = getDefaultDiscoveryAdapter;
@@ -6647,5 +6856,6 @@ exports.noopLogger = noopLogger;
6647
6856
  exports.normalizeProviderUrl = normalizeProviderUrl;
6648
6857
  exports.routeRequests = routeRequests;
6649
6858
  exports.setDefaultUsageTrackingDriver = setDefaultUsageTrackingDriver;
6859
+ exports.toUsageStats = toUsageStats;
6650
6860
  //# sourceMappingURL=node.js.map
6651
6861
  //# sourceMappingURL=node.js.map