@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.mjs
CHANGED
|
@@ -3,10 +3,8 @@ import { EventStore } from 'applesauce-core';
|
|
|
3
3
|
import { tap } from 'rxjs';
|
|
4
4
|
import { getDecodedToken } from '@cashu/cashu-ts';
|
|
5
5
|
import { createStore } from 'zustand/vanilla';
|
|
6
|
-
import { Transform
|
|
7
|
-
import
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import * as os from 'os';
|
|
6
|
+
import { Transform } from 'stream';
|
|
7
|
+
import { StringDecoder } from 'string_decoder';
|
|
10
8
|
|
|
11
9
|
// core/errors.ts
|
|
12
10
|
var InsufficientBalanceError = class extends Error {
|
|
@@ -387,6 +385,7 @@ var ModelManager = class _ModelManager {
|
|
|
387
385
|
if (!(error instanceof Error)) return false;
|
|
388
386
|
const msg = error.message.toLowerCase();
|
|
389
387
|
if (msg.includes("fetch failed")) return true;
|
|
388
|
+
if (msg.includes("429")) return true;
|
|
390
389
|
if (msg.includes("502")) return true;
|
|
391
390
|
if (msg.includes("503")) return true;
|
|
392
391
|
if (msg.includes("504")) return true;
|
|
@@ -665,6 +664,7 @@ var MintDiscovery = class {
|
|
|
665
664
|
if (!(error instanceof Error)) return false;
|
|
666
665
|
const msg = error.message.toLowerCase();
|
|
667
666
|
if (msg.includes("fetch failed")) return true;
|
|
667
|
+
if (msg.includes("429")) return true;
|
|
668
668
|
if (msg.includes("502")) return true;
|
|
669
669
|
if (msg.includes("503")) return true;
|
|
670
670
|
if (msg.includes("504")) return true;
|
|
@@ -691,10 +691,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
691
691
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
692
692
|
if (typeof window === "undefined") {
|
|
693
693
|
try {
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
const logPath =
|
|
697
|
-
|
|
694
|
+
const fs = await import('fs');
|
|
695
|
+
const path = await import('path');
|
|
696
|
+
const logPath = path.join(process.cwd(), "audit.log");
|
|
697
|
+
fs.appendFileSync(logPath, logLine);
|
|
698
698
|
} catch (error) {
|
|
699
699
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
700
700
|
}
|
|
@@ -1273,7 +1273,7 @@ var CashuSpender = class {
|
|
|
1273
1273
|
};
|
|
1274
1274
|
|
|
1275
1275
|
// wallet/BalanceManager.ts
|
|
1276
|
-
var BalanceManager = class {
|
|
1276
|
+
var BalanceManager = class _BalanceManager {
|
|
1277
1277
|
constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
|
|
1278
1278
|
this.walletAdapter = walletAdapter;
|
|
1279
1279
|
this.storageAdapter = storageAdapter;
|
|
@@ -1290,6 +1290,47 @@ var BalanceManager = class {
|
|
|
1290
1290
|
}
|
|
1291
1291
|
}
|
|
1292
1292
|
cashuSpender;
|
|
1293
|
+
/** In-memory guard for per-provider wallet mutations (topup / refund) */
|
|
1294
|
+
providerWalletOps = /* @__PURE__ */ new Map();
|
|
1295
|
+
/** Cooldown (ms) between opposite operations on the same provider */
|
|
1296
|
+
static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
|
|
1297
|
+
/**
|
|
1298
|
+
* Check whether a wallet operation (topup/refund) may run for a provider.
|
|
1299
|
+
* Returns the reason when blocked.
|
|
1300
|
+
*/
|
|
1301
|
+
_canRunProviderWalletOperation(baseUrl, type) {
|
|
1302
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1303
|
+
if (!existing) {
|
|
1304
|
+
return { allowed: true };
|
|
1305
|
+
}
|
|
1306
|
+
if (existing.type === type) {
|
|
1307
|
+
return { allowed: true };
|
|
1308
|
+
}
|
|
1309
|
+
if (!existing.endTime) {
|
|
1310
|
+
return {
|
|
1311
|
+
allowed: false,
|
|
1312
|
+
reason: `Provider wallet operation locked; ${existing.type} in progress`
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
const elapsed = Date.now() - existing.endTime;
|
|
1316
|
+
if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
|
|
1317
|
+
return {
|
|
1318
|
+
allowed: false,
|
|
1319
|
+
reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
this.providerWalletOps.delete(baseUrl);
|
|
1323
|
+
return { allowed: true };
|
|
1324
|
+
}
|
|
1325
|
+
_beginProviderWalletOperation(baseUrl, type) {
|
|
1326
|
+
this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
|
|
1327
|
+
}
|
|
1328
|
+
_endProviderWalletOperation(baseUrl, type) {
|
|
1329
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1330
|
+
if (existing && existing.type === type) {
|
|
1331
|
+
existing.endTime = Date.now();
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1293
1334
|
async getBalanceState() {
|
|
1294
1335
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
1295
1336
|
const units = this.walletAdapter.getMintUnits();
|
|
@@ -1324,6 +1365,20 @@ var BalanceManager = class {
|
|
|
1324
1365
|
* @returns Refund result
|
|
1325
1366
|
*/
|
|
1326
1367
|
async refundApiKey(options) {
|
|
1368
|
+
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1369
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
|
|
1370
|
+
if (!guard.allowed) {
|
|
1371
|
+
console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
|
|
1372
|
+
return { success: false, message: guard.reason };
|
|
1373
|
+
}
|
|
1374
|
+
this._beginProviderWalletOperation(baseUrl, "refund");
|
|
1375
|
+
try {
|
|
1376
|
+
return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
|
|
1377
|
+
} finally {
|
|
1378
|
+
this._endProviderWalletOperation(baseUrl, "refund");
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
async _refundApiKeyImpl(options) {
|
|
1327
1382
|
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1328
1383
|
if (!apiKey) {
|
|
1329
1384
|
return { success: false, message: "No API key to refund" };
|
|
@@ -1452,6 +1507,20 @@ var BalanceManager = class {
|
|
|
1452
1507
|
* Top up API key balance with a cashu token
|
|
1453
1508
|
*/
|
|
1454
1509
|
async topUp(options) {
|
|
1510
|
+
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1511
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
|
|
1512
|
+
if (!guard.allowed) {
|
|
1513
|
+
console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
|
|
1514
|
+
return { success: false, message: guard.reason };
|
|
1515
|
+
}
|
|
1516
|
+
this._beginProviderWalletOperation(baseUrl, "topup");
|
|
1517
|
+
try {
|
|
1518
|
+
return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
|
|
1519
|
+
} finally {
|
|
1520
|
+
this._endProviderWalletOperation(baseUrl, "topup");
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
async _topUpImpl(options) {
|
|
1455
1524
|
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1456
1525
|
if (!amount || amount <= 0) {
|
|
1457
1526
|
return { success: false, message: "Invalid top up amount" };
|
|
@@ -1612,7 +1681,7 @@ var BalanceManager = class {
|
|
|
1612
1681
|
p2pkPubkey
|
|
1613
1682
|
);
|
|
1614
1683
|
console.log(
|
|
1615
|
-
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
1684
|
+
`[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])])))}`
|
|
1616
1685
|
);
|
|
1617
1686
|
return {
|
|
1618
1687
|
success: true,
|
|
@@ -2807,7 +2876,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
2807
2876
|
const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
|
|
2808
2877
|
const totalInputTokens = approximateTokens + imageTokens;
|
|
2809
2878
|
const sp = model?.sats_pricing;
|
|
2810
|
-
if (!sp)
|
|
2879
|
+
if (!sp) {
|
|
2880
|
+
return 0;
|
|
2881
|
+
}
|
|
2811
2882
|
if (!sp.max_completion_cost) {
|
|
2812
2883
|
return sp.max_cost ?? 50;
|
|
2813
2884
|
}
|
|
@@ -4516,26 +4587,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
|
|
|
4516
4587
|
var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
|
|
4517
4588
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4518
4589
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4519
|
-
function
|
|
4590
|
+
function mergeUsage(previous, next) {
|
|
4591
|
+
if (!previous) return next;
|
|
4592
|
+
return {
|
|
4593
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
4594
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
4595
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
4596
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
4597
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
4598
|
+
};
|
|
4599
|
+
}
|
|
4600
|
+
function hasUsageChanged(previous, next) {
|
|
4601
|
+
if (!previous) return true;
|
|
4602
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
4603
|
+
}
|
|
4604
|
+
async function inspectSSEWebStream(stream, onUsage, onResponseId) {
|
|
4605
|
+
const reader = stream.getReader();
|
|
4606
|
+
const decoder = new TextDecoder("utf-8");
|
|
4520
4607
|
let buffer = "";
|
|
4521
4608
|
let capturedUsage = null;
|
|
4609
|
+
let capturedResponseId;
|
|
4522
4610
|
let responseIdCaptured = false;
|
|
4523
|
-
const
|
|
4524
|
-
if (
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4611
|
+
const inspectDataPayload = (jsonText) => {
|
|
4612
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
const trimmed = jsonText.trim();
|
|
4616
|
+
if (!trimmed || trimmed === "[DONE]") return;
|
|
4617
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
|
|
4618
|
+
try {
|
|
4619
|
+
const data = JSON.parse(trimmed);
|
|
4620
|
+
if (!responseIdCaptured) {
|
|
4621
|
+
const responseId = data?.id;
|
|
4622
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
4623
|
+
capturedResponseId = responseId.trim();
|
|
4624
|
+
onResponseId?.(capturedResponseId);
|
|
4625
|
+
responseIdCaptured = true;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
const usage = extractUsageFromSSEJson(data);
|
|
4629
|
+
if (usage) {
|
|
4630
|
+
const merged = mergeUsage(capturedUsage, usage);
|
|
4631
|
+
if (hasUsageChanged(capturedUsage, merged)) {
|
|
4632
|
+
capturedUsage = merged;
|
|
4633
|
+
onUsage(merged);
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
} catch {
|
|
4637
|
+
}
|
|
4532
4638
|
};
|
|
4533
|
-
const
|
|
4534
|
-
if (
|
|
4535
|
-
|
|
4639
|
+
const inspectEventBlock = (eventBlock) => {
|
|
4640
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4641
|
+
return;
|
|
4642
|
+
}
|
|
4643
|
+
const lines = eventBlock.split(/\r?\n/);
|
|
4644
|
+
const dataParts = [];
|
|
4645
|
+
for (const line of lines) {
|
|
4646
|
+
if (!line || line.startsWith(":")) continue;
|
|
4647
|
+
if (line.startsWith("data:")) {
|
|
4648
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
4649
|
+
dataParts.push(value);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
if (dataParts.length === 0) return;
|
|
4653
|
+
inspectDataPayload(dataParts.join("\n"));
|
|
4654
|
+
};
|
|
4655
|
+
const drainBufferedEvents = () => {
|
|
4656
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4657
|
+
let lastIndex = 0;
|
|
4658
|
+
let match;
|
|
4659
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4660
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4661
|
+
lastIndex = match.index + match[0].length;
|
|
4662
|
+
if (block.length > 0) inspectEventBlock(block);
|
|
4663
|
+
}
|
|
4664
|
+
if (lastIndex > 0) buffer = buffer.slice(lastIndex);
|
|
4536
4665
|
};
|
|
4666
|
+
try {
|
|
4667
|
+
while (true) {
|
|
4668
|
+
const { value, done } = await reader.read();
|
|
4669
|
+
if (done) break;
|
|
4670
|
+
if (value && value.byteLength > 0) {
|
|
4671
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4672
|
+
drainBufferedEvents();
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
buffer += decoder.decode();
|
|
4676
|
+
drainBufferedEvents();
|
|
4677
|
+
if (buffer.length > 0) {
|
|
4678
|
+
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4679
|
+
if (tail.length > 0) inspectEventBlock(tail);
|
|
4680
|
+
buffer = "";
|
|
4681
|
+
}
|
|
4682
|
+
} catch {
|
|
4683
|
+
} finally {
|
|
4684
|
+
try {
|
|
4685
|
+
reader.releaseLock();
|
|
4686
|
+
} catch {
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
return {
|
|
4690
|
+
capturedUsage: capturedUsage ?? void 0,
|
|
4691
|
+
capturedResponseId
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
function createSSEParserTransform(onUsage, onResponseId) {
|
|
4695
|
+
let buffer = "";
|
|
4696
|
+
const decoder = new StringDecoder("utf8");
|
|
4697
|
+
let capturedUsage = null;
|
|
4698
|
+
let responseIdCaptured = false;
|
|
4537
4699
|
const inspectDataPayload = (jsonText) => {
|
|
4538
|
-
if (responseIdCaptured && capturedUsage
|
|
4700
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4539
4701
|
return;
|
|
4540
4702
|
}
|
|
4541
4703
|
const trimmed = jsonText.trim();
|
|
@@ -4562,7 +4724,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4562
4724
|
}
|
|
4563
4725
|
};
|
|
4564
4726
|
const inspectEventBlock = (eventBlock) => {
|
|
4565
|
-
if (responseIdCaptured && capturedUsage
|
|
4727
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4566
4728
|
return;
|
|
4567
4729
|
}
|
|
4568
4730
|
const lines = eventBlock.split(/\r?\n/);
|
|
@@ -4578,32 +4740,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4578
4740
|
const payload = dataParts.join("\n");
|
|
4579
4741
|
inspectDataPayload(payload);
|
|
4580
4742
|
};
|
|
4581
|
-
const
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4743
|
+
const processBufferedEvents = () => {
|
|
4744
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4745
|
+
let lastIndex = 0;
|
|
4746
|
+
let match;
|
|
4747
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4748
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4749
|
+
lastIndex = match.index + match[0].length;
|
|
4750
|
+
if (block.length > 0) {
|
|
4751
|
+
inspectEventBlock(block);
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
if (lastIndex > 0) {
|
|
4755
|
+
buffer = buffer.slice(lastIndex);
|
|
4756
|
+
}
|
|
4585
4757
|
};
|
|
4586
4758
|
return new Transform({
|
|
4587
4759
|
transform(chunk, _encoding, callback) {
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
let match;
|
|
4592
|
-
while ((match = terminator.exec(buffer)) !== null) {
|
|
4593
|
-
const block = buffer.slice(lastIndex, match.index);
|
|
4594
|
-
lastIndex = match.index + match[0].length;
|
|
4595
|
-
emitEventBlock(this, block);
|
|
4596
|
-
}
|
|
4597
|
-
if (lastIndex > 0) {
|
|
4598
|
-
buffer = buffer.slice(lastIndex);
|
|
4599
|
-
}
|
|
4760
|
+
this.push(chunk);
|
|
4761
|
+
buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4762
|
+
processBufferedEvents();
|
|
4600
4763
|
callback();
|
|
4601
4764
|
},
|
|
4602
4765
|
flush(callback) {
|
|
4766
|
+
buffer += decoder.end();
|
|
4767
|
+
processBufferedEvents();
|
|
4603
4768
|
if (buffer.length > 0) {
|
|
4604
4769
|
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4605
4770
|
if (tail.length > 0) {
|
|
4606
|
-
|
|
4771
|
+
inspectEventBlock(tail);
|
|
4607
4772
|
}
|
|
4608
4773
|
buffer = "";
|
|
4609
4774
|
}
|
|
@@ -4611,6 +4776,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4611
4776
|
}
|
|
4612
4777
|
});
|
|
4613
4778
|
}
|
|
4779
|
+
|
|
4780
|
+
// client/RoutstrClient.ts
|
|
4614
4781
|
var TOPUP_MARGIN = 1.2;
|
|
4615
4782
|
var RoutstrClient = class {
|
|
4616
4783
|
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
@@ -4710,31 +4877,12 @@ var RoutstrClient = class {
|
|
|
4710
4877
|
*/
|
|
4711
4878
|
async routeRequest(params) {
|
|
4712
4879
|
const prepared = await this._prepareRoutedRequest(params);
|
|
4713
|
-
const
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
modelId: prepared.modelId,
|
|
4720
|
-
usage: prepared.capturedUsage,
|
|
4721
|
-
requestId: prepared.capturedResponseId,
|
|
4722
|
-
clientApiKey: prepared.clientApiKey
|
|
4723
|
-
});
|
|
4724
|
-
prepared.response.satsSpent = satsSpent;
|
|
4725
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
4726
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
4727
|
-
return prepared.response;
|
|
4728
|
-
}
|
|
4729
|
-
async routeRequestToNodeResponse(params) {
|
|
4730
|
-
const { res } = params;
|
|
4731
|
-
const prepared = await this._prepareRoutedRequest(params);
|
|
4732
|
-
res.statusCode = prepared.response.status;
|
|
4733
|
-
prepared.response.headers.forEach((value, key) => {
|
|
4734
|
-
res.setHeader(key, value);
|
|
4735
|
-
});
|
|
4736
|
-
const body = prepared.response.body;
|
|
4737
|
-
if (!body) {
|
|
4880
|
+
const contentType = prepared.response.headers.get("content-type") || "";
|
|
4881
|
+
const isSSE = contentType.includes("text/event-stream");
|
|
4882
|
+
const runFinalize = async () => {
|
|
4883
|
+
const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
|
|
4884
|
+
const usage = capturedUsage ?? prepared.capturedUsage;
|
|
4885
|
+
const requestId = capturedResponseId ?? prepared.capturedResponseId;
|
|
4738
4886
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4739
4887
|
token: prepared.tokenUsed,
|
|
4740
4888
|
baseUrl: prepared.baseUrlUsed,
|
|
@@ -4742,51 +4890,25 @@ var RoutstrClient = class {
|
|
|
4742
4890
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4743
4891
|
response: prepared.response,
|
|
4744
4892
|
modelId: prepared.modelId,
|
|
4745
|
-
usage
|
|
4746
|
-
requestId
|
|
4893
|
+
usage,
|
|
4894
|
+
requestId,
|
|
4747
4895
|
clientApiKey: prepared.clientApiKey
|
|
4748
4896
|
});
|
|
4749
4897
|
prepared.response.satsSpent = satsSpent;
|
|
4750
|
-
|
|
4751
|
-
|
|
4898
|
+
prepared.response.usage = usage;
|
|
4899
|
+
prepared.response.requestId = requestId;
|
|
4900
|
+
return satsSpent;
|
|
4901
|
+
};
|
|
4902
|
+
if (isSSE) {
|
|
4903
|
+
const finalizePromise = runFinalize().catch((error) => {
|
|
4904
|
+
this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
|
|
4905
|
+
return 0;
|
|
4906
|
+
});
|
|
4907
|
+
prepared.response.finalize = () => finalizePromise;
|
|
4908
|
+
return prepared.response;
|
|
4752
4909
|
}
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
let settled = false;
|
|
4756
|
-
const finish = async () => {
|
|
4757
|
-
if (settled) return;
|
|
4758
|
-
settled = true;
|
|
4759
|
-
try {
|
|
4760
|
-
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4761
|
-
token: prepared.tokenUsed,
|
|
4762
|
-
baseUrl: prepared.baseUrlUsed,
|
|
4763
|
-
mintUrl: params.mintUrl,
|
|
4764
|
-
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4765
|
-
response: prepared.response,
|
|
4766
|
-
modelId: prepared.modelId,
|
|
4767
|
-
usage: prepared.capturedUsage,
|
|
4768
|
-
requestId: prepared.capturedResponseId,
|
|
4769
|
-
clientApiKey: prepared.clientApiKey
|
|
4770
|
-
});
|
|
4771
|
-
prepared.response.satsSpent = satsSpent;
|
|
4772
|
-
prepared.response.usage = prepared.capturedUsage;
|
|
4773
|
-
prepared.response.requestId = prepared.capturedResponseId;
|
|
4774
|
-
resolve();
|
|
4775
|
-
} catch (error) {
|
|
4776
|
-
reject(error);
|
|
4777
|
-
}
|
|
4778
|
-
};
|
|
4779
|
-
const fail = (error) => {
|
|
4780
|
-
if (settled) return;
|
|
4781
|
-
settled = true;
|
|
4782
|
-
reject(error);
|
|
4783
|
-
};
|
|
4784
|
-
res.once("finish", finish);
|
|
4785
|
-
res.once("close", finish);
|
|
4786
|
-
res.once("error", fail);
|
|
4787
|
-
nodeReadable.once("error", fail);
|
|
4788
|
-
nodeReadable.pipe(res);
|
|
4789
|
-
});
|
|
4910
|
+
await runFinalize();
|
|
4911
|
+
return prepared.response;
|
|
4790
4912
|
}
|
|
4791
4913
|
async _prepareRoutedRequest(params) {
|
|
4792
4914
|
const {
|
|
@@ -4810,9 +4932,23 @@ var RoutstrClient = class {
|
|
|
4810
4932
|
);
|
|
4811
4933
|
selectedModel = providerModel ?? void 0;
|
|
4812
4934
|
if (selectedModel) {
|
|
4935
|
+
const requestMessages = Array.isArray(
|
|
4936
|
+
body?.messages
|
|
4937
|
+
) ? body.messages : [];
|
|
4938
|
+
const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
|
|
4939
|
+
this._log(
|
|
4940
|
+
"DEBUG",
|
|
4941
|
+
"[RoutstrClient] generic request pricing input",
|
|
4942
|
+
{
|
|
4943
|
+
modelId: selectedModel.id,
|
|
4944
|
+
messageCount: requestMessages.length,
|
|
4945
|
+
maxTokens: requestMaxTokens
|
|
4946
|
+
}
|
|
4947
|
+
);
|
|
4813
4948
|
requiredSats = this.providerManager.getRequiredSatsForModel(
|
|
4814
4949
|
selectedModel,
|
|
4815
|
-
|
|
4950
|
+
requestMessages,
|
|
4951
|
+
requestMaxTokens
|
|
4816
4952
|
);
|
|
4817
4953
|
}
|
|
4818
4954
|
}
|
|
@@ -4849,22 +4985,18 @@ var RoutstrClient = class {
|
|
|
4849
4985
|
let processedResponse = response;
|
|
4850
4986
|
let capturedUsage;
|
|
4851
4987
|
let capturedResponseId;
|
|
4988
|
+
let usagePromise = Promise.resolve({});
|
|
4852
4989
|
if (contentType.includes("text/event-stream") && response.body) {
|
|
4853
|
-
const
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
const logStream = fs.createWriteStream(logFile);
|
|
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
|
-
}
|
|
4990
|
+
const [clientStream, inspectStream] = response.body.tee();
|
|
4991
|
+
processedResponse = new Response(clientStream, {
|
|
4992
|
+
status: response.status,
|
|
4993
|
+
statusText: response.statusText,
|
|
4994
|
+
headers: response.headers
|
|
4866
4995
|
});
|
|
4867
|
-
|
|
4996
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
4997
|
+
processedResponse.token = response.token;
|
|
4998
|
+
usagePromise = inspectSSEWebStream(
|
|
4999
|
+
inspectStream,
|
|
4868
5000
|
(usage) => {
|
|
4869
5001
|
capturedUsage = usage;
|
|
4870
5002
|
processedResponse.usage = usage;
|
|
@@ -4874,17 +5006,7 @@ var RoutstrClient = class {
|
|
|
4874
5006
|
processedResponse.requestId = responseId;
|
|
4875
5007
|
}
|
|
4876
5008
|
);
|
|
4877
|
-
|
|
4878
|
-
const webStream = Readable.toWeb(
|
|
4879
|
-
transformed
|
|
4880
|
-
);
|
|
4881
|
-
processedResponse = new Response(webStream, {
|
|
4882
|
-
status: response.status,
|
|
4883
|
-
statusText: response.statusText,
|
|
4884
|
-
headers: response.headers
|
|
4885
|
-
});
|
|
4886
|
-
processedResponse.baseUrl = response.baseUrl;
|
|
4887
|
-
processedResponse.token = response.token;
|
|
5009
|
+
processedResponse.usagePromise = usagePromise;
|
|
4888
5010
|
}
|
|
4889
5011
|
return {
|
|
4890
5012
|
response: processedResponse,
|
|
@@ -4894,7 +5016,8 @@ var RoutstrClient = class {
|
|
|
4894
5016
|
modelId,
|
|
4895
5017
|
capturedUsage,
|
|
4896
5018
|
capturedResponseId,
|
|
4897
|
-
clientApiKey
|
|
5019
|
+
clientApiKey,
|
|
5020
|
+
usagePromise
|
|
4898
5021
|
};
|
|
4899
5022
|
}
|
|
4900
5023
|
/**
|
|
@@ -5045,9 +5168,9 @@ var RoutstrClient = class {
|
|
|
5045
5168
|
* Make the API request with failover support
|
|
5046
5169
|
*/
|
|
5047
5170
|
async _makeRequest(params) {
|
|
5048
|
-
const { path
|
|
5171
|
+
const { path, method, body, baseUrl, token, headers } = params;
|
|
5049
5172
|
try {
|
|
5050
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
5173
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
5051
5174
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5052
5175
|
const response = await fetch(url, {
|
|
5053
5176
|
method,
|
|
@@ -5097,7 +5220,7 @@ var RoutstrClient = class {
|
|
|
5097
5220
|
*/
|
|
5098
5221
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
5099
5222
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
5100
|
-
const { path
|
|
5223
|
+
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5101
5224
|
let tryNextProvider = false;
|
|
5102
5225
|
const errorMessage = responseBody;
|
|
5103
5226
|
this._log(
|
|
@@ -5169,11 +5292,12 @@ var RoutstrClient = class {
|
|
|
5169
5292
|
baseUrl
|
|
5170
5293
|
);
|
|
5171
5294
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
5172
|
-
const
|
|
5173
|
-
|
|
5295
|
+
const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1e3 : currentBalanceInfo.reserved ?? 0;
|
|
5296
|
+
const shortfall = Math.max(0, params.requiredSats - currentBalance + reservedBalance);
|
|
5297
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
5174
5298
|
this._log(
|
|
5175
5299
|
"DEBUG",
|
|
5176
|
-
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
5300
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`
|
|
5177
5301
|
);
|
|
5178
5302
|
} catch (e) {
|
|
5179
5303
|
this._log(
|
|
@@ -5318,10 +5442,10 @@ var RoutstrClient = class {
|
|
|
5318
5442
|
tryNextProvider = true;
|
|
5319
5443
|
}
|
|
5320
5444
|
}
|
|
5321
|
-
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
5445
|
+
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 429 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
5322
5446
|
this._log(
|
|
5323
5447
|
"DEBUG",
|
|
5324
|
-
`[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
5448
|
+
`[RoutstrClient] _handleErrorResponse: Status ${status} (${status === 429 ? "rate limited" : "auth/server error"}), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
5325
5449
|
);
|
|
5326
5450
|
if (this.mode === "apikeys") {
|
|
5327
5451
|
this._log(
|
|
@@ -5399,7 +5523,7 @@ var RoutstrClient = class {
|
|
|
5399
5523
|
});
|
|
5400
5524
|
return this._makeRequest({
|
|
5401
5525
|
...params,
|
|
5402
|
-
path
|
|
5526
|
+
path,
|
|
5403
5527
|
method,
|
|
5404
5528
|
body,
|
|
5405
5529
|
baseUrl: nextProvider,
|
|
@@ -5807,7 +5931,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5807
5931
|
const {
|
|
5808
5932
|
modelId,
|
|
5809
5933
|
requestBody,
|
|
5810
|
-
path
|
|
5934
|
+
path = "/v1/chat/completions",
|
|
5811
5935
|
headers = {},
|
|
5812
5936
|
forcedProvider,
|
|
5813
5937
|
walletAdapter,
|
|
@@ -5906,17 +6030,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
5906
6030
|
client,
|
|
5907
6031
|
baseUrl,
|
|
5908
6032
|
mintUrl,
|
|
5909
|
-
path
|
|
6033
|
+
path,
|
|
5910
6034
|
headers,
|
|
5911
6035
|
modelId,
|
|
5912
6036
|
proxiedBody
|
|
5913
6037
|
};
|
|
5914
6038
|
}
|
|
5915
6039
|
async function routeRequests(options) {
|
|
5916
|
-
const { client, baseUrl, mintUrl, path
|
|
6040
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5917
6041
|
try {
|
|
5918
6042
|
const response = await client.routeRequest({
|
|
5919
|
-
path
|
|
6043
|
+
path,
|
|
5920
6044
|
method: "POST",
|
|
5921
6045
|
body: proxiedBody,
|
|
5922
6046
|
headers,
|
|
@@ -5935,27 +6059,6 @@ async function routeRequests(options) {
|
|
|
5935
6059
|
throw error;
|
|
5936
6060
|
}
|
|
5937
6061
|
}
|
|
5938
|
-
async function routeRequestsToNodeResponse(options) {
|
|
5939
|
-
const { res } = options;
|
|
5940
|
-
const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5941
|
-
try {
|
|
5942
|
-
await client.routeRequestToNodeResponse({
|
|
5943
|
-
path: path2,
|
|
5944
|
-
method: "POST",
|
|
5945
|
-
body: proxiedBody,
|
|
5946
|
-
headers,
|
|
5947
|
-
baseUrl,
|
|
5948
|
-
mintUrl,
|
|
5949
|
-
modelId,
|
|
5950
|
-
res
|
|
5951
|
-
});
|
|
5952
|
-
} catch (error) {
|
|
5953
|
-
if (error instanceof Error && (error.message.includes("401") || error.message.includes("402") || error.message.includes("403"))) {
|
|
5954
|
-
throw new Error(`Authentication failed: ${error.message}`);
|
|
5955
|
-
}
|
|
5956
|
-
throw error;
|
|
5957
|
-
}
|
|
5958
|
-
}
|
|
5959
6062
|
function extractMaxTokens(requestBody) {
|
|
5960
6063
|
if (!requestBody || typeof requestBody !== "object") {
|
|
5961
6064
|
return void 0;
|
|
@@ -5973,6 +6076,6 @@ function extractStream(requestBody) {
|
|
|
5973
6076
|
return typeof stream === "boolean" ? stream : void 0;
|
|
5974
6077
|
}
|
|
5975
6078
|
|
|
5976
|
-
export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, isOnionUrl, isTorContext, localStorageDriver, normalizeProviderUrl, routeRequests,
|
|
6079
|
+
export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, inspectSSEWebStream, isOnionUrl, isTorContext, localStorageDriver, normalizeProviderUrl, routeRequests, setDefaultUsageTrackingDriver };
|
|
5977
6080
|
//# sourceMappingURL=index.mjs.map
|
|
5978
6081
|
//# sourceMappingURL=index.mjs.map
|