@shop-os/modelkit 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/catalog.js CHANGED
@@ -1720,6 +1720,369 @@ var ELEVENLABS_VOICES = [
1720
1720
  previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/pqHfZKP75CvOlQylNhV4/d782b3ff-84ba-4029-848c-acf01285524d.mp3"
1721
1721
  }
1722
1722
  ];
1723
+ var PREMADE_ID = /\/premade\/voices\/([A-Za-z0-9]+)\//;
1724
+ var ELEVENLABS_DIRECT_VOICES = ELEVENLABS_VOICES.flatMap((v) => {
1725
+ const id = v.previewUrl ? PREMADE_ID.exec(v.previewUrl)?.[1] : void 0;
1726
+ return id ? [{ ...v, id }] : [];
1727
+ });
1728
+ var ELEVENLABS_DIRECT_DEFAULT_VOICE = "EXAVITQu4vr4xnSDxMaL";
1729
+
1730
+ // src/catalog/speech-elevenlabs-direct.ts
1731
+ var ELEVENLABS_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
1732
+ var PER_CALL = { unit: "images" };
1733
+ var PER_REQUEST = { unit: "requests" };
1734
+ var ELEVENLABS_DIRECT_MODELS = [
1735
+ {
1736
+ id: "elevenlabs-v4-direct",
1737
+ aliases: [],
1738
+ label: "ElevenLabs v4 (direct)",
1739
+ type: "speech",
1740
+ adapter: "elevenlabs",
1741
+ providerModelId: "eleven_v4",
1742
+ lifecycle: "active",
1743
+ exposure: [],
1744
+ constraints: {
1745
+ // Premade voices under their ElevenLabs ids (the direct API takes ids, not names).
1746
+ voices: ELEVENLABS_DIRECT_VOICES,
1747
+ languages: ELEVENLABS_V4_LANGUAGES,
1748
+ outputFormats: ELEVENLABS_MP3_FORMATS,
1749
+ supports: [],
1750
+ // ElevenLabs docs: 10,000 characters per request.
1751
+ prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
1752
+ },
1753
+ defaults: { voice: ELEVENLABS_DIRECT_DEFAULT_VOICE, outputFormat: "mp3_44100_128" },
1754
+ usage: PER_CALL,
1755
+ pricingKind: "flat",
1756
+ display: {
1757
+ provider: "elevenlabs",
1758
+ logo: ELEVENLABS_LOGO,
1759
+ description: "ElevenLabs v4 text to speech with audio tags, in a premade voice or your own cloned voice; up to 10,000 characters."
1760
+ },
1761
+ capabilities: { customVoices: true }
1762
+ },
1763
+ {
1764
+ id: "elevenlabs-ivc",
1765
+ aliases: [],
1766
+ label: "ElevenLabs Instant Voice Clone",
1767
+ type: "voice-clone",
1768
+ adapter: "elevenlabs",
1769
+ providerModelId: "ivc",
1770
+ lifecycle: "active",
1771
+ exposure: [],
1772
+ constraints: {
1773
+ // POST /v1/voices/add; ElevenLabs takes up to 25 samples and advises 1-2 minutes of
1774
+ // clean speech in total. Each clone uses a voice slot on our plan until deleted.
1775
+ references: { audios: 25, audioFormats: ["mp3", "wav", "m4a", "webm", "ogg"] },
1776
+ supports: []
1777
+ },
1778
+ defaults: {},
1779
+ usage: PER_REQUEST,
1780
+ pricingKind: "flat",
1781
+ display: {
1782
+ provider: "elevenlabs",
1783
+ logo: ELEVENLABS_LOGO,
1784
+ description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on ElevenLabs v4 (direct)."
1785
+ },
1786
+ capabilities: {}
1787
+ }
1788
+ ];
1789
+
1790
+ // src/catalog/voices-fish.ts
1791
+ var FISH_VOICES = [
1792
+ {
1793
+ id: "933563129e564b19a115bedd57b7406a",
1794
+ name: "Sarah",
1795
+ gender: "female",
1796
+ languages: ["en"],
1797
+ description: "An engaged speaker.",
1798
+ previewUrl: "https://platform.r2.fish.audio/task/4e204945670c460c86fdab57512cb308.mp3"
1799
+ },
1800
+ {
1801
+ id: "bf322df2096a46f18c579d0baa36f41d",
1802
+ name: "Adrian",
1803
+ gender: "male",
1804
+ languages: ["en"],
1805
+ description: "A steady and reliable narrator.",
1806
+ previewUrl: "https://platform.r2.fish.audio/task/d730b09f4a484316a77ecc86137e85fb.mp3"
1807
+ },
1808
+ {
1809
+ id: "536d3a5e000945adb7038665781a4aca",
1810
+ name: "Ethan",
1811
+ gender: "male",
1812
+ languages: ["en"],
1813
+ description: "A curious explainer.",
1814
+ previewUrl: "https://platform.r2.fish.audio/task/bdd677ed767744fc91f166468786264b.mp3"
1815
+ },
1816
+ {
1817
+ id: "b347db033a6549378b48d00acb0d06cd",
1818
+ name: "Selene",
1819
+ gender: "female",
1820
+ languages: ["en"],
1821
+ description: "A meditative female voice.",
1822
+ previewUrl: "https://platform.r2.fish.audio/task/fcf629b4a0b343558f461338ba1b9da4.mp3"
1823
+ },
1824
+ {
1825
+ id: "9a9cf47702da476aa4629e2506d4a857",
1826
+ name: "Hannah",
1827
+ gender: "female",
1828
+ languages: ["en"],
1829
+ description: "A conversation specialist.",
1830
+ previewUrl: "https://platform.r2.fish.audio/task/08d658835feb427db808e37060b78eb0.mp3"
1831
+ },
1832
+ {
1833
+ id: "79d0bd3e4e5444b18f7b6d89b5927bf1",
1834
+ name: "Jordan",
1835
+ gender: "male",
1836
+ languages: ["en"],
1837
+ description: "A motivational speaker.",
1838
+ previewUrl: "https://platform.r2.fish.audio/task/edecbc0bdd4c46c39eda8520a0f729ef.mp3"
1839
+ },
1840
+ {
1841
+ id: "e3cd384158934cc9a01029cd7d278634",
1842
+ name: "Laura",
1843
+ gender: "female",
1844
+ languages: ["en"],
1845
+ description: "A confident female narrator.",
1846
+ previewUrl: "https://platform.r2.fish.audio/task/e821bef995344d519caa1c93d808db23.mp3"
1847
+ },
1848
+ {
1849
+ id: "f3a2b90078d54a65af4b95f63bc7798e",
1850
+ name: "Sheila",
1851
+ gender: "female",
1852
+ accent: "american",
1853
+ languages: ["en"],
1854
+ description: "A young American English woman's voice.",
1855
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/9bb9e1a1cf3f47f38700da1c5c0a678a.mp3"
1856
+ },
1857
+ {
1858
+ id: "0db6e93af420470b9d9b9a886dbaa954",
1859
+ name: "Kai",
1860
+ gender: "male",
1861
+ languages: ["en"],
1862
+ description: "A friendly English man's voice for companion apps and everyday conversation.",
1863
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/13306f1172544c3ba3106d1e363feaa6.mp3"
1864
+ },
1865
+ {
1866
+ id: "8abfa9ebd4c24a3399b68c3cdac8c7b5",
1867
+ name: "Freya",
1868
+ gender: "female",
1869
+ accent: "british",
1870
+ languages: ["en"],
1871
+ 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.",
1872
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/07918705a56b42daaab149f65392cea7.mp3"
1873
+ },
1874
+ {
1875
+ id: "2f64e528a4f2406691925ab1e0ed5b7f",
1876
+ name: "Ananya",
1877
+ gender: "female",
1878
+ languages: ["hi"],
1879
+ 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",
1880
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a17a700668d8405f8316db120733145c.mp3"
1881
+ },
1882
+ {
1883
+ id: "cefd91a07b99468f9148e2580990082d",
1884
+ name: "Arjun",
1885
+ gender: "male",
1886
+ languages: ["hi"],
1887
+ 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",
1888
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/a0c4a8eadb7841cdae8e5e68031a2dfb.mp3"
1889
+ },
1890
+ {
1891
+ id: "3f065d660212478a9c697fa965b4d098",
1892
+ name: "Carmen",
1893
+ gender: "female",
1894
+ languages: ["es"],
1895
+ description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
1896
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/3421be48b6cb4c28bf829c25188e7e74.mp3"
1897
+ },
1898
+ {
1899
+ id: "ec0718028a2445fb9be8e8bfb56a0150",
1900
+ name: "Rodrigo",
1901
+ gender: "male",
1902
+ languages: ["es"],
1903
+ description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
1904
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/5a77c32365604800ad5b4eb13c5585df.mp3"
1905
+ },
1906
+ {
1907
+ id: "10a3a20742114a4ea6dd441e7591850f",
1908
+ name: "Manon",
1909
+ gender: "female",
1910
+ languages: ["fr"],
1911
+ description: "Une voix claire et vive, adapt\xE9e aux assistants vocaux et au support client.",
1912
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/f7b75ee1fa524e48a8cefb003cf72952.mp3"
1913
+ },
1914
+ {
1915
+ id: "f69bca092b674168a8d02d61ca20943c",
1916
+ name: "Lucas",
1917
+ gender: "male",
1918
+ languages: ["fr"],
1919
+ description: "Une voix claire et assur\xE9e, adapt\xE9e aux assistants vocaux et au service client.",
1920
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/86fca3e56a014a3d8a11450854bb8311.mp3"
1921
+ },
1922
+ {
1923
+ id: "3937b44b604249f18b94913daef94043",
1924
+ name: "Lena",
1925
+ gender: "female",
1926
+ languages: ["de"],
1927
+ description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
1928
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/8659fec80b74442dab68a7755716a401.mp3"
1929
+ },
1930
+ {
1931
+ id: "c1e7cc926aa5469aa966c353094d4b0b",
1932
+ name: "Lukas",
1933
+ gender: "male",
1934
+ languages: ["de"],
1935
+ description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
1936
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/fb1d538126bb4fe38f8366250f6a2136.mp3"
1937
+ },
1938
+ {
1939
+ id: "b3c060cc41544a7da94a2c487647c902",
1940
+ name: "Let\xEDcia",
1941
+ gender: "female",
1942
+ accent: "brazilian",
1943
+ languages: ["pt"],
1944
+ 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.",
1945
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/13c6c0d96b5c4069951f89868ee15360.mp3"
1946
+ },
1947
+ {
1948
+ id: "add7bcb8d3c8423bbfa152ca232aad6e",
1949
+ name: "Ricardo",
1950
+ gender: "male",
1951
+ accent: "brazilian",
1952
+ languages: ["pt"],
1953
+ 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.",
1954
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/67475fc717ae45cbb38e647a2dd37fe1.mp3"
1955
+ },
1956
+ {
1957
+ id: "b2d9d8db057042688a5e318b8f405bc2",
1958
+ name: "Kyoko",
1959
+ gender: "female",
1960
+ languages: ["ja"],
1961
+ 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",
1962
+ previewUrl: "https://platform.r2.fish.audio/task/993b3ccb52124341aff79580f1ce5a5a.mp3"
1963
+ },
1964
+ {
1965
+ id: "297a6fd278df47c3b9da9bfdf55ac89a",
1966
+ name: "Satoru",
1967
+ gender: "male",
1968
+ languages: ["ja"],
1969
+ 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",
1970
+ previewUrl: "https://platform.r2.fish.audio/task/1b4712fb831f4c149eef6b0133157418.mp3"
1971
+ },
1972
+ {
1973
+ id: "0b14f34a13a94ed88fe6113193749bb0",
1974
+ name: "Fatima",
1975
+ gender: "female",
1976
+ languages: ["ar"],
1977
+ 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.",
1978
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/93406e1b905a4c4994306e3421243390.mp3"
1979
+ },
1980
+ {
1981
+ id: "74c6aba5cbf94a15bbdc547ffce5cb38",
1982
+ name: "Yutong",
1983
+ gender: "female",
1984
+ languages: ["zh"],
1985
+ 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",
1986
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a48320f37fe44c4699549a1afc3778e8.mp3"
1987
+ }
1988
+ ];
1989
+
1990
+ // src/catalog/speech-fish.ts
1991
+ var FISH_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/fishaudio.png";
1992
+ var PER_CALL2 = { unit: "images" };
1993
+ var PER_REQUEST2 = { unit: "requests" };
1994
+ var TTS_MAX_CHARS = 1e4;
1995
+ function fishTts(id, label, providerModelId, description) {
1996
+ return {
1997
+ id,
1998
+ aliases: [],
1999
+ label,
2000
+ type: "speech",
2001
+ adapter: "fish",
2002
+ providerModelId,
2003
+ lifecycle: "active",
2004
+ exposure: [],
2005
+ constraints: {
2006
+ voices: FISH_VOICES,
2007
+ outputFormats: ["mp3", "wav"],
2008
+ supports: [],
2009
+ prompt: { referenceSyntax: "numbered", maxChars: TTS_MAX_CHARS }
2010
+ },
2011
+ defaults: { voice: "9a9cf47702da476aa4629e2506d4a857", outputFormat: "mp3" },
2012
+ usage: PER_CALL2,
2013
+ pricingKind: "flat",
2014
+ display: { provider: "fishaudio", logo: FISH_LOGO, description },
2015
+ capabilities: { customVoices: true }
2016
+ };
2017
+ }
2018
+ var FISH_MODELS = [
2019
+ fishTts(
2020
+ "fish-s2.1-pro",
2021
+ "Fish Audio S2.1 Pro",
2022
+ "s2.1-pro",
2023
+ "Fish Audio S2.1 Pro text to speech in 83 languages, with [bracket] directions and your own cloned voices."
2024
+ ),
2025
+ fishTts(
2026
+ "fish-s2.1-pro-free",
2027
+ "Fish Audio S2.1 Pro Free",
2028
+ "s2.1-pro-free",
2029
+ "Fish Audio S2.1 Pro on the free developer tier: the same voices at no cost, without latency or data-processing guarantees."
2030
+ ),
2031
+ fishTts(
2032
+ "fish-drama-3",
2033
+ "Fish Audio Drama 3",
2034
+ "drama-3",
2035
+ "Fish Audio Drama 3 text to speech you direct like an actor: [directions], sounds, pauses and per-word tags."
2036
+ ),
2037
+ {
2038
+ id: "fish-voice-clone",
2039
+ aliases: [],
2040
+ label: "Fish Audio Voice Clone",
2041
+ type: "voice-clone",
2042
+ adapter: "fish",
2043
+ providerModelId: "voice-clone",
2044
+ lifecycle: "active",
2045
+ exposure: [],
2046
+ constraints: {
2047
+ // POST /model takes 1-20 voice files; Fish advises 10 s or more of clean speech each.
2048
+ references: { audios: 20, audioFormats: ["wav", "mp3", "m4a", "opus"] },
2049
+ supports: []
2050
+ },
2051
+ defaults: {},
2052
+ usage: PER_REQUEST2,
2053
+ pricingKind: "flat",
2054
+ display: {
2055
+ provider: "fishaudio",
2056
+ logo: FISH_LOGO,
2057
+ description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on the Fish Audio speech models."
2058
+ },
2059
+ capabilities: {}
2060
+ },
2061
+ {
2062
+ id: "fish-voice-design-1",
2063
+ aliases: [],
2064
+ label: "Fish Audio Voice Design",
2065
+ type: "voice-design",
2066
+ adapter: "fish",
2067
+ providerModelId: "voice-design-1",
2068
+ lifecycle: "active",
2069
+ exposure: [],
2070
+ constraints: {
2071
+ voiceDesign: { maxCandidates: 4, previewMaxChars: 150 },
2072
+ supports: [],
2073
+ prompt: { referenceSyntax: "numbered", maxChars: 2e3 }
2074
+ },
2075
+ defaults: {},
2076
+ usage: PER_REQUEST2,
2077
+ pricingKind: "flat",
2078
+ display: {
2079
+ provider: "fishaudio",
2080
+ logo: FISH_LOGO,
2081
+ description: "Describe a voice and get up to four short candidates; keep one as a cloned voice."
2082
+ },
2083
+ capabilities: {}
2084
+ }
2085
+ ];
1723
2086
 
1724
2087
  // src/catalog/voices-gemini.ts
1725
2088
  var GEMINI_TTS_VOICES = [
@@ -3296,11 +3659,11 @@ var MINIMAX_VOICES = [
3296
3659
  ];
3297
3660
 
3298
3661
  // src/catalog/media-nodes.ts
3299
- var ELEVENLABS_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3662
+ var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3300
3663
  var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
3301
3664
  var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3302
3665
  var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
3303
- var PER_CALL = { unit: "images" };
3666
+ var PER_CALL3 = { unit: "images" };
3304
3667
  var EXPOSURE4 = ["cowork", "space_builder"];
3305
3668
  var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
3306
3669
  function elevenLabsTts(tts) {
@@ -3321,9 +3684,9 @@ function elevenLabsTts(tts) {
3321
3684
  prompt: { referenceSyntax: "numbered", maxChars: tts.maxChars }
3322
3685
  },
3323
3686
  defaults: { voice: "Rachel", ...tts.formats && { outputFormat: "mp3_44100_128" } },
3324
- usage: PER_CALL,
3687
+ usage: PER_CALL3,
3325
3688
  pricingKind: "flat",
3326
- display: { provider: "elevenlabs", logo: ELEVENLABS_LOGO, description: tts.description },
3689
+ display: { provider: "elevenlabs", logo: ELEVENLABS_LOGO2, description: tts.description },
3327
3690
  capabilities: {}
3328
3691
  };
3329
3692
  }
@@ -3345,7 +3708,7 @@ function geminiTts(id, label, description) {
3345
3708
  prompt: { referenceSyntax: "numbered", maxChars: 8192 }
3346
3709
  },
3347
3710
  defaults: { voice: "Kore" },
3348
- usage: PER_CALL,
3711
+ usage: PER_CALL3,
3349
3712
  pricingKind: "flat",
3350
3713
  display: { provider: "google", logo: GOOGLE_LOGO, description },
3351
3714
  capabilities: {}
@@ -3368,7 +3731,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
3368
3731
  },
3369
3732
  defaults: {},
3370
3733
  ...syncModel !== void 0 && { providerOptions: { fal: { model: syncModel } } },
3371
- usage: PER_CALL,
3734
+ usage: PER_CALL3,
3372
3735
  pricingKind: "flat",
3373
3736
  display: { provider: "sync", logo: SYNC_LOGO, description },
3374
3737
  capabilities: { acceptsSourceVideo: true }
@@ -3429,7 +3792,7 @@ var MEDIA_NODE_MODELS = [
3429
3792
  prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
3430
3793
  },
3431
3794
  defaults: { voice: "Wise_Woman" },
3432
- usage: PER_CALL,
3795
+ usage: PER_CALL3,
3433
3796
  pricingKind: "flat",
3434
3797
  display: {
3435
3798
  provider: "minimax",
@@ -3456,11 +3819,11 @@ var MEDIA_NODE_MODELS = [
3456
3819
  supports: []
3457
3820
  },
3458
3821
  defaults: { voice: "Rachel", outputFormat: "mp3_44100_128" },
3459
- usage: PER_CALL,
3822
+ usage: PER_CALL3,
3460
3823
  pricingKind: "flat",
3461
3824
  display: {
3462
3825
  provider: "elevenlabs",
3463
- logo: ELEVENLABS_LOGO,
3826
+ logo: ELEVENLABS_LOGO2,
3464
3827
  description: "ElevenLabs speech to speech; re-voices a recording in another voice and keeps its timing and delivery."
3465
3828
  },
3466
3829
  capabilities: {}
@@ -3483,11 +3846,11 @@ var MEDIA_NODE_MODELS = [
3483
3846
  supports: []
3484
3847
  },
3485
3848
  defaults: {},
3486
- usage: PER_CALL,
3849
+ usage: PER_CALL3,
3487
3850
  pricingKind: "flat",
3488
3851
  display: {
3489
3852
  provider: "elevenlabs",
3490
- logo: ELEVENLABS_LOGO,
3853
+ logo: ELEVENLABS_LOGO2,
3491
3854
  description: "ElevenLabs dubbing; translates the speech in a video or audio file into another language in the original voices."
3492
3855
  },
3493
3856
  capabilities: { acceptsSourceVideo: true }
@@ -3530,7 +3893,7 @@ var MEDIA_NODE_MODELS = [
3530
3893
  supports: []
3531
3894
  },
3532
3895
  defaults: {},
3533
- usage: PER_CALL,
3896
+ usage: PER_CALL3,
3534
3897
  pricingKind: "flat",
3535
3898
  display: {
3536
3899
  provider: "veed",
@@ -3582,16 +3945,18 @@ var MEDIA_NODE_MODELS = [
3582
3945
  supports: []
3583
3946
  },
3584
3947
  defaults: {},
3585
- usage: PER_CALL,
3948
+ usage: PER_CALL3,
3586
3949
  pricingKind: "flat",
3587
3950
  display: {
3588
3951
  provider: "elevenlabs",
3589
- logo: ELEVENLABS_LOGO,
3952
+ logo: ELEVENLABS_LOGO2,
3590
3953
  description: "ElevenLabs Scribe v2 speech to text with word timings, speaker labels and audio-event tags."
3591
3954
  },
3592
3955
  capabilities: {}
3593
3956
  },
3594
- SAM_3_IMAGE
3957
+ SAM_3_IMAGE,
3958
+ ...FISH_MODELS,
3959
+ ...ELEVENLABS_DIRECT_MODELS
3595
3960
  ];
3596
3961
 
3597
3962
  // src/catalog/model3d.ts
@@ -3787,7 +4152,7 @@ var MODEL3D_MODELS = [
3787
4152
 
3788
4153
  // src/catalog/other.ts
3789
4154
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
3790
- var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
4155
+ var ELEVENLABS_LOGO3 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3791
4156
  var MINIMAX_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3792
4157
  var OTHER_MODELS = [
3793
4158
  {
@@ -3952,7 +4317,7 @@ var OTHER_MODELS = [
3952
4317
  pricingKind: "flat",
3953
4318
  display: {
3954
4319
  provider: "elevenlabs",
3955
- logo: ELEVENLABS_LOGO2,
4320
+ logo: ELEVENLABS_LOGO3,
3956
4321
  description: "ElevenLabs Music v2.5 text-to-music; full songs or instrumentals from 3 seconds to 1 minute."
3957
4322
  },
3958
4323
  capabilities: {}
@@ -4004,7 +4369,7 @@ var OTHER_MODELS = [
4004
4369
  pricingKind: "flat",
4005
4370
  display: {
4006
4371
  provider: "elevenlabs",
4007
- logo: ELEVENLABS_LOGO2,
4372
+ logo: ELEVENLABS_LOGO3,
4008
4373
  description: "ElevenLabs Sound Effects v2; a sound effect from a text description, up to 22 seconds."
4009
4374
  },
4010
4375
  capabilities: {}
@@ -7232,7 +7597,9 @@ var ADAPTER_IDS = [
7232
7597
  "prodia",
7233
7598
  "higgsfield",
7234
7599
  "xai-video",
7235
- "luma"
7600
+ "luma",
7601
+ "fish",
7602
+ "elevenlabs"
7236
7603
  ];
7237
7604
  var ModelTypeSchema = z3.enum([
7238
7605
  "text",
@@ -7249,6 +7616,8 @@ var ModelTypeSchema = z3.enum([
7249
7616
  "dubbing",
7250
7617
  "lipsync",
7251
7618
  "transcription",
7619
+ "voice-clone",
7620
+ "voice-design",
7252
7621
  "model3d"
7253
7622
  ]);
7254
7623
  var LifecycleSchema = z3.enum(["active", "deprecated", "retired"]);
@@ -7424,6 +7793,10 @@ var ModelConstraintsSchema = z3.looseObject({
7424
7793
  supports: z3.array(SupportedOptionSchema).optional(),
7425
7794
  ratioIgnoredWith: z3.array(z3.enum(["firstFrame", "sourceVideo"])).optional(),
7426
7795
  voices: z3.array(VoiceOptionSchema).optional(),
7796
+ voiceDesign: z3.object({
7797
+ maxCandidates: z3.number().int().positive(),
7798
+ previewMaxChars: z3.number().int().positive()
7799
+ }).optional(),
7427
7800
  languages: z3.array(LanguageOptionSchema).optional(),
7428
7801
  outputFormats: z3.array(z3.string().min(1)).min(1).optional(),
7429
7802
  sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: z3.boolean().optional() }).optional(),
@@ -7522,6 +7895,7 @@ var CatalogDisplaySchema = z3.looseObject({
7522
7895
  });
7523
7896
  var CatalogCapabilitiesSchema = z3.looseObject({
7524
7897
  acceptsSourceVideo: z3.boolean().optional(),
7898
+ customVoices: z3.boolean().optional(),
7525
7899
  videoEdit: z3.boolean().optional(),
7526
7900
  framesExcludeReferences: z3.boolean().optional(),
7527
7901
  sourceVideoExcludesFrames: z3.boolean().optional(),
@@ -9400,8 +9774,17 @@ function resolveSpeechOptions(model, request, onUnsupported = "error") {
9400
9774
  const warnings = [];
9401
9775
  checkScript(model, request.text);
9402
9776
  const resolved = { model: model.id, text: request.text };
9403
- const voice = resolveVoice(model, request.voice, onUnsupported, warnings);
9404
- if (voice !== void 0) resolved.voice = voice;
9777
+ const customVoice = request.customVoice?.trim();
9778
+ if (customVoice) {
9779
+ if (model.capabilities?.customVoices !== true) {
9780
+ throw unsupported3(model, "does not take a custom voice");
9781
+ }
9782
+ if (request.voice?.trim()) throw unsupported3(model, "takes voice or customVoice, not both");
9783
+ resolved.customVoice = customVoice;
9784
+ } else {
9785
+ const voice = resolveVoice(model, request.voice, onUnsupported, warnings);
9786
+ if (voice !== void 0) resolved.voice = voice;
9787
+ }
9405
9788
  const language = resolveListed(
9406
9789
  model,
9407
9790
  {
@@ -9523,6 +9906,106 @@ function resolveTranscriptionOptions(model, request, onUnsupported = "error") {
9523
9906
  return { request: resolved, warnings };
9524
9907
  }
9525
9908
 
9909
+ // src/resolve-voice.ts
9910
+ function refuse(model, code, message) {
9911
+ return new ModelKitError({ code, message: `${model.label} ${message}`, model: model.id });
9912
+ }
9913
+ function checkSample(model, sample, index) {
9914
+ const limits = model.constraints.references?.audioSeconds;
9915
+ const length = sample.audio.durationSeconds;
9916
+ if (limits === void 0 || length === void 0) return;
9917
+ if (limits.min !== void 0 && length < limits.min || limits.max !== void 0 && length > limits.max) {
9918
+ const range2 = `${limits.min ?? 0}-${limits.max ?? "any"} s`;
9919
+ throw refuse(
9920
+ model,
9921
+ "unsupported_option",
9922
+ `takes samples of ${range2}; sample ${index + 1} is ${length} s`
9923
+ );
9924
+ }
9925
+ }
9926
+ function resolveVoiceCloneOptions(model, request) {
9927
+ const title = request.title.trim();
9928
+ if (!title) throw refuse(model, "missing_input", "needs a title");
9929
+ if (request.samples.length === 0) throw refuse(model, "missing_input", "needs a voice sample");
9930
+ const max = model.constraints.references?.audios;
9931
+ if (max !== void 0 && request.samples.length > max) {
9932
+ throw refuse(
9933
+ model,
9934
+ "unsupported_option",
9935
+ `takes up to ${max} samples; got ${request.samples.length}`
9936
+ );
9937
+ }
9938
+ request.samples.forEach((sample, index) => checkSample(model, sample, index));
9939
+ const total = model.constraints.references?.audioSeconds?.total;
9940
+ const seconds3 = request.samples.reduce((sum, s) => sum + (s.audio.durationSeconds ?? 0), 0);
9941
+ if (total !== void 0 && seconds3 > total) {
9942
+ throw refuse(
9943
+ model,
9944
+ "unsupported_option",
9945
+ `takes up to ${total} s of samples; got ${seconds3} s`
9946
+ );
9947
+ }
9948
+ for (const field of ["transcript", "designSignature"]) {
9949
+ const set = request.samples.filter((s) => s[field]?.trim()).length;
9950
+ if (set !== 0 && set !== request.samples.length) {
9951
+ throw refuse(model, "missing_input", `needs ${field} on every sample or on none`);
9952
+ }
9953
+ }
9954
+ return { request: { ...request, model: model.id, title }, warnings: [] };
9955
+ }
9956
+ function resolveVoiceDesignOptions(model, request, onUnsupported = "error") {
9957
+ const warnings = [];
9958
+ const prompt = request.prompt.trim();
9959
+ if (!prompt) throw refuse(model, "missing_input", "needs a voice description");
9960
+ const maxChars = promptConventions(model).maxChars;
9961
+ if (maxChars !== void 0 && prompt.length > maxChars) {
9962
+ throw refuse(
9963
+ model,
9964
+ "unsupported_option",
9965
+ `takes up to ${maxChars} characters; got ${prompt.length}`
9966
+ );
9967
+ }
9968
+ const limits = model.constraints.voiceDesign;
9969
+ const resolved = { model: model.id, prompt };
9970
+ const preview2 = request.previewText?.trim();
9971
+ if (preview2) {
9972
+ if (limits !== void 0 && preview2.length > limits.previewMaxChars) {
9973
+ throw refuse(
9974
+ model,
9975
+ "unsupported_option",
9976
+ `takes a preview line of up to ${limits.previewMaxChars} characters; got ${preview2.length}`
9977
+ );
9978
+ }
9979
+ resolved.previewText = preview2;
9980
+ }
9981
+ const language = request.language?.trim();
9982
+ if (language) resolved.language = language;
9983
+ if (request.seed !== void 0) resolved.seed = request.seed;
9984
+ const count2 = request.count ?? model.defaults.count;
9985
+ if (count2 !== void 0) {
9986
+ const cap = limits?.maxCandidates ?? count2;
9987
+ if (!Number.isInteger(count2) || count2 < 1 || count2 > cap) {
9988
+ if (onUnsupported === "error" || !Number.isInteger(count2)) {
9989
+ throw refuse(
9990
+ model,
9991
+ "unsupported_option",
9992
+ `returns 1-${cap} candidates; got ${count2}`
9993
+ );
9994
+ }
9995
+ const to = Math.min(Math.max(count2, 1), cap);
9996
+ warnings.push({
9997
+ code: "option_ignored",
9998
+ field: "count",
9999
+ from: String(count2),
10000
+ to: String(to),
10001
+ message: `count ${count2} replaced by ${to} for ${model.id}`
10002
+ });
10003
+ resolved.count = to;
10004
+ } else resolved.count = count2;
10005
+ }
10006
+ return { request: resolved, warnings };
10007
+ }
10008
+
9526
10009
  // src/resolve-call.ts
9527
10010
  var MEDIA_METHODS = {
9528
10011
  text: [],
@@ -9538,6 +10021,9 @@ var MEDIA_METHODS = {
9538
10021
  lipsync: ["lipsync"],
9539
10022
  dubbing: ["dub"],
9540
10023
  transcription: ["transcribe"],
10024
+ // deleteVoice runs on the voice-clone row too; it checks with assertMethod("createVoice").
10025
+ "voice-clone": ["createVoice"],
10026
+ "voice-design": ["designVoice"],
9541
10027
  "video-tool": ["submitVideoTool"],
9542
10028
  // 3D jobs run through submitModel3d, which checks the row type itself; their preflight
9543
10029
  // is model3dInputRefusal, not resolveOptions.
@@ -9552,6 +10038,8 @@ var METHOD_NOUN = {
9552
10038
  lipsync: "a lipsync model",
9553
10039
  dub: "a dubbing model",
9554
10040
  transcribe: "a transcription model",
10041
+ createVoice: "a voice cloning model",
10042
+ designVoice: "a voice design model",
9555
10043
  submitVideoTool: "a video tool"
9556
10044
  };
9557
10045
  function assertMethod(model, method) {
@@ -9594,6 +10082,14 @@ function resolveTranscriptionCall(model, request, onUnsupported = "error") {
9594
10082
  assertMethod(model, "transcribe");
9595
10083
  return resolveTranscriptionOptions(model, request, onUnsupported);
9596
10084
  }
10085
+ function resolveVoiceCloneCall(model, request) {
10086
+ assertMethod(model, "createVoice");
10087
+ return resolveVoiceCloneOptions(model, request);
10088
+ }
10089
+ function resolveVoiceDesignCall(model, request, onUnsupported = "error") {
10090
+ assertMethod(model, "designVoice");
10091
+ return resolveVoiceDesignOptions(model, request, onUnsupported);
10092
+ }
9597
10093
  function resolveVideoToolCall(model, request) {
9598
10094
  assertMethod(model, "submitVideoTool");
9599
10095
  const plan = planVideoTool(model, request);
@@ -9640,6 +10136,13 @@ function resolveOptions(model, call, onUnsupported = "error") {
9640
10136
  method: call.method,
9641
10137
  ...resolveTranscriptionCall(model, call.request, onUnsupported)
9642
10138
  };
10139
+ case "createVoice":
10140
+ return { method: call.method, ...resolveVoiceCloneCall(model, call.request) };
10141
+ case "designVoice":
10142
+ return {
10143
+ method: call.method,
10144
+ ...resolveVoiceDesignCall(model, call.request, onUnsupported)
10145
+ };
9643
10146
  case "submitVideoTool":
9644
10147
  return { method: call.method, ...resolveVideoToolCall(model, call.request) };
9645
10148
  }
@@ -9699,6 +10202,8 @@ export {
9699
10202
  resolveTranscriptionOptions,
9700
10203
  resolveVideoOptions,
9701
10204
  resolveVoiceChangeOptions,
10205
+ resolveVoiceCloneOptions,
10206
+ resolveVoiceDesignOptions,
9702
10207
  sortCatalogRows,
9703
10208
  takesTargetAspectRatio,
9704
10209
  videoLengthViolation,