@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/client/index.mjs
CHANGED
|
@@ -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
|
|
58
|
-
const
|
|
59
|
-
const logPath =
|
|
60
|
-
|
|
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(
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
console.log(
|
|
1917
|
-
|
|
1918
|
-
|
|
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(
|
|
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(
|
|
1926
|
-
|
|
1927
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
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 (
|
|
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(/\/$/, "")}${
|
|
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,
|