chore(stage-ui,stage-app,stage-tamagotchi): prepare for pure local STT/TTS
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import type { RemovableRef } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
ProviderAdvancedSettings,
|
||||
ProviderApiKeyInput,
|
||||
ProviderBaseUrlInput,
|
||||
ProviderBasicSettings,
|
||||
ProviderSettingsContainer,
|
||||
ProviderSettingsLayout,
|
||||
} from '@proj-airi/stage-ui/components'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const providersStore = useProvidersStore()
|
||||
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
|
||||
|
||||
// Get provider metadata
|
||||
const providerId = 'openai'
|
||||
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
|
||||
|
||||
// Use computed properties for settings
|
||||
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 || 'https://api.openai.com/v1/',
|
||||
set: (value) => {
|
||||
if (!providers.value[providerId])
|
||||
providers.value[providerId] = {}
|
||||
|
||||
providers.value[providerId].baseUrl = value
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize provider if it doesn't exist
|
||||
if (!providers.value[providerId]) {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize refs with current values
|
||||
apiKey.value = providers.value[providerId]?.apiKey || ''
|
||||
baseUrl.value = providers.value[providerId]?.baseUrl || 'https://api.openai.com/v1/'
|
||||
})
|
||||
|
||||
// Watch settings and update the provider configuration
|
||||
watch([apiKey, baseUrl], () => {
|
||||
providers.value[providerId] = {
|
||||
...providers.value[providerId],
|
||||
apiKey: apiKey.value,
|
||||
baseUrl: baseUrl.value || 'https://api.openai.com/v1/',
|
||||
}
|
||||
})
|
||||
|
||||
function handleResetSettings() {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProviderSettingsLayout
|
||||
:provider-name="providerMetadata?.localizedName || 'OpenAI'"
|
||||
:provider-icon="providerMetadata?.icon"
|
||||
: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 || 'OpenAI'"
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</ProviderBasicSettings>
|
||||
|
||||
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
|
||||
<ProviderBaseUrlInput
|
||||
v-model="baseUrl"
|
||||
placeholder="https://api.openai.com/v1/"
|
||||
/>
|
||||
</ProviderAdvancedSettings>
|
||||
</ProviderSettingsContainer>
|
||||
</ProviderSettingsLayout>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: settings
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import type { RemovableRef } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
ProviderAdvancedSettings,
|
||||
ProviderApiKeyInput,
|
||||
ProviderBaseUrlInput,
|
||||
ProviderBasicSettings,
|
||||
ProviderSettingsContainer,
|
||||
ProviderSettingsLayout,
|
||||
} from '@proj-airi/stage-ui/components'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const providersStore = useProvidersStore()
|
||||
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
|
||||
|
||||
// Get provider metadata
|
||||
const providerId = 'openai'
|
||||
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
|
||||
|
||||
// Use computed properties for settings
|
||||
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 || 'https://api.openai.com/v1/',
|
||||
set: (value) => {
|
||||
if (!providers.value[providerId])
|
||||
providers.value[providerId] = {}
|
||||
|
||||
providers.value[providerId].baseUrl = value
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize provider if it doesn't exist
|
||||
if (!providers.value[providerId]) {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize refs with current values
|
||||
apiKey.value = providers.value[providerId]?.apiKey || ''
|
||||
baseUrl.value = providers.value[providerId]?.baseUrl || 'https://api.openai.com/v1/'
|
||||
})
|
||||
|
||||
// Watch settings and update the provider configuration
|
||||
watch([apiKey, baseUrl], () => {
|
||||
providers.value[providerId] = {
|
||||
...providers.value[providerId],
|
||||
apiKey: apiKey.value,
|
||||
baseUrl: baseUrl.value || 'https://api.openai.com/v1/',
|
||||
}
|
||||
})
|
||||
|
||||
function handleResetSettings() {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProviderSettingsLayout
|
||||
:provider-name="providerMetadata?.localizedName || 'OpenAI'"
|
||||
:provider-icon="providerMetadata?.icon"
|
||||
: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 || 'OpenAI'"
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</ProviderBasicSettings>
|
||||
|
||||
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
|
||||
<ProviderBaseUrlInput
|
||||
v-model="baseUrl"
|
||||
placeholder="https://api.openai.com/v1/"
|
||||
/>
|
||||
</ProviderAdvancedSettings>
|
||||
</ProviderSettingsContainer>
|
||||
</ProviderSettingsLayout>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: settings
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import type { RemovableRef } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
ProviderAdvancedSettings,
|
||||
ProviderApiKeyInput,
|
||||
ProviderBaseUrlInput,
|
||||
ProviderBasicSettings,
|
||||
ProviderSettingsContainer,
|
||||
ProviderSettingsLayout,
|
||||
} from '@proj-airi/stage-ui/components'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const providersStore = useProvidersStore()
|
||||
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
|
||||
|
||||
// Get provider metadata
|
||||
const providerId = 'openai'
|
||||
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
|
||||
|
||||
// Use computed properties for settings
|
||||
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 || 'https://api.openai.com/v1/',
|
||||
set: (value) => {
|
||||
if (!providers.value[providerId])
|
||||
providers.value[providerId] = {}
|
||||
|
||||
providers.value[providerId].baseUrl = value
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize provider if it doesn't exist
|
||||
if (!providers.value[providerId]) {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize refs with current values
|
||||
apiKey.value = providers.value[providerId]?.apiKey || ''
|
||||
baseUrl.value = providers.value[providerId]?.baseUrl || 'https://api.openai.com/v1/'
|
||||
})
|
||||
|
||||
// Watch settings and update the provider configuration
|
||||
watch([apiKey, baseUrl], () => {
|
||||
providers.value[providerId] = {
|
||||
...providers.value[providerId],
|
||||
apiKey: apiKey.value,
|
||||
baseUrl: baseUrl.value || 'https://api.openai.com/v1/',
|
||||
}
|
||||
})
|
||||
|
||||
function handleResetSettings() {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProviderSettingsLayout
|
||||
:provider-name="providerMetadata?.localizedName || 'OpenAI'"
|
||||
:provider-icon="providerMetadata?.icon"
|
||||
: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 || 'OpenAI'"
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</ProviderBasicSettings>
|
||||
|
||||
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
|
||||
<ProviderBaseUrlInput
|
||||
v-model="baseUrl"
|
||||
placeholder="https://api.openai.com/v1/"
|
||||
/>
|
||||
</ProviderAdvancedSettings>
|
||||
</ProviderSettingsContainer>
|
||||
</ProviderSettingsLayout>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: settings
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import type { RemovableRef } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
ProviderAdvancedSettings,
|
||||
ProviderApiKeyInput,
|
||||
ProviderBaseUrlInput,
|
||||
ProviderBasicSettings,
|
||||
ProviderSettingsContainer,
|
||||
ProviderSettingsLayout,
|
||||
} from '@proj-airi/stage-ui/components'
|
||||
import { useProvidersStore } from '@proj-airi/stage-ui/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const providersStore = useProvidersStore()
|
||||
const { providers } = storeToRefs(providersStore) as { providers: RemovableRef<Record<string, any>> }
|
||||
|
||||
// Get provider metadata
|
||||
const providerId = 'openai'
|
||||
const providerMetadata = computed(() => providersStore.getProviderMetadata(providerId))
|
||||
|
||||
// Use computed properties for settings
|
||||
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 || 'https://api.openai.com/v1/',
|
||||
set: (value) => {
|
||||
if (!providers.value[providerId])
|
||||
providers.value[providerId] = {}
|
||||
|
||||
providers.value[providerId].baseUrl = value
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize provider if it doesn't exist
|
||||
if (!providers.value[providerId]) {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize refs with current values
|
||||
apiKey.value = providers.value[providerId]?.apiKey || ''
|
||||
baseUrl.value = providers.value[providerId]?.baseUrl || 'https://api.openai.com/v1/'
|
||||
})
|
||||
|
||||
// Watch settings and update the provider configuration
|
||||
watch([apiKey, baseUrl], () => {
|
||||
providers.value[providerId] = {
|
||||
...providers.value[providerId],
|
||||
apiKey: apiKey.value,
|
||||
baseUrl: baseUrl.value || 'https://api.openai.com/v1/',
|
||||
}
|
||||
})
|
||||
|
||||
function handleResetSettings() {
|
||||
providers.value[providerId] = {
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProviderSettingsLayout
|
||||
:provider-name="providerMetadata?.localizedName || 'OpenAI'"
|
||||
:provider-icon="providerMetadata?.icon"
|
||||
: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 || 'OpenAI'"
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</ProviderBasicSettings>
|
||||
|
||||
<ProviderAdvancedSettings :title="t('settings.pages.providers.common.section.advanced.title')">
|
||||
<ProviderBaseUrlInput
|
||||
v-model="baseUrl"
|
||||
placeholder="https://api.openai.com/v1/"
|
||||
/>
|
||||
</ProviderAdvancedSettings>
|
||||
</ProviderSettingsContainer>
|
||||
</ProviderSettingsLayout>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
meta:
|
||||
layout: settings
|
||||
stageTransition:
|
||||
name: slide
|
||||
</route>
|
||||
@@ -88,6 +88,7 @@ words:
|
||||
- iconify
|
||||
- intlify
|
||||
- Kawaii
|
||||
- kebabcase
|
||||
- kwaa
|
||||
- lemonnekogh
|
||||
- libsodium
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"@xsai-ext/providers-local": "catalog:",
|
||||
"@xsai-ext/shared-providers": "catalog:",
|
||||
"@xsai-transformers/embed": "^0.0.6",
|
||||
"@xsai-transformers/shared": "^0.0.6",
|
||||
"@xsai/embed": "catalog:",
|
||||
"@xsai/generate-speech": "catalog:",
|
||||
"@xsai/model": "catalog:",
|
||||
@@ -120,6 +121,7 @@
|
||||
"@xsai/tool": "catalog:",
|
||||
"culori": "^4.0.1",
|
||||
"defu": "^6.1.4",
|
||||
"gpuu": "^1.0.3",
|
||||
"jszip": "^3.10.1",
|
||||
"localforage": "^1.10.0",
|
||||
"pinia": "^3.0.3",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ChatProvider, ChatProviderWithExtraOptions, EmbedProvider, EmbedProviderWithExtraOptions, SpeechProvider, SpeechProviderWithExtraOptions, TranscriptionProvider, TranscriptionProviderWithExtraOptions } from '@xsai-ext/shared-providers'
|
||||
import type { ProgressInfo } from '@xsai-transformers/shared/types'
|
||||
import type {
|
||||
UnAlibabaCloudOptions,
|
||||
UnElevenLabsOptions,
|
||||
@@ -7,7 +8,7 @@ import type {
|
||||
VoiceProviderWithExtraOptions,
|
||||
} from 'unspeech'
|
||||
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import { computedAsync, useLocalStorage } from '@vueuse/core'
|
||||
import {
|
||||
createAnthropic,
|
||||
createDeepSeek,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
} from '@xsai-ext/providers-cloud'
|
||||
import { createOllama, createPlayer2 } from '@xsai-ext/providers-local'
|
||||
import { listModels } from '@xsai/model'
|
||||
import { isWebGPUSupported } from 'gpuu/webgpu'
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
createUnAlibabaCloud,
|
||||
@@ -40,16 +42,38 @@ import { models as elevenLabsModels } from './providers/elevenlabs/list-models'
|
||||
|
||||
export interface ProviderMetadata {
|
||||
id: string
|
||||
order?: number
|
||||
category: 'chat' | 'embed' | 'speech' | 'transcription'
|
||||
tasks: string[]
|
||||
nameKey: string // i18n key for provider name
|
||||
name: string // Default name (fallback)
|
||||
descriptionKey: string // i18n key for description
|
||||
description: string // Default description (fallback)
|
||||
/**
|
||||
* Indicates whether the provider is available.
|
||||
* If not specified, the provider is always available.
|
||||
*
|
||||
* May be specified when any of the following criteria is required:
|
||||
*
|
||||
* Platform requirements:
|
||||
*
|
||||
* - app-* providers are only available on desktop, this is responsible for Tauri runtime checks
|
||||
* - web-* providers are only available on web, this means Node.js and Tauri should not be imported or used
|
||||
*
|
||||
* System spec requirements:
|
||||
*
|
||||
* - may requires WebGPU / NVIDIA / other types of GPU,
|
||||
* on Web, WebGPU will automatically compiled to use targeting GPU hardware
|
||||
* - may requires significant amount of GPU memory to run, especially for
|
||||
* using of small language models within browser or Tauri app
|
||||
* - may requires significant amount of memory to run, especially for those
|
||||
* non-WebGPU supported environments.
|
||||
*/
|
||||
isAvailableBy?: () => Promise<boolean> | boolean
|
||||
icon?: string
|
||||
iconColor?: string
|
||||
iconImage?: string
|
||||
defaultOptions?: Record<string, unknown>
|
||||
defaultOptions?: () => Record<string, unknown>
|
||||
createProvider: (config: Record<string, unknown>) =>
|
||||
| ChatProvider
|
||||
| ChatProviderWithExtraOptions
|
||||
@@ -62,6 +86,7 @@ export interface ProviderMetadata {
|
||||
capabilities: {
|
||||
listModels?: (config: Record<string, unknown>) => Promise<ModelInfo[]>
|
||||
listVoices?: (config: Record<string, unknown>) => Promise<VoiceInfo[]>
|
||||
loadModel?: (config: Record<string, unknown>, hooks?: { onProgress?: (progress: ProgressInfo) => Promise<void> | void }) => Promise<void>
|
||||
}
|
||||
validators: {
|
||||
validateProviderConfig: (config: Record<string, unknown>) => Promise<boolean> | boolean
|
||||
@@ -138,9 +163,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.openrouter.description',
|
||||
description: 'openrouter.ai',
|
||||
icon: 'i-lobe-icons:openrouter',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://openrouter.ai/api/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createOpenRouter((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -153,6 +178,212 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
'app-local-audio-speech': {
|
||||
id: 'app-local-audio-speech',
|
||||
category: 'speech',
|
||||
tasks: ['text-to-speech', 'tts'],
|
||||
isAvailableBy: async () => {
|
||||
if ('window' in globalThis && globalThis.window != null) {
|
||||
if ('__TAURI__' in globalThis.window && globalThis.window.__TAURI__ != null) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
nameKey: 'settings.pages.providers.provider.app-local-audio-speech.title',
|
||||
name: 'App (Local)',
|
||||
descriptionKey: 'settings.pages.providers.provider.app-local-audio-speech.description',
|
||||
description: 'https://github.com/huggingface/candle',
|
||||
icon: 'i-lobe-icons:huggingface',
|
||||
defaultOptions: () => ({}),
|
||||
createProvider: config => createOpenAI((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
return (await listModels({
|
||||
...createOpenAI((config.baseUrl as string).trim()).model(),
|
||||
})).map((model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
provider: 'app-local-candle',
|
||||
description: '',
|
||||
contextLength: 0,
|
||||
deprecated: false,
|
||||
} satisfies ModelInfo
|
||||
})
|
||||
},
|
||||
},
|
||||
validators: {
|
||||
validateProviderConfig: (config) => {
|
||||
if (!config.baseUrl)
|
||||
return false
|
||||
|
||||
// Check if the Ollama server is reachable
|
||||
return fetch(`${(config.baseUrl as string).trim()}models`)
|
||||
.then(response => response.ok)
|
||||
.catch(() => false)
|
||||
},
|
||||
},
|
||||
},
|
||||
'app-local-audio-transcription': {
|
||||
id: 'app-local-audio-transcription',
|
||||
category: 'transcription',
|
||||
tasks: ['speech-to-text', 'automatic-speech-recognition', 'asr', 'stt'],
|
||||
isAvailableBy: async () => {
|
||||
if ('window' in globalThis && globalThis.window != null) {
|
||||
if ('__TAURI__' in globalThis.window && globalThis.window.__TAURI__ != null) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
nameKey: 'settings.pages.providers.provider.app-local-audio-transcription.title',
|
||||
name: 'App (Local)',
|
||||
descriptionKey: 'settings.pages.providers.provider.app-local-audio-transcription.description',
|
||||
description: 'https://github.com/huggingface/candle',
|
||||
icon: 'i-lobe-icons:huggingface',
|
||||
defaultOptions: () => ({}),
|
||||
createProvider: config => createOpenAI((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
return (await listModels({
|
||||
...createOpenAI((config.baseUrl as string).trim()).model(),
|
||||
})).map((model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
provider: 'app-local-candle',
|
||||
description: '',
|
||||
contextLength: 0,
|
||||
deprecated: false,
|
||||
} satisfies ModelInfo
|
||||
})
|
||||
},
|
||||
},
|
||||
validators: {
|
||||
validateProviderConfig: (config) => {
|
||||
if (!config.baseUrl)
|
||||
return false
|
||||
|
||||
// Check if the Ollama server is reachable
|
||||
return fetch(`${(config.baseUrl as string).trim()}models`)
|
||||
.then(response => response.ok)
|
||||
.catch(() => false)
|
||||
},
|
||||
},
|
||||
},
|
||||
'browser-local-audio-speech': {
|
||||
id: 'browser-local-audio-speech',
|
||||
category: 'speech',
|
||||
tasks: ['text-to-speech', 'tts'],
|
||||
isAvailableBy: async () => {
|
||||
const webGPUAvailable = await isWebGPUSupported()
|
||||
if (webGPUAvailable) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ('navigator' in globalThis && globalThis.navigator != null && 'deviceMemory' in globalThis.navigator && typeof globalThis.navigator.deviceMemory === 'number') {
|
||||
const memory = globalThis.navigator.deviceMemory
|
||||
// Check if the device has at least 8GB of RAM
|
||||
if (memory >= 8) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
nameKey: 'settings.pages.providers.provider.browser-local-audio-speech.title',
|
||||
name: 'Browser (Local)',
|
||||
descriptionKey: 'settings.pages.providers.provider.browser-local-audio-speech.description',
|
||||
description: 'https://github.com/moeru-ai/xsai-transformers',
|
||||
icon: 'i-lobe-icons:huggingface',
|
||||
defaultOptions: () => ({}),
|
||||
createProvider: config => createOpenAI((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
return (await listModels({
|
||||
...createOpenAI((config.baseUrl as string).trim()).model(),
|
||||
})).map((model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
provider: 'browser-local-transformers',
|
||||
description: '',
|
||||
contextLength: 0,
|
||||
deprecated: false,
|
||||
} satisfies ModelInfo
|
||||
})
|
||||
},
|
||||
},
|
||||
validators: {
|
||||
validateProviderConfig: (config) => {
|
||||
if (!config.baseUrl)
|
||||
return false
|
||||
|
||||
// Check if the Ollama server is reachable
|
||||
return fetch(`${(config.baseUrl as string).trim()}models`)
|
||||
.then(response => response.ok)
|
||||
.catch(() => false)
|
||||
},
|
||||
},
|
||||
},
|
||||
'browser-local-audio-transcription': {
|
||||
id: 'browser-local-audio-transcription',
|
||||
category: 'transcription',
|
||||
tasks: ['speech-to-text', 'automatic-speech-recognition', 'asr', 'stt'],
|
||||
isAvailableBy: async () => {
|
||||
const webGPUAvailable = await isWebGPUSupported()
|
||||
if (webGPUAvailable) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ('navigator' in globalThis && globalThis.navigator != null && 'deviceMemory' in globalThis.navigator && typeof globalThis.navigator.deviceMemory === 'number') {
|
||||
const memory = globalThis.navigator.deviceMemory
|
||||
// Check if the device has at least 8GB of RAM
|
||||
if (memory >= 8) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
nameKey: 'settings.pages.providers.provider.browser-local-audio-transcription.title',
|
||||
name: 'Browser (Local)',
|
||||
descriptionKey: 'settings.pages.providers.provider.browser-local-audio-transcription.description',
|
||||
description: 'https://github.com/moeru-ai/xsai-transformers',
|
||||
icon: 'i-lobe-icons:huggingface',
|
||||
defaultOptions: () => ({}),
|
||||
createProvider: config => createOpenAI((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
return (await listModels({
|
||||
...createOpenAI((config.baseUrl as string).trim()).model(),
|
||||
})).map((model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
provider: 'browser-local-transformers',
|
||||
description: '',
|
||||
contextLength: 0,
|
||||
deprecated: false,
|
||||
} satisfies ModelInfo
|
||||
})
|
||||
},
|
||||
},
|
||||
validators: {
|
||||
validateProviderConfig: (config) => {
|
||||
if (!config.baseUrl)
|
||||
return false
|
||||
|
||||
// Check if the Ollama server is reachable
|
||||
return fetch(`${(config.baseUrl as string).trim()}models`)
|
||||
.then(response => response.ok)
|
||||
.catch(() => false)
|
||||
},
|
||||
},
|
||||
},
|
||||
'ollama': {
|
||||
id: 'ollama',
|
||||
category: 'chat',
|
||||
@@ -162,9 +393,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.ollama.description',
|
||||
description: 'ollama.com',
|
||||
icon: 'i-lobe-icons:ollama',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'http://localhost:11434/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createOllama((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -203,9 +434,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.ollama.description',
|
||||
description: 'ollama.com',
|
||||
icon: 'i-lobe-icons:ollama',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'http://localhost:11434/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createOllama((config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -302,9 +533,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.openai.description',
|
||||
description: 'openai.com',
|
||||
icon: 'i-lobe-icons:openai',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -337,9 +568,44 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.openai.description',
|
||||
description: 'openai.com',
|
||||
icon: 'i-lobe-icons:openai',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}),
|
||||
createProvider: config => createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
return (await listModels({
|
||||
...createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()).model(),
|
||||
})).map((model) => {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
provider: 'openai',
|
||||
description: '',
|
||||
contextLength: 0,
|
||||
deprecated: false,
|
||||
} satisfies ModelInfo
|
||||
})
|
||||
},
|
||||
},
|
||||
validators: {
|
||||
validateProviderConfig: (config) => {
|
||||
return !!config.apiKey && !!config.baseUrl
|
||||
},
|
||||
},
|
||||
},
|
||||
'openai-audio-transcription': {
|
||||
id: 'openai-audio-transcription',
|
||||
category: 'transcription',
|
||||
tasks: ['speech-to-text', 'automatic-speech-recognition', 'asr', 'stt'],
|
||||
nameKey: 'settings.pages.providers.provider.openai.title',
|
||||
name: 'OpenAI',
|
||||
descriptionKey: 'settings.pages.providers.provider.openai.description',
|
||||
description: 'openai.com',
|
||||
icon: 'i-lobe-icons:openai',
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.openai.com/v1/',
|
||||
}),
|
||||
createProvider: config => createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -372,9 +638,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.anthropic.description',
|
||||
description: 'anthropic.com',
|
||||
icon: 'i-lobe-icons:anthropic',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.anthropic.com/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createAnthropic((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async () => {
|
||||
@@ -445,9 +711,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.google-generative-ai.description',
|
||||
description: 'ai.google.dev',
|
||||
icon: 'i-lobe-icons:gemini',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createGoogleGenerativeAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -504,9 +770,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
},
|
||||
},
|
||||
'deepseek': {
|
||||
id: 'deepseek',
|
||||
category: 'chat',
|
||||
tasks: ['text-generation'],
|
||||
id: 'deepseek',
|
||||
nameKey: 'settings.pages.providers.provider.deepseek.title',
|
||||
name: 'DeepSeek',
|
||||
descriptionKey: 'settings.pages.providers.provider.deepseek.description',
|
||||
@@ -544,13 +810,13 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.elevenlabs.description',
|
||||
description: 'elevenlabs.io',
|
||||
icon: 'i-simple-icons:elevenlabs',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://unspeech.hyp3r.link/v1/',
|
||||
voiceSettings: {
|
||||
similarityBoost: 0.75,
|
||||
stability: 0.5,
|
||||
},
|
||||
},
|
||||
}),
|
||||
createProvider: config => createUnElevenLabs((config.apiKey as string).trim(), (config.baseUrl as string).trim()) as SpeechProviderWithExtraOptions<string, UnElevenLabsOptions>,
|
||||
capabilities: {
|
||||
listModels: async () => {
|
||||
@@ -615,9 +881,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.microsoft-speech.description',
|
||||
description: 'speech.microsoft.com',
|
||||
iconColor: 'i-lobe-icons:microsoft',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://unspeech.hyp3r.link/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createUnMicrosoft((config.apiKey as string).trim(), (config.baseUrl as string).trim()) as SpeechProviderWithExtraOptions<string, UnMicrosoftOptions>,
|
||||
capabilities: {
|
||||
listModels: async () => {
|
||||
@@ -666,9 +932,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.alibaba-cloud-model-studio.description',
|
||||
description: 'bailian.console.aliyun.com',
|
||||
iconColor: 'i-lobe-icons:alibabacloud',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://unspeech.hyp3r.link/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createUnAlibabaCloud((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listVoices: async (config) => {
|
||||
@@ -725,9 +991,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.volcengine.description',
|
||||
description: 'volcengine.com',
|
||||
iconColor: 'i-lobe-icons:volcengine',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://unspeech.hyp3r.link/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createUnVolcengine((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listVoices: async (config) => {
|
||||
@@ -872,9 +1138,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.featherless.description',
|
||||
description: 'featherless.ai',
|
||||
icon: 'i-lobe-icons:featherless-ai',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.featherless.ai/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createOpenAI((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async (config) => {
|
||||
@@ -906,9 +1172,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
name: 'Player2 API',
|
||||
descriptionKey: 'settings.pages.providers.provider.player2.description',
|
||||
description: 'player2.game',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'http://localhost:4315/v1/',
|
||||
},
|
||||
}),
|
||||
createProvider: (config) => {
|
||||
return createPlayer2((config.baseUrl as string).trim())
|
||||
},
|
||||
@@ -963,9 +1229,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
descriptionKey: 'settings.pages.providers.provider.perplexity.description',
|
||||
description: 'perplexity.ai',
|
||||
icon: 'i-lobe-icons:perplexity',
|
||||
defaultOptions: {
|
||||
defaultOptions: () => ({
|
||||
baseUrl: 'https://api.perplexity.ai',
|
||||
},
|
||||
}),
|
||||
createProvider: config => createPerplexity((config.apiKey as string).trim(), (config.baseUrl as string).trim()),
|
||||
capabilities: {
|
||||
listModels: async () => {
|
||||
@@ -1100,8 +1366,9 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
function initializeProvider(providerId: string) {
|
||||
if (!providerCredentials.value[providerId]) {
|
||||
const metadata = providerMetadata[providerId]
|
||||
const defaultOptions = metadata.defaultOptions?.() || {}
|
||||
providerCredentials.value[providerId] = {
|
||||
baseUrl: metadata.defaultOptions?.baseUrl || '',
|
||||
baseUrl: defaultOptions.baseUrl || '',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1236,20 +1503,32 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const availableProvidersMetadata = computed(() => {
|
||||
return availableProviders.value.map(id => getProviderMetadata(id))
|
||||
})
|
||||
const availableProvidersMetadata = computedAsync<ProviderMetadata[]>(async () => {
|
||||
const providers: ProviderMetadata[] = []
|
||||
|
||||
for (const provider of allProvidersMetadata.value) {
|
||||
const p = getProviderMetadata(provider.id)
|
||||
const isAvailableBy = p.isAvailableBy || (() => true)
|
||||
|
||||
const isAvailable = await isAvailableBy()
|
||||
if (isAvailable) {
|
||||
providers.push(provider)
|
||||
}
|
||||
}
|
||||
|
||||
return providers
|
||||
}, [])
|
||||
|
||||
const allChatProvidersMetadata = computed(() => {
|
||||
return allProvidersMetadata.value.filter(metadata => metadata.category === 'chat')
|
||||
return availableProvidersMetadata.value.filter(metadata => metadata.category === 'chat')
|
||||
})
|
||||
|
||||
const allAudioSpeechProvidersMetadata = computed(() => {
|
||||
return allProvidersMetadata.value.filter(metadata => metadata.category === 'speech')
|
||||
return availableProvidersMetadata.value.filter(metadata => metadata.category === 'speech')
|
||||
})
|
||||
|
||||
const allAudioTranscriptionProvidersMetadata = computed(() => {
|
||||
return allProvidersMetadata.value.filter(metadata => metadata.category === 'transcription')
|
||||
return availableProvidersMetadata.value.filter(metadata => metadata.category === 'transcription')
|
||||
})
|
||||
|
||||
const configuredChatProvidersMetadata = computed(() => {
|
||||
|
||||
Generated
+6
@@ -1009,6 +1009,9 @@ importers:
|
||||
'@xsai-transformers/embed':
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6(web-worker@1.5.0)
|
||||
'@xsai-transformers/shared':
|
||||
specifier: ^0.0.6
|
||||
version: 0.0.6
|
||||
'@xsai/embed':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.0-beta.4
|
||||
@@ -1033,6 +1036,9 @@ importers:
|
||||
defu:
|
||||
specifier: ^6.1.4
|
||||
version: 6.1.4
|
||||
gpuu:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
|
||||
Reference in New Issue
Block a user