From 63e101feb48f278be3caa17361d8e78712cf2b0b Mon Sep 17 00:00:00 2001 From: digua Date: Wed, 27 May 2026 19:48:57 +0800 Subject: [PATCH] fix(cli): fix third-party service misidentified as local when editing in CLI Web - Add apiKeySet field to GET /_web/ai/llm/configs response - Allow saving in edit mode without re-entering API key when key already set - Skip API key validation on save when editing with existing key unchanged - Show edit-mode placeholder hint on API Key input - Add missing customModels/disableThinking/isReasoningModel to CLI route types Co-authored-by: Cursor --- apps/cli/src/ai/llm-config.ts | 1 + apps/cli/src/http/routes/ai.ts | 8 +++++++- .../common/Settings/AI/AIModelEditModal.vue | 4 ++-- src/components/common/Settings/AI/useAIConfigForm.ts | 12 ++++++++++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/apps/cli/src/ai/llm-config.ts b/apps/cli/src/ai/llm-config.ts index 0f23a038..af18cb3d 100644 --- a/apps/cli/src/ai/llm-config.ts +++ b/apps/cli/src/ai/llm-config.ts @@ -27,6 +27,7 @@ export interface AIServiceConfig { apiFormat?: string disableThinking?: boolean isReasoningModel?: boolean + customModels?: Array<{ id: string; name: string }> authProfile?: string } diff --git a/apps/cli/src/http/routes/ai.ts b/apps/cli/src/http/routes/ai.ts index e14d4013..11bbff83 100644 --- a/apps/cli/src/http/routes/ai.ts +++ b/apps/cli/src/http/routes/ai.ts @@ -179,7 +179,7 @@ export function registerAiRoutes( const data = JSON.parse(fs.readFileSync(configPath, 'utf-8')) const configs = (data.configs || []).map((c: Record) => { const { apiKey: _k, ...rest } = c - return { ...rest, apiKey: '' } + return { ...rest, apiKey: '', apiKeySet: !!c.authProfile } }) return { configs, @@ -268,6 +268,9 @@ export function registerAiRoutes( baseUrl?: string maxTokens?: number apiFormat?: string + disableThinking?: boolean + isReasoningModel?: boolean + customModels?: Array<{ id: string; name: string }> } }>('/_web/ai/llm/configs', async (request) => { const aiDataDir = getAiDir(dbManager) @@ -284,6 +287,9 @@ export function registerAiRoutes( baseUrl?: string maxTokens?: number apiFormat?: string + disableThinking?: boolean + isReasoningModel?: boolean + customModels?: Array<{ id: string; name: string }> } }>('/_web/ai/llm/configs/:id', async (request) => { const aiDataDir = getAiDir(dbManager) diff --git a/src/components/common/Settings/AI/AIModelEditModal.vue b/src/components/common/Settings/AI/AIModelEditModal.vue index 8a265d54..ad7a2571 100644 --- a/src/components/common/Settings/AI/AIModelEditModal.vue +++ b/src/components/common/Settings/AI/AIModelEditModal.vue @@ -172,7 +172,7 @@ function closeModal() { { const { provider, apiKey, baseUrl, model } = formData.value + const isEdit = props.mode.value === 'edit' + const existingKeySet = isEdit && props.config.value?.apiKeySet if (isLocalMode.value) { return baseUrl.trim() && model.trim() } if (isOpenAICompat.value) { - return baseUrl.trim() && apiKey.trim() && model.trim() + return baseUrl.trim() && (apiKey.trim() || existingKeySet) && model.trim() } if (!provider) return false - return apiKey.trim() + return apiKey.trim() || existingKeySet }) const apiFormatItems = computed(() => @@ -690,6 +692,12 @@ export function useAIConfigForm(props: { return doSave() } + const hasNewApiKey = !!formData.value.apiKey.trim() + const isEditWithExistingKey = props.mode.value === 'edit' && props.config.value?.apiKeySet && !hasNewApiKey + if (isEditWithExistingKey) { + return doSave() + } + isValidating.value = true try { const testApiKey = formData.value.apiKey.trim() || 'sk-no-key-required'