@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.
- package/dist/client/index.d.mts +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +96 -48
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +74 -48
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +102 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,9 @@ import { tap } from 'rxjs';
|
|
|
4
4
|
import { getDecodedToken } from '@cashu/cashu-ts';
|
|
5
5
|
import { createStore } from 'zustand/vanilla';
|
|
6
6
|
import { Transform, Readable } from 'stream';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
7
10
|
|
|
8
11
|
// core/errors.ts
|
|
9
12
|
var InsufficientBalanceError = class extends Error {
|
|
@@ -688,10 +691,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
688
691
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
689
692
|
if (typeof window === "undefined") {
|
|
690
693
|
try {
|
|
691
|
-
const
|
|
692
|
-
const
|
|
693
|
-
const logPath =
|
|
694
|
-
|
|
694
|
+
const fs2 = await import('fs');
|
|
695
|
+
const path2 = await import('path');
|
|
696
|
+
const logPath = path2.join(process.cwd(), "audit.log");
|
|
697
|
+
fs2.appendFileSync(logPath, logLine);
|
|
695
698
|
} catch (error) {
|
|
696
699
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
697
700
|
}
|
|
@@ -2415,6 +2418,7 @@ var ProviderManager = class _ProviderManager {
|
|
|
2415
2418
|
}
|
|
2416
2419
|
/**
|
|
2417
2420
|
* Clean up expired cooldown entries
|
|
2421
|
+
* Also removes the provider from failedProviders so it can be retried
|
|
2418
2422
|
*/
|
|
2419
2423
|
cleanupExpiredCooldowns() {
|
|
2420
2424
|
const now = Date.now();
|
|
@@ -2427,6 +2431,10 @@ var ProviderManager = class _ProviderManager {
|
|
|
2427
2431
|
console.log(
|
|
2428
2432
|
`[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
|
|
2429
2433
|
);
|
|
2434
|
+
this.failedProviders.delete(url);
|
|
2435
|
+
if (this.store) {
|
|
2436
|
+
this.store.getState().removeFailedProvider(url);
|
|
2437
|
+
}
|
|
2430
2438
|
}
|
|
2431
2439
|
return !isExpired;
|
|
2432
2440
|
}
|
|
@@ -2600,60 +2608,47 @@ var ProviderManager = class _ProviderManager {
|
|
|
2600
2608
|
const disabledProviders = new Set(
|
|
2601
2609
|
this.providerRegistry.getDisabledProviders()
|
|
2602
2610
|
);
|
|
2603
|
-
console.log(
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
console.log(
|
|
2607
|
-
|
|
2608
|
-
|
|
2611
|
+
console.log(
|
|
2612
|
+
`[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`
|
|
2613
|
+
);
|
|
2614
|
+
console.log(
|
|
2615
|
+
`[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`
|
|
2616
|
+
);
|
|
2617
|
+
console.log(
|
|
2618
|
+
`[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url]) => url)}`
|
|
2619
|
+
);
|
|
2609
2620
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
2610
|
-
console.log(
|
|
2621
|
+
console.log(
|
|
2622
|
+
`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`
|
|
2623
|
+
);
|
|
2611
2624
|
const candidates = [];
|
|
2612
|
-
let skippedCurrent = 0, skippedFailed = 0, skippedDisabled = 0, skippedCooldown = 0, skippedOnion = 0, skippedNoModel = 0;
|
|
2613
2625
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
2614
2626
|
if (baseUrl === currentBaseUrl) {
|
|
2615
|
-
console.log(
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
}
|
|
2619
|
-
if (this.failedProviders.has(baseUrl)) {
|
|
2620
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (failed): ${baseUrl}`);
|
|
2621
|
-
skippedFailed++;
|
|
2627
|
+
console.log(
|
|
2628
|
+
`[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`
|
|
2629
|
+
);
|
|
2622
2630
|
continue;
|
|
2623
2631
|
}
|
|
2624
2632
|
if (disabledProviders.has(baseUrl)) {
|
|
2625
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (disabled): ${baseUrl}`);
|
|
2626
|
-
skippedDisabled++;
|
|
2627
2633
|
continue;
|
|
2628
2634
|
}
|
|
2629
2635
|
if (this.isOnCooldown(baseUrl)) {
|
|
2630
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (cooldown): ${baseUrl}`);
|
|
2631
|
-
skippedCooldown++;
|
|
2632
2636
|
continue;
|
|
2633
2637
|
}
|
|
2634
2638
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
2635
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (onion/http): ${baseUrl}`);
|
|
2636
|
-
skippedOnion++;
|
|
2637
2639
|
continue;
|
|
2638
2640
|
}
|
|
2639
2641
|
const model = models.find((m) => m.id === modelId);
|
|
2640
2642
|
if (!model) {
|
|
2641
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (no model ${modelId}): ${baseUrl} has models: ${models.map((m) => m.id).join(", ")}`);
|
|
2642
|
-
skippedNoModel++;
|
|
2643
2643
|
continue;
|
|
2644
2644
|
}
|
|
2645
2645
|
const cost = model.sats_pricing?.completion ?? 0;
|
|
2646
|
-
console.log(`[findNextBestProvider:${this.instanceId}] CANDIDATE: ${baseUrl} cost: ${cost}`);
|
|
2647
2646
|
candidates.push({ baseUrl, model, cost });
|
|
2648
2647
|
}
|
|
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}`);
|
|
2651
2648
|
candidates.sort((a, b) => a.cost - b.cost);
|
|
2652
2649
|
if (candidates.length > 0) {
|
|
2653
|
-
console.log(`[findNextBestProvider:${this.instanceId}] Selected provider: ${candidates[0].baseUrl} with cost: ${candidates[0].cost}`);
|
|
2654
2650
|
return candidates[0].baseUrl;
|
|
2655
2651
|
} else {
|
|
2656
|
-
console.log(`[findNextBestProvider:${this.instanceId}] No candidate providers found`);
|
|
2657
2652
|
return null;
|
|
2658
2653
|
}
|
|
2659
2654
|
} catch (error) {
|
|
@@ -4523,10 +4518,26 @@ var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await g
|
|
|
4523
4518
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4524
4519
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
4525
4520
|
let buffer = "";
|
|
4526
|
-
let
|
|
4521
|
+
let capturedUsage = null;
|
|
4527
4522
|
let responseIdCaptured = false;
|
|
4523
|
+
const mergeUsage = (previous, next) => {
|
|
4524
|
+
if (!previous) return next;
|
|
4525
|
+
return {
|
|
4526
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
4527
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
4528
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
4529
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
4530
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
4531
|
+
};
|
|
4532
|
+
};
|
|
4533
|
+
const hasUsageChanged = (previous, next) => {
|
|
4534
|
+
if (!previous) return true;
|
|
4535
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
4536
|
+
};
|
|
4528
4537
|
const inspectDataPayload = (jsonText) => {
|
|
4529
|
-
if (
|
|
4538
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
4539
|
+
return;
|
|
4540
|
+
}
|
|
4530
4541
|
const trimmed = jsonText.trim();
|
|
4531
4542
|
if (!trimmed || trimmed === "[DONE]") return;
|
|
4532
4543
|
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
|
|
@@ -4539,18 +4550,21 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4539
4550
|
responseIdCaptured = true;
|
|
4540
4551
|
}
|
|
4541
4552
|
}
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4553
|
+
const usage = extractUsageFromSSEJson(data);
|
|
4554
|
+
if (usage) {
|
|
4555
|
+
const mergedUsage = mergeUsage(capturedUsage, usage);
|
|
4556
|
+
if (hasUsageChanged(capturedUsage, mergedUsage)) {
|
|
4557
|
+
capturedUsage = mergedUsage;
|
|
4558
|
+
onUsage(mergedUsage);
|
|
4547
4559
|
}
|
|
4548
4560
|
}
|
|
4549
4561
|
} catch {
|
|
4550
4562
|
}
|
|
4551
4563
|
};
|
|
4552
4564
|
const inspectEventBlock = (eventBlock) => {
|
|
4553
|
-
if (
|
|
4565
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4554
4568
|
const lines = eventBlock.split(/\r?\n/);
|
|
4555
4569
|
const dataParts = [];
|
|
4556
4570
|
for (const line of lines) {
|
|
@@ -4776,7 +4790,7 @@ var RoutstrClient = class {
|
|
|
4776
4790
|
}
|
|
4777
4791
|
async _prepareRoutedRequest(params) {
|
|
4778
4792
|
const {
|
|
4779
|
-
path,
|
|
4793
|
+
path: requestPath,
|
|
4780
4794
|
method,
|
|
4781
4795
|
body,
|
|
4782
4796
|
headers = {},
|
|
@@ -4817,7 +4831,7 @@ var RoutstrClient = class {
|
|
|
4817
4831
|
const baseHeaders = this._buildBaseHeaders();
|
|
4818
4832
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
4819
4833
|
const response = await this._makeRequest({
|
|
4820
|
-
path,
|
|
4834
|
+
path: requestPath,
|
|
4821
4835
|
method,
|
|
4822
4836
|
body: method === "GET" ? void 0 : requestBody,
|
|
4823
4837
|
baseUrl,
|
|
@@ -4836,7 +4850,20 @@ var RoutstrClient = class {
|
|
|
4836
4850
|
let capturedUsage;
|
|
4837
4851
|
let capturedResponseId;
|
|
4838
4852
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
4853
|
+
const logDir = path.join(os.homedir(), ".routstrd", "stream-response");
|
|
4854
|
+
if (!fs.existsSync(logDir)) {
|
|
4855
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
4856
|
+
}
|
|
4857
|
+
const logFile = path.join(logDir, `${Date.now()}.jsonl`);
|
|
4858
|
+
const logStream = fs.createWriteStream(logFile);
|
|
4839
4859
|
const nodeReadable = Readable.fromWeb(response.body);
|
|
4860
|
+
const loggingTransform = new Transform({
|
|
4861
|
+
transform(chunk, encoding, callback) {
|
|
4862
|
+
const raw = chunk.toString();
|
|
4863
|
+
logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
|
|
4864
|
+
callback(null, chunk);
|
|
4865
|
+
}
|
|
4866
|
+
});
|
|
4840
4867
|
const sseParser = createSSEParserTransform(
|
|
4841
4868
|
(usage) => {
|
|
4842
4869
|
capturedUsage = usage;
|
|
@@ -4847,7 +4874,7 @@ var RoutstrClient = class {
|
|
|
4847
4874
|
processedResponse.requestId = responseId;
|
|
4848
4875
|
}
|
|
4849
4876
|
);
|
|
4850
|
-
const transformed = nodeReadable.pipe(sseParser, { end: true });
|
|
4877
|
+
const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
|
|
4851
4878
|
const webStream = Readable.toWeb(
|
|
4852
4879
|
transformed
|
|
4853
4880
|
);
|
|
@@ -4916,7 +4943,6 @@ var RoutstrClient = class {
|
|
|
4916
4943
|
callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
|
|
4917
4944
|
const baseHeaders = this._buildBaseHeaders(headers);
|
|
4918
4945
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
4919
|
-
this.providerManager.resetFailedProviders();
|
|
4920
4946
|
const providerInfo = await this.providerRegistry.getProviderInfo(baseUrl);
|
|
4921
4947
|
const providerVersion = providerInfo?.version ?? "";
|
|
4922
4948
|
let modelIdForRequest = selectedModel.id;
|
|
@@ -5019,9 +5045,9 @@ var RoutstrClient = class {
|
|
|
5019
5045
|
* Make the API request with failover support
|
|
5020
5046
|
*/
|
|
5021
5047
|
async _makeRequest(params) {
|
|
5022
|
-
const { path, method, body, baseUrl, token, headers } = params;
|
|
5048
|
+
const { path: path2, method, body, baseUrl, token, headers } = params;
|
|
5023
5049
|
try {
|
|
5024
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
5050
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
|
|
5025
5051
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5026
5052
|
const response = await fetch(url, {
|
|
5027
5053
|
method,
|
|
@@ -5071,7 +5097,7 @@ var RoutstrClient = class {
|
|
|
5071
5097
|
*/
|
|
5072
5098
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
5073
5099
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
5074
|
-
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5100
|
+
const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5075
5101
|
let tryNextProvider = false;
|
|
5076
5102
|
const errorMessage = responseBody;
|
|
5077
5103
|
this._log(
|
|
@@ -5373,7 +5399,7 @@ var RoutstrClient = class {
|
|
|
5373
5399
|
});
|
|
5374
5400
|
return this._makeRequest({
|
|
5375
5401
|
...params,
|
|
5376
|
-
path,
|
|
5402
|
+
path: path2,
|
|
5377
5403
|
method,
|
|
5378
5404
|
body,
|
|
5379
5405
|
baseUrl: nextProvider,
|
|
@@ -5781,7 +5807,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5781
5807
|
const {
|
|
5782
5808
|
modelId,
|
|
5783
5809
|
requestBody,
|
|
5784
|
-
path = "/v1/chat/completions",
|
|
5810
|
+
path: path2 = "/v1/chat/completions",
|
|
5785
5811
|
headers = {},
|
|
5786
5812
|
forcedProvider,
|
|
5787
5813
|
walletAdapter,
|
|
@@ -5880,17 +5906,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
5880
5906
|
client,
|
|
5881
5907
|
baseUrl,
|
|
5882
5908
|
mintUrl,
|
|
5883
|
-
path,
|
|
5909
|
+
path: path2,
|
|
5884
5910
|
headers,
|
|
5885
5911
|
modelId,
|
|
5886
5912
|
proxiedBody
|
|
5887
5913
|
};
|
|
5888
5914
|
}
|
|
5889
5915
|
async function routeRequests(options) {
|
|
5890
|
-
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5916
|
+
const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5891
5917
|
try {
|
|
5892
5918
|
const response = await client.routeRequest({
|
|
5893
|
-
path,
|
|
5919
|
+
path: path2,
|
|
5894
5920
|
method: "POST",
|
|
5895
5921
|
body: proxiedBody,
|
|
5896
5922
|
headers,
|
|
@@ -5911,10 +5937,10 @@ async function routeRequests(options) {
|
|
|
5911
5937
|
}
|
|
5912
5938
|
async function routeRequestsToNodeResponse(options) {
|
|
5913
5939
|
const { res } = options;
|
|
5914
|
-
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5940
|
+
const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5915
5941
|
try {
|
|
5916
5942
|
await client.routeRequestToNodeResponse({
|
|
5917
|
-
path,
|
|
5943
|
+
path: path2,
|
|
5918
5944
|
method: "POST",
|
|
5919
5945
|
body: proxiedBody,
|
|
5920
5946
|
headers,
|