@shop-os/modelkit 0.9.0 → 0.10.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/CHANGELOG.md +25 -0
- package/README.md +2 -1
- package/dist/catalog.cjs +464 -395
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +6 -3
- package/dist/catalog.d.ts +6 -3
- package/dist/catalog.js +464 -395
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +974 -623
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +973 -623
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -66,6 +66,7 @@ __export(index_exports, {
|
|
|
66
66
|
dataOnlyCapable: () => dataOnlyCapable,
|
|
67
67
|
describe: () => describe,
|
|
68
68
|
describeThrown: () => describeThrown,
|
|
69
|
+
elevenLabsAdapter: () => elevenLabsAdapter,
|
|
69
70
|
experimental_generateImage: () => generateImage,
|
|
70
71
|
falAdapter: () => falAdapter,
|
|
71
72
|
falWebhookBodySchema: () => falWebhookBodySchema,
|
|
@@ -1371,303 +1372,6 @@ var VEED_SUBTITLES = {
|
|
|
1371
1372
|
}
|
|
1372
1373
|
};
|
|
1373
1374
|
|
|
1374
|
-
// src/catalog/voices-fish.ts
|
|
1375
|
-
var FISH_VOICES = [
|
|
1376
|
-
{
|
|
1377
|
-
id: "933563129e564b19a115bedd57b7406a",
|
|
1378
|
-
name: "Sarah",
|
|
1379
|
-
gender: "female",
|
|
1380
|
-
languages: ["en"],
|
|
1381
|
-
description: "An engaged speaker.",
|
|
1382
|
-
previewUrl: "https://platform.r2.fish.audio/task/4e204945670c460c86fdab57512cb308.mp3"
|
|
1383
|
-
},
|
|
1384
|
-
{
|
|
1385
|
-
id: "bf322df2096a46f18c579d0baa36f41d",
|
|
1386
|
-
name: "Adrian",
|
|
1387
|
-
gender: "male",
|
|
1388
|
-
languages: ["en"],
|
|
1389
|
-
description: "A steady and reliable narrator.",
|
|
1390
|
-
previewUrl: "https://platform.r2.fish.audio/task/d730b09f4a484316a77ecc86137e85fb.mp3"
|
|
1391
|
-
},
|
|
1392
|
-
{
|
|
1393
|
-
id: "536d3a5e000945adb7038665781a4aca",
|
|
1394
|
-
name: "Ethan",
|
|
1395
|
-
gender: "male",
|
|
1396
|
-
languages: ["en"],
|
|
1397
|
-
description: "A curious explainer.",
|
|
1398
|
-
previewUrl: "https://platform.r2.fish.audio/task/bdd677ed767744fc91f166468786264b.mp3"
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
id: "b347db033a6549378b48d00acb0d06cd",
|
|
1402
|
-
name: "Selene",
|
|
1403
|
-
gender: "female",
|
|
1404
|
-
languages: ["en"],
|
|
1405
|
-
description: "A meditative female voice.",
|
|
1406
|
-
previewUrl: "https://platform.r2.fish.audio/task/fcf629b4a0b343558f461338ba1b9da4.mp3"
|
|
1407
|
-
},
|
|
1408
|
-
{
|
|
1409
|
-
id: "9a9cf47702da476aa4629e2506d4a857",
|
|
1410
|
-
name: "Hannah",
|
|
1411
|
-
gender: "female",
|
|
1412
|
-
languages: ["en"],
|
|
1413
|
-
description: "A conversation specialist.",
|
|
1414
|
-
previewUrl: "https://platform.r2.fish.audio/task/08d658835feb427db808e37060b78eb0.mp3"
|
|
1415
|
-
},
|
|
1416
|
-
{
|
|
1417
|
-
id: "79d0bd3e4e5444b18f7b6d89b5927bf1",
|
|
1418
|
-
name: "Jordan",
|
|
1419
|
-
gender: "male",
|
|
1420
|
-
languages: ["en"],
|
|
1421
|
-
description: "A motivational speaker.",
|
|
1422
|
-
previewUrl: "https://platform.r2.fish.audio/task/edecbc0bdd4c46c39eda8520a0f729ef.mp3"
|
|
1423
|
-
},
|
|
1424
|
-
{
|
|
1425
|
-
id: "e3cd384158934cc9a01029cd7d278634",
|
|
1426
|
-
name: "Laura",
|
|
1427
|
-
gender: "female",
|
|
1428
|
-
languages: ["en"],
|
|
1429
|
-
description: "A confident female narrator.",
|
|
1430
|
-
previewUrl: "https://platform.r2.fish.audio/task/e821bef995344d519caa1c93d808db23.mp3"
|
|
1431
|
-
},
|
|
1432
|
-
{
|
|
1433
|
-
id: "f3a2b90078d54a65af4b95f63bc7798e",
|
|
1434
|
-
name: "Sheila",
|
|
1435
|
-
gender: "female",
|
|
1436
|
-
accent: "american",
|
|
1437
|
-
languages: ["en"],
|
|
1438
|
-
description: "A young American English woman's voice.",
|
|
1439
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/9bb9e1a1cf3f47f38700da1c5c0a678a.mp3"
|
|
1440
|
-
},
|
|
1441
|
-
{
|
|
1442
|
-
id: "0db6e93af420470b9d9b9a886dbaa954",
|
|
1443
|
-
name: "Kai",
|
|
1444
|
-
gender: "male",
|
|
1445
|
-
languages: ["en"],
|
|
1446
|
-
description: "A friendly English man's voice for companion apps and everyday conversation.",
|
|
1447
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/13306f1172544c3ba3106d1e363feaa6.mp3"
|
|
1448
|
-
},
|
|
1449
|
-
{
|
|
1450
|
-
id: "8abfa9ebd4c24a3399b68c3cdac8c7b5",
|
|
1451
|
-
name: "Freya",
|
|
1452
|
-
gender: "female",
|
|
1453
|
-
accent: "british",
|
|
1454
|
-
languages: ["en"],
|
|
1455
|
-
description: "A natural, professional British English voice with a brisk, conversational pace. Clear and easy to follow, suited to voice assistants, customer support and everyday narration.",
|
|
1456
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/07918705a56b42daaab149f65392cea7.mp3"
|
|
1457
|
-
},
|
|
1458
|
-
{
|
|
1459
|
-
id: "2f64e528a4f2406691925ab1e0ed5b7f",
|
|
1460
|
-
name: "Ananya",
|
|
1461
|
-
gender: "female",
|
|
1462
|
-
languages: ["hi"],
|
|
1463
|
-
description: "\u0938\u093E\u092B\u093C \u0914\u0930 \u0906\u0924\u094D\u092E\u0935\u093F\u0936\u094D\u0935\u093E\u0938 \u092D\u0930\u0940 \u0906\u0935\u093E\u091C\u093C, \u091C\u094B \u0935\u0949\u0907\u0938 \u0905\u0938\u093F\u0938\u094D\u091F\u0947\u0902\u091F, \u0917\u094D\u0930\u093E\u0939\u0915 \u0938\u0947\u0935\u093E \u0914\u0930 \u092A\u094D\u0930\u0938\u094D\u0924\u0941\u0924\u093F\u092F\u094B\u0902 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u092F\u0941\u0915\u094D\u0924 \u0939\u0948\u0964",
|
|
1464
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a17a700668d8405f8316db120733145c.mp3"
|
|
1465
|
-
},
|
|
1466
|
-
{
|
|
1467
|
-
id: "cefd91a07b99468f9148e2580990082d",
|
|
1468
|
-
name: "Arjun",
|
|
1469
|
-
gender: "male",
|
|
1470
|
-
languages: ["hi"],
|
|
1471
|
-
description: "\u0938\u093E\u092B\u093C \u0914\u0930 \u0906\u0924\u094D\u092E\u0935\u093F\u0936\u094D\u0935\u093E\u0938 \u092D\u0930\u0940 \u0906\u0935\u093E\u091C\u093C, \u091C\u094B \u0935\u0949\u0907\u0938 \u0905\u0938\u093F\u0938\u094D\u091F\u0947\u0902\u091F, \u0917\u094D\u0930\u093E\u0939\u0915 \u0938\u0947\u0935\u093E \u0914\u0930 \u092A\u094D\u0930\u0938\u094D\u0924\u0941\u0924\u093F\u092F\u094B\u0902 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u092F\u0941\u0915\u094D\u0924 \u0939\u0948\u0964",
|
|
1472
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/a0c4a8eadb7841cdae8e5e68031a2dfb.mp3"
|
|
1473
|
-
},
|
|
1474
|
-
{
|
|
1475
|
-
id: "3f065d660212478a9c697fa965b4d098",
|
|
1476
|
-
name: "Carmen",
|
|
1477
|
-
gender: "female",
|
|
1478
|
-
languages: ["es"],
|
|
1479
|
-
description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
|
|
1480
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/3421be48b6cb4c28bf829c25188e7e74.mp3"
|
|
1481
|
-
},
|
|
1482
|
-
{
|
|
1483
|
-
id: "ec0718028a2445fb9be8e8bfb56a0150",
|
|
1484
|
-
name: "Rodrigo",
|
|
1485
|
-
gender: "male",
|
|
1486
|
-
languages: ["es"],
|
|
1487
|
-
description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
|
|
1488
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/5a77c32365604800ad5b4eb13c5585df.mp3"
|
|
1489
|
-
},
|
|
1490
|
-
{
|
|
1491
|
-
id: "10a3a20742114a4ea6dd441e7591850f",
|
|
1492
|
-
name: "Manon",
|
|
1493
|
-
gender: "female",
|
|
1494
|
-
languages: ["fr"],
|
|
1495
|
-
description: "Une voix claire et vive, adapt\xE9e aux assistants vocaux et au support client.",
|
|
1496
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/f7b75ee1fa524e48a8cefb003cf72952.mp3"
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
id: "f69bca092b674168a8d02d61ca20943c",
|
|
1500
|
-
name: "Lucas",
|
|
1501
|
-
gender: "male",
|
|
1502
|
-
languages: ["fr"],
|
|
1503
|
-
description: "Une voix claire et assur\xE9e, adapt\xE9e aux assistants vocaux et au service client.",
|
|
1504
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/86fca3e56a014a3d8a11450854bb8311.mp3"
|
|
1505
|
-
},
|
|
1506
|
-
{
|
|
1507
|
-
id: "3937b44b604249f18b94913daef94043",
|
|
1508
|
-
name: "Lena",
|
|
1509
|
-
gender: "female",
|
|
1510
|
-
languages: ["de"],
|
|
1511
|
-
description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
|
|
1512
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/8659fec80b74442dab68a7755716a401.mp3"
|
|
1513
|
-
},
|
|
1514
|
-
{
|
|
1515
|
-
id: "c1e7cc926aa5469aa966c353094d4b0b",
|
|
1516
|
-
name: "Lukas",
|
|
1517
|
-
gender: "male",
|
|
1518
|
-
languages: ["de"],
|
|
1519
|
-
description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
|
|
1520
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/fb1d538126bb4fe38f8366250f6a2136.mp3"
|
|
1521
|
-
},
|
|
1522
|
-
{
|
|
1523
|
-
id: "b3c060cc41544a7da94a2c487647c902",
|
|
1524
|
-
name: "Let\xEDcia",
|
|
1525
|
-
gender: "female",
|
|
1526
|
-
accent: "brazilian",
|
|
1527
|
-
languages: ["pt"],
|
|
1528
|
-
description: "A Brazilian-accented Portuguese woman's voice, in her late twenties. Soft, warm, intimate. A freelance designer from Belo Horizonte who enjoys long walks in the park. Best suited to companion apps and casual everyday conversation.",
|
|
1529
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/13c6c0d96b5c4069951f89868ee15360.mp3"
|
|
1530
|
-
},
|
|
1531
|
-
{
|
|
1532
|
-
id: "add7bcb8d3c8423bbfa152ca232aad6e",
|
|
1533
|
-
name: "Ricardo",
|
|
1534
|
-
gender: "male",
|
|
1535
|
-
accent: "brazilian",
|
|
1536
|
-
languages: ["pt"],
|
|
1537
|
-
description: "A Brazilian-accented Portuguese man's voice, in his late forties. Clear, professional, monotone. A high school history teacher from Curitiba who loves classical music. Best suited to lessons, tutoring and explainer content.",
|
|
1538
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/67475fc717ae45cbb38e647a2dd37fe1.mp3"
|
|
1539
|
-
},
|
|
1540
|
-
{
|
|
1541
|
-
id: "b2d9d8db057042688a5e318b8f405bc2",
|
|
1542
|
-
name: "Kyoko",
|
|
1543
|
-
gender: "female",
|
|
1544
|
-
languages: ["ja"],
|
|
1545
|
-
description: "\u4E01\u5BE7\u3067\u843D\u3061\u7740\u3044\u305F\u8A71\u3057\u65B9\u304C\u7279\u5FB4\u306E\u5973\u6027\u306E\u58F0\u3002\u304A\u5BA2\u69D8\u5BFE\u5FDC\u3084\u60C5\u5831\u63D0\u4F9B\u306B\u6700\u9069\u3002",
|
|
1546
|
-
previewUrl: "https://platform.r2.fish.audio/task/993b3ccb52124341aff79580f1ce5a5a.mp3"
|
|
1547
|
-
},
|
|
1548
|
-
{
|
|
1549
|
-
id: "297a6fd278df47c3b9da9bfdf55ac89a",
|
|
1550
|
-
name: "Satoru",
|
|
1551
|
-
gender: "male",
|
|
1552
|
-
languages: ["ja"],
|
|
1553
|
-
description: "\u82E5\u3005\u3057\u304F\u3082\u843D\u3061\u7740\u304D\u306E\u3042\u308B\u7537\u6027\u306E\u58F0\u3002\u65E5\u5E38\u306E\u8A9E\u308A\u3084\u30EA\u30E9\u30C3\u30AF\u30B9\u3057\u305F\u30CA\u30EC\u30FC\u30B7\u30E7\u30F3\u306B\u6700\u9069\u3002",
|
|
1554
|
-
previewUrl: "https://platform.r2.fish.audio/task/1b4712fb831f4c149eef6b0133157418.mp3"
|
|
1555
|
-
},
|
|
1556
|
-
{
|
|
1557
|
-
id: "0b14f34a13a94ed88fe6113193749bb0",
|
|
1558
|
-
name: "Fatima",
|
|
1559
|
-
gender: "female",
|
|
1560
|
-
languages: ["ar"],
|
|
1561
|
-
description: "\u0635\u0648\u062A \u0648\u0627\u0636\u062D \u0648\u0648\u0627\u062B\u0642 \u064A\u0646\u0627\u0633\u0628 \u0627\u0644\u0645\u0633\u0627\u0639\u062F\u0627\u062A \u0627\u0644\u0635\u0648\u062A\u064A\u0629 \u0648\u062E\u062F\u0645\u0629 \u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u0648\u0627\u0644\u0639\u0631\u0648\u0636 \u0627\u0644\u062A\u0642\u062F\u064A\u0645\u064A\u0629.",
|
|
1562
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/93406e1b905a4c4994306e3421243390.mp3"
|
|
1563
|
-
},
|
|
1564
|
-
{
|
|
1565
|
-
id: "74c6aba5cbf94a15bbdc547ffce5cb38",
|
|
1566
|
-
name: "Yutong",
|
|
1567
|
-
gender: "female",
|
|
1568
|
-
languages: ["zh"],
|
|
1569
|
-
description: "\u58F0\u97F3\u6E05\u6670\u81EA\u7136\uFF0C\u8868\u8FBE\u5229\u843D\uFF0C\u9002\u5408\u8BED\u97F3\u52A9\u624B\u3001\u5BA2\u670D\u548C\u65E5\u5E38\u64AD\u62A5\u3002",
|
|
1570
|
-
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a48320f37fe44c4699549a1afc3778e8.mp3"
|
|
1571
|
-
}
|
|
1572
|
-
];
|
|
1573
|
-
|
|
1574
|
-
// src/catalog/speech-fish.ts
|
|
1575
|
-
var FISH_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/fishaudio.png";
|
|
1576
|
-
var PER_CALL = { unit: "images" };
|
|
1577
|
-
var PER_REQUEST = { unit: "requests" };
|
|
1578
|
-
var TTS_MAX_CHARS = 1e4;
|
|
1579
|
-
function fishTts(id, label, providerModelId, description) {
|
|
1580
|
-
return {
|
|
1581
|
-
id,
|
|
1582
|
-
aliases: [],
|
|
1583
|
-
label,
|
|
1584
|
-
type: "speech",
|
|
1585
|
-
adapter: "fish",
|
|
1586
|
-
providerModelId,
|
|
1587
|
-
lifecycle: "active",
|
|
1588
|
-
exposure: [],
|
|
1589
|
-
constraints: {
|
|
1590
|
-
voices: FISH_VOICES,
|
|
1591
|
-
outputFormats: ["mp3", "wav"],
|
|
1592
|
-
supports: [],
|
|
1593
|
-
prompt: { referenceSyntax: "numbered", maxChars: TTS_MAX_CHARS }
|
|
1594
|
-
},
|
|
1595
|
-
defaults: { voice: "9a9cf47702da476aa4629e2506d4a857", outputFormat: "mp3" },
|
|
1596
|
-
usage: PER_CALL,
|
|
1597
|
-
pricingKind: "flat",
|
|
1598
|
-
display: { provider: "fishaudio", logo: FISH_LOGO, description },
|
|
1599
|
-
capabilities: { customVoices: true }
|
|
1600
|
-
};
|
|
1601
|
-
}
|
|
1602
|
-
var FISH_MODELS = [
|
|
1603
|
-
fishTts(
|
|
1604
|
-
"fish-s2.1-pro",
|
|
1605
|
-
"Fish Audio S2.1 Pro",
|
|
1606
|
-
"s2.1-pro",
|
|
1607
|
-
"Fish Audio S2.1 Pro text to speech in 83 languages, with [bracket] directions and your own cloned voices."
|
|
1608
|
-
),
|
|
1609
|
-
fishTts(
|
|
1610
|
-
"fish-s2.1-pro-free",
|
|
1611
|
-
"Fish Audio S2.1 Pro Free",
|
|
1612
|
-
"s2.1-pro-free",
|
|
1613
|
-
"Fish Audio S2.1 Pro on the free developer tier: the same voices at no cost, without latency or data-processing guarantees."
|
|
1614
|
-
),
|
|
1615
|
-
fishTts(
|
|
1616
|
-
"fish-drama-3",
|
|
1617
|
-
"Fish Audio Drama 3",
|
|
1618
|
-
"drama-3",
|
|
1619
|
-
"Fish Audio Drama 3 text to speech you direct like an actor: [directions], sounds, pauses and per-word tags."
|
|
1620
|
-
),
|
|
1621
|
-
{
|
|
1622
|
-
id: "fish-voice-clone",
|
|
1623
|
-
aliases: [],
|
|
1624
|
-
label: "Fish Audio Voice Clone",
|
|
1625
|
-
type: "voice-clone",
|
|
1626
|
-
adapter: "fish",
|
|
1627
|
-
providerModelId: "voice-clone",
|
|
1628
|
-
lifecycle: "active",
|
|
1629
|
-
exposure: [],
|
|
1630
|
-
constraints: {
|
|
1631
|
-
// POST /model takes 1-20 voice files; Fish advises 10 s or more of clean speech each.
|
|
1632
|
-
references: { audios: 20, audioFormats: ["wav", "mp3", "m4a", "opus"] },
|
|
1633
|
-
supports: []
|
|
1634
|
-
},
|
|
1635
|
-
defaults: {},
|
|
1636
|
-
usage: PER_REQUEST,
|
|
1637
|
-
pricingKind: "flat",
|
|
1638
|
-
display: {
|
|
1639
|
-
provider: "fishaudio",
|
|
1640
|
-
logo: FISH_LOGO,
|
|
1641
|
-
description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on the Fish Audio speech models."
|
|
1642
|
-
},
|
|
1643
|
-
capabilities: {}
|
|
1644
|
-
},
|
|
1645
|
-
{
|
|
1646
|
-
id: "fish-voice-design-1",
|
|
1647
|
-
aliases: [],
|
|
1648
|
-
label: "Fish Audio Voice Design",
|
|
1649
|
-
type: "voice-design",
|
|
1650
|
-
adapter: "fish",
|
|
1651
|
-
providerModelId: "voice-design-1",
|
|
1652
|
-
lifecycle: "active",
|
|
1653
|
-
exposure: [],
|
|
1654
|
-
constraints: {
|
|
1655
|
-
voiceDesign: { maxCandidates: 4, previewMaxChars: 150 },
|
|
1656
|
-
supports: [],
|
|
1657
|
-
prompt: { referenceSyntax: "numbered", maxChars: 2e3 }
|
|
1658
|
-
},
|
|
1659
|
-
defaults: {},
|
|
1660
|
-
usage: PER_REQUEST,
|
|
1661
|
-
pricingKind: "flat",
|
|
1662
|
-
display: {
|
|
1663
|
-
provider: "fishaudio",
|
|
1664
|
-
logo: FISH_LOGO,
|
|
1665
|
-
description: "Describe a voice and get up to four short candidates; keep one as a cloned voice."
|
|
1666
|
-
},
|
|
1667
|
-
capabilities: {}
|
|
1668
|
-
}
|
|
1669
|
-
];
|
|
1670
|
-
|
|
1671
1375
|
// src/catalog/languages-audio.ts
|
|
1672
1376
|
var ELEVENLABS_V4_LANGUAGES = [
|
|
1673
1377
|
{ code: "af", name: "Afrikaans" },
|
|
@@ -2054,119 +1758,483 @@ var ELEVENLABS_VOICES = [
|
|
|
2054
1758
|
id: "Callum",
|
|
2055
1759
|
name: "Callum",
|
|
2056
1760
|
gender: "male",
|
|
2057
|
-
accent: "american",
|
|
2058
|
-
languages: ["en", "fr", "hi"],
|
|
2059
|
-
description: "Deceptively gravelly, yet unsettling edge.",
|
|
2060
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/N2lVS1w4EtoT3dr4eOWO/ac833bd8-ffda-4938-9ebc-b0f99ca25481.mp3"
|
|
1761
|
+
accent: "american",
|
|
1762
|
+
languages: ["en", "fr", "hi"],
|
|
1763
|
+
description: "Deceptively gravelly, yet unsettling edge.",
|
|
1764
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/N2lVS1w4EtoT3dr4eOWO/ac833bd8-ffda-4938-9ebc-b0f99ca25481.mp3"
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
id: "River",
|
|
1768
|
+
name: "River",
|
|
1769
|
+
gender: "neutral",
|
|
1770
|
+
accent: "american",
|
|
1771
|
+
languages: ["en", "it", "fr", "pt", "zh"],
|
|
1772
|
+
description: "A relaxed, neutral voice ready for narrations or conversational projects.",
|
|
1773
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/SAz9YHcvj6GT2YYXdXww/e6c95f0b-2227-491a-b3d7-2249240decb7.mp3"
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
id: "Liam",
|
|
1777
|
+
name: "Liam",
|
|
1778
|
+
gender: "male",
|
|
1779
|
+
accent: "american",
|
|
1780
|
+
languages: ["en", "de", "pt", "cs", "pl", "tr", "hi"],
|
|
1781
|
+
description: "A young adult with energy and warmth - suitable for reels and shorts.",
|
|
1782
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/TX3LPaxmHKxFdv7VOQHJ/63148076-6363-42db-aea8-31424308b92c.mp3"
|
|
1783
|
+
},
|
|
1784
|
+
{ id: "Charlotte", name: "Charlotte" },
|
|
1785
|
+
{
|
|
1786
|
+
id: "Alice",
|
|
1787
|
+
name: "Alice",
|
|
1788
|
+
gender: "female",
|
|
1789
|
+
accent: "british",
|
|
1790
|
+
languages: ["en", "it", "fr", "ar", "ja", "pl", "hi"],
|
|
1791
|
+
description: "Clear and engaging, friendly woman with a British accent suitable for e-learning.",
|
|
1792
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/Xb7hH8MSUJpSbSDYk0k2/d10f7534-11f6-41fe-a012-2de1e482d336.mp3"
|
|
1793
|
+
},
|
|
1794
|
+
{
|
|
1795
|
+
id: "Matilda",
|
|
1796
|
+
name: "Matilda",
|
|
1797
|
+
gender: "female",
|
|
1798
|
+
accent: "american",
|
|
1799
|
+
languages: ["en", "it", "fr", "de", "ar", "es"],
|
|
1800
|
+
description: "A professional woman with a pleasing alto pitch. Suitable for many use cases.",
|
|
1801
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/XrExE9yKIg1WjnnlVkGX/b930e18d-6b4d-466e-bab2-0ae97c6d8535.mp3"
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
id: "Will",
|
|
1805
|
+
name: "Will",
|
|
1806
|
+
gender: "male",
|
|
1807
|
+
accent: "american",
|
|
1808
|
+
languages: ["en", "fr", "de", "pt", "zh", "cs", "fil", "sk", "es", "sv"],
|
|
1809
|
+
description: "Conversational and laid back.",
|
|
1810
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/bIHbv24MWmeRgasZH58o/8caf8f3d-ad29-4980-af41-53f20c72d7a4.mp3"
|
|
1811
|
+
},
|
|
1812
|
+
{
|
|
1813
|
+
id: "Jessica",
|
|
1814
|
+
name: "Jessica",
|
|
1815
|
+
gender: "female",
|
|
1816
|
+
accent: "american",
|
|
1817
|
+
languages: ["en", "fr", "ar", "ja", "zh", "de", "cs", "hi"],
|
|
1818
|
+
description: "Young and popular, this playful American female voice is perfect for trendy content.",
|
|
1819
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/cgSgspJ2msm6clMCkdW9/56a97bf8-b69b-448f-846c-c3a11683d45a.mp3"
|
|
1820
|
+
},
|
|
1821
|
+
{
|
|
1822
|
+
id: "Eric",
|
|
1823
|
+
name: "Eric",
|
|
1824
|
+
gender: "male",
|
|
1825
|
+
accent: "american",
|
|
1826
|
+
languages: ["en", "fr", "pt", "de", "sk", "es"],
|
|
1827
|
+
description: "A smooth tenor pitch from a man in his 40s - perfect for agentic use cases.",
|
|
1828
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/cjVigY5qzO86Huf0OWal/d098fda0-6456-4030-b3d8-63aa048c9070.mp3"
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
id: "Chris",
|
|
1832
|
+
name: "Chris",
|
|
1833
|
+
gender: "male",
|
|
1834
|
+
accent: "american",
|
|
1835
|
+
languages: ["en", "fr", "ar", "pt", "sv", "hi"],
|
|
1836
|
+
description: "Natural and real, this down-to-earth voice is great across many use-cases.",
|
|
1837
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/iP95p4xoKVk53GoZ742B/3f4bde72-cc48-40dd-829f-57fbf906f4d7.mp3"
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
id: "Brian",
|
|
1841
|
+
name: "Brian",
|
|
1842
|
+
gender: "male",
|
|
1843
|
+
accent: "american",
|
|
1844
|
+
languages: ["en", "ar", "zh", "pt", "de", "nl", "sk", "ro", "hi"],
|
|
1845
|
+
description: "Middle-aged man with a resonant and comforting tone. Great for narrations and advertisements.",
|
|
1846
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/nPczCjzI2devNBz1zQrb/2dd3e72c-4fd3-42f1-93ea-abc5d4e5aa1d.mp3"
|
|
1847
|
+
},
|
|
1848
|
+
{
|
|
1849
|
+
id: "Daniel",
|
|
1850
|
+
name: "Daniel",
|
|
1851
|
+
gender: "male",
|
|
1852
|
+
accent: "british",
|
|
1853
|
+
languages: ["en", "de", "tr"],
|
|
1854
|
+
description: "A strong voice perfect for delivering a professional broadcast or news story.",
|
|
1855
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/onwK4e9ZLuTAKqWW03F9/7eee0236-1a72-4b86-b303-5dcadc007ba9.mp3"
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
id: "Lily",
|
|
1859
|
+
name: "Lily",
|
|
1860
|
+
gender: "female",
|
|
1861
|
+
accent: "british",
|
|
1862
|
+
languages: ["en", "it", "de", "zh", "cs", "nl", "pl"],
|
|
1863
|
+
description: "Velvety British female voice delivers news and narrations with warmth and clarity.",
|
|
1864
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/pFZP5JQG7iQjIQuC4Bku/89b68b35-b3dd-4348-a84a-a3c13a3c2b30.mp3"
|
|
1865
|
+
},
|
|
1866
|
+
{
|
|
1867
|
+
id: "Bill",
|
|
1868
|
+
name: "Bill",
|
|
1869
|
+
gender: "male",
|
|
1870
|
+
accent: "american",
|
|
1871
|
+
languages: ["en", "fr", "ar", "zh", "de", "cs"],
|
|
1872
|
+
description: "Friendly and comforting voice ready to narrate your stories.",
|
|
1873
|
+
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/pqHfZKP75CvOlQylNhV4/d782b3ff-84ba-4029-848c-acf01285524d.mp3"
|
|
1874
|
+
}
|
|
1875
|
+
];
|
|
1876
|
+
var PREMADE_ID = /\/premade\/voices\/([A-Za-z0-9]+)\//;
|
|
1877
|
+
var ELEVENLABS_DIRECT_VOICES = ELEVENLABS_VOICES.flatMap((v) => {
|
|
1878
|
+
const id = v.previewUrl ? PREMADE_ID.exec(v.previewUrl)?.[1] : void 0;
|
|
1879
|
+
return id ? [{ ...v, id }] : [];
|
|
1880
|
+
});
|
|
1881
|
+
var ELEVENLABS_DIRECT_DEFAULT_VOICE = "EXAVITQu4vr4xnSDxMaL";
|
|
1882
|
+
|
|
1883
|
+
// src/catalog/speech-elevenlabs-direct.ts
|
|
1884
|
+
var ELEVENLABS_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
|
|
1885
|
+
var PER_CALL = { unit: "images" };
|
|
1886
|
+
var PER_REQUEST = { unit: "requests" };
|
|
1887
|
+
var ELEVENLABS_DIRECT_MODELS = [
|
|
1888
|
+
{
|
|
1889
|
+
id: "elevenlabs-v4-direct",
|
|
1890
|
+
aliases: [],
|
|
1891
|
+
label: "ElevenLabs v4 (direct)",
|
|
1892
|
+
type: "speech",
|
|
1893
|
+
adapter: "elevenlabs",
|
|
1894
|
+
providerModelId: "eleven_v4",
|
|
1895
|
+
lifecycle: "active",
|
|
1896
|
+
exposure: [],
|
|
1897
|
+
constraints: {
|
|
1898
|
+
// Premade voices under their ElevenLabs ids (the direct API takes ids, not names).
|
|
1899
|
+
voices: ELEVENLABS_DIRECT_VOICES,
|
|
1900
|
+
languages: ELEVENLABS_V4_LANGUAGES,
|
|
1901
|
+
outputFormats: ELEVENLABS_MP3_FORMATS,
|
|
1902
|
+
supports: [],
|
|
1903
|
+
// ElevenLabs docs: 10,000 characters per request.
|
|
1904
|
+
prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
|
|
1905
|
+
},
|
|
1906
|
+
defaults: { voice: ELEVENLABS_DIRECT_DEFAULT_VOICE, outputFormat: "mp3_44100_128" },
|
|
1907
|
+
usage: PER_CALL,
|
|
1908
|
+
pricingKind: "flat",
|
|
1909
|
+
display: {
|
|
1910
|
+
provider: "elevenlabs",
|
|
1911
|
+
logo: ELEVENLABS_LOGO,
|
|
1912
|
+
description: "ElevenLabs v4 text to speech with audio tags, in a premade voice or your own cloned voice; up to 10,000 characters."
|
|
1913
|
+
},
|
|
1914
|
+
capabilities: { customVoices: true }
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
id: "elevenlabs-ivc",
|
|
1918
|
+
aliases: [],
|
|
1919
|
+
label: "ElevenLabs Instant Voice Clone",
|
|
1920
|
+
type: "voice-clone",
|
|
1921
|
+
adapter: "elevenlabs",
|
|
1922
|
+
providerModelId: "ivc",
|
|
1923
|
+
lifecycle: "active",
|
|
1924
|
+
exposure: [],
|
|
1925
|
+
constraints: {
|
|
1926
|
+
// POST /v1/voices/add; ElevenLabs takes up to 25 samples and advises 1-2 minutes of
|
|
1927
|
+
// clean speech in total. Each clone uses a voice slot on our plan until deleted.
|
|
1928
|
+
references: { audios: 25, audioFormats: ["mp3", "wav", "m4a", "webm", "ogg"] },
|
|
1929
|
+
supports: []
|
|
1930
|
+
},
|
|
1931
|
+
defaults: {},
|
|
1932
|
+
usage: PER_REQUEST,
|
|
1933
|
+
pricingKind: "flat",
|
|
1934
|
+
display: {
|
|
1935
|
+
provider: "elevenlabs",
|
|
1936
|
+
logo: ELEVENLABS_LOGO,
|
|
1937
|
+
description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on ElevenLabs v4 (direct)."
|
|
1938
|
+
},
|
|
1939
|
+
capabilities: {}
|
|
1940
|
+
}
|
|
1941
|
+
];
|
|
1942
|
+
|
|
1943
|
+
// src/catalog/voices-fish.ts
|
|
1944
|
+
var FISH_VOICES = [
|
|
1945
|
+
{
|
|
1946
|
+
id: "933563129e564b19a115bedd57b7406a",
|
|
1947
|
+
name: "Sarah",
|
|
1948
|
+
gender: "female",
|
|
1949
|
+
languages: ["en"],
|
|
1950
|
+
description: "An engaged speaker.",
|
|
1951
|
+
previewUrl: "https://platform.r2.fish.audio/task/4e204945670c460c86fdab57512cb308.mp3"
|
|
1952
|
+
},
|
|
1953
|
+
{
|
|
1954
|
+
id: "bf322df2096a46f18c579d0baa36f41d",
|
|
1955
|
+
name: "Adrian",
|
|
1956
|
+
gender: "male",
|
|
1957
|
+
languages: ["en"],
|
|
1958
|
+
description: "A steady and reliable narrator.",
|
|
1959
|
+
previewUrl: "https://platform.r2.fish.audio/task/d730b09f4a484316a77ecc86137e85fb.mp3"
|
|
1960
|
+
},
|
|
1961
|
+
{
|
|
1962
|
+
id: "536d3a5e000945adb7038665781a4aca",
|
|
1963
|
+
name: "Ethan",
|
|
1964
|
+
gender: "male",
|
|
1965
|
+
languages: ["en"],
|
|
1966
|
+
description: "A curious explainer.",
|
|
1967
|
+
previewUrl: "https://platform.r2.fish.audio/task/bdd677ed767744fc91f166468786264b.mp3"
|
|
1968
|
+
},
|
|
1969
|
+
{
|
|
1970
|
+
id: "b347db033a6549378b48d00acb0d06cd",
|
|
1971
|
+
name: "Selene",
|
|
1972
|
+
gender: "female",
|
|
1973
|
+
languages: ["en"],
|
|
1974
|
+
description: "A meditative female voice.",
|
|
1975
|
+
previewUrl: "https://platform.r2.fish.audio/task/fcf629b4a0b343558f461338ba1b9da4.mp3"
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
id: "9a9cf47702da476aa4629e2506d4a857",
|
|
1979
|
+
name: "Hannah",
|
|
1980
|
+
gender: "female",
|
|
1981
|
+
languages: ["en"],
|
|
1982
|
+
description: "A conversation specialist.",
|
|
1983
|
+
previewUrl: "https://platform.r2.fish.audio/task/08d658835feb427db808e37060b78eb0.mp3"
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
id: "79d0bd3e4e5444b18f7b6d89b5927bf1",
|
|
1987
|
+
name: "Jordan",
|
|
1988
|
+
gender: "male",
|
|
1989
|
+
languages: ["en"],
|
|
1990
|
+
description: "A motivational speaker.",
|
|
1991
|
+
previewUrl: "https://platform.r2.fish.audio/task/edecbc0bdd4c46c39eda8520a0f729ef.mp3"
|
|
1992
|
+
},
|
|
1993
|
+
{
|
|
1994
|
+
id: "e3cd384158934cc9a01029cd7d278634",
|
|
1995
|
+
name: "Laura",
|
|
1996
|
+
gender: "female",
|
|
1997
|
+
languages: ["en"],
|
|
1998
|
+
description: "A confident female narrator.",
|
|
1999
|
+
previewUrl: "https://platform.r2.fish.audio/task/e821bef995344d519caa1c93d808db23.mp3"
|
|
2000
|
+
},
|
|
2001
|
+
{
|
|
2002
|
+
id: "f3a2b90078d54a65af4b95f63bc7798e",
|
|
2003
|
+
name: "Sheila",
|
|
2004
|
+
gender: "female",
|
|
2005
|
+
accent: "american",
|
|
2006
|
+
languages: ["en"],
|
|
2007
|
+
description: "A young American English woman's voice.",
|
|
2008
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/9bb9e1a1cf3f47f38700da1c5c0a678a.mp3"
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
id: "0db6e93af420470b9d9b9a886dbaa954",
|
|
2012
|
+
name: "Kai",
|
|
2013
|
+
gender: "male",
|
|
2014
|
+
languages: ["en"],
|
|
2015
|
+
description: "A friendly English man's voice for companion apps and everyday conversation.",
|
|
2016
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/13306f1172544c3ba3106d1e363feaa6.mp3"
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
id: "8abfa9ebd4c24a3399b68c3cdac8c7b5",
|
|
2020
|
+
name: "Freya",
|
|
2021
|
+
gender: "female",
|
|
2022
|
+
accent: "british",
|
|
2023
|
+
languages: ["en"],
|
|
2024
|
+
description: "A natural, professional British English voice with a brisk, conversational pace. Clear and easy to follow, suited to voice assistants, customer support and everyday narration.",
|
|
2025
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/07918705a56b42daaab149f65392cea7.mp3"
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
id: "2f64e528a4f2406691925ab1e0ed5b7f",
|
|
2029
|
+
name: "Ananya",
|
|
2030
|
+
gender: "female",
|
|
2031
|
+
languages: ["hi"],
|
|
2032
|
+
description: "\u0938\u093E\u092B\u093C \u0914\u0930 \u0906\u0924\u094D\u092E\u0935\u093F\u0936\u094D\u0935\u093E\u0938 \u092D\u0930\u0940 \u0906\u0935\u093E\u091C\u093C, \u091C\u094B \u0935\u0949\u0907\u0938 \u0905\u0938\u093F\u0938\u094D\u091F\u0947\u0902\u091F, \u0917\u094D\u0930\u093E\u0939\u0915 \u0938\u0947\u0935\u093E \u0914\u0930 \u092A\u094D\u0930\u0938\u094D\u0924\u0941\u0924\u093F\u092F\u094B\u0902 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u092F\u0941\u0915\u094D\u0924 \u0939\u0948\u0964",
|
|
2033
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a17a700668d8405f8316db120733145c.mp3"
|
|
2034
|
+
},
|
|
2035
|
+
{
|
|
2036
|
+
id: "cefd91a07b99468f9148e2580990082d",
|
|
2037
|
+
name: "Arjun",
|
|
2038
|
+
gender: "male",
|
|
2039
|
+
languages: ["hi"],
|
|
2040
|
+
description: "\u0938\u093E\u092B\u093C \u0914\u0930 \u0906\u0924\u094D\u092E\u0935\u093F\u0936\u094D\u0935\u093E\u0938 \u092D\u0930\u0940 \u0906\u0935\u093E\u091C\u093C, \u091C\u094B \u0935\u0949\u0907\u0938 \u0905\u0938\u093F\u0938\u094D\u091F\u0947\u0902\u091F, \u0917\u094D\u0930\u093E\u0939\u0915 \u0938\u0947\u0935\u093E \u0914\u0930 \u092A\u094D\u0930\u0938\u094D\u0924\u0941\u0924\u093F\u092F\u094B\u0902 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u092F\u0941\u0915\u094D\u0924 \u0939\u0948\u0964",
|
|
2041
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/a0c4a8eadb7841cdae8e5e68031a2dfb.mp3"
|
|
2061
2042
|
},
|
|
2062
2043
|
{
|
|
2063
|
-
id: "
|
|
2064
|
-
name: "
|
|
2065
|
-
gender: "
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/SAz9YHcvj6GT2YYXdXww/e6c95f0b-2227-491a-b3d7-2249240decb7.mp3"
|
|
2044
|
+
id: "3f065d660212478a9c697fa965b4d098",
|
|
2045
|
+
name: "Carmen",
|
|
2046
|
+
gender: "female",
|
|
2047
|
+
languages: ["es"],
|
|
2048
|
+
description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
|
|
2049
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/3421be48b6cb4c28bf829c25188e7e74.mp3"
|
|
2070
2050
|
},
|
|
2071
2051
|
{
|
|
2072
|
-
id: "
|
|
2073
|
-
name: "
|
|
2052
|
+
id: "ec0718028a2445fb9be8e8bfb56a0150",
|
|
2053
|
+
name: "Rodrigo",
|
|
2074
2054
|
gender: "male",
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/TX3LPaxmHKxFdv7VOQHJ/63148076-6363-42db-aea8-31424308b92c.mp3"
|
|
2055
|
+
languages: ["es"],
|
|
2056
|
+
description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
|
|
2057
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/5a77c32365604800ad5b4eb13c5585df.mp3"
|
|
2079
2058
|
},
|
|
2080
|
-
{ id: "Charlotte", name: "Charlotte" },
|
|
2081
2059
|
{
|
|
2082
|
-
id: "
|
|
2083
|
-
name: "
|
|
2060
|
+
id: "10a3a20742114a4ea6dd441e7591850f",
|
|
2061
|
+
name: "Manon",
|
|
2084
2062
|
gender: "female",
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/Xb7hH8MSUJpSbSDYk0k2/d10f7534-11f6-41fe-a012-2de1e482d336.mp3"
|
|
2063
|
+
languages: ["fr"],
|
|
2064
|
+
description: "Une voix claire et vive, adapt\xE9e aux assistants vocaux et au support client.",
|
|
2065
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/f7b75ee1fa524e48a8cefb003cf72952.mp3"
|
|
2089
2066
|
},
|
|
2090
2067
|
{
|
|
2091
|
-
id: "
|
|
2092
|
-
name: "
|
|
2068
|
+
id: "f69bca092b674168a8d02d61ca20943c",
|
|
2069
|
+
name: "Lucas",
|
|
2070
|
+
gender: "male",
|
|
2071
|
+
languages: ["fr"],
|
|
2072
|
+
description: "Une voix claire et assur\xE9e, adapt\xE9e aux assistants vocaux et au service client.",
|
|
2073
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/86fca3e56a014a3d8a11450854bb8311.mp3"
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
id: "3937b44b604249f18b94913daef94043",
|
|
2077
|
+
name: "Lena",
|
|
2093
2078
|
gender: "female",
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/XrExE9yKIg1WjnnlVkGX/b930e18d-6b4d-466e-bab2-0ae97c6d8535.mp3"
|
|
2079
|
+
languages: ["de"],
|
|
2080
|
+
description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
|
|
2081
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/8659fec80b74442dab68a7755716a401.mp3"
|
|
2098
2082
|
},
|
|
2099
2083
|
{
|
|
2100
|
-
id: "
|
|
2101
|
-
name: "
|
|
2084
|
+
id: "c1e7cc926aa5469aa966c353094d4b0b",
|
|
2085
|
+
name: "Lukas",
|
|
2102
2086
|
gender: "male",
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/bIHbv24MWmeRgasZH58o/8caf8f3d-ad29-4980-af41-53f20c72d7a4.mp3"
|
|
2087
|
+
languages: ["de"],
|
|
2088
|
+
description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
|
|
2089
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/fb1d538126bb4fe38f8366250f6a2136.mp3"
|
|
2107
2090
|
},
|
|
2108
2091
|
{
|
|
2109
|
-
id: "
|
|
2110
|
-
name: "
|
|
2092
|
+
id: "b3c060cc41544a7da94a2c487647c902",
|
|
2093
|
+
name: "Let\xEDcia",
|
|
2111
2094
|
gender: "female",
|
|
2112
|
-
accent: "
|
|
2113
|
-
languages: ["
|
|
2114
|
-
description: "
|
|
2115
|
-
previewUrl: "https://
|
|
2095
|
+
accent: "brazilian",
|
|
2096
|
+
languages: ["pt"],
|
|
2097
|
+
description: "A Brazilian-accented Portuguese woman's voice, in her late twenties. Soft, warm, intimate. A freelance designer from Belo Horizonte who enjoys long walks in the park. Best suited to companion apps and casual everyday conversation.",
|
|
2098
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/13c6c0d96b5c4069951f89868ee15360.mp3"
|
|
2116
2099
|
},
|
|
2117
2100
|
{
|
|
2118
|
-
id: "
|
|
2119
|
-
name: "
|
|
2101
|
+
id: "add7bcb8d3c8423bbfa152ca232aad6e",
|
|
2102
|
+
name: "Ricardo",
|
|
2120
2103
|
gender: "male",
|
|
2121
|
-
accent: "
|
|
2122
|
-
languages: ["
|
|
2123
|
-
description: "A
|
|
2124
|
-
previewUrl: "https://
|
|
2104
|
+
accent: "brazilian",
|
|
2105
|
+
languages: ["pt"],
|
|
2106
|
+
description: "A Brazilian-accented Portuguese man's voice, in his late forties. Clear, professional, monotone. A high school history teacher from Curitiba who loves classical music. Best suited to lessons, tutoring and explainer content.",
|
|
2107
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/67475fc717ae45cbb38e647a2dd37fe1.mp3"
|
|
2125
2108
|
},
|
|
2126
2109
|
{
|
|
2127
|
-
id: "
|
|
2128
|
-
name: "
|
|
2129
|
-
gender: "
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/iP95p4xoKVk53GoZ742B/3f4bde72-cc48-40dd-829f-57fbf906f4d7.mp3"
|
|
2110
|
+
id: "b2d9d8db057042688a5e318b8f405bc2",
|
|
2111
|
+
name: "Kyoko",
|
|
2112
|
+
gender: "female",
|
|
2113
|
+
languages: ["ja"],
|
|
2114
|
+
description: "\u4E01\u5BE7\u3067\u843D\u3061\u7740\u3044\u305F\u8A71\u3057\u65B9\u304C\u7279\u5FB4\u306E\u5973\u6027\u306E\u58F0\u3002\u304A\u5BA2\u69D8\u5BFE\u5FDC\u3084\u60C5\u5831\u63D0\u4F9B\u306B\u6700\u9069\u3002",
|
|
2115
|
+
previewUrl: "https://platform.r2.fish.audio/task/993b3ccb52124341aff79580f1ce5a5a.mp3"
|
|
2134
2116
|
},
|
|
2135
2117
|
{
|
|
2136
|
-
id: "
|
|
2137
|
-
name: "
|
|
2118
|
+
id: "297a6fd278df47c3b9da9bfdf55ac89a",
|
|
2119
|
+
name: "Satoru",
|
|
2138
2120
|
gender: "male",
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/nPczCjzI2devNBz1zQrb/2dd3e72c-4fd3-42f1-93ea-abc5d4e5aa1d.mp3"
|
|
2121
|
+
languages: ["ja"],
|
|
2122
|
+
description: "\u82E5\u3005\u3057\u304F\u3082\u843D\u3061\u7740\u304D\u306E\u3042\u308B\u7537\u6027\u306E\u58F0\u3002\u65E5\u5E38\u306E\u8A9E\u308A\u3084\u30EA\u30E9\u30C3\u30AF\u30B9\u3057\u305F\u30CA\u30EC\u30FC\u30B7\u30E7\u30F3\u306B\u6700\u9069\u3002",
|
|
2123
|
+
previewUrl: "https://platform.r2.fish.audio/task/1b4712fb831f4c149eef6b0133157418.mp3"
|
|
2143
2124
|
},
|
|
2144
2125
|
{
|
|
2145
|
-
id: "
|
|
2146
|
-
name: "
|
|
2147
|
-
gender: "
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/onwK4e9ZLuTAKqWW03F9/7eee0236-1a72-4b86-b303-5dcadc007ba9.mp3"
|
|
2126
|
+
id: "0b14f34a13a94ed88fe6113193749bb0",
|
|
2127
|
+
name: "Fatima",
|
|
2128
|
+
gender: "female",
|
|
2129
|
+
languages: ["ar"],
|
|
2130
|
+
description: "\u0635\u0648\u062A \u0648\u0627\u0636\u062D \u0648\u0648\u0627\u062B\u0642 \u064A\u0646\u0627\u0633\u0628 \u0627\u0644\u0645\u0633\u0627\u0639\u062F\u0627\u062A \u0627\u0644\u0635\u0648\u062A\u064A\u0629 \u0648\u062E\u062F\u0645\u0629 \u0627\u0644\u0639\u0645\u0644\u0627\u0621 \u0648\u0627\u0644\u0639\u0631\u0648\u0636 \u0627\u0644\u062A\u0642\u062F\u064A\u0645\u064A\u0629.",
|
|
2131
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/93406e1b905a4c4994306e3421243390.mp3"
|
|
2152
2132
|
},
|
|
2153
2133
|
{
|
|
2154
|
-
id: "
|
|
2155
|
-
name: "
|
|
2134
|
+
id: "74c6aba5cbf94a15bbdc547ffce5cb38",
|
|
2135
|
+
name: "Yutong",
|
|
2156
2136
|
gender: "female",
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2137
|
+
languages: ["zh"],
|
|
2138
|
+
description: "\u58F0\u97F3\u6E05\u6670\u81EA\u7136\uFF0C\u8868\u8FBE\u5229\u843D\uFF0C\u9002\u5408\u8BED\u97F3\u52A9\u624B\u3001\u5BA2\u670D\u548C\u65E5\u5E38\u64AD\u62A5\u3002",
|
|
2139
|
+
previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a48320f37fe44c4699549a1afc3778e8.mp3"
|
|
2140
|
+
}
|
|
2141
|
+
];
|
|
2142
|
+
|
|
2143
|
+
// src/catalog/speech-fish.ts
|
|
2144
|
+
var FISH_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/fishaudio.png";
|
|
2145
|
+
var PER_CALL2 = { unit: "images" };
|
|
2146
|
+
var PER_REQUEST2 = { unit: "requests" };
|
|
2147
|
+
var EXPOSURE4 = ["cowork", "space_builder"];
|
|
2148
|
+
var TTS_MAX_CHARS = 1e4;
|
|
2149
|
+
function fishTts(id, label, providerModelId, description) {
|
|
2150
|
+
return {
|
|
2151
|
+
id,
|
|
2152
|
+
aliases: [],
|
|
2153
|
+
label,
|
|
2154
|
+
type: "speech",
|
|
2155
|
+
adapter: "fish",
|
|
2156
|
+
providerModelId,
|
|
2157
|
+
lifecycle: "active",
|
|
2158
|
+
exposure: EXPOSURE4,
|
|
2159
|
+
constraints: {
|
|
2160
|
+
voices: FISH_VOICES,
|
|
2161
|
+
outputFormats: ["mp3", "wav"],
|
|
2162
|
+
supports: [],
|
|
2163
|
+
prompt: { referenceSyntax: "numbered", maxChars: TTS_MAX_CHARS }
|
|
2164
|
+
},
|
|
2165
|
+
defaults: { voice: "9a9cf47702da476aa4629e2506d4a857", outputFormat: "mp3" },
|
|
2166
|
+
usage: PER_CALL2,
|
|
2167
|
+
pricingKind: "flat",
|
|
2168
|
+
display: { provider: "fishaudio", logo: FISH_LOGO, description },
|
|
2169
|
+
capabilities: { customVoices: true }
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
var FISH_MODELS = [
|
|
2173
|
+
fishTts(
|
|
2174
|
+
"fish-s2.1-pro",
|
|
2175
|
+
"Fish Audio S2.1 Pro",
|
|
2176
|
+
"s2.1-pro",
|
|
2177
|
+
"Fish Audio S2.1 Pro text to speech in 83 languages, with [bracket] directions and your own cloned voices."
|
|
2178
|
+
),
|
|
2179
|
+
fishTts(
|
|
2180
|
+
"fish-s2.1-pro-free",
|
|
2181
|
+
"Fish Audio S2.1 Pro Free",
|
|
2182
|
+
"s2.1-pro-free",
|
|
2183
|
+
"Fish Audio S2.1 Pro on the free developer tier: the same voices at no cost, without latency or data-processing guarantees."
|
|
2184
|
+
),
|
|
2185
|
+
fishTts(
|
|
2186
|
+
"fish-drama-3",
|
|
2187
|
+
"Fish Audio Drama 3",
|
|
2188
|
+
"drama-3",
|
|
2189
|
+
"Fish Audio Drama 3 text to speech you direct like an actor: [directions], sounds, pauses and per-word tags."
|
|
2190
|
+
),
|
|
2191
|
+
{
|
|
2192
|
+
id: "fish-voice-clone",
|
|
2193
|
+
aliases: [],
|
|
2194
|
+
label: "Fish Audio Voice Clone",
|
|
2195
|
+
type: "voice-clone",
|
|
2196
|
+
adapter: "fish",
|
|
2197
|
+
providerModelId: "voice-clone",
|
|
2198
|
+
lifecycle: "active",
|
|
2199
|
+
exposure: EXPOSURE4,
|
|
2200
|
+
constraints: {
|
|
2201
|
+
// POST /model takes 1-20 voice files; Fish advises 10 s or more of clean speech each.
|
|
2202
|
+
references: { audios: 20, audioFormats: ["wav", "mp3", "m4a", "opus"] },
|
|
2203
|
+
supports: []
|
|
2204
|
+
},
|
|
2205
|
+
defaults: {},
|
|
2206
|
+
usage: PER_REQUEST2,
|
|
2207
|
+
pricingKind: "flat",
|
|
2208
|
+
display: {
|
|
2209
|
+
provider: "fishaudio",
|
|
2210
|
+
logo: FISH_LOGO,
|
|
2211
|
+
description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on the Fish Audio speech models."
|
|
2212
|
+
},
|
|
2213
|
+
capabilities: {}
|
|
2161
2214
|
},
|
|
2162
2215
|
{
|
|
2163
|
-
id: "
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2216
|
+
id: "fish-voice-design-1",
|
|
2217
|
+
aliases: [],
|
|
2218
|
+
label: "Fish Audio Voice Design",
|
|
2219
|
+
type: "voice-design",
|
|
2220
|
+
adapter: "fish",
|
|
2221
|
+
providerModelId: "voice-design-1",
|
|
2222
|
+
lifecycle: "active",
|
|
2223
|
+
exposure: EXPOSURE4,
|
|
2224
|
+
constraints: {
|
|
2225
|
+
voiceDesign: { maxCandidates: 4, previewMaxChars: 150 },
|
|
2226
|
+
supports: [],
|
|
2227
|
+
prompt: { referenceSyntax: "numbered", maxChars: 2e3 }
|
|
2228
|
+
},
|
|
2229
|
+
defaults: {},
|
|
2230
|
+
usage: PER_REQUEST2,
|
|
2231
|
+
pricingKind: "flat",
|
|
2232
|
+
display: {
|
|
2233
|
+
provider: "fishaudio",
|
|
2234
|
+
logo: FISH_LOGO,
|
|
2235
|
+
description: "Describe a voice and get up to four short candidates; keep one as a cloned voice."
|
|
2236
|
+
},
|
|
2237
|
+
capabilities: {}
|
|
2170
2238
|
}
|
|
2171
2239
|
];
|
|
2172
2240
|
|
|
@@ -3745,12 +3813,12 @@ var MINIMAX_VOICES = [
|
|
|
3745
3813
|
];
|
|
3746
3814
|
|
|
3747
3815
|
// src/catalog/media-nodes.ts
|
|
3748
|
-
var
|
|
3816
|
+
var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
|
|
3749
3817
|
var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
|
|
3750
3818
|
var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
|
|
3751
3819
|
var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
|
|
3752
|
-
var
|
|
3753
|
-
var
|
|
3820
|
+
var PER_CALL3 = { unit: "images" };
|
|
3821
|
+
var EXPOSURE5 = ["cowork", "space_builder"];
|
|
3754
3822
|
var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
|
|
3755
3823
|
function elevenLabsTts(tts) {
|
|
3756
3824
|
return {
|
|
@@ -3761,7 +3829,7 @@ function elevenLabsTts(tts) {
|
|
|
3761
3829
|
adapter: "fal",
|
|
3762
3830
|
providerModelId: tts.providerModelId,
|
|
3763
3831
|
lifecycle: "active",
|
|
3764
|
-
exposure:
|
|
3832
|
+
exposure: EXPOSURE5,
|
|
3765
3833
|
constraints: {
|
|
3766
3834
|
voices: ELEVENLABS_VOICES,
|
|
3767
3835
|
languages: tts.languages,
|
|
@@ -3770,9 +3838,9 @@ function elevenLabsTts(tts) {
|
|
|
3770
3838
|
prompt: { referenceSyntax: "numbered", maxChars: tts.maxChars }
|
|
3771
3839
|
},
|
|
3772
3840
|
defaults: { voice: "Rachel", ...tts.formats && { outputFormat: "mp3_44100_128" } },
|
|
3773
|
-
usage:
|
|
3841
|
+
usage: PER_CALL3,
|
|
3774
3842
|
pricingKind: "flat",
|
|
3775
|
-
display: { provider: "elevenlabs", logo:
|
|
3843
|
+
display: { provider: "elevenlabs", logo: ELEVENLABS_LOGO2, description: tts.description },
|
|
3776
3844
|
capabilities: {}
|
|
3777
3845
|
};
|
|
3778
3846
|
}
|
|
@@ -3786,7 +3854,7 @@ function geminiTts(id, label, description) {
|
|
|
3786
3854
|
adapter: "fal",
|
|
3787
3855
|
providerModelId,
|
|
3788
3856
|
lifecycle: "active",
|
|
3789
|
-
exposure:
|
|
3857
|
+
exposure: EXPOSURE5,
|
|
3790
3858
|
constraints: {
|
|
3791
3859
|
voices: GEMINI_TTS_VOICES,
|
|
3792
3860
|
supports: ["style"],
|
|
@@ -3794,7 +3862,7 @@ function geminiTts(id, label, description) {
|
|
|
3794
3862
|
prompt: { referenceSyntax: "numbered", maxChars: 8192 }
|
|
3795
3863
|
},
|
|
3796
3864
|
defaults: { voice: "Kore" },
|
|
3797
|
-
usage:
|
|
3865
|
+
usage: PER_CALL3,
|
|
3798
3866
|
pricingKind: "flat",
|
|
3799
3867
|
display: { provider: "google", logo: GOOGLE_LOGO, description },
|
|
3800
3868
|
capabilities: {}
|
|
@@ -3809,7 +3877,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
|
|
|
3809
3877
|
adapter: "fal",
|
|
3810
3878
|
providerModelId,
|
|
3811
3879
|
lifecycle: "active",
|
|
3812
|
-
exposure:
|
|
3880
|
+
exposure: EXPOSURE5,
|
|
3813
3881
|
constraints: {
|
|
3814
3882
|
requires: ["sourceVideo", "sourceAudio"],
|
|
3815
3883
|
references: LIPSYNC_AUDIO,
|
|
@@ -3817,7 +3885,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
|
|
|
3817
3885
|
},
|
|
3818
3886
|
defaults: {},
|
|
3819
3887
|
...syncModel !== void 0 && { providerOptions: { fal: { model: syncModel } } },
|
|
3820
|
-
usage:
|
|
3888
|
+
usage: PER_CALL3,
|
|
3821
3889
|
pricingKind: "flat",
|
|
3822
3890
|
display: { provider: "sync", logo: SYNC_LOGO, description },
|
|
3823
3891
|
capabilities: { acceptsSourceVideo: true }
|
|
@@ -3870,7 +3938,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3870
3938
|
adapter: "fal",
|
|
3871
3939
|
providerModelId: "fal-ai/minimax/speech-2.8-hd",
|
|
3872
3940
|
lifecycle: "active",
|
|
3873
|
-
exposure:
|
|
3941
|
+
exposure: EXPOSURE5,
|
|
3874
3942
|
constraints: {
|
|
3875
3943
|
voices: MINIMAX_VOICES,
|
|
3876
3944
|
languages: MINIMAX_SPEECH_LANGUAGES,
|
|
@@ -3878,7 +3946,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3878
3946
|
prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
|
|
3879
3947
|
},
|
|
3880
3948
|
defaults: { voice: "Wise_Woman" },
|
|
3881
|
-
usage:
|
|
3949
|
+
usage: PER_CALL3,
|
|
3882
3950
|
pricingKind: "flat",
|
|
3883
3951
|
display: {
|
|
3884
3952
|
provider: "minimax",
|
|
@@ -3895,7 +3963,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3895
3963
|
adapter: "fal",
|
|
3896
3964
|
providerModelId: "fal-ai/elevenlabs/voice-changer",
|
|
3897
3965
|
lifecycle: "active",
|
|
3898
|
-
exposure:
|
|
3966
|
+
exposure: EXPOSURE5,
|
|
3899
3967
|
constraints: {
|
|
3900
3968
|
requires: ["sourceAudio"],
|
|
3901
3969
|
// The flat price assumes at most a minute of speech.
|
|
@@ -3905,11 +3973,11 @@ var MEDIA_NODE_MODELS = [
|
|
|
3905
3973
|
supports: []
|
|
3906
3974
|
},
|
|
3907
3975
|
defaults: { voice: "Rachel", outputFormat: "mp3_44100_128" },
|
|
3908
|
-
usage:
|
|
3976
|
+
usage: PER_CALL3,
|
|
3909
3977
|
pricingKind: "flat",
|
|
3910
3978
|
display: {
|
|
3911
3979
|
provider: "elevenlabs",
|
|
3912
|
-
logo:
|
|
3980
|
+
logo: ELEVENLABS_LOGO2,
|
|
3913
3981
|
description: "ElevenLabs speech to speech; re-voices a recording in another voice and keeps its timing and delivery."
|
|
3914
3982
|
},
|
|
3915
3983
|
capabilities: {}
|
|
@@ -3922,7 +3990,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3922
3990
|
adapter: "fal",
|
|
3923
3991
|
providerModelId: "fal-ai/elevenlabs/dubbing",
|
|
3924
3992
|
lifecycle: "active",
|
|
3925
|
-
exposure:
|
|
3993
|
+
exposure: EXPOSURE5,
|
|
3926
3994
|
constraints: {
|
|
3927
3995
|
requiresOneOf: ["sourceVideo", "sourceAudio"],
|
|
3928
3996
|
// The flat price covers 60 s of source.
|
|
@@ -3932,11 +4000,11 @@ var MEDIA_NODE_MODELS = [
|
|
|
3932
4000
|
supports: []
|
|
3933
4001
|
},
|
|
3934
4002
|
defaults: {},
|
|
3935
|
-
usage:
|
|
4003
|
+
usage: PER_CALL3,
|
|
3936
4004
|
pricingKind: "flat",
|
|
3937
4005
|
display: {
|
|
3938
4006
|
provider: "elevenlabs",
|
|
3939
|
-
logo:
|
|
4007
|
+
logo: ELEVENLABS_LOGO2,
|
|
3940
4008
|
description: "ElevenLabs dubbing; translates the speech in a video or audio file into another language in the original voices."
|
|
3941
4009
|
},
|
|
3942
4010
|
capabilities: { acceptsSourceVideo: true }
|
|
@@ -3970,7 +4038,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3970
4038
|
adapter: "fal",
|
|
3971
4039
|
providerModelId: "veed/lipsync/v2",
|
|
3972
4040
|
lifecycle: "active",
|
|
3973
|
-
exposure:
|
|
4041
|
+
exposure: EXPOSURE5,
|
|
3974
4042
|
constraints: {
|
|
3975
4043
|
requires: ["sourceVideo", "sourceAudio"],
|
|
3976
4044
|
references: LIPSYNC_AUDIO,
|
|
@@ -3979,7 +4047,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3979
4047
|
supports: []
|
|
3980
4048
|
},
|
|
3981
4049
|
defaults: {},
|
|
3982
|
-
usage:
|
|
4050
|
+
usage: PER_CALL3,
|
|
3983
4051
|
pricingKind: "flat",
|
|
3984
4052
|
display: {
|
|
3985
4053
|
provider: "veed",
|
|
@@ -3996,7 +4064,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
3996
4064
|
adapter: "fal",
|
|
3997
4065
|
providerModelId: "minimax/h3-max/lip-sync/image-to-video",
|
|
3998
4066
|
lifecycle: "active",
|
|
3999
|
-
exposure:
|
|
4067
|
+
exposure: EXPOSURE5,
|
|
4000
4068
|
constraints: {
|
|
4001
4069
|
requires: ["firstFrame", "sourceAudio"],
|
|
4002
4070
|
resolutions: ["480p", "768p", "1080p", "2k"],
|
|
@@ -4022,7 +4090,7 @@ var MEDIA_NODE_MODELS = [
|
|
|
4022
4090
|
adapter: "fal",
|
|
4023
4091
|
providerModelId: "fal-ai/elevenlabs/speech-to-text/scribe-v2",
|
|
4024
4092
|
lifecycle: "active",
|
|
4025
|
-
exposure:
|
|
4093
|
+
exposure: EXPOSURE5,
|
|
4026
4094
|
constraints: {
|
|
4027
4095
|
requires: ["sourceAudio"],
|
|
4028
4096
|
// The flat price covers 10 minutes of audio.
|
|
@@ -4031,17 +4099,18 @@ var MEDIA_NODE_MODELS = [
|
|
|
4031
4099
|
supports: []
|
|
4032
4100
|
},
|
|
4033
4101
|
defaults: {},
|
|
4034
|
-
usage:
|
|
4102
|
+
usage: PER_CALL3,
|
|
4035
4103
|
pricingKind: "flat",
|
|
4036
4104
|
display: {
|
|
4037
4105
|
provider: "elevenlabs",
|
|
4038
|
-
logo:
|
|
4106
|
+
logo: ELEVENLABS_LOGO2,
|
|
4039
4107
|
description: "ElevenLabs Scribe v2 speech to text with word timings, speaker labels and audio-event tags."
|
|
4040
4108
|
},
|
|
4041
4109
|
capabilities: {}
|
|
4042
4110
|
},
|
|
4043
4111
|
SAM_3_IMAGE,
|
|
4044
|
-
...FISH_MODELS
|
|
4112
|
+
...FISH_MODELS,
|
|
4113
|
+
...ELEVENLABS_DIRECT_MODELS
|
|
4045
4114
|
];
|
|
4046
4115
|
|
|
4047
4116
|
// src/catalog/model3d.ts
|
|
@@ -4237,7 +4306,7 @@ var MODEL3D_MODELS = [
|
|
|
4237
4306
|
|
|
4238
4307
|
// src/catalog/other.ts
|
|
4239
4308
|
var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
|
|
4240
|
-
var
|
|
4309
|
+
var ELEVENLABS_LOGO3 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
|
|
4241
4310
|
var MINIMAX_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
|
|
4242
4311
|
var OTHER_MODELS = [
|
|
4243
4312
|
{
|
|
@@ -4402,7 +4471,7 @@ var OTHER_MODELS = [
|
|
|
4402
4471
|
pricingKind: "flat",
|
|
4403
4472
|
display: {
|
|
4404
4473
|
provider: "elevenlabs",
|
|
4405
|
-
logo:
|
|
4474
|
+
logo: ELEVENLABS_LOGO3,
|
|
4406
4475
|
description: "ElevenLabs Music v2.5 text-to-music; full songs or instrumentals from 3 seconds to 1 minute."
|
|
4407
4476
|
},
|
|
4408
4477
|
capabilities: {}
|
|
@@ -4454,7 +4523,7 @@ var OTHER_MODELS = [
|
|
|
4454
4523
|
pricingKind: "flat",
|
|
4455
4524
|
display: {
|
|
4456
4525
|
provider: "elevenlabs",
|
|
4457
|
-
logo:
|
|
4526
|
+
logo: ELEVENLABS_LOGO3,
|
|
4458
4527
|
description: "ElevenLabs Sound Effects v2; a sound effect from a text description, up to 22 seconds."
|
|
4459
4528
|
},
|
|
4460
4529
|
capabilities: {}
|
|
@@ -5636,7 +5705,7 @@ var VIDEO_FAMILY_MODELS = [
|
|
|
5636
5705
|
|
|
5637
5706
|
// src/catalog/video-luma.ts
|
|
5638
5707
|
var VIDEO_USAGE2 = { unit: "seconds" };
|
|
5639
|
-
var
|
|
5708
|
+
var EXPOSURE6 = ["cowork", "space_builder"];
|
|
5640
5709
|
var LUMA_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/luma-color.png";
|
|
5641
5710
|
var RAY_RATIOS = ["9:16", "3:4", "1:1", "4:3", "16:9", "21:9"];
|
|
5642
5711
|
var RAY_RESOLUTIONS = ["360p", "720p", "1080p"];
|
|
@@ -5654,7 +5723,7 @@ var LUMA_VIDEO_MODELS = [
|
|
|
5654
5723
|
adapter: "luma",
|
|
5655
5724
|
providerModelId: "ray-3.2",
|
|
5656
5725
|
lifecycle: "active",
|
|
5657
|
-
exposure:
|
|
5726
|
+
exposure: EXPOSURE6,
|
|
5658
5727
|
constraints: {
|
|
5659
5728
|
aspectRatios: ["adaptive", ...RAY_RATIOS],
|
|
5660
5729
|
resolutions: [...RAY_RESOLUTIONS],
|
|
@@ -5686,7 +5755,7 @@ var LUMA_VIDEO_MODELS = [
|
|
|
5686
5755
|
adapter: "luma",
|
|
5687
5756
|
providerModelId: "ray-3.2",
|
|
5688
5757
|
lifecycle: "active",
|
|
5689
|
-
exposure:
|
|
5758
|
+
exposure: EXPOSURE6,
|
|
5690
5759
|
constraints: {
|
|
5691
5760
|
resolutions: [...RAY_RESOLUTIONS],
|
|
5692
5761
|
requires: ["sourceVideo"],
|
|
@@ -5719,7 +5788,7 @@ var LUMA_VIDEO_MODELS = [
|
|
|
5719
5788
|
adapter: "luma",
|
|
5720
5789
|
providerModelId: "ray-3.2",
|
|
5721
5790
|
lifecycle: "active",
|
|
5722
|
-
exposure:
|
|
5791
|
+
exposure: EXPOSURE6,
|
|
5723
5792
|
constraints: {
|
|
5724
5793
|
aspectRatios: [...RAY_RATIOS],
|
|
5725
5794
|
resolutions: [...RAY_RESOLUTIONS],
|
|
@@ -7677,7 +7746,8 @@ var ADAPTER_IDS = [
|
|
|
7677
7746
|
"higgsfield",
|
|
7678
7747
|
"xai-video",
|
|
7679
7748
|
"luma",
|
|
7680
|
-
"fish"
|
|
7749
|
+
"fish",
|
|
7750
|
+
"elevenlabs"
|
|
7681
7751
|
];
|
|
7682
7752
|
var ModelTypeSchema = import_zod3.z.enum([
|
|
7683
7753
|
"text",
|
|
@@ -8890,16 +8960,16 @@ async function generateSeedreamImage(req, ctx) {
|
|
|
8890
8960
|
} else {
|
|
8891
8961
|
throwArkError(void 0, "Seedream returned no image URL or base64 data", modelId);
|
|
8892
8962
|
}
|
|
8893
|
-
const
|
|
8963
|
+
const usage5 = {
|
|
8894
8964
|
model: modelId,
|
|
8895
8965
|
provider: "ark",
|
|
8896
8966
|
modality: "IMAGE",
|
|
8897
8967
|
unit: "images",
|
|
8898
8968
|
quantity: 1
|
|
8899
8969
|
};
|
|
8900
|
-
if (req.size !== void 0)
|
|
8901
|
-
if (req.quality !== void 0)
|
|
8902
|
-
return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage:
|
|
8970
|
+
if (req.size !== void 0) usage5.imageSize = req.size;
|
|
8971
|
+
if (req.quality !== void 0) usage5.imageQuality = req.quality;
|
|
8972
|
+
return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage5, raw: data };
|
|
8903
8973
|
}
|
|
8904
8974
|
var SeedanceUsageSchema = import_zod7.z.object({
|
|
8905
8975
|
total_tokens: import_zod7.z.number().optional(),
|
|
@@ -8940,7 +9010,7 @@ function seedanceErrorMessage(error, status) {
|
|
|
8940
9010
|
if (asObject.success && asObject.data.message !== void 0) return asObject.data.message;
|
|
8941
9011
|
return `Seedance task ${status}`;
|
|
8942
9012
|
}
|
|
8943
|
-
function seedanceJob(ctx, key, jobId,
|
|
9013
|
+
function seedanceJob(ctx, key, jobId, usage5) {
|
|
8944
9014
|
const modelId = ctx.model.id;
|
|
8945
9015
|
const fetchStatus = async () => {
|
|
8946
9016
|
const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
|
|
@@ -8990,21 +9060,21 @@ function seedanceJob(ctx, key, jobId, usage4) {
|
|
|
8990
9060
|
modelId
|
|
8991
9061
|
);
|
|
8992
9062
|
if (polled.duration !== void 0 && polled.duration > 0) {
|
|
8993
|
-
|
|
8994
|
-
|
|
9063
|
+
usage5.quantity = polled.duration;
|
|
9064
|
+
usage5.durationSeconds = polled.duration;
|
|
8995
9065
|
}
|
|
8996
9066
|
const rendered = VideoResolutionSchema.safeParse(
|
|
8997
9067
|
polled.resolution?.toLowerCase()
|
|
8998
9068
|
);
|
|
8999
|
-
if (rendered.success)
|
|
9069
|
+
if (rendered.success) usage5.resolution = rendered.data;
|
|
9000
9070
|
const tokens = polled.usage;
|
|
9001
9071
|
if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
|
|
9002
|
-
|
|
9003
|
-
|
|
9072
|
+
usage5.inputTokens = tokens.total_tokens - tokens.completion_tokens;
|
|
9073
|
+
usage5.outputTokens = tokens.completion_tokens;
|
|
9004
9074
|
}
|
|
9005
9075
|
return {
|
|
9006
9076
|
video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
|
|
9007
|
-
usage:
|
|
9077
|
+
usage: usage5,
|
|
9008
9078
|
raw: polled
|
|
9009
9079
|
};
|
|
9010
9080
|
}
|
|
@@ -9110,8 +9180,8 @@ async function generateSeedanceVideo(req, ctx) {
|
|
|
9110
9180
|
);
|
|
9111
9181
|
if (!submit.id) throwArkError(void 0, "Seedance task create returned no id", modelId);
|
|
9112
9182
|
const jobId = submit.id;
|
|
9113
|
-
const
|
|
9114
|
-
return seedanceJob(ctx, key, jobId,
|
|
9183
|
+
const usage5 = seedanceUsage(ctx, req, taskType === "edit" ? requestedDuration : duration);
|
|
9184
|
+
return seedanceJob(ctx, key, jobId, usage5);
|
|
9115
9185
|
}
|
|
9116
9186
|
|
|
9117
9187
|
// src/adapters/fal.ts
|
|
@@ -10599,7 +10669,7 @@ async function collectFalPlanImages(payload, plan4, ctx, req) {
|
|
|
10599
10669
|
return { bytes, mime: mime || "image/png" };
|
|
10600
10670
|
})
|
|
10601
10671
|
);
|
|
10602
|
-
const
|
|
10672
|
+
const usage5 = {
|
|
10603
10673
|
model: ctx.model.id,
|
|
10604
10674
|
provider: "fal",
|
|
10605
10675
|
modality: "IMAGE",
|
|
@@ -10607,9 +10677,9 @@ async function collectFalPlanImages(payload, plan4, ctx, req) {
|
|
|
10607
10677
|
quantity: images.length
|
|
10608
10678
|
};
|
|
10609
10679
|
const billedSize = plan4.imageSize ?? req.size;
|
|
10610
|
-
if (billedSize !== void 0 && ctx.model.type === "image")
|
|
10611
|
-
if (plan4.imageQuality !== void 0)
|
|
10612
|
-
const result = { images, usage:
|
|
10680
|
+
if (billedSize !== void 0 && ctx.model.type === "image") usage5.imageSize = billedSize;
|
|
10681
|
+
if (plan4.imageQuality !== void 0) usage5.imageQuality = plan4.imageQuality;
|
|
10682
|
+
const result = { images, usage: usage5, raw: payload };
|
|
10613
10683
|
const remote = files.find((f) => !f.url.startsWith("data:"));
|
|
10614
10684
|
if (remote !== void 0) result.outputUrl = remote.url;
|
|
10615
10685
|
return result;
|
|
@@ -12772,9 +12842,282 @@ function planUsage(ctx, plan4) {
|
|
|
12772
12842
|
};
|
|
12773
12843
|
}
|
|
12774
12844
|
|
|
12775
|
-
// src/adapters/
|
|
12845
|
+
// src/adapters/elevenlabs.ts
|
|
12776
12846
|
var import_zod21 = require("zod");
|
|
12777
|
-
var DEFAULT_BASE_URL = "https://api.
|
|
12847
|
+
var DEFAULT_BASE_URL = "https://api.elevenlabs.io";
|
|
12848
|
+
var DEFAULT_FORMAT = "mp3_44100_128";
|
|
12849
|
+
var ELEVENLABS_TTS_MODEL_IDS = /* @__PURE__ */ new Set(["eleven_v4"]);
|
|
12850
|
+
var ELEVENLABS_IVC = "ivc";
|
|
12851
|
+
var VOICE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
12852
|
+
function elevenLabsRowRefusal(row2) {
|
|
12853
|
+
const id = row2.providerModelId;
|
|
12854
|
+
if (row2.type === "speech") {
|
|
12855
|
+
return ELEVENLABS_TTS_MODEL_IDS.has(id) ? void 0 : `elevenlabs speech rows need providerModelId ${[...ELEVENLABS_TTS_MODEL_IDS].join(", ")}`;
|
|
12856
|
+
}
|
|
12857
|
+
if (row2.type === "voice-clone") {
|
|
12858
|
+
return id === ELEVENLABS_IVC ? void 0 : `elevenlabs voice-clone rows need providerModelId ${ELEVENLABS_IVC}`;
|
|
12859
|
+
}
|
|
12860
|
+
return `elevenlabs has no ${row2.type} rows`;
|
|
12861
|
+
}
|
|
12862
|
+
function elevenLabsError(ctx, code, message) {
|
|
12863
|
+
return new ModelKitError({
|
|
12864
|
+
code,
|
|
12865
|
+
message,
|
|
12866
|
+
provider: "elevenlabs",
|
|
12867
|
+
model: ctx.model.id,
|
|
12868
|
+
retryable: false
|
|
12869
|
+
});
|
|
12870
|
+
}
|
|
12871
|
+
function checkRow(ctx, type) {
|
|
12872
|
+
const refusal2 = ctx.model.type === type ? elevenLabsRowRefusal(ctx.model) : void 0;
|
|
12873
|
+
if (ctx.model.type !== type || refusal2 !== void 0) {
|
|
12874
|
+
throw elevenLabsError(
|
|
12875
|
+
ctx,
|
|
12876
|
+
"unsupported_option",
|
|
12877
|
+
refusal2 ?? `${ctx.model.id} is not a ${type} row`
|
|
12878
|
+
);
|
|
12879
|
+
}
|
|
12880
|
+
}
|
|
12881
|
+
function checkVoiceId(ctx, field, id) {
|
|
12882
|
+
if (!VOICE_ID.test(id))
|
|
12883
|
+
throw elevenLabsError(ctx, "unsupported_option", `${field} is not a voice id`);
|
|
12884
|
+
return id;
|
|
12885
|
+
}
|
|
12886
|
+
function elevenLabsKey(ctx) {
|
|
12887
|
+
const key = ctx.keys.elevenlabs;
|
|
12888
|
+
if (!key) throw elevenLabsError(ctx, "auth", "Missing API key for elevenlabs");
|
|
12889
|
+
return key;
|
|
12890
|
+
}
|
|
12891
|
+
function elevenLabsUrl(ctx, path) {
|
|
12892
|
+
return `${ctx.endpoints?.elevenlabs ?? DEFAULT_BASE_URL}${path}`;
|
|
12893
|
+
}
|
|
12894
|
+
var errorDetail = import_zod21.z.object({
|
|
12895
|
+
detail: import_zod21.z.object({ status: import_zod21.z.string(), message: import_zod21.z.string().optional() })
|
|
12896
|
+
});
|
|
12897
|
+
function errorStatus(body) {
|
|
12898
|
+
const parsed = errorDetail.safeParse(body);
|
|
12899
|
+
return parsed.success ? parsed.data.detail : void 0;
|
|
12900
|
+
}
|
|
12901
|
+
function classifyResponse(ctx, status, body) {
|
|
12902
|
+
const error = classifyHttpError(status, body, "elevenlabs", ctx.model.id);
|
|
12903
|
+
const detail = errorStatus(body);
|
|
12904
|
+
if (detail?.status === "voice_limit_reached") {
|
|
12905
|
+
return new ModelKitError({
|
|
12906
|
+
code: "provider_rejected",
|
|
12907
|
+
message: detail.message ?? "ElevenLabs voice limit reached",
|
|
12908
|
+
provider: "elevenlabs",
|
|
12909
|
+
model: ctx.model.id,
|
|
12910
|
+
status,
|
|
12911
|
+
body,
|
|
12912
|
+
cause: error,
|
|
12913
|
+
retryable: false
|
|
12914
|
+
});
|
|
12915
|
+
}
|
|
12916
|
+
if (detail?.status === "quota_exceeded") {
|
|
12917
|
+
return new ModelKitError({
|
|
12918
|
+
code: "provider_billing",
|
|
12919
|
+
message: error.message,
|
|
12920
|
+
provider: "elevenlabs",
|
|
12921
|
+
model: ctx.model.id,
|
|
12922
|
+
status,
|
|
12923
|
+
body,
|
|
12924
|
+
cause: error,
|
|
12925
|
+
retryable: false
|
|
12926
|
+
});
|
|
12927
|
+
}
|
|
12928
|
+
return error;
|
|
12929
|
+
}
|
|
12930
|
+
async function elevenLabsFetch(ctx, path, init) {
|
|
12931
|
+
throwIfAborted(ctx.signal);
|
|
12932
|
+
const request = {
|
|
12933
|
+
method: init.method,
|
|
12934
|
+
headers: { "xi-api-key": elevenLabsKey(ctx), ...init.headers }
|
|
12935
|
+
};
|
|
12936
|
+
if (init.body !== void 0) request.body = init.body;
|
|
12937
|
+
if (ctx.signal !== void 0) request.signal = ctx.signal;
|
|
12938
|
+
let res;
|
|
12939
|
+
try {
|
|
12940
|
+
res = await ctx.fetch(elevenLabsUrl(ctx, path), request);
|
|
12941
|
+
} catch (cause) {
|
|
12942
|
+
throw classifyThrown("elevenlabs", cause, ctx.model.id);
|
|
12943
|
+
}
|
|
12944
|
+
if (!res.ok) throw classifyResponse(ctx, res.status, await readBody(res));
|
|
12945
|
+
return res;
|
|
12946
|
+
}
|
|
12947
|
+
function usage2(ctx) {
|
|
12948
|
+
return {
|
|
12949
|
+
model: ctx.model.id,
|
|
12950
|
+
provider: "elevenlabs",
|
|
12951
|
+
modality: "AUDIO",
|
|
12952
|
+
unit: ctx.model.usage.unit,
|
|
12953
|
+
quantity: 1
|
|
12954
|
+
};
|
|
12955
|
+
}
|
|
12956
|
+
function audioMime(res) {
|
|
12957
|
+
const header = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
12958
|
+
return header?.startsWith("audio/") ? header : "audio/mpeg";
|
|
12959
|
+
}
|
|
12960
|
+
async function generateSpeech(req, ctx) {
|
|
12961
|
+
checkRow(ctx, "speech");
|
|
12962
|
+
const voice = req.customVoice ?? req.voice;
|
|
12963
|
+
if (voice === void 0) throw elevenLabsError(ctx, "missing_input", "needs a voice");
|
|
12964
|
+
const voiceId = checkVoiceId(ctx, req.customVoice ? "customVoice" : "voice", voice);
|
|
12965
|
+
const format = req.outputFormat ?? DEFAULT_FORMAT;
|
|
12966
|
+
const body = { text: req.text, model_id: ctx.model.providerModelId };
|
|
12967
|
+
if (req.language !== void 0) body.language_code = req.language;
|
|
12968
|
+
const path = `/v1/text-to-speech/${encodeURIComponent(voiceId)}?output_format=${encodeURIComponent(format)}`;
|
|
12969
|
+
const { bytes, mime } = await withRetry(
|
|
12970
|
+
async () => {
|
|
12971
|
+
const res = await elevenLabsFetch(ctx, path, {
|
|
12972
|
+
method: "POST",
|
|
12973
|
+
headers: { "Content-Type": "application/json" },
|
|
12974
|
+
body: JSON.stringify(body)
|
|
12975
|
+
});
|
|
12976
|
+
let audio;
|
|
12977
|
+
try {
|
|
12978
|
+
audio = new Uint8Array(await res.arrayBuffer());
|
|
12979
|
+
} catch (cause) {
|
|
12980
|
+
throw classifyThrown("elevenlabs", cause, ctx.model.id);
|
|
12981
|
+
}
|
|
12982
|
+
return { bytes: audio, mime: audioMime(res) };
|
|
12983
|
+
},
|
|
12984
|
+
mediaRetryOptions(ctx.call, ctx.signal)
|
|
12985
|
+
);
|
|
12986
|
+
if (bytes.byteLength === 0)
|
|
12987
|
+
throw elevenLabsError(ctx, "provider_rejected", "ElevenLabs TTS returned no audio");
|
|
12988
|
+
return { audio: { bytes, mime }, usage: usage2(ctx) };
|
|
12989
|
+
}
|
|
12990
|
+
var createdVoiceSchema = import_zod21.z.object({
|
|
12991
|
+
voice_id: import_zod21.z.string().min(1),
|
|
12992
|
+
requires_verification: import_zod21.z.boolean().optional().catch(void 0)
|
|
12993
|
+
});
|
|
12994
|
+
function sampleMime(mime) {
|
|
12995
|
+
return mime.startsWith("audio/") || mime.startsWith("video/") ? mime : "application/octet-stream";
|
|
12996
|
+
}
|
|
12997
|
+
function fileName(index, mime, ref) {
|
|
12998
|
+
const fromMime = mime === "application/octet-stream" ? void 0 : mime.split("/")[1]?.replace("mpeg", "mp3").replace("x-", "");
|
|
12999
|
+
const fromUrl = "url" in ref ? /\.([A-Za-z0-9]{2,4})$/.exec(new URL(ref.url).pathname)?.[1] : void 0;
|
|
13000
|
+
const ext = fromMime || fromUrl?.toLowerCase();
|
|
13001
|
+
return ext ? `sample-${index + 1}.${ext}` : `sample-${index + 1}`;
|
|
13002
|
+
}
|
|
13003
|
+
async function cloneForm(req, ctx) {
|
|
13004
|
+
if (req.samples.length === 0)
|
|
13005
|
+
throw elevenLabsError(ctx, "missing_input", "needs a voice sample");
|
|
13006
|
+
if (req.samples.some((s) => s.designSignature?.trim())) {
|
|
13007
|
+
throw elevenLabsError(
|
|
13008
|
+
ctx,
|
|
13009
|
+
"unsupported_option",
|
|
13010
|
+
"ElevenLabs cannot keep a voice design signature; clone from recordings only"
|
|
13011
|
+
);
|
|
13012
|
+
}
|
|
13013
|
+
const form = new FormData();
|
|
13014
|
+
form.append("name", req.title.trim());
|
|
13015
|
+
if (req.description?.trim()) form.append("description", req.description.trim());
|
|
13016
|
+
if (req.enhance !== void 0) form.append("remove_background_noise", String(req.enhance));
|
|
13017
|
+
for (const [index, sample] of req.samples.entries()) {
|
|
13018
|
+
const { bytes, mime } = await mediaRefToBytes(sample.audio, ctx.fetch, {
|
|
13019
|
+
signal: ctx.signal,
|
|
13020
|
+
provider: "elevenlabs",
|
|
13021
|
+
model: ctx.model.id
|
|
13022
|
+
});
|
|
13023
|
+
const type = sampleMime(mime);
|
|
13024
|
+
const audio = new Blob([new Uint8Array(bytes)], { type });
|
|
13025
|
+
form.append("files", audio, fileName(index, type, sample.audio));
|
|
13026
|
+
}
|
|
13027
|
+
return form;
|
|
13028
|
+
}
|
|
13029
|
+
async function onlyRateLimitRetries(fn) {
|
|
13030
|
+
try {
|
|
13031
|
+
return await fn();
|
|
13032
|
+
} catch (cause) {
|
|
13033
|
+
if (!isModelKitError(cause) || !cause.retryable || cause.code === "rate_limited")
|
|
13034
|
+
throw cause;
|
|
13035
|
+
throw new ModelKitError({
|
|
13036
|
+
code: cause.code,
|
|
13037
|
+
message: cause.message,
|
|
13038
|
+
...cause.provider !== void 0 && { provider: cause.provider },
|
|
13039
|
+
...cause.model !== void 0 && { model: cause.model },
|
|
13040
|
+
...cause.status !== void 0 && { status: cause.status },
|
|
13041
|
+
...cause.body !== void 0 && { body: cause.body },
|
|
13042
|
+
cause,
|
|
13043
|
+
retryable: false
|
|
13044
|
+
});
|
|
13045
|
+
}
|
|
13046
|
+
}
|
|
13047
|
+
async function createVoice(req, ctx) {
|
|
13048
|
+
checkRow(ctx, "voice-clone");
|
|
13049
|
+
const form = await cloneForm(req, ctx);
|
|
13050
|
+
const res = await withRetry(
|
|
13051
|
+
() => onlyRateLimitRetries(
|
|
13052
|
+
() => elevenLabsFetch(ctx, "/v1/voices/add", { method: "POST", body: form })
|
|
13053
|
+
),
|
|
13054
|
+
mediaRetryOptions(ctx.call, ctx.signal)
|
|
13055
|
+
);
|
|
13056
|
+
const raw = await readBody(res);
|
|
13057
|
+
const parsed = createdVoiceSchema.safeParse(raw);
|
|
13058
|
+
if (!parsed.success) {
|
|
13059
|
+
throw new ModelKitError({
|
|
13060
|
+
code: "provider_rejected",
|
|
13061
|
+
message: "ElevenLabs returned no voice id",
|
|
13062
|
+
provider: "elevenlabs",
|
|
13063
|
+
model: ctx.model.id,
|
|
13064
|
+
status: res.status,
|
|
13065
|
+
body: raw,
|
|
13066
|
+
retryable: false
|
|
13067
|
+
});
|
|
13068
|
+
}
|
|
13069
|
+
return {
|
|
13070
|
+
voiceId: parsed.data.voice_id,
|
|
13071
|
+
state: parsed.data.requires_verification === true ? "created" : "trained",
|
|
13072
|
+
usage: usage2(ctx),
|
|
13073
|
+
raw
|
|
13074
|
+
};
|
|
13075
|
+
}
|
|
13076
|
+
var NOT_FOUND_STATUSES = /* @__PURE__ */ new Set([
|
|
13077
|
+
"voice_not_found",
|
|
13078
|
+
"voice_does_not_exist"
|
|
13079
|
+
]);
|
|
13080
|
+
function notFound(cause) {
|
|
13081
|
+
if (cause.status === 404) return true;
|
|
13082
|
+
const status = errorStatus(cause.body)?.status;
|
|
13083
|
+
return cause.status === 400 && status !== void 0 && NOT_FOUND_STATUSES.has(status);
|
|
13084
|
+
}
|
|
13085
|
+
async function deleteVoice(req, ctx) {
|
|
13086
|
+
checkRow(ctx, "voice-clone");
|
|
13087
|
+
const id = encodeURIComponent(checkVoiceId(ctx, "voiceId", req.voiceId));
|
|
13088
|
+
let attempts = 0;
|
|
13089
|
+
await withRetry(
|
|
13090
|
+
async () => {
|
|
13091
|
+
attempts += 1;
|
|
13092
|
+
try {
|
|
13093
|
+
await elevenLabsFetch(ctx, `/v1/voices/${id}`, { method: "DELETE" });
|
|
13094
|
+
} catch (cause) {
|
|
13095
|
+
if (!isModelKitError(cause) || !notFound(cause)) throw cause;
|
|
13096
|
+
if (attempts > 1) return;
|
|
13097
|
+
throw new ModelKitError({
|
|
13098
|
+
code: "provider_rejected",
|
|
13099
|
+
message: `ElevenLabs has no voice ${req.voiceId} under this key set; delete with the tier that created it`,
|
|
13100
|
+
provider: "elevenlabs",
|
|
13101
|
+
model: ctx.model.id,
|
|
13102
|
+
...cause.status !== void 0 && { status: cause.status },
|
|
13103
|
+
cause,
|
|
13104
|
+
retryable: false
|
|
13105
|
+
});
|
|
13106
|
+
}
|
|
13107
|
+
},
|
|
13108
|
+
mediaRetryOptions(ctx.call, ctx.signal)
|
|
13109
|
+
);
|
|
13110
|
+
}
|
|
13111
|
+
var elevenLabsAdapter = {
|
|
13112
|
+
id: "elevenlabs",
|
|
13113
|
+
generateSpeech,
|
|
13114
|
+
createVoice,
|
|
13115
|
+
deleteVoice
|
|
13116
|
+
};
|
|
13117
|
+
|
|
13118
|
+
// src/adapters/fish.ts
|
|
13119
|
+
var import_zod22 = require("zod");
|
|
13120
|
+
var DEFAULT_BASE_URL2 = "https://api.fish.audio";
|
|
12778
13121
|
var FISH_TTS_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
12779
13122
|
"s2.1-pro",
|
|
12780
13123
|
"s2.1-pro-free",
|
|
@@ -12782,7 +13125,7 @@ var FISH_TTS_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
|
12782
13125
|
]);
|
|
12783
13126
|
var FISH_VOICE_CLONE = "voice-clone";
|
|
12784
13127
|
var FISH_VOICE_DESIGN = "voice-design-1";
|
|
12785
|
-
var
|
|
13128
|
+
var VOICE_ID2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
12786
13129
|
function fishRowRefusal(row2) {
|
|
12787
13130
|
const id = row2.providerModelId;
|
|
12788
13131
|
if (row2.type === "speech") {
|
|
@@ -12805,7 +13148,7 @@ function fishError(ctx, code, message) {
|
|
|
12805
13148
|
retryable: false
|
|
12806
13149
|
});
|
|
12807
13150
|
}
|
|
12808
|
-
function
|
|
13151
|
+
function checkRow2(ctx, type) {
|
|
12809
13152
|
const refusal2 = ctx.model.type === type ? fishRowRefusal(ctx.model) : void 0;
|
|
12810
13153
|
if (ctx.model.type !== type || refusal2 !== void 0) {
|
|
12811
13154
|
throw fishError(
|
|
@@ -12815,8 +13158,8 @@ function checkRow(ctx, type) {
|
|
|
12815
13158
|
);
|
|
12816
13159
|
}
|
|
12817
13160
|
}
|
|
12818
|
-
function
|
|
12819
|
-
if (!
|
|
13161
|
+
function checkVoiceId2(ctx, field, id) {
|
|
13162
|
+
if (!VOICE_ID2.test(id))
|
|
12820
13163
|
throw fishError(ctx, "unsupported_option", `${field} is not a voice id`);
|
|
12821
13164
|
return id;
|
|
12822
13165
|
}
|
|
@@ -12826,7 +13169,7 @@ function fishKey(ctx) {
|
|
|
12826
13169
|
return key;
|
|
12827
13170
|
}
|
|
12828
13171
|
function fishUrl(ctx, path) {
|
|
12829
|
-
return `${ctx.endpoints?.fish ??
|
|
13172
|
+
return `${ctx.endpoints?.fish ?? DEFAULT_BASE_URL2}${path}`;
|
|
12830
13173
|
}
|
|
12831
13174
|
async function fishFetch(ctx, path, init) {
|
|
12832
13175
|
throwIfAborted(ctx.signal);
|
|
@@ -12845,7 +13188,7 @@ async function fishFetch(ctx, path, init) {
|
|
|
12845
13188
|
if (!res.ok) throw classifyHttpError(res.status, await readBody(res), "fish", ctx.model.id);
|
|
12846
13189
|
return res;
|
|
12847
13190
|
}
|
|
12848
|
-
function
|
|
13191
|
+
function usage3(ctx) {
|
|
12849
13192
|
return {
|
|
12850
13193
|
model: ctx.model.id,
|
|
12851
13194
|
provider: "fish",
|
|
@@ -12854,18 +13197,18 @@ function usage2(ctx) {
|
|
|
12854
13197
|
quantity: 1
|
|
12855
13198
|
};
|
|
12856
13199
|
}
|
|
12857
|
-
function
|
|
13200
|
+
function audioMime2(res, format) {
|
|
12858
13201
|
const header = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
12859
13202
|
if (header?.startsWith("audio/")) return header;
|
|
12860
13203
|
return format === "wav" ? "audio/wav" : "audio/mpeg";
|
|
12861
13204
|
}
|
|
12862
|
-
async function
|
|
12863
|
-
|
|
13205
|
+
async function generateSpeech2(req, ctx) {
|
|
13206
|
+
checkRow2(ctx, "speech");
|
|
12864
13207
|
const format = req.outputFormat ?? "mp3";
|
|
12865
13208
|
const body = { text: req.text, format };
|
|
12866
13209
|
const voice = req.customVoice ?? req.voice;
|
|
12867
13210
|
if (voice !== void 0) {
|
|
12868
|
-
body.reference_id =
|
|
13211
|
+
body.reference_id = checkVoiceId2(ctx, req.customVoice ? "customVoice" : "voice", voice);
|
|
12869
13212
|
}
|
|
12870
13213
|
if (req.language !== void 0) body.language = req.language;
|
|
12871
13214
|
const { bytes, mime } = await withRetry(
|
|
@@ -12881,19 +13224,19 @@ async function generateSpeech(req, ctx) {
|
|
|
12881
13224
|
} catch (cause) {
|
|
12882
13225
|
throw classifyThrown("fish", cause, ctx.model.id);
|
|
12883
13226
|
}
|
|
12884
|
-
return { bytes: audio, mime:
|
|
13227
|
+
return { bytes: audio, mime: audioMime2(res, format) };
|
|
12885
13228
|
},
|
|
12886
13229
|
mediaRetryOptions(ctx.call, ctx.signal)
|
|
12887
13230
|
);
|
|
12888
13231
|
if (bytes.byteLength === 0)
|
|
12889
13232
|
throw fishError(ctx, "provider_rejected", "Fish TTS returned no audio");
|
|
12890
|
-
return { audio: { bytes, mime }, usage:
|
|
13233
|
+
return { audio: { bytes, mime }, usage: usage3(ctx) };
|
|
12891
13234
|
}
|
|
12892
|
-
var createdModelSchema =
|
|
12893
|
-
_id:
|
|
12894
|
-
state:
|
|
13235
|
+
var createdModelSchema = import_zod22.z.object({
|
|
13236
|
+
_id: import_zod22.z.string().min(1),
|
|
13237
|
+
state: import_zod22.z.enum(["created", "training", "trained", "failed"]).optional().catch(void 0)
|
|
12895
13238
|
});
|
|
12896
|
-
function
|
|
13239
|
+
function fileName2(index, mime) {
|
|
12897
13240
|
const ext = mime.split("/")[1]?.replace("mpeg", "mp3").replace("x-", "") || "bin";
|
|
12898
13241
|
return `sample-${index + 1}.${ext}`;
|
|
12899
13242
|
}
|
|
@@ -12905,7 +13248,7 @@ function allOrNone(ctx, field, values) {
|
|
|
12905
13248
|
}
|
|
12906
13249
|
return set;
|
|
12907
13250
|
}
|
|
12908
|
-
async function
|
|
13251
|
+
async function cloneForm2(req, ctx) {
|
|
12909
13252
|
if (req.samples.length === 0) throw fishError(ctx, "missing_input", "needs a voice sample");
|
|
12910
13253
|
const transcripts = allOrNone(
|
|
12911
13254
|
ctx,
|
|
@@ -12931,13 +13274,13 @@ async function cloneForm(req, ctx) {
|
|
|
12931
13274
|
model: ctx.model.id
|
|
12932
13275
|
});
|
|
12933
13276
|
const audio = new Blob([new Uint8Array(bytes)], { type: mime });
|
|
12934
|
-
form.append("voices", audio,
|
|
13277
|
+
form.append("voices", audio, fileName2(index, mime));
|
|
12935
13278
|
}
|
|
12936
13279
|
for (const text2 of transcripts) form.append("texts", text2.trim());
|
|
12937
13280
|
for (const signature of signatures) form.append("voice_design_signatures", signature);
|
|
12938
13281
|
return form;
|
|
12939
13282
|
}
|
|
12940
|
-
async function
|
|
13283
|
+
async function onlyRateLimitRetries2(fn) {
|
|
12941
13284
|
try {
|
|
12942
13285
|
return await fn();
|
|
12943
13286
|
} catch (cause) {
|
|
@@ -12955,11 +13298,11 @@ async function onlyRateLimitRetries(fn) {
|
|
|
12955
13298
|
});
|
|
12956
13299
|
}
|
|
12957
13300
|
}
|
|
12958
|
-
async function
|
|
12959
|
-
|
|
12960
|
-
const form = await
|
|
13301
|
+
async function createVoice2(req, ctx) {
|
|
13302
|
+
checkRow2(ctx, "voice-clone");
|
|
13303
|
+
const form = await cloneForm2(req, ctx);
|
|
12961
13304
|
const res = await withRetry(
|
|
12962
|
-
() =>
|
|
13305
|
+
() => onlyRateLimitRetries2(() => fishFetch(ctx, "/model", { method: "POST", body: form })),
|
|
12963
13306
|
mediaRetryOptions(ctx.call, ctx.signal)
|
|
12964
13307
|
);
|
|
12965
13308
|
const raw = await readBody(res);
|
|
@@ -12975,23 +13318,23 @@ async function createVoice(req, ctx) {
|
|
|
12975
13318
|
retryable: false
|
|
12976
13319
|
});
|
|
12977
13320
|
}
|
|
12978
|
-
const result = { voiceId: parsed.data._id, usage:
|
|
13321
|
+
const result = { voiceId: parsed.data._id, usage: usage3(ctx), raw };
|
|
12979
13322
|
if (parsed.data.state !== void 0) result.state = parsed.data.state;
|
|
12980
13323
|
return result;
|
|
12981
13324
|
}
|
|
12982
|
-
var designSchema =
|
|
12983
|
-
candidates:
|
|
12984
|
-
|
|
12985
|
-
id:
|
|
12986
|
-
index:
|
|
12987
|
-
audio_base64:
|
|
12988
|
-
duration_ms:
|
|
12989
|
-
signature:
|
|
13325
|
+
var designSchema = import_zod22.z.object({
|
|
13326
|
+
candidates: import_zod22.z.array(
|
|
13327
|
+
import_zod22.z.looseObject({
|
|
13328
|
+
id: import_zod22.z.string().min(1),
|
|
13329
|
+
index: import_zod22.z.number().int(),
|
|
13330
|
+
audio_base64: import_zod22.z.string().min(1),
|
|
13331
|
+
duration_ms: import_zod22.z.number().nonnegative(),
|
|
13332
|
+
signature: import_zod22.z.string().min(1).optional()
|
|
12990
13333
|
})
|
|
12991
13334
|
).min(1)
|
|
12992
13335
|
});
|
|
12993
13336
|
async function designVoice(req, ctx) {
|
|
12994
|
-
|
|
13337
|
+
checkRow2(ctx, "voice-design");
|
|
12995
13338
|
const body = { instruction: req.prompt.trim() };
|
|
12996
13339
|
if (req.previewText?.trim()) body.reference_text = req.previewText.trim();
|
|
12997
13340
|
if (req.language !== void 0) body.language = req.language;
|
|
@@ -13019,11 +13362,11 @@ async function designVoice(req, ctx) {
|
|
|
13019
13362
|
// rejects a clone whose signature does not verify, so a wrong one fails loudly.
|
|
13020
13363
|
signature: c.signature ?? c.id
|
|
13021
13364
|
}));
|
|
13022
|
-
return { candidates, usage:
|
|
13365
|
+
return { candidates, usage: usage3(ctx) };
|
|
13023
13366
|
}
|
|
13024
|
-
async function
|
|
13025
|
-
|
|
13026
|
-
const id = encodeURIComponent(
|
|
13367
|
+
async function deleteVoice2(req, ctx) {
|
|
13368
|
+
checkRow2(ctx, "voice-clone");
|
|
13369
|
+
const id = encodeURIComponent(checkVoiceId2(ctx, "voiceId", req.voiceId));
|
|
13027
13370
|
let attempts = 0;
|
|
13028
13371
|
await withRetry(
|
|
13029
13372
|
async () => {
|
|
@@ -13049,18 +13392,18 @@ async function deleteVoice(req, ctx) {
|
|
|
13049
13392
|
}
|
|
13050
13393
|
var fishAdapter = {
|
|
13051
13394
|
id: "fish",
|
|
13052
|
-
generateSpeech,
|
|
13053
|
-
createVoice,
|
|
13395
|
+
generateSpeech: generateSpeech2,
|
|
13396
|
+
createVoice: createVoice2,
|
|
13054
13397
|
designVoice,
|
|
13055
|
-
deleteVoice
|
|
13398
|
+
deleteVoice: deleteVoice2
|
|
13056
13399
|
};
|
|
13057
13400
|
|
|
13058
13401
|
// src/adapters/google-genai.ts
|
|
13059
|
-
var
|
|
13402
|
+
var import_zod24 = require("zod");
|
|
13060
13403
|
var import_genai = require("@google/genai");
|
|
13061
13404
|
|
|
13062
13405
|
// src/adapters/google-files.ts
|
|
13063
|
-
var
|
|
13406
|
+
var import_zod23 = require("zod");
|
|
13064
13407
|
var GOOGLE_FILES_BASE = "https://generativelanguage.googleapis.com";
|
|
13065
13408
|
var GOOGLE_FILES_POLL_INTERVAL_MS = 2e3;
|
|
13066
13409
|
var GOOGLE_FILES_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -13070,17 +13413,17 @@ var GENERIC_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
|
13070
13413
|
"binary/octet-stream",
|
|
13071
13414
|
"application/binary"
|
|
13072
13415
|
]);
|
|
13073
|
-
var uploadedFileSchema =
|
|
13074
|
-
file:
|
|
13075
|
-
uri:
|
|
13076
|
-
name:
|
|
13077
|
-
state:
|
|
13078
|
-
mimeType:
|
|
13416
|
+
var uploadedFileSchema = import_zod23.z.object({
|
|
13417
|
+
file: import_zod23.z.object({
|
|
13418
|
+
uri: import_zod23.z.string().optional(),
|
|
13419
|
+
name: import_zod23.z.string().optional(),
|
|
13420
|
+
state: import_zod23.z.string().optional(),
|
|
13421
|
+
mimeType: import_zod23.z.string().optional()
|
|
13079
13422
|
}).optional()
|
|
13080
13423
|
});
|
|
13081
|
-
var fileStateSchema =
|
|
13082
|
-
state:
|
|
13083
|
-
error:
|
|
13424
|
+
var fileStateSchema = import_zod23.z.object({
|
|
13425
|
+
state: import_zod23.z.string().optional(),
|
|
13426
|
+
error: import_zod23.z.object({ message: import_zod23.z.string().optional() }).optional()
|
|
13084
13427
|
});
|
|
13085
13428
|
function resolveMime(declared, fallback) {
|
|
13086
13429
|
const clean = declared?.split(";")[0]?.trim().toLowerCase();
|
|
@@ -13187,7 +13530,7 @@ async function startAndUpload(bytes, mimeType, opts) {
|
|
|
13187
13530
|
if (uploaded.file?.state !== void 0) ref.state = uploaded.file.state;
|
|
13188
13531
|
return ref;
|
|
13189
13532
|
}
|
|
13190
|
-
async function waitForActive(
|
|
13533
|
+
async function waitForActive(fileName3, opts) {
|
|
13191
13534
|
const pollOptions2 = {
|
|
13192
13535
|
intervalMs: GOOGLE_FILES_POLL_INTERVAL_MS,
|
|
13193
13536
|
timeoutMs: GOOGLE_FILES_POLL_TIMEOUT_MS
|
|
@@ -13200,14 +13543,14 @@ async function waitForActive(fileName2, opts) {
|
|
|
13200
13543
|
retryInit.signal = opts.signal;
|
|
13201
13544
|
}
|
|
13202
13545
|
await pollUntil(async () => {
|
|
13203
|
-
const res = await opts.fetchImpl(`${GOOGLE_FILES_BASE}/v1beta/${
|
|
13546
|
+
const res = await opts.fetchImpl(`${GOOGLE_FILES_BASE}/v1beta/${fileName3}`, retryInit);
|
|
13204
13547
|
if (res.ok) {
|
|
13205
13548
|
const json = fileStateSchema.parse(await res.json());
|
|
13206
13549
|
if (json.state === "ACTIVE") return true;
|
|
13207
13550
|
if (json.state === "FAILED") {
|
|
13208
13551
|
throw classifyHttpError(
|
|
13209
13552
|
void 0,
|
|
13210
|
-
`Files API processing failed for ${
|
|
13553
|
+
`Files API processing failed for ${fileName3}: ${json.error?.message ?? "unknown error"}`,
|
|
13211
13554
|
"google-genai",
|
|
13212
13555
|
opts.model
|
|
13213
13556
|
);
|
|
@@ -13219,9 +13562,9 @@ async function waitForActive(fileName2, opts) {
|
|
|
13219
13562
|
throw classifyHttpError(res.status, text2, "google-genai", opts.model);
|
|
13220
13563
|
}, pollOptions2);
|
|
13221
13564
|
}
|
|
13222
|
-
async function deleteGoogleFile(
|
|
13565
|
+
async function deleteGoogleFile(fileName3, apiKey, fetchImpl) {
|
|
13223
13566
|
try {
|
|
13224
|
-
await fetchImpl(`${GOOGLE_FILES_BASE}/v1beta/${
|
|
13567
|
+
await fetchImpl(`${GOOGLE_FILES_BASE}/v1beta/${fileName3}`, {
|
|
13225
13568
|
method: "DELETE",
|
|
13226
13569
|
headers: { "x-goog-api-key": apiKey }
|
|
13227
13570
|
});
|
|
@@ -13301,7 +13644,7 @@ var OMNI_POLL_INTERVAL_MS = 1e4;
|
|
|
13301
13644
|
var RESULT_TIMEOUT_MS3 = 15 * 60 * 1e3;
|
|
13302
13645
|
var OMNI_INLINE_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
13303
13646
|
var MEDIA_PART_INLINE_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
13304
|
-
var errorTextSchema =
|
|
13647
|
+
var errorTextSchema = import_zod24.z.string();
|
|
13305
13648
|
function isGoogleGenaiVideoKnown(providerModelId) {
|
|
13306
13649
|
return providerModelId.startsWith("veo-") || providerModelId.startsWith("gemini-omni");
|
|
13307
13650
|
}
|
|
@@ -13496,7 +13839,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
|
|
|
13496
13839
|
}
|
|
13497
13840
|
};
|
|
13498
13841
|
const jobId = operation.name ?? crypto.randomUUID();
|
|
13499
|
-
const
|
|
13842
|
+
const usage5 = videoUsage2(ctx, duration, req.resolution ?? "720p");
|
|
13500
13843
|
return {
|
|
13501
13844
|
jobId,
|
|
13502
13845
|
async poll() {
|
|
@@ -13534,7 +13877,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
|
|
|
13534
13877
|
modelId
|
|
13535
13878
|
);
|
|
13536
13879
|
}
|
|
13537
|
-
return { video: await downloadVideo(ctx, uri, apiKey, signal), usage:
|
|
13880
|
+
return { video: await downloadVideo(ctx, uri, apiKey, signal), usage: usage5, raw: finished };
|
|
13538
13881
|
}
|
|
13539
13882
|
};
|
|
13540
13883
|
}
|
|
@@ -13645,7 +13988,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
|
|
|
13645
13988
|
return file.fileUri;
|
|
13646
13989
|
};
|
|
13647
13990
|
const cleanupUploads = async () => {
|
|
13648
|
-
for (const
|
|
13991
|
+
for (const fileName3 of uploadedNames) await deleteGoogleFile(fileName3, apiKey, ctx.fetch);
|
|
13649
13992
|
};
|
|
13650
13993
|
const callCreate = (input) => client2.interactions.create(
|
|
13651
13994
|
previousId !== void 0 ? {
|
|
@@ -13739,7 +14082,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
|
|
|
13739
14082
|
if (!(extend && !chained)) await cleanupUploads();
|
|
13740
14083
|
}
|
|
13741
14084
|
const jobId = interaction.id ?? crypto.randomUUID();
|
|
13742
|
-
const
|
|
14085
|
+
const usage5 = videoUsage2(ctx, duration, omniResolution ?? "720p");
|
|
13743
14086
|
const getInteraction = async () => {
|
|
13744
14087
|
try {
|
|
13745
14088
|
const getParams = { stream: false };
|
|
@@ -13787,7 +14130,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
|
|
|
13787
14130
|
bytes: Buffer.from(video.data, "base64"),
|
|
13788
14131
|
mime: video.mime_type ?? "video/mp4"
|
|
13789
14132
|
},
|
|
13790
|
-
usage:
|
|
14133
|
+
usage: usage5,
|
|
13791
14134
|
raw: done,
|
|
13792
14135
|
providerRef
|
|
13793
14136
|
};
|
|
@@ -13795,7 +14138,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
|
|
|
13795
14138
|
if (video.uri)
|
|
13796
14139
|
return {
|
|
13797
14140
|
video: await downloadVideo(ctx, video.uri, apiKey, signal),
|
|
13798
|
-
usage:
|
|
14141
|
+
usage: usage5,
|
|
13799
14142
|
raw: done,
|
|
13800
14143
|
providerRef
|
|
13801
14144
|
};
|
|
@@ -13965,15 +14308,15 @@ async function prepareGoogleMediaPart(ref, mediaType, ctx) {
|
|
|
13965
14308
|
// src/adapters/google-image.ts
|
|
13966
14309
|
var import_google2 = require("@ai-sdk/google");
|
|
13967
14310
|
var import_ai2 = require("ai");
|
|
13968
|
-
var
|
|
14311
|
+
var import_zod25 = require("zod");
|
|
13969
14312
|
var GEMINI_IMAGE_TIMEOUT_MS = 6e5;
|
|
13970
14313
|
var defaultGoogleImageDeps = {
|
|
13971
14314
|
createGoogleClient: (opts) => (0, import_google2.createGoogleGenerativeAI)(opts),
|
|
13972
14315
|
runTextGeneration: (opts) => (0, import_ai2.generateText)(opts)
|
|
13973
14316
|
};
|
|
13974
|
-
var causeChainNodeSchema =
|
|
13975
|
-
message:
|
|
13976
|
-
cause:
|
|
14317
|
+
var causeChainNodeSchema = import_zod25.z.object({
|
|
14318
|
+
message: import_zod25.z.string().optional(),
|
|
14319
|
+
cause: import_zod25.z.unknown().optional()
|
|
13977
14320
|
});
|
|
13978
14321
|
function extractBlockReason(cause) {
|
|
13979
14322
|
let current = cause;
|
|
@@ -14073,7 +14416,7 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
|
|
|
14073
14416
|
retryable: false
|
|
14074
14417
|
});
|
|
14075
14418
|
}
|
|
14076
|
-
const
|
|
14419
|
+
const usage5 = {
|
|
14077
14420
|
model: ctx.model.id,
|
|
14078
14421
|
provider: "google-image",
|
|
14079
14422
|
modality: "IMAGE",
|
|
@@ -14081,19 +14424,19 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
|
|
|
14081
14424
|
quantity: images.length,
|
|
14082
14425
|
imageQuality: "auto"
|
|
14083
14426
|
};
|
|
14084
|
-
if (req.size !== void 0)
|
|
14427
|
+
if (req.size !== void 0) usage5.imageSize = req.size;
|
|
14085
14428
|
if (result.usage?.inputTokens !== void 0)
|
|
14086
|
-
|
|
14429
|
+
usage5.inputTokens = result.usage.inputTokens;
|
|
14087
14430
|
if (result.usage?.outputTokens !== void 0)
|
|
14088
|
-
|
|
14089
|
-
return { images, usage:
|
|
14431
|
+
usage5.outputTokens = result.usage.outputTokens;
|
|
14432
|
+
return { images, usage: usage5, raw: result };
|
|
14090
14433
|
}
|
|
14091
14434
|
};
|
|
14092
14435
|
}
|
|
14093
14436
|
var googleImageAdapter = createGoogleImageAdapter();
|
|
14094
14437
|
|
|
14095
14438
|
// src/adapters/higgsfield.ts
|
|
14096
|
-
var
|
|
14439
|
+
var import_zod26 = require("zod");
|
|
14097
14440
|
|
|
14098
14441
|
// src/adapters/higgsfield-motions.ts
|
|
14099
14442
|
var MOTION_MAP = {
|
|
@@ -14312,14 +14655,14 @@ function klingEndpointFields(providerModelId) {
|
|
|
14312
14655
|
const path = (providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId).toLowerCase();
|
|
14313
14656
|
return KLING_ENDPOINT_FIELDS.get(path) ?? UNLISTED_KLING_FIELDS;
|
|
14314
14657
|
}
|
|
14315
|
-
var klingCreateSchema =
|
|
14316
|
-
var klingStatusSchema =
|
|
14317
|
-
status:
|
|
14318
|
-
video:
|
|
14658
|
+
var klingCreateSchema = import_zod26.z.object({ status_url: import_zod26.z.string() });
|
|
14659
|
+
var klingStatusSchema = import_zod26.z.object({
|
|
14660
|
+
status: import_zod26.z.string().optional(),
|
|
14661
|
+
video: import_zod26.z.object({ url: import_zod26.z.string().optional() }).optional(),
|
|
14319
14662
|
error: JsonValueSchema.optional()
|
|
14320
14663
|
});
|
|
14321
|
-
var errorMessageSchema =
|
|
14322
|
-
var textValueSchema =
|
|
14664
|
+
var errorMessageSchema = import_zod26.z.object({ message: import_zod26.z.string().optional() });
|
|
14665
|
+
var textValueSchema = import_zod26.z.string();
|
|
14323
14666
|
function klingErrorDetail(error) {
|
|
14324
14667
|
if (error === void 0) return "unknown_error";
|
|
14325
14668
|
const asText = textValueSchema.safeParse(error);
|
|
@@ -14475,8 +14818,8 @@ async function generateKlingVideo(req, ctx) {
|
|
|
14475
14818
|
}
|
|
14476
14819
|
};
|
|
14477
14820
|
}
|
|
14478
|
-
var dopMotionOptionsSchema =
|
|
14479
|
-
modelkit:
|
|
14821
|
+
var dopMotionOptionsSchema = import_zod26.z.object({
|
|
14822
|
+
modelkit: import_zod26.z.object({ motion: import_zod26.z.string().optional() }).optional()
|
|
14480
14823
|
});
|
|
14481
14824
|
function dopMotionId(ctx) {
|
|
14482
14825
|
const parsed = dopMotionOptionsSchema.safeParse(ctx.model.providerOptions);
|
|
@@ -14492,7 +14835,7 @@ function dopMotionId(ctx) {
|
|
|
14492
14835
|
});
|
|
14493
14836
|
return id;
|
|
14494
14837
|
}
|
|
14495
|
-
var jsonRecordSchema =
|
|
14838
|
+
var jsonRecordSchema = import_zod26.z.record(import_zod26.z.string(), JsonValueSchema);
|
|
14496
14839
|
var DOP_URL_KEYS = ["url", "video_url", "output_url", "result_url", "download_url"];
|
|
14497
14840
|
var DOP_NESTED_KEYS = ["video", "result", "results"];
|
|
14498
14841
|
function dopUrl(body) {
|
|
@@ -14520,8 +14863,8 @@ function dopUrl(body) {
|
|
|
14520
14863
|
}
|
|
14521
14864
|
return void 0;
|
|
14522
14865
|
}
|
|
14523
|
-
var dopCreateSchema =
|
|
14524
|
-
var dopStatusSchema =
|
|
14866
|
+
var dopCreateSchema = import_zod26.z.object({ id: import_zod26.z.string() });
|
|
14867
|
+
var dopStatusSchema = import_zod26.z.object({ status: import_zod26.z.string().optional() });
|
|
14525
14868
|
async function generateDopVideo(req, ctx) {
|
|
14526
14869
|
const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
|
|
14527
14870
|
if (!firstFrame) {
|
|
@@ -14648,7 +14991,7 @@ var higgsfieldAdapter = {
|
|
|
14648
14991
|
};
|
|
14649
14992
|
|
|
14650
14993
|
// src/adapters/luma.ts
|
|
14651
|
-
var
|
|
14994
|
+
var import_zod27 = require("zod");
|
|
14652
14995
|
|
|
14653
14996
|
// src/adapters/mp4-duration.ts
|
|
14654
14997
|
function mp4DurationSeconds(bytes) {
|
|
@@ -14704,7 +15047,7 @@ function findBox(bytes, from, to, type) {
|
|
|
14704
15047
|
}
|
|
14705
15048
|
|
|
14706
15049
|
// src/adapters/luma.ts
|
|
14707
|
-
var
|
|
15050
|
+
var DEFAULT_BASE_URL3 = "https://agents.lumalabs.ai";
|
|
14708
15051
|
var IMAGE_POLL_INTERVAL_MS = 3e3;
|
|
14709
15052
|
var IMAGE_TIMEOUT_MS = 5 * 6e4;
|
|
14710
15053
|
var VIDEO_POLL_INTERVAL_MS = 5e3;
|
|
@@ -14733,7 +15076,7 @@ function lumaModel(ctx) {
|
|
|
14733
15076
|
return ctx.model.providerModelId.toLowerCase();
|
|
14734
15077
|
}
|
|
14735
15078
|
function lumaBase(ctx) {
|
|
14736
|
-
return ctx.endpoints?.luma ??
|
|
15079
|
+
return ctx.endpoints?.luma ?? DEFAULT_BASE_URL3;
|
|
14737
15080
|
}
|
|
14738
15081
|
function lumaKey(ctx) {
|
|
14739
15082
|
const key = ctx.keys.luma;
|
|
@@ -14865,17 +15208,17 @@ function lumaReframeResolution(req, ctx) {
|
|
|
14865
15208
|
});
|
|
14866
15209
|
return "720p";
|
|
14867
15210
|
}
|
|
14868
|
-
var lumaGenerationSchema =
|
|
14869
|
-
id:
|
|
14870
|
-
state:
|
|
14871
|
-
output:
|
|
14872
|
-
|
|
14873
|
-
type:
|
|
14874
|
-
url:
|
|
15211
|
+
var lumaGenerationSchema = import_zod27.z.looseObject({
|
|
15212
|
+
id: import_zod27.z.string(),
|
|
15213
|
+
state: import_zod27.z.string().optional(),
|
|
15214
|
+
output: import_zod27.z.array(
|
|
15215
|
+
import_zod27.z.looseObject({
|
|
15216
|
+
type: import_zod27.z.string().optional(),
|
|
15217
|
+
url: import_zod27.z.string().optional()
|
|
14875
15218
|
})
|
|
14876
15219
|
).optional(),
|
|
14877
|
-
failure_reason:
|
|
14878
|
-
failure_code:
|
|
15220
|
+
failure_reason: import_zod27.z.string().optional().nullable(),
|
|
15221
|
+
failure_code: import_zod27.z.string().optional().nullable()
|
|
14879
15222
|
});
|
|
14880
15223
|
function lumaFailure(ctx, code, reason) {
|
|
14881
15224
|
const detail = reason ?? code ?? "unknown_error";
|
|
@@ -15307,7 +15650,7 @@ var lumaAdapter = {
|
|
|
15307
15650
|
|
|
15308
15651
|
// src/adapters/openai-image.ts
|
|
15309
15652
|
var import_openai2 = __toESM(require("openai"), 1);
|
|
15310
|
-
var
|
|
15653
|
+
var import_zod28 = require("zod");
|
|
15311
15654
|
var MAX_EDIT_REFERENCES = 16;
|
|
15312
15655
|
function aspectRatioToSize(ratio, tier) {
|
|
15313
15656
|
const TIER_LONG_EDGE = {
|
|
@@ -15390,9 +15733,9 @@ function toProviderError2(cause, ctx) {
|
|
|
15390
15733
|
ctx.model.id
|
|
15391
15734
|
);
|
|
15392
15735
|
}
|
|
15393
|
-
var openaiUsageSchema =
|
|
15394
|
-
input_tokens:
|
|
15395
|
-
output_tokens:
|
|
15736
|
+
var openaiUsageSchema = import_zod28.z.object({
|
|
15737
|
+
input_tokens: import_zod28.z.number().optional(),
|
|
15738
|
+
output_tokens: import_zod28.z.number().optional()
|
|
15396
15739
|
});
|
|
15397
15740
|
var openaiImageAdapter = {
|
|
15398
15741
|
id: "openai-image",
|
|
@@ -15517,7 +15860,7 @@ var openaiImageAdapter = {
|
|
|
15517
15860
|
const usageParsed = openaiUsageSchema.safeParse(response.usage);
|
|
15518
15861
|
const inputTokens = usageParsed.success ? usageParsed.data.input_tokens : void 0;
|
|
15519
15862
|
const outputTokens = usageParsed.success ? usageParsed.data.output_tokens : void 0;
|
|
15520
|
-
const
|
|
15863
|
+
const usage5 = {
|
|
15521
15864
|
model: ctx.model.id,
|
|
15522
15865
|
provider: "openai-image",
|
|
15523
15866
|
modality: "IMAGE",
|
|
@@ -15525,10 +15868,10 @@ var openaiImageAdapter = {
|
|
|
15525
15868
|
quantity: images.length,
|
|
15526
15869
|
imageQuality: quality
|
|
15527
15870
|
};
|
|
15528
|
-
if (inputTokens !== void 0)
|
|
15529
|
-
if (outputTokens !== void 0)
|
|
15530
|
-
if (req.size !== void 0)
|
|
15531
|
-
return { images, usage:
|
|
15871
|
+
if (inputTokens !== void 0) usage5.inputTokens = inputTokens;
|
|
15872
|
+
if (outputTokens !== void 0) usage5.outputTokens = outputTokens;
|
|
15873
|
+
if (req.size !== void 0) usage5.imageSize = req.size;
|
|
15874
|
+
return { images, usage: usage5, raw: response };
|
|
15532
15875
|
}
|
|
15533
15876
|
};
|
|
15534
15877
|
|
|
@@ -15784,16 +16127,16 @@ async function generateProdiaImage(req, ctx) {
|
|
|
15784
16127
|
new Uint8Array(await res.arrayBuffer()),
|
|
15785
16128
|
res.headers.get("content-type")
|
|
15786
16129
|
);
|
|
15787
|
-
const
|
|
16130
|
+
const usage5 = {
|
|
15788
16131
|
model: ctx.model.id,
|
|
15789
16132
|
provider: "prodia",
|
|
15790
16133
|
modality: "IMAGE",
|
|
15791
16134
|
unit: "images",
|
|
15792
16135
|
quantity: 1
|
|
15793
16136
|
};
|
|
15794
|
-
if (req.size !== void 0)
|
|
15795
|
-
if (req.quality !== void 0)
|
|
15796
|
-
return { images: [{ bytes, mime }], usage:
|
|
16137
|
+
if (req.size !== void 0) usage5.imageSize = req.size;
|
|
16138
|
+
if (req.quality !== void 0) usage5.imageQuality = req.quality;
|
|
16139
|
+
return { images: [{ bytes, mime }], usage: usage5, raw: void 0 };
|
|
15797
16140
|
}
|
|
15798
16141
|
var prodiaAdapter = {
|
|
15799
16142
|
id: "prodia",
|
|
@@ -15804,13 +16147,13 @@ var prodiaAdapter = {
|
|
|
15804
16147
|
var import_replicate = __toESM(require("replicate"), 1);
|
|
15805
16148
|
|
|
15806
16149
|
// src/adapters/replicate-http.ts
|
|
15807
|
-
var
|
|
15808
|
-
var predictionSchema =
|
|
15809
|
-
id:
|
|
15810
|
-
status:
|
|
16150
|
+
var import_zod29 = require("zod");
|
|
16151
|
+
var predictionSchema = import_zod29.z.object({
|
|
16152
|
+
id: import_zod29.z.string(),
|
|
16153
|
+
status: import_zod29.z.string(),
|
|
15811
16154
|
output: JsonValueSchema.optional(),
|
|
15812
16155
|
error: JsonValueSchema.optional(),
|
|
15813
|
-
metrics:
|
|
16156
|
+
metrics: import_zod29.z.object({ predict_time: import_zod29.z.number().optional() }).optional()
|
|
15814
16157
|
});
|
|
15815
16158
|
function createReplicateHttpClient(opts) {
|
|
15816
16159
|
async function request(path, body, signal) {
|
|
@@ -15861,7 +16204,7 @@ function createReplicateHttpClient(opts) {
|
|
|
15861
16204
|
}
|
|
15862
16205
|
|
|
15863
16206
|
// src/adapters/replicate.ts
|
|
15864
|
-
var
|
|
16207
|
+
var import_zod30 = require("zod");
|
|
15865
16208
|
var POLL_INTERVAL_MS = 1e3;
|
|
15866
16209
|
var POLL_TIMEOUT_MS = 15 * 6e4;
|
|
15867
16210
|
var defaultReplicateDeps = {
|
|
@@ -15901,8 +16244,8 @@ function route(providerModelId) {
|
|
|
15901
16244
|
if (!providerModelId.includes("/")) return { version: providerModelId };
|
|
15902
16245
|
return { model: providerModelId };
|
|
15903
16246
|
}
|
|
15904
|
-
var replicateResponseStatusSchema =
|
|
15905
|
-
response:
|
|
16247
|
+
var replicateResponseStatusSchema = import_zod30.z.object({
|
|
16248
|
+
response: import_zod30.z.object({ status: import_zod30.z.number() }).partial()
|
|
15906
16249
|
});
|
|
15907
16250
|
function replicateError(cause, ctx) {
|
|
15908
16251
|
if (isModelKitError(cause)) return cause;
|
|
@@ -15925,7 +16268,7 @@ function isUrlFn(value) {
|
|
|
15925
16268
|
function extractOutputUrl(output) {
|
|
15926
16269
|
const root = Array.isArray(output) ? output[0] : output;
|
|
15927
16270
|
if (root === void 0) throw noOutput();
|
|
15928
|
-
const asString =
|
|
16271
|
+
const asString = import_zod30.z.string().safeParse(root);
|
|
15929
16272
|
if (asString.success && asString.data) return asString.data;
|
|
15930
16273
|
if (root instanceof URL) return root.href;
|
|
15931
16274
|
if (isObjectOutput(root)) {
|
|
@@ -15934,15 +16277,15 @@ function extractOutputUrl(output) {
|
|
|
15934
16277
|
if (urlValue !== void 0 && isUrlFn(urlValue)) {
|
|
15935
16278
|
const called = urlValue();
|
|
15936
16279
|
if (called instanceof URL) return called.href;
|
|
15937
|
-
const calledString =
|
|
16280
|
+
const calledString = import_zod30.z.string().safeParse(called);
|
|
15938
16281
|
if (calledString.success && calledString.data) return calledString.data;
|
|
15939
16282
|
} else {
|
|
15940
|
-
const direct =
|
|
16283
|
+
const direct = import_zod30.z.string().safeParse(urlValue);
|
|
15941
16284
|
if (direct.success && direct.data) return direct.data;
|
|
15942
16285
|
}
|
|
15943
16286
|
}
|
|
15944
16287
|
if ("default" in root) {
|
|
15945
|
-
const fallback =
|
|
16288
|
+
const fallback = import_zod30.z.string().safeParse(root.default);
|
|
15946
16289
|
if (fallback.success && fallback.data) return fallback.data;
|
|
15947
16290
|
}
|
|
15948
16291
|
}
|
|
@@ -16003,7 +16346,7 @@ async function runPrediction(ctx, input, deps) {
|
|
|
16003
16346
|
}
|
|
16004
16347
|
return void 0;
|
|
16005
16348
|
}, pollOpts);
|
|
16006
|
-
const predictTime =
|
|
16349
|
+
const predictTime = import_zod30.z.number().safeParse(done.metrics?.predict_time);
|
|
16007
16350
|
if (done.output === void 0) throw noOutput();
|
|
16008
16351
|
return {
|
|
16009
16352
|
url: extractOutputUrl(done.output),
|
|
@@ -16249,9 +16592,9 @@ async function generateReplicateImage(req, ctx, deps) {
|
|
|
16249
16592
|
}
|
|
16250
16593
|
const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
|
|
16251
16594
|
const file = await download(ctx, url);
|
|
16252
|
-
const
|
|
16253
|
-
|
|
16254
|
-
return { images: [{ bytes: file.bytes, mime: file.mime }], usage:
|
|
16595
|
+
const usage5 = imageUsage3(ctx, req);
|
|
16596
|
+
usage5.quantity = predictTime;
|
|
16597
|
+
return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage5, raw, outputUrl: url };
|
|
16255
16598
|
}
|
|
16256
16599
|
function klingInput(req) {
|
|
16257
16600
|
const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
|
|
@@ -16356,7 +16699,7 @@ async function generateReplicateVideo(req, ctx, deps) {
|
|
|
16356
16699
|
throw replicateError(cause, ctx);
|
|
16357
16700
|
}
|
|
16358
16701
|
if (current.status === "succeeded") {
|
|
16359
|
-
const predictTime =
|
|
16702
|
+
const predictTime = import_zod30.z.number().safeParse(current.metrics?.predict_time);
|
|
16360
16703
|
if (current.output === void 0) throw noOutput();
|
|
16361
16704
|
return {
|
|
16362
16705
|
url: extractOutputUrl(current.output),
|
|
@@ -16375,9 +16718,9 @@ async function generateReplicateVideo(req, ctx, deps) {
|
|
|
16375
16718
|
return void 0;
|
|
16376
16719
|
};
|
|
16377
16720
|
const toResult = (done) => {
|
|
16378
|
-
const
|
|
16379
|
-
|
|
16380
|
-
return { video: { url: done.url }, usage:
|
|
16721
|
+
const usage5 = videoUsage5(ctx, req);
|
|
16722
|
+
usage5.quantity = done.predictTime;
|
|
16723
|
+
return { video: { url: done.url }, usage: usage5, raw: done.raw };
|
|
16381
16724
|
};
|
|
16382
16725
|
return {
|
|
16383
16726
|
jobId,
|
|
@@ -16484,21 +16827,21 @@ async function generateReplicateAudio(req, ctx, deps) {
|
|
|
16484
16827
|
input = specResult.input;
|
|
16485
16828
|
}
|
|
16486
16829
|
const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
|
|
16487
|
-
const
|
|
16830
|
+
const usage5 = audioUsage(ctx, req);
|
|
16488
16831
|
if (ctx.model.usage.unit === "seconds") {
|
|
16489
16832
|
const seconds3 = elevenLabsMusicDuration(req.durationSeconds);
|
|
16490
|
-
|
|
16491
|
-
|
|
16833
|
+
usage5.quantity = seconds3;
|
|
16834
|
+
usage5.durationSeconds = seconds3;
|
|
16492
16835
|
} else {
|
|
16493
|
-
|
|
16836
|
+
usage5.quantity = predictTime;
|
|
16494
16837
|
}
|
|
16495
|
-
return { audio: { url }, usage:
|
|
16838
|
+
return { audio: { url }, usage: usage5, raw: predictionRaw(raw) };
|
|
16496
16839
|
}
|
|
16497
16840
|
|
|
16498
16841
|
// src/adapters/xai-video.ts
|
|
16499
16842
|
var import_ai3 = require("ai");
|
|
16500
16843
|
var import_xai2 = require("@ai-sdk/xai");
|
|
16501
|
-
var
|
|
16844
|
+
var import_zod31 = require("zod");
|
|
16502
16845
|
var POLL_INTERVAL_MS2 = 1e4;
|
|
16503
16846
|
var POLL_TIMEOUT_MS2 = 15 * 6e4;
|
|
16504
16847
|
var defaultXaiDeps = {
|
|
@@ -16511,8 +16854,8 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
|
|
|
16511
16854
|
};
|
|
16512
16855
|
}
|
|
16513
16856
|
var xaiVideoAdapter = createXaiVideoAdapter();
|
|
16514
|
-
var xaiResponseStatusSchema =
|
|
16515
|
-
response:
|
|
16857
|
+
var xaiResponseStatusSchema = import_zod31.z.object({
|
|
16858
|
+
response: import_zod31.z.object({ status: import_zod31.z.number() }).partial()
|
|
16516
16859
|
});
|
|
16517
16860
|
function xaiError(cause, ctx) {
|
|
16518
16861
|
const info = describeThrown(cause);
|
|
@@ -16520,12 +16863,12 @@ function xaiError(cause, ctx) {
|
|
|
16520
16863
|
const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
|
|
16521
16864
|
return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
|
|
16522
16865
|
}
|
|
16523
|
-
var xaiMetaSchema =
|
|
16866
|
+
var xaiMetaSchema = import_zod31.z.object({
|
|
16524
16867
|
videoUrl: JsonValueSchema.optional(),
|
|
16525
16868
|
costInUsdTicks: JsonValueSchema.optional(),
|
|
16526
16869
|
duration: JsonValueSchema.optional()
|
|
16527
16870
|
});
|
|
16528
|
-
var xaiProviderMetadataSchema =
|
|
16871
|
+
var xaiProviderMetadataSchema = import_zod31.z.object({ xai: xaiMetaSchema });
|
|
16529
16872
|
function metaOf(result) {
|
|
16530
16873
|
const parsed = xaiProviderMetadataSchema.partial().safeParse(result.providerMetadata);
|
|
16531
16874
|
if (!parsed.success) return {};
|
|
@@ -16637,12 +16980,12 @@ async function mapResult(ctx, req, result) {
|
|
|
16637
16980
|
if (first) {
|
|
16638
16981
|
return {
|
|
16639
16982
|
video: { bytes: Uint8Array.from(first.uint8Array), mime: first.mediaType },
|
|
16640
|
-
usage:
|
|
16983
|
+
usage: usage4(ctx, req, metaOf(result)),
|
|
16641
16984
|
raw: result.providerMetadata
|
|
16642
16985
|
};
|
|
16643
16986
|
}
|
|
16644
16987
|
const meta = metaOf(result);
|
|
16645
|
-
const videoUrl =
|
|
16988
|
+
const videoUrl = import_zod31.z.string().safeParse(meta.videoUrl);
|
|
16646
16989
|
if (videoUrl.success && videoUrl.data) {
|
|
16647
16990
|
const res = await ctx.fetch(videoUrl.data);
|
|
16648
16991
|
if (!res.ok) {
|
|
@@ -16652,7 +16995,7 @@ async function mapResult(ctx, req, result) {
|
|
|
16652
16995
|
const mime = res.headers.get("content-type") ?? "video/mp4";
|
|
16653
16996
|
return {
|
|
16654
16997
|
video: { bytes: new Uint8Array(await res.arrayBuffer()), mime },
|
|
16655
|
-
usage:
|
|
16998
|
+
usage: usage4(ctx, req, meta),
|
|
16656
16999
|
raw: result.providerMetadata
|
|
16657
17000
|
};
|
|
16658
17001
|
}
|
|
@@ -16664,8 +17007,8 @@ async function mapResult(ctx, req, result) {
|
|
|
16664
17007
|
retryable: false
|
|
16665
17008
|
});
|
|
16666
17009
|
}
|
|
16667
|
-
function
|
|
16668
|
-
const costTicks =
|
|
17010
|
+
function usage4(ctx, req, meta) {
|
|
17011
|
+
const costTicks = import_zod31.z.number().safeParse(meta.costInUsdTicks);
|
|
16669
17012
|
const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
|
|
16670
17013
|
const event = {
|
|
16671
17014
|
model: ctx.model.id,
|
|
@@ -16705,7 +17048,8 @@ var DEFAULT_ADAPTERS = {
|
|
|
16705
17048
|
higgsfield: higgsfieldAdapter,
|
|
16706
17049
|
"xai-video": xaiVideoAdapter,
|
|
16707
17050
|
luma: lumaAdapter,
|
|
16708
|
-
fish: fishAdapter
|
|
17051
|
+
fish: fishAdapter,
|
|
17052
|
+
elevenlabs: elevenLabsAdapter
|
|
16709
17053
|
};
|
|
16710
17054
|
|
|
16711
17055
|
// src/catalog/data-only.ts
|
|
@@ -16744,15 +17088,15 @@ var FAL_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
|
16744
17088
|
"transcription",
|
|
16745
17089
|
"mask"
|
|
16746
17090
|
]);
|
|
16747
|
-
function
|
|
16748
|
-
return row2.adapter === "fish" && row2.type === "speech";
|
|
17091
|
+
function directSpeech(row2) {
|
|
17092
|
+
return (row2.adapter === "fish" || row2.adapter === "elevenlabs") && row2.type === "speech";
|
|
16749
17093
|
}
|
|
16750
17094
|
function dataOnlyCapable(row2) {
|
|
16751
17095
|
const adapter = DEFAULT_ADAPTERS[row2.adapter];
|
|
16752
17096
|
if (!adapterServesType(adapter, row2.type)) {
|
|
16753
17097
|
return { ok: false, reason: `${row2.adapter} cannot serve ${row2.type} rows` };
|
|
16754
17098
|
}
|
|
16755
|
-
if (FAL_NODE_TYPES.has(row2.type) && row2.adapter !== "fal" && !
|
|
17099
|
+
if (FAL_NODE_TYPES.has(row2.type) && row2.adapter !== "fal" && !directSpeech(row2)) {
|
|
16756
17100
|
return { ok: false, reason: `only fal serves ${row2.type} rows` };
|
|
16757
17101
|
}
|
|
16758
17102
|
switch (row2.adapter) {
|
|
@@ -16834,6 +17178,10 @@ function dataOnlyCapable(row2) {
|
|
|
16834
17178
|
const reason = fishRowRefusal(row2);
|
|
16835
17179
|
return reason === void 0 ? { ok: true } : { ok: false, reason };
|
|
16836
17180
|
}
|
|
17181
|
+
case "elevenlabs": {
|
|
17182
|
+
const reason = elevenLabsRowRefusal(row2);
|
|
17183
|
+
return reason === void 0 ? { ok: true } : { ok: false, reason };
|
|
17184
|
+
}
|
|
16837
17185
|
default: {
|
|
16838
17186
|
return assertNever(row2.adapter);
|
|
16839
17187
|
}
|
|
@@ -16853,22 +17201,22 @@ var liveState = store[KEY] ??= {
|
|
|
16853
17201
|
var liveCatalog = liveState.catalog;
|
|
16854
17202
|
|
|
16855
17203
|
// src/catalog/load.ts
|
|
16856
|
-
var
|
|
17204
|
+
var import_zod32 = require("zod");
|
|
16857
17205
|
function rowCount() {
|
|
16858
17206
|
return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
|
|
16859
17207
|
}
|
|
16860
17208
|
function authHeaders(auth) {
|
|
16861
17209
|
return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
|
|
16862
17210
|
}
|
|
16863
|
-
var EnvelopeSchema =
|
|
16864
|
-
rows:
|
|
16865
|
-
revision:
|
|
17211
|
+
var EnvelopeSchema = import_zod32.z.object({
|
|
17212
|
+
rows: import_zod32.z.array(JsonValueSchema),
|
|
17213
|
+
revision: import_zod32.z.string().optional()
|
|
16866
17214
|
});
|
|
16867
17215
|
function dropReason(path, message) {
|
|
16868
17216
|
return path.length === 0 ? message : `${path.map((part) => String(part)).join(".")} ${message}`;
|
|
16869
17217
|
}
|
|
16870
17218
|
function rowId(raw) {
|
|
16871
|
-
const parsed =
|
|
17219
|
+
const parsed = import_zod32.z.object({ id: import_zod32.z.string().min(1) }).loose().safeParse(raw);
|
|
16872
17220
|
if (!parsed.success) return "?";
|
|
16873
17221
|
return parsed.data.id;
|
|
16874
17222
|
}
|
|
@@ -18029,7 +18377,8 @@ var ADAPTER_KEYS = {
|
|
|
18029
18377
|
higgsfield: ["higgsfield"],
|
|
18030
18378
|
"xai-video": ["xai"],
|
|
18031
18379
|
luma: ["luma"],
|
|
18032
|
-
fish: ["fish"]
|
|
18380
|
+
fish: ["fish"],
|
|
18381
|
+
elevenlabs: ["elevenlabs"]
|
|
18033
18382
|
};
|
|
18034
18383
|
function hasAll(keys, names) {
|
|
18035
18384
|
if (!keys) return false;
|
|
@@ -18089,6 +18438,7 @@ function keysFromEnv(env, prefix = "") {
|
|
|
18089
18438
|
set("higgsfieldSecret", pick2("HIGGSFIELD_API_SECRET"));
|
|
18090
18439
|
set("luma", pick2("LUMA_API_KEY", "LUMA_AGENTS_API_KEY"));
|
|
18091
18440
|
set("fish", pick2("FISH_AUDIO_API_KEY"));
|
|
18441
|
+
set("elevenlabs", pick2("ELEVENLABS_API_KEY"));
|
|
18092
18442
|
set("cloudflare", pick2("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
|
|
18093
18443
|
const bearer = pick2("AWS_BEARER_TOKEN_BEDROCK");
|
|
18094
18444
|
const accessKeyId = pick2("AWS_ACCESS_KEY_ID");
|
|
@@ -18111,7 +18461,7 @@ function keysFromEnv(env, prefix = "") {
|
|
|
18111
18461
|
|
|
18112
18462
|
// src/text.ts
|
|
18113
18463
|
var import_ai4 = require("ai");
|
|
18114
|
-
var
|
|
18464
|
+
var import_zod33 = require("zod");
|
|
18115
18465
|
function mergeTextProviderOptions(catalog, caller) {
|
|
18116
18466
|
const merged = {};
|
|
18117
18467
|
const catalogOptions = catalog.providerOptions ?? {};
|
|
@@ -18150,17 +18500,17 @@ function passthroughTextModel(model) {
|
|
|
18150
18500
|
pricingKind: "token"
|
|
18151
18501
|
};
|
|
18152
18502
|
}
|
|
18153
|
-
var perplexityCostSchema =
|
|
18154
|
-
perplexity:
|
|
18503
|
+
var perplexityCostSchema = import_zod33.z.object({
|
|
18504
|
+
perplexity: import_zod33.z.object({ cost: import_zod33.z.object({ totalCost: import_zod33.z.number().finite().nonnegative() }) })
|
|
18155
18505
|
});
|
|
18156
18506
|
function reportedCostUsd(metadata) {
|
|
18157
18507
|
const parsed = perplexityCostSchema.safeParse(metadata);
|
|
18158
18508
|
return parsed.success ? parsed.data.perplexity.cost.totalCost : void 0;
|
|
18159
18509
|
}
|
|
18160
|
-
function textUsageEvent(model, provider,
|
|
18161
|
-
const inputTokens =
|
|
18162
|
-
const outputTokens =
|
|
18163
|
-
const total =
|
|
18510
|
+
function textUsageEvent(model, provider, usage5, providerMetadata) {
|
|
18511
|
+
const inputTokens = usage5.inputTokens ?? 0;
|
|
18512
|
+
const outputTokens = usage5.outputTokens ?? 0;
|
|
18513
|
+
const total = usage5.totalTokens ?? inputTokens + outputTokens;
|
|
18164
18514
|
const event = {
|
|
18165
18515
|
model: model.id,
|
|
18166
18516
|
provider,
|
|
@@ -18195,7 +18545,7 @@ function createTextSteps(initial, resolve, callerProviderOptions) {
|
|
|
18195
18545
|
return { ...prepared, providerOptions };
|
|
18196
18546
|
};
|
|
18197
18547
|
}
|
|
18198
|
-
function
|
|
18548
|
+
function usage5(results) {
|
|
18199
18549
|
const grouped = /* @__PURE__ */ new Map();
|
|
18200
18550
|
for (const [index, step] of results.entries()) {
|
|
18201
18551
|
const target = steps.get(index) ?? initial;
|
|
@@ -18219,12 +18569,12 @@ function createTextSteps(initial, resolve, callerProviderOptions) {
|
|
|
18219
18569
|
}
|
|
18220
18570
|
return [...grouped.values()];
|
|
18221
18571
|
}
|
|
18222
|
-
return { wrap, usage:
|
|
18572
|
+
return { wrap, usage: usage5 };
|
|
18223
18573
|
}
|
|
18224
18574
|
|
|
18225
18575
|
// src/client.ts
|
|
18226
18576
|
var import_ai5 = require("ai");
|
|
18227
|
-
var
|
|
18577
|
+
var import_zod34 = require("zod");
|
|
18228
18578
|
|
|
18229
18579
|
// src/generated-image.ts
|
|
18230
18580
|
function toGeneratedImage(image) {
|
|
@@ -18346,9 +18696,9 @@ function providerJobMethods(kind, deps) {
|
|
|
18346
18696
|
if (!methods.result) throw missing3(adapter, ctx.model);
|
|
18347
18697
|
const result = await methods.result(sub, ctx, webhook);
|
|
18348
18698
|
const base = { ...result.usage, model: ctx.model.id, unit: ctx.model.usage.unit };
|
|
18349
|
-
const
|
|
18350
|
-
await deps.emit(
|
|
18351
|
-
return { ...result, usage:
|
|
18699
|
+
const usage5 = sub.submittedAt === void 0 ? base : deps.timed(base, sub.submittedAt);
|
|
18700
|
+
await deps.emit(usage5);
|
|
18701
|
+
return { ...result, usage: usage5 };
|
|
18352
18702
|
},
|
|
18353
18703
|
async cancel(sub, call = {}) {
|
|
18354
18704
|
const { methods, adapter, ctx } = target(sub.model, call);
|
|
@@ -18588,10 +18938,10 @@ function audioNodeMethods(deps) {
|
|
|
18588
18938
|
const startedAt = Date.now();
|
|
18589
18939
|
const outcome = await adapter.dub(resolved, ctx);
|
|
18590
18940
|
if (outcome.kind === "audio") {
|
|
18591
|
-
const { audio, usage:
|
|
18941
|
+
const { audio, usage: usage5, raw } = outcome.result;
|
|
18592
18942
|
return {
|
|
18593
18943
|
audio,
|
|
18594
|
-
usage: await deps.settle(model,
|
|
18944
|
+
usage: await deps.settle(model, usage5, startedAt),
|
|
18595
18945
|
...raw !== void 0 && { raw }
|
|
18596
18946
|
};
|
|
18597
18947
|
}
|
|
@@ -18629,10 +18979,10 @@ function textAbortOptions(sdk, context) {
|
|
|
18629
18979
|
if (abortSignal) return { abortSignal };
|
|
18630
18980
|
return {};
|
|
18631
18981
|
}
|
|
18632
|
-
var catalogId =
|
|
18633
|
-
var exactSize =
|
|
18634
|
-
var sdkRatio =
|
|
18635
|
-
var sizeTier =
|
|
18982
|
+
var catalogId = import_zod34.z.string();
|
|
18983
|
+
var exactSize = import_zod34.z.templateLiteral([import_zod34.z.number(), "x", import_zod34.z.number()]);
|
|
18984
|
+
var sdkRatio = import_zod34.z.templateLiteral([import_zod34.z.number(), ":", import_zod34.z.number()]);
|
|
18985
|
+
var sizeTier = import_zod34.z.enum(["512", "1K", "2K", "4K"]);
|
|
18636
18986
|
function isCatalogId(model) {
|
|
18637
18987
|
return catalogId.safeParse(model).success;
|
|
18638
18988
|
}
|
|
@@ -18845,7 +19195,7 @@ function createModelKit(config, adapters) {
|
|
|
18845
19195
|
const images = result.images.map(
|
|
18846
19196
|
(file) => toGeneratedImage({ bytes: file.uint8Array, mime: file.mediaType })
|
|
18847
19197
|
);
|
|
18848
|
-
const
|
|
19198
|
+
const usage5 = timed(
|
|
18849
19199
|
{
|
|
18850
19200
|
model: model.modelId,
|
|
18851
19201
|
provider: model.provider,
|
|
@@ -18855,8 +19205,8 @@ function createModelKit(config, adapters) {
|
|
|
18855
19205
|
},
|
|
18856
19206
|
startedAt
|
|
18857
19207
|
);
|
|
18858
|
-
await emit(
|
|
18859
|
-
return { image: images[0], images, usage:
|
|
19208
|
+
await emit(usage5);
|
|
19209
|
+
return { image: images[0], images, usage: usage5, raw: result, warnings };
|
|
18860
19210
|
}
|
|
18861
19211
|
function passthroughVideo(req, call) {
|
|
18862
19212
|
const {
|
|
@@ -18932,7 +19282,7 @@ function createModelKit(config, adapters) {
|
|
|
18932
19282
|
let settled;
|
|
18933
19283
|
let failed = false;
|
|
18934
19284
|
const done = pending.then(async (result) => {
|
|
18935
|
-
const
|
|
19285
|
+
const usage5 = timed(
|
|
18936
19286
|
{
|
|
18937
19287
|
model: model.modelId,
|
|
18938
19288
|
provider: model.provider,
|
|
@@ -18945,10 +19295,10 @@ function createModelKit(config, adapters) {
|
|
|
18945
19295
|
);
|
|
18946
19296
|
settled = {
|
|
18947
19297
|
video: { bytes: result.video.uint8Array, mime: result.video.mediaType },
|
|
18948
|
-
usage:
|
|
19298
|
+
usage: usage5,
|
|
18949
19299
|
raw: result
|
|
18950
19300
|
};
|
|
18951
|
-
await emit(
|
|
19301
|
+
await emit(usage5);
|
|
18952
19302
|
return settled;
|
|
18953
19303
|
});
|
|
18954
19304
|
done.catch(() => {
|
|
@@ -18975,12 +19325,12 @@ function createModelKit(config, adapters) {
|
|
|
18975
19325
|
if (settled) return settled;
|
|
18976
19326
|
const pending = result(opts).then(
|
|
18977
19327
|
async (out) => {
|
|
18978
|
-
const
|
|
19328
|
+
const usage5 = timed(
|
|
18979
19329
|
{ ...out.usage, model: model.id, unit: model.usage.unit },
|
|
18980
19330
|
startedAt
|
|
18981
19331
|
);
|
|
18982
|
-
await emit(
|
|
18983
|
-
return { ...out, usage:
|
|
19332
|
+
await emit(usage5);
|
|
19333
|
+
return { ...out, usage: usage5 };
|
|
18984
19334
|
},
|
|
18985
19335
|
(cause) => {
|
|
18986
19336
|
settled = void 0;
|
|
@@ -19000,9 +19350,9 @@ function createModelKit(config, adapters) {
|
|
|
19000
19350
|
return context(model, signal ? { ...call, signal } : call, warnings);
|
|
19001
19351
|
},
|
|
19002
19352
|
async settle(model, event, startedAt) {
|
|
19003
|
-
const
|
|
19004
|
-
await emit(
|
|
19005
|
-
return
|
|
19353
|
+
const usage5 = timed({ ...event, model: model.id, unit: model.usage.unit }, startedAt);
|
|
19354
|
+
await emit(usage5);
|
|
19355
|
+
return usage5;
|
|
19006
19356
|
},
|
|
19007
19357
|
wrapVideoJob
|
|
19008
19358
|
});
|
|
@@ -19038,11 +19388,11 @@ function createModelKit(config, adapters) {
|
|
|
19038
19388
|
});
|
|
19039
19389
|
const startedAt = Date.now();
|
|
19040
19390
|
const result = await adapter.generateImage(request, ctx);
|
|
19041
|
-
const
|
|
19391
|
+
const usage5 = timed(
|
|
19042
19392
|
{ ...result.usage, model: model.id, unit: model.usage.unit },
|
|
19043
19393
|
startedAt
|
|
19044
19394
|
);
|
|
19045
|
-
await emit(
|
|
19395
|
+
await emit(usage5);
|
|
19046
19396
|
const images = result.images.map(toGeneratedImage);
|
|
19047
19397
|
const first = images[0];
|
|
19048
19398
|
if (!first)
|
|
@@ -19051,7 +19401,7 @@ function createModelKit(config, adapters) {
|
|
|
19051
19401
|
message: `${model.id} returned no image`,
|
|
19052
19402
|
model: model.id
|
|
19053
19403
|
});
|
|
19054
|
-
return { ...result, usage:
|
|
19404
|
+
return { ...result, usage: usage5, image: first, images, warnings };
|
|
19055
19405
|
},
|
|
19056
19406
|
async generateVideo(req, call = {}) {
|
|
19057
19407
|
mediaRetryOptions(call);
|
|
@@ -19150,12 +19500,12 @@ function createModelKit(config, adapters) {
|
|
|
19150
19500
|
}
|
|
19151
19501
|
const startedAt = Date.now();
|
|
19152
19502
|
const result = await adapter.generateAudio(resolved, ctx);
|
|
19153
|
-
const
|
|
19503
|
+
const usage5 = timed(
|
|
19154
19504
|
{ ...result.usage, model: model.id, unit: model.usage.unit },
|
|
19155
19505
|
startedAt
|
|
19156
19506
|
);
|
|
19157
|
-
await emit(
|
|
19158
|
-
return { ...result, usage:
|
|
19507
|
+
await emit(usage5);
|
|
19508
|
+
return { ...result, usage: usage5 };
|
|
19159
19509
|
},
|
|
19160
19510
|
async prepareMediaPart(modelId, ref, mediaType, call = {}) {
|
|
19161
19511
|
mediaRetryOptions(call);
|
|
@@ -19235,8 +19585,8 @@ function createModelKit(config, adapters) {
|
|
|
19235
19585
|
options.prepareStep ?? options.experimental_prepareStep
|
|
19236
19586
|
),
|
|
19237
19587
|
onFinish: async (event) => {
|
|
19238
|
-
for (const
|
|
19239
|
-
await emit(textTimed(withRowUnit(
|
|
19588
|
+
for (const usage5 of steps.usage(event.steps))
|
|
19589
|
+
await emit(textTimed(withRowUnit(usage5), event.steps.length, startedAt));
|
|
19240
19590
|
await callerOnFinish?.(event);
|
|
19241
19591
|
}
|
|
19242
19592
|
});
|
|
@@ -19355,6 +19705,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
|
|
|
19355
19705
|
dataOnlyCapable,
|
|
19356
19706
|
describe,
|
|
19357
19707
|
describeThrown,
|
|
19708
|
+
elevenLabsAdapter,
|
|
19358
19709
|
experimental_generateImage,
|
|
19359
19710
|
falAdapter,
|
|
19360
19711
|
falWebhookBodySchema,
|