@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.d.mts
CHANGED
package/dist/client/index.d.ts
CHANGED
package/dist/client/index.js
CHANGED
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
var cashuTs = require('@cashu/cashu-ts');
|
|
4
4
|
var vanilla = require('zustand/vanilla');
|
|
5
5
|
var stream = require('stream');
|
|
6
|
+
var fs = require('fs');
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
29
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
30
|
+
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
6
31
|
|
|
7
32
|
// core/errors.ts
|
|
8
33
|
var InsufficientBalanceError = class extends Error {
|
|
@@ -56,10 +81,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
56
81
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
57
82
|
if (typeof window === "undefined") {
|
|
58
83
|
try {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const logPath =
|
|
62
|
-
|
|
84
|
+
const fs2 = await import('fs');
|
|
85
|
+
const path2 = await import('path');
|
|
86
|
+
const logPath = path2.join(process.cwd(), "audit.log");
|
|
87
|
+
fs2.appendFileSync(logPath, logLine);
|
|
63
88
|
} catch (error) {
|
|
64
89
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
65
90
|
}
|
|
@@ -1727,6 +1752,7 @@ var ProviderManager = class _ProviderManager {
|
|
|
1727
1752
|
}
|
|
1728
1753
|
/**
|
|
1729
1754
|
* Clean up expired cooldown entries
|
|
1755
|
+
* Also removes the provider from failedProviders so it can be retried
|
|
1730
1756
|
*/
|
|
1731
1757
|
cleanupExpiredCooldowns() {
|
|
1732
1758
|
const now = Date.now();
|
|
@@ -1739,6 +1765,10 @@ var ProviderManager = class _ProviderManager {
|
|
|
1739
1765
|
console.log(
|
|
1740
1766
|
`[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
|
|
1741
1767
|
);
|
|
1768
|
+
this.failedProviders.delete(url);
|
|
1769
|
+
if (this.store) {
|
|
1770
|
+
this.store.getState().removeFailedProvider(url);
|
|
1771
|
+
}
|
|
1742
1772
|
}
|
|
1743
1773
|
return !isExpired;
|
|
1744
1774
|
}
|
|
@@ -1912,60 +1942,47 @@ var ProviderManager = class _ProviderManager {
|
|
|
1912
1942
|
const disabledProviders = new Set(
|
|
1913
1943
|
this.providerRegistry.getDisabledProviders()
|
|
1914
1944
|
);
|
|
1915
|
-
console.log(
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
console.log(
|
|
1919
|
-
|
|
1920
|
-
|
|
1945
|
+
console.log(
|
|
1946
|
+
`[findNextBestProvider:${this.instanceId}] Starting search for model: ${modelId}`
|
|
1947
|
+
);
|
|
1948
|
+
console.log(
|
|
1949
|
+
`[findNextBestProvider:${this.instanceId}] disabledProviders: ${[...disabledProviders]}`
|
|
1950
|
+
);
|
|
1951
|
+
console.log(
|
|
1952
|
+
`[findNextBestProvider:${this.instanceId}] providersOnCooldown: ${this.providersOnCoolDown.map(([url]) => url)}`
|
|
1953
|
+
);
|
|
1921
1954
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
1922
|
-
console.log(
|
|
1955
|
+
console.log(
|
|
1956
|
+
`[findNextBestProvider:${this.instanceId}] Total providers in registry: ${Object.keys(allProviders).length}`
|
|
1957
|
+
);
|
|
1923
1958
|
const candidates = [];
|
|
1924
|
-
let skippedCurrent = 0, skippedFailed = 0, skippedDisabled = 0, skippedCooldown = 0, skippedOnion = 0, skippedNoModel = 0;
|
|
1925
1959
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
1926
1960
|
if (baseUrl === currentBaseUrl) {
|
|
1927
|
-
console.log(
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
}
|
|
1931
|
-
if (this.failedProviders.has(baseUrl)) {
|
|
1932
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (failed): ${baseUrl}`);
|
|
1933
|
-
skippedFailed++;
|
|
1961
|
+
console.log(
|
|
1962
|
+
`[findNextBestProvider:${this.instanceId}] SKIP (current): ${baseUrl}`
|
|
1963
|
+
);
|
|
1934
1964
|
continue;
|
|
1935
1965
|
}
|
|
1936
1966
|
if (disabledProviders.has(baseUrl)) {
|
|
1937
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (disabled): ${baseUrl}`);
|
|
1938
|
-
skippedDisabled++;
|
|
1939
1967
|
continue;
|
|
1940
1968
|
}
|
|
1941
1969
|
if (this.isOnCooldown(baseUrl)) {
|
|
1942
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (cooldown): ${baseUrl}`);
|
|
1943
|
-
skippedCooldown++;
|
|
1944
1970
|
continue;
|
|
1945
1971
|
}
|
|
1946
1972
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
1947
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (onion/http): ${baseUrl}`);
|
|
1948
|
-
skippedOnion++;
|
|
1949
1973
|
continue;
|
|
1950
1974
|
}
|
|
1951
1975
|
const model = models.find((m) => m.id === modelId);
|
|
1952
1976
|
if (!model) {
|
|
1953
|
-
console.log(`[findNextBestProvider:${this.instanceId}] SKIP (no model ${modelId}): ${baseUrl} has models: ${models.map((m) => m.id).join(", ")}`);
|
|
1954
|
-
skippedNoModel++;
|
|
1955
1977
|
continue;
|
|
1956
1978
|
}
|
|
1957
1979
|
const cost = model.sats_pricing?.completion ?? 0;
|
|
1958
|
-
console.log(`[findNextBestProvider:${this.instanceId}] CANDIDATE: ${baseUrl} cost: ${cost}`);
|
|
1959
1980
|
candidates.push({ baseUrl, model, cost });
|
|
1960
1981
|
}
|
|
1961
|
-
console.log(`[findNextBestProvider:${this.instanceId}] Skipped: current=${skippedCurrent}, failed=${skippedFailed}, disabled=${skippedDisabled}, cooldown=${skippedCooldown}, onion=${skippedOnion}, noModel=${skippedNoModel}`);
|
|
1962
|
-
console.log(`[findNextBestProvider:${this.instanceId}] Total candidates: ${candidates.length}`);
|
|
1963
1982
|
candidates.sort((a, b) => a.cost - b.cost);
|
|
1964
1983
|
if (candidates.length > 0) {
|
|
1965
|
-
console.log(`[findNextBestProvider:${this.instanceId}] Selected provider: ${candidates[0].baseUrl} with cost: ${candidates[0].cost}`);
|
|
1966
1984
|
return candidates[0].baseUrl;
|
|
1967
1985
|
} else {
|
|
1968
|
-
console.log(`[findNextBestProvider:${this.instanceId}] No candidate providers found`);
|
|
1969
1986
|
return null;
|
|
1970
1987
|
}
|
|
1971
1988
|
} catch (error) {
|
|
@@ -3438,10 +3455,26 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
3438
3455
|
};
|
|
3439
3456
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
3440
3457
|
let buffer = "";
|
|
3441
|
-
let
|
|
3458
|
+
let capturedUsage = null;
|
|
3442
3459
|
let responseIdCaptured = false;
|
|
3460
|
+
const mergeUsage = (previous, next) => {
|
|
3461
|
+
if (!previous) return next;
|
|
3462
|
+
return {
|
|
3463
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
3464
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
3465
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
3466
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
3467
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
3468
|
+
};
|
|
3469
|
+
};
|
|
3470
|
+
const hasUsageChanged = (previous, next) => {
|
|
3471
|
+
if (!previous) return true;
|
|
3472
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
3473
|
+
};
|
|
3443
3474
|
const inspectDataPayload = (jsonText) => {
|
|
3444
|
-
if (
|
|
3475
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
3476
|
+
return;
|
|
3477
|
+
}
|
|
3445
3478
|
const trimmed = jsonText.trim();
|
|
3446
3479
|
if (!trimmed || trimmed === "[DONE]") return;
|
|
3447
3480
|
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
|
|
@@ -3454,18 +3487,21 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
3454
3487
|
responseIdCaptured = true;
|
|
3455
3488
|
}
|
|
3456
3489
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3490
|
+
const usage = extractUsageFromSSEJson(data);
|
|
3491
|
+
if (usage) {
|
|
3492
|
+
const mergedUsage = mergeUsage(capturedUsage, usage);
|
|
3493
|
+
if (hasUsageChanged(capturedUsage, mergedUsage)) {
|
|
3494
|
+
capturedUsage = mergedUsage;
|
|
3495
|
+
onUsage(mergedUsage);
|
|
3462
3496
|
}
|
|
3463
3497
|
}
|
|
3464
3498
|
} catch {
|
|
3465
3499
|
}
|
|
3466
3500
|
};
|
|
3467
3501
|
const inspectEventBlock = (eventBlock) => {
|
|
3468
|
-
if (
|
|
3502
|
+
if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3469
3505
|
const lines = eventBlock.split(/\r?\n/);
|
|
3470
3506
|
const dataParts = [];
|
|
3471
3507
|
for (const line of lines) {
|
|
@@ -3691,7 +3727,7 @@ var RoutstrClient = class {
|
|
|
3691
3727
|
}
|
|
3692
3728
|
async _prepareRoutedRequest(params) {
|
|
3693
3729
|
const {
|
|
3694
|
-
path,
|
|
3730
|
+
path: requestPath,
|
|
3695
3731
|
method,
|
|
3696
3732
|
body,
|
|
3697
3733
|
headers = {},
|
|
@@ -3732,7 +3768,7 @@ var RoutstrClient = class {
|
|
|
3732
3768
|
const baseHeaders = this._buildBaseHeaders();
|
|
3733
3769
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
3734
3770
|
const response = await this._makeRequest({
|
|
3735
|
-
path,
|
|
3771
|
+
path: requestPath,
|
|
3736
3772
|
method,
|
|
3737
3773
|
body: method === "GET" ? void 0 : requestBody,
|
|
3738
3774
|
baseUrl,
|
|
@@ -3751,7 +3787,20 @@ var RoutstrClient = class {
|
|
|
3751
3787
|
let capturedUsage;
|
|
3752
3788
|
let capturedResponseId;
|
|
3753
3789
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
3790
|
+
const logDir = path__namespace.join(os__namespace.homedir(), ".routstrd", "stream-response");
|
|
3791
|
+
if (!fs__namespace.existsSync(logDir)) {
|
|
3792
|
+
fs__namespace.mkdirSync(logDir, { recursive: true });
|
|
3793
|
+
}
|
|
3794
|
+
const logFile = path__namespace.join(logDir, `${Date.now()}.jsonl`);
|
|
3795
|
+
const logStream = fs__namespace.createWriteStream(logFile);
|
|
3754
3796
|
const nodeReadable = stream.Readable.fromWeb(response.body);
|
|
3797
|
+
const loggingTransform = new stream.Transform({
|
|
3798
|
+
transform(chunk, encoding, callback) {
|
|
3799
|
+
const raw = chunk.toString();
|
|
3800
|
+
logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
|
|
3801
|
+
callback(null, chunk);
|
|
3802
|
+
}
|
|
3803
|
+
});
|
|
3755
3804
|
const sseParser = createSSEParserTransform(
|
|
3756
3805
|
(usage) => {
|
|
3757
3806
|
capturedUsage = usage;
|
|
@@ -3762,7 +3811,7 @@ var RoutstrClient = class {
|
|
|
3762
3811
|
processedResponse.requestId = responseId;
|
|
3763
3812
|
}
|
|
3764
3813
|
);
|
|
3765
|
-
const transformed = nodeReadable.pipe(sseParser, { end: true });
|
|
3814
|
+
const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
|
|
3766
3815
|
const webStream = stream.Readable.toWeb(
|
|
3767
3816
|
transformed
|
|
3768
3817
|
);
|
|
@@ -3831,7 +3880,6 @@ var RoutstrClient = class {
|
|
|
3831
3880
|
callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
|
|
3832
3881
|
const baseHeaders = this._buildBaseHeaders(headers);
|
|
3833
3882
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
3834
|
-
this.providerManager.resetFailedProviders();
|
|
3835
3883
|
const providerInfo = await this.providerRegistry.getProviderInfo(baseUrl);
|
|
3836
3884
|
const providerVersion = providerInfo?.version ?? "";
|
|
3837
3885
|
let modelIdForRequest = selectedModel.id;
|
|
@@ -3934,9 +3982,9 @@ var RoutstrClient = class {
|
|
|
3934
3982
|
* Make the API request with failover support
|
|
3935
3983
|
*/
|
|
3936
3984
|
async _makeRequest(params) {
|
|
3937
|
-
const { path, method, body, baseUrl, token, headers } = params;
|
|
3985
|
+
const { path: path2, method, body, baseUrl, token, headers } = params;
|
|
3938
3986
|
try {
|
|
3939
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
3987
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
|
|
3940
3988
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
3941
3989
|
const response = await fetch(url, {
|
|
3942
3990
|
method,
|
|
@@ -3986,7 +4034,7 @@ var RoutstrClient = class {
|
|
|
3986
4034
|
*/
|
|
3987
4035
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
3988
4036
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
3989
|
-
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
4037
|
+
const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
3990
4038
|
let tryNextProvider = false;
|
|
3991
4039
|
const errorMessage = responseBody;
|
|
3992
4040
|
this._log(
|
|
@@ -4288,7 +4336,7 @@ var RoutstrClient = class {
|
|
|
4288
4336
|
});
|
|
4289
4337
|
return this._makeRequest({
|
|
4290
4338
|
...params,
|
|
4291
|
-
path,
|
|
4339
|
+
path: path2,
|
|
4292
4340
|
method,
|
|
4293
4341
|
body,
|
|
4294
4342
|
baseUrl: nextProvider,
|