floe-guard 0.17.0 → 0.18.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/adapters/livekit.cjs +398 -32
- package/dist/adapters/livekit.cjs.map +1 -1
- package/dist/adapters/livekit.d.cts +1 -1
- package/dist/adapters/livekit.d.ts +1 -1
- package/dist/adapters/livekit.js +1 -1
- package/dist/adapters/retell.cjs +398 -32
- package/dist/adapters/retell.cjs.map +1 -1
- package/dist/adapters/retell.d.cts +1 -1
- package/dist/adapters/retell.d.ts +1 -1
- package/dist/adapters/retell.js +1 -1
- package/dist/adapters/vapi.cjs +398 -32
- package/dist/adapters/vapi.cjs.map +1 -1
- package/dist/adapters/vapi.d.cts +2 -2
- package/dist/adapters/vapi.d.ts +2 -2
- package/dist/adapters/vapi.js +1 -1
- package/dist/{chunk-DCR7TFYJ.js → chunk-PYN4VDXR.js} +413 -35
- package/dist/chunk-PYN4VDXR.js.map +1 -0
- package/dist/{guard-Bgp6N_TG.d.cts → guard-DR5n03kr.d.cts} +11 -8
- package/dist/{guard-Bgp6N_TG.d.ts → guard-DR5n03kr.d.ts} +11 -8
- package/dist/index.cjs +413 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +148 -22
- package/dist/index.d.ts +148 -22
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cost_map.json +237 -15
- package/dist/chunk-DCR7TFYJ.js.map +0 -1
|
@@ -50,7 +50,7 @@ var UnpriceableLegError = class extends FloeGuardError {
|
|
|
50
50
|
constructor(vendor, mode) {
|
|
51
51
|
const shown = vendor === null ? "None" : `'${vendor}'`;
|
|
52
52
|
super(
|
|
53
|
-
`Cannot price ${mode} vendor ${shown}:
|
|
53
|
+
`Cannot price ${mode} vendor ${shown}: no per-call override, no usable entry in your rate card, and none in the bundled per-unit leg map \u2014 or an entry exists whose mode/unit is wrong for a ${mode} leg, which is refused rather than reinterpreted. The guard cannot enforce a budget on spend it cannot measure. Declare the rate you pay in your rate card (FLOE_RATE_CARD) or pass a per-unit override to enable enforcement.`
|
|
54
54
|
);
|
|
55
55
|
this.name = "UnpriceableVoiceError";
|
|
56
56
|
this.vendor = vendor;
|
|
@@ -76,8 +76,8 @@ function preCall(guard, options = {}) {
|
|
|
76
76
|
// src/cost_map.json
|
|
77
77
|
var cost_map_default = {
|
|
78
78
|
__meta__: {
|
|
79
|
-
generated_at: "2026-09-
|
|
80
|
-
source: "LiteLLM public model prices (bundled snapshot);
|
|
79
|
+
generated_at: "2026-09-21",
|
|
80
|
+
source: "LiteLLM public model prices (bundled snapshot); per-unit leg rates (stt/tts/telephony/sms/ocr/gpu/avatar) hand-curated from vendor list pages, leg rates last retrieved 2026-09-18"
|
|
81
81
|
},
|
|
82
82
|
"chat-latest": {
|
|
83
83
|
input_cost_per_token: 5e-6,
|
|
@@ -621,18 +621,18 @@ var cost_map_default = {
|
|
|
621
621
|
cache_read_input_token_cost: 3e-8
|
|
622
622
|
},
|
|
623
623
|
"gemini-flash-latest": {
|
|
624
|
-
input_cost_per_token:
|
|
625
|
-
output_cost_per_token:
|
|
624
|
+
input_cost_per_token: 75e-8,
|
|
625
|
+
output_cost_per_token: 375e-8,
|
|
626
626
|
litellm_provider: "gemini",
|
|
627
627
|
mode: "chat",
|
|
628
|
-
cache_read_input_token_cost:
|
|
628
|
+
cache_read_input_token_cost: 75e-9
|
|
629
629
|
},
|
|
630
630
|
"gemini-flash-lite-latest": {
|
|
631
|
-
input_cost_per_token:
|
|
632
|
-
output_cost_per_token:
|
|
631
|
+
input_cost_per_token: 3e-7,
|
|
632
|
+
output_cost_per_token: 25e-7,
|
|
633
633
|
litellm_provider: "gemini",
|
|
634
634
|
mode: "chat",
|
|
635
|
-
cache_read_input_token_cost:
|
|
635
|
+
cache_read_input_token_cost: 3e-8
|
|
636
636
|
},
|
|
637
637
|
"gemini-gemma-2-27b-it": {
|
|
638
638
|
input_cost_per_token: 35e-8,
|
|
@@ -659,11 +659,11 @@ var cost_map_default = {
|
|
|
659
659
|
mode: "chat"
|
|
660
660
|
},
|
|
661
661
|
"gemini-pro-latest": {
|
|
662
|
-
input_cost_per_token:
|
|
663
|
-
output_cost_per_token:
|
|
662
|
+
input_cost_per_token: 2e-6,
|
|
663
|
+
output_cost_per_token: 12e-6,
|
|
664
664
|
litellm_provider: "gemini",
|
|
665
665
|
mode: "chat",
|
|
666
|
-
cache_read_input_token_cost:
|
|
666
|
+
cache_read_input_token_cost: 2e-7
|
|
667
667
|
},
|
|
668
668
|
"gemini-robotics-er-1.5-preview": {
|
|
669
669
|
input_cost_per_token: 3e-7,
|
|
@@ -678,11 +678,11 @@ var cost_map_default = {
|
|
|
678
678
|
mode: "chat"
|
|
679
679
|
},
|
|
680
680
|
"gemini-robotics-er-2-preview": {
|
|
681
|
-
input_cost_per_token:
|
|
682
|
-
output_cost_per_token:
|
|
681
|
+
input_cost_per_token: 1e-6,
|
|
682
|
+
output_cost_per_token: 5e-6,
|
|
683
683
|
litellm_provider: "gemini",
|
|
684
684
|
mode: "chat",
|
|
685
|
-
cache_read_input_token_cost:
|
|
685
|
+
cache_read_input_token_cost: 1e-7
|
|
686
686
|
},
|
|
687
687
|
"gemini-robotics-er-2-streaming-preview": {
|
|
688
688
|
input_cost_per_token: 2e-6,
|
|
@@ -1078,6 +1078,13 @@ var cost_map_default = {
|
|
|
1078
1078
|
mode: "chat",
|
|
1079
1079
|
cache_read_input_token_cost: 5e-7
|
|
1080
1080
|
},
|
|
1081
|
+
"gpt-5.5-cyber": {
|
|
1082
|
+
input_cost_per_token: 125e-7,
|
|
1083
|
+
output_cost_per_token: 75e-6,
|
|
1084
|
+
litellm_provider: "openai",
|
|
1085
|
+
mode: "chat",
|
|
1086
|
+
cache_read_input_token_cost: 125e-8
|
|
1087
|
+
},
|
|
1081
1088
|
"gpt-5.6": {
|
|
1082
1089
|
input_cost_per_token: 4e-6,
|
|
1083
1090
|
output_cost_per_token: 2e-5,
|
|
@@ -1162,6 +1169,13 @@ var cost_map_default = {
|
|
|
1162
1169
|
litellm_provider: "openai",
|
|
1163
1170
|
mode: "chat"
|
|
1164
1171
|
},
|
|
1172
|
+
"gpt-rosalind-research": {
|
|
1173
|
+
input_cost_per_token: 5e-6,
|
|
1174
|
+
output_cost_per_token: 25e-6,
|
|
1175
|
+
litellm_provider: "openai",
|
|
1176
|
+
mode: "chat",
|
|
1177
|
+
cache_read_input_token_cost: 5e-7
|
|
1178
|
+
},
|
|
1165
1179
|
"llama-3.1-8b-instant": {
|
|
1166
1180
|
input_cost_per_token: 5e-8,
|
|
1167
1181
|
output_cost_per_token: 8e-8,
|
|
@@ -1287,7 +1301,7 @@ var cost_map_default = {
|
|
|
1287
1301
|
litellm_provider: "openai",
|
|
1288
1302
|
mode: "embedding"
|
|
1289
1303
|
},
|
|
1290
|
-
|
|
1304
|
+
__legs__: {
|
|
1291
1305
|
"deepgram-nova-3": {
|
|
1292
1306
|
mode: "stt",
|
|
1293
1307
|
unit: "usd_per_second",
|
|
@@ -1365,29 +1379,355 @@ var cost_map_default = {
|
|
|
1365
1379
|
unit: "usd_per_minute",
|
|
1366
1380
|
rate: 4e-3,
|
|
1367
1381
|
provider: "twilio"
|
|
1382
|
+
},
|
|
1383
|
+
"twilio-sms-us-outbound": {
|
|
1384
|
+
mode: "sms",
|
|
1385
|
+
unit: "usd_per_segment",
|
|
1386
|
+
rate: 83e-4,
|
|
1387
|
+
provider: "twilio",
|
|
1388
|
+
source_url: "https://www.twilio.com/en-us/sms/pricing/us",
|
|
1389
|
+
retrieved_at: "2026-09-18"
|
|
1390
|
+
},
|
|
1391
|
+
"twilio-sms-us-inbound": {
|
|
1392
|
+
mode: "sms",
|
|
1393
|
+
unit: "usd_per_segment",
|
|
1394
|
+
rate: 83e-4,
|
|
1395
|
+
provider: "twilio",
|
|
1396
|
+
source_url: "https://www.twilio.com/en-us/sms/pricing/us",
|
|
1397
|
+
retrieved_at: "2026-09-18"
|
|
1398
|
+
},
|
|
1399
|
+
"telnyx-sms-us-outbound": {
|
|
1400
|
+
mode: "sms",
|
|
1401
|
+
unit: "usd_per_segment",
|
|
1402
|
+
rate: 4e-3,
|
|
1403
|
+
provider: "telnyx",
|
|
1404
|
+
source_url: "https://telnyx.com/pricing/messaging",
|
|
1405
|
+
retrieved_at: "2026-09-18"
|
|
1406
|
+
},
|
|
1407
|
+
"telnyx-sms-us-inbound": {
|
|
1408
|
+
mode: "sms",
|
|
1409
|
+
unit: "usd_per_segment",
|
|
1410
|
+
rate: 4e-3,
|
|
1411
|
+
provider: "telnyx",
|
|
1412
|
+
source_url: "https://telnyx.com/pricing/messaging",
|
|
1413
|
+
retrieved_at: "2026-09-18"
|
|
1414
|
+
},
|
|
1415
|
+
"modal-b300": {
|
|
1416
|
+
mode: "gpu",
|
|
1417
|
+
unit: "usd_per_gpu_second",
|
|
1418
|
+
rate: 1972e-6,
|
|
1419
|
+
provider: "modal",
|
|
1420
|
+
source_url: "https://modal.com/pricing",
|
|
1421
|
+
retrieved_at: "2026-09-18"
|
|
1422
|
+
},
|
|
1423
|
+
"modal-b200": {
|
|
1424
|
+
mode: "gpu",
|
|
1425
|
+
unit: "usd_per_gpu_second",
|
|
1426
|
+
rate: 1736e-6,
|
|
1427
|
+
provider: "modal",
|
|
1428
|
+
source_url: "https://modal.com/pricing",
|
|
1429
|
+
retrieved_at: "2026-09-18"
|
|
1430
|
+
},
|
|
1431
|
+
"modal-h200-sxm": {
|
|
1432
|
+
mode: "gpu",
|
|
1433
|
+
unit: "usd_per_gpu_second",
|
|
1434
|
+
rate: 1261e-6,
|
|
1435
|
+
provider: "modal",
|
|
1436
|
+
source_url: "https://modal.com/pricing",
|
|
1437
|
+
retrieved_at: "2026-09-18"
|
|
1438
|
+
},
|
|
1439
|
+
"modal-h100-sxm5": {
|
|
1440
|
+
mode: "gpu",
|
|
1441
|
+
unit: "usd_per_gpu_second",
|
|
1442
|
+
rate: 1097e-6,
|
|
1443
|
+
provider: "modal",
|
|
1444
|
+
source_url: "https://modal.com/pricing",
|
|
1445
|
+
retrieved_at: "2026-09-18"
|
|
1446
|
+
},
|
|
1447
|
+
"modal-rtx-pro-6000": {
|
|
1448
|
+
mode: "gpu",
|
|
1449
|
+
unit: "usd_per_gpu_second",
|
|
1450
|
+
rate: 842e-6,
|
|
1451
|
+
provider: "modal",
|
|
1452
|
+
source_url: "https://modal.com/pricing",
|
|
1453
|
+
retrieved_at: "2026-09-18"
|
|
1454
|
+
},
|
|
1455
|
+
"modal-a100-80gb": {
|
|
1456
|
+
mode: "gpu",
|
|
1457
|
+
unit: "usd_per_gpu_second",
|
|
1458
|
+
rate: 694e-6,
|
|
1459
|
+
provider: "modal",
|
|
1460
|
+
source_url: "https://modal.com/pricing",
|
|
1461
|
+
retrieved_at: "2026-09-18"
|
|
1462
|
+
},
|
|
1463
|
+
"modal-a100-40gb": {
|
|
1464
|
+
mode: "gpu",
|
|
1465
|
+
unit: "usd_per_gpu_second",
|
|
1466
|
+
rate: 583e-6,
|
|
1467
|
+
provider: "modal",
|
|
1468
|
+
source_url: "https://modal.com/pricing",
|
|
1469
|
+
retrieved_at: "2026-09-18"
|
|
1470
|
+
},
|
|
1471
|
+
"modal-l40s": {
|
|
1472
|
+
mode: "gpu",
|
|
1473
|
+
unit: "usd_per_gpu_second",
|
|
1474
|
+
rate: 542e-6,
|
|
1475
|
+
provider: "modal",
|
|
1476
|
+
source_url: "https://modal.com/pricing",
|
|
1477
|
+
retrieved_at: "2026-09-18"
|
|
1478
|
+
},
|
|
1479
|
+
"modal-a10": {
|
|
1480
|
+
mode: "gpu",
|
|
1481
|
+
unit: "usd_per_gpu_second",
|
|
1482
|
+
rate: 306e-6,
|
|
1483
|
+
provider: "modal",
|
|
1484
|
+
source_url: "https://modal.com/pricing",
|
|
1485
|
+
retrieved_at: "2026-09-18"
|
|
1486
|
+
},
|
|
1487
|
+
"modal-l4": {
|
|
1488
|
+
mode: "gpu",
|
|
1489
|
+
unit: "usd_per_gpu_second",
|
|
1490
|
+
rate: 222e-6,
|
|
1491
|
+
provider: "modal",
|
|
1492
|
+
source_url: "https://modal.com/pricing",
|
|
1493
|
+
retrieved_at: "2026-09-18"
|
|
1494
|
+
},
|
|
1495
|
+
"modal-t4": {
|
|
1496
|
+
mode: "gpu",
|
|
1497
|
+
unit: "usd_per_gpu_second",
|
|
1498
|
+
rate: 164e-6,
|
|
1499
|
+
provider: "modal",
|
|
1500
|
+
source_url: "https://modal.com/pricing",
|
|
1501
|
+
retrieved_at: "2026-09-18"
|
|
1502
|
+
},
|
|
1503
|
+
"gcp-vision-text-detection": {
|
|
1504
|
+
mode: "ocr",
|
|
1505
|
+
unit: "usd_per_page",
|
|
1506
|
+
rate: 15e-4,
|
|
1507
|
+
provider: "gcp",
|
|
1508
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1509
|
+
retrieved_at: "2026-09-18"
|
|
1510
|
+
},
|
|
1511
|
+
"gcp-vision-document-text-detection": {
|
|
1512
|
+
mode: "ocr",
|
|
1513
|
+
unit: "usd_per_page",
|
|
1514
|
+
rate: 15e-4,
|
|
1515
|
+
provider: "gcp",
|
|
1516
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1517
|
+
retrieved_at: "2026-09-18"
|
|
1518
|
+
},
|
|
1519
|
+
"gcp-vision-text-detection-high-volume": {
|
|
1520
|
+
mode: "ocr",
|
|
1521
|
+
unit: "usd_per_page",
|
|
1522
|
+
rate: 6e-4,
|
|
1523
|
+
provider: "gcp",
|
|
1524
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1525
|
+
retrieved_at: "2026-09-18"
|
|
1526
|
+
},
|
|
1527
|
+
"gcp-vision-document-text-detection-high-volume": {
|
|
1528
|
+
mode: "ocr",
|
|
1529
|
+
unit: "usd_per_page",
|
|
1530
|
+
rate: 6e-4,
|
|
1531
|
+
provider: "gcp",
|
|
1532
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1533
|
+
retrieved_at: "2026-09-18"
|
|
1534
|
+
},
|
|
1535
|
+
"aws-textract-detect-document-text": {
|
|
1536
|
+
mode: "ocr",
|
|
1537
|
+
unit: "usd_per_page",
|
|
1538
|
+
rate: 15e-4,
|
|
1539
|
+
provider: "aws",
|
|
1540
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1541
|
+
retrieved_at: "2026-09-18"
|
|
1542
|
+
},
|
|
1543
|
+
"aws-textract-detect-document-text-high-volume": {
|
|
1544
|
+
mode: "ocr",
|
|
1545
|
+
unit: "usd_per_page",
|
|
1546
|
+
rate: 6e-4,
|
|
1547
|
+
provider: "aws",
|
|
1548
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1549
|
+
retrieved_at: "2026-09-18"
|
|
1550
|
+
},
|
|
1551
|
+
"aws-textract-analyze-forms": {
|
|
1552
|
+
mode: "ocr",
|
|
1553
|
+
unit: "usd_per_page",
|
|
1554
|
+
rate: 0.05,
|
|
1555
|
+
provider: "aws",
|
|
1556
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1557
|
+
retrieved_at: "2026-09-18"
|
|
1558
|
+
},
|
|
1559
|
+
"aws-textract-analyze-tables": {
|
|
1560
|
+
mode: "ocr",
|
|
1561
|
+
unit: "usd_per_page",
|
|
1562
|
+
rate: 0.015,
|
|
1563
|
+
provider: "aws",
|
|
1564
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1565
|
+
retrieved_at: "2026-09-18"
|
|
1566
|
+
},
|
|
1567
|
+
"tavus-cvi-starter": {
|
|
1568
|
+
mode: "avatar",
|
|
1569
|
+
unit: "usd_per_minute",
|
|
1570
|
+
rate: 0.37,
|
|
1571
|
+
provider: "tavus",
|
|
1572
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1573
|
+
retrieved_at: "2026-09-18"
|
|
1574
|
+
},
|
|
1575
|
+
"tavus-cvi-growth": {
|
|
1576
|
+
mode: "avatar",
|
|
1577
|
+
unit: "usd_per_minute",
|
|
1578
|
+
rate: 0.32,
|
|
1579
|
+
provider: "tavus",
|
|
1580
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1581
|
+
retrieved_at: "2026-09-18"
|
|
1582
|
+
},
|
|
1583
|
+
"tavus-cvi-business": {
|
|
1584
|
+
mode: "avatar",
|
|
1585
|
+
unit: "usd_per_minute",
|
|
1586
|
+
rate: 0.26,
|
|
1587
|
+
provider: "tavus",
|
|
1588
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1589
|
+
retrieved_at: "2026-09-18"
|
|
1368
1590
|
}
|
|
1369
1591
|
}
|
|
1370
1592
|
};
|
|
1371
1593
|
|
|
1372
|
-
// src/
|
|
1373
|
-
var VOICE_MAP = cost_map_default["__voice__"] ?? {};
|
|
1594
|
+
// src/leg-units.ts
|
|
1374
1595
|
var unitForMode = {
|
|
1375
1596
|
stt: "usd_per_second",
|
|
1376
1597
|
tts: "usd_per_1k_chars",
|
|
1377
|
-
telephony: "usd_per_minute"
|
|
1598
|
+
telephony: "usd_per_minute",
|
|
1599
|
+
sms: "usd_per_segment",
|
|
1600
|
+
ocr: "usd_per_page",
|
|
1601
|
+
gpu: "usd_per_gpu_second",
|
|
1602
|
+
avatar: "usd_per_minute"
|
|
1378
1603
|
};
|
|
1604
|
+
var LEG_MODES = Object.keys(unitForMode);
|
|
1605
|
+
|
|
1606
|
+
// src/rate-card.ts
|
|
1607
|
+
var RATE_CARD_ENV = "FLOE_RATE_CARD";
|
|
1608
|
+
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set([
|
|
1609
|
+
"mode",
|
|
1610
|
+
"unit",
|
|
1611
|
+
"rate",
|
|
1612
|
+
"provider",
|
|
1613
|
+
"source_url",
|
|
1614
|
+
"retrieved_at",
|
|
1615
|
+
"confirmed"
|
|
1616
|
+
]);
|
|
1617
|
+
var REQUIRED_ENTRY_KEYS = ["mode", "unit", "rate"];
|
|
1618
|
+
var RATE_CARD = {};
|
|
1619
|
+
function envValue(name) {
|
|
1620
|
+
const p = globalThis.process;
|
|
1621
|
+
return p?.env?.[name];
|
|
1622
|
+
}
|
|
1623
|
+
function validateEntry(key, entry) {
|
|
1624
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
1625
|
+
throw new TypeError(`Rate card entry '${key}' must be an object.`);
|
|
1626
|
+
}
|
|
1627
|
+
const e = entry;
|
|
1628
|
+
const extra = Object.keys(e).filter((k) => !ALLOWED_ENTRY_KEYS.has(k)).sort();
|
|
1629
|
+
if (extra.length) {
|
|
1630
|
+
throw new TypeError(
|
|
1631
|
+
`Rate card entry '${key}' has unknown field(s): ${extra.join(", ")}. Allowed: ${[...ALLOWED_ENTRY_KEYS].sort().join(", ")}.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const missing = REQUIRED_ENTRY_KEYS.filter((k) => !(k in e));
|
|
1635
|
+
if (missing.length) {
|
|
1636
|
+
throw new TypeError(`Rate card entry '${key}' is missing required field(s): ${missing.join(", ")}.`);
|
|
1637
|
+
}
|
|
1638
|
+
for (const field of ["mode", "unit"]) {
|
|
1639
|
+
if (typeof e[field] !== "string" || !e[field]) {
|
|
1640
|
+
throw new TypeError(`Rate card entry '${key}': ${field} must be a non-empty string.`);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
const mode = e.mode;
|
|
1644
|
+
if (!LEG_MODES.includes(mode)) {
|
|
1645
|
+
throw new TypeError(
|
|
1646
|
+
`Rate card entry '${key}': unknown mode '${mode}'. Expected one of: ${[...LEG_MODES].sort().join(", ")}.`
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
const expectedUnit = unitForMode[mode];
|
|
1650
|
+
if (e.unit !== expectedUnit) {
|
|
1651
|
+
throw new TypeError(
|
|
1652
|
+
`Rate card entry '${key}': a '${mode}' leg bills in '${expectedUnit}', got '${String(e.unit)}'. The unit is not a label \u2014 pricing multiplies by it, so the wrong one mis-bills by whatever the conversion factor is.`
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
const rate = e.rate;
|
|
1656
|
+
if (typeof rate !== "number" || !Number.isFinite(rate) || rate < 0) {
|
|
1657
|
+
throw new RangeError(
|
|
1658
|
+
`Rate card entry '${key}': rate must be a finite, non-negative number, got ${String(rate)}.`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
if ("confirmed" in e && typeof e.confirmed !== "boolean") {
|
|
1662
|
+
throw new TypeError(`Rate card entry '${key}': confirmed must be true or false.`);
|
|
1663
|
+
}
|
|
1664
|
+
for (const field of ["provider", "source_url", "retrieved_at"]) {
|
|
1665
|
+
if (field in e && typeof e[field] !== "string") {
|
|
1666
|
+
throw new TypeError(`Rate card entry '${key}': ${field} must be a string.`);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function loadRateCard(source) {
|
|
1671
|
+
let input = source;
|
|
1672
|
+
if (input === void 0 || input === null) {
|
|
1673
|
+
const fromEnv = envValue(RATE_CARD_ENV);
|
|
1674
|
+
if (!fromEnv) return {};
|
|
1675
|
+
input = fromEnv;
|
|
1676
|
+
}
|
|
1677
|
+
let raw;
|
|
1678
|
+
if (typeof input === "string") {
|
|
1679
|
+
try {
|
|
1680
|
+
raw = JSON.parse(input);
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
throw new TypeError(
|
|
1683
|
+
`Rate card is not valid JSON: ${e.message}. ${RATE_CARD_ENV} holds inline JSON in this package, not a file path.`
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
} else {
|
|
1687
|
+
raw = input;
|
|
1688
|
+
}
|
|
1689
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1690
|
+
throw new TypeError("Rate card must be a JSON object mapping vendor keys to entries.");
|
|
1691
|
+
}
|
|
1692
|
+
const out = {};
|
|
1693
|
+
for (const [key, entry] of Object.entries(raw)) {
|
|
1694
|
+
validateEntry(key, entry);
|
|
1695
|
+
out[key] = { ...entry };
|
|
1696
|
+
}
|
|
1697
|
+
return out;
|
|
1698
|
+
}
|
|
1699
|
+
function freeze(card) {
|
|
1700
|
+
for (const entry of Object.values(card)) Object.freeze(entry);
|
|
1701
|
+
return Object.freeze(card);
|
|
1702
|
+
}
|
|
1703
|
+
function currentRateCard() {
|
|
1704
|
+
return RATE_CARD;
|
|
1705
|
+
}
|
|
1706
|
+
RATE_CARD = freeze(loadRateCard());
|
|
1707
|
+
|
|
1708
|
+
// src/voice-pricing.ts
|
|
1709
|
+
var rawCostMap = cost_map_default;
|
|
1710
|
+
var VOICE_MAP = rawCostMap["__legs__"] ?? rawCostMap["__voice__"] ?? {};
|
|
1379
1711
|
function finiteNonNegative(value) {
|
|
1380
1712
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1381
1713
|
}
|
|
1382
|
-
function
|
|
1714
|
+
function entryIsUsable(entry, mode) {
|
|
1715
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
1716
|
+
const e = entry;
|
|
1717
|
+
if (e.mode !== mode) return false;
|
|
1718
|
+
if (e.unit !== unitForMode[mode]) return false;
|
|
1719
|
+
return finiteNonNegative(e.rate);
|
|
1720
|
+
}
|
|
1721
|
+
function lookupLegEntry(model, mode) {
|
|
1383
1722
|
if (model === null || model === void 0) return null;
|
|
1723
|
+
const card = currentRateCard();
|
|
1724
|
+
const declared = Object.prototype.hasOwnProperty.call(card, model) ? card[model] : void 0;
|
|
1725
|
+
if (declared && declared.mode === mode) {
|
|
1726
|
+
return entryIsUsable(declared, mode) ? { entry: declared, source: "rate_card" } : null;
|
|
1727
|
+
}
|
|
1384
1728
|
const entry = Object.prototype.hasOwnProperty.call(VOICE_MAP, model) ? VOICE_MAP[model] : void 0;
|
|
1385
|
-
if (
|
|
1386
|
-
|
|
1387
|
-
if (entry.unit !== unitForMode[mode]) return null;
|
|
1388
|
-
const rate = entry.rate;
|
|
1389
|
-
if (!finiteNonNegative(rate)) return null;
|
|
1390
|
-
return rate;
|
|
1729
|
+
if (entryIsUsable(entry, mode)) return { entry, source: "cost_map" };
|
|
1730
|
+
return null;
|
|
1391
1731
|
}
|
|
1392
1732
|
function resolveVoiceRate(model, mode, override) {
|
|
1393
1733
|
if (override !== void 0 && override !== null) {
|
|
@@ -1396,20 +1736,46 @@ function resolveVoiceRate(model, mode, override) {
|
|
|
1396
1736
|
`voice ${mode} override must be a finite, non-negative number, got ${override}`
|
|
1397
1737
|
);
|
|
1398
1738
|
}
|
|
1399
|
-
return {
|
|
1739
|
+
return {
|
|
1740
|
+
mode,
|
|
1741
|
+
unit: unitForMode[mode],
|
|
1742
|
+
rate: override,
|
|
1743
|
+
source: "override",
|
|
1744
|
+
confirmed: true
|
|
1745
|
+
};
|
|
1400
1746
|
}
|
|
1401
|
-
const
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1747
|
+
const found = lookupLegEntry(model, mode);
|
|
1748
|
+
if (found === null) throw new UnpriceableLegError(model ?? null, mode);
|
|
1749
|
+
const { entry, source } = found;
|
|
1750
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
1751
|
+
return {
|
|
1752
|
+
mode,
|
|
1753
|
+
unit: unitForMode[mode],
|
|
1754
|
+
rate: entry.rate,
|
|
1755
|
+
source,
|
|
1756
|
+
...str(entry.provider) !== void 0 ? { provider: str(entry.provider) } : {},
|
|
1757
|
+
...str(entry.source_url) !== void 0 ? { source_url: str(entry.source_url) } : {},
|
|
1758
|
+
...str(entry.retrieved_at) !== void 0 ? { retrieved_at: str(entry.retrieved_at) } : {},
|
|
1759
|
+
// A declared rate is confirmed unless the card says otherwise (an imported or
|
|
1760
|
+
// draft card can mark entries unconfirmed); a bundled list price is
|
|
1761
|
+
// UNCONFIRMED until a human accepts it.
|
|
1762
|
+
confirmed: typeof entry.confirmed === "boolean" ? entry.confirmed : source === "rate_card"
|
|
1763
|
+
};
|
|
1404
1764
|
}
|
|
1405
1765
|
function voiceLegCost(mode, quantity, rate) {
|
|
1406
1766
|
if (!Number.isFinite(quantity)) {
|
|
1407
1767
|
throw new RangeError(`voice ${mode} quantity must be a finite number, got ${quantity}`);
|
|
1408
1768
|
}
|
|
1409
1769
|
const q = Math.max(0, quantity);
|
|
1410
|
-
if (mode === "stt"
|
|
1770
|
+
if (mode === "stt" || // seconds * $/second
|
|
1771
|
+
mode === "telephony" || // minutes * $/minute
|
|
1772
|
+
mode === "sms" || // segments * $/segment
|
|
1773
|
+
mode === "ocr" || // pages * $/page
|
|
1774
|
+
mode === "gpu" || // gpu-seconds * $/gpu-second
|
|
1775
|
+
mode === "avatar") {
|
|
1776
|
+
return q * rate;
|
|
1777
|
+
}
|
|
1411
1778
|
if (mode === "tts") return q / 1e3 * rate;
|
|
1412
|
-
if (mode === "telephony") return q * rate;
|
|
1413
1779
|
throw new RangeError(`unknown voice mode ${mode}`);
|
|
1414
1780
|
}
|
|
1415
1781
|
function priceVoiceLeg(mode, quantity, options = {}) {
|