|
1 | 1 | <script setup lang="ts"> |
2 | | -import type { RemovableRef } from '@vueuse/core' |
3 | 2 | import type { SpeechProvider } from '@xsai-ext/shared-providers' |
4 | 3 |
|
5 | 4 | import { |
6 | | - Alert, |
7 | | - ProviderAdvancedSettings, |
8 | | - ProviderApiKeyInput, |
9 | | - ProviderBaseUrlInput, |
10 | | - ProviderBasicSettings, |
11 | | - ProviderSettingsContainer, |
12 | | - ProviderSettingsLayout, |
13 | | - SpeechPlaygroundOpenAICompatible, |
| 5 | + SpeechPlayground, |
| 6 | + SpeechProviderSettings, |
14 | 7 | } from '@proj-airi/stage-ui/components' |
15 | | -import { useProviderValidation } from '@proj-airi/stage-ui/composables/use-provider-validation' |
16 | 8 | import { useSpeechStore } from '@proj-airi/stage-ui/stores/modules/speech' |
17 | 9 | import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers' |
18 | 10 | import { FieldRange } from '@proj-airi/ui' |
19 | 11 | import { storeToRefs } from 'pinia' |
20 | | -import { computed, ref } from 'vue' |
| 12 | +import { computed, ref, watch } from 'vue' |
| 13 | +import { useI18n } from 'vue-i18n' |
21 | 14 |
|
22 | 15 | const speechStore = useSpeechStore() |
23 | 16 | const providersStore = useProvidersStore() |
24 | | -const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> } |
| 17 | +const { providers } = storeToRefs(providersStore) |
| 18 | +const { t } = useI18n() |
25 | 19 |
|
26 | 20 | const defaultVoiceSettings = { |
27 | 21 | speed: 1.0, |
28 | 22 | } |
29 | 23 |
|
30 | 24 | // Get provider metadata |
31 | 25 | const providerId = 'comet-api-speech' |
32 | | -
|
33 | | -// Settings refs |
34 | | -const apiKey = computed({ |
35 | | - get: () => providers.value[providerId]?.apiKey || '', |
36 | | - set: (value) => { |
37 | | - if (providers.value[providerId]) |
38 | | - providers.value[providerId].apiKey = value |
39 | | - }, |
40 | | -}) |
41 | | -
|
42 | | -const baseUrl = computed({ |
43 | | - get: () => providers.value[providerId]?.baseUrl || '', |
44 | | - set: (value) => { |
45 | | - if (providers.value[providerId]) |
46 | | - providers.value[providerId].baseUrl = value |
47 | | - }, |
48 | | -}) |
49 | | -
|
50 | | -const model = computed({ |
51 | | - get: () => providers.value[providerId]?.model || 'tts-1', |
52 | | - set: (value) => { |
53 | | - if (providers.value[providerId]) |
54 | | - providers.value[providerId].model = value |
55 | | - }, |
56 | | -}) |
57 | | -
|
58 | | -const voice = computed({ |
59 | | - get: () => providers.value[providerId]?.voice || 'alloy', |
60 | | - set: (value) => { |
61 | | - if (providers.value[providerId]) |
62 | | - providers.value[providerId].voice = value |
63 | | - }, |
64 | | -}) |
| 26 | +const defaultModel = 'gpt-4o-mini-tts' |
65 | 27 |
|
66 | 28 | const speed = ref<number>(1.0) |
67 | 29 |
|
68 | 30 | // Check if API key is configured |
69 | 31 | const apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey) |
70 | 32 |
|
71 | | -// Generate speech with specific parameters |
72 | | -async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean, modelId?: string) { |
| 33 | +const availableVoices = computed(() => { |
| 34 | + return speechStore.availableVoices[providerId] || [] |
| 35 | +}) |
| 36 | +
|
| 37 | +// Generate speech with ElevenLabs-specific parameters |
| 38 | +async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean) { |
73 | 39 | const provider = await providersStore.getProviderInstance<SpeechProvider<string>>(providerId) |
74 | | - if (!provider) |
| 40 | + if (!provider) { |
75 | 41 | throw new Error('Failed to initialize speech provider') |
| 42 | + } |
76 | 43 |
|
| 44 | + // Get provider configuration |
77 | 45 | const providerConfig = providersStore.getProviderConfig(providerId) |
78 | 46 |
|
| 47 | + // Get model from configuration or use default |
| 48 | + const model = providerConfig.model as string | undefined || defaultModel |
| 49 | +
|
| 50 | + // ElevenLabs doesn't need SSML conversion, but if SSML is provided, use it directly |
79 | 51 | return await speechStore.speech( |
80 | 52 | provider, |
81 | | - modelId || model.value, |
| 53 | + model, |
82 | 54 | input, |
83 | | - voiceId || voice.value, |
| 55 | + voiceId, |
84 | 56 | { |
85 | 57 | ...providerConfig, |
86 | 58 | ...defaultVoiceSettings, |
87 | | - speed: speed.value, |
88 | 59 | }, |
89 | 60 | ) |
90 | 61 | } |
91 | 62 |
|
92 | | -// Use the composable to get validation logic and state |
93 | | -const { |
94 | | - t, |
95 | | - router, |
96 | | - providerMetadata, |
97 | | - isValidating, |
98 | | - isValid, |
99 | | - validationMessage, |
100 | | - handleResetSettings, |
101 | | -} = useProviderValidation(providerId) |
| 63 | +watch(speed, async () => { |
| 64 | + const providerConfig = providersStore.getProviderConfig(providerId) |
| 65 | + providerConfig.speed = speed.value |
| 66 | +}) |
102 | 67 | </script> |
103 | 68 |
|
104 | 69 | <template> |
105 | | - <ProviderSettingsLayout |
106 | | - :provider-name="providerMetadata?.localizedName" |
107 | | - :provider-icon-color="providerMetadata?.iconColor" |
108 | | - :on-back="() => router.back()" |
| 70 | + <SpeechProviderSettings |
| 71 | + :provider-id="providerId" |
| 72 | + :default-model="defaultModel" |
| 73 | + :additional-settings="defaultVoiceSettings" |
109 | 74 | > |
110 | | - <ProviderSettingsContainer> |
111 | | - <ProviderBasicSettings |
112 | | - :title="t('settings.pages.providers.common.section.basic.title')" |
113 | | - :description="t('settings.pages.providers.common.section.basic.description')" |
114 | | - :on-reset="handleResetSettings" |
115 | | - > |
116 | | - <ProviderApiKeyInput |
117 | | - v-model="apiKey" |
118 | | - :required="false" |
119 | | - :provider-name="providerMetadata?.localizedName" |
120 | | - placeholder="sk-..." |
121 | | - /> |
122 | | - </ProviderBasicSettings> |
123 | | - |
124 | | - <ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')"> |
125 | | - <ProviderBaseUrlInput |
126 | | - v-model="baseUrl" |
127 | | - :placeholder="providerMetadata?.defaultOptions?.().baseUrl as string || 'https://api.cometapi.com/v1/'" |
128 | | - /> |
129 | | - <FieldRange |
130 | | - v-model="speed" |
131 | | - :label="t('settings.pages.providers.provider.common.fields.field.speed.label')" |
132 | | - :description="t('settings.pages.providers.provider.common.fields.field.speed.description')" |
133 | | - :min="0.5" |
134 | | - :max="2.0" :step="0.01" |
135 | | - /> |
136 | | - </ProviderAdvancedSettings> |
137 | | - |
138 | | - <!-- Validation Status --> |
139 | | - <Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error"> |
140 | | - <template #title> |
141 | | - {{ t('settings.dialogs.onboarding.validationFailed') }} |
142 | | - </template> |
143 | | - <template v-if="validationMessage" #content> |
144 | | - <div class="whitespace-pre-wrap break-all"> |
145 | | - {{ validationMessage }} |
146 | | - </div> |
147 | | - </template> |
148 | | - </Alert> |
149 | | - <Alert v-if="isValid && isValidating === 0" type="success"> |
150 | | - <template #title> |
151 | | - {{ t('settings.dialogs.onboarding.validationSuccess') }} |
152 | | - </template> |
153 | | - </Alert> |
154 | | - </ProviderSettingsContainer> |
155 | | - |
156 | | - <SpeechPlaygroundOpenAICompatible |
157 | | - v-model:model-value="model" |
158 | | - v-model:voice="voice" |
159 | | - :generate-speech="handleGenerateSpeech" |
160 | | - :api-key-configured="apiKeyConfigured" |
161 | | - default-text="Hello! This is a test of the Comet API Speech." |
162 | | - /> |
163 | | - </ProviderSettingsLayout> |
| 75 | + <!-- Voice settings specific to ElevenLabs --> |
| 76 | + <template #voice-settings> |
| 77 | + <!-- Speed control - common to most providers --> |
| 78 | + <FieldRange |
| 79 | + v-model="speed" |
| 80 | + :label="t('settings.pages.providers.provider.common.fields.field.speed.label')" |
| 81 | + :description="t('settings.pages.providers.provider.common.fields.field.speed.description')" |
| 82 | + :min="0.5" |
| 83 | + :max="2.0" :step="0.01" |
| 84 | + /> |
| 85 | + </template> |
| 86 | + |
| 87 | + <template #playground> |
| 88 | + <SpeechPlayground |
| 89 | + :available-voices="availableVoices" |
| 90 | + :generate-speech="handleGenerateSpeech" |
| 91 | + :api-key-configured="apiKeyConfigured" |
| 92 | + default-text="Hello! This is a test of the OpenAI Speech." |
| 93 | + /> |
| 94 | + </template> |
| 95 | + </SpeechProviderSettings> |
164 | 96 | </template> |
165 | 97 |
|
166 | 98 | <route lang="yaml"> |
|
0 commit comments