@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.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 {
|
|
@@ -691,10 +689,10 @@ var AuditLogger = class _AuditLogger {
|
|
|
691
689
|
const logLine = JSON.stringify(fullEntry) + "\n";
|
|
692
690
|
if (typeof window === "undefined") {
|
|
693
691
|
try {
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
const logPath =
|
|
697
|
-
|
|
692
|
+
const fs = await import('fs');
|
|
693
|
+
const path = await import('path');
|
|
694
|
+
const logPath = path.join(process.cwd(), "audit.log");
|
|
695
|
+
fs.appendFileSync(logPath, logLine);
|
|
698
696
|
} catch (error) {
|
|
699
697
|
console.error("[AuditLogger] Failed to write to file:", error);
|
|
700
698
|
}
|
|
@@ -1273,7 +1271,7 @@ var CashuSpender = class {
|
|
|
1273
1271
|
};
|
|
1274
1272
|
|
|
1275
1273
|
// wallet/BalanceManager.ts
|
|
1276
|
-
var BalanceManager = class {
|
|
1274
|
+
var BalanceManager = class _BalanceManager {
|
|
1277
1275
|
constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
|
|
1278
1276
|
this.walletAdapter = walletAdapter;
|
|
1279
1277
|
this.storageAdapter = storageAdapter;
|
|
@@ -1290,6 +1288,47 @@ var BalanceManager = class {
|
|
|
1290
1288
|
}
|
|
1291
1289
|
}
|
|
1292
1290
|
cashuSpender;
|
|
1291
|
+
/** In-memory guard for per-provider wallet mutations (topup / refund) */
|
|
1292
|
+
providerWalletOps = /* @__PURE__ */ new Map();
|
|
1293
|
+
/** Cooldown (ms) between opposite operations on the same provider */
|
|
1294
|
+
static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
|
|
1295
|
+
/**
|
|
1296
|
+
* Check whether a wallet operation (topup/refund) may run for a provider.
|
|
1297
|
+
* Returns the reason when blocked.
|
|
1298
|
+
*/
|
|
1299
|
+
_canRunProviderWalletOperation(baseUrl, type) {
|
|
1300
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1301
|
+
if (!existing) {
|
|
1302
|
+
return { allowed: true };
|
|
1303
|
+
}
|
|
1304
|
+
if (existing.type === type) {
|
|
1305
|
+
return { allowed: true };
|
|
1306
|
+
}
|
|
1307
|
+
if (!existing.endTime) {
|
|
1308
|
+
return {
|
|
1309
|
+
allowed: false,
|
|
1310
|
+
reason: `Provider wallet operation locked; ${existing.type} in progress`
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
const elapsed = Date.now() - existing.endTime;
|
|
1314
|
+
if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
|
|
1315
|
+
return {
|
|
1316
|
+
allowed: false,
|
|
1317
|
+
reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
this.providerWalletOps.delete(baseUrl);
|
|
1321
|
+
return { allowed: true };
|
|
1322
|
+
}
|
|
1323
|
+
_beginProviderWalletOperation(baseUrl, type) {
|
|
1324
|
+
this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
|
|
1325
|
+
}
|
|
1326
|
+
_endProviderWalletOperation(baseUrl, type) {
|
|
1327
|
+
const existing = this.providerWalletOps.get(baseUrl);
|
|
1328
|
+
if (existing && existing.type === type) {
|
|
1329
|
+
existing.endTime = Date.now();
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1293
1332
|
async getBalanceState() {
|
|
1294
1333
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
1295
1334
|
const units = this.walletAdapter.getMintUnits();
|
|
@@ -1324,6 +1363,20 @@ var BalanceManager = class {
|
|
|
1324
1363
|
* @returns Refund result
|
|
1325
1364
|
*/
|
|
1326
1365
|
async refundApiKey(options) {
|
|
1366
|
+
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1367
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
|
|
1368
|
+
if (!guard.allowed) {
|
|
1369
|
+
console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
|
|
1370
|
+
return { success: false, message: guard.reason };
|
|
1371
|
+
}
|
|
1372
|
+
this._beginProviderWalletOperation(baseUrl, "refund");
|
|
1373
|
+
try {
|
|
1374
|
+
return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
|
|
1375
|
+
} finally {
|
|
1376
|
+
this._endProviderWalletOperation(baseUrl, "refund");
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
async _refundApiKeyImpl(options) {
|
|
1327
1380
|
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
1328
1381
|
if (!apiKey) {
|
|
1329
1382
|
return { success: false, message: "No API key to refund" };
|
|
@@ -1452,6 +1505,20 @@ var BalanceManager = class {
|
|
|
1452
1505
|
* Top up API key balance with a cashu token
|
|
1453
1506
|
*/
|
|
1454
1507
|
async topUp(options) {
|
|
1508
|
+
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1509
|
+
const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
|
|
1510
|
+
if (!guard.allowed) {
|
|
1511
|
+
console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
|
|
1512
|
+
return { success: false, message: guard.reason };
|
|
1513
|
+
}
|
|
1514
|
+
this._beginProviderWalletOperation(baseUrl, "topup");
|
|
1515
|
+
try {
|
|
1516
|
+
return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
|
|
1517
|
+
} finally {
|
|
1518
|
+
this._endProviderWalletOperation(baseUrl, "topup");
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
async _topUpImpl(options) {
|
|
1455
1522
|
const { mintUrl, baseUrl, amount, token: providedToken } = options;
|
|
1456
1523
|
if (!amount || amount <= 0) {
|
|
1457
1524
|
return { success: false, message: "Invalid top up amount" };
|
|
@@ -1612,7 +1679,7 @@ var BalanceManager = class {
|
|
|
1612
1679
|
p2pkPubkey
|
|
1613
1680
|
);
|
|
1614
1681
|
console.log(
|
|
1615
|
-
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
1682
|
+
`[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
1683
|
);
|
|
1617
1684
|
return {
|
|
1618
1685
|
success: true,
|
|
@@ -2807,7 +2874,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
2807
2874
|
const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
|
|
2808
2875
|
const totalInputTokens = approximateTokens + imageTokens;
|
|
2809
2876
|
const sp = model?.sats_pricing;
|
|
2810
|
-
if (!sp)
|
|
2877
|
+
if (!sp) {
|
|
2878
|
+
return 0;
|
|
2879
|
+
}
|
|
2811
2880
|
if (!sp.max_completion_cost) {
|
|
2812
2881
|
return sp.max_cost ?? 50;
|
|
2813
2882
|
}
|
|
@@ -4516,26 +4585,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
|
|
|
4516
4585
|
var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
|
|
4517
4586
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4518
4587
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4519
|
-
function
|
|
4588
|
+
function mergeUsage(previous, next) {
|
|
4589
|
+
if (!previous) return next;
|
|
4590
|
+
return {
|
|
4591
|
+
promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
|
|
4592
|
+
completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
|
|
4593
|
+
totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
|
|
4594
|
+
cost: next.cost > 0 ? next.cost : previous.cost,
|
|
4595
|
+
satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
|
|
4596
|
+
};
|
|
4597
|
+
}
|
|
4598
|
+
function hasUsageChanged(previous, next) {
|
|
4599
|
+
if (!previous) return true;
|
|
4600
|
+
return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
|
|
4601
|
+
}
|
|
4602
|
+
async function inspectSSEWebStream(stream, onUsage, onResponseId) {
|
|
4603
|
+
const reader = stream.getReader();
|
|
4604
|
+
const decoder = new TextDecoder("utf-8");
|
|
4520
4605
|
let buffer = "";
|
|
4521
4606
|
let capturedUsage = null;
|
|
4607
|
+
let capturedResponseId;
|
|
4522
4608
|
let responseIdCaptured = false;
|
|
4523
|
-
const
|
|
4524
|
-
if (
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4609
|
+
const inspectDataPayload = (jsonText) => {
|
|
4610
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4611
|
+
return;
|
|
4612
|
+
}
|
|
4613
|
+
const trimmed = jsonText.trim();
|
|
4614
|
+
if (!trimmed || trimmed === "[DONE]") return;
|
|
4615
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
|
|
4616
|
+
try {
|
|
4617
|
+
const data = JSON.parse(trimmed);
|
|
4618
|
+
if (!responseIdCaptured) {
|
|
4619
|
+
const responseId = data?.id;
|
|
4620
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
4621
|
+
capturedResponseId = responseId.trim();
|
|
4622
|
+
onResponseId?.(capturedResponseId);
|
|
4623
|
+
responseIdCaptured = true;
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
const usage = extractUsageFromSSEJson(data);
|
|
4627
|
+
if (usage) {
|
|
4628
|
+
const merged = mergeUsage(capturedUsage, usage);
|
|
4629
|
+
if (hasUsageChanged(capturedUsage, merged)) {
|
|
4630
|
+
capturedUsage = merged;
|
|
4631
|
+
onUsage(merged);
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
} catch {
|
|
4635
|
+
}
|
|
4532
4636
|
};
|
|
4533
|
-
const
|
|
4534
|
-
if (
|
|
4535
|
-
|
|
4637
|
+
const inspectEventBlock = (eventBlock) => {
|
|
4638
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4639
|
+
return;
|
|
4640
|
+
}
|
|
4641
|
+
const lines = eventBlock.split(/\r?\n/);
|
|
4642
|
+
const dataParts = [];
|
|
4643
|
+
for (const line of lines) {
|
|
4644
|
+
if (!line || line.startsWith(":")) continue;
|
|
4645
|
+
if (line.startsWith("data:")) {
|
|
4646
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
4647
|
+
dataParts.push(value);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
if (dataParts.length === 0) return;
|
|
4651
|
+
inspectDataPayload(dataParts.join("\n"));
|
|
4652
|
+
};
|
|
4653
|
+
const drainBufferedEvents = () => {
|
|
4654
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4655
|
+
let lastIndex = 0;
|
|
4656
|
+
let match;
|
|
4657
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4658
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4659
|
+
lastIndex = match.index + match[0].length;
|
|
4660
|
+
if (block.length > 0) inspectEventBlock(block);
|
|
4661
|
+
}
|
|
4662
|
+
if (lastIndex > 0) buffer = buffer.slice(lastIndex);
|
|
4536
4663
|
};
|
|
4664
|
+
try {
|
|
4665
|
+
while (true) {
|
|
4666
|
+
const { value, done } = await reader.read();
|
|
4667
|
+
if (done) break;
|
|
4668
|
+
if (value && value.byteLength > 0) {
|
|
4669
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4670
|
+
drainBufferedEvents();
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
buffer += decoder.decode();
|
|
4674
|
+
drainBufferedEvents();
|
|
4675
|
+
if (buffer.length > 0) {
|
|
4676
|
+
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4677
|
+
if (tail.length > 0) inspectEventBlock(tail);
|
|
4678
|
+
buffer = "";
|
|
4679
|
+
}
|
|
4680
|
+
} catch {
|
|
4681
|
+
} finally {
|
|
4682
|
+
try {
|
|
4683
|
+
reader.releaseLock();
|
|
4684
|
+
} catch {
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
return {
|
|
4688
|
+
capturedUsage: capturedUsage ?? void 0,
|
|
4689
|
+
capturedResponseId
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
function createSSEParserTransform(onUsage, onResponseId) {
|
|
4693
|
+
let buffer = "";
|
|
4694
|
+
const decoder = new StringDecoder("utf8");
|
|
4695
|
+
let capturedUsage = null;
|
|
4696
|
+
let responseIdCaptured = false;
|
|
4537
4697
|
const inspectDataPayload = (jsonText) => {
|
|
4538
|
-
if (responseIdCaptured && capturedUsage
|
|
4698
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4539
4699
|
return;
|
|
4540
4700
|
}
|
|
4541
4701
|
const trimmed = jsonText.trim();
|
|
@@ -4562,7 +4722,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4562
4722
|
}
|
|
4563
4723
|
};
|
|
4564
4724
|
const inspectEventBlock = (eventBlock) => {
|
|
4565
|
-
if (responseIdCaptured && capturedUsage
|
|
4725
|
+
if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
|
|
4566
4726
|
return;
|
|
4567
4727
|
}
|
|
4568
4728
|
const lines = eventBlock.split(/\r?\n/);
|
|
@@ -4578,32 +4738,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4578
4738
|
const payload = dataParts.join("\n");
|
|
4579
4739
|
inspectDataPayload(payload);
|
|
4580
4740
|
};
|
|
4581
|
-
const
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4741
|
+
const processBufferedEvents = () => {
|
|
4742
|
+
const terminator = /\r?\n\r?\n/g;
|
|
4743
|
+
let lastIndex = 0;
|
|
4744
|
+
let match;
|
|
4745
|
+
while ((match = terminator.exec(buffer)) !== null) {
|
|
4746
|
+
const block = buffer.slice(lastIndex, match.index);
|
|
4747
|
+
lastIndex = match.index + match[0].length;
|
|
4748
|
+
if (block.length > 0) {
|
|
4749
|
+
inspectEventBlock(block);
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
if (lastIndex > 0) {
|
|
4753
|
+
buffer = buffer.slice(lastIndex);
|
|
4754
|
+
}
|
|
4585
4755
|
};
|
|
4586
4756
|
return new Transform({
|
|
4587
4757
|
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
|
-
}
|
|
4758
|
+
this.push(chunk);
|
|
4759
|
+
buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4760
|
+
processBufferedEvents();
|
|
4600
4761
|
callback();
|
|
4601
4762
|
},
|
|
4602
4763
|
flush(callback) {
|
|
4764
|
+
buffer += decoder.end();
|
|
4765
|
+
processBufferedEvents();
|
|
4603
4766
|
if (buffer.length > 0) {
|
|
4604
4767
|
const tail = buffer.replace(/\r?\n+$/, "");
|
|
4605
4768
|
if (tail.length > 0) {
|
|
4606
|
-
|
|
4769
|
+
inspectEventBlock(tail);
|
|
4607
4770
|
}
|
|
4608
4771
|
buffer = "";
|
|
4609
4772
|
}
|
|
@@ -4611,6 +4774,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4611
4774
|
}
|
|
4612
4775
|
});
|
|
4613
4776
|
}
|
|
4777
|
+
|
|
4778
|
+
// client/RoutstrClient.ts
|
|
4614
4779
|
var TOPUP_MARGIN = 1.2;
|
|
4615
4780
|
var RoutstrClient = class {
|
|
4616
4781
|
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
@@ -4710,31 +4875,12 @@ var RoutstrClient = class {
|
|
|
4710
4875
|
*/
|
|
4711
4876
|
async routeRequest(params) {
|
|
4712
4877
|
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) {
|
|
4878
|
+
const contentType = prepared.response.headers.get("content-type") || "";
|
|
4879
|
+
const isSSE = contentType.includes("text/event-stream");
|
|
4880
|
+
const runFinalize = async () => {
|
|
4881
|
+
const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
|
|
4882
|
+
const usage = capturedUsage ?? prepared.capturedUsage;
|
|
4883
|
+
const requestId = capturedResponseId ?? prepared.capturedResponseId;
|
|
4738
4884
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4739
4885
|
token: prepared.tokenUsed,
|
|
4740
4886
|
baseUrl: prepared.baseUrlUsed,
|
|
@@ -4742,51 +4888,25 @@ var RoutstrClient = class {
|
|
|
4742
4888
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4743
4889
|
response: prepared.response,
|
|
4744
4890
|
modelId: prepared.modelId,
|
|
4745
|
-
usage
|
|
4746
|
-
requestId
|
|
4891
|
+
usage,
|
|
4892
|
+
requestId,
|
|
4747
4893
|
clientApiKey: prepared.clientApiKey
|
|
4748
4894
|
});
|
|
4749
4895
|
prepared.response.satsSpent = satsSpent;
|
|
4750
|
-
|
|
4751
|
-
|
|
4896
|
+
prepared.response.usage = usage;
|
|
4897
|
+
prepared.response.requestId = requestId;
|
|
4898
|
+
return satsSpent;
|
|
4899
|
+
};
|
|
4900
|
+
if (isSSE) {
|
|
4901
|
+
const finalizePromise = runFinalize().catch((error) => {
|
|
4902
|
+
this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
|
|
4903
|
+
return 0;
|
|
4904
|
+
});
|
|
4905
|
+
prepared.response.finalize = () => finalizePromise;
|
|
4906
|
+
return prepared.response;
|
|
4752
4907
|
}
|
|
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
|
-
});
|
|
4908
|
+
await runFinalize();
|
|
4909
|
+
return prepared.response;
|
|
4790
4910
|
}
|
|
4791
4911
|
async _prepareRoutedRequest(params) {
|
|
4792
4912
|
const {
|
|
@@ -4810,9 +4930,23 @@ var RoutstrClient = class {
|
|
|
4810
4930
|
);
|
|
4811
4931
|
selectedModel = providerModel ?? void 0;
|
|
4812
4932
|
if (selectedModel) {
|
|
4933
|
+
const requestMessages = Array.isArray(
|
|
4934
|
+
body?.messages
|
|
4935
|
+
) ? body.messages : [];
|
|
4936
|
+
const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
|
|
4937
|
+
this._log(
|
|
4938
|
+
"DEBUG",
|
|
4939
|
+
"[RoutstrClient] generic request pricing input",
|
|
4940
|
+
{
|
|
4941
|
+
modelId: selectedModel.id,
|
|
4942
|
+
messageCount: requestMessages.length,
|
|
4943
|
+
maxTokens: requestMaxTokens
|
|
4944
|
+
}
|
|
4945
|
+
);
|
|
4813
4946
|
requiredSats = this.providerManager.getRequiredSatsForModel(
|
|
4814
4947
|
selectedModel,
|
|
4815
|
-
|
|
4948
|
+
requestMessages,
|
|
4949
|
+
requestMaxTokens
|
|
4816
4950
|
);
|
|
4817
4951
|
}
|
|
4818
4952
|
}
|
|
@@ -4849,22 +4983,18 @@ var RoutstrClient = class {
|
|
|
4849
4983
|
let processedResponse = response;
|
|
4850
4984
|
let capturedUsage;
|
|
4851
4985
|
let capturedResponseId;
|
|
4986
|
+
let usagePromise = Promise.resolve({});
|
|
4852
4987
|
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
|
-
}
|
|
4988
|
+
const [clientStream, inspectStream] = response.body.tee();
|
|
4989
|
+
processedResponse = new Response(clientStream, {
|
|
4990
|
+
status: response.status,
|
|
4991
|
+
statusText: response.statusText,
|
|
4992
|
+
headers: response.headers
|
|
4866
4993
|
});
|
|
4867
|
-
|
|
4994
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
4995
|
+
processedResponse.token = response.token;
|
|
4996
|
+
usagePromise = inspectSSEWebStream(
|
|
4997
|
+
inspectStream,
|
|
4868
4998
|
(usage) => {
|
|
4869
4999
|
capturedUsage = usage;
|
|
4870
5000
|
processedResponse.usage = usage;
|
|
@@ -4874,17 +5004,7 @@ var RoutstrClient = class {
|
|
|
4874
5004
|
processedResponse.requestId = responseId;
|
|
4875
5005
|
}
|
|
4876
5006
|
);
|
|
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;
|
|
5007
|
+
processedResponse.usagePromise = usagePromise;
|
|
4888
5008
|
}
|
|
4889
5009
|
return {
|
|
4890
5010
|
response: processedResponse,
|
|
@@ -4894,7 +5014,8 @@ var RoutstrClient = class {
|
|
|
4894
5014
|
modelId,
|
|
4895
5015
|
capturedUsage,
|
|
4896
5016
|
capturedResponseId,
|
|
4897
|
-
clientApiKey
|
|
5017
|
+
clientApiKey,
|
|
5018
|
+
usagePromise
|
|
4898
5019
|
};
|
|
4899
5020
|
}
|
|
4900
5021
|
/**
|
|
@@ -5045,9 +5166,9 @@ var RoutstrClient = class {
|
|
|
5045
5166
|
* Make the API request with failover support
|
|
5046
5167
|
*/
|
|
5047
5168
|
async _makeRequest(params) {
|
|
5048
|
-
const { path
|
|
5169
|
+
const { path, method, body, baseUrl, token, headers } = params;
|
|
5049
5170
|
try {
|
|
5050
|
-
const url = `${baseUrl.replace(/\/$/, "")}${
|
|
5171
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
5051
5172
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5052
5173
|
const response = await fetch(url, {
|
|
5053
5174
|
method,
|
|
@@ -5097,7 +5218,7 @@ var RoutstrClient = class {
|
|
|
5097
5218
|
*/
|
|
5098
5219
|
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
5099
5220
|
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
5100
|
-
const { path
|
|
5221
|
+
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
5101
5222
|
let tryNextProvider = false;
|
|
5102
5223
|
const errorMessage = responseBody;
|
|
5103
5224
|
this._log(
|
|
@@ -5170,7 +5291,7 @@ var RoutstrClient = class {
|
|
|
5170
5291
|
);
|
|
5171
5292
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
5172
5293
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
5173
|
-
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
5294
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
5174
5295
|
this._log(
|
|
5175
5296
|
"DEBUG",
|
|
5176
5297
|
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
@@ -5399,7 +5520,7 @@ var RoutstrClient = class {
|
|
|
5399
5520
|
});
|
|
5400
5521
|
return this._makeRequest({
|
|
5401
5522
|
...params,
|
|
5402
|
-
path
|
|
5523
|
+
path,
|
|
5403
5524
|
method,
|
|
5404
5525
|
body,
|
|
5405
5526
|
baseUrl: nextProvider,
|
|
@@ -5807,7 +5928,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5807
5928
|
const {
|
|
5808
5929
|
modelId,
|
|
5809
5930
|
requestBody,
|
|
5810
|
-
path
|
|
5931
|
+
path = "/v1/chat/completions",
|
|
5811
5932
|
headers = {},
|
|
5812
5933
|
forcedProvider,
|
|
5813
5934
|
walletAdapter,
|
|
@@ -5906,17 +6027,17 @@ async function resolveRouteRequestContext(options) {
|
|
|
5906
6027
|
client,
|
|
5907
6028
|
baseUrl,
|
|
5908
6029
|
mintUrl,
|
|
5909
|
-
path
|
|
6030
|
+
path,
|
|
5910
6031
|
headers,
|
|
5911
6032
|
modelId,
|
|
5912
6033
|
proxiedBody
|
|
5913
6034
|
};
|
|
5914
6035
|
}
|
|
5915
6036
|
async function routeRequests(options) {
|
|
5916
|
-
const { client, baseUrl, mintUrl, path
|
|
6037
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5917
6038
|
try {
|
|
5918
6039
|
const response = await client.routeRequest({
|
|
5919
|
-
path
|
|
6040
|
+
path,
|
|
5920
6041
|
method: "POST",
|
|
5921
6042
|
body: proxiedBody,
|
|
5922
6043
|
headers,
|
|
@@ -5935,27 +6056,6 @@ async function routeRequests(options) {
|
|
|
5935
6056
|
throw error;
|
|
5936
6057
|
}
|
|
5937
6058
|
}
|
|
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
6059
|
function extractMaxTokens(requestBody) {
|
|
5960
6060
|
if (!requestBody || typeof requestBody !== "object") {
|
|
5961
6061
|
return void 0;
|
|
@@ -5973,6 +6073,6 @@ function extractStream(requestBody) {
|
|
|
5973
6073
|
return typeof stream === "boolean" ? stream : void 0;
|
|
5974
6074
|
}
|
|
5975
6075
|
|
|
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,
|
|
6076
|
+
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
6077
|
//# sourceMappingURL=index.mjs.map
|
|
5978
6078
|
//# sourceMappingURL=index.mjs.map
|