@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
@@ -1059,8 +1059,8 @@ var BalanceManager = class _BalanceManager {
1059
1059
  const refundableProviderBalance = Object.entries(
1060
1060
  balanceState.providerBalances
1061
1061
  ).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
1062
- if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 2) {
1063
- await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount);
1062
+ if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 3) {
1063
+ await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, adjustedAmount);
1064
1064
  return this.createProviderToken({
1065
1065
  ...options,
1066
1066
  retryCount: retryCount + 1
@@ -1199,33 +1199,47 @@ var BalanceManager = class _BalanceManager {
1199
1199
  }
1200
1200
  return candidates;
1201
1201
  }
1202
- async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
1202
+ async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount, requiredAmount) {
1203
1203
  const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
1204
1204
  const forceRefund = retryCount >= 2;
1205
- const apiKeysToRefund = apiKeyDistribution.filter(
1206
- (apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
1207
- );
1208
- const apiKeyRefundResults = await Promise.allSettled(
1209
- apiKeysToRefund.map(async (apiKeyEntry) => {
1210
- const fullApiKeyEntry = this.storageAdapter.getApiKey(
1211
- apiKeyEntry.baseUrl
1212
- );
1213
- if (!fullApiKeyEntry) {
1214
- return { baseUrl: apiKeyEntry.baseUrl, success: false };
1215
- }
1216
- const result = await this.refundApiKey({
1205
+ const candidates = apiKeyDistribution.filter((apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0).map((apiKey) => {
1206
+ const full = this.storageAdapter.getApiKey(apiKey.baseUrl);
1207
+ return {
1208
+ baseUrl: apiKey.baseUrl,
1209
+ amount: apiKey.amount,
1210
+ lastUsed: full?.lastUsed ?? 0,
1211
+ key: full?.key
1212
+ };
1213
+ }).filter((c) => c.key != null).sort((a, b) => a.lastUsed - b.lastUsed);
1214
+ if (candidates.length === 0) return;
1215
+ if (forceRefund) {
1216
+ for (const candidate of candidates) {
1217
+ await this.refundApiKey({
1217
1218
  mintUrl,
1218
- baseUrl: apiKeyEntry.baseUrl,
1219
- apiKey: fullApiKeyEntry.key,
1220
- forceRefund
1219
+ baseUrl: candidate.baseUrl,
1220
+ apiKey: candidate.key,
1221
+ forceRefund: true
1221
1222
  });
1222
- return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
1223
- })
1224
- );
1225
- for (const result of apiKeyRefundResults) {
1226
- if (result.status === "fulfilled" && result.value.success) {
1227
- this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
1223
+ const newState = await this.getBalanceState();
1224
+ const newAvailable = (newState.mintBalances[mintUrl] || 0) + (newState.providerBalances[baseUrl] || 0);
1225
+ if (newAvailable >= requiredAmount) {
1226
+ this.logger.log(
1227
+ `_refundOtherProvidersForTopUp: freed enough balance (${newAvailable} >= ${requiredAmount}), stopping early`
1228
+ );
1229
+ return;
1230
+ }
1228
1231
  }
1232
+ } else {
1233
+ await Promise.allSettled(
1234
+ candidates.map(
1235
+ (candidate) => this.refundApiKey({
1236
+ mintUrl,
1237
+ baseUrl: candidate.baseUrl,
1238
+ apiKey: candidate.key,
1239
+ forceRefund: false
1240
+ })
1241
+ )
1242
+ );
1229
1243
  }
1230
1244
  }
1231
1245
  /**
@@ -2050,6 +2064,91 @@ var SDK_STORAGE_KEYS = {
2050
2064
  PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
2051
2065
  };
2052
2066
 
2067
+ // storage/usageTracking/aggregate.ts
2068
+ var pad2 = (n) => String(n).padStart(2, "0");
2069
+ var jsGroupKey = (entry, groupBy, tzOffsetMinutes) => {
2070
+ switch (groupBy) {
2071
+ case "modelId":
2072
+ return entry.modelId ?? null;
2073
+ case "baseUrl":
2074
+ return entry.baseUrl ?? null;
2075
+ case "client":
2076
+ return entry.client ?? null;
2077
+ case "sessionId":
2078
+ return entry.sessionId ?? null;
2079
+ case "provider":
2080
+ return entry.provider ?? null;
2081
+ case "day": {
2082
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
2083
+ return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
2084
+ }
2085
+ case "hour": {
2086
+ const d = new Date(entry.timestamp - tzOffsetMinutes * 6e4);
2087
+ return pad2(d.getUTCHours());
2088
+ }
2089
+ }
2090
+ };
2091
+ var reduceAggregate = (entries, options = {}) => {
2092
+ const emptyRow = (group) => ({
2093
+ group,
2094
+ requests: 0,
2095
+ promptTokens: 0,
2096
+ completionTokens: 0,
2097
+ totalTokens: 0,
2098
+ cost: 0,
2099
+ satsCost: 0,
2100
+ baseMsats: 0,
2101
+ inputMsats: 0,
2102
+ outputMsats: 0,
2103
+ totalMsats: 0,
2104
+ totalUsd: 0,
2105
+ cacheReadInputTokens: 0,
2106
+ cacheCreationInputTokens: 0,
2107
+ cacheReadMsats: 0,
2108
+ cacheCreationMsats: 0
2109
+ });
2110
+ const accumulate = (row, entry) => {
2111
+ row.requests += 1;
2112
+ row.promptTokens += entry.promptTokens;
2113
+ row.completionTokens += entry.completionTokens;
2114
+ row.totalTokens += entry.totalTokens;
2115
+ row.cost += entry.cost;
2116
+ row.satsCost += entry.satsCost;
2117
+ row.baseMsats += entry.baseMsats ?? 0;
2118
+ row.inputMsats += entry.inputMsats ?? 0;
2119
+ row.outputMsats += entry.outputMsats ?? 0;
2120
+ row.totalMsats += entry.totalMsats ?? 0;
2121
+ row.totalUsd += entry.totalUsd ?? 0;
2122
+ row.cacheReadInputTokens += entry.cacheReadInputTokens ?? 0;
2123
+ row.cacheCreationInputTokens += entry.cacheCreationInputTokens ?? 0;
2124
+ row.cacheReadMsats += entry.cacheReadMsats ?? 0;
2125
+ row.cacheCreationMsats += entry.cacheCreationMsats ?? 0;
2126
+ };
2127
+ if (!options.groupBy) {
2128
+ const total = emptyRow(null);
2129
+ for (const entry of entries) accumulate(total, entry);
2130
+ return [total];
2131
+ }
2132
+ const tz = options.tzOffsetMinutes ?? 0;
2133
+ const groups = /* @__PURE__ */ new Map();
2134
+ for (const entry of entries) {
2135
+ const key = jsGroupKey(entry, options.groupBy, tz);
2136
+ let row = groups.get(key);
2137
+ if (!row) {
2138
+ row = emptyRow(key);
2139
+ groups.set(key, row);
2140
+ }
2141
+ accumulate(row, entry);
2142
+ }
2143
+ const rows = [...groups.values()];
2144
+ if (options.groupBy === "day" || options.groupBy === "hour") {
2145
+ rows.sort((a, b) => (a.group ?? "").localeCompare(b.group ?? ""));
2146
+ } else {
2147
+ rows.sort((a, b) => b.satsCost - a.satsCost);
2148
+ }
2149
+ return rows;
2150
+ };
2151
+
2053
2152
  // storage/usageTracking/indexedDB.ts
2054
2153
  var DEFAULT_DB_NAME = "routstr-sdk";
2055
2154
  var DEFAULT_STORE_NAME = "usage_tracking";
@@ -2112,6 +2211,9 @@ var matchesFilters = (entry, options = {}) => {
2112
2211
  if (options.client && entry.client !== options.client) {
2113
2212
  return false;
2114
2213
  }
2214
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
2215
+ return false;
2216
+ }
2115
2217
  if (options.provider && entry.provider !== options.provider) {
2116
2218
  return false;
2117
2219
  }
@@ -2210,6 +2312,10 @@ var createIndexedDBUsageTrackingDriver = (options = {}) => {
2210
2312
  const results = await this.list(options2);
2211
2313
  return results.length;
2212
2314
  },
2315
+ async aggregate(options2 = {}) {
2316
+ const entries = await this.list(options2);
2317
+ return reduceAggregate(entries, options2);
2318
+ },
2213
2319
  async deleteOlderThan(timestamp) {
2214
2320
  await ensureMigrated();
2215
2321
  const db = await getDb();
@@ -2267,6 +2373,9 @@ var matchesFilters2 = (entry, options = {}) => {
2267
2373
  if (options.client && entry.client !== options.client) {
2268
2374
  return false;
2269
2375
  }
2376
+ if (options.clients && options.clients.length > 0 && (entry.client == null || !options.clients.includes(entry.client))) {
2377
+ return false;
2378
+ }
2270
2379
  if (options.provider && entry.provider !== options.provider) {
2271
2380
  return false;
2272
2381
  }
@@ -2299,6 +2408,10 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
2299
2408
  async count(options = {}) {
2300
2409
  return (await this.list(options)).length;
2301
2410
  },
2411
+ async aggregate(options = {}) {
2412
+ const entries = [...store.values()].filter((entry) => matchesFilters2(entry, options));
2413
+ return reduceAggregate(entries, options);
2414
+ },
2302
2415
  async deleteOlderThan(timestamp) {
2303
2416
  let deleted = 0;
2304
2417
  for (const [id, entry] of store.entries()) {
@@ -2921,13 +3034,14 @@ function hasUsageChanged(previous, next) {
2921
3034
  function isInspectionComplete(responseIdCaptured, usage) {
2922
3035
  return responseIdCaptured && !!usage && usage.totalTokens > 0 && typeof usage.totalMsats === "number" && !!usage.provider;
2923
3036
  }
2924
- async function inspectSSEWebStream(stream, onUsage, onResponseId) {
3037
+ async function inspectSSEWebStream(stream, onUsage, onResponseId, options) {
2925
3038
  const reader = stream.getReader();
2926
3039
  const decoder = new TextDecoder("utf-8");
2927
3040
  let buffer = "";
2928
3041
  let capturedUsage = null;
2929
3042
  let capturedResponseId;
2930
3043
  let responseIdCaptured = false;
3044
+ let rawChunkSequence = 0;
2931
3045
  const inspectDataPayload = (jsonText) => {
2932
3046
  const trimmed = jsonText.trim();
2933
3047
  if (!trimmed || trimmed === "[DONE]") {
@@ -2998,7 +3112,9 @@ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
2998
3112
  const { value, done } = await reader.read();
2999
3113
  if (done) break;
3000
3114
  if (value && value.byteLength > 0) {
3001
- buffer += decoder.decode(value, { stream: true });
3115
+ const text = decoder.decode(value, { stream: true });
3116
+ void options?.onRawChunk?.(value, rawChunkSequence++, text);
3117
+ buffer += text;
3002
3118
  drainBufferedEvents();
3003
3119
  }
3004
3120
  }
@@ -3143,6 +3259,7 @@ var RoutstrClient = class {
3143
3259
  this.mode = mode;
3144
3260
  this.usageTrackingDriver = options.usageTrackingDriver;
3145
3261
  this.sdkStore = options.sdkStore;
3262
+ this.requestResponseLogSink = options.requestResponseLogSink;
3146
3263
  this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore, this.logger);
3147
3264
  }
3148
3265
  walletAdapter;
@@ -3157,6 +3274,7 @@ var RoutstrClient = class {
3157
3274
  usageTrackingDriver;
3158
3275
  sdkStore;
3159
3276
  logger;
3277
+ requestResponseLogSink;
3160
3278
  /**
3161
3279
  * Get the current client mode
3162
3280
  */
@@ -3347,6 +3465,7 @@ var RoutstrClient = class {
3347
3465
  let usagePromise = Promise.resolve({});
3348
3466
  if (contentType.includes("text/event-stream") && response.body) {
3349
3467
  const [clientStream, inspectStream] = response.body.tee();
3468
+ const requestResponseLogId = response.requestResponseLogId;
3350
3469
  processedResponse = new Response(clientStream, {
3351
3470
  status: response.status,
3352
3471
  statusText: response.statusText,
@@ -3354,6 +3473,7 @@ var RoutstrClient = class {
3354
3473
  });
3355
3474
  processedResponse.baseUrl = response.baseUrl;
3356
3475
  processedResponse.token = response.token;
3476
+ processedResponse.requestResponseLogId = requestResponseLogId;
3357
3477
  usagePromise = inspectSSEWebStream(
3358
3478
  inspectStream,
3359
3479
  (usage) => {
@@ -3363,8 +3483,23 @@ var RoutstrClient = class {
3363
3483
  (responseId) => {
3364
3484
  capturedResponseId = responseId;
3365
3485
  processedResponse.requestId = responseId;
3486
+ },
3487
+ {
3488
+ onRawChunk: (_chunk, sequence, text) => {
3489
+ void this.requestResponseLogSink?.logResponseChunk?.(
3490
+ requestResponseLogId,
3491
+ sequence,
3492
+ text
3493
+ );
3494
+ }
3366
3495
  }
3367
- );
3496
+ ).then(async (result) => {
3497
+ await this.requestResponseLogSink?.logResponseEnd?.(requestResponseLogId);
3498
+ return result;
3499
+ }).catch(async (error) => {
3500
+ await this.requestResponseLogSink?.logResponseError?.(requestResponseLogId, error);
3501
+ throw error;
3502
+ });
3368
3503
  processedResponse.usagePromise = usagePromise;
3369
3504
  }
3370
3505
  return {
@@ -3398,16 +3533,30 @@ var RoutstrClient = class {
3398
3533
  const { path, method, body, baseUrl, token, headers } = params;
3399
3534
  try {
3400
3535
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
3536
+ const requestBodyText = body === void 0 || method === "GET" ? void 0 : JSON.stringify(body);
3537
+ const requestLogId = await this.requestResponseLogSink?.logRequest?.({
3538
+ method,
3539
+ url,
3540
+ path,
3541
+ baseUrl,
3542
+ headers,
3543
+ body,
3544
+ rawBody: requestBodyText
3545
+ });
3401
3546
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
3402
3547
  const response = await fetch(url, {
3403
3548
  method,
3404
3549
  headers,
3405
- body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
3550
+ body: requestBodyText
3406
3551
  });
3407
3552
  if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
3408
3553
  response.baseUrl = baseUrl;
3409
3554
  response.token = token;
3555
+ response.requestResponseLogId = requestLogId;
3556
+ await this.requestResponseLogSink?.logResponseStart?.(requestLogId, response);
3557
+ const contentType = response.headers.get("content-type") || "";
3410
3558
  if (!response.ok) {
3559
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
3411
3560
  const requestId = response.headers.get("x-routstr-request-id") || void 0;
3412
3561
  let bodyText;
3413
3562
  try {
@@ -3425,6 +3574,9 @@ var RoutstrClient = class {
3425
3574
  params.retryCount ?? 0
3426
3575
  );
3427
3576
  }
3577
+ if (!contentType.includes("text/event-stream")) {
3578
+ void this.requestResponseLogSink?.logResponseBody?.(requestLogId, response.clone());
3579
+ }
3428
3580
  return response;
3429
3581
  } catch (error) {
3430
3582
  if (isNetworkErrorMessage(error?.message || "")) {
@@ -4314,15 +4466,8 @@ async function fetchAIResponse(options, callbacks, deps) {
4314
4466
  const apiMessages = await convertMessages(messageHistory);
4315
4467
  callbacks.onPaymentProcessing?.(true);
4316
4468
  callbacks.onTokenCreated?.(deps.getPendingCashuTokenAmount?.() ?? 0);
4317
- const providerInfo = await deps.providerRegistry.getProviderInfo(baseUrl);
4318
- const providerVersion = providerInfo?.version ?? "";
4319
- let modelIdForRequest = selectedModel.id;
4320
- if (/^0\.1\./.test(providerVersion)) {
4321
- const newModel = await deps.client.getProviderManager().getModelForProvider(baseUrl, selectedModel.id);
4322
- modelIdForRequest = newModel?.id ?? selectedModel.id;
4323
- }
4324
4469
  const body = {
4325
- model: modelIdForRequest,
4470
+ model: selectedModel.id,
4326
4471
  messages: apiMessages,
4327
4472
  stream: true
4328
4473
  };
@@ -4436,6 +4581,6 @@ function handleError(error, callbacks, alertLevel, logger) {
4436
4581
  }
4437
4582
  }
4438
4583
 
4439
- export { ProviderManager, RoutstrClient, StreamProcessor, createSSEParserTransform, fetchAIResponse, inspectSSEWebStream };
4584
+ export { ProviderManager, RoutstrClient, StreamProcessor, createSSEParserTransform, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, inspectSSEWebStream, toUsageStats };
4440
4585
  //# sourceMappingURL=index.mjs.map
4441
4586
  //# sourceMappingURL=index.mjs.map