@routstr/sdk 0.2.9 → 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.
package/dist/index.mjs CHANGED
@@ -1927,17 +1927,48 @@ function extractResponseId(body) {
1927
1927
  return trimmed.length > 0 ? trimmed : void 0;
1928
1928
  }
1929
1929
  function extractUsageFromSSEJson(parsed, fallbackSatsCost = 0) {
1930
- if (!parsed || typeof parsed !== "object" || !parsed.usage) {
1930
+ if (!parsed || typeof parsed !== "object") {
1931
+ return null;
1932
+ }
1933
+ if (!parsed.usage && parsed.cost && typeof parsed.cost === "object") {
1934
+ const costObj = parsed.cost;
1935
+ const msats2 = costObj.total_msats ?? 0;
1936
+ const cost2 = costObj.total_usd ?? 0;
1937
+ if (msats2 === 0 && cost2 === 0) return null;
1938
+ return {
1939
+ promptTokens: Number(costObj.input_tokens ?? 0),
1940
+ completionTokens: Number(costObj.output_tokens ?? 0),
1941
+ totalTokens: Number((costObj.input_tokens ?? 0) + (costObj.output_tokens ?? 0)),
1942
+ cost: Number(cost2),
1943
+ satsCost: msats2 > 0 ? msats2 / 1e3 : fallbackSatsCost
1944
+ };
1945
+ }
1946
+ if (!parsed.usage) {
1931
1947
  return null;
1932
1948
  }
1933
1949
  const usage = parsed.usage;
1934
1950
  const usageCost = usage.cost;
1935
- const cost = typeof usageCost === "number" ? usageCost : usageCost?.total_usd ?? parsed.metadata?.routstr?.cost?.total_usd ?? 0;
1936
- const msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1e3 : 0);
1951
+ let cost = 0;
1952
+ let msats = 0;
1953
+ if (typeof usageCost === "number") {
1954
+ cost = usageCost;
1955
+ } else if (usageCost && typeof usageCost === "object") {
1956
+ cost = usageCost.total_usd ?? 0;
1957
+ msats = usageCost.total_msats ?? 0;
1958
+ }
1959
+ if (cost === 0) {
1960
+ cost = parsed.metadata?.routstr?.cost?.total_usd ?? 0;
1961
+ }
1962
+ if (msats === 0) {
1963
+ msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1e3 : 0);
1964
+ }
1965
+ const promptTokens = Number(usage.prompt_tokens ?? usage.input_tokens ?? 0);
1966
+ const completionTokens = Number(usage.completion_tokens ?? usage.output_tokens ?? 0);
1967
+ const totalTokens = Number(usage.total_tokens ?? promptTokens + completionTokens);
1937
1968
  const result = {
1938
- promptTokens: Number(usage.prompt_tokens ?? 0),
1939
- completionTokens: Number(usage.completion_tokens ?? 0),
1940
- totalTokens: Number(usage.total_tokens ?? 0),
1969
+ promptTokens,
1970
+ completionTokens,
1971
+ totalTokens,
1941
1972
  cost: Number(cost ?? 0),
1942
1973
  satsCost: msats > 0 ? msats / 1e3 : fallbackSatsCost
1943
1974
  };
@@ -2063,11 +2094,14 @@ var StreamProcessor = class {
2063
2094
  if (parsed.choices?.[0]?.delta?.reasoning) {
2064
2095
  result.reasoning = parsed.choices[0].delta.reasoning;
2065
2096
  }
2066
- if (parsed.usage) {
2067
- result.usage = toUsageStats(extractUsageFromSSEJson(parsed)) ?? {
2068
- total_tokens: parsed.usage.total_tokens,
2069
- prompt_tokens: parsed.usage.prompt_tokens,
2070
- completion_tokens: parsed.usage.completion_tokens
2097
+ const extractedUsage = extractUsageFromSSEJson(parsed);
2098
+ if (extractedUsage) {
2099
+ result.usage = toUsageStats(extractedUsage);
2100
+ } else if (parsed.usage) {
2101
+ result.usage = {
2102
+ total_tokens: parsed.usage.total_tokens ?? parsed.usage.input_tokens + parsed.usage.output_tokens,
2103
+ prompt_tokens: parsed.usage.prompt_tokens ?? parsed.usage.input_tokens,
2104
+ completion_tokens: parsed.usage.completion_tokens ?? parsed.usage.output_tokens
2071
2105
  };
2072
2106
  }
2073
2107
  if (parsed.id) {
@@ -2566,22 +2600,62 @@ var ProviderManager = class _ProviderManager {
2566
2600
  const disabledProviders = new Set(
2567
2601
  this.providerRegistry.getDisabledProviders()
2568
2602
  );
2603
+ console.log(`[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`);
2604
+ console.log(`[findNextBestProvider:${this.instanceId}] currentBaseUrl: ${currentBaseUrl}`);
2605
+ console.log(`[findNextBestProvider:${this.instanceId}] torMode: ${torMode}`);
2606
+ console.log(`[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`);
2607
+ console.log(`[findNextBestProvider:${this.instanceId}] failedProviders: ${[...this.failedProviders]}`);
2608
+ console.log(`[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url]) => url)}`);
2569
2609
  const allProviders = this.providerRegistry.getAllProvidersModels();
2610
+ console.log(`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`);
2570
2611
  const candidates = [];
2612
+ let skippedCurrent = 0, skippedFailed = 0, skippedDisabled = 0, skippedCooldown = 0, skippedOnion = 0, skippedNoModel = 0;
2571
2613
  for (const [baseUrl, models] of Object.entries(allProviders)) {
2572
- if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
2614
+ if (baseUrl === currentBaseUrl) {
2615
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`);
2616
+ skippedCurrent++;
2617
+ continue;
2618
+ }
2619
+ if (this.failedProviders.has(baseUrl)) {
2620
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (failed): ${baseUrl}`);
2621
+ skippedFailed++;
2622
+ continue;
2623
+ }
2624
+ if (disabledProviders.has(baseUrl)) {
2625
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (disabled): ${baseUrl}`);
2626
+ skippedDisabled++;
2627
+ continue;
2628
+ }
2629
+ if (this.isOnCooldown(baseUrl)) {
2630
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (cooldown): ${baseUrl}`);
2631
+ skippedCooldown++;
2573
2632
  continue;
2574
2633
  }
2575
2634
  if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
2635
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (onion/http): ${baseUrl}`);
2636
+ skippedOnion++;
2576
2637
  continue;
2577
2638
  }
2578
2639
  const model = models.find((m) => m.id === modelId);
2579
- if (!model) continue;
2640
+ if (!model) {
2641
+ console.log(`[findNextBestProvider:${this.instanceId}] SKIP (no model ${modelId}): ${baseUrl} has models: ${models.map((m) => m.id).join(", ")}`);
2642
+ skippedNoModel++;
2643
+ continue;
2644
+ }
2580
2645
  const cost = model.sats_pricing?.completion ?? 0;
2646
+ console.log(`[findNextBestProvider:${this.instanceId}] CANDIDATE: ${baseUrl} cost: ${cost}`);
2581
2647
  candidates.push({ baseUrl, model, cost });
2582
2648
  }
2649
+ console.log(`[findNextBestProvider:${this.instanceId}] Skipped: current=${skippedCurrent}, failed=${skippedFailed}, disabled=${skippedDisabled}, cooldown=${skippedCooldown}, onion=${skippedOnion}, noModel=${skippedNoModel}`);
2650
+ console.log(`[findNextBestProvider:${this.instanceId}] Total candidates: ${candidates.length}`);
2583
2651
  candidates.sort((a, b) => a.cost - b.cost);
2584
- return candidates.length > 0 ? candidates[0].baseUrl : null;
2652
+ if (candidates.length > 0) {
2653
+ console.log(`[findNextBestProvider:${this.instanceId}] Selected provider: ${candidates[0].baseUrl} with cost: ${candidates[0].cost}`);
2654
+ return candidates[0].baseUrl;
2655
+ } else {
2656
+ console.log(`[findNextBestProvider:${this.instanceId}] No candidate providers found`);
2657
+ return null;
2658
+ }
2585
2659
  } catch (error) {
2586
2660
  console.error("Error finding next best provider:", error);
2587
2661
  return null;
@@ -4451,67 +4525,74 @@ function createSSEParserTransform(onUsage, onResponseId) {
4451
4525
  let buffer = "";
4452
4526
  let usageCaptured = false;
4453
4527
  let responseIdCaptured = false;
4454
- const maybeCaptureUsageFromJson = (jsonText) => {
4528
+ const inspectDataPayload = (jsonText) => {
4529
+ if (usageCaptured && responseIdCaptured) return;
4530
+ const trimmed = jsonText.trim();
4531
+ if (!trimmed || trimmed === "[DONE]") return;
4532
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
4455
4533
  try {
4456
- const data = JSON.parse(jsonText);
4457
- const responseId = data.id;
4458
- if (typeof responseId === "string" && responseId.trim().length > 0) {
4459
- onResponseId?.(responseId.trim());
4460
- responseIdCaptured = true;
4534
+ const data = JSON.parse(trimmed);
4535
+ if (!responseIdCaptured) {
4536
+ const responseId = data?.id;
4537
+ if (typeof responseId === "string" && responseId.trim().length > 0) {
4538
+ onResponseId?.(responseId.trim());
4539
+ responseIdCaptured = true;
4540
+ }
4461
4541
  }
4462
- const usage = extractUsageFromSSEJson(data);
4463
- if (usage) {
4464
- onUsage(usage);
4465
- usageCaptured = true;
4542
+ if (!usageCaptured) {
4543
+ const usage = extractUsageFromSSEJson(data);
4544
+ if (usage) {
4545
+ onUsage(usage);
4546
+ usageCaptured = true;
4547
+ }
4466
4548
  }
4467
4549
  } catch {
4468
4550
  }
4469
4551
  };
4470
- const processLine = (self, line) => {
4471
- const trimmed = line.trim();
4472
- if (!trimmed) {
4473
- return;
4474
- }
4475
- if (trimmed === "data: [DONE]" || trimmed === "[DONE]") {
4476
- self.push("data: [DONE]\n\n");
4477
- return;
4478
- }
4479
- if (trimmed.startsWith("data:")) {
4480
- const dataStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed.slice(5).trimStart();
4481
- if (dataStr === "[DONE]") {
4482
- self.push("data: [DONE]\n\n");
4483
- return;
4484
- }
4485
- maybeCaptureUsageFromJson(dataStr);
4486
- self.push(`data: ${dataStr}
4487
-
4488
- `);
4489
- return;
4490
- }
4491
- if (trimmed.startsWith("{")) {
4492
- maybeCaptureUsageFromJson(trimmed);
4493
- self.push(`data: ${trimmed}
4494
-
4495
- `);
4496
- return;
4497
- }
4498
- self.push(line + "\n");
4552
+ const inspectEventBlock = (eventBlock) => {
4553
+ if (usageCaptured && responseIdCaptured) return;
4554
+ const lines = eventBlock.split(/\r?\n/);
4555
+ const dataParts = [];
4556
+ for (const line of lines) {
4557
+ if (!line || line.startsWith(":")) continue;
4558
+ if (line.startsWith("data:")) {
4559
+ const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
4560
+ dataParts.push(value);
4561
+ }
4562
+ }
4563
+ if (dataParts.length === 0) return;
4564
+ const payload = dataParts.join("\n");
4565
+ inspectDataPayload(payload);
4566
+ };
4567
+ const emitEventBlock = (self, eventBlock) => {
4568
+ if (eventBlock.length === 0) return;
4569
+ inspectEventBlock(eventBlock);
4570
+ self.push(eventBlock + "\n\n");
4499
4571
  };
4500
4572
  return new Transform({
4501
- transform(chunk, encoding, callback) {
4573
+ transform(chunk, _encoding, callback) {
4502
4574
  buffer += chunk.toString();
4503
- const lines = buffer.split(/\r?\n/);
4504
- buffer = lines.pop() || "";
4505
- for (const line of lines) {
4506
- processLine(this, line);
4575
+ const terminator = /\r?\n\r?\n/g;
4576
+ let lastIndex = 0;
4577
+ let match;
4578
+ while ((match = terminator.exec(buffer)) !== null) {
4579
+ const block = buffer.slice(lastIndex, match.index);
4580
+ lastIndex = match.index + match[0].length;
4581
+ emitEventBlock(this, block);
4582
+ }
4583
+ if (lastIndex > 0) {
4584
+ buffer = buffer.slice(lastIndex);
4507
4585
  }
4508
4586
  callback();
4509
4587
  },
4510
4588
  flush(callback) {
4511
- if (buffer.trim()) {
4512
- processLine(this, buffer);
4589
+ if (buffer.length > 0) {
4590
+ const tail = buffer.replace(/\r?\n+$/, "");
4591
+ if (tail.length > 0) {
4592
+ emitEventBlock(this, tail);
4593
+ }
4594
+ buffer = "";
4513
4595
  }
4514
- buffer = "";
4515
4596
  callback();
4516
4597
  }
4517
4598
  });
@@ -4992,9 +5073,10 @@ var RoutstrClient = class {
4992
5073
  const MAX_RETRIES_PER_PROVIDER = 2;
4993
5074
  const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
4994
5075
  let tryNextProvider = false;
5076
+ const errorMessage = responseBody;
4995
5077
  this._log(
4996
5078
  "DEBUG",
4997
- `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}`
5079
+ `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}, errorMessage=${errorMessage}`
4998
5080
  );
4999
5081
  this._log(
5000
5082
  "DEBUG",