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