diff --git a/apps/stage-web/package.json b/apps/stage-web/package.json index 1f4503ac5..0585a5904 100644 --- a/apps/stage-web/package.json +++ b/apps/stage-web/package.json @@ -50,6 +50,7 @@ "@vueuse/shared": "^12.8.2", "@xsai-ext/providers-cloud": "catalog:", "@xsai-ext/providers-local": "catalog:", + "@xsai-ext/shared-providers": "catalog:", "@xsai/generate-speech": "catalog:", "@xsai/generate-text": "catalog:", "@xsai/model": "catalog:", diff --git a/apps/stage-web/src/components/Layouts/InteractiveArea.vue b/apps/stage-web/src/components/Layouts/InteractiveArea.vue index 5a3550b38..eb851dc85 100644 --- a/apps/stage-web/src/components/Layouts/InteractiveArea.vue +++ b/apps/stage-web/src/components/Layouts/InteractiveArea.vue @@ -1,8 +1,10 @@ @@ -44,11 +44,11 @@ onMounted(async () => { - + - + Provider @@ -62,19 +62,30 @@ onMounted(async () => { See also: https://stackoverflow.com/a/33737340 --> @@ -135,114 +146,22 @@ onMounted(async () => { - + - - - - - - - - - - - - - - - {{ $t('settings.modules.consciousness.provider-model-selection.search_results', { - count: filteredModels.length, - total: providerModels.length, - }) }} - - - - - - - {{ - $t('settings.modules.consciousness.provider-model-selection.no_search_results') }} - - {{ $t('settings.modules.consciousness.provider-model-selection.no_search_results_description', { - query: modelSearchQuery }) }} - - - - - - - - - - - - - - - {{ isModelListExpanded ? $t('settings.modules.consciousness.provider-model-selection.collapse') : $t('settings.modules.consciousness.provider-model-selection.expand') }} - - - - - + @@ -251,10 +170,10 @@ onMounted(async () => { - + {{ $t('settings.modules.consciousness.provider-model-selection.title') }} - + {{ $t('settings.modules.consciousness.provider-model-selection.subtitle') }} diff --git a/apps/stage-web/src/pages/settings/modules/index.vue b/apps/stage-web/src/pages/settings/modules/index.vue index 3a7b0d222..7e333673b 100644 --- a/apps/stage-web/src/pages/settings/modules/index.vue +++ b/apps/stage-web/src/pages/settings/modules/index.vue @@ -2,7 +2,6 @@ import { computed } from 'vue' import { useRouter } from 'vue-router' -import FactorioIcon from '../../../assets/icons/modules/games/factorio.png' import IconStatusItem from '../../../components/Menu/IconStatusItem.vue' const router = useRouter() @@ -28,70 +27,70 @@ const modulesList = computed(() => [ to: '/settings/modules/consciousness', configured: false, }, - { - id: 'hearing', - name: 'Hearing', - description: 'Hearing, speech recognition, etc.', - icon: 'i-lucide:ear', - to: '', - configured: false, - }, - { - id: 'messaging-discord', - name: 'Discord', - description: 'Messaging, notifications, etc.', - icon: 'i-simple-icons:discord', - to: '', - configured: false, - }, + // { + // id: 'hearing', + // name: 'Hearing', + // description: 'Hearing, speech recognition, etc.', + // icon: 'i-lucide:ear', + // to: '', + // configured: false, + // }, + // { + // id: 'messaging-discord', + // name: 'Discord', + // description: 'Messaging, notifications, etc.', + // icon: 'i-simple-icons:discord', + // to: '', + // configured: false, + // }, { id: 'speech', name: 'Speech', description: 'Speech synthesis, etc.', icon: 'i-lucide:mic', - to: '', - configured: false, - }, - { - id: 'memory-short-term', - name: 'Short-Term Memory', - description: 'Short-term memory, etc.', - icon: 'i-lucide:book', - to: '', - configured: false, - }, - { - id: 'memory-long-term', - name: 'Long-Term Memory', - description: 'Long-term memory, etc.', - icon: 'i-lucide:book-copy', - to: '', - configured: false, - }, - { - id: 'vision', - name: 'Vision', - description: 'Vision, etc.', - icon: 'i-lucide:eye', - to: '', - configured: false, - }, - { - id: 'game-minecraft', - name: 'Minecraft', - description: 'Playing Minecraft with you, etc.', - iconColor: 'i-vscode-icons:file-type-minecraft', - to: '', - configured: false, - }, - { - id: 'game-factorio', - name: 'Factorio', - description: 'Playing Factorio with you, etc.', - iconImage: FactorioIcon, - to: '', + to: '/settings/modules/speech', configured: false, }, + // { + // id: 'memory-short-term', + // name: 'Short-Term Memory', + // description: 'Short-term memory, etc.', + // icon: 'i-lucide:book', + // to: '', + // configured: false, + // }, + // { + // id: 'memory-long-term', + // name: 'Long-Term Memory', + // description: 'Long-term memory, etc.', + // icon: 'i-lucide:book-copy', + // to: '', + // configured: false, + // }, + // { + // id: 'vision', + // name: 'Vision', + // description: 'Vision, etc.', + // icon: 'i-lucide:eye', + // to: '', + // configured: false, + // }, + // { + // id: 'game-minecraft', + // name: 'Minecraft', + // description: 'Playing Minecraft with you, etc.', + // iconColor: 'i-vscode-icons:file-type-minecraft', + // to: '', + // configured: false, + // }, + // { + // id: 'game-factorio', + // name: 'Factorio', + // description: 'Playing Factorio with you, etc.', + // iconImage: FactorioIcon, + // to: '', + // configured: false, + // }, ]) diff --git a/apps/stage-web/src/pages/settings/modules/speech.vue b/apps/stage-web/src/pages/settings/modules/speech.vue new file mode 100644 index 000000000..b1336220f --- /dev/null +++ b/apps/stage-web/src/pages/settings/modules/speech.vue @@ -0,0 +1,273 @@ + + + + + + + + + + Modules + + + Speech + + + + + + + + + Provider + + + Select the suitable speech provider + + + + + + + + + + + No Speech Providers Configured + Click here to set up your speech + providers + + + + + + + + + + + + + + Voice Configuration + + + Customize how your AI assistant speaks + + + + + + + + + Loading available voices... + + + + + + + Error loading voices + {{ speechProviderError }} + + + + + + + + + Voice Name + + updateVoiceName((event.target as HTMLInputElement).value)" + > + + For ElevenLabs, enter the exact voice name from your account + + + + + + + Model + + + + Monolingual v1 + + + Multilingual v1 + + + Multilingual v2 + + + + + + + + + Pitch Adjustment (%) + + + updatePitch(parseInt((event.target as HTMLInputElement).value))" + > + {{ pitch }}% + + + + + Speech Rate + + + updateRate(parseFloat((event.target as HTMLInputElement).value))" + > + {{ rate }}x + + + + + + + + SSML Support + + + + + + + + + Enable Speech Synthesis Markup Language for more control over speech output + + + + SSML Example + + {{ ssmlExample }} + + + + + + + + + + + + + +meta: + stageTransition: + name: slide + diff --git a/apps/stage-web/src/pages/settings/providers/elevenlabs.vue b/apps/stage-web/src/pages/settings/providers/elevenlabs.vue new file mode 100644 index 000000000..1bdcbfde4 --- /dev/null +++ b/apps/stage-web/src/pages/settings/providers/elevenlabs.vue @@ -0,0 +1,272 @@ + + + + + + + + + + Provider + + + {{ providerMetadata?.localizedName }} + + + + + + + + + Basic + + + Essential settings + + + + + + + API Key + * + + + API Key for {{ providerMetadata?.localizedName }} + + + + + + + + + + + Voice Settings + + + + + + Language + + + Select voice language + + + + + {{ language }} + + + + + + + + Voice + + + Select preferred voice + + + + + {{ voice.name }} + + + + + + + + Pitch + + + Adjust voice pitch + + + + + {{ speechStore.pitch }} + + + + + + + Rate + + + Adjust speaking rate + + + + + {{ speechStore.rate.toFixed(1) }} + + + + + + + SSML + + + Enable SSML support + + + + + {{ speechStore.ssmlEnabled ? 'Enabled' : 'Disabled' }} + + + + + + + + + slotProps.setVisible(!slotProps.visible) && toggleAdvancedVisible()" + > + + Advanced + + + + + + + + + + + Base URL + + + Custom base URL (optional) + + + + + + + + Reset Voice Settings + + + + + + + + + + diff --git a/apps/stage-web/src/pages/settings/providers/openai.vue b/apps/stage-web/src/pages/settings/providers/openai.vue index 732cb4fb3..6053d8c2a 100644 --- a/apps/stage-web/src/pages/settings/providers/openai.vue +++ b/apps/stage-web/src/pages/settings/providers/openai.vue @@ -46,69 +46,78 @@ watch([apiKey, baseUrl], () => { - {}"> + - - Basic - - - - + - - API Key - * - - - API Key for OpenAI + + Basic + + + Essential settings - - - - - - slotProps.setVisible(!slotProps.visible) && toggleAdvancedVisible()" - > - - Advanced - - - - - - - - - - - Base URL + + + + + API Key + * + + + API Key for OpenAI + - - Custom base URL (optional) - - - - + + + - - + + + + + slotProps.setVisible(!slotProps.visible) && toggleAdvancedVisible()" + > + + Advanced + + + + + + + + + + + Base URL + + + Custom base URL (optional) + + + + + + + + diff --git a/apps/stage-web/src/pages/settings/providers/openrouter.vue b/apps/stage-web/src/pages/settings/providers/openrouter.vue index 559a4232f..d086d8df4 100644 --- a/apps/stage-web/src/pages/settings/providers/openrouter.vue +++ b/apps/stage-web/src/pages/settings/providers/openrouter.vue @@ -50,69 +50,78 @@ watch([apiKey, baseUrl], () => { - {}"> + - - Basic - - - - + - - API Key - * - - - API Key for {{ providerMetadata?.localizedName }} + + Basic + + + Essential settings - - - - - - slotProps.setVisible(!slotProps.visible) && toggleAdvancedVisible()" - > - - Advanced - - - - - - - - - - - Base URL + + + + + API Key + * + + + API Key for {{ providerMetadata?.localizedName }} + - - Custom base URL (optional) - - - - + + + - - + + + + + slotProps.setVisible(!slotProps.visible) && toggleAdvancedVisible()" + > + + Advanced + + + + + + + + + + + Base URL + + + Custom base URL (optional) + + + + + + + + diff --git a/cspell.config.yaml b/cspell.config.yaml index e574c5178..1e0da871e 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -81,6 +81,7 @@ words: - lobehub - logg - lucide + - magnifer - Maru - matchall - micvad @@ -160,5 +161,6 @@ words: - xsai - xsschema - zhipu + - ssml ignoreWords: [] import: [] diff --git a/packages/stage-ui/src/components/Form/Radio/RadioCardDetail.vue b/packages/stage-ui/src/components/Form/Radio/RadioCardDetail.vue index 8bab7b0cd..62cba2027 100644 --- a/packages/stage-ui/src/components/Form/Radio/RadioCardDetail.vue +++ b/packages/stage-ui/src/components/Form/Radio/RadioCardDetail.vue @@ -48,11 +48,12 @@ function updateCustomInput(event: Event) { +import { computed, ref } from 'vue' + +import RadioCardDetail from './Radio/RadioCardDetail.vue' + +interface Item { + id: string + name: string + description?: string + deprecated?: boolean + customizable?: boolean +} + +interface Props { + items: Item[] + modelValue: string + searchable?: boolean + searchPlaceholder?: string + searchNoResultsTitle?: string + searchNoResultsDescription?: string + searchResultsText?: string + customInputPlaceholder?: string + expandButtonText?: string + collapseButtonText?: string +} + +const props = withDefaults(defineProps(), { + searchable: true, + searchPlaceholder: 'Search...', + searchNoResultsTitle: 'No results found', + searchNoResultsDescription: 'Try a different search term', + searchResultsText: '{count} of {total} results', + customInputPlaceholder: 'Enter custom value', + expandButtonText: 'Show more', + collapseButtonText: 'Show less', +}) + +const emit = defineEmits<{ + 'update:modelValue': [value: string] + 'update:customValue': [value: string] +}>() + +const searchQuery = ref('') +const isListExpanded = ref(false) +const customValue = ref('') + +const filteredItems = computed(() => { + if (!searchQuery.value) + return props.items + + const query = searchQuery.value.toLowerCase() + return props.items.filter(item => + item.name.toLowerCase().includes(query) + || (item.description && item.description.toLowerCase().includes(query)), + ) +}) + +function clearSearch() { + searchQuery.value = '' +} + +function updateCustomValue(value: string) { + customValue.value = value + emit('update:customValue', value) +} + + + + + + + + + + + + + + + + + + + + {{ searchResultsText.replace('{count}', filteredItems.length.toString()).replace('{total}', items.length.toString()) }} + + + + + + + {{ searchNoResultsTitle }} + + {{ searchNoResultsDescription.replace('{query}', searchQuery) }} + + + + + + + + + + + + + + + {{ isListExpanded ? collapseButtonText : expandButtonText }} + + + + + + + diff --git a/packages/stage-ui/src/components/Widgets/Stage.vue b/packages/stage-ui/src/components/Widgets/Stage.vue index dff5934a7..0a0ed58b5 100644 --- a/packages/stage-ui/src/components/Widgets/Stage.vue +++ b/packages/stage-ui/src/components/Widgets/Stage.vue @@ -1,10 +1,10 @@
+ For ElevenLabs, enter the exact voice name from your account +
+ Enable Speech Synthesis Markup Language for more control over speech output +
{{ ssmlExample }}