egaki 0.5.0 → 0.7.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/CHANGELOG.md +188 -0
- package/dist/cli/cache-utils.d.ts +33 -0
- package/dist/cli/cache-utils.d.ts.map +1 -0
- package/dist/cli/cache-utils.js +157 -0
- package/dist/cli/cached-generate.d.ts +105 -0
- package/dist/cli/cached-generate.d.ts.map +1 -0
- package/dist/cli/cached-generate.js +208 -0
- package/dist/cli/cached-generate.test.d.ts +11 -0
- package/dist/cli/cached-generate.test.d.ts.map +1 -0
- package/dist/cli/cached-generate.test.js +431 -0
- package/dist/cli/cartesia-provider.d.ts +16 -0
- package/dist/cli/cartesia-provider.d.ts.map +1 -0
- package/dist/cli/cartesia-provider.js +184 -0
- package/dist/cli/chatgpt-auth.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +1329 -0
- package/dist/cli/clone-generate.d.ts +26 -0
- package/dist/cli/clone-generate.d.ts.map +1 -0
- package/dist/cli/clone-generate.js +148 -0
- package/dist/{credentials.d.ts → cli/credentials.d.ts} +11 -2
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +88 -5
- package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
- package/dist/cli/elevenlabs-speech-provider.js +199 -0
- package/dist/cli/generate.d.ts +140 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +604 -0
- package/dist/cli/generate.test.d.ts +2 -0
- package/dist/cli/generate.test.d.ts.map +1 -0
- package/dist/cli/generate.test.js +131 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/{login.js → cli/login.js} +45 -4
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/cli/model-catalog.d.ts +141 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/cli/model-catalog.js +2135 -0
- package/dist/{models.d.ts → cli/models.d.ts} +16 -9
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/cli/models.js +313 -0
- package/dist/cli/open-browser.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.d.ts +3 -0
- package/dist/cli/openai-speech-provider.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.js +28 -0
- package/dist/cli/separation-generate.d.ts +34 -0
- package/dist/cli/separation-generate.d.ts.map +1 -0
- package/dist/cli/separation-generate.js +117 -0
- package/dist/cli/speech-catalog.d.ts +35 -0
- package/dist/cli/speech-catalog.d.ts.map +1 -0
- package/dist/cli/speech-catalog.js +195 -0
- package/dist/cli/speech-generate.d.ts +86 -0
- package/dist/cli/speech-generate.d.ts.map +1 -0
- package/dist/cli/speech-generate.js +156 -0
- package/dist/cli/speech-models.d.ts +8 -0
- package/dist/cli/speech-models.d.ts.map +1 -0
- package/dist/cli/speech-models.js +109 -0
- package/dist/cli/speech-provider.test.d.ts +2 -0
- package/dist/cli/speech-provider.test.d.ts.map +1 -0
- package/dist/cli/speech-provider.test.js +197 -0
- package/dist/cli/subscription.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.d.ts +35 -0
- package/dist/cli/transcription-catalog.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.js +192 -0
- package/dist/cli/transcription-generate.d.ts +62 -0
- package/dist/cli/transcription-generate.d.ts.map +1 -0
- package/dist/cli/transcription-generate.js +144 -0
- package/dist/cli/transcription-models.d.ts +9 -0
- package/dist/cli/transcription-models.d.ts.map +1 -0
- package/dist/cli/transcription-models.js +100 -0
- package/dist/cli/transcription-providers.d.ts +22 -0
- package/dist/cli/transcription-providers.d.ts.map +1 -0
- package/dist/cli/transcription-providers.js +290 -0
- package/dist/cli/video-model-catalog.d.ts +2 -0
- package/dist/cli/video-model-catalog.d.ts.map +1 -0
- package/dist/cli/video-model-catalog.js +5 -0
- package/dist/cli/xai-auth.d.ts +29 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/cli/xai-auth.js +325 -0
- package/dist/vite/angled-screen.d.ts +57 -0
- package/dist/vite/angled-screen.d.ts.map +1 -0
- package/dist/vite/angled-screen.js +115 -0
- package/dist/vite/app.d.ts +51 -0
- package/dist/vite/app.d.ts.map +1 -0
- package/dist/vite/app.js +150 -0
- package/dist/vite/bands-shader.d.ts +47 -0
- package/dist/vite/bands-shader.d.ts.map +1 -0
- package/dist/vite/bands-shader.js +267 -0
- package/dist/vite/code-block.d.ts +107 -0
- package/dist/vite/code-block.d.ts.map +1 -0
- package/dist/vite/code-block.js +641 -0
- package/dist/vite/components.d.ts +36 -0
- package/dist/vite/components.d.ts.map +1 -0
- package/dist/vite/components.js +113 -0
- package/dist/vite/dispersion-rings-shader.d.ts +44 -0
- package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
- package/dist/vite/dispersion-rings-shader.js +282 -0
- package/dist/vite/easing-curves.d.ts +129 -0
- package/dist/vite/easing-curves.d.ts.map +1 -0
- package/dist/vite/easing-curves.js +584 -0
- package/dist/vite/easing-curves.test.d.ts +2 -0
- package/dist/vite/easing-curves.test.d.ts.map +1 -0
- package/dist/vite/easing-curves.test.js +200 -0
- package/dist/vite/generated-media-client.d.ts +35 -0
- package/dist/vite/generated-media-client.d.ts.map +1 -0
- package/dist/vite/generated-media-client.js +76 -0
- package/dist/vite/keyframes.d.ts +129 -0
- package/dist/vite/keyframes.d.ts.map +1 -0
- package/dist/vite/keyframes.js +194 -0
- package/dist/vite/layout-editor.d.ts +45 -0
- package/dist/vite/layout-editor.d.ts.map +1 -0
- package/dist/vite/layout-editor.js +587 -0
- package/dist/vite/layout-transition.d.ts +136 -0
- package/dist/vite/layout-transition.d.ts.map +1 -0
- package/dist/vite/layout-transition.js +581 -0
- package/dist/vite/liquid-gradient-shader.d.ts +62 -0
- package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
- package/dist/vite/liquid-gradient-shader.js +413 -0
- package/dist/vite/mdx-client.d.ts +15 -0
- package/dist/vite/mdx-client.d.ts.map +1 -0
- package/dist/vite/mdx-client.js +265 -0
- package/dist/vite/mdx-parse.d.ts +102 -0
- package/dist/vite/mdx-parse.d.ts.map +1 -0
- package/dist/vite/mdx-parse.js +251 -0
- package/dist/vite/mdx-provided-components.d.ts +18 -0
- package/dist/vite/mdx-provided-components.d.ts.map +1 -0
- package/dist/vite/mdx-provided-components.js +14 -0
- package/dist/vite/mdx-video.d.ts +327 -0
- package/dist/vite/mdx-video.d.ts.map +1 -0
- package/dist/vite/mdx-video.js +596 -0
- package/dist/vite/mdx-video.test.d.ts +9 -0
- package/dist/vite/mdx-video.test.d.ts.map +1 -0
- package/dist/vite/mdx-video.test.js +1945 -0
- package/dist/vite/media-components.d.ts +45 -0
- package/dist/vite/media-components.d.ts.map +1 -0
- package/dist/vite/media-components.js +355 -0
- package/dist/vite/media-duration-store.d.ts +66 -0
- package/dist/vite/media-duration-store.d.ts.map +1 -0
- package/dist/vite/media-duration-store.js +204 -0
- package/dist/vite/player-page.d.ts +35 -0
- package/dist/vite/player-page.d.ts.map +1 -0
- package/dist/vite/player-page.js +720 -0
- package/dist/vite/render-client.d.ts +18 -0
- package/dist/vite/render-client.d.ts.map +1 -0
- package/dist/vite/render-client.js +67 -0
- package/dist/vite/sdk.d.ts +221 -0
- package/dist/vite/sdk.d.ts.map +1 -0
- package/dist/vite/sdk.js +355 -0
- package/dist/vite/server-components.d.ts +55 -0
- package/dist/vite/server-components.d.ts.map +1 -0
- package/dist/vite/server-components.js +156 -0
- package/dist/vite/server-mdx.d.ts +74 -0
- package/dist/vite/server-mdx.d.ts.map +1 -0
- package/dist/vite/server-mdx.js +259 -0
- package/dist/vite/shader-renderer.d.ts +48 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -0
- package/dist/vite/shader-renderer.js +411 -0
- package/dist/vite/store-hooks.d.ts +12 -0
- package/dist/vite/store-hooks.d.ts.map +1 -0
- package/dist/vite/store-hooks.js +93 -0
- package/dist/vite/store.d.ts +75 -0
- package/dist/vite/store.d.ts.map +1 -0
- package/dist/vite/store.js +52 -0
- package/dist/vite/store.test.d.ts +6 -0
- package/dist/vite/store.test.d.ts.map +1 -0
- package/dist/vite/store.test.js +172 -0
- package/dist/vite/tweakpane-hook.d.ts +94 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -0
- package/dist/vite/tweakpane-hook.js +450 -0
- package/dist/vite/vite-plugin.d.ts +28 -0
- package/dist/vite/vite-plugin.d.ts.map +1 -0
- package/dist/vite/vite-plugin.js +538 -0
- package/dist/vite/wave-gradient-shader.d.ts +41 -0
- package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
- package/dist/vite/wave-gradient-shader.js +207 -0
- package/package.json +103 -25
- package/skills/egaki/SKILL.md +162 -1
- package/src/cli/cache-utils.ts +169 -0
- package/src/cli/cached-generate.test.ts +531 -0
- package/src/cli/cached-generate.ts +348 -0
- package/src/cli/cartesia-provider.ts +249 -0
- package/src/cli/cli.ts +1818 -0
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +90 -7
- package/src/cli/elevenlabs-speech-provider.ts +243 -0
- package/src/cli/generate.test.ts +166 -0
- package/src/cli/generate.ts +912 -0
- package/src/{login.ts → cli/login.ts} +48 -3
- package/src/cli/main.ts +8 -0
- package/src/cli/model-catalog.ts +2336 -0
- package/src/cli/models.ts +408 -0
- package/src/cli/openai-speech-provider.ts +34 -0
- package/src/cli/separation-generate.ts +187 -0
- package/src/cli/speech-catalog.ts +253 -0
- package/src/cli/speech-generate.ts +275 -0
- package/src/cli/speech-models.ts +146 -0
- package/src/cli/speech-provider.test.ts +226 -0
- package/src/cli/transcription-catalog.ts +250 -0
- package/src/cli/transcription-generate.ts +214 -0
- package/src/cli/transcription-models.ts +136 -0
- package/src/cli/transcription-providers.ts +427 -0
- package/src/cli/video-model-catalog.ts +14 -0
- package/src/cli/xai-auth.ts +396 -0
- package/src/vite/angled-screen.tsx +214 -0
- package/src/vite/app.tsx +197 -0
- package/src/vite/bands-shader.tsx +319 -0
- package/src/vite/code-block.tsx +939 -0
- package/src/vite/components.tsx +278 -0
- package/src/vite/dispersion-rings-shader.tsx +331 -0
- package/src/vite/easing-curves.test.ts +232 -0
- package/src/vite/easing-curves.ts +743 -0
- package/src/vite/generated-media-client.tsx +121 -0
- package/src/vite/keyframes.tsx +347 -0
- package/src/vite/layout-editor.tsx +709 -0
- package/src/vite/layout-transition.tsx +706 -0
- package/src/vite/liquid-gradient-shader.tsx +474 -0
- package/src/vite/mdx-client.tsx +420 -0
- package/src/vite/mdx-parse.ts +343 -0
- package/src/vite/mdx-provided-components.ts +19 -0
- package/src/vite/mdx-video.test.tsx +2219 -0
- package/src/vite/mdx-video.tsx +976 -0
- package/src/vite/media-components.tsx +473 -0
- package/src/vite/media-duration-store.ts +225 -0
- package/src/vite/motion-timing.ts +194 -0
- package/src/vite/player-page.tsx +1107 -0
- package/src/vite/render-client.ts +84 -0
- package/src/vite/sdk.ts +598 -0
- package/src/vite/server-components.tsx +241 -0
- package/src/vite/server-mdx.ts +285 -0
- package/src/vite/shader-renderer.tsx +535 -0
- package/src/vite/store-hooks.ts +117 -0
- package/src/vite/store.test.ts +202 -0
- package/src/vite/store.ts +115 -0
- package/src/vite/styles.css +21 -0
- package/src/vite/tweakpane-hook.tsx +593 -0
- package/src/vite/virtual-modules.d.ts +30 -0
- package/src/vite/vite-plugin.ts +584 -0
- package/src/vite/wave-gradient-shader.tsx +253 -0
- package/README.md +0 -266
- package/dist/antigravity-auth.d.ts +0 -15
- package/dist/antigravity-auth.d.ts.map +0 -1
- package/dist/antigravity-auth.js +0 -544
- package/dist/antigravity-image.d.ts +0 -11
- package/dist/antigravity-image.d.ts.map +0 -1
- package/dist/antigravity-image.js +0 -285
- package/dist/chatgpt-auth.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -968
- package/dist/credentials.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.d.ts +0 -11
- package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.js +0 -64
- package/dist/login.d.ts.map +0 -1
- package/dist/model-catalog.d.ts +0 -42
- package/dist/model-catalog.d.ts.map +0 -1
- package/dist/model-catalog.js +0 -1001
- package/dist/models.d.ts.map +0 -1
- package/dist/models.js +0 -246
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts +0 -52
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/video-model-catalog.js +0 -306
- package/src/cli.ts +0 -1378
- package/src/model-catalog.ts +0 -1077
- package/src/models.ts +0 -309
- package/src/video-model-catalog.ts +0 -363
- /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
- /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
- /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
- /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
- /package/dist/{subscription.js → cli/subscription.js} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Speech model catalog — single source of truth for all TTS models.
|
|
2
|
+
// Mirrors the image/video catalog pattern: every model's provider, pricing,
|
|
3
|
+
// features, and default voices live here. Other modules derive their data
|
|
4
|
+
// from this catalog.
|
|
5
|
+
//
|
|
6
|
+
// Pricing sources:
|
|
7
|
+
// OpenAI: https://platform.openai.com/docs/pricing (per 1M characters)
|
|
8
|
+
// ElevenLabs: https://elevenlabs.io/pricing (per 1K characters, varies by plan)
|
|
9
|
+
// Cartesia: https://cartesia.ai/pricing (plan-based, ~1 credit per character)
|
|
10
|
+
// ─── shared fragments ────────────────────────────────────────────────────────
|
|
11
|
+
const openaiSpeechBase = {
|
|
12
|
+
provider: 'openai',
|
|
13
|
+
strategy: 'speech',
|
|
14
|
+
};
|
|
15
|
+
const elevenlabsSpeechBase = {
|
|
16
|
+
provider: 'elevenlabs',
|
|
17
|
+
strategy: 'speech',
|
|
18
|
+
};
|
|
19
|
+
const cartesiaSpeechBase = {
|
|
20
|
+
provider: 'cartesia',
|
|
21
|
+
strategy: 'speech',
|
|
22
|
+
};
|
|
23
|
+
// ─── catalog ─────────────────────────────────────────────────────────────────
|
|
24
|
+
export const SPEECH_CATALOG = [
|
|
25
|
+
// ── OpenAI ─────────────────────────────────────────────────────────────
|
|
26
|
+
{
|
|
27
|
+
id: 'tts-1',
|
|
28
|
+
name: 'TTS-1 (Standard)',
|
|
29
|
+
description: 'OpenAI standard text-to-speech. Low latency, good for real-time use.',
|
|
30
|
+
...openaiSpeechBase,
|
|
31
|
+
released: '2023-11',
|
|
32
|
+
cost: { type: 'per-character', perMillionChars: 15 },
|
|
33
|
+
features: {
|
|
34
|
+
outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
|
|
35
|
+
instructions: false,
|
|
36
|
+
speed: true,
|
|
37
|
+
language: false,
|
|
38
|
+
maxChars: 4096,
|
|
39
|
+
defaultVoices: ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova', 'sage', 'shimmer'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'tts-1-hd',
|
|
44
|
+
name: 'TTS-1 HD',
|
|
45
|
+
description: 'OpenAI high-definition text-to-speech. Higher fidelity audio.',
|
|
46
|
+
...openaiSpeechBase,
|
|
47
|
+
released: '2023-11',
|
|
48
|
+
cost: { type: 'per-character', perMillionChars: 30 },
|
|
49
|
+
features: {
|
|
50
|
+
outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
|
|
51
|
+
instructions: false,
|
|
52
|
+
speed: true,
|
|
53
|
+
language: false,
|
|
54
|
+
maxChars: 4096,
|
|
55
|
+
defaultVoices: ['alloy', 'ash', 'coral', 'echo', 'fable', 'onyx', 'nova', 'sage', 'shimmer'],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'gpt-4o-mini-tts',
|
|
60
|
+
name: 'GPT-4o Mini TTS',
|
|
61
|
+
description: 'OpenAI GPT-4o Mini with text-to-speech. Supports style instructions ' +
|
|
62
|
+
'for expressive speech control.',
|
|
63
|
+
...openaiSpeechBase,
|
|
64
|
+
released: '2025-03',
|
|
65
|
+
cost: { type: 'per-character', perMillionChars: 12 },
|
|
66
|
+
features: {
|
|
67
|
+
outputFormats: ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'],
|
|
68
|
+
instructions: true,
|
|
69
|
+
speed: true,
|
|
70
|
+
language: false,
|
|
71
|
+
maxChars: 2000,
|
|
72
|
+
defaultVoices: [
|
|
73
|
+
'alloy', 'ash', 'ballad', 'cedar', 'coral', 'echo',
|
|
74
|
+
'fable', 'marin', 'nova', 'onyx', 'sage', 'shimmer', 'verse',
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
// ── ElevenLabs ─────────────────────────────────────────────────────────
|
|
79
|
+
{
|
|
80
|
+
id: 'eleven_v3',
|
|
81
|
+
name: 'ElevenLabs v3',
|
|
82
|
+
description: 'Latest ElevenLabs model with best quality and expressiveness.',
|
|
83
|
+
...elevenlabsSpeechBase,
|
|
84
|
+
released: '2025-06',
|
|
85
|
+
cost: { type: 'per-character', perMillionChars: 100 },
|
|
86
|
+
features: {
|
|
87
|
+
outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
|
|
88
|
+
instructions: false,
|
|
89
|
+
speed: false,
|
|
90
|
+
language: true,
|
|
91
|
+
maxChars: 0,
|
|
92
|
+
defaultVoices: [],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'eleven_multilingual_v2',
|
|
97
|
+
name: 'ElevenLabs Multilingual v2',
|
|
98
|
+
description: 'ElevenLabs multilingual model. Supports 29 languages.',
|
|
99
|
+
...elevenlabsSpeechBase,
|
|
100
|
+
released: '2024-01',
|
|
101
|
+
cost: { type: 'per-character', perMillionChars: 100 },
|
|
102
|
+
features: {
|
|
103
|
+
outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
|
|
104
|
+
instructions: false,
|
|
105
|
+
speed: false,
|
|
106
|
+
language: true,
|
|
107
|
+
maxChars: 0,
|
|
108
|
+
defaultVoices: [],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'eleven_flash_v2_5',
|
|
113
|
+
name: 'ElevenLabs Flash v2.5',
|
|
114
|
+
description: 'ElevenLabs low-latency model optimized for speed.',
|
|
115
|
+
...elevenlabsSpeechBase,
|
|
116
|
+
released: '2024-08',
|
|
117
|
+
cost: { type: 'per-character', perMillionChars: 50 },
|
|
118
|
+
features: {
|
|
119
|
+
outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
|
|
120
|
+
instructions: false,
|
|
121
|
+
speed: false,
|
|
122
|
+
language: true,
|
|
123
|
+
maxChars: 0,
|
|
124
|
+
defaultVoices: [],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'eleven_turbo_v2_5',
|
|
129
|
+
name: 'ElevenLabs Turbo v2.5',
|
|
130
|
+
description: 'ElevenLabs turbo model. Balanced quality and speed.',
|
|
131
|
+
...elevenlabsSpeechBase,
|
|
132
|
+
released: '2024-06',
|
|
133
|
+
cost: { type: 'per-character', perMillionChars: 50 },
|
|
134
|
+
features: {
|
|
135
|
+
outputFormats: ['mp3_44100_128', 'mp3_22050_32', 'pcm_16000', 'pcm_22050', 'pcm_24000', 'pcm_44100', 'ulaw_8000'],
|
|
136
|
+
instructions: false,
|
|
137
|
+
speed: false,
|
|
138
|
+
language: true,
|
|
139
|
+
maxChars: 0,
|
|
140
|
+
defaultVoices: [],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
// ── Cartesia ───────────────────────────────────────────────────────────
|
|
144
|
+
// Pricing is plan-based (~1 credit per character). The per-million rate
|
|
145
|
+
// below is estimated from the Startup plan ($49/1.25M credits ≈ $39/M).
|
|
146
|
+
// See https://cartesia.ai/pricing for current plans.
|
|
147
|
+
{
|
|
148
|
+
id: 'sonic-3.5',
|
|
149
|
+
name: 'Cartesia Sonic 3.5',
|
|
150
|
+
description: 'Fastest, most natural Cartesia TTS. #1 for naturalness, sub-90ms latency, ' +
|
|
151
|
+
'42 languages. Recommended for production.',
|
|
152
|
+
...cartesiaSpeechBase,
|
|
153
|
+
released: '2026-05',
|
|
154
|
+
cost: { type: 'per-character', perMillionChars: 39 },
|
|
155
|
+
features: {
|
|
156
|
+
outputFormats: ['mp3', 'wav', 'raw'],
|
|
157
|
+
instructions: false,
|
|
158
|
+
speed: true,
|
|
159
|
+
language: true,
|
|
160
|
+
maxChars: 0,
|
|
161
|
+
defaultVoices: [
|
|
162
|
+
'f786b574-daa5-4673-aa0c-cbe3e8534c02', // Katie (en-US Female)
|
|
163
|
+
'db6b0ed5-d5d3-463d-ae85-518a07d3c2b4', // Skylar (en-US Female)
|
|
164
|
+
'a5136bf9-224c-4d76-b823-52bd5efcffcc', // Jameson (en-US Male)
|
|
165
|
+
'62ae83ad-4f6a-430b-af41-a9bede9286ca', // Gemma (en-GB Female)
|
|
166
|
+
'ef191366-f52f-447a-a398-ed8c0f2943a1', // Archie (en-GB Male)
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'sonic-3',
|
|
172
|
+
name: 'Cartesia Sonic 3',
|
|
173
|
+
description: 'Previous generation Cartesia TTS. Still high quality with pronunciation ' +
|
|
174
|
+
'dictionaries support.',
|
|
175
|
+
...cartesiaSpeechBase,
|
|
176
|
+
released: '2025-01',
|
|
177
|
+
cost: { type: 'per-character', perMillionChars: 39 },
|
|
178
|
+
features: {
|
|
179
|
+
outputFormats: ['mp3', 'wav', 'raw'],
|
|
180
|
+
instructions: false,
|
|
181
|
+
speed: true,
|
|
182
|
+
language: true,
|
|
183
|
+
maxChars: 0,
|
|
184
|
+
defaultVoices: [
|
|
185
|
+
'f786b574-daa5-4673-aa0c-cbe3e8534c02', // Katie (en-US Female)
|
|
186
|
+
'db6b0ed5-d5d3-463d-ae85-518a07d3c2b4', // Skylar (en-US Female)
|
|
187
|
+
'a5136bf9-224c-4d76-b823-52bd5efcffcc', // Jameson (en-US Male)
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
// ─── lookup helpers ──────────────────────────────────────────────────────────
|
|
193
|
+
export function findSpeechModel(modelId) {
|
|
194
|
+
return SPEECH_CATALOG.find((m) => m.id === modelId);
|
|
195
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { GeneratedFile } from './generate.js';
|
|
2
|
+
import type { WordTimestamp } from './transcription-generate.js';
|
|
3
|
+
export type { WordTimestamp };
|
|
4
|
+
export interface SpeechProviderResult {
|
|
5
|
+
audio: Uint8Array;
|
|
6
|
+
mediaType: string;
|
|
7
|
+
/** Word-level timestamps, if the provider supports them. */
|
|
8
|
+
timestamps?: WordTimestamp[];
|
|
9
|
+
}
|
|
10
|
+
export interface SpeechProviderOptions {
|
|
11
|
+
text: string;
|
|
12
|
+
modelId: string;
|
|
13
|
+
voice?: string;
|
|
14
|
+
outputFormat?: string;
|
|
15
|
+
instructions?: string;
|
|
16
|
+
speed?: number;
|
|
17
|
+
language?: string;
|
|
18
|
+
abortSignal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
export interface SpeechProvider {
|
|
21
|
+
generate(options: SpeechProviderOptions): Promise<SpeechProviderResult>;
|
|
22
|
+
}
|
|
23
|
+
/** Speech model IDs from the catalog. Accepts arbitrary strings too. */
|
|
24
|
+
export type SpeechModelId = 'tts-1' | 'tts-1-hd' | 'gpt-4o-mini-tts' | 'eleven_v3' | 'eleven_multilingual_v2' | 'eleven_flash_v2_5' | 'eleven_turbo_v2_5' | 'sonic-3.5' | 'sonic-3' | (string & {});
|
|
25
|
+
/** Built-in voice presets across providers. Accepts arbitrary strings too. */
|
|
26
|
+
export type SpeechVoice = 'alloy' | 'ash' | 'coral' | 'echo' | 'fable' | 'onyx' | 'nova' | 'sage' | 'shimmer' | 'ballad' | 'cedar' | 'marin' | 'verse' | (string & {});
|
|
27
|
+
/** Audio output formats. */
|
|
28
|
+
export type AudioOutputFormat = 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm' | 'raw' | (string & {});
|
|
29
|
+
export interface GenerateSpeechOptions {
|
|
30
|
+
/** The text to convert to speech. */
|
|
31
|
+
text: string;
|
|
32
|
+
/** Speech model ID. Defaults to DEFAULT_SPEECH_MODEL ('tts-1') if omitted. */
|
|
33
|
+
model?: SpeechModelId;
|
|
34
|
+
/** Voice ID or name (provider-specific). */
|
|
35
|
+
voice?: SpeechVoice;
|
|
36
|
+
/** Output audio format: mp3, wav, pcm, opus, aac, flac, etc. */
|
|
37
|
+
outputFormat?: AudioOutputFormat;
|
|
38
|
+
/** Style instructions for models that support it (e.g. gpt-4o-mini-tts). */
|
|
39
|
+
instructions?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Playback speed multiplier. `1.0` is normal speed; values below slow
|
|
42
|
+
* down, values above speed up.
|
|
43
|
+
*
|
|
44
|
+
* Accepted ranges vary by provider:
|
|
45
|
+
*
|
|
46
|
+
* | Provider | Range | Notes |
|
|
47
|
+
* |-------------|-------------|--------------------------------------------------------------|
|
|
48
|
+
* | OpenAI | 0.25 – 4.0 | Works with `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`. |
|
|
49
|
+
* | ElevenLabs | 0.25 – 4.0 | Passed as `voice_settings.speed`. Extreme values degrade |
|
|
50
|
+
* | | | quality. Flash/turbo models may ignore it. |
|
|
51
|
+
* | Cartesia | 0.6 – 1.5 | Passed as `generation_config.speed`. Sonic models treat it |
|
|
52
|
+
* | | | as guidance, not an exact multiplier. |
|
|
53
|
+
*
|
|
54
|
+
* @default 1.0
|
|
55
|
+
*/
|
|
56
|
+
speed?: number;
|
|
57
|
+
/** ISO 639-1 language code (e.g. "en", "es", "fr"). */
|
|
58
|
+
language?: string;
|
|
59
|
+
/** Abort signal for cancelling the request. */
|
|
60
|
+
abortSignal?: AbortSignal;
|
|
61
|
+
}
|
|
62
|
+
export interface GenerateSpeechResult {
|
|
63
|
+
audio: GeneratedFile;
|
|
64
|
+
model: string;
|
|
65
|
+
cost: number | null;
|
|
66
|
+
/** Word-level timestamps, if the provider supports them (ElevenLabs, Cartesia). */
|
|
67
|
+
timestamps?: WordTimestamp[];
|
|
68
|
+
}
|
|
69
|
+
export declare function calculateSpeechCost(cost: {
|
|
70
|
+
type: 'per-character';
|
|
71
|
+
perMillionChars: number;
|
|
72
|
+
}, textLength: number): number;
|
|
73
|
+
/**
|
|
74
|
+
* Generate speech audio from text. Auto-detects which provider to use
|
|
75
|
+
* based on model ID. Shares credentials and subscription with the CLI.
|
|
76
|
+
* Returns word-level timestamps when the provider supports them.
|
|
77
|
+
*/
|
|
78
|
+
export declare function generateSpeechUncached(opts: GenerateSpeechOptions): Promise<Error | GenerateSpeechResult>;
|
|
79
|
+
export interface CachedSpeechResult {
|
|
80
|
+
src: string;
|
|
81
|
+
timestamps?: WordTimestamp[];
|
|
82
|
+
}
|
|
83
|
+
export declare const generateSpeech: import("./cached-generate.js").CachedGenerateFn<GenerateSpeechOptions & {
|
|
84
|
+
seed?: number;
|
|
85
|
+
}, CachedSpeechResult>;
|
|
86
|
+
//# sourceMappingURL=speech-generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-generate.d.ts","sourceRoot":"","sources":["../../src/cli/speech-generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Programmatic API for egaki speech generation.
|
|
2
|
+
// Returns Error | Result (errore style) instead of process.exit/console.error.
|
|
3
|
+
//
|
|
4
|
+
// Two variants:
|
|
5
|
+
// - `generateSpeech(opts)` — cached, writes to public/generated/audio/, returns { src }
|
|
6
|
+
// - `generateSpeechUncached(opts)` — raw, returns bytes in GenerateSpeechResult
|
|
7
|
+
//
|
|
8
|
+
// Uses our own SpeechProvider interface instead of the Vercel AI SDK's
|
|
9
|
+
// experimental_generateSpeech. This lets providers return word-level timestamps
|
|
10
|
+
// alongside audio (ElevenLabs /with-timestamps, Cartesia /tts/sse).
|
|
11
|
+
//
|
|
12
|
+
// Usage:
|
|
13
|
+
// import { generateSpeech, generateSpeechUncached } from 'egaki/generate'
|
|
14
|
+
// const cached = await generateSpeech({ text: 'Hello world' })
|
|
15
|
+
// if (!(cached instanceof Error)) cached.src // '/generated/audio/hello-world-a1b2c3d4.mp3'
|
|
16
|
+
// const raw = await generateSpeechUncached({ text: 'Hello world' })
|
|
17
|
+
// if (!(raw instanceof Error)) raw.audio.uint8Array // raw bytes
|
|
18
|
+
// if (raw.timestamps) raw.timestamps[0] // { word: 'Hello', startSecond: 0, endSecond: 0.4 }
|
|
19
|
+
import { injectCredentialsToEnv } from './credentials.js';
|
|
20
|
+
import { getSpeechModelConfig, getSpeechProvider, DEFAULT_SPEECH_MODEL, } from './speech-models.js';
|
|
21
|
+
// ─── cost calculation ────────────────────────────────────────────────────────
|
|
22
|
+
export function calculateSpeechCost(cost, textLength) {
|
|
23
|
+
return (textLength / 1_000_000) * cost.perMillionChars;
|
|
24
|
+
}
|
|
25
|
+
// ─── media type inference ────────────────────────────────────────────────────
|
|
26
|
+
function inferAudioMediaType(format) {
|
|
27
|
+
switch (format) {
|
|
28
|
+
case 'mp3':
|
|
29
|
+
return 'audio/mpeg';
|
|
30
|
+
case 'wav':
|
|
31
|
+
return 'audio/wav';
|
|
32
|
+
case 'pcm':
|
|
33
|
+
case 'raw':
|
|
34
|
+
return 'audio/pcm';
|
|
35
|
+
case 'opus':
|
|
36
|
+
return 'audio/opus';
|
|
37
|
+
case 'aac':
|
|
38
|
+
return 'audio/aac';
|
|
39
|
+
case 'flac':
|
|
40
|
+
return 'audio/flac';
|
|
41
|
+
default:
|
|
42
|
+
return 'audio/mpeg';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function inferMediaTypeFromBytes(audio, requestedFormat) {
|
|
46
|
+
// Check magic bytes first
|
|
47
|
+
if (audio.length >= 3 && audio[0] === 0x49 && audio[1] === 0x44 && audio[2] === 0x33) {
|
|
48
|
+
return 'audio/mpeg'; // ID3 header (MP3)
|
|
49
|
+
}
|
|
50
|
+
if (audio.length >= 2 && audio[0] === 0xff && (audio[1] & 0xe0) === 0xe0) {
|
|
51
|
+
return 'audio/mpeg'; // MP3 sync word
|
|
52
|
+
}
|
|
53
|
+
if (audio.length >= 4 && audio[0] === 0x52 && audio[1] === 0x49 && audio[2] === 0x46 && audio[3] === 0x46) {
|
|
54
|
+
return 'audio/wav'; // RIFF header
|
|
55
|
+
}
|
|
56
|
+
if (audio.length >= 4 && audio[0] === 0x4f && audio[1] === 0x67 && audio[2] === 0x67 && audio[3] === 0x53) {
|
|
57
|
+
return 'audio/ogg'; // OggS header (could be opus)
|
|
58
|
+
}
|
|
59
|
+
if (audio.length >= 4 && audio[0] === 0x66 && audio[1] === 0x4c && audio[2] === 0x61 && audio[3] === 0x43) {
|
|
60
|
+
return 'audio/flac'; // fLaC header
|
|
61
|
+
}
|
|
62
|
+
// Fall back to requested format
|
|
63
|
+
return inferAudioMediaType(requestedFormat);
|
|
64
|
+
}
|
|
65
|
+
// ─── generateSpeech ──────────────────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* Generate speech audio from text. Auto-detects which provider to use
|
|
68
|
+
* based on model ID. Shares credentials and subscription with the CLI.
|
|
69
|
+
* Returns word-level timestamps when the provider supports them.
|
|
70
|
+
*/
|
|
71
|
+
export async function generateSpeechUncached(opts) {
|
|
72
|
+
injectCredentialsToEnv();
|
|
73
|
+
const model = opts.model ?? DEFAULT_SPEECH_MODEL;
|
|
74
|
+
const config = getSpeechModelConfig(model);
|
|
75
|
+
if (config instanceof Error)
|
|
76
|
+
return config;
|
|
77
|
+
const provider = getSpeechProvider(config.provider);
|
|
78
|
+
if (provider instanceof Error)
|
|
79
|
+
return provider;
|
|
80
|
+
let result;
|
|
81
|
+
try {
|
|
82
|
+
result = await provider.generate({
|
|
83
|
+
text: opts.text,
|
|
84
|
+
modelId: model,
|
|
85
|
+
voice: opts.voice,
|
|
86
|
+
outputFormat: opts.outputFormat,
|
|
87
|
+
instructions: opts.instructions,
|
|
88
|
+
speed: opts.speed,
|
|
89
|
+
language: opts.language,
|
|
90
|
+
abortSignal: opts.abortSignal,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
95
|
+
}
|
|
96
|
+
const mediaType = result.mediaType || inferMediaTypeFromBytes(result.audio, opts.outputFormat);
|
|
97
|
+
const cost = calculateSpeechCost(config.cost, opts.text.length);
|
|
98
|
+
return {
|
|
99
|
+
audio: { uint8Array: result.audio, mediaType },
|
|
100
|
+
model,
|
|
101
|
+
cost,
|
|
102
|
+
timestamps: result.timestamps,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// ─── cached generateSpeech ──────────────────────────────────────────────────
|
|
106
|
+
// Writes audio to public/generated/audio/ and a sidecar .timestamps.json
|
|
107
|
+
// next to it when the provider returns word timestamps. On cache hit,
|
|
108
|
+
// the sidecar is read back to populate the result.
|
|
109
|
+
//
|
|
110
|
+
// Uses cachedGenerate with postWrite to write the timestamps sidecar after
|
|
111
|
+
// the audio file, and deserialize to read it back on cache hit.
|
|
112
|
+
import fs from 'node:fs';
|
|
113
|
+
import path from 'node:path';
|
|
114
|
+
import { cachedGenerate } from './cached-generate.js';
|
|
115
|
+
import { extensionFromMediaType } from './cache-utils.js';
|
|
116
|
+
/** Compute the sidecar timestamps path for an audio file.
|
|
117
|
+
* Stored in a `timestamps/` subdirectory to avoid cache key collision:
|
|
118
|
+
* findCachedFile() matches any filename containing the hash, so a sidecar
|
|
119
|
+
* like `foo-hash.mp3.timestamps.json` in the same directory would be
|
|
120
|
+
* mistakenly returned as the "cached audio" file. */
|
|
121
|
+
function timestampsPathFor(filePath) {
|
|
122
|
+
return path.join(path.dirname(filePath), 'timestamps', path.basename(filePath) + '.json');
|
|
123
|
+
}
|
|
124
|
+
export const generateSpeech = cachedGenerate({
|
|
125
|
+
namespace: 'audio',
|
|
126
|
+
prefixFrom: (p) => p.text,
|
|
127
|
+
modelFrom: (p) => p.model,
|
|
128
|
+
generate: async (params) => {
|
|
129
|
+
const result = await generateSpeechUncached(params);
|
|
130
|
+
if (result instanceof Error)
|
|
131
|
+
throw result;
|
|
132
|
+
return { audio: result.audio, timestamps: result.timestamps };
|
|
133
|
+
},
|
|
134
|
+
serialize: (output) => ({
|
|
135
|
+
bytes: output.audio.uint8Array,
|
|
136
|
+
extension: extensionFromMediaType(output.audio.mediaType),
|
|
137
|
+
}),
|
|
138
|
+
postWrite: (filePath, output) => {
|
|
139
|
+
if (output.timestamps?.length) {
|
|
140
|
+
const tsPath = timestampsPathFor(filePath);
|
|
141
|
+
fs.mkdirSync(path.dirname(tsPath), { recursive: true });
|
|
142
|
+
fs.writeFileSync(tsPath, JSON.stringify(output.timestamps, null, 2));
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
deserialize: ({ urlPath, filePath }) => {
|
|
146
|
+
const tsPath = timestampsPathFor(filePath);
|
|
147
|
+
let timestamps;
|
|
148
|
+
try {
|
|
149
|
+
if (fs.existsSync(tsPath)) {
|
|
150
|
+
timestamps = JSON.parse(fs.readFileSync(tsPath, 'utf-8'));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch { /* non-fatal: timestamps are optional */ }
|
|
154
|
+
return { src: urlPath, timestamps };
|
|
155
|
+
},
|
|
156
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SpeechProvider } from './speech-generate.js';
|
|
2
|
+
import { type SpeechModelEntry } from './speech-catalog.js';
|
|
3
|
+
export { type SpeechModelEntry };
|
|
4
|
+
export declare const DEFAULT_SPEECH_MODEL = "tts-1";
|
|
5
|
+
export declare function getSpeechModelConfig(modelId: string): Error | SpeechModelEntry;
|
|
6
|
+
export declare function ensureSpeechProviderKey(providerName: string): Error | undefined;
|
|
7
|
+
export declare function getSpeechProvider(providerName: string): Error | SpeechProvider;
|
|
8
|
+
//# sourceMappingURL=speech-models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-models.d.ts","sourceRoot":"","sources":["../../src/cli/speech-models.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAM1D,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,KAAK,gBAAgB,EAAE,CAAA;AAEhC,eAAO,MAAM,oBAAoB,UAAU,CAAA;AAqC3C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,gBAAgB,CAM9E;AAgCD,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAY/E;AAUD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,GAAG,cAAc,CA0B9E"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Speech model registry for egaki.
|
|
2
|
+
// Maps speech model IDs to their provider and SpeechProvider factory.
|
|
3
|
+
//
|
|
4
|
+
// Provider resolution priority (same as image/video):
|
|
5
|
+
// 1. Direct provider key → direct provider implementation
|
|
6
|
+
// 2. Egaki API key → route through egaki gateway (future)
|
|
7
|
+
// 3. No key → error with subscription recommendation
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
import { PROVIDERS, injectCredentialsToEnv, } from './credentials.js';
|
|
10
|
+
import { ValidationError } from './models.js';
|
|
11
|
+
import { SPEECH_CATALOG, findSpeechModel, } from './speech-catalog.js';
|
|
12
|
+
export {};
|
|
13
|
+
export const DEFAULT_SPEECH_MODEL = 'tts-1';
|
|
14
|
+
const SPEECH_PROVIDER_FACTORIES = {
|
|
15
|
+
openai: async () => {
|
|
16
|
+
const { createOpenAISpeechProvider } = await import('./openai-speech-provider.js');
|
|
17
|
+
return createOpenAISpeechProvider();
|
|
18
|
+
},
|
|
19
|
+
elevenlabs: async () => {
|
|
20
|
+
const { createElevenLabsSpeechProvider } = await import('./elevenlabs-speech-provider.js');
|
|
21
|
+
return createElevenLabsSpeechProvider();
|
|
22
|
+
},
|
|
23
|
+
cartesia: async () => {
|
|
24
|
+
const { createCartesiaSpeechProvider } = await import('./cartesia-provider.js');
|
|
25
|
+
return createCartesiaSpeechProvider();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
// ─── key checking ────────────────────────────────────────────────────────────
|
|
29
|
+
function hasDirectProviderKey(providerName) {
|
|
30
|
+
const info = PROVIDERS[providerName];
|
|
31
|
+
if (!info)
|
|
32
|
+
return false;
|
|
33
|
+
return Boolean(process.env[info.envVar]);
|
|
34
|
+
}
|
|
35
|
+
function hasEgakiKey() {
|
|
36
|
+
const info = PROVIDERS['egaki'];
|
|
37
|
+
if (!info)
|
|
38
|
+
return false;
|
|
39
|
+
return Boolean(process.env[info.envVar]);
|
|
40
|
+
}
|
|
41
|
+
// ─── config lookup ───────────────────────────────────────────────────────────
|
|
42
|
+
export function getSpeechModelConfig(modelId) {
|
|
43
|
+
const entry = findSpeechModel(modelId);
|
|
44
|
+
if (!entry) {
|
|
45
|
+
return new ValidationError(`Unknown speech model: ${modelId}`);
|
|
46
|
+
}
|
|
47
|
+
return entry;
|
|
48
|
+
}
|
|
49
|
+
function resolveAuthSource(providerName) {
|
|
50
|
+
const info = PROVIDERS[providerName];
|
|
51
|
+
if (info && process.env[info.envVar]) {
|
|
52
|
+
return { type: 'api-key', label: info.envVar };
|
|
53
|
+
}
|
|
54
|
+
if (hasEgakiKey()) {
|
|
55
|
+
return { type: 'egaki-gateway' };
|
|
56
|
+
}
|
|
57
|
+
return { type: 'api-key', label: 'unknown' };
|
|
58
|
+
}
|
|
59
|
+
function logAuthSource(source) {
|
|
60
|
+
switch (source.type) {
|
|
61
|
+
case 'api-key':
|
|
62
|
+
console.error(pc.dim(`Auth: ${source.label}`));
|
|
63
|
+
break;
|
|
64
|
+
case 'egaki-gateway':
|
|
65
|
+
console.error(pc.dim('Auth: Egaki subscription (gateway)'));
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ─── ensure key ──────────────────────────────────────────────────────────────
|
|
70
|
+
export function ensureSpeechProviderKey(providerName) {
|
|
71
|
+
if (hasDirectProviderKey(providerName))
|
|
72
|
+
return undefined;
|
|
73
|
+
// Speech models don't go through the gateway yet (AI Gateway doesn't
|
|
74
|
+
// support speech routing). Direct provider key is required.
|
|
75
|
+
const info = PROVIDERS[providerName];
|
|
76
|
+
const label = info?.label || providerName;
|
|
77
|
+
return new ValidationError(`Missing API key for ${label}. ` +
|
|
78
|
+
`Run: egaki login --provider ${providerName} --key <key>. ` +
|
|
79
|
+
(info ? info.hint : ''));
|
|
80
|
+
}
|
|
81
|
+
// ─── provider cache ──────────────────────────────────────────────────────────
|
|
82
|
+
/** Global cache of provider instances, keyed by provider name. */
|
|
83
|
+
const providerCache = globalThis.__egakiSpeechProviderCache ??= new Map();
|
|
84
|
+
// ─── public factory ──────────────────────────────────────────────────────────
|
|
85
|
+
export function getSpeechProvider(providerName) {
|
|
86
|
+
injectCredentialsToEnv();
|
|
87
|
+
const keyError = ensureSpeechProviderKey(providerName);
|
|
88
|
+
if (keyError)
|
|
89
|
+
return keyError;
|
|
90
|
+
const authSource = resolveAuthSource(providerName);
|
|
91
|
+
logAuthSource(authSource);
|
|
92
|
+
const factory = SPEECH_PROVIDER_FACTORIES[providerName];
|
|
93
|
+
if (!factory) {
|
|
94
|
+
return new Error(`Speech generation is not supported for provider: ${providerName}`);
|
|
95
|
+
}
|
|
96
|
+
// Return a lazy provider: the factory is async but we want getSpeechProvider
|
|
97
|
+
// to be sync for ergonomic use in generateSpeechUncached. The provider promise
|
|
98
|
+
// is cached globally per provider name so the factory runs once across all calls.
|
|
99
|
+
return {
|
|
100
|
+
async generate(options) {
|
|
101
|
+
const cached = providerCache.get(providerName);
|
|
102
|
+
if (cached)
|
|
103
|
+
return (await cached).generate(options);
|
|
104
|
+
const promise = factory();
|
|
105
|
+
providerCache.set(providerName, promise);
|
|
106
|
+
return (await promise).generate(options);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-provider.test.d.ts","sourceRoot":"","sources":["../../src/cli/speech-provider.test.ts"],"names":[],"mappings":""}
|