floe-guard 0.17.0 → 0.18.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/adapters/livekit.cjs +371 -19
- 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 +371 -19
- 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 +371 -19
- 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-IJZ256WH.js} +386 -22
- package/dist/chunk-IJZ256WH.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 +386 -18
- 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 +210 -2
- 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;
|
|
@@ -77,7 +77,7 @@ function preCall(guard, options = {}) {
|
|
|
77
77
|
var cost_map_default = {
|
|
78
78
|
__meta__: {
|
|
79
79
|
generated_at: "2026-09-14",
|
|
80
|
-
source: "LiteLLM public model prices (bundled snapshot);
|
|
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,
|
|
@@ -1287,7 +1287,7 @@ var cost_map_default = {
|
|
|
1287
1287
|
litellm_provider: "openai",
|
|
1288
1288
|
mode: "embedding"
|
|
1289
1289
|
},
|
|
1290
|
-
|
|
1290
|
+
__legs__: {
|
|
1291
1291
|
"deepgram-nova-3": {
|
|
1292
1292
|
mode: "stt",
|
|
1293
1293
|
unit: "usd_per_second",
|
|
@@ -1365,29 +1365,355 @@ var cost_map_default = {
|
|
|
1365
1365
|
unit: "usd_per_minute",
|
|
1366
1366
|
rate: 4e-3,
|
|
1367
1367
|
provider: "twilio"
|
|
1368
|
+
},
|
|
1369
|
+
"twilio-sms-us-outbound": {
|
|
1370
|
+
mode: "sms",
|
|
1371
|
+
unit: "usd_per_segment",
|
|
1372
|
+
rate: 83e-4,
|
|
1373
|
+
provider: "twilio",
|
|
1374
|
+
source_url: "https://www.twilio.com/en-us/sms/pricing/us",
|
|
1375
|
+
retrieved_at: "2026-09-18"
|
|
1376
|
+
},
|
|
1377
|
+
"twilio-sms-us-inbound": {
|
|
1378
|
+
mode: "sms",
|
|
1379
|
+
unit: "usd_per_segment",
|
|
1380
|
+
rate: 83e-4,
|
|
1381
|
+
provider: "twilio",
|
|
1382
|
+
source_url: "https://www.twilio.com/en-us/sms/pricing/us",
|
|
1383
|
+
retrieved_at: "2026-09-18"
|
|
1384
|
+
},
|
|
1385
|
+
"telnyx-sms-us-outbound": {
|
|
1386
|
+
mode: "sms",
|
|
1387
|
+
unit: "usd_per_segment",
|
|
1388
|
+
rate: 4e-3,
|
|
1389
|
+
provider: "telnyx",
|
|
1390
|
+
source_url: "https://telnyx.com/pricing/messaging",
|
|
1391
|
+
retrieved_at: "2026-09-18"
|
|
1392
|
+
},
|
|
1393
|
+
"telnyx-sms-us-inbound": {
|
|
1394
|
+
mode: "sms",
|
|
1395
|
+
unit: "usd_per_segment",
|
|
1396
|
+
rate: 4e-3,
|
|
1397
|
+
provider: "telnyx",
|
|
1398
|
+
source_url: "https://telnyx.com/pricing/messaging",
|
|
1399
|
+
retrieved_at: "2026-09-18"
|
|
1400
|
+
},
|
|
1401
|
+
"modal-b300": {
|
|
1402
|
+
mode: "gpu",
|
|
1403
|
+
unit: "usd_per_gpu_second",
|
|
1404
|
+
rate: 1972e-6,
|
|
1405
|
+
provider: "modal",
|
|
1406
|
+
source_url: "https://modal.com/pricing",
|
|
1407
|
+
retrieved_at: "2026-09-18"
|
|
1408
|
+
},
|
|
1409
|
+
"modal-b200": {
|
|
1410
|
+
mode: "gpu",
|
|
1411
|
+
unit: "usd_per_gpu_second",
|
|
1412
|
+
rate: 1736e-6,
|
|
1413
|
+
provider: "modal",
|
|
1414
|
+
source_url: "https://modal.com/pricing",
|
|
1415
|
+
retrieved_at: "2026-09-18"
|
|
1416
|
+
},
|
|
1417
|
+
"modal-h200-sxm": {
|
|
1418
|
+
mode: "gpu",
|
|
1419
|
+
unit: "usd_per_gpu_second",
|
|
1420
|
+
rate: 1261e-6,
|
|
1421
|
+
provider: "modal",
|
|
1422
|
+
source_url: "https://modal.com/pricing",
|
|
1423
|
+
retrieved_at: "2026-09-18"
|
|
1424
|
+
},
|
|
1425
|
+
"modal-h100-sxm5": {
|
|
1426
|
+
mode: "gpu",
|
|
1427
|
+
unit: "usd_per_gpu_second",
|
|
1428
|
+
rate: 1097e-6,
|
|
1429
|
+
provider: "modal",
|
|
1430
|
+
source_url: "https://modal.com/pricing",
|
|
1431
|
+
retrieved_at: "2026-09-18"
|
|
1432
|
+
},
|
|
1433
|
+
"modal-rtx-pro-6000": {
|
|
1434
|
+
mode: "gpu",
|
|
1435
|
+
unit: "usd_per_gpu_second",
|
|
1436
|
+
rate: 842e-6,
|
|
1437
|
+
provider: "modal",
|
|
1438
|
+
source_url: "https://modal.com/pricing",
|
|
1439
|
+
retrieved_at: "2026-09-18"
|
|
1440
|
+
},
|
|
1441
|
+
"modal-a100-80gb": {
|
|
1442
|
+
mode: "gpu",
|
|
1443
|
+
unit: "usd_per_gpu_second",
|
|
1444
|
+
rate: 694e-6,
|
|
1445
|
+
provider: "modal",
|
|
1446
|
+
source_url: "https://modal.com/pricing",
|
|
1447
|
+
retrieved_at: "2026-09-18"
|
|
1448
|
+
},
|
|
1449
|
+
"modal-a100-40gb": {
|
|
1450
|
+
mode: "gpu",
|
|
1451
|
+
unit: "usd_per_gpu_second",
|
|
1452
|
+
rate: 583e-6,
|
|
1453
|
+
provider: "modal",
|
|
1454
|
+
source_url: "https://modal.com/pricing",
|
|
1455
|
+
retrieved_at: "2026-09-18"
|
|
1456
|
+
},
|
|
1457
|
+
"modal-l40s": {
|
|
1458
|
+
mode: "gpu",
|
|
1459
|
+
unit: "usd_per_gpu_second",
|
|
1460
|
+
rate: 542e-6,
|
|
1461
|
+
provider: "modal",
|
|
1462
|
+
source_url: "https://modal.com/pricing",
|
|
1463
|
+
retrieved_at: "2026-09-18"
|
|
1464
|
+
},
|
|
1465
|
+
"modal-a10": {
|
|
1466
|
+
mode: "gpu",
|
|
1467
|
+
unit: "usd_per_gpu_second",
|
|
1468
|
+
rate: 306e-6,
|
|
1469
|
+
provider: "modal",
|
|
1470
|
+
source_url: "https://modal.com/pricing",
|
|
1471
|
+
retrieved_at: "2026-09-18"
|
|
1472
|
+
},
|
|
1473
|
+
"modal-l4": {
|
|
1474
|
+
mode: "gpu",
|
|
1475
|
+
unit: "usd_per_gpu_second",
|
|
1476
|
+
rate: 222e-6,
|
|
1477
|
+
provider: "modal",
|
|
1478
|
+
source_url: "https://modal.com/pricing",
|
|
1479
|
+
retrieved_at: "2026-09-18"
|
|
1480
|
+
},
|
|
1481
|
+
"modal-t4": {
|
|
1482
|
+
mode: "gpu",
|
|
1483
|
+
unit: "usd_per_gpu_second",
|
|
1484
|
+
rate: 164e-6,
|
|
1485
|
+
provider: "modal",
|
|
1486
|
+
source_url: "https://modal.com/pricing",
|
|
1487
|
+
retrieved_at: "2026-09-18"
|
|
1488
|
+
},
|
|
1489
|
+
"gcp-vision-text-detection": {
|
|
1490
|
+
mode: "ocr",
|
|
1491
|
+
unit: "usd_per_page",
|
|
1492
|
+
rate: 15e-4,
|
|
1493
|
+
provider: "gcp",
|
|
1494
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1495
|
+
retrieved_at: "2026-09-18"
|
|
1496
|
+
},
|
|
1497
|
+
"gcp-vision-document-text-detection": {
|
|
1498
|
+
mode: "ocr",
|
|
1499
|
+
unit: "usd_per_page",
|
|
1500
|
+
rate: 15e-4,
|
|
1501
|
+
provider: "gcp",
|
|
1502
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1503
|
+
retrieved_at: "2026-09-18"
|
|
1504
|
+
},
|
|
1505
|
+
"aws-textract-detect-document-text": {
|
|
1506
|
+
mode: "ocr",
|
|
1507
|
+
unit: "usd_per_page",
|
|
1508
|
+
rate: 15e-4,
|
|
1509
|
+
provider: "aws",
|
|
1510
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1511
|
+
retrieved_at: "2026-09-18"
|
|
1512
|
+
},
|
|
1513
|
+
"aws-textract-analyze-forms": {
|
|
1514
|
+
mode: "ocr",
|
|
1515
|
+
unit: "usd_per_page",
|
|
1516
|
+
rate: 0.05,
|
|
1517
|
+
provider: "aws",
|
|
1518
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1519
|
+
retrieved_at: "2026-09-18"
|
|
1520
|
+
},
|
|
1521
|
+
"aws-textract-analyze-tables": {
|
|
1522
|
+
mode: "ocr",
|
|
1523
|
+
unit: "usd_per_page",
|
|
1524
|
+
rate: 0.015,
|
|
1525
|
+
provider: "aws",
|
|
1526
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1527
|
+
retrieved_at: "2026-09-18"
|
|
1528
|
+
},
|
|
1529
|
+
"tavus-cvi-starter": {
|
|
1530
|
+
mode: "avatar",
|
|
1531
|
+
unit: "usd_per_minute",
|
|
1532
|
+
rate: 0.37,
|
|
1533
|
+
provider: "tavus",
|
|
1534
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1535
|
+
retrieved_at: "2026-09-18"
|
|
1536
|
+
},
|
|
1537
|
+
"gcp-vision-text-detection-high-volume": {
|
|
1538
|
+
mode: "ocr",
|
|
1539
|
+
unit: "usd_per_page",
|
|
1540
|
+
rate: 6e-4,
|
|
1541
|
+
provider: "gcp",
|
|
1542
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1543
|
+
retrieved_at: "2026-09-18"
|
|
1544
|
+
},
|
|
1545
|
+
"gcp-vision-document-text-detection-high-volume": {
|
|
1546
|
+
mode: "ocr",
|
|
1547
|
+
unit: "usd_per_page",
|
|
1548
|
+
rate: 6e-4,
|
|
1549
|
+
provider: "gcp",
|
|
1550
|
+
source_url: "https://cloud.google.com/vision/pricing",
|
|
1551
|
+
retrieved_at: "2026-09-18"
|
|
1552
|
+
},
|
|
1553
|
+
"aws-textract-detect-document-text-high-volume": {
|
|
1554
|
+
mode: "ocr",
|
|
1555
|
+
unit: "usd_per_page",
|
|
1556
|
+
rate: 6e-4,
|
|
1557
|
+
provider: "aws",
|
|
1558
|
+
source_url: "https://aws.amazon.com/textract/pricing/",
|
|
1559
|
+
retrieved_at: "2026-09-18"
|
|
1560
|
+
},
|
|
1561
|
+
"tavus-cvi-growth": {
|
|
1562
|
+
mode: "avatar",
|
|
1563
|
+
unit: "usd_per_minute",
|
|
1564
|
+
rate: 0.32,
|
|
1565
|
+
provider: "tavus",
|
|
1566
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1567
|
+
retrieved_at: "2026-09-18"
|
|
1568
|
+
},
|
|
1569
|
+
"tavus-cvi-business": {
|
|
1570
|
+
mode: "avatar",
|
|
1571
|
+
unit: "usd_per_minute",
|
|
1572
|
+
rate: 0.26,
|
|
1573
|
+
provider: "tavus",
|
|
1574
|
+
source_url: "https://www.tavus.io/pricing",
|
|
1575
|
+
retrieved_at: "2026-09-18"
|
|
1368
1576
|
}
|
|
1369
1577
|
}
|
|
1370
1578
|
};
|
|
1371
1579
|
|
|
1372
|
-
// src/
|
|
1373
|
-
var VOICE_MAP = cost_map_default["__voice__"] ?? {};
|
|
1580
|
+
// src/leg-units.ts
|
|
1374
1581
|
var unitForMode = {
|
|
1375
1582
|
stt: "usd_per_second",
|
|
1376
1583
|
tts: "usd_per_1k_chars",
|
|
1377
|
-
telephony: "usd_per_minute"
|
|
1584
|
+
telephony: "usd_per_minute",
|
|
1585
|
+
sms: "usd_per_segment",
|
|
1586
|
+
ocr: "usd_per_page",
|
|
1587
|
+
gpu: "usd_per_gpu_second",
|
|
1588
|
+
avatar: "usd_per_minute"
|
|
1378
1589
|
};
|
|
1590
|
+
var LEG_MODES = Object.keys(unitForMode);
|
|
1591
|
+
|
|
1592
|
+
// src/rate-card.ts
|
|
1593
|
+
var RATE_CARD_ENV = "FLOE_RATE_CARD";
|
|
1594
|
+
var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set([
|
|
1595
|
+
"mode",
|
|
1596
|
+
"unit",
|
|
1597
|
+
"rate",
|
|
1598
|
+
"provider",
|
|
1599
|
+
"source_url",
|
|
1600
|
+
"retrieved_at",
|
|
1601
|
+
"confirmed"
|
|
1602
|
+
]);
|
|
1603
|
+
var REQUIRED_ENTRY_KEYS = ["mode", "unit", "rate"];
|
|
1604
|
+
var RATE_CARD = {};
|
|
1605
|
+
function envValue(name) {
|
|
1606
|
+
const p = globalThis.process;
|
|
1607
|
+
return p?.env?.[name];
|
|
1608
|
+
}
|
|
1609
|
+
function validateEntry(key, entry) {
|
|
1610
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
1611
|
+
throw new TypeError(`Rate card entry '${key}' must be an object.`);
|
|
1612
|
+
}
|
|
1613
|
+
const e = entry;
|
|
1614
|
+
const extra = Object.keys(e).filter((k) => !ALLOWED_ENTRY_KEYS.has(k)).sort();
|
|
1615
|
+
if (extra.length) {
|
|
1616
|
+
throw new TypeError(
|
|
1617
|
+
`Rate card entry '${key}' has unknown field(s): ${extra.join(", ")}. Allowed: ${[...ALLOWED_ENTRY_KEYS].sort().join(", ")}.`
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
const missing = REQUIRED_ENTRY_KEYS.filter((k) => !(k in e));
|
|
1621
|
+
if (missing.length) {
|
|
1622
|
+
throw new TypeError(`Rate card entry '${key}' is missing required field(s): ${missing.join(", ")}.`);
|
|
1623
|
+
}
|
|
1624
|
+
for (const field of ["mode", "unit"]) {
|
|
1625
|
+
if (typeof e[field] !== "string" || !e[field]) {
|
|
1626
|
+
throw new TypeError(`Rate card entry '${key}': ${field} must be a non-empty string.`);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
const mode = e.mode;
|
|
1630
|
+
if (!LEG_MODES.includes(mode)) {
|
|
1631
|
+
throw new TypeError(
|
|
1632
|
+
`Rate card entry '${key}': unknown mode '${mode}'. Expected one of: ${[...LEG_MODES].sort().join(", ")}.`
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
const expectedUnit = unitForMode[mode];
|
|
1636
|
+
if (e.unit !== expectedUnit) {
|
|
1637
|
+
throw new TypeError(
|
|
1638
|
+
`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.`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
const rate = e.rate;
|
|
1642
|
+
if (typeof rate !== "number" || !Number.isFinite(rate) || rate < 0) {
|
|
1643
|
+
throw new RangeError(
|
|
1644
|
+
`Rate card entry '${key}': rate must be a finite, non-negative number, got ${String(rate)}.`
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
if ("confirmed" in e && typeof e.confirmed !== "boolean") {
|
|
1648
|
+
throw new TypeError(`Rate card entry '${key}': confirmed must be true or false.`);
|
|
1649
|
+
}
|
|
1650
|
+
for (const field of ["provider", "source_url", "retrieved_at"]) {
|
|
1651
|
+
if (field in e && typeof e[field] !== "string") {
|
|
1652
|
+
throw new TypeError(`Rate card entry '${key}': ${field} must be a string.`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
function loadRateCard(source) {
|
|
1657
|
+
let input = source;
|
|
1658
|
+
if (input === void 0 || input === null) {
|
|
1659
|
+
const fromEnv = envValue(RATE_CARD_ENV);
|
|
1660
|
+
if (!fromEnv) return {};
|
|
1661
|
+
input = fromEnv;
|
|
1662
|
+
}
|
|
1663
|
+
let raw;
|
|
1664
|
+
if (typeof input === "string") {
|
|
1665
|
+
try {
|
|
1666
|
+
raw = JSON.parse(input);
|
|
1667
|
+
} catch (e) {
|
|
1668
|
+
throw new TypeError(
|
|
1669
|
+
`Rate card is not valid JSON: ${e.message}. ${RATE_CARD_ENV} holds inline JSON in this package, not a file path.`
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
} else {
|
|
1673
|
+
raw = input;
|
|
1674
|
+
}
|
|
1675
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1676
|
+
throw new TypeError("Rate card must be a JSON object mapping vendor keys to entries.");
|
|
1677
|
+
}
|
|
1678
|
+
const out = {};
|
|
1679
|
+
for (const [key, entry] of Object.entries(raw)) {
|
|
1680
|
+
validateEntry(key, entry);
|
|
1681
|
+
out[key] = { ...entry };
|
|
1682
|
+
}
|
|
1683
|
+
return out;
|
|
1684
|
+
}
|
|
1685
|
+
function freeze(card) {
|
|
1686
|
+
for (const entry of Object.values(card)) Object.freeze(entry);
|
|
1687
|
+
return Object.freeze(card);
|
|
1688
|
+
}
|
|
1689
|
+
function currentRateCard() {
|
|
1690
|
+
return RATE_CARD;
|
|
1691
|
+
}
|
|
1692
|
+
RATE_CARD = freeze(loadRateCard());
|
|
1693
|
+
|
|
1694
|
+
// src/voice-pricing.ts
|
|
1695
|
+
var rawCostMap = cost_map_default;
|
|
1696
|
+
var VOICE_MAP = rawCostMap["__legs__"] ?? rawCostMap["__voice__"] ?? {};
|
|
1379
1697
|
function finiteNonNegative(value) {
|
|
1380
1698
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1381
1699
|
}
|
|
1382
|
-
function
|
|
1700
|
+
function entryIsUsable(entry, mode) {
|
|
1701
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
1702
|
+
const e = entry;
|
|
1703
|
+
if (e.mode !== mode) return false;
|
|
1704
|
+
if (e.unit !== unitForMode[mode]) return false;
|
|
1705
|
+
return finiteNonNegative(e.rate);
|
|
1706
|
+
}
|
|
1707
|
+
function lookupLegEntry(model, mode) {
|
|
1383
1708
|
if (model === null || model === void 0) return null;
|
|
1709
|
+
const card = currentRateCard();
|
|
1710
|
+
const declared = Object.prototype.hasOwnProperty.call(card, model) ? card[model] : void 0;
|
|
1711
|
+
if (declared && declared.mode === mode) {
|
|
1712
|
+
return entryIsUsable(declared, mode) ? { entry: declared, source: "rate_card" } : null;
|
|
1713
|
+
}
|
|
1384
1714
|
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;
|
|
1715
|
+
if (entryIsUsable(entry, mode)) return { entry, source: "cost_map" };
|
|
1716
|
+
return null;
|
|
1391
1717
|
}
|
|
1392
1718
|
function resolveVoiceRate(model, mode, override) {
|
|
1393
1719
|
if (override !== void 0 && override !== null) {
|
|
@@ -1396,20 +1722,46 @@ function resolveVoiceRate(model, mode, override) {
|
|
|
1396
1722
|
`voice ${mode} override must be a finite, non-negative number, got ${override}`
|
|
1397
1723
|
);
|
|
1398
1724
|
}
|
|
1399
|
-
return {
|
|
1725
|
+
return {
|
|
1726
|
+
mode,
|
|
1727
|
+
unit: unitForMode[mode],
|
|
1728
|
+
rate: override,
|
|
1729
|
+
source: "override",
|
|
1730
|
+
confirmed: true
|
|
1731
|
+
};
|
|
1400
1732
|
}
|
|
1401
|
-
const
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1733
|
+
const found = lookupLegEntry(model, mode);
|
|
1734
|
+
if (found === null) throw new UnpriceableLegError(model ?? null, mode);
|
|
1735
|
+
const { entry, source } = found;
|
|
1736
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
1737
|
+
return {
|
|
1738
|
+
mode,
|
|
1739
|
+
unit: unitForMode[mode],
|
|
1740
|
+
rate: entry.rate,
|
|
1741
|
+
source,
|
|
1742
|
+
...str(entry.provider) !== void 0 ? { provider: str(entry.provider) } : {},
|
|
1743
|
+
...str(entry.source_url) !== void 0 ? { source_url: str(entry.source_url) } : {},
|
|
1744
|
+
...str(entry.retrieved_at) !== void 0 ? { retrieved_at: str(entry.retrieved_at) } : {},
|
|
1745
|
+
// A declared rate is confirmed unless the card says otherwise (an imported or
|
|
1746
|
+
// draft card can mark entries unconfirmed); a bundled list price is
|
|
1747
|
+
// UNCONFIRMED until a human accepts it.
|
|
1748
|
+
confirmed: typeof entry.confirmed === "boolean" ? entry.confirmed : source === "rate_card"
|
|
1749
|
+
};
|
|
1404
1750
|
}
|
|
1405
1751
|
function voiceLegCost(mode, quantity, rate) {
|
|
1406
1752
|
if (!Number.isFinite(quantity)) {
|
|
1407
1753
|
throw new RangeError(`voice ${mode} quantity must be a finite number, got ${quantity}`);
|
|
1408
1754
|
}
|
|
1409
1755
|
const q = Math.max(0, quantity);
|
|
1410
|
-
if (mode === "stt"
|
|
1756
|
+
if (mode === "stt" || // seconds * $/second
|
|
1757
|
+
mode === "telephony" || // minutes * $/minute
|
|
1758
|
+
mode === "sms" || // segments * $/segment
|
|
1759
|
+
mode === "ocr" || // pages * $/page
|
|
1760
|
+
mode === "gpu" || // gpu-seconds * $/gpu-second
|
|
1761
|
+
mode === "avatar") {
|
|
1762
|
+
return q * rate;
|
|
1763
|
+
}
|
|
1411
1764
|
if (mode === "tts") return q / 1e3 * rate;
|
|
1412
|
-
if (mode === "telephony") return q * rate;
|
|
1413
1765
|
throw new RangeError(`unknown voice mode ${mode}`);
|
|
1414
1766
|
}
|
|
1415
1767
|
function priceVoiceLeg(mode, quantity, options = {}) {
|