feat(stage-ui,stage-pages) allow to "continue anyway" with failing provider validation (#811)

This commit is contained in:
Rin
2025-12-17 19:58:49 +08:00
committed by GitHub
parent 84c053b44f
commit bbaa287dab
20 changed files with 125 additions and 9 deletions
@@ -421,6 +421,7 @@ pages:
and behave properly. You could think of it as the brain of the
characters living in AIRI system.
common:
continueAnyway: Continue Anyway
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI requiere que al menos un proveedor de {chat} esté configurado para pensar y comportarse correctamente. Podrías pensarlo como el cerebro de los personajes que viven en el sistema AIRI.
common:
continueAnyway: Continuar de todos modos
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI nécessite quau moins un fournisseur {chat} soit configuré pour penser et se comporter correctement. On peut le considérer comme le cerveau des personnages vivant dans le système AIRI.
common:
continueAnyway: Continuer quand même
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRIが正常に思考し動作するためには、少なくとも1つの{chat}プロバイダーの設定が必要です。 これはAIRIシステム内に存在するキャラクターの脳と考えることができます。
common:
continueAnyway: 続行する
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI requires at least one {chat} provider to be configured to think, and behave properly. You could think of it as the brain of the characters living in AIRI system.
common:
continueAnyway: 계속 진행
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI требует как минимум одного {chat}-провайдера для корректной работы. Можно считать его "мозгом" персонажей в системе AIRI.
common:
continueAnyway: Продолжить всё равно
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
{chat} AIRI cần ít nhất một nhà cung cấp để suy nghĩ và hoạt động đúng cách. Có thể coi nó như bộ não của nhân vật AIRI trong hệ thống.
common:
continueAnyway: Tiếp tục bất chấp
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI 需要配置至少一个 {chat} 服务来源,才能正常思考和运作。你可以把它看作是 AIRI 系统中角色的大脑。
common:
continueAnyway: 仍然继续
fields:
field:
api-key:
@@ -404,6 +404,7 @@ pages:
description: >
AIRI 需要設定至少一個 {chat} 服務來源,才能正常思考與運作。你可以把它看作是 AIRI 系統中角色的大腦。
common:
continueAnyway: 仍然繼續
fields:
field:
api-key:
@@ -49,6 +49,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -81,7 +82,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -65,6 +65,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -114,7 +115,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -46,6 +46,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -79,7 +80,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -37,6 +37,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -61,7 +62,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -38,6 +38,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
const headers = ref<{ key: string, value: string }[]>(Object.entries(providers.value[providerId]?.headers || {}).map(([key, value]) => ({ key, value } as { key: string, value: string })) || [{ key: '', value: '' }])
@@ -151,7 +152,16 @@ onMounted(() => {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -98,6 +98,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -138,7 +139,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -115,6 +115,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
function float32ToInt16(buffer: Float32Array) {
@@ -397,7 +398,16 @@ onBeforeUnmount(async () => {
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template #content>
<div class="whitespace-pre-wrap break-all">
@@ -79,6 +79,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -116,7 +117,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -79,6 +79,7 @@ const {
isValid,
validationMessage,
handleResetSettings,
forceValid,
} = useProviderValidation(providerId)
</script>
@@ -116,7 +117,16 @@ const {
<!-- Validation Status -->
<Alert v-if="!isValid && isValidating === 0 && validationMessage" type="error">
<template #title>
{{ t('settings.dialogs.onboarding.validationFailed') }}
<div class="w-full flex items-center justify-between">
<span>{{ t('settings.dialogs.onboarding.validationFailed') }}</span>
<button
type="button"
class="ml-2 rounded bg-red-100 px-2 py-0.5 text-xs text-red-600 font-medium transition-colors dark:bg-red-800/30 hover:bg-red-200 dark:text-red-300 dark:hover:bg-red-700/40"
@click="forceValid"
>
{{ t('settings.pages.providers.common.continueAnyway') }}
</button>
</div>
</template>
<template v-if="validationMessage" #content>
<div class="whitespace-pre-wrap break-all">
@@ -122,6 +122,12 @@ export function useProviderValidation(providerId: string) {
isValidating.value = 0
}
function forceValid() {
isValid.value = true
validationMessage.value = ''
providersStore.forceProviderConfigured(providerId)
}
return {
t,
router,
@@ -133,5 +139,6 @@ export function useProviderValidation(providerId: string) {
isValid,
validationMessage,
handleResetSettings,
forceValid,
}
}
+10
View File
@@ -1936,6 +1936,15 @@ export const useProvidersStore = defineStore('providers', () => {
const isLoadingModels = ref<Record<string, boolean>>({})
const modelLoadError = ref<Record<string, string | null>>({})
function forceProviderConfigured(providerId: string) {
configuredProviders.value[providerId] = true
// Also cache the current config to prevent re-validation from overwriting
const config = providerCredentials.value[providerId]
if (config) {
validatedCredentials.value[providerId] = JSON.stringify(config)
}
}
async function resetProviderSettings() {
providerCredentials.value = {}
configuredProviders.value = {}
@@ -2150,6 +2159,7 @@ export const useProvidersStore = defineStore('providers', () => {
loadModelsForConfiguredProviders,
getProviderInstance,
resetProviderSettings,
forceProviderConfigured,
availableProvidersMetadata,
allChatProvidersMetadata,
allAudioSpeechProvidersMetadata,