@routstr/sdk 0.2.8 → 0.2.11

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.
@@ -1293,17 +1293,48 @@ function extractResponseId(body) {
1293
1293
  return trimmed.length > 0 ? trimmed : void 0;
1294
1294
  }
1295
1295
  function extractUsageFromSSEJson(parsed, fallbackSatsCost = 0) {
1296
- if (!parsed || typeof parsed !== "object" || !parsed.usage) {
1296
+ if (!parsed || typeof parsed !== "object") {
1297
+ return null;
1298
+ }
1299
+ if (!parsed.usage && parsed.cost && typeof parsed.cost === "object") {
1300
+ const costObj = parsed.cost;
1301
+ const msats2 = costObj.total_msats ?? 0;
1302
+ const cost2 = costObj.total_usd ?? 0;
1303
+ if (msats2 === 0 && cost2 === 0) return null;
1304
+ return {
1305
+ promptTokens: Number(costObj.input_tokens ?? 0),
1306
+ completionTokens: Number(costObj.output_tokens ?? 0),
1307
+ totalTokens: Number((costObj.input_tokens ?? 0) + (costObj.output_tokens ?? 0)),
1308
+ cost: Number(cost2),
1309
+ satsCost: msats2 > 0 ? msats2 / 1e3 : fallbackSatsCost
1310
+ };
1311
+ }
1312
+ if (!parsed.usage) {
1297
1313
  return null;
1298
1314
  }
1299
1315
  const usage = parsed.usage;
1300
1316
  const usageCost = usage.cost;
1301
- const cost = typeof usageCost === "number" ? usageCost : usageCost?.total_usd ?? parsed.metadata?.routstr?.cost?.total_usd ?? 0;
1302
- const msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1e3 : 0);
1317
+ let cost = 0;
1318
+ let msats = 0;
1319
+ if (typeof usageCost === "number") {
1320
+ cost = usageCost;
1321
+ } else if (usageCost && typeof usageCost === "object") {
1322
+ cost = usageCost.total_usd ?? 0;
1323
+ msats = usageCost.total_msats ?? 0;
1324
+ }
1325
+ if (cost === 0) {
1326
+ cost = parsed.metadata?.routstr?.cost?.total_usd ?? 0;
1327
+ }
1328
+ if (msats === 0) {
1329
+ msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1e3 : 0);
1330
+ }
1331
+ const promptTokens = Number(usage.prompt_tokens ?? usage.input_tokens ?? 0);
1332
+ const completionTokens = Number(usage.completion_tokens ?? usage.output_tokens ?? 0);
1333
+ const totalTokens = Number(usage.total_tokens ?? promptTokens + completionTokens);
1303
1334
  const result = {
1304
- promptTokens: Number(usage.prompt_tokens ?? 0),
1305
- completionTokens: Number(usage.completion_tokens ?? 0),
1306
- totalTokens: Number(usage.total_tokens ?? 0),
1335
+ promptTokens,
1336
+ completionTokens,
1337
+ totalTokens,
1307
1338
  cost: Number(cost ?? 0),
1308
1339
  satsCost: msats > 0 ? msats / 1e3 : fallbackSatsCost
1309
1340
  };
@@ -1429,11 +1460,14 @@ var StreamProcessor = class {
1429
1460
  if (parsed.choices?.[0]?.delta?.reasoning) {
1430
1461
  result.reasoning = parsed.choices[0].delta.reasoning;
1431
1462
  }
1432
- if (parsed.usage) {
1433
- result.usage = toUsageStats(extractUsageFromSSEJson(parsed)) ?? {
1434
- total_tokens: parsed.usage.total_tokens,
1435
- prompt_tokens: parsed.usage.prompt_tokens,
1436
- completion_tokens: parsed.usage.completion_tokens
1463
+ const extractedUsage = extractUsageFromSSEJson(parsed);
1464
+ if (extractedUsage) {
1465
+ result.usage = toUsageStats(extractedUsage);
1466
+ } else if (parsed.usage) {
1467
+ result.usage = {
1468
+ total_tokens: parsed.usage.total_tokens ?? parsed.usage.input_tokens + parsed.usage.output_tokens,
1469
+ prompt_tokens: parsed.usage.prompt_tokens ?? parsed.usage.input_tokens,
1470
+ completion_tokens: parsed.usage.completion_tokens ?? parsed.usage.output_tokens
1437
1471
  };
1438
1472
  }
1439
1473
  if (parsed.id) {
@@ -1876,22 +1910,62 @@ var ProviderManager = class _ProviderManager {
1876
1910
  const disabledProviders = new Set(
1877
1911
  this.providerRegistry.getDisabledProviders()
1878
1912
  );
1913
+ console.log(`[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`);
1914
+ console.log(`[findNextBestProvider:${this.instanceId}] currentBaseUrl: ${currentBaseUrl}`);
1915
+ console.log(`[findNextBestProvider:${this.instanceId}] torMode: ${torMode}`);
1916
+ console.log(`[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`);
1917
+ console.log(`[findNextBestProvider:${this.instanceId}] failedProviders: ${[...this.failedProviders]}`);
1918
+ console.log(`[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url]) => url)}`);
1879
1919
  const allProviders = this.providerRegistry.getAllProvidersModels();
1920
+ console.log(`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`);
1880
1921
  const candidates = [];
1922
+ let skippedCurrent = 0, skippedFailed = 0, skippedDisabled = 0, skippedCooldown = 0, skippedOnion = 0, skippedNoModel = 0;
1881
1923
  for (const [baseUrl, models] of Object.entries(allProviders)) {
1882
- if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
1924
+ if (baseUrl === currentBaseUrl) {
1925
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`);
1926
+ skippedCurrent++;
1927
+ continue;
1928
+ }
1929
+ if (this.failedProviders.has(baseUrl)) {
1930
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (failed): ${baseUrl}`);
1931
+ skippedFailed++;
1932
+ continue;
1933
+ }
1934
+ if (disabledProviders.has(baseUrl)) {
1935
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (disabled): ${baseUrl}`);
1936
+ skippedDisabled++;
1937
+ continue;
1938
+ }
1939
+ if (this.isOnCooldown(baseUrl)) {
1940
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (cooldown): ${baseUrl}`);
1941
+ skippedCooldown++;
1883
1942
  continue;
1884
1943
  }
1885
1944
  if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
1945
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (onion/http): ${baseUrl}`);
1946
+ skippedOnion++;
1886
1947
  continue;
1887
1948
  }
1888
1949
  const model = models.find((m) => m.id === modelId);
1889
- if (!model) continue;
1950
+ if (!model) {
1951
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (no model ${modelId}): ${baseUrl} has models: ${models.map((m) => m.id).join(", ")}`);
1952
+ skippedNoModel++;
1953
+ continue;
1954
+ }
1890
1955
  const cost = model.sats_pricing?.completion ?? 0;
1956
+ console.log(`[findNextBestProvider:${this.instanceId}] CANDIDATE: ${baseUrl} cost: ${cost}`);
1891
1957
  candidates.push({ baseUrl, model, cost });
1892
1958
  }
1959
+ console.log(`[findNextBestProvider:${this.instanceId}] Skipped: current=${skippedCurrent}, failed=${skippedFailed}, disabled=${skippedDisabled}, cooldown=${skippedCooldown}, onion=${skippedOnion}, noModel=${skippedNoModel}`);
1960
+ console.log(`[findNextBestProvider:${this.instanceId}] Total candidates: ${candidates.length}`);
1893
1961
  candidates.sort((a, b) => a.cost - b.cost);
1894
- return candidates.length > 0 ? candidates[0].baseUrl : null;
1962
+ if (candidates.length > 0) {
1963
+ console.log(`[findNextBestProvider:${this.instanceId}] Selected provider: ${candidates[0].baseUrl} with cost: ${candidates[0].cost}`);
1964
+ return candidates[0].baseUrl;
1965
+ } else {
1966
+ console.log(`[findNextBestProvider:${this.instanceId}] No candidate providers found`);
1967
+ return null;
1968
+ }
1895
1969
  } catch (error) {
1896
1970
  console.error("Error finding next best provider:", error);
1897
1971
  return null;
@@ -3364,67 +3438,74 @@ function createSSEParserTransform(onUsage, onResponseId) {
3364
3438
  let buffer = "";
3365
3439
  let usageCaptured = false;
3366
3440
  let responseIdCaptured = false;
3367
- const maybeCaptureUsageFromJson = (jsonText) => {
3441
+ const inspectDataPayload = (jsonText) => {
3442
+ if (usageCaptured && responseIdCaptured) return;
3443
+ const trimmed = jsonText.trim();
3444
+ if (!trimmed || trimmed === "[DONE]") return;
3445
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
3368
3446
  try {
3369
- const data = JSON.parse(jsonText);
3370
- const responseId = data.id;
3371
- if (typeof responseId === "string" && responseId.trim().length > 0) {
3372
- onResponseId?.(responseId.trim());
3373
- responseIdCaptured = true;
3447
+ const data = JSON.parse(trimmed);
3448
+ if (!responseIdCaptured) {
3449
+ const responseId = data?.id;
3450
+ if (typeof responseId === "string" && responseId.trim().length > 0) {
3451
+ onResponseId?.(responseId.trim());
3452
+ responseIdCaptured = true;
3453
+ }
3374
3454
  }
3375
- const usage = extractUsageFromSSEJson(data);
3376
- if (usage) {
3377
- onUsage(usage);
3378
- usageCaptured = true;
3455
+ if (!usageCaptured) {
3456
+ const usage = extractUsageFromSSEJson(data);
3457
+ if (usage) {
3458
+ onUsage(usage);
3459
+ usageCaptured = true;
3460
+ }
3379
3461
  }
3380
3462
  } catch {
3381
3463
  }
3382
3464
  };
3383
- const processLine = (self, line) => {
3384
- const trimmed = line.trim();
3385
- if (!trimmed) {
3386
- return;
3387
- }
3388
- if (trimmed === "data: [DONE]" || trimmed === "[DONE]") {
3389
- self.push("data: [DONE]\n\n");
3390
- return;
3391
- }
3392
- if (trimmed.startsWith("data:")) {
3393
- const dataStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed.slice(5).trimStart();
3394
- if (dataStr === "[DONE]") {
3395
- self.push("data: [DONE]\n\n");
3396
- return;
3465
+ const inspectEventBlock = (eventBlock) => {
3466
+ if (usageCaptured && responseIdCaptured) return;
3467
+ const lines = eventBlock.split(/\r?\n/);
3468
+ const dataParts = [];
3469
+ for (const line of lines) {
3470
+ if (!line || line.startsWith(":")) continue;
3471
+ if (line.startsWith("data:")) {
3472
+ const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
3473
+ dataParts.push(value);
3397
3474
  }
3398
- maybeCaptureUsageFromJson(dataStr);
3399
- self.push(`data: ${dataStr}
3400
-
3401
- `);
3402
- return;
3403
3475
  }
3404
- if (trimmed.startsWith("{")) {
3405
- maybeCaptureUsageFromJson(trimmed);
3406
- self.push(`data: ${trimmed}
3407
-
3408
- `);
3409
- return;
3410
- }
3411
- self.push(line + "\n");
3476
+ if (dataParts.length === 0) return;
3477
+ const payload = dataParts.join("\n");
3478
+ inspectDataPayload(payload);
3479
+ };
3480
+ const emitEventBlock = (self, eventBlock) => {
3481
+ if (eventBlock.length === 0) return;
3482
+ inspectEventBlock(eventBlock);
3483
+ self.push(eventBlock + "\n\n");
3412
3484
  };
3413
3485
  return new Transform({
3414
- transform(chunk, encoding, callback) {
3486
+ transform(chunk, _encoding, callback) {
3415
3487
  buffer += chunk.toString();
3416
- const lines = buffer.split(/\r?\n/);
3417
- buffer = lines.pop() || "";
3418
- for (const line of lines) {
3419
- processLine(this, line);
3488
+ const terminator = /\r?\n\r?\n/g;
3489
+ let lastIndex = 0;
3490
+ let match;
3491
+ while ((match = terminator.exec(buffer)) !== null) {
3492
+ const block = buffer.slice(lastIndex, match.index);
3493
+ lastIndex = match.index + match[0].length;
3494
+ emitEventBlock(this, block);
3495
+ }
3496
+ if (lastIndex > 0) {
3497
+ buffer = buffer.slice(lastIndex);
3420
3498
  }
3421
3499
  callback();
3422
3500
  },
3423
3501
  flush(callback) {
3424
- if (buffer.trim()) {
3425
- processLine(this, buffer);
3502
+ if (buffer.length > 0) {
3503
+ const tail = buffer.replace(/\r?\n+$/, "");
3504
+ if (tail.length > 0) {
3505
+ emitEventBlock(this, tail);
3506
+ }
3507
+ buffer = "";
3426
3508
  }
3427
- buffer = "";
3428
3509
  callback();
3429
3510
  }
3430
3511
  });
@@ -3905,9 +3986,10 @@ var RoutstrClient = class {
3905
3986
  const MAX_RETRIES_PER_PROVIDER = 2;
3906
3987
  const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
3907
3988
  let tryNextProvider = false;
3989
+ const errorMessage = responseBody;
3908
3990
  this._log(
3909
3991
  "DEBUG",
3910
- `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}`
3992
+ `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}, errorMessage=${errorMessage}`
3911
3993
  );
3912
3994
  this._log(
3913
3995
  "DEBUG",
@@ -4289,12 +4371,6 @@ var RoutstrClient = class {
4289
4371
  clientApiKey
4290
4372
  });
4291
4373
  (async () => {
4292
- try {
4293
- const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
4294
- this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
4295
- } catch (error) {
4296
- this._log("ERROR", "Failed to refund providers:", error);
4297
- }
4298
4374
  })();
4299
4375
  return satsSpent;
4300
4376
  }