@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/client/index.d.mts +15 -0
- package/dist/client/index.d.ts +15 -0
- package/dist/client/index.js +142 -60
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +142 -60
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +144 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +144 -62
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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"
|
|
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
|
-
|
|
1938
|
-
|
|
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
|
|
1941
|
-
completionTokens
|
|
1942
|
-
totalTokens
|
|
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
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
|
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(
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
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
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
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
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
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,
|
|
4575
|
+
transform(chunk, _encoding, callback) {
|
|
4504
4576
|
buffer += chunk.toString();
|
|
4505
|
-
const
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
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.
|
|
4514
|
-
|
|
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",
|