feat(stage-ui): periodically check Ollama connectivity
This commit is contained in:
@@ -53,6 +53,10 @@ export const providerLmStudio = defineProvider<LMStudioConfig>({
|
|||||||
validators: {
|
validators: {
|
||||||
...createOpenAICompatibleValidators({
|
...createOpenAICompatibleValidators({
|
||||||
checks: ['connectivity', 'model_list'],
|
checks: ['connectivity', 'model_list'],
|
||||||
|
schedule: {
|
||||||
|
mode: 'interval',
|
||||||
|
intervalMs: 15_000,
|
||||||
|
},
|
||||||
connectivityFailureReason: ({ errorMessage }) =>
|
connectivityFailureReason: ({ errorMessage }) =>
|
||||||
`Failed to reach LM Studio server, error: ${errorMessage} occurred.\n\nMake sure LM Studio is running and the local server is started. You can start the local server in LM Studio by going to the 'Local Server' tab and clicking 'Start Server'.`,
|
`Failed to reach LM Studio server, error: ${errorMessage} occurred.\n\nMake sure LM Studio is running and the local server is started. You can start the local server in LM Studio by going to the 'Local Server' tab and clicking 'Start Server'.`,
|
||||||
modelListFailureReason: ({ errorMessage }) =>
|
modelListFailureReason: ({ errorMessage }) =>
|
||||||
|
|||||||
@@ -166,7 +166,11 @@ export const providerOllama = defineProvider<OllamaConfig>({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
validateProvider: createOpenAICompatibleValidators({
|
validateProvider: createOpenAICompatibleValidators({
|
||||||
checks: ['connectivity', 'model_list', 'chat_completions'],
|
checks: ['connectivity', 'model_list'],
|
||||||
|
schedule: {
|
||||||
|
mode: 'interval',
|
||||||
|
intervalMs: 15_000,
|
||||||
|
},
|
||||||
connectivityFailureReason: ({ errorMessage }) =>
|
connectivityFailureReason: ({ errorMessage }) =>
|
||||||
`Failed to reach Ollama server, error: ${errorMessage} occurred.\n\nIf you are using Ollama locally, this is likely the CORS (Cross-Origin Resource Sharing) security issue, where you will need to set OLLAMA_ORIGINS=* or OLLAMA_ORIGINS=https://airi.moeru.ai,http://localhost environment variable before launching Ollama server to make this work.`,
|
`Failed to reach Ollama server, error: ${errorMessage} occurred.\n\nIf you are using Ollama locally, this is likely the CORS (Cross-Origin Resource Sharing) security issue, where you will need to set OLLAMA_ORIGINS=* or OLLAMA_ORIGINS=https://airi.moeru.ai,http://localhost environment variable before launching Ollama server to make this work.`,
|
||||||
})!.validateProvider,
|
})!.validateProvider,
|
||||||
|
|||||||
@@ -48,6 +48,25 @@ export interface ProviderValidationResult {
|
|||||||
valid: boolean
|
valid: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProviderValidatorSchedule {
|
||||||
|
mode: 'once' | 'interval'
|
||||||
|
intervalMs?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProviderConfigValidator<TConfig> {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
validator: (config: TConfig, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult>
|
||||||
|
schedule?: ProviderValidatorSchedule
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProviderRuntimeValidator<TConfig> {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
validator: (config: TConfig, provider: ProviderInstance, providerExtra: ProviderExtraMethods<TConfig>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult>
|
||||||
|
schedule?: ProviderValidatorSchedule
|
||||||
|
}
|
||||||
|
|
||||||
export interface ModelInfo {
|
export interface ModelInfo {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -121,8 +140,8 @@ export interface ProviderDefinition<TConfig extends any = any> {
|
|||||||
extraMethods?: ProviderExtraMethods<TConfig>
|
extraMethods?: ProviderExtraMethods<TConfig>
|
||||||
validationRequiredWhen?: (config: TConfig) => boolean
|
validationRequiredWhen?: (config: TConfig) => boolean
|
||||||
validators?: {
|
validators?: {
|
||||||
validateConfig?: Array<(contextOptions: { t: ComposerTranslation }) => { id: string, name: string, validator: (config: TConfig, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>
|
validateConfig?: Array<(contextOptions: { t: ComposerTranslation }) => ProviderConfigValidator<TConfig>>
|
||||||
validateProvider?: Array<(contextOptions: { t: ComposerTranslation }) => { id: string, name: string, validator: (config: TConfig, provider: ProviderInstance, providerExtra: ProviderExtraMethods<TConfig>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>
|
validateProvider?: Array<(contextOptions: { t: ComposerTranslation }) => ProviderRuntimeValidator<TConfig>>
|
||||||
}
|
}
|
||||||
capabilities?: {
|
capabilities?: {
|
||||||
transcription?: {
|
transcription?: {
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ type OpenAICompatibleValidationCheck = 'connectivity' | 'model_list' | 'chat_com
|
|||||||
interface OpenAICompatibleValidationOptions<TConfig extends { apiKey?: string, baseUrl?: string }> {
|
interface OpenAICompatibleValidationOptions<TConfig extends { apiKey?: string, baseUrl?: string }> {
|
||||||
checks?: OpenAICompatibleValidationCheck[]
|
checks?: OpenAICompatibleValidationCheck[]
|
||||||
additionalHeaders?: Record<string, string>
|
additionalHeaders?: Record<string, string>
|
||||||
|
schedule?: {
|
||||||
|
mode: 'once' | 'interval'
|
||||||
|
intervalMs?: number
|
||||||
|
}
|
||||||
connectivityFailureReason?: (input: { config: TConfig, error: unknown, errorMessage: string }) => string
|
connectivityFailureReason?: (input: { config: TConfig, error: unknown, errorMessage: string }) => string
|
||||||
modelListFailureReason?: (input: { config: TConfig, error: unknown, errorMessage: string }) => string
|
modelListFailureReason?: (input: { config: TConfig, error: unknown, errorMessage: string }) => string
|
||||||
}
|
}
|
||||||
@@ -236,6 +240,7 @@ export function createOpenAICompatibleValidators<TConfig extends { apiKey?: stri
|
|||||||
validatorConfig.validateProvider?.push(({ t }) => ({
|
validatorConfig.validateProvider?.push(({ t }) => ({
|
||||||
id: 'openai-compatible:check-connectivity',
|
id: 'openai-compatible:check-connectivity',
|
||||||
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-connectivity.title'),
|
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-connectivity.title'),
|
||||||
|
schedule: options?.schedule,
|
||||||
validator: async (config, provider, providerExtra, contextOptions) => {
|
validator: async (config, provider, providerExtra, contextOptions) => {
|
||||||
const errors: Array<{ error: unknown }> = []
|
const errors: Array<{ error: unknown }> = []
|
||||||
const result = await getChatCheckResult(
|
const result = await getChatCheckResult(
|
||||||
@@ -266,6 +271,7 @@ export function createOpenAICompatibleValidators<TConfig extends { apiKey?: stri
|
|||||||
validatorConfig.validateProvider?.push(({ t }) => ({
|
validatorConfig.validateProvider?.push(({ t }) => ({
|
||||||
id: 'openai-compatible:check-chat-completions',
|
id: 'openai-compatible:check-chat-completions',
|
||||||
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-supports-chat-completion.title'),
|
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-supports-chat-completion.title'),
|
||||||
|
schedule: options?.schedule,
|
||||||
validator: async (config, provider, providerExtra, contextOptions) => {
|
validator: async (config, provider, providerExtra, contextOptions) => {
|
||||||
const errors: Array<{ error: unknown }> = []
|
const errors: Array<{ error: unknown }> = []
|
||||||
const result = await getChatCheckResult(
|
const result = await getChatCheckResult(
|
||||||
@@ -292,6 +298,7 @@ export function createOpenAICompatibleValidators<TConfig extends { apiKey?: stri
|
|||||||
validatorConfig.validateProvider?.push(({ t }) => ({
|
validatorConfig.validateProvider?.push(({ t }) => ({
|
||||||
id: 'openai-compatible:check-model-list',
|
id: 'openai-compatible:check-model-list',
|
||||||
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-supports-model-listing.title'),
|
name: t('settings.pages.providers.catalog.edit.validators.openai-compatible.check-supports-model-listing.title'),
|
||||||
|
schedule: options?.schedule,
|
||||||
validator: async (config, provider, providerExtra) => {
|
validator: async (config, provider, providerExtra) => {
|
||||||
const errors: Array<{ error: unknown }> = []
|
const errors: Array<{ error: unknown }> = []
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import type { MaybePromise } from 'clustr'
|
|
||||||
import type { ComposerTranslation } from 'vue-i18n'
|
import type { ComposerTranslation } from 'vue-i18n'
|
||||||
|
|
||||||
import type { ProviderDefinition, ProviderExtraMethods, ProviderInstance, ProviderValidationResult } from '../types'
|
import type {
|
||||||
|
ProviderConfigValidator,
|
||||||
|
ProviderDefinition,
|
||||||
|
ProviderExtraMethods,
|
||||||
|
ProviderInstance,
|
||||||
|
ProviderRuntimeValidator,
|
||||||
|
} from '../types'
|
||||||
|
|
||||||
import { errorMessageFrom, merge } from '@moeru/std'
|
import { errorMessageFrom, merge } from '@moeru/std'
|
||||||
|
|
||||||
@@ -19,8 +24,8 @@ export interface ProviderValidationPlan {
|
|||||||
steps: ProviderValidationStep[]
|
steps: ProviderValidationStep[]
|
||||||
config: Record<string, unknown>
|
config: Record<string, unknown>
|
||||||
definition: ProviderDefinition
|
definition: ProviderDefinition
|
||||||
configValidators: Array<{ id: string, name: string, validator: (config: Record<string, unknown>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>
|
configValidators: ProviderConfigValidator<Record<string, unknown>>[]
|
||||||
providerValidators: Array<{ id: string, name: string, validator: (config: Record<string, unknown>, provider: ProviderInstance, providerExtra: ProviderExtraMethods<Record<string, unknown>>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>
|
providerValidators: ProviderRuntimeValidator<Record<string, unknown>>[]
|
||||||
providerExtra: ProviderExtraMethods<Record<string, unknown>> | undefined
|
providerExtra: ProviderExtraMethods<Record<string, unknown>> | undefined
|
||||||
shouldValidate: boolean
|
shouldValidate: boolean
|
||||||
}
|
}
|
||||||
@@ -31,7 +36,7 @@ export interface ProviderValidationCallbacks {
|
|||||||
onValidatorError?: (info: { kind: ProviderValidationStepKind, index: number, step: ProviderValidationStep, error: unknown }) => void
|
onValidatorError?: (info: { kind: ProviderValidationStepKind, index: number, step: ProviderValidationStep, error: unknown }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createConfigValidationSteps(configValidators: Array<{ id: string, name: string, validator: (config: Record<string, unknown>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>): ProviderValidationStep[] {
|
export function createConfigValidationSteps(configValidators: ProviderConfigValidator<Record<string, unknown>>[]): ProviderValidationStep[] {
|
||||||
return configValidators.map(validator => ({
|
return configValidators.map(validator => ({
|
||||||
id: validator.id,
|
id: validator.id,
|
||||||
label: validator.name,
|
label: validator.name,
|
||||||
@@ -41,7 +46,7 @@ export function createConfigValidationSteps(configValidators: Array<{ id: string
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createProviderValidationSteps(providerValidators: Array<{ id: string, name: string, validator: (config: Record<string, unknown>, provider: ProviderInstance, providerExtra: ProviderExtraMethods<Record<string, unknown>>, contextOptions: { t: ComposerTranslation }) => MaybePromise<ProviderValidationResult> }>): ProviderValidationStep[] {
|
export function createProviderValidationSteps(providerValidators: ProviderRuntimeValidator<Record<string, unknown>>[]): ProviderValidationStep[] {
|
||||||
return providerValidators.map(validator => ({
|
return providerValidators.map(validator => ({
|
||||||
id: validator.id,
|
id: validator.id,
|
||||||
label: validator.name,
|
label: validator.name,
|
||||||
@@ -51,6 +56,24 @@ export function createProviderValidationSteps(providerValidators: Array<{ id: st
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProviderValidationIntervalMs(options: {
|
||||||
|
definition: ProviderDefinition
|
||||||
|
contextOptions: { t: ComposerTranslation }
|
||||||
|
defaultIntervalMs?: number
|
||||||
|
}) {
|
||||||
|
const validators = (options.definition.validators?.validateProvider || []).map(creator => creator(options.contextOptions))
|
||||||
|
const defaultIntervalMs = options.defaultIntervalMs ?? 15_000
|
||||||
|
const intervals = validators
|
||||||
|
.filter(validator => validator.schedule?.mode === 'interval')
|
||||||
|
.map(validator => validator.schedule?.intervalMs || defaultIntervalMs)
|
||||||
|
|
||||||
|
if (intervals.length === 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(...intervals)
|
||||||
|
}
|
||||||
|
|
||||||
export function getValidatorsOfProvider(options: {
|
export function getValidatorsOfProvider(options: {
|
||||||
definition: ProviderDefinition
|
definition: ProviderDefinition
|
||||||
config: Record<string, unknown>
|
config: Record<string, unknown>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import type {
|
|||||||
import type { AliyunRealtimeSpeechExtraOptions } from './providers/aliyun/stream-transcription'
|
import type { AliyunRealtimeSpeechExtraOptions } from './providers/aliyun/stream-transcription'
|
||||||
|
|
||||||
import { isStageTamagotchi, isUrl } from '@proj-airi/stage-shared'
|
import { isStageTamagotchi, isUrl } from '@proj-airi/stage-shared'
|
||||||
import { computedAsync, useLocalStorage } from '@vueuse/core'
|
import { computedAsync, useIntervalFn, useLocalStorage } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
createOpenAI,
|
createOpenAI,
|
||||||
} from '@xsai-ext/providers/create'
|
} from '@xsai-ext/providers/create'
|
||||||
@@ -47,6 +47,7 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { listProviders as listDefinedProviders } from '../libs/providers'
|
import { listProviders as listDefinedProviders } from '../libs/providers'
|
||||||
|
import { getProviderValidationIntervalMs } from '../libs/providers/validators/run'
|
||||||
import { getKokoroWorker } from '../workers/kokoro'
|
import { getKokoroWorker } from '../workers/kokoro'
|
||||||
import { getDefaultKokoroModel, KOKORO_MODELS, kokoroModelsToModelInfo } from '../workers/kokoro/constants'
|
import { getDefaultKokoroModel, KOKORO_MODELS, kokoroModelsToModelInfo } from '../workers/kokoro/constants'
|
||||||
import { createAliyunNLSProvider as createAliyunNlsStreamProvider } from './providers/aliyun/stream-transcription'
|
import { createAliyunNLSProvider as createAliyunNlsStreamProvider } from './providers/aliyun/stream-transcription'
|
||||||
@@ -1676,12 +1677,25 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
// Progressive migration bridge:
|
// Progressive migration bridge:
|
||||||
// translate unified provider definitions from libs/providers to legacy store metadata.
|
// translate unified provider definitions from libs/providers to legacy store metadata.
|
||||||
// Existing metadata remains as fallback for providers not yet migrated.
|
// Existing metadata remains as fallback for providers not yet migrated.
|
||||||
|
const definedProviders = listDefinedProviders()
|
||||||
|
|
||||||
const translatedProviderMetadata = convertProviderDefinitionsToMetadata(
|
const translatedProviderMetadata = convertProviderDefinitionsToMetadata(
|
||||||
listDefinedProviders(),
|
definedProviders,
|
||||||
t,
|
t,
|
||||||
providerMetadata,
|
providerMetadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const providerValidationIntervalMsById = new Map<string, number>()
|
||||||
|
for (const definition of definedProviders) {
|
||||||
|
const intervalMs = getProviderValidationIntervalMs({
|
||||||
|
definition,
|
||||||
|
contextOptions: { t },
|
||||||
|
})
|
||||||
|
if (intervalMs && intervalMs > 0) {
|
||||||
|
providerValidationIntervalMsById.set(definition.id, intervalMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Keep only legacy ASR/TTS providers as hand-written metadata.
|
// Keep only legacy ASR/TTS providers as hand-written metadata.
|
||||||
// All other categories are sourced from unified definitions in libs/providers.
|
// All other categories are sourced from unified definitions in libs/providers.
|
||||||
for (const [providerId, existing] of Object.entries(providerMetadata)) {
|
for (const [providerId, existing] of Object.entries(providerMetadata)) {
|
||||||
@@ -1700,6 +1714,8 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
|
|
||||||
// const validatedCredentials = ref<Record<string, string>>({})
|
// const validatedCredentials = ref<Record<string, string>>({})
|
||||||
const providerRuntimeState = ref<Record<string, ProviderRuntimeState>>({})
|
const providerRuntimeState = ref<Record<string, ProviderRuntimeState>>({})
|
||||||
|
const providerValidationInFlight = new Map<string, Promise<boolean>>()
|
||||||
|
const providerRevalidationLoops = new Map<string, { resume: () => void }>()
|
||||||
|
|
||||||
const configuredProviders = computed(() => {
|
const configuredProviders = computed(() => {
|
||||||
const result: Record<string, boolean> = {}
|
const result: Record<string, boolean> = {}
|
||||||
@@ -1719,7 +1735,7 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configuration validation functions
|
// Configuration validation functions
|
||||||
async function validateProvider(providerId: string): Promise<boolean> {
|
async function validateProvider(providerId: string, options: { force?: boolean } = {}): Promise<boolean> {
|
||||||
const metadata = providerMetadata[providerId]
|
const metadata = providerMetadata[providerId]
|
||||||
if (!metadata)
|
if (!metadata)
|
||||||
return false
|
return false
|
||||||
@@ -1737,26 +1753,43 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
|
|
||||||
const configString = JSON.stringify(config || {})
|
const configString = JSON.stringify(config || {})
|
||||||
const runtimeState = providerRuntimeState.value[providerId]
|
const runtimeState = providerRuntimeState.value[providerId]
|
||||||
|
const cacheKey = `${providerId}:${configString}`
|
||||||
|
const forceValidation = options.force === true
|
||||||
|
|
||||||
if (runtimeState?.validatedCredentialHash === configString && typeof runtimeState.isConfigured === 'boolean')
|
if (!forceValidation && runtimeState?.validatedCredentialHash === configString && typeof runtimeState.isConfigured === 'boolean')
|
||||||
return runtimeState.isConfigured
|
return runtimeState.isConfigured
|
||||||
|
|
||||||
// Always cache the current config string to prevent re-validating the same config
|
if (!forceValidation) {
|
||||||
if (providerRuntimeState.value[providerId]) {
|
const pending = providerValidationInFlight.get(cacheKey)
|
||||||
providerRuntimeState.value[providerId].validatedCredentialHash = configString
|
if (pending) {
|
||||||
}
|
return pending
|
||||||
|
|
||||||
const validationResult = await metadata.validators.validateProviderConfig(config || {})
|
|
||||||
|
|
||||||
if (providerRuntimeState.value[providerId]) {
|
|
||||||
providerRuntimeState.value[providerId].isConfigured = validationResult.valid
|
|
||||||
// Auto-mark Web Speech API as added if valid and available
|
|
||||||
if (validationResult.valid && ['browser-web-speech-api', 'player2'].includes(providerId)) {
|
|
||||||
markProviderAdded(providerId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationResult.valid
|
const runValidation = async () => {
|
||||||
|
const validationResult = await metadata.validators.validateProviderConfig(config || {})
|
||||||
|
|
||||||
|
if (providerRuntimeState.value[providerId]) {
|
||||||
|
providerRuntimeState.value[providerId].isConfigured = validationResult.valid
|
||||||
|
providerRuntimeState.value[providerId].validatedCredentialHash = configString
|
||||||
|
// Auto-mark Web Speech API as added if valid and available
|
||||||
|
if (validationResult.valid && ['browser-web-speech-api', 'player2'].includes(providerId)) {
|
||||||
|
markProviderAdded(providerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validationResult.valid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceValidation) {
|
||||||
|
return runValidation()
|
||||||
|
}
|
||||||
|
|
||||||
|
const task = runValidation()
|
||||||
|
providerValidationInFlight.set(cacheKey, task)
|
||||||
|
return task.finally(() => {
|
||||||
|
providerValidationInFlight.delete(cacheKey)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create computed properties for each provider's configuration status
|
// Create computed properties for each provider's configuration status
|
||||||
@@ -1788,6 +1821,23 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
// Initialize all providers
|
// Initialize all providers
|
||||||
Object.keys(providerMetadata).forEach(initializeProvider)
|
Object.keys(providerMetadata).forEach(initializeProvider)
|
||||||
|
|
||||||
|
function startPeriodicRuntimeValidation() {
|
||||||
|
for (const [providerId, intervalMs] of providerValidationIntervalMsById.entries()) {
|
||||||
|
if (!providerMetadata[providerId] || intervalMs <= 0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (providerRevalidationLoops.has(providerId)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const loop = useIntervalFn(() => {
|
||||||
|
void validateProvider(providerId, { force: true })
|
||||||
|
}, intervalMs, { immediate: false, immediateCallback: false })
|
||||||
|
loop.resume()
|
||||||
|
providerRevalidationLoops.set(providerId, loop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update configuration status for all configured providers
|
// Update configuration status for all configured providers
|
||||||
async function updateConfigurationStatus() {
|
async function updateConfigurationStatus() {
|
||||||
await Promise.all(Object.entries(providerMetadata)
|
await Promise.all(Object.entries(providerMetadata)
|
||||||
@@ -1810,6 +1860,7 @@ export const useProvidersStore = defineStore('providers', () => {
|
|||||||
|
|
||||||
// Call initially and watch for changes
|
// Call initially and watch for changes
|
||||||
watch(providerCredentials, updateConfigurationStatus, { deep: true, immediate: true })
|
watch(providerCredentials, updateConfigurationStatus, { deep: true, immediate: true })
|
||||||
|
startPeriodicRuntimeValidation()
|
||||||
|
|
||||||
// Available providers (only those that are properly configured)
|
// Available providers (only those that are properly configured)
|
||||||
const availableProviders = computed(() => Object.keys(providerMetadata).filter(providerId => providerRuntimeState.value[providerId]?.isConfigured))
|
const availableProviders = computed(() => Object.keys(providerMetadata).filter(providerId => providerRuntimeState.value[providerId]?.isConfigured))
|
||||||
|
|||||||
Reference in New Issue
Block a user