feat(stage-pages,stage-ui,i18n): supported CometAPI

Close #619
This commit is contained in:
Neko Ayaka
2025-10-11 16:16:22 +08:00
parent 6bf15a8803
commit 7e7b2cdfed
10 changed files with 383 additions and 0 deletions
+1
View File
@@ -40,6 +40,7 @@ words:
- clustr
- collectblock
- colorjs
- cometapi
- Comfortaa
- composables
- cooldown
@@ -568,6 +568,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: Transcription Playground
@@ -506,6 +506,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: Playground de Transcripción
@@ -509,6 +509,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: Terrain de test de transcription
@@ -490,6 +490,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: Песочница транскрипции
@@ -495,6 +495,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: Transcription Playground
@@ -500,6 +500,9 @@ pages:
302-ai:
description: 302.AI
title: 302.AI
comet-api:
description: CometAPI.com
title: Comet API
transcriptions:
playground:
title: 实验平台
@@ -0,0 +1,171 @@
<script setup lang="ts">
import type { RemovableRef } from '@vueuse/core'
import type { SpeechProvider } from '@xsai-ext/shared-providers'
import {
Alert,
ProviderAdvancedSettings,
ProviderApiKeyInput,
ProviderBaseUrlInput,
ProviderBasicSettings,
ProviderSettingsContainer,
ProviderSettingsLayout,
SpeechPlaygroundOpenAICompatible,
} from '@proj-airi/stage-ui/components'
import { useProviderValidation } from '@proj-airi/stage-ui/composables/use-provider-validation'
import { useSpeechStore } from '@proj-airi/stage-ui/stores/modules/speech'
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
import { FieldRange } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
const speechStore = useSpeechStore()
const providersStore = useProvidersStore()
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
const defaultVoiceSettings = {
speed: 1.0,
}
// Get provider metadata
const providerId = 'comet-api-speech'
// Settings refs
const apiKey = computed({
get: () => providers.value[providerId]?.apiKey || '',
set: (value) => {
if (providers.value[providerId])
providers.value[providerId].apiKey = value
},
})
const baseUrl = computed({
get: () => providers.value[providerId]?.baseUrl || '',
set: (value) => {
if (providers.value[providerId])
providers.value[providerId].baseUrl = value
},
})
const model = computed({
get: () => providers.value[providerId]?.model || 'tts-1',
set: (value) => {
if (providers.value[providerId])
providers.value[providerId].model = value
},
})
const voice = computed({
get: () => providers.value[providerId]?.voice || 'alloy',
set: (value) => {
if (providers.value[providerId])
providers.value[providerId].voice = value
},
})
const speed = ref<number>(1.0)
// Check if API key is configured
const apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey)
// Generate speech with specific parameters
async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean, modelId?: string) {
const provider = await providersStore.getProviderInstance<SpeechProvider<string>>(providerId)
if (!provider)
throw new Error('Failed to initialize speech provider')
const providerConfig = providersStore.getProviderConfig(providerId)
return await speechStore.speech(
provider,
modelId || model.value,
input,
voiceId || voice.value,
{
...providerConfig,
...defaultVoiceSettings,
speed: speed.value,
},
)
}
// Use the composable to get validation logic and state
const {
t,
router,
providerMetadata,
isValidating,
isValid,
validationMessage,
handleResetSettings,
} = useProviderValidation(providerId)
</script>
<template>
<ProviderSettingsLayout
:provider-name="providerMetadata?.localizedName"
:provider-icon-color="providerMetadata?.iconColor"
:on-back="() => router.back()"
>
<ProviderSettingsContainer>
<ProviderBasicSettings
:title="t('settings.pages.providers.common.section.basic.title')"
:description="t('settings.pages.providers.common.section.basic.description')"
:on-reset="handleResetSettings"
>
<ProviderApiKeyInput
v-model="apiKey"
:required="false"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
</ProviderBasicSettings>
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
<ProviderBaseUrlInput
v-model="baseUrl"
:placeholder="providerMetadata?.defaultOptions?.().baseUrl as string || 'https://api.cometapi.com/v1/'"
/>
<FieldRange
v-model="speed"
:label="t('settings.pages.providers.provider.common.fields.field.speed.label')"
:description="t('settings.pages.providers.provider.common.fields.field.speed.description')"
:min="0.5"
:max="2.0" :step="0.01"
/>
</ProviderAdvancedSettings>
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
{{ validationMessage }}
</div>
</template>
</Alert>
<Alert v-if="isValid && isValidating === 0" type="success">
<template #title>
{{ t('settings.dialogs.onboarding.validationSuccess') }}
</template>
</Alert>
</ProviderSettingsContainer>
<SpeechPlaygroundOpenAICompatible
v-model:model-value="model"
v-model:voice="voice"
:generate-speech="handleGenerateSpeech"
:api-key-configured="apiKeyConfigured"
default-text="Hello! This is a test of the Comet API Speech."
/>
</ProviderSettingsLayout>
</template>
<route lang="yaml">
meta:
layout: settings
stageTransition:
name: slide
</route>
@@ -0,0 +1,145 @@
<script setup lang="ts">
import type { RemovableRef } from '@vueuse/core'
import type { TranscriptionProvider } from '@xsai-ext/shared-providers'
import {
Alert,
ProviderAdvancedSettings,
ProviderApiKeyInput,
ProviderBaseUrlInput,
ProviderBasicSettings,
ProviderSettingsContainer,
ProviderSettingsLayout,
TranscriptionPlayground,
} from '@proj-airi/stage-ui/components'
import { useProviderValidation } from '@proj-airi/stage-ui/composables/use-provider-validation'
import { useHearingStore } from '@proj-airi/stage-ui/stores/modules/hearing'
import { useProvidersStore } from '@proj-airi/stage-ui/stores/providers'
import { FieldInput } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
const providerId = 'comet-api-transcription'
const hearingStore = useHearingStore()
const providersStore = useProvidersStore()
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
// Define computed properties for credentials
const apiKey = computed({
get: () => providers.value[providerId]?.apiKey || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].apiKey = value
},
})
const baseUrl = computed({
get: () => providers.value[providerId]?.baseUrl || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].baseUrl = value
},
})
const model = computed({
get: () => providers.value[providerId]?.model || '',
set: (value) => {
if (!providers.value[providerId])
providers.value[providerId] = {}
providers.value[providerId].model = value
},
})
// Check if API key is configured
const apiKeyConfigured = computed(() => !!providers.value[providerId]?.apiKey)
// Generate transcription
async function handleGenerateTranscription(file: File) {
const provider = await providersStore.getProviderInstance<TranscriptionProvider<string>>(providerId)
if (!provider)
throw new Error('Failed to initialize transcription provider')
return await hearingStore.transcription(
provider,
model.value,
file,
'json',
)
}
// Use the composable to get validation logic and state
const {
t,
router,
providerMetadata,
isValidating,
isValid,
validationMessage,
handleResetSettings,
} = useProviderValidation(providerId)
</script>
<template>
<ProviderSettingsLayout
:provider-name="providerMetadata?.localizedName"
:provider-icon-color="providerMetadata?.iconColor"
:on-back="() => router.back()"
>
<ProviderSettingsContainer>
<ProviderBasicSettings
:title="t('settings.pages.providers.common.section.basic.title')"
:description="t('settings.pages.providers.common.section.basic.description')"
:on-reset="handleResetSettings"
>
<ProviderApiKeyInput
v-model="apiKey"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
<FieldInput
v-model="model"
:label="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.manual_model_name')"
:placeholder="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.manual_model_placeholder')"
/>
</ProviderBasicSettings>
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
<ProviderBaseUrlInput
v-model="baseUrl"
:placeholder="providerMetadata?.defaultOptions?.().baseUrl as string || 'https://api.cometapi.com/v1/'"
/>
</ProviderAdvancedSettings>
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
{{ validationMessage }}
</div>
</template>
</Alert>
<Alert v-if="isValid && isValidating === 0" type="success">
<template #title>
{{ t('settings.dialogs.onboarding.validationSuccess') }}
</template>
</Alert>
</ProviderSettingsContainer>
<TranscriptionPlayground
:generate-transcription="handleGenerateTranscription"
:api-key-configured="apiKeyConfigured"
/>
</ProviderSettingsLayout>
</template>
<route lang="yaml">
meta:
layout: settings
stageTransition:
name: slide
</route>
+48
View File
@@ -39,6 +39,8 @@ import {
createEmbedProvider,
createMetadataProvider,
createModelProvider,
createSpeechProvider,
createTranscriptionProvider,
merge,
} from '@xsai-ext/shared-providers'
import { listModels } from '@xsai/model'
@@ -1250,6 +1252,38 @@ export const useProvidersStore = defineStore('providers', () => {
},
},
},
'comet-api-speech': buildOpenAICompatibleProvider({
id: 'comet-api-speech',
name: 'CometAPI Speech',
nameKey: 'settings.pages.providers.provider.comet-api.title',
descriptionKey: 'settings.pages.providers.provider.comet-api.description',
icon: 'i-lobe-icons:cometapi',
description: 'cometapi.com',
category: 'speech',
tasks: ['text-to-speech'],
defaultBaseUrl: 'https://api.cometapi.com/v1/',
creator: (apiKey, baseURL = 'https://api.cometapi.com/v1/') => merge(
createModelProvider({ apiKey, baseURL }),
createSpeechProvider({ apiKey, baseURL }),
),
validation: ['model_list'],
}),
'comet-api-transcription': buildOpenAICompatibleProvider({
id: 'comet-api-transcription',
name: 'CometAPI Transcription',
nameKey: 'settings.pages.providers.provider.comet-api.title',
descriptionKey: 'settings.pages.providers.provider.comet-api.description',
icon: 'i-lobe-icons:cometapi',
description: 'cometapi.com',
category: 'transcription',
tasks: ['speech-to-text', 'automatic-speech-recognition', 'asr', 'stt'],
defaultBaseUrl: 'https://api.cometapi.com/v1/',
creator: (apiKey, baseURL = 'https://api.cometapi.com/v1/') => merge(
createModelProvider({ apiKey, baseURL }),
createTranscriptionProvider({ apiKey, baseURL }),
),
validation: ['model_list'],
}),
'together-ai': buildOpenAICompatibleProvider({
id: 'together-ai',
name: 'Together.ai',
@@ -1482,6 +1516,20 @@ export const useProvidersStore = defineStore('providers', () => {
},
},
},
'comet-api': buildOpenAICompatibleProvider({
id: 'comet-api',
name: 'CometAPI',
nameKey: 'settings.pages.providers.provider.comet-api.title',
descriptionKey: 'settings.pages.providers.provider.comet-api.description',
icon: 'i-lobe-icons:cometapi',
description: 'cometapi.com',
defaultBaseUrl: 'https://api.cometapi.com/v1/',
creator: (apiKey, baseURL = 'https://api.cometapi.com/v1/') => merge(
createChatProvider({ apiKey, baseURL }),
createModelProvider({ apiKey, baseURL }),
),
validation: ['model_list'],
}),
'perplexity-ai': buildOpenAICompatibleProvider({
id: 'perplexity-ai',
name: 'Perplexity',