@routstr/sdk 0.2.11 → 0.2.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.
@@ -1,6 +1,9 @@
1
1
  import { getDecodedToken } from '@cashu/cashu-ts';
2
2
  import { createStore } from 'zustand/vanilla';
3
3
  import { Transform, Readable } from 'stream';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as os from 'os';
4
7
 
5
8
  // core/errors.ts
6
9
  var InsufficientBalanceError = class extends Error {
@@ -54,10 +57,10 @@ var AuditLogger = class _AuditLogger {
54
57
  const logLine = JSON.stringify(fullEntry) + "\n";
55
58
  if (typeof window === "undefined") {
56
59
  try {
57
- const fs = await import('fs');
58
- const path = await import('path');
59
- const logPath = path.join(process.cwd(), "audit.log");
60
- fs.appendFileSync(logPath, logLine);
60
+ const fs2 = await import('fs');
61
+ const path2 = await import('path');
62
+ const logPath = path2.join(process.cwd(), "audit.log");
63
+ fs2.appendFileSync(logPath, logLine);
61
64
  } catch (error) {
62
65
  console.error("[AuditLogger] Failed to write to file:", error);
63
66
  }
@@ -1725,6 +1728,7 @@ var ProviderManager = class _ProviderManager {
1725
1728
  }
1726
1729
  /**
1727
1730
  * Clean up expired cooldown entries
1731
+ * Also removes the provider from failedProviders so it can be retried
1728
1732
  */
1729
1733
  cleanupExpiredCooldowns() {
1730
1734
  const now = Date.now();
@@ -1737,6 +1741,10 @@ var ProviderManager = class _ProviderManager {
1737
1741
  console.log(
1738
1742
  `[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
1739
1743
  );
1744
+ this.failedProviders.delete(url);
1745
+ if (this.store) {
1746
+ this.store.getState().removeFailedProvider(url);
1747
+ }
1740
1748
  }
1741
1749
  return !isExpired;
1742
1750
  }
@@ -1910,60 +1918,47 @@ var ProviderManager = class _ProviderManager {
1910
1918
  const disabledProviders = new Set(
1911
1919
  this.providerRegistry.getDisabledProviders()
1912
1920
  );
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)}`);
1921
+ console.log(
1922
+ `[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`
1923
+ );
1924
+ console.log(
1925
+ `[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`
1926
+ );
1927
+ console.log(
1928
+ `[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url]) => url)}`
1929
+ );
1919
1930
  const allProviders = this.providerRegistry.getAllProvidersModels();
1920
- console.log(`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`);
1931
+ console.log(
1932
+ `[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`
1933
+ );
1921
1934
  const candidates = [];
1922
- let skippedCurrent = 0, skippedFailed = 0, skippedDisabled = 0, skippedCooldown = 0, skippedOnion = 0, skippedNoModel = 0;
1923
1935
  for (const [baseUrl, models] of Object.entries(allProviders)) {
1924
1936
  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++;
1937
+ console.log(
1938
+ `[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`
1939
+ );
1932
1940
  continue;
1933
1941
  }
1934
1942
  if (disabledProviders.has(baseUrl)) {
1935
- console.log(`[findNextBestProvider:${this.instanceId}] SKIP (disabled): ${baseUrl}`);
1936
- skippedDisabled++;
1937
1943
  continue;
1938
1944
  }
1939
1945
  if (this.isOnCooldown(baseUrl)) {
1940
- console.log(`[findNextBestProvider:${this.instanceId}] SKIP (cooldown): ${baseUrl}`);
1941
- skippedCooldown++;
1942
1946
  continue;
1943
1947
  }
1944
1948
  if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
1945
- console.log(`[findNextBestProvider:${this.instanceId}] SKIP (onion/http): ${baseUrl}`);
1946
- skippedOnion++;
1947
1949
  continue;
1948
1950
  }
1949
1951
  const model = models.find((m) => m.id === modelId);
1950
1952
  if (!model) {
1951
- console.log(`[findNextBestProvider:${this.instanceId}] SKIP (no model ${modelId}): ${baseUrl} has models: ${models.map((m) => m.id).join(", ")}`);
1952
- skippedNoModel++;
1953
1953
  continue;
1954
1954
  }
1955
1955
  const cost = model.sats_pricing?.completion ?? 0;
1956
- console.log(`[findNextBestProvider:${this.instanceId}] CANDIDATE: ${baseUrl} cost: ${cost}`);
1957
1956
  candidates.push({ baseUrl, model, cost });
1958
1957
  }
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}`);
1961
1958
  candidates.sort((a, b) => a.cost - b.cost);
1962
1959
  if (candidates.length > 0) {
1963
- console.log(`[findNextBestProvider:${this.instanceId}] Selected provider: ${candidates[0].baseUrl} with cost: ${candidates[0].cost}`);
1964
1960
  return candidates[0].baseUrl;
1965
1961
  } else {
1966
- console.log(`[findNextBestProvider:${this.instanceId}] No candidate providers found`);
1967
1962
  return null;
1968
1963
  }
1969
1964
  } catch (error) {
@@ -3436,10 +3431,26 @@ var getDefaultUsageTrackingDriver = () => {
3436
3431
  };
3437
3432
  function createSSEParserTransform(onUsage, onResponseId) {
3438
3433
  let buffer = "";
3439
- let usageCaptured = false;
3434
+ let capturedUsage = null;
3440
3435
  let responseIdCaptured = false;
3436
+ const mergeUsage = (previous, next) => {
3437
+ if (!previous) return next;
3438
+ return {
3439
+ promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
3440
+ completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
3441
+ totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
3442
+ cost: next.cost > 0 ? next.cost : previous.cost,
3443
+ satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
3444
+ };
3445
+ };
3446
+ const hasUsageChanged = (previous, next) => {
3447
+ if (!previous) return true;
3448
+ return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
3449
+ };
3441
3450
  const inspectDataPayload = (jsonText) => {
3442
- if (usageCaptured && responseIdCaptured) return;
3451
+ if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
3452
+ return;
3453
+ }
3443
3454
  const trimmed = jsonText.trim();
3444
3455
  if (!trimmed || trimmed === "[DONE]") return;
3445
3456
  if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
@@ -3452,18 +3463,21 @@ function createSSEParserTransform(onUsage, onResponseId) {
3452
3463
  responseIdCaptured = true;
3453
3464
  }
3454
3465
  }
3455
- if (!usageCaptured) {
3456
- const usage = extractUsageFromSSEJson(data);
3457
- if (usage) {
3458
- onUsage(usage);
3459
- usageCaptured = true;
3466
+ const usage = extractUsageFromSSEJson(data);
3467
+ if (usage) {
3468
+ const mergedUsage = mergeUsage(capturedUsage, usage);
3469
+ if (hasUsageChanged(capturedUsage, mergedUsage)) {
3470
+ capturedUsage = mergedUsage;
3471
+ onUsage(mergedUsage);
3460
3472
  }
3461
3473
  }
3462
3474
  } catch {
3463
3475
  }
3464
3476
  };
3465
3477
  const inspectEventBlock = (eventBlock) => {
3466
- if (usageCaptured && responseIdCaptured) return;
3478
+ if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
3479
+ return;
3480
+ }
3467
3481
  const lines = eventBlock.split(/\r?\n/);
3468
3482
  const dataParts = [];
3469
3483
  for (const line of lines) {
@@ -3689,7 +3703,7 @@ var RoutstrClient = class {
3689
3703
  }
3690
3704
  async _prepareRoutedRequest(params) {
3691
3705
  const {
3692
- path,
3706
+ path: requestPath,
3693
3707
  method,
3694
3708
  body,
3695
3709
  headers = {},
@@ -3730,7 +3744,7 @@ var RoutstrClient = class {
3730
3744
  const baseHeaders = this._buildBaseHeaders();
3731
3745
  const requestHeaders = this._withAuthHeader(baseHeaders, token);
3732
3746
  const response = await this._makeRequest({
3733
- path,
3747
+ path: requestPath,
3734
3748
  method,
3735
3749
  body: method === "GET" ? void 0 : requestBody,
3736
3750
  baseUrl,
@@ -3749,7 +3763,20 @@ var RoutstrClient = class {
3749
3763
  let capturedUsage;
3750
3764
  let capturedResponseId;
3751
3765
  if (contentType.includes("text/event-stream") && response.body) {
3766
+ const logDir = path.join(os.homedir(), ".routstrd", "stream-response");
3767
+ if (!fs.existsSync(logDir)) {
3768
+ fs.mkdirSync(logDir, { recursive: true });
3769
+ }
3770
+ const logFile = path.join(logDir, `${Date.now()}.jsonl`);
3771
+ const logStream = fs.createWriteStream(logFile);
3752
3772
  const nodeReadable = Readable.fromWeb(response.body);
3773
+ const loggingTransform = new Transform({
3774
+ transform(chunk, encoding, callback) {
3775
+ const raw = chunk.toString();
3776
+ logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
3777
+ callback(null, chunk);
3778
+ }
3779
+ });
3753
3780
  const sseParser = createSSEParserTransform(
3754
3781
  (usage) => {
3755
3782
  capturedUsage = usage;
@@ -3760,7 +3787,7 @@ var RoutstrClient = class {
3760
3787
  processedResponse.requestId = responseId;
3761
3788
  }
3762
3789
  );
3763
- const transformed = nodeReadable.pipe(sseParser, { end: true });
3790
+ const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
3764
3791
  const webStream = Readable.toWeb(
3765
3792
  transformed
3766
3793
  );
@@ -3829,7 +3856,6 @@ var RoutstrClient = class {
3829
3856
  callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
3830
3857
  const baseHeaders = this._buildBaseHeaders(headers);
3831
3858
  const requestHeaders = this._withAuthHeader(baseHeaders, token);
3832
- this.providerManager.resetFailedProviders();
3833
3859
  const providerInfo = await this.providerRegistry.getProviderInfo(baseUrl);
3834
3860
  const providerVersion = providerInfo?.version ?? "";
3835
3861
  let modelIdForRequest = selectedModel.id;
@@ -3932,9 +3958,9 @@ var RoutstrClient = class {
3932
3958
  * Make the API request with failover support
3933
3959
  */
3934
3960
  async _makeRequest(params) {
3935
- const { path, method, body, baseUrl, token, headers } = params;
3961
+ const { path: path2, method, body, baseUrl, token, headers } = params;
3936
3962
  try {
3937
- const url = `${baseUrl.replace(/\/$/, "")}${path}`;
3963
+ const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
3938
3964
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
3939
3965
  const response = await fetch(url, {
3940
3966
  method,
@@ -3984,7 +4010,7 @@ var RoutstrClient = class {
3984
4010
  */
3985
4011
  async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
3986
4012
  const MAX_RETRIES_PER_PROVIDER = 2;
3987
- const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
4013
+ const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
3988
4014
  let tryNextProvider = false;
3989
4015
  const errorMessage = responseBody;
3990
4016
  this._log(
@@ -4286,7 +4312,7 @@ var RoutstrClient = class {
4286
4312
  });
4287
4313
  return this._makeRequest({
4288
4314
  ...params,
4289
- path,
4315
+ path: path2,
4290
4316
  method,
4291
4317
  body,
4292
4318
  baseUrl: nextProvider,