diff --git a/src/composables/__tests__/useLlmProviderDirectory.spec.ts b/src/composables/__tests__/useLlmProviderDirectory.spec.ts new file mode 100644 index 00000000..b9716541 --- /dev/null +++ b/src/composables/__tests__/useLlmProviderDirectory.spec.ts @@ -0,0 +1,77 @@ +import { useLlmProviderDirectory } from '@/composables/useLlmProviderDirectory' +import { mount } from '@vue/test-utils' +import { defineComponent, nextTick, ref } from 'vue' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const mocks = vi.hoisted(() => ({ + apiGet: vi.fn(), +})) + +vi.mock('@/api', () => ({ + default: { + get: (...args: unknown[]) => mocks.apiGet(...args), + }, +})) + +function createProvider(id: string, runtime: string) { + return { + id, + name: id, + runtime, + default_base_url: '', + base_url_presets: [], + base_url_editable: true, + requires_base_url: false, + supports_api_key: true, + api_key_label: 'API Key', + api_key_hint: '', + supports_model_refresh: true, + oauth_methods: [], + auth_status: { connected: false }, + } +} + +describe('useLlmProviderDirectory', () => { + beforeEach(() => { + mocks.apiGet.mockReset() + }) + + it('仅为 OpenAI 兼容 runtime 显示 API 协议字段', async () => { + mocks.apiGet.mockResolvedValue({ + success: true, + data: [createProvider('openai', 'openai_compatible'), createProvider('deepseek', 'deepseek')], + }) + + const Harness = defineComponent({ + setup() { + const provider = ref('openai') + const directory = useLlmProviderDirectory({ + provider, + apiKey: ref(''), + baseUrl: ref(''), + model: ref(''), + }) + + return { + loadProviders: directory.loadProviders, + selectProvider: (value: string) => { + provider.value = value + }, + showApiProtocolField: directory.showApiProtocolField, + } + }, + template: '
', + }) + + const wrapper = mount(Harness) + await wrapper.vm.loadProviders() + + expect(wrapper.vm.showApiProtocolField).toBe(true) + + wrapper.vm.selectProvider('deepseek') + await nextTick() + + expect(wrapper.vm.showApiProtocolField).toBe(false) + wrapper.unmount() + }) +}) diff --git a/src/composables/useLlmProviderDirectory.ts b/src/composables/useLlmProviderDirectory.ts index 6793d10f..eba0789f 100644 --- a/src/composables/useLlmProviderDirectory.ts +++ b/src/composables/useLlmProviderDirectory.ts @@ -128,7 +128,7 @@ export function useLlmProviderDirectory(options: UseLlmProviderDirectoryOptions) ) const showApiKeyField = computed(() => selectedProvider.value?.supports_api_key !== false) // OpenAI 兼容接口才需要选择 API 协议(Chat Completions / Responses)。 - const showApiProtocolField = computed(() => selectedProvider.value?.runtime === 'openai') + const showApiProtocolField = computed(() => selectedProvider.value?.runtime === 'openai_compatible') const hasUsableCredential = computed(() => { if (providerConnected.value) return true return Boolean(normalizeValue(options.apiKey.value)) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 47263289..067b827c 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -1766,6 +1766,7 @@ export default { llmProviderHint: 'Select the LLM service provider to use', llmModel: 'LLM Model Name', llmModelHint: 'Specify the LLM model to use, such as deepseek-v4-flash, gpt-5.4, etc.', + llmModelPlaceholder: 'Enter or select an LLM model name', llmModelResolvedHint: 'Max context has been auto-filled to {context}K from the model catalog. Source: {source}', llmThinking: 'Thinking Mode / Depth', llmThinkingHint: @@ -1794,11 +1795,13 @@ export default { llmApiKeyPlaceholder: 'Please enter API key', llmBaseUrl: 'LLM Base URL', llmBaseUrlHint: 'Base URL for LLM API, used for custom API endpoints', + llmBaseUrlPlaceholder: 'Enter the LLM API base URL', llmUseProxy: 'Use System Proxy', llmUseProxyHint: 'When enabled, Agent connections to the current LLM provider use the system proxy from advanced settings.', llmUserAgent: 'User-Agent', llmUserAgentHint: 'User-Agent sent to OpenAI-compatible APIs. Leave empty to use the SDK default.', + llmUserAgentPlaceholder: 'Leave empty to use the SDK default User-Agent', llmApiProtocol: 'API Protocol', llmApiProtocolHint: 'Request protocol for OpenAI-compatible APIs: auto-select by model capability, or force Chat Completions / Responses.', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 367cfa70..03dada23 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1753,6 +1753,7 @@ export default { llmProviderHint: '选择使用的LLM服务提供商', llmModel: 'LLM模型名称', llmModelHint: '指定使用的LLM模型,如deepseek-v4-flash、gpt-5.4等', + llmModelPlaceholder: '请输入或选择LLM模型名称', llmModelResolvedHint: '已根据模型目录自动回填最大上下文为 {context}K,来源:{source}', llmThinking: '思考模式 / 深度', llmThinkingHint: @@ -1780,10 +1781,12 @@ export default { llmApiKeyPlaceholder: '请输入API密钥', llmBaseUrl: 'LLM基础URL', llmBaseUrlHint: 'LLM API的基础URL地址,用于自定义API端点', + llmBaseUrlPlaceholder: '请输入LLM API基础URL', llmUseProxy: '使用系统代理', llmUseProxyHint: '启用后,Agent 连接当前 LLM 提供商时会应用高级设置中的系统代理', llmUserAgent: 'User-Agent', llmUserAgentHint: 'OpenAI 兼容接口请求使用的 User-Agent,留空则使用 SDK 默认值', + llmUserAgentPlaceholder: '留空则使用SDK默认User-Agent', llmApiProtocol: 'API 协议', llmApiProtocolHint: 'OpenAI 兼容接口的请求协议:自动按模型能力选择,或强制使用 Chat Completions / Responses', llmApiProtocolAuto: '自动 (auto)', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 1bf96ff7..880d8bac 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -1752,6 +1752,7 @@ export default { llmProviderHint: '選擇使用的LLM服務提供商', llmModel: 'LLM模型名稱', llmModelHint: '指定使用的LLM模型,如deepseek-v4-flash、gpt-5.4等', + llmModelPlaceholder: '請輸入或選擇LLM模型名稱', llmModelResolvedHint: '已根據模型目錄自動回填最大上下文為 {context}K,來源:{source}', llmThinking: '思考模式 / 深度', llmThinkingHint: @@ -1779,10 +1780,12 @@ export default { llmApiKeyPlaceholder: '請輸入API密鑰', llmBaseUrl: 'LLM基礎URL', llmBaseUrlHint: 'LLM API的基礎URL地址,用於自定義API端點', + llmBaseUrlPlaceholder: '請輸入LLM API基礎URL', llmUseProxy: '使用系統代理', llmUseProxyHint: '啟用後,Agent 連接目前 LLM 提供商時會套用進階設定中的系統代理', llmUserAgent: 'User-Agent', llmUserAgentHint: 'OpenAI 兼容接口請求使用的 User-Agent,留空則使用 SDK 預設值', + llmUserAgentPlaceholder: '留空則使用SDK預設User-Agent', llmApiProtocol: 'API 協議', llmApiProtocolHint: 'OpenAI 兼容接口的請求協議:自動按模型能力選擇,或強制使用 Chat Completions / Responses', llmApiProtocolAuto: '自動 (auto)', diff --git a/src/views/setting/AccountSettingSystem.vue b/src/views/setting/AccountSettingSystem.vue index 88d48bfa..c1bfa7d2 100644 --- a/src/views/setting/AccountSettingSystem.vue +++ b/src/views/setting/AccountSettingSystem.vue @@ -1243,7 +1243,7 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {