@routstr/sdk 0.2.12 → 0.3.1
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 +29 -15
- package/dist/client/index.d.ts +29 -15
- package/dist/client/index.js +267 -166
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +268 -146
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.js +2 -0
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +2 -0
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -10
- package/dist/index.d.ts +3 -10
- package/dist/index.js +273 -192
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +274 -171
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +13 -0
- package/dist/wallet/index.d.ts +13 -0
- package/dist/wallet/index.js +71 -2
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +71 -2
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,31 +6,7 @@ var rxjs = require('rxjs');
|
|
|
6
6
|
var cashuTs = require('@cashu/cashu-ts');
|
|
7
7
|
var vanilla = require('zustand/vanilla');
|
|
8
8
|
var stream = require('stream');
|
|
9
|
-
var
|
|
10
|
-
var path = require('path');
|
|
11
|
-
var os = require('os');
|
|
12
|
-
|
|
13
|
-
function _interopNamespace(e) {
|
|
14
|
-
if (e && e.__esModule) return e;
|
|
15
|
-
var n = Object.create(null);
|
|
16
|
-
if (e) {
|
|
17
|
-
Object.keys(e).forEach(function (k) {
|
|
18
|
-
if (k !== 'default') {
|
|
19
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
-
enumerable: true,
|
|
22
|
-
get: function () { return e[k]; }
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
n.default = e;
|
|
28
|
-
return Object.freeze(n);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
32
|
-
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
33
|
-
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
9
|
+
var string_decoder = require('string_decoder');
|
|
34
10
|
|
|
35
11
|
// core/errors.ts
|
|
36
12
|
var InsufficientBalanceError = class extends Error {
|
|
@@ -411,6 +387,7 @@ var ModelManager = class _ModelManager {
|
|
|
411
387
|
if (!(error instanceof Error)) return false;
|
|
412
388
|
const msg = error.message.toLowerCase();
|
|
413
389
|
if (msg.includes("fetch failed")) return true;
|
|
390
|
+
if (msg.includes("429")) return true;
|
|
414
391
|
if (msg.includes("502")) return true;
|
|
415
392
|
if (msg.includes("503")) return true;
|
|
416
393
|
if (msg.includes("504")) return true;
|
|
@@ -689,6 +666,7 @@ var MintDiscovery = class {
|
|
|
689
666
|
if (!(error instanceof Error)) return false;
|
|
690
667
|
const msg = error.message.toLowerCase();
|
|
691
668
|
if (msg.includes("fetch failed")) return true;
|
|
669
|
+
if (msg.includes("429")) return true;
|
|
692
670
|
if (msg.includes("502")) return true;
|
|
693
671
|
if (msg.includes("503")) return true;
|
|
694
672
|
if (msg.includes("504")) return true;
|
|
@@ -715,10 +693,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
715
693
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
716
694
|
if (typeof window === "undefined") {
|
|
717
695
|
try {
|
|
718
|
-
const
|
|
719
|
-
const
|
|
720
|
-
const logPath =
|
|
721
|
-
|
|
696
|
+
const fs = await import('fs');
|
|
697
|
+
const path = await import('path');
|
|
698
|
+
const logPath = path.join(process.cwd(), "audit.log");
|
|
699
|
+
fs.appendFileSync(logPath, logLine);
|
|
722
700
|
} catch (error) {
|
|
723
701
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
724
702
|
}
|
|
@@ -1297,7 +1275,7 @@ var CashuSpender = class {
|
|
|
1297
1275
|
};
|
|
1298
1276
|
|
|
1299
1277
|
// wallet/BalanceManager.ts
|
|
1300
|
-
var BalanceManager = class {
|
|
1278
|
+
var BalanceManager = class _BalanceManager {
|
|
1301
1279
|
constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
|
|
1302
1280
|
this.walletAdapter = walletAdapter;
|
|
1303
1281
|
this.storageAdapter = storageAdapter;
|
|
@@ -1314,6 +1292,47 @@ var BalanceManager = class {
|
|
|
1314
1292
|
}
|
|
1315
1293
|
}
|
|
1316
1294
|
cashuSpender;
|
|
1295
|
+
/** In-memory guard for per-provider wallet mutations (topup / refund) */
|
|
1296
|
+
providerWalletOps = /* @__PURE__ */ new Map();
|
|
1297
|
+
/** Cooldown (ms) between opposite operations on the same provider */
|
|
1298
|
+
static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
|
|
1299
|
+
/**
|
|
1300
|
+
* Check whether a wallet operation (topup/refund) may run for a provider.
|
|
1301
|
+
* Returns the reason when blocked.
|
|
1302
|
+
*/
|
|
1303
|
+
_canRunProviderWalletOperation(baseUrl, type) {
|
|
1304
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1305
|
+
if (!existing) {
|
|
1306
|
+
return { allowed: true };
|
|
1307
|
+
}
|
|
1308
|
+
if (existing.type === type) {
|
|
1309
|
+
return { allowed: true };
|
|
1310
|
+
}
|
|
1311
|
+
if (!existing.endTime) {
|
|
1312
|
+
return {
|
|
1313
|
+
allowed: false,
|
|
1314
|
+
reason: `Provider wallet operation locked; ${existing.type} in progress`
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
const elapsed = Date.now() - existing.endTime;
|
|
1318
|
+
if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
|
|
1319
|
+
return {
|
|
1320
|
+
allowed: false,
|
|
1321
|
+
reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
this.providerWalletOps.delete(baseUrl);
|
|
1325
|
+
return { allowed: true };
|
|
1326
|
+
}
|
|
1327
|
+
_beginProviderWalletOperation(baseUrl, type) {
|
|
1328
|
+
this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
|
|
1329
|
+
}
|
|
1330
|
+
_endProviderWalletOperation(baseUrl, type) {
|
|
1331
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1332
|
+
if (existing && existing.type === type) {
|
|
1333
|
+
existing.endTime = Date.now();
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1317
1336
|
async getBalanceState() {
|
|
1318
1337
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
1319
1338
|
const units = this.walletAdapter.getMintUnits();
|
|
@@ -1348,6 +1367,20 @@ var BalanceManager = class {
|
|
|
1348
1367
|
* @returns Refund result
|
|
1349
1368
|
*/
|
|
1350
1369
|
async refundApiKey(options) {
|
|
1370
|
+
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1371
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
|
|
1372
|
+
if (!guard.allowed) {
|
|
1373
|
+
console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
|
|
1374
|
+
return { success: false, message: guard.reason };
|
|
1375
|
+
}
|
|
1376
|
+
this._beginProviderWalletOperation(baseUrl, "refund");
|
|
1377
|
+
try {
|
|
1378
|
+
return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
|
|
1379
|
+
} finally {
|
|
1380
|
+
this._endProviderWalletOperation(baseUrl, "refund");
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
async _refundApiKeyImpl(options) {
|
|
1351
1384
|
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1352
1385
|
if (!apiKey) {
|
|
1353
1386
|
return { success: false, message: "No API key to refund" };
|
|
@@ -1476,6 +1509,20 @@ var BalanceManager = class {
|
|
|
1476
1509
|
* Top up API key balance with a cashu token
|
|
1477
1510
|
*/
|
|
1478
1511
|
async topUp(options) {
|
|
1512
|
+
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1513
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
|
|
1514
|
+
if (!guard.allowed) {
|
|
1515
|
+
console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
|
|
1516
|
+
return { success: false, message: guard.reason };
|
|
1517
|
+
}
|
|
1518
|
+
this._beginProviderWalletOperation(baseUrl, "topup");
|
|
1519
|
+
try {
|
|
1520
|
+
return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
|
|
1521
|
+
} finally {
|
|
1522
|
+
this._endProviderWalletOperation(baseUrl, "topup");
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
async _topUpImpl(options) {
|
|
1479
1526
|
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1480
1527
|
if (!amount || amount <= 0) {
|
|
1481
1528
|
return { success: false, message: "Invalid top up amount" };
|
|
@@ -1636,7 +1683,7 @@ var BalanceManager = class {
|
|
|
1636
1683
|
p2pkPubkey
|
|
1637
1684
|
);
|
|
1638
1685
|
console.log(
|
|
1639
|
-
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
1686
|
+
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}, all mint balances: ${JSON.stringify(Object.fromEntries(Object.entries(balances).map(([mint, balance]) => [mint, getBalanceInSats(balance, units[mint])])))}`
|
|
1640
1687
|
);
|
|
1641
1688
|
return {
|
|
1642
1689
|
success: true,
|
|
@@ -2831,7 +2878,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
2831
2878
|
const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
|
|
2832
2879
|
const totalInputTokens = approximateTokens + imageTokens;
|
|
2833
2880
|
const sp = model?.sats_pricing;
|
|
2834
|
-
if (!sp)
|
|
2881
|
+
if (!sp) {
|
|
2882
|
+
return 0;
|
|
2883
|
+
}
|
|
2835
2884
|
if (!sp.max_completion_cost) {
|
|
2836
2885
|
return sp.max_cost ?? 50;
|
|
2837
2886
|
}
|
|
@@ -4540,26 +4589,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
|
|
|
4540
4589
|
var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
|
|
4541
4590
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4542
4591
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4543
|
-
function
|
|
4592
|
+
function mergeUsage(previous, next) {
|
|
4593
|
+
if (!previous) return next;
|
|
4594
|
+
return {
|
|
4595
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
4596
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
4597
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
4598
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
4599
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
4600
|
+
};
|
|
4601
|
+
}
|
|
4602
|
+
function hasUsageChanged(previous, next) {
|
|
4603
|
+
if (!previous) return true;
|
|
4604
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
4605
|
+
}
|
|
4606
|
+
async function inspectSSEWebStream(stream, onUsage, onResponseId) {
|
|
4607
|
+
const reader = stream.getReader();
|
|
4608
|
+
const decoder = new TextDecoder("utf-8");
|
|
4544
4609
|
let buffer = "";
|
|
4545
4610
|
let capturedUsage = null;
|
|
4611
|
+
let capturedResponseId;
|
|
4546
4612
|
let responseIdCaptured = false;
|
|
4547
|
-
const
|
|
4548
|
-
if (
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4613
|
+
const inspectDataPayload = (jsonText) => {
|
|
4614
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4615
|
+
return;
|
|
4616
|
+
}
|
|
4617
|
+
const trimmed = jsonText.trim();
|
|
4618
|
+
if (!trimmed || trimmed === "[DONE]") return;
|
|
4619
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
|
|
4620
|
+
try {
|
|
4621
|
+
const data = JSON.parse(trimmed);
|
|
4622
|
+
if (!responseIdCaptured) {
|
|
4623
|
+
const responseId = data?.id;
|
|
4624
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
4625
|
+
capturedResponseId = responseId.trim();
|
|
4626
|
+
onResponseId?.(capturedResponseId);
|
|
4627
|
+
responseIdCaptured = true;
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
const usage = extractUsageFromSSEJson(data);
|
|
4631
|
+
if (usage) {
|
|
4632
|
+
const merged = mergeUsage(capturedUsage, usage);
|
|
4633
|
+
if (hasUsageChanged(capturedUsage, merged)) {
|
|
4634
|
+
capturedUsage = merged;
|
|
4635
|
+
onUsage(merged);
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
} catch {
|
|
4639
|
+
}
|
|
4640
|
+
};
|
|
4641
|
+
const inspectEventBlock = (eventBlock) => {
|
|
4642
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4643
|
+
return;
|
|
4644
|
+
}
|
|
4645
|
+
const lines = eventBlock.split(/\r?\n/);
|
|
4646
|
+
const dataParts = [];
|
|
4647
|
+
for (const line of lines) {
|
|
4648
|
+
if (!line || line.startsWith(":")) continue;
|
|
4649
|
+
if (line.startsWith("data:")) {
|
|
4650
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
4651
|
+
dataParts.push(value);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (dataParts.length === 0) return;
|
|
4655
|
+
inspectDataPayload(dataParts.join("\n"));
|
|
4556
4656
|
};
|
|
4557
|
-
const
|
|
4558
|
-
|
|
4559
|
-
|
|
4657
|
+
const drainBufferedEvents = () => {
|
|
4658
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4659
|
+
let lastIndex = 0;
|
|
4660
|
+
let match;
|
|
4661
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4662
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4663
|
+
lastIndex = match.index + match[0].length;
|
|
4664
|
+
if (block.length > 0) inspectEventBlock(block);
|
|
4665
|
+
}
|
|
4666
|
+
if (lastIndex > 0) buffer = buffer.slice(lastIndex);
|
|
4560
4667
|
};
|
|
4668
|
+
try {
|
|
4669
|
+
while (true) {
|
|
4670
|
+
const { value, done } = await reader.read();
|
|
4671
|
+
if (done) break;
|
|
4672
|
+
if (value && value.byteLength > 0) {
|
|
4673
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4674
|
+
drainBufferedEvents();
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
buffer += decoder.decode();
|
|
4678
|
+
drainBufferedEvents();
|
|
4679
|
+
if (buffer.length > 0) {
|
|
4680
|
+
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4681
|
+
if (tail.length > 0) inspectEventBlock(tail);
|
|
4682
|
+
buffer = "";
|
|
4683
|
+
}
|
|
4684
|
+
} catch {
|
|
4685
|
+
} finally {
|
|
4686
|
+
try {
|
|
4687
|
+
reader.releaseLock();
|
|
4688
|
+
} catch {
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
return {
|
|
4692
|
+
capturedUsage: capturedUsage ?? void 0,
|
|
4693
|
+
capturedResponseId
|
|
4694
|
+
};
|
|
4695
|
+
}
|
|
4696
|
+
function createSSEParserTransform(onUsage, onResponseId) {
|
|
4697
|
+
let buffer = "";
|
|
4698
|
+
const decoder = new string_decoder.StringDecoder("utf8");
|
|
4699
|
+
let capturedUsage = null;
|
|
4700
|
+
let responseIdCaptured = false;
|
|
4561
4701
|
const inspectDataPayload = (jsonText) => {
|
|
4562
|
-
if (responseIdCaptured && capturedUsage
|
|
4702
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4563
4703
|
return;
|
|
4564
4704
|
}
|
|
4565
4705
|
const trimmed = jsonText.trim();
|
|
@@ -4586,7 +4726,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4586
4726
|
}
|
|
4587
4727
|
};
|
|
4588
4728
|
const inspectEventBlock = (eventBlock) => {
|
|
4589
|
-
if (responseIdCaptured && capturedUsage
|
|
4729
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4590
4730
|
return;
|
|
4591
4731
|
}
|
|
4592
4732
|
const lines = eventBlock.split(/\r?\n/);
|
|
@@ -4602,32 +4742,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4602
4742
|
const payload = dataParts.join("\n");
|
|
4603
4743
|
inspectDataPayload(payload);
|
|
4604
4744
|
};
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4745
|
+
const processBufferedEvents = () => {
|
|
4746
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4747
|
+
let lastIndex = 0;
|
|
4748
|
+
let match;
|
|
4749
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4750
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4751
|
+
lastIndex = match.index + match[0].length;
|
|
4752
|
+
if (block.length > 0) {
|
|
4753
|
+
inspectEventBlock(block);
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
if (lastIndex > 0) {
|
|
4757
|
+
buffer = buffer.slice(lastIndex);
|
|
4758
|
+
}
|
|
4609
4759
|
};
|
|
4610
4760
|
return new stream.Transform({
|
|
4611
4761
|
transform(chunk, _encoding, callback) {
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
let match;
|
|
4616
|
-
while ((match = terminator.exec(buffer)) !== null) {
|
|
4617
|
-
const block = buffer.slice(lastIndex, match.index);
|
|
4618
|
-
lastIndex = match.index + match[0].length;
|
|
4619
|
-
emitEventBlock(this, block);
|
|
4620
|
-
}
|
|
4621
|
-
if (lastIndex > 0) {
|
|
4622
|
-
buffer = buffer.slice(lastIndex);
|
|
4623
|
-
}
|
|
4762
|
+
this.push(chunk);
|
|
4763
|
+
buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4764
|
+
processBufferedEvents();
|
|
4624
4765
|
callback();
|
|
4625
4766
|
},
|
|
4626
4767
|
flush(callback) {
|
|
4768
|
+
buffer += decoder.end();
|
|
4769
|
+
processBufferedEvents();
|
|
4627
4770
|
if (buffer.length > 0) {
|
|
4628
4771
|
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4629
4772
|
if (tail.length > 0) {
|
|
4630
|
-
|
|
4773
|
+
inspectEventBlock(tail);
|
|
4631
4774
|
}
|
|
4632
4775
|
buffer = "";
|
|
4633
4776
|
}
|
|
@@ -4635,6 +4778,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4635
4778
|
}
|
|
4636
4779
|
});
|
|
4637
4780
|
}
|
|
4781
|
+
|
|
4782
|
+
// client/RoutstrClient.ts
|
|
4638
4783
|
var TOPUP_MARGIN = 1.2;
|
|
4639
4784
|
var RoutstrClient = class {
|
|
4640
4785
|
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
@@ -4734,31 +4879,12 @@ var RoutstrClient = class {
|
|
|
4734
4879
|
*/
|
|
4735
4880
|
async routeRequest(params) {
|
|
4736
4881
|
const prepared = await this._prepareRoutedRequest(params);
|
|
4737
|
-
const
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
modelId: prepared.modelId,
|
|
4744
|
-
usage: prepared.capturedUsage,
|
|
4745
|
-
requestId: prepared.capturedResponseId,
|
|
4746
|
-
clientApiKey: prepared.clientApiKey
|
|
4747
|
-
});
|
|
4748
|
-
prepared.response.satsSpent = satsSpent;
|
|
4749
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
4750
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
4751
|
-
return prepared.response;
|
|
4752
|
-
}
|
|
4753
|
-
async routeRequestToNodeResponse(params) {
|
|
4754
|
-
const { res } = params;
|
|
4755
|
-
const prepared = await this._prepareRoutedRequest(params);
|
|
4756
|
-
res.statusCode = prepared.response.status;
|
|
4757
|
-
prepared.response.headers.forEach((value, key) => {
|
|
4758
|
-
res.setHeader(key, value);
|
|
4759
|
-
});
|
|
4760
|
-
const body = prepared.response.body;
|
|
4761
|
-
if (!body) {
|
|
4882
|
+
const contentType = prepared.response.headers.get("content-type") || "";
|
|
4883
|
+
const isSSE = contentType.includes("text/event-stream");
|
|
4884
|
+
const runFinalize = async () => {
|
|
4885
|
+
const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
|
|
4886
|
+
const usage = capturedUsage ?? prepared.capturedUsage;
|
|
4887
|
+
const requestId = capturedResponseId ?? prepared.capturedResponseId;
|
|
4762
4888
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4763
4889
|
token: prepared.tokenUsed,
|
|
4764
4890
|
baseUrl: prepared.baseUrlUsed,
|
|
@@ -4766,51 +4892,25 @@ var RoutstrClient = class {
|
|
|
4766
4892
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4767
4893
|
response: prepared.response,
|
|
4768
4894
|
modelId: prepared.modelId,
|
|
4769
|
-
usage
|
|
4770
|
-
requestId
|
|
4895
|
+
usage,
|
|
4896
|
+
requestId,
|
|
4771
4897
|
clientApiKey: prepared.clientApiKey
|
|
4772
4898
|
});
|
|
4773
4899
|
prepared.response.satsSpent = satsSpent;
|
|
4774
|
-
|
|
4775
|
-
|
|
4900
|
+
prepared.response.usage = usage;
|
|
4901
|
+
prepared.response.requestId = requestId;
|
|
4902
|
+
return satsSpent;
|
|
4903
|
+
};
|
|
4904
|
+
if (isSSE) {
|
|
4905
|
+
const finalizePromise = runFinalize().catch((error) => {
|
|
4906
|
+
this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
|
|
4907
|
+
return 0;
|
|
4908
|
+
});
|
|
4909
|
+
prepared.response.finalize = () => finalizePromise;
|
|
4910
|
+
return prepared.response;
|
|
4776
4911
|
}
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
let settled = false;
|
|
4780
|
-
const finish = async () => {
|
|
4781
|
-
if (settled) return;
|
|
4782
|
-
settled = true;
|
|
4783
|
-
try {
|
|
4784
|
-
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4785
|
-
token: prepared.tokenUsed,
|
|
4786
|
-
baseUrl: prepared.baseUrlUsed,
|
|
4787
|
-
mintUrl: params.mintUrl,
|
|
4788
|
-
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4789
|
-
response: prepared.response,
|
|
4790
|
-
modelId: prepared.modelId,
|
|
4791
|
-
usage: prepared.capturedUsage,
|
|
4792
|
-
requestId: prepared.capturedResponseId,
|
|
4793
|
-
clientApiKey: prepared.clientApiKey
|
|
4794
|
-
});
|
|
4795
|
-
prepared.response.satsSpent = satsSpent;
|
|
4796
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
4797
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
4798
|
-
resolve();
|
|
4799
|
-
} catch (error) {
|
|
4800
|
-
reject(error);
|
|
4801
|
-
}
|
|
4802
|
-
};
|
|
4803
|
-
const fail = (error) => {
|
|
4804
|
-
if (settled) return;
|
|
4805
|
-
settled = true;
|
|
4806
|
-
reject(error);
|
|
4807
|
-
};
|
|
4808
|
-
res.once("finish", finish);
|
|
4809
|
-
res.once("close", finish);
|
|
4810
|
-
res.once("error", fail);
|
|
4811
|
-
nodeReadable.once("error", fail);
|
|
4812
|
-
nodeReadable.pipe(res);
|
|
4813
|
-
});
|
|
4912
|
+
await runFinalize();
|
|
4913
|
+
return prepared.response;
|
|
4814
4914
|
}
|
|
4815
4915
|
async _prepareRoutedRequest(params) {
|
|
4816
4916
|
const {
|
|
@@ -4834,9 +4934,23 @@ var RoutstrClient = class {
|
|
|
4834
4934
|
);
|
|
4835
4935
|
selectedModel = providerModel ?? void 0;
|
|
4836
4936
|
if (selectedModel) {
|
|
4937
|
+
const requestMessages = Array.isArray(
|
|
4938
|
+
body?.messages
|
|
4939
|
+
) ? body.messages : [];
|
|
4940
|
+
const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
|
|
4941
|
+
this._log(
|
|
4942
|
+
"DEBUG",
|
|
4943
|
+
"[RoutstrClient] generic request pricing input",
|
|
4944
|
+
{
|
|
4945
|
+
modelId: selectedModel.id,
|
|
4946
|
+
messageCount: requestMessages.length,
|
|
4947
|
+
maxTokens: requestMaxTokens
|
|
4948
|
+
}
|
|
4949
|
+
);
|
|
4837
4950
|
requiredSats = this.providerManager.getRequiredSatsForModel(
|
|
4838
4951
|
selectedModel,
|
|
4839
|
-
|
|
4952
|
+
requestMessages,
|
|
4953
|
+
requestMaxTokens
|
|
4840
4954
|
);
|
|
4841
4955
|
}
|
|
4842
4956
|
}
|
|
@@ -4873,22 +4987,18 @@ var RoutstrClient = class {
|
|
|
4873
4987
|
let processedResponse = response;
|
|
4874
4988
|
let capturedUsage;
|
|
4875
4989
|
let capturedResponseId;
|
|
4990
|
+
let usagePromise = Promise.resolve({});
|
|
4876
4991
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
4877
|
-
const
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
const logStream = fs__namespace.createWriteStream(logFile);
|
|
4883
|
-
const nodeReadable = stream.Readable.fromWeb(response.body);
|
|
4884
|
-
const loggingTransform = new stream.Transform({
|
|
4885
|
-
transform(chunk, encoding, callback) {
|
|
4886
|
-
const raw = chunk.toString();
|
|
4887
|
-
logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
|
|
4888
|
-
callback(null, chunk);
|
|
4889
|
-
}
|
|
4992
|
+
const [clientStream, inspectStream] = response.body.tee();
|
|
4993
|
+
processedResponse = new Response(clientStream, {
|
|
4994
|
+
status: response.status,
|
|
4995
|
+
statusText: response.statusText,
|
|
4996
|
+
headers: response.headers
|
|
4890
4997
|
});
|
|
4891
|
-
|
|
4998
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
4999
|
+
processedResponse.token = response.token;
|
|
5000
|
+
usagePromise = inspectSSEWebStream(
|
|
5001
|
+
inspectStream,
|
|
4892
5002
|
(usage) => {
|
|
4893
5003
|
capturedUsage = usage;
|
|
4894
5004
|
processedResponse.usage = usage;
|
|
@@ -4898,17 +5008,7 @@ var RoutstrClient = class {
|
|
|
4898
5008
|
processedResponse.requestId = responseId;
|
|
4899
5009
|
}
|
|
4900
5010
|
);
|
|
4901
|
-
|
|
4902
|
-
const webStream = stream.Readable.toWeb(
|
|
4903
|
-
transformed
|
|
4904
|
-
);
|
|
4905
|
-
processedResponse = new Response(webStream, {
|
|
4906
|
-
status: response.status,
|
|
4907
|
-
statusText: response.statusText,
|
|
4908
|
-
headers: response.headers
|
|
4909
|
-
});
|
|
4910
|
-
processedResponse.baseUrl = response.baseUrl;
|
|
4911
|
-
processedResponse.token = response.token;
|
|
5011
|
+
processedResponse.usagePromise = usagePromise;
|
|
4912
5012
|
}
|
|
4913
5013
|
return {
|
|
4914
5014
|
response: processedResponse,
|
|
@@ -4918,7 +5018,8 @@ var RoutstrClient = class {
|
|
|
4918
5018
|
modelId,
|
|
4919
5019
|
capturedUsage,
|
|
4920
5020
|
capturedResponseId,
|
|
4921
|
-
clientApiKey
|
|
5021
|
+
clientApiKey,
|
|
5022
|
+
usagePromise
|
|
4922
5023
|
};
|
|
4923
5024
|
}
|
|
4924
5025
|
/**
|
|
@@ -5069,9 +5170,9 @@ var RoutstrClient = class {
|
|
|
5069
5170
|
* Make the API request with failover support
|
|
5070
5171
|
*/
|
|
5071
5172
|
async _makeRequest(params) {
|
|
5072
|
-
const { path
|
|
5173
|
+
const { path, method, body, baseUrl, token, headers } = params;
|
|
5073
5174
|
try {
|
|
5074
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
5175
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
5075
5176
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5076
5177
|
const response = await fetch(url, {
|
|
5077
5178
|
method,
|
|
@@ -5121,7 +5222,7 @@ var RoutstrClient = class {
|
|
|
5121
5222
|
*/
|
|
5122
5223
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
5123
5224
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
5124
|
-
const { path
|
|
5225
|
+
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5125
5226
|
let tryNextProvider = false;
|
|
5126
5227
|
const errorMessage = responseBody;
|
|
5127
5228
|
this._log(
|
|
@@ -5193,11 +5294,12 @@ var RoutstrClient = class {
|
|
|
5193
5294
|
baseUrl
|
|
5194
5295
|
);
|
|
5195
5296
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
5196
|
-
const
|
|
5197
|
-
|
|
5297
|
+
const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1e3 : currentBalanceInfo.reserved ?? 0;
|
|
5298
|
+
const shortfall = Math.max(0, params.requiredSats - currentBalance + reservedBalance);
|
|
5299
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
5198
5300
|
this._log(
|
|
5199
5301
|
"DEBUG",
|
|
5200
|
-
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
5302
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`
|
|
5201
5303
|
);
|
|
5202
5304
|
} catch (e) {
|
|
5203
5305
|
this._log(
|
|
@@ -5342,10 +5444,10 @@ var RoutstrClient = class {
|
|
|
5342
5444
|
tryNextProvider = true;
|
|
5343
5445
|
}
|
|
5344
5446
|
}
|
|
5345
|
-
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
5447
|
+
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 429 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
5346
5448
|
this._log(
|
|
5347
5449
|
"DEBUG",
|
|
5348
|
-
`[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
5450
|
+
`[RoutstrClient] _handleErrorResponse: Status ${status} (${status === 429 ? "rate limited" : "auth/server error"}), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
5349
5451
|
);
|
|
5350
5452
|
if (this.mode === "apikeys") {
|
|
5351
5453
|
this._log(
|
|
@@ -5423,7 +5525,7 @@ var RoutstrClient = class {
|
|
|
5423
5525
|
});
|
|
5424
5526
|
return this._makeRequest({
|
|
5425
5527
|
...params,
|
|
5426
|
-
path
|
|
5528
|
+
path,
|
|
5427
5529
|
method,
|
|
5428
5530
|
body,
|
|
5429
5531
|
baseUrl: nextProvider,
|
|
@@ -5831,7 +5933,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5831
5933
|
const {
|
|
5832
5934
|
modelId,
|
|
5833
5935
|
requestBody,
|
|
5834
|
-
path
|
|
5936
|
+
path = "/v1/chat/completions",
|
|
5835
5937
|
headers = {},
|
|
5836
5938
|
forcedProvider,
|
|
5837
5939
|
walletAdapter,
|
|
@@ -5930,17 +6032,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
5930
6032
|
client,
|
|
5931
6033
|
baseUrl,
|
|
5932
6034
|
mintUrl,
|
|
5933
|
-
path
|
|
6035
|
+
path,
|
|
5934
6036
|
headers,
|
|
5935
6037
|
modelId,
|
|
5936
6038
|
proxiedBody
|
|
5937
6039
|
};
|
|
5938
6040
|
}
|
|
5939
6041
|
async function routeRequests(options) {
|
|
5940
|
-
const { client, baseUrl, mintUrl, path
|
|
6042
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5941
6043
|
try {
|
|
5942
6044
|
const response = await client.routeRequest({
|
|
5943
|
-
path
|
|
6045
|
+
path,
|
|
5944
6046
|
method: "POST",
|
|
5945
6047
|
body: proxiedBody,
|
|
5946
6048
|
headers,
|
|
@@ -5959,27 +6061,6 @@ async function routeRequests(options) {
|
|
|
5959
6061
|
throw error;
|
|
5960
6062
|
}
|
|
5961
6063
|
}
|
|
5962
|
-
async function routeRequestsToNodeResponse(options) {
|
|
5963
|
-
const { res } = options;
|
|
5964
|
-
const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5965
|
-
try {
|
|
5966
|
-
await client.routeRequestToNodeResponse({
|
|
5967
|
-
path: path2,
|
|
5968
|
-
method: "POST",
|
|
5969
|
-
body: proxiedBody,
|
|
5970
|
-
headers,
|
|
5971
|
-
baseUrl,
|
|
5972
|
-
mintUrl,
|
|
5973
|
-
modelId,
|
|
5974
|
-
res
|
|
5975
|
-
});
|
|
5976
|
-
} catch (error) {
|
|
5977
|
-
if (error instanceof Error && (error.message.includes("401") || error.message.includes("402") || error.message.includes("403"))) {
|
|
5978
|
-
throw new Error(`Authentication failed: ${error.message}`);
|
|
5979
|
-
}
|
|
5980
|
-
throw error;
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
6064
|
function extractMaxTokens(requestBody) {
|
|
5984
6065
|
if (!requestBody || typeof requestBody !== "object") {
|
|
5985
6066
|
return void 0;
|
|
@@ -6036,12 +6117,12 @@ exports.getDefaultSdkStore = getDefaultSdkStore;
|
|
|
6036
6117
|
exports.getDefaultStorageAdapter = getDefaultStorageAdapter;
|
|
6037
6118
|
exports.getDefaultUsageTrackingDriver = getDefaultUsageTrackingDriver;
|
|
6038
6119
|
exports.getProviderEndpoints = getProviderEndpoints;
|
|
6120
|
+
exports.inspectSSEWebStream = inspectSSEWebStream;
|
|
6039
6121
|
exports.isOnionUrl = isOnionUrl;
|
|
6040
6122
|
exports.isTorContext = isTorContext;
|
|
6041
6123
|
exports.localStorageDriver = localStorageDriver;
|
|
6042
6124
|
exports.normalizeProviderUrl = normalizeProviderUrl;
|
|
6043
6125
|
exports.routeRequests = routeRequests;
|
|
6044
|
-
exports.routeRequestsToNodeResponse = routeRequestsToNodeResponse;
|
|
6045
6126
|
exports.setDefaultUsageTrackingDriver = setDefaultUsageTrackingDriver;
|
|
6046
6127
|
//# sourceMappingURL=index.js.map
|
|
6047
6128
|
//# sourceMappingURL=index.js.map
|