mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: add icon support for speech providers and update related configurations
This commit is contained in:
@@ -35,6 +35,8 @@ import {
|
||||
Lmstudio,
|
||||
Meta,
|
||||
MetaColor,
|
||||
Microsoft,
|
||||
MicrosoftColor,
|
||||
Minimax,
|
||||
MinimaxColor,
|
||||
Mistral,
|
||||
@@ -105,6 +107,8 @@ export const iconMap: Record<string, Component> = {
|
||||
'cohere-color': CohereColor,
|
||||
'azure': Azure,
|
||||
'azure-color': AzureColor,
|
||||
'microsoft': Microsoft,
|
||||
'microsoft-color': MicrosoftColor,
|
||||
'nvidia': Nvidia,
|
||||
'nvidia-color': NvidiaColor,
|
||||
'fireworks': Fireworks,
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<section class="flex items-center gap-3">
|
||||
<Volume2 class="size-5" />
|
||||
<span class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted">
|
||||
<ProviderIcon
|
||||
v-if="curProvider?.icon"
|
||||
:icon="curProvider.icon"
|
||||
size="1.5em"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{{ getInitials(curProvider?.name) }}
|
||||
</span>
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-sm font-semibold truncate">
|
||||
{{ curProvider?.name }}
|
||||
@@ -191,7 +203,7 @@ import {
|
||||
Switch,
|
||||
} from '@memohai/ui'
|
||||
import ModelConfigEditor from './model-config-editor.vue'
|
||||
import { ChevronDown, ChevronUp, Eye, EyeOff, Volume2 } from 'lucide-vue-next'
|
||||
import { ChevronDown, ChevronUp, Eye, EyeOff } from 'lucide-vue-next'
|
||||
import { computed, inject, reactive, ref, watch } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -199,6 +211,7 @@ import { useQuery, useQueryCache } from '@pinia/colada'
|
||||
import { getSpeechModels, getSpeechProvidersMeta, putModelsById, putProvidersById } from '@memohai/sdk'
|
||||
import type { TtsSpeechModelResponse, TtsSpeechProviderResponse } from '@memohai/sdk'
|
||||
import LoadingButton from '@/components/loading-button/index.vue'
|
||||
import ProviderIcon from '@/components/provider-icon/index.vue'
|
||||
|
||||
interface SpeechFieldSchema {
|
||||
key: string
|
||||
@@ -233,6 +246,11 @@ interface SpeechProviderMeta {
|
||||
models?: SpeechModelMeta[]
|
||||
}
|
||||
|
||||
function getInitials(name: string | undefined) {
|
||||
const label = name?.trim() ?? ''
|
||||
return label ? label.slice(0, 2).toUpperCase() : '?'
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
const curProvider = inject('curTtsProvider', ref<TtsSpeechProviderResponse>())
|
||||
const curProviderId = computed(() => curProvider.value?.id)
|
||||
|
||||
@@ -18,6 +18,12 @@ import type { TtsSpeechProviderResponse } from '@memohai/sdk'
|
||||
import ProviderSetting from './components/provider-setting.vue'
|
||||
import { Volume2 } from 'lucide-vue-next'
|
||||
import MasterDetailSidebarLayout from '@/components/master-detail-sidebar-layout/index.vue'
|
||||
import ProviderIcon from '@/components/provider-icon/index.vue'
|
||||
|
||||
function getInitials(name: string | undefined) {
|
||||
const label = name?.trim() ?? ''
|
||||
return label ? label.slice(0, 2).toUpperCase() : '?'
|
||||
}
|
||||
|
||||
const { data: providerData } = useQuery({
|
||||
key: () => ['speech-providers'],
|
||||
@@ -79,9 +85,17 @@ watch(filteredProviders, (list) => {
|
||||
>
|
||||
<span class="relative shrink-0">
|
||||
<span class="flex size-7 items-center justify-center rounded-full bg-muted">
|
||||
<Volume2
|
||||
class="size-3.5 text-muted-foreground"
|
||||
<ProviderIcon
|
||||
v-if="item.icon"
|
||||
:icon="item.icon"
|
||||
size="1.25em"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{{ getInitials(item.name) }}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="item.enable !== false"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
name: Alibaba Cloud Speech
|
||||
client_type: alibabacloud-speech
|
||||
icon: bailian-color
|
||||
|
||||
models:
|
||||
- model_id: cosyvoice-tts
|
||||
name: CosyVoice TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,7 @@
|
||||
name: Deepgram Speech
|
||||
client_type: deepgram-speech
|
||||
|
||||
models:
|
||||
- model_id: deepgram-tts
|
||||
name: Deepgram TTS
|
||||
type: speech
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Edge
|
||||
client_type: edge-speech
|
||||
icon: edge
|
||||
icon: microsoft
|
||||
|
||||
models:
|
||||
- model_id: edge-read-aloud
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
name: ElevenLabs Speech
|
||||
client_type: elevenlabs-speech
|
||||
|
||||
models:
|
||||
- model_id: elevenlabs-tts
|
||||
name: ElevenLabs TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,8 @@
|
||||
name: Microsoft Speech
|
||||
client_type: microsoft-speech
|
||||
icon: azure-color
|
||||
|
||||
models:
|
||||
- model_id: microsoft-tts
|
||||
name: Microsoft TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,8 @@
|
||||
name: MiniMax Speech
|
||||
client_type: minimax-speech
|
||||
icon: minimax-color
|
||||
|
||||
models:
|
||||
- model_id: minimax-tts
|
||||
name: MiniMax TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,8 @@
|
||||
name: OpenAI Speech
|
||||
client_type: openai-speech
|
||||
icon: openai
|
||||
|
||||
models:
|
||||
- model_id: gpt-4o-mini-tts
|
||||
name: GPT-4o Mini TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,8 @@
|
||||
name: OpenRouter Speech
|
||||
client_type: openrouter-speech
|
||||
icon: openrouter
|
||||
|
||||
models:
|
||||
- model_id: openrouter-tts
|
||||
name: OpenRouter TTS
|
||||
type: speech
|
||||
@@ -0,0 +1,8 @@
|
||||
name: Volcengine Speech
|
||||
client_type: volcengine-speech
|
||||
icon: volcengine-color
|
||||
|
||||
models:
|
||||
- model_id: sami-tts
|
||||
name: SAMI TTS
|
||||
type: speech
|
||||
@@ -17,11 +17,15 @@ func SyncRegistry(ctx context.Context, logger *slog.Logger, queries *sqlc.Querie
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal speech provider config: %w", err)
|
||||
}
|
||||
var icon pgtype.Text
|
||||
if def.Icon != "" {
|
||||
icon = pgtype.Text{String: def.Icon, Valid: true}
|
||||
}
|
||||
|
||||
provider, err := queries.UpsertRegistryProvider(ctx, sqlc.UpsertRegistryProviderParams{
|
||||
Name: def.DisplayName,
|
||||
ClientType: string(def.ClientType),
|
||||
Icon: pgtype.Text{},
|
||||
Icon: icon,
|
||||
Config: configJSON,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ type ProviderFactory func(config map[string]any) (sdk.SpeechProvider, error)
|
||||
type ProviderDefinition struct {
|
||||
ClientType models.ClientType
|
||||
DisplayName string
|
||||
Icon string
|
||||
Description string
|
||||
ConfigSchema ConfigSchema
|
||||
DefaultModel string
|
||||
@@ -123,6 +124,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeEdgeSpeech,
|
||||
DisplayName: "Microsoft Edge",
|
||||
Icon: "microsoft",
|
||||
Description: "Free Edge Read Aloud TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{stringField("base_url", "Base URL", "Override the Edge WebSocket endpoint", false, "", 10)}},
|
||||
DefaultModel: "edge-read-aloud",
|
||||
@@ -163,6 +165,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeOpenAISpeech,
|
||||
DisplayName: "OpenAI Speech",
|
||||
Icon: "openai",
|
||||
Description: "OpenAI /audio/speech compatible TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("api_key", "API Key", "Bearer API key", true, 10),
|
||||
@@ -204,6 +207,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeOpenRouterSpeech,
|
||||
DisplayName: "OpenRouter Speech",
|
||||
Icon: "openrouter",
|
||||
Description: "OpenRouter audio modality TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("api_key", "API Key", "OpenRouter API key", true, 10),
|
||||
@@ -333,6 +337,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeMiniMaxSpeech,
|
||||
DisplayName: "MiniMax Speech",
|
||||
Icon: "minimax-color",
|
||||
Description: "MiniMax TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("api_key", "API Key", "MiniMax API key", true, 10),
|
||||
@@ -380,6 +385,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeVolcengineSpeech,
|
||||
DisplayName: "Volcengine Speech",
|
||||
Icon: "volcengine-color",
|
||||
Description: "Volcengine SAMI TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("access_key", "Access Key", "Volcengine access key", true, 10),
|
||||
@@ -431,6 +437,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeAlibabaSpeech,
|
||||
DisplayName: "Alibaba Cloud Speech",
|
||||
Icon: "bailian-color",
|
||||
Description: "DashScope CosyVoice TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("api_key", "API Key", "DashScope API key", true, 10),
|
||||
@@ -478,6 +485,7 @@ func defaultProviderDefinitions() []ProviderDefinition {
|
||||
{
|
||||
ClientType: models.ClientTypeMicrosoftSpeech,
|
||||
DisplayName: "Microsoft Speech",
|
||||
Icon: "azure-color",
|
||||
Description: "Azure Cognitive Services TTS",
|
||||
ConfigSchema: ConfigSchema{Fields: []FieldSchema{
|
||||
secretField("api_key", "API Key", "Azure speech subscription key", true, 10),
|
||||
|
||||
@@ -209,10 +209,15 @@ func mergeConfig(parts ...map[string]any) map[string]any {
|
||||
}
|
||||
|
||||
func toSpeechProviderResponse(row sqlc.Provider) SpeechProviderResponse {
|
||||
icon := ""
|
||||
if row.Icon.Valid {
|
||||
icon = row.Icon.String
|
||||
}
|
||||
return SpeechProviderResponse{
|
||||
ID: row.ID.String(),
|
||||
Name: row.Name,
|
||||
ClientType: row.ClientType,
|
||||
Icon: icon,
|
||||
Enable: row.Enable,
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
UpdatedAt: row.UpdatedAt.Time,
|
||||
|
||||
@@ -17,6 +17,7 @@ type SpeechProviderResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ClientType string `json:"client_type"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Enable bool `json:"enable"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -1709,6 +1709,7 @@ export type TtsSpeechProviderResponse = {
|
||||
client_type?: string;
|
||||
created_at?: string;
|
||||
enable?: boolean;
|
||||
icon?: string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
updated_at?: string;
|
||||
|
||||
@@ -13111,6 +13111,9 @@ const docTemplate = `{
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -13102,6 +13102,9 @@
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2880,6 +2880,8 @@ definitions:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
icon:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
|
||||
Reference in New Issue
Block a user