@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.cjs CHANGED
@@ -74,6 +74,8 @@ __export(catalog_entry_exports, {
74
74
  resolveTranscriptionOptions: () => resolveTranscriptionOptions,
75
75
  resolveVideoOptions: () => resolveVideoOptions,
76
76
  resolveVoiceChangeOptions: () => resolveVoiceChangeOptions,
77
+ resolveVoiceCloneOptions: () => resolveVoiceCloneOptions,
78
+ resolveVoiceDesignOptions: () => resolveVoiceDesignOptions,
77
79
  sortCatalogRows: () => sortCatalogRows,
78
80
  takesTargetAspectRatio: () => takesTargetAspectRatio,
79
81
  videoLengthViolation: () => videoLengthViolation,
@@ -1803,6 +1805,369 @@ var ELEVENLABS_VOICES = [
1803
1805
  previewUrl: "https://storage.googleapis.com/eleven-public-prod/premade/voices/pqHfZKP75CvOlQylNhV4/d782b3ff-84ba-4029-848c-acf01285524d.mp3"
1804
1806
  }
1805
1807
  ];
1808
+ var PREMADE_ID = /\/premade\/voices\/([A-Za-z0-9]+)\//;
1809
+ var ELEVENLABS_DIRECT_VOICES = ELEVENLABS_VOICES.flatMap((v) => {
1810
+ const id = v.previewUrl ? PREMADE_ID.exec(v.previewUrl)?.[1] : void 0;
1811
+ return id ? [{ ...v, id }] : [];
1812
+ });
1813
+ var ELEVENLABS_DIRECT_DEFAULT_VOICE = "EXAVITQu4vr4xnSDxMaL";
1814
+
1815
+ // src/catalog/speech-elevenlabs-direct.ts
1816
+ var ELEVENLABS_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
1817
+ var PER_CALL = { unit: "images" };
1818
+ var PER_REQUEST = { unit: "requests" };
1819
+ var ELEVENLABS_DIRECT_MODELS = [
1820
+ {
1821
+ id: "elevenlabs-v4-direct",
1822
+ aliases: [],
1823
+ label: "ElevenLabs v4 (direct)",
1824
+ type: "speech",
1825
+ adapter: "elevenlabs",
1826
+ providerModelId: "eleven_v4",
1827
+ lifecycle: "active",
1828
+ exposure: [],
1829
+ constraints: {
1830
+ // Premade voices under their ElevenLabs ids (the direct API takes ids, not names).
1831
+ voices: ELEVENLABS_DIRECT_VOICES,
1832
+ languages: ELEVENLABS_V4_LANGUAGES,
1833
+ outputFormats: ELEVENLABS_MP3_FORMATS,
1834
+ supports: [],
1835
+ // ElevenLabs docs: 10,000 characters per request.
1836
+ prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
1837
+ },
1838
+ defaults: { voice: ELEVENLABS_DIRECT_DEFAULT_VOICE, outputFormat: "mp3_44100_128" },
1839
+ usage: PER_CALL,
1840
+ pricingKind: "flat",
1841
+ display: {
1842
+ provider: "elevenlabs",
1843
+ logo: ELEVENLABS_LOGO,
1844
+ description: "ElevenLabs v4 text to speech with audio tags, in a premade voice or your own cloned voice; up to 10,000 characters."
1845
+ },
1846
+ capabilities: { customVoices: true }
1847
+ },
1848
+ {
1849
+ id: "elevenlabs-ivc",
1850
+ aliases: [],
1851
+ label: "ElevenLabs Instant Voice Clone",
1852
+ type: "voice-clone",
1853
+ adapter: "elevenlabs",
1854
+ providerModelId: "ivc",
1855
+ lifecycle: "active",
1856
+ exposure: [],
1857
+ constraints: {
1858
+ // POST /v1/voices/add; ElevenLabs takes up to 25 samples and advises 1-2 minutes of
1859
+ // clean speech in total. Each clone uses a voice slot on our plan until deleted.
1860
+ references: { audios: 25, audioFormats: ["mp3", "wav", "m4a", "webm", "ogg"] },
1861
+ supports: []
1862
+ },
1863
+ defaults: {},
1864
+ usage: PER_REQUEST,
1865
+ pricingKind: "flat",
1866
+ display: {
1867
+ provider: "elevenlabs",
1868
+ logo: ELEVENLABS_LOGO,
1869
+ description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on ElevenLabs v4 (direct)."
1870
+ },
1871
+ capabilities: {}
1872
+ }
1873
+ ];
1874
+
1875
+ // src/catalog/voices-fish.ts
1876
+ var FISH_VOICES = [
1877
+ {
1878
+ id: "933563129e564b19a115bedd57b7406a",
1879
+ name: "Sarah",
1880
+ gender: "female",
1881
+ languages: ["en"],
1882
+ description: "An engaged speaker.",
1883
+ previewUrl: "https://platform.r2.fish.audio/task/4e204945670c460c86fdab57512cb308.mp3"
1884
+ },
1885
+ {
1886
+ id: "bf322df2096a46f18c579d0baa36f41d",
1887
+ name: "Adrian",
1888
+ gender: "male",
1889
+ languages: ["en"],
1890
+ description: "A steady and reliable narrator.",
1891
+ previewUrl: "https://platform.r2.fish.audio/task/d730b09f4a484316a77ecc86137e85fb.mp3"
1892
+ },
1893
+ {
1894
+ id: "536d3a5e000945adb7038665781a4aca",
1895
+ name: "Ethan",
1896
+ gender: "male",
1897
+ languages: ["en"],
1898
+ description: "A curious explainer.",
1899
+ previewUrl: "https://platform.r2.fish.audio/task/bdd677ed767744fc91f166468786264b.mp3"
1900
+ },
1901
+ {
1902
+ id: "b347db033a6549378b48d00acb0d06cd",
1903
+ name: "Selene",
1904
+ gender: "female",
1905
+ languages: ["en"],
1906
+ description: "A meditative female voice.",
1907
+ previewUrl: "https://platform.r2.fish.audio/task/fcf629b4a0b343558f461338ba1b9da4.mp3"
1908
+ },
1909
+ {
1910
+ id: "9a9cf47702da476aa4629e2506d4a857",
1911
+ name: "Hannah",
1912
+ gender: "female",
1913
+ languages: ["en"],
1914
+ description: "A conversation specialist.",
1915
+ previewUrl: "https://platform.r2.fish.audio/task/08d658835feb427db808e37060b78eb0.mp3"
1916
+ },
1917
+ {
1918
+ id: "79d0bd3e4e5444b18f7b6d89b5927bf1",
1919
+ name: "Jordan",
1920
+ gender: "male",
1921
+ languages: ["en"],
1922
+ description: "A motivational speaker.",
1923
+ previewUrl: "https://platform.r2.fish.audio/task/edecbc0bdd4c46c39eda8520a0f729ef.mp3"
1924
+ },
1925
+ {
1926
+ id: "e3cd384158934cc9a01029cd7d278634",
1927
+ name: "Laura",
1928
+ gender: "female",
1929
+ languages: ["en"],
1930
+ description: "A confident female narrator.",
1931
+ previewUrl: "https://platform.r2.fish.audio/task/e821bef995344d519caa1c93d808db23.mp3"
1932
+ },
1933
+ {
1934
+ id: "f3a2b90078d54a65af4b95f63bc7798e",
1935
+ name: "Sheila",
1936
+ gender: "female",
1937
+ accent: "american",
1938
+ languages: ["en"],
1939
+ description: "A young American English woman's voice.",
1940
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/9bb9e1a1cf3f47f38700da1c5c0a678a.mp3"
1941
+ },
1942
+ {
1943
+ id: "0db6e93af420470b9d9b9a886dbaa954",
1944
+ name: "Kai",
1945
+ gender: "male",
1946
+ languages: ["en"],
1947
+ description: "A friendly English man's voice for companion apps and everyday conversation.",
1948
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/13306f1172544c3ba3106d1e363feaa6.mp3"
1949
+ },
1950
+ {
1951
+ id: "8abfa9ebd4c24a3399b68c3cdac8c7b5",
1952
+ name: "Freya",
1953
+ gender: "female",
1954
+ accent: "british",
1955
+ languages: ["en"],
1956
+ 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.",
1957
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/07918705a56b42daaab149f65392cea7.mp3"
1958
+ },
1959
+ {
1960
+ id: "2f64e528a4f2406691925ab1e0ed5b7f",
1961
+ name: "Ananya",
1962
+ gender: "female",
1963
+ languages: ["hi"],
1964
+ 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",
1965
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a17a700668d8405f8316db120733145c.mp3"
1966
+ },
1967
+ {
1968
+ id: "cefd91a07b99468f9148e2580990082d",
1969
+ name: "Arjun",
1970
+ gender: "male",
1971
+ languages: ["hi"],
1972
+ 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",
1973
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/a0c4a8eadb7841cdae8e5e68031a2dfb.mp3"
1974
+ },
1975
+ {
1976
+ id: "3f065d660212478a9c697fa965b4d098",
1977
+ name: "Carmen",
1978
+ gender: "female",
1979
+ languages: ["es"],
1980
+ description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
1981
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/3421be48b6cb4c28bf829c25188e7e74.mp3"
1982
+ },
1983
+ {
1984
+ id: "ec0718028a2445fb9be8e8bfb56a0150",
1985
+ name: "Rodrigo",
1986
+ gender: "male",
1987
+ languages: ["es"],
1988
+ description: "Una voz clara y segura para asistentes de voz, atenci\xF3n al cliente y presentaciones.",
1989
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/5a77c32365604800ad5b4eb13c5585df.mp3"
1990
+ },
1991
+ {
1992
+ id: "10a3a20742114a4ea6dd441e7591850f",
1993
+ name: "Manon",
1994
+ gender: "female",
1995
+ languages: ["fr"],
1996
+ description: "Une voix claire et vive, adapt\xE9e aux assistants vocaux et au support client.",
1997
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/f7b75ee1fa524e48a8cefb003cf72952.mp3"
1998
+ },
1999
+ {
2000
+ id: "f69bca092b674168a8d02d61ca20943c",
2001
+ name: "Lucas",
2002
+ gender: "male",
2003
+ languages: ["fr"],
2004
+ description: "Une voix claire et assur\xE9e, adapt\xE9e aux assistants vocaux et au service client.",
2005
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/86fca3e56a014a3d8a11450854bb8311.mp3"
2006
+ },
2007
+ {
2008
+ id: "3937b44b604249f18b94913daef94043",
2009
+ name: "Lena",
2010
+ gender: "female",
2011
+ languages: ["de"],
2012
+ description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
2013
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/8659fec80b74442dab68a7755716a401.mp3"
2014
+ },
2015
+ {
2016
+ id: "c1e7cc926aa5469aa966c353094d4b0b",
2017
+ name: "Lukas",
2018
+ gender: "male",
2019
+ languages: ["de"],
2020
+ description: "Eine klare, souver\xE4ne Stimme f\xFCr Sprachassistenten, Kundenservice und Pr\xE4sentationen.",
2021
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/fb1d538126bb4fe38f8366250f6a2136.mp3"
2022
+ },
2023
+ {
2024
+ id: "b3c060cc41544a7da94a2c487647c902",
2025
+ name: "Let\xEDcia",
2026
+ gender: "female",
2027
+ accent: "brazilian",
2028
+ languages: ["pt"],
2029
+ 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.",
2030
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/13c6c0d96b5c4069951f89868ee15360.mp3"
2031
+ },
2032
+ {
2033
+ id: "add7bcb8d3c8423bbfa152ca232aad6e",
2034
+ name: "Ricardo",
2035
+ gender: "male",
2036
+ accent: "brazilian",
2037
+ languages: ["pt"],
2038
+ 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.",
2039
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-10-12/67475fc717ae45cbb38e647a2dd37fe1.mp3"
2040
+ },
2041
+ {
2042
+ id: "b2d9d8db057042688a5e318b8f405bc2",
2043
+ name: "Kyoko",
2044
+ gender: "female",
2045
+ languages: ["ja"],
2046
+ 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",
2047
+ previewUrl: "https://platform.r2.fish.audio/task/993b3ccb52124341aff79580f1ce5a5a.mp3"
2048
+ },
2049
+ {
2050
+ id: "297a6fd278df47c3b9da9bfdf55ac89a",
2051
+ name: "Satoru",
2052
+ gender: "male",
2053
+ languages: ["ja"],
2054
+ 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",
2055
+ previewUrl: "https://platform.r2.fish.audio/task/1b4712fb831f4c149eef6b0133157418.mp3"
2056
+ },
2057
+ {
2058
+ id: "0b14f34a13a94ed88fe6113193749bb0",
2059
+ name: "Fatima",
2060
+ gender: "female",
2061
+ languages: ["ar"],
2062
+ 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.",
2063
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-24/93406e1b905a4c4994306e3421243390.mp3"
2064
+ },
2065
+ {
2066
+ id: "74c6aba5cbf94a15bbdc547ffce5cb38",
2067
+ name: "Yutong",
2068
+ gender: "female",
2069
+ languages: ["zh"],
2070
+ 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",
2071
+ previewUrl: "https://platform.r2.fish.audio/tasks/2026-08-25/a48320f37fe44c4699549a1afc3778e8.mp3"
2072
+ }
2073
+ ];
2074
+
2075
+ // src/catalog/speech-fish.ts
2076
+ var FISH_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/fishaudio.png";
2077
+ var PER_CALL2 = { unit: "images" };
2078
+ var PER_REQUEST2 = { unit: "requests" };
2079
+ var TTS_MAX_CHARS = 1e4;
2080
+ function fishTts(id, label, providerModelId, description) {
2081
+ return {
2082
+ id,
2083
+ aliases: [],
2084
+ label,
2085
+ type: "speech",
2086
+ adapter: "fish",
2087
+ providerModelId,
2088
+ lifecycle: "active",
2089
+ exposure: [],
2090
+ constraints: {
2091
+ voices: FISH_VOICES,
2092
+ outputFormats: ["mp3", "wav"],
2093
+ supports: [],
2094
+ prompt: { referenceSyntax: "numbered", maxChars: TTS_MAX_CHARS }
2095
+ },
2096
+ defaults: { voice: "9a9cf47702da476aa4629e2506d4a857", outputFormat: "mp3" },
2097
+ usage: PER_CALL2,
2098
+ pricingKind: "flat",
2099
+ display: { provider: "fishaudio", logo: FISH_LOGO, description },
2100
+ capabilities: { customVoices: true }
2101
+ };
2102
+ }
2103
+ var FISH_MODELS = [
2104
+ fishTts(
2105
+ "fish-s2.1-pro",
2106
+ "Fish Audio S2.1 Pro",
2107
+ "s2.1-pro",
2108
+ "Fish Audio S2.1 Pro text to speech in 83 languages, with [bracket] directions and your own cloned voices."
2109
+ ),
2110
+ fishTts(
2111
+ "fish-s2.1-pro-free",
2112
+ "Fish Audio S2.1 Pro Free",
2113
+ "s2.1-pro-free",
2114
+ "Fish Audio S2.1 Pro on the free developer tier: the same voices at no cost, without latency or data-processing guarantees."
2115
+ ),
2116
+ fishTts(
2117
+ "fish-drama-3",
2118
+ "Fish Audio Drama 3",
2119
+ "drama-3",
2120
+ "Fish Audio Drama 3 text to speech you direct like an actor: [directions], sounds, pauses and per-word tags."
2121
+ ),
2122
+ {
2123
+ id: "fish-voice-clone",
2124
+ aliases: [],
2125
+ label: "Fish Audio Voice Clone",
2126
+ type: "voice-clone",
2127
+ adapter: "fish",
2128
+ providerModelId: "voice-clone",
2129
+ lifecycle: "active",
2130
+ exposure: [],
2131
+ constraints: {
2132
+ // POST /model takes 1-20 voice files; Fish advises 10 s or more of clean speech each.
2133
+ references: { audios: 20, audioFormats: ["wav", "mp3", "m4a", "opus"] },
2134
+ supports: []
2135
+ },
2136
+ defaults: {},
2137
+ usage: PER_REQUEST2,
2138
+ pricingKind: "flat",
2139
+ display: {
2140
+ provider: "fishaudio",
2141
+ logo: FISH_LOGO,
2142
+ description: "Clone a private, reusable voice from one or more clean recordings, then speak with it on the Fish Audio speech models."
2143
+ },
2144
+ capabilities: {}
2145
+ },
2146
+ {
2147
+ id: "fish-voice-design-1",
2148
+ aliases: [],
2149
+ label: "Fish Audio Voice Design",
2150
+ type: "voice-design",
2151
+ adapter: "fish",
2152
+ providerModelId: "voice-design-1",
2153
+ lifecycle: "active",
2154
+ exposure: [],
2155
+ constraints: {
2156
+ voiceDesign: { maxCandidates: 4, previewMaxChars: 150 },
2157
+ supports: [],
2158
+ prompt: { referenceSyntax: "numbered", maxChars: 2e3 }
2159
+ },
2160
+ defaults: {},
2161
+ usage: PER_REQUEST2,
2162
+ pricingKind: "flat",
2163
+ display: {
2164
+ provider: "fishaudio",
2165
+ logo: FISH_LOGO,
2166
+ description: "Describe a voice and get up to four short candidates; keep one as a cloned voice."
2167
+ },
2168
+ capabilities: {}
2169
+ }
2170
+ ];
1806
2171
 
1807
2172
  // src/catalog/voices-gemini.ts
1808
2173
  var GEMINI_TTS_VOICES = [
@@ -3379,11 +3744,11 @@ var MINIMAX_VOICES = [
3379
3744
  ];
3380
3745
 
3381
3746
  // src/catalog/media-nodes.ts
3382
- var ELEVENLABS_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3747
+ var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3383
3748
  var GOOGLE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
3384
3749
  var MINIMAX_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3385
3750
  var SYNC_LOGO = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/sync.png";
3386
- var PER_CALL = { unit: "images" };
3751
+ var PER_CALL3 = { unit: "images" };
3387
3752
  var EXPOSURE4 = ["cowork", "space_builder"];
3388
3753
  var LIPSYNC_AUDIO = { audioSeconds: { max: 30 } };
3389
3754
  function elevenLabsTts(tts) {
@@ -3404,9 +3769,9 @@ function elevenLabsTts(tts) {
3404
3769
  prompt: { referenceSyntax: "numbered", maxChars: tts.maxChars }
3405
3770
  },
3406
3771
  defaults: { voice: "Rachel", ...tts.formats && { outputFormat: "mp3_44100_128" } },
3407
- usage: PER_CALL,
3772
+ usage: PER_CALL3,
3408
3773
  pricingKind: "flat",
3409
- display: { provider: "elevenlabs", logo: ELEVENLABS_LOGO, description: tts.description },
3774
+ display: { provider: "elevenlabs", logo: ELEVENLABS_LOGO2, description: tts.description },
3410
3775
  capabilities: {}
3411
3776
  };
3412
3777
  }
@@ -3428,7 +3793,7 @@ function geminiTts(id, label, description) {
3428
3793
  prompt: { referenceSyntax: "numbered", maxChars: 8192 }
3429
3794
  },
3430
3795
  defaults: { voice: "Kore" },
3431
- usage: PER_CALL,
3796
+ usage: PER_CALL3,
3432
3797
  pricingKind: "flat",
3433
3798
  display: { provider: "google", logo: GOOGLE_LOGO, description },
3434
3799
  capabilities: {}
@@ -3451,7 +3816,7 @@ function syncLipsync(id, label, providerModelId, syncModel, description) {
3451
3816
  },
3452
3817
  defaults: {},
3453
3818
  ...syncModel !== void 0 && { providerOptions: { fal: { model: syncModel } } },
3454
- usage: PER_CALL,
3819
+ usage: PER_CALL3,
3455
3820
  pricingKind: "flat",
3456
3821
  display: { provider: "sync", logo: SYNC_LOGO, description },
3457
3822
  capabilities: { acceptsSourceVideo: true }
@@ -3512,7 +3877,7 @@ var MEDIA_NODE_MODELS = [
3512
3877
  prompt: { referenceSyntax: "numbered", maxChars: 1e4 }
3513
3878
  },
3514
3879
  defaults: { voice: "Wise_Woman" },
3515
- usage: PER_CALL,
3880
+ usage: PER_CALL3,
3516
3881
  pricingKind: "flat",
3517
3882
  display: {
3518
3883
  provider: "minimax",
@@ -3539,11 +3904,11 @@ var MEDIA_NODE_MODELS = [
3539
3904
  supports: []
3540
3905
  },
3541
3906
  defaults: { voice: "Rachel", outputFormat: "mp3_44100_128" },
3542
- usage: PER_CALL,
3907
+ usage: PER_CALL3,
3543
3908
  pricingKind: "flat",
3544
3909
  display: {
3545
3910
  provider: "elevenlabs",
3546
- logo: ELEVENLABS_LOGO,
3911
+ logo: ELEVENLABS_LOGO2,
3547
3912
  description: "ElevenLabs speech to speech; re-voices a recording in another voice and keeps its timing and delivery."
3548
3913
  },
3549
3914
  capabilities: {}
@@ -3566,11 +3931,11 @@ var MEDIA_NODE_MODELS = [
3566
3931
  supports: []
3567
3932
  },
3568
3933
  defaults: {},
3569
- usage: PER_CALL,
3934
+ usage: PER_CALL3,
3570
3935
  pricingKind: "flat",
3571
3936
  display: {
3572
3937
  provider: "elevenlabs",
3573
- logo: ELEVENLABS_LOGO,
3938
+ logo: ELEVENLABS_LOGO2,
3574
3939
  description: "ElevenLabs dubbing; translates the speech in a video or audio file into another language in the original voices."
3575
3940
  },
3576
3941
  capabilities: { acceptsSourceVideo: true }
@@ -3613,7 +3978,7 @@ var MEDIA_NODE_MODELS = [
3613
3978
  supports: []
3614
3979
  },
3615
3980
  defaults: {},
3616
- usage: PER_CALL,
3981
+ usage: PER_CALL3,
3617
3982
  pricingKind: "flat",
3618
3983
  display: {
3619
3984
  provider: "veed",
@@ -3665,16 +4030,18 @@ var MEDIA_NODE_MODELS = [
3665
4030
  supports: []
3666
4031
  },
3667
4032
  defaults: {},
3668
- usage: PER_CALL,
4033
+ usage: PER_CALL3,
3669
4034
  pricingKind: "flat",
3670
4035
  display: {
3671
4036
  provider: "elevenlabs",
3672
- logo: ELEVENLABS_LOGO,
4037
+ logo: ELEVENLABS_LOGO2,
3673
4038
  description: "ElevenLabs Scribe v2 speech to text with word timings, speaker labels and audio-event tags."
3674
4039
  },
3675
4040
  capabilities: {}
3676
4041
  },
3677
- SAM_3_IMAGE
4042
+ SAM_3_IMAGE,
4043
+ ...FISH_MODELS,
4044
+ ...ELEVENLABS_DIRECT_MODELS
3678
4045
  ];
3679
4046
 
3680
4047
  // src/catalog/model3d.ts
@@ -3870,7 +4237,7 @@ var MODEL3D_MODELS = [
3870
4237
 
3871
4238
  // src/catalog/other.ts
3872
4239
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
3873
- var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
4240
+ var ELEVENLABS_LOGO3 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
3874
4241
  var MINIMAX_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png";
3875
4242
  var OTHER_MODELS = [
3876
4243
  {
@@ -4035,7 +4402,7 @@ var OTHER_MODELS = [
4035
4402
  pricingKind: "flat",
4036
4403
  display: {
4037
4404
  provider: "elevenlabs",
4038
- logo: ELEVENLABS_LOGO2,
4405
+ logo: ELEVENLABS_LOGO3,
4039
4406
  description: "ElevenLabs Music v2.5 text-to-music; full songs or instrumentals from 3 seconds to 1 minute."
4040
4407
  },
4041
4408
  capabilities: {}
@@ -4087,7 +4454,7 @@ var OTHER_MODELS = [
4087
4454
  pricingKind: "flat",
4088
4455
  display: {
4089
4456
  provider: "elevenlabs",
4090
- logo: ELEVENLABS_LOGO2,
4457
+ logo: ELEVENLABS_LOGO3,
4091
4458
  description: "ElevenLabs Sound Effects v2; a sound effect from a text description, up to 22 seconds."
4092
4459
  },
4093
4460
  capabilities: {}
@@ -7315,7 +7682,9 @@ var ADAPTER_IDS = [
7315
7682
  "prodia",
7316
7683
  "higgsfield",
7317
7684
  "xai-video",
7318
- "luma"
7685
+ "luma",
7686
+ "fish",
7687
+ "elevenlabs"
7319
7688
  ];
7320
7689
  var ModelTypeSchema = import_zod3.z.enum([
7321
7690
  "text",
@@ -7332,6 +7701,8 @@ var ModelTypeSchema = import_zod3.z.enum([
7332
7701
  "dubbing",
7333
7702
  "lipsync",
7334
7703
  "transcription",
7704
+ "voice-clone",
7705
+ "voice-design",
7335
7706
  "model3d"
7336
7707
  ]);
7337
7708
  var LifecycleSchema = import_zod3.z.enum(["active", "deprecated", "retired"]);
@@ -7507,6 +7878,10 @@ var ModelConstraintsSchema = import_zod3.z.looseObject({
7507
7878
  supports: import_zod3.z.array(SupportedOptionSchema).optional(),
7508
7879
  ratioIgnoredWith: import_zod3.z.array(import_zod3.z.enum(["firstFrame", "sourceVideo"])).optional(),
7509
7880
  voices: import_zod3.z.array(VoiceOptionSchema).optional(),
7881
+ voiceDesign: import_zod3.z.object({
7882
+ maxCandidates: import_zod3.z.number().int().positive(),
7883
+ previewMaxChars: import_zod3.z.number().int().positive()
7884
+ }).optional(),
7510
7885
  languages: import_zod3.z.array(LanguageOptionSchema).optional(),
7511
7886
  outputFormats: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).optional(),
7512
7887
  sourceVideoSeconds: VideoSecondsLimitsSchema.omit({ total: true }).extend({ inReferenceTotal: import_zod3.z.boolean().optional() }).optional(),
@@ -7605,6 +7980,7 @@ var CatalogDisplaySchema = import_zod3.z.looseObject({
7605
7980
  });
7606
7981
  var CatalogCapabilitiesSchema = import_zod3.z.looseObject({
7607
7982
  acceptsSourceVideo: import_zod3.z.boolean().optional(),
7983
+ customVoices: import_zod3.z.boolean().optional(),
7608
7984
  videoEdit: import_zod3.z.boolean().optional(),
7609
7985
  framesExcludeReferences: import_zod3.z.boolean().optional(),
7610
7986
  sourceVideoExcludesFrames: import_zod3.z.boolean().optional(),
@@ -9483,8 +9859,17 @@ function resolveSpeechOptions(model, request, onUnsupported = "error") {
9483
9859
  const warnings = [];
9484
9860
  checkScript(model, request.text);
9485
9861
  const resolved = { model: model.id, text: request.text };
9486
- const voice = resolveVoice(model, request.voice, onUnsupported, warnings);
9487
- if (voice !== void 0) resolved.voice = voice;
9862
+ const customVoice = request.customVoice?.trim();
9863
+ if (customVoice) {
9864
+ if (model.capabilities?.customVoices !== true) {
9865
+ throw unsupported3(model, "does not take a custom voice");
9866
+ }
9867
+ if (request.voice?.trim()) throw unsupported3(model, "takes voice or customVoice, not both");
9868
+ resolved.customVoice = customVoice;
9869
+ } else {
9870
+ const voice = resolveVoice(model, request.voice, onUnsupported, warnings);
9871
+ if (voice !== void 0) resolved.voice = voice;
9872
+ }
9488
9873
  const language = resolveListed(
9489
9874
  model,
9490
9875
  {
@@ -9606,6 +9991,106 @@ function resolveTranscriptionOptions(model, request, onUnsupported = "error") {
9606
9991
  return { request: resolved, warnings };
9607
9992
  }
9608
9993
 
9994
+ // src/resolve-voice.ts
9995
+ function refuse(model, code, message) {
9996
+ return new ModelKitError({ code, message: `${model.label} ${message}`, model: model.id });
9997
+ }
9998
+ function checkSample(model, sample, index) {
9999
+ const limits = model.constraints.references?.audioSeconds;
10000
+ const length = sample.audio.durationSeconds;
10001
+ if (limits === void 0 || length === void 0) return;
10002
+ if (limits.min !== void 0 && length < limits.min || limits.max !== void 0 && length > limits.max) {
10003
+ const range2 = `${limits.min ?? 0}-${limits.max ?? "any"} s`;
10004
+ throw refuse(
10005
+ model,
10006
+ "unsupported_option",
10007
+ `takes samples of ${range2}; sample ${index + 1} is ${length} s`
10008
+ );
10009
+ }
10010
+ }
10011
+ function resolveVoiceCloneOptions(model, request) {
10012
+ const title = request.title.trim();
10013
+ if (!title) throw refuse(model, "missing_input", "needs a title");
10014
+ if (request.samples.length === 0) throw refuse(model, "missing_input", "needs a voice sample");
10015
+ const max = model.constraints.references?.audios;
10016
+ if (max !== void 0 && request.samples.length > max) {
10017
+ throw refuse(
10018
+ model,
10019
+ "unsupported_option",
10020
+ `takes up to ${max} samples; got ${request.samples.length}`
10021
+ );
10022
+ }
10023
+ request.samples.forEach((sample, index) => checkSample(model, sample, index));
10024
+ const total = model.constraints.references?.audioSeconds?.total;
10025
+ const seconds3 = request.samples.reduce((sum, s) => sum + (s.audio.durationSeconds ?? 0), 0);
10026
+ if (total !== void 0 && seconds3 > total) {
10027
+ throw refuse(
10028
+ model,
10029
+ "unsupported_option",
10030
+ `takes up to ${total} s of samples; got ${seconds3} s`
10031
+ );
10032
+ }
10033
+ for (const field of ["transcript", "designSignature"]) {
10034
+ const set = request.samples.filter((s) => s[field]?.trim()).length;
10035
+ if (set !== 0 && set !== request.samples.length) {
10036
+ throw refuse(model, "missing_input", `needs ${field} on every sample or on none`);
10037
+ }
10038
+ }
10039
+ return { request: { ...request, model: model.id, title }, warnings: [] };
10040
+ }
10041
+ function resolveVoiceDesignOptions(model, request, onUnsupported = "error") {
10042
+ const warnings = [];
10043
+ const prompt = request.prompt.trim();
10044
+ if (!prompt) throw refuse(model, "missing_input", "needs a voice description");
10045
+ const maxChars = promptConventions(model).maxChars;
10046
+ if (maxChars !== void 0 && prompt.length > maxChars) {
10047
+ throw refuse(
10048
+ model,
10049
+ "unsupported_option",
10050
+ `takes up to ${maxChars} characters; got ${prompt.length}`
10051
+ );
10052
+ }
10053
+ const limits = model.constraints.voiceDesign;
10054
+ const resolved = { model: model.id, prompt };
10055
+ const preview2 = request.previewText?.trim();
10056
+ if (preview2) {
10057
+ if (limits !== void 0 && preview2.length > limits.previewMaxChars) {
10058
+ throw refuse(
10059
+ model,
10060
+ "unsupported_option",
10061
+ `takes a preview line of up to ${limits.previewMaxChars} characters; got ${preview2.length}`
10062
+ );
10063
+ }
10064
+ resolved.previewText = preview2;
10065
+ }
10066
+ const language = request.language?.trim();
10067
+ if (language) resolved.language = language;
10068
+ if (request.seed !== void 0) resolved.seed = request.seed;
10069
+ const count2 = request.count ?? model.defaults.count;
10070
+ if (count2 !== void 0) {
10071
+ const cap = limits?.maxCandidates ?? count2;
10072
+ if (!Number.isInteger(count2) || count2 < 1 || count2 > cap) {
10073
+ if (onUnsupported === "error" || !Number.isInteger(count2)) {
10074
+ throw refuse(
10075
+ model,
10076
+ "unsupported_option",
10077
+ `returns 1-${cap} candidates; got ${count2}`
10078
+ );
10079
+ }
10080
+ const to = Math.min(Math.max(count2, 1), cap);
10081
+ warnings.push({
10082
+ code: "option_ignored",
10083
+ field: "count",
10084
+ from: String(count2),
10085
+ to: String(to),
10086
+ message: `count ${count2} replaced by ${to} for ${model.id}`
10087
+ });
10088
+ resolved.count = to;
10089
+ } else resolved.count = count2;
10090
+ }
10091
+ return { request: resolved, warnings };
10092
+ }
10093
+
9609
10094
  // src/resolve-call.ts
9610
10095
  var MEDIA_METHODS = {
9611
10096
  text: [],
@@ -9621,6 +10106,9 @@ var MEDIA_METHODS = {
9621
10106
  lipsync: ["lipsync"],
9622
10107
  dubbing: ["dub"],
9623
10108
  transcription: ["transcribe"],
10109
+ // deleteVoice runs on the voice-clone row too; it checks with assertMethod("createVoice").
10110
+ "voice-clone": ["createVoice"],
10111
+ "voice-design": ["designVoice"],
9624
10112
  "video-tool": ["submitVideoTool"],
9625
10113
  // 3D jobs run through submitModel3d, which checks the row type itself; their preflight
9626
10114
  // is model3dInputRefusal, not resolveOptions.
@@ -9635,6 +10123,8 @@ var METHOD_NOUN = {
9635
10123
  lipsync: "a lipsync model",
9636
10124
  dub: "a dubbing model",
9637
10125
  transcribe: "a transcription model",
10126
+ createVoice: "a voice cloning model",
10127
+ designVoice: "a voice design model",
9638
10128
  submitVideoTool: "a video tool"
9639
10129
  };
9640
10130
  function assertMethod(model, method) {
@@ -9677,6 +10167,14 @@ function resolveTranscriptionCall(model, request, onUnsupported = "error") {
9677
10167
  assertMethod(model, "transcribe");
9678
10168
  return resolveTranscriptionOptions(model, request, onUnsupported);
9679
10169
  }
10170
+ function resolveVoiceCloneCall(model, request) {
10171
+ assertMethod(model, "createVoice");
10172
+ return resolveVoiceCloneOptions(model, request);
10173
+ }
10174
+ function resolveVoiceDesignCall(model, request, onUnsupported = "error") {
10175
+ assertMethod(model, "designVoice");
10176
+ return resolveVoiceDesignOptions(model, request, onUnsupported);
10177
+ }
9680
10178
  function resolveVideoToolCall(model, request) {
9681
10179
  assertMethod(model, "submitVideoTool");
9682
10180
  const plan = planVideoTool(model, request);
@@ -9723,6 +10221,13 @@ function resolveOptions(model, call, onUnsupported = "error") {
9723
10221
  method: call.method,
9724
10222
  ...resolveTranscriptionCall(model, call.request, onUnsupported)
9725
10223
  };
10224
+ case "createVoice":
10225
+ return { method: call.method, ...resolveVoiceCloneCall(model, call.request) };
10226
+ case "designVoice":
10227
+ return {
10228
+ method: call.method,
10229
+ ...resolveVoiceDesignCall(model, call.request, onUnsupported)
10230
+ };
9726
10231
  case "submitVideoTool":
9727
10232
  return { method: call.method, ...resolveVideoToolCall(model, call.request) };
9728
10233
  }