@routstr/sdk 0.2.12 → 0.3.0
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 +262 -162
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +263 -142
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -10
- package/dist/index.d.ts +3 -10
- package/dist/index.js +266 -188
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +267 -167
- 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 {
|
|
@@ -715,10 +691,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
715
691
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
716
692
|
if (typeof window === "undefined") {
|
|
717
693
|
try {
|
|
718
|
-
const
|
|
719
|
-
const
|
|
720
|
-
const logPath =
|
|
721
|
-
|
|
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);
|
|
722
698
|
} catch (error) {
|
|
723
699
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
724
700
|
}
|
|
@@ -1297,7 +1273,7 @@ var CashuSpender = class {
|
|
|
1297
1273
|
};
|
|
1298
1274
|
|
|
1299
1275
|
// wallet/BalanceManager.ts
|
|
1300
|
-
var BalanceManager = class {
|
|
1276
|
+
var BalanceManager = class _BalanceManager {
|
|
1301
1277
|
constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
|
|
1302
1278
|
this.walletAdapter = walletAdapter;
|
|
1303
1279
|
this.storageAdapter = storageAdapter;
|
|
@@ -1314,6 +1290,47 @@ var BalanceManager = class {
|
|
|
1314
1290
|
}
|
|
1315
1291
|
}
|
|
1316
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
|
+
}
|
|
1317
1334
|
async getBalanceState() {
|
|
1318
1335
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
1319
1336
|
const units = this.walletAdapter.getMintUnits();
|
|
@@ -1348,6 +1365,20 @@ var BalanceManager = class {
|
|
|
1348
1365
|
* @returns Refund result
|
|
1349
1366
|
*/
|
|
1350
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) {
|
|
1351
1382
|
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1352
1383
|
if (!apiKey) {
|
|
1353
1384
|
return { success: false, message: "No API key to refund" };
|
|
@@ -1476,6 +1507,20 @@ var BalanceManager = class {
|
|
|
1476
1507
|
* Top up API key balance with a cashu token
|
|
1477
1508
|
*/
|
|
1478
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) {
|
|
1479
1524
|
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1480
1525
|
if (!amount || amount <= 0) {
|
|
1481
1526
|
return { success: false, message: "Invalid top up amount" };
|
|
@@ -1636,7 +1681,7 @@ var BalanceManager = class {
|
|
|
1636
1681
|
p2pkPubkey
|
|
1637
1682
|
);
|
|
1638
1683
|
console.log(
|
|
1639
|
-
`[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])])))}`
|
|
1640
1685
|
);
|
|
1641
1686
|
return {
|
|
1642
1687
|
success: true,
|
|
@@ -2831,7 +2876,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
2831
2876
|
const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
|
|
2832
2877
|
const totalInputTokens = approximateTokens + imageTokens;
|
|
2833
2878
|
const sp = model?.sats_pricing;
|
|
2834
|
-
if (!sp)
|
|
2879
|
+
if (!sp) {
|
|
2880
|
+
return 0;
|
|
2881
|
+
}
|
|
2835
2882
|
if (!sp.max_completion_cost) {
|
|
2836
2883
|
return sp.max_cost ?? 50;
|
|
2837
2884
|
}
|
|
@@ -4540,26 +4587,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
|
|
|
4540
4587
|
var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
|
|
4541
4588
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4542
4589
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4543
|
-
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");
|
|
4544
4607
|
let buffer = "";
|
|
4545
4608
|
let capturedUsage = null;
|
|
4609
|
+
let capturedResponseId;
|
|
4546
4610
|
let responseIdCaptured = false;
|
|
4547
|
-
const
|
|
4548
|
-
if (
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
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
|
+
}
|
|
4638
|
+
};
|
|
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"));
|
|
4556
4654
|
};
|
|
4557
|
-
const
|
|
4558
|
-
|
|
4559
|
-
|
|
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);
|
|
4560
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 string_decoder.StringDecoder("utf8");
|
|
4697
|
+
let capturedUsage = null;
|
|
4698
|
+
let responseIdCaptured = false;
|
|
4561
4699
|
const inspectDataPayload = (jsonText) => {
|
|
4562
|
-
if (responseIdCaptured && capturedUsage
|
|
4700
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4563
4701
|
return;
|
|
4564
4702
|
}
|
|
4565
4703
|
const trimmed = jsonText.trim();
|
|
@@ -4586,7 +4724,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4586
4724
|
}
|
|
4587
4725
|
};
|
|
4588
4726
|
const inspectEventBlock = (eventBlock) => {
|
|
4589
|
-
if (responseIdCaptured && capturedUsage
|
|
4727
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4590
4728
|
return;
|
|
4591
4729
|
}
|
|
4592
4730
|
const lines = eventBlock.split(/\r?\n/);
|
|
@@ -4602,32 +4740,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4602
4740
|
const payload = dataParts.join("\n");
|
|
4603
4741
|
inspectDataPayload(payload);
|
|
4604
4742
|
};
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
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
|
+
}
|
|
4609
4757
|
};
|
|
4610
4758
|
return new stream.Transform({
|
|
4611
4759
|
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
|
-
}
|
|
4760
|
+
this.push(chunk);
|
|
4761
|
+
buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4762
|
+
processBufferedEvents();
|
|
4624
4763
|
callback();
|
|
4625
4764
|
},
|
|
4626
4765
|
flush(callback) {
|
|
4766
|
+
buffer += decoder.end();
|
|
4767
|
+
processBufferedEvents();
|
|
4627
4768
|
if (buffer.length > 0) {
|
|
4628
4769
|
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4629
4770
|
if (tail.length > 0) {
|
|
4630
|
-
|
|
4771
|
+
inspectEventBlock(tail);
|
|
4631
4772
|
}
|
|
4632
4773
|
buffer = "";
|
|
4633
4774
|
}
|
|
@@ -4635,6 +4776,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4635
4776
|
}
|
|
4636
4777
|
});
|
|
4637
4778
|
}
|
|
4779
|
+
|
|
4780
|
+
// client/RoutstrClient.ts
|
|
4638
4781
|
var TOPUP_MARGIN = 1.2;
|
|
4639
4782
|
var RoutstrClient = class {
|
|
4640
4783
|
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
@@ -4734,31 +4877,12 @@ var RoutstrClient = class {
|
|
|
4734
4877
|
*/
|
|
4735
4878
|
async routeRequest(params) {
|
|
4736
4879
|
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) {
|
|
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;
|
|
4762
4886
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4763
4887
|
token: prepared.tokenUsed,
|
|
4764
4888
|
baseUrl: prepared.baseUrlUsed,
|
|
@@ -4766,51 +4890,25 @@ var RoutstrClient = class {
|
|
|
4766
4890
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4767
4891
|
response: prepared.response,
|
|
4768
4892
|
modelId: prepared.modelId,
|
|
4769
|
-
usage
|
|
4770
|
-
requestId
|
|
4893
|
+
usage,
|
|
4894
|
+
requestId,
|
|
4771
4895
|
clientApiKey: prepared.clientApiKey
|
|
4772
4896
|
});
|
|
4773
4897
|
prepared.response.satsSpent = satsSpent;
|
|
4774
|
-
|
|
4775
|
-
|
|
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;
|
|
4776
4909
|
}
|
|
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
|
-
});
|
|
4910
|
+
await runFinalize();
|
|
4911
|
+
return prepared.response;
|
|
4814
4912
|
}
|
|
4815
4913
|
async _prepareRoutedRequest(params) {
|
|
4816
4914
|
const {
|
|
@@ -4834,9 +4932,23 @@ var RoutstrClient = class {
|
|
|
4834
4932
|
);
|
|
4835
4933
|
selectedModel = providerModel ?? void 0;
|
|
4836
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
|
+
);
|
|
4837
4948
|
requiredSats = this.providerManager.getRequiredSatsForModel(
|
|
4838
4949
|
selectedModel,
|
|
4839
|
-
|
|
4950
|
+
requestMessages,
|
|
4951
|
+
requestMaxTokens
|
|
4840
4952
|
);
|
|
4841
4953
|
}
|
|
4842
4954
|
}
|
|
@@ -4873,22 +4985,18 @@ var RoutstrClient = class {
|
|
|
4873
4985
|
let processedResponse = response;
|
|
4874
4986
|
let capturedUsage;
|
|
4875
4987
|
let capturedResponseId;
|
|
4988
|
+
let usagePromise = Promise.resolve({});
|
|
4876
4989
|
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
|
-
}
|
|
4990
|
+
const [clientStream, inspectStream] = response.body.tee();
|
|
4991
|
+
processedResponse = new Response(clientStream, {
|
|
4992
|
+
status: response.status,
|
|
4993
|
+
statusText: response.statusText,
|
|
4994
|
+
headers: response.headers
|
|
4890
4995
|
});
|
|
4891
|
-
|
|
4996
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
4997
|
+
processedResponse.token = response.token;
|
|
4998
|
+
usagePromise = inspectSSEWebStream(
|
|
4999
|
+
inspectStream,
|
|
4892
5000
|
(usage) => {
|
|
4893
5001
|
capturedUsage = usage;
|
|
4894
5002
|
processedResponse.usage = usage;
|
|
@@ -4898,17 +5006,7 @@ var RoutstrClient = class {
|
|
|
4898
5006
|
processedResponse.requestId = responseId;
|
|
4899
5007
|
}
|
|
4900
5008
|
);
|
|
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;
|
|
5009
|
+
processedResponse.usagePromise = usagePromise;
|
|
4912
5010
|
}
|
|
4913
5011
|
return {
|
|
4914
5012
|
response: processedResponse,
|
|
@@ -4918,7 +5016,8 @@ var RoutstrClient = class {
|
|
|
4918
5016
|
modelId,
|
|
4919
5017
|
capturedUsage,
|
|
4920
5018
|
capturedResponseId,
|
|
4921
|
-
clientApiKey
|
|
5019
|
+
clientApiKey,
|
|
5020
|
+
usagePromise
|
|
4922
5021
|
};
|
|
4923
5022
|
}
|
|
4924
5023
|
/**
|
|
@@ -5069,9 +5168,9 @@ var RoutstrClient = class {
|
|
|
5069
5168
|
* Make the API request with failover support
|
|
5070
5169
|
*/
|
|
5071
5170
|
async _makeRequest(params) {
|
|
5072
|
-
const { path
|
|
5171
|
+
const { path, method, body, baseUrl, token, headers } = params;
|
|
5073
5172
|
try {
|
|
5074
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
5173
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
5075
5174
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5076
5175
|
const response = await fetch(url, {
|
|
5077
5176
|
method,
|
|
@@ -5121,7 +5220,7 @@ var RoutstrClient = class {
|
|
|
5121
5220
|
*/
|
|
5122
5221
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
5123
5222
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
5124
|
-
const { path
|
|
5223
|
+
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5125
5224
|
let tryNextProvider = false;
|
|
5126
5225
|
const errorMessage = responseBody;
|
|
5127
5226
|
this._log(
|
|
@@ -5194,7 +5293,7 @@ var RoutstrClient = class {
|
|
|
5194
5293
|
);
|
|
5195
5294
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
5196
5295
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
5197
|
-
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
5296
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
5198
5297
|
this._log(
|
|
5199
5298
|
"DEBUG",
|
|
5200
5299
|
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
@@ -5423,7 +5522,7 @@ var RoutstrClient = class {
|
|
|
5423
5522
|
});
|
|
5424
5523
|
return this._makeRequest({
|
|
5425
5524
|
...params,
|
|
5426
|
-
path
|
|
5525
|
+
path,
|
|
5427
5526
|
method,
|
|
5428
5527
|
body,
|
|
5429
5528
|
baseUrl: nextProvider,
|
|
@@ -5831,7 +5930,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5831
5930
|
const {
|
|
5832
5931
|
modelId,
|
|
5833
5932
|
requestBody,
|
|
5834
|
-
path
|
|
5933
|
+
path = "/v1/chat/completions",
|
|
5835
5934
|
headers = {},
|
|
5836
5935
|
forcedProvider,
|
|
5837
5936
|
walletAdapter,
|
|
@@ -5930,17 +6029,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
5930
6029
|
client,
|
|
5931
6030
|
baseUrl,
|
|
5932
6031
|
mintUrl,
|
|
5933
|
-
path
|
|
6032
|
+
path,
|
|
5934
6033
|
headers,
|
|
5935
6034
|
modelId,
|
|
5936
6035
|
proxiedBody
|
|
5937
6036
|
};
|
|
5938
6037
|
}
|
|
5939
6038
|
async function routeRequests(options) {
|
|
5940
|
-
const { client, baseUrl, mintUrl, path
|
|
6039
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5941
6040
|
try {
|
|
5942
6041
|
const response = await client.routeRequest({
|
|
5943
|
-
path
|
|
6042
|
+
path,
|
|
5944
6043
|
method: "POST",
|
|
5945
6044
|
body: proxiedBody,
|
|
5946
6045
|
headers,
|
|
@@ -5959,27 +6058,6 @@ async function routeRequests(options) {
|
|
|
5959
6058
|
throw error;
|
|
5960
6059
|
}
|
|
5961
6060
|
}
|
|
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
6061
|
function extractMaxTokens(requestBody) {
|
|
5984
6062
|
if (!requestBody || typeof requestBody !== "object") {
|
|
5985
6063
|
return void 0;
|
|
@@ -6036,12 +6114,12 @@ exports.getDefaultSdkStore = getDefaultSdkStore;
|
|
|
6036
6114
|
exports.getDefaultStorageAdapter = getDefaultStorageAdapter;
|
|
6037
6115
|
exports.getDefaultUsageTrackingDriver = getDefaultUsageTrackingDriver;
|
|
6038
6116
|
exports.getProviderEndpoints = getProviderEndpoints;
|
|
6117
|
+
exports.inspectSSEWebStream = inspectSSEWebStream;
|
|
6039
6118
|
exports.isOnionUrl = isOnionUrl;
|
|
6040
6119
|
exports.isTorContext = isTorContext;
|
|
6041
6120
|
exports.localStorageDriver = localStorageDriver;
|
|
6042
6121
|
exports.normalizeProviderUrl = normalizeProviderUrl;
|
|
6043
6122
|
exports.routeRequests = routeRequests;
|
|
6044
|
-
exports.routeRequestsToNodeResponse = routeRequestsToNodeResponse;
|
|
6045
6123
|
exports.setDefaultUsageTrackingDriver = setDefaultUsageTrackingDriver;
|
|
6046
6124
|
//# sourceMappingURL=index.js.map
|
|
6047
6125
|
//# sourceMappingURL=index.js.map
|