diff --git a/mateclaw-server/src/main/java/vip/mate/llm/model/CreateCustomProviderRequest.java b/mateclaw-server/src/main/java/vip/mate/llm/model/CreateCustomProviderRequest.java index 7a910524..66da006a 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/model/CreateCustomProviderRequest.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/model/CreateCustomProviderRequest.java @@ -12,5 +12,6 @@ public class CreateCustomProviderRequest { private String apiKeyPrefix; private String protocol; private String chatModel; + private Boolean requireApiKey; private List models; } diff --git a/mateclaw-server/src/main/java/vip/mate/llm/model/ProviderConfigRequest.java b/mateclaw-server/src/main/java/vip/mate/llm/model/ProviderConfigRequest.java index 97982771..fffe412d 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/model/ProviderConfigRequest.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/model/ProviderConfigRequest.java @@ -11,6 +11,7 @@ public class ProviderConfigRequest { private String protocol; private String chatModel; private Map generateKwargs; + private Boolean requireApiKey; /** * RFC-009 P3.5: provider's position in the multi-model failover chain. * {@code 0} = excluded; positive ints define ascending try-order. When diff --git a/mateclaw-server/src/main/java/vip/mate/llm/service/ModelProviderService.java b/mateclaw-server/src/main/java/vip/mate/llm/service/ModelProviderService.java index b3f2b999..b51b6ac1 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/service/ModelProviderService.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/service/ModelProviderService.java @@ -129,6 +129,9 @@ public class ModelProviderService { provider.setBaseUrl(request.getBaseUrl()); provider.setChatModel(ModelProtocol.resolveChatModel(request.getProtocol(), request.getChatModel())); provider.setGenerateKwargs(writeJson(request.getGenerateKwargs())); + if (request.getRequireApiKey() != null) { + provider.setRequireApiKey(request.getRequireApiKey()); + } // RFC-009 P3.5: only update fallback priority when the caller explicitly // sends a value. null leaves it untouched (existing chain unchanged). if (request.getFallbackPriority() != null) { @@ -169,7 +172,7 @@ public class ModelProviderService { provider.setSupportModelDiscovery(false); provider.setSupportConnectionCheck(false); provider.setFreezeUrl(false); - provider.setRequireApiKey(true); + provider.setRequireApiKey(request.getRequireApiKey() == null || Boolean.TRUE.equals(request.getRequireApiKey())); modelProviderMapper.insert(provider); if (request.getModels() != null) { diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index 53c72eb5..269b544f 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -608,6 +608,7 @@ export default { protocolGemini: 'Gemini Native', protocolDashScope: 'DashScope Native', advancedHint: 'Use this for generation options such as temperature, max_tokens, and top_p.', + requireApiKeyHint: 'Turn this off for internal or local OpenAI-compatible services that do not require auth. Connection tests will omit the Authorization header.', fallbackPriorityHint: 'Pool try-order (lower wins): 0 = excluded, 1 = first in line, 2 = second, and so on. Providers sharing the same value are tried in alphabetical order of their ID.', fallbackBadge: 'Preferred #{priority}', fallbackBadgeTitle: 'Position in the available pool — lower numbers are tried first', @@ -683,6 +684,7 @@ export default { providerName: 'Provider Name', defaultBaseUrl: 'Default Base URL', apiKeyPrefix: 'API Key Prefix', + requireApiKey: 'Requires API Key', protocol: 'Protocol', generateKwargs: 'Generate Kwargs (JSON)', fallbackPriority: 'Pool Try-Order', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index 0ecb43a5..b5b90508 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -494,6 +494,7 @@ export default { protocolGemini: 'Gemini 原生', protocolDashScope: 'DashScope 原生', advancedHint: '用于补充 temperature、max_tokens、top_p 等生成参数。', + requireApiKeyHint: '公司内部或本地 OpenAI 兼容服务如果不需要鉴权,可以关闭此项;测试连接时将不会发送 Authorization 头。', fallbackPriorityHint: '池内尝试顺序(数字越小越先):0 = 不参与;1 = 第一顺位;2 = 第二顺位,依此类推。多个提供商共用同一数字时按 ID 字典序。', fallbackBadge: '偏好 #{priority}', fallbackBadgeTitle: '可用池内的尝试顺序,数字越小越先尝试', @@ -569,6 +570,7 @@ export default { providerName: '提供商名称', defaultBaseUrl: '默认 Base URL', apiKeyPrefix: 'API Key 前缀', + requireApiKey: '需要 API Key', protocol: '协议', generateKwargs: 'Generate Kwargs (JSON)', fallbackPriority: '池内尝试顺序', diff --git a/mateclaw-ui/src/views/Settings/Models/composables/useProviderForm.ts b/mateclaw-ui/src/views/Settings/Models/composables/useProviderForm.ts index 43477b6b..5e50c050 100644 --- a/mateclaw-ui/src/views/Settings/Models/composables/useProviderForm.ts +++ b/mateclaw-ui/src/views/Settings/Models/composables/useProviderForm.ts @@ -39,6 +39,7 @@ export function useProviderForm(deps: ListDeps) { apiKeyPrefix: 'sk-', protocol: 'openai-compatible', chatModel: 'OpenAIChatModel', + requireApiKey: true, generateKwargsText: '{}', enableSearch: false, searchStrategy: '', @@ -110,6 +111,7 @@ export function useProviderForm(deps: ListDeps) { apiKeyPrefix: 'sk-', protocol: 'openai-compatible', chatModel: 'OpenAIChatModel', + requireApiKey: true, generateKwargsText: '{}', enableSearch: false, searchStrategy: '', @@ -134,6 +136,7 @@ export function useProviderForm(deps: ListDeps) { apiKeyPrefix: provider.apiKeyPrefix || 'sk-', protocol, chatModel: provider.chatModel || 'OpenAIChatModel', + requireApiKey: protocol === 'openai-compatible' ? provider.requireApiKey !== false : true, generateKwargsText: JSON.stringify(kwargs, null, 2), enableSearch: searchDefault, searchStrategy: (kwargs.searchStrategy as string) || '', @@ -175,12 +178,16 @@ export function useProviderForm(deps: ListDeps) { } // RFC-009 P3.5: clamp to non-negative, coerce string input back to integer. const fallbackPriority = Math.max(0, Math.floor(Number(providerForm.fallbackPriority) || 0)) + const requireApiKey = providerForm.protocol === 'openai-compatible' + ? providerForm.requireApiKey + : true if (editingProvider.value) { await modelApi.updateProviderConfig(editingProvider.value.id, { apiKey: providerForm.apiKey, baseUrl: providerForm.baseUrl, protocol: providerForm.protocol, chatModel: protocolToChatModel(providerForm.protocol), + requireApiKey, generateKwargs: kwargs, fallbackPriority, }) @@ -192,6 +199,7 @@ export function useProviderForm(deps: ListDeps) { apiKeyPrefix: providerForm.apiKeyPrefix, protocol: providerForm.protocol, chatModel: protocolToChatModel(providerForm.protocol), + requireApiKey, models: [], }) if (providerForm.apiKey || providerForm.generateKwargsText || fallbackPriority > 0) { @@ -200,6 +208,7 @@ export function useProviderForm(deps: ListDeps) { baseUrl: providerForm.baseUrl, protocol: providerForm.protocol, chatModel: protocolToChatModel(providerForm.protocol), + requireApiKey, generateKwargs: kwargs, fallbackPriority, }) diff --git a/mateclaw-ui/src/views/Settings/Models/modals/ProviderConfigModal.vue b/mateclaw-ui/src/views/Settings/Models/modals/ProviderConfigModal.vue index 51735809..6f98f587 100644 --- a/mateclaw-ui/src/views/Settings/Models/modals/ProviderConfigModal.vue +++ b/mateclaw-ui/src/views/Settings/Models/modals/ProviderConfigModal.vue @@ -29,6 +29,16 @@ />
{{ baseUrlHint }}
+
+
+ + +
+
{{ t('settings.model.requireApiKeyHint') }}
+
@@ -65,7 +75,7 @@
-
+
{{ t('settings.model.leaveBlankKeep') }}
-
+
@@ -177,6 +187,7 @@ defineProps<{ apiKeyPrefix: string protocol: string chatModel: string + requireApiKey: boolean generateKwargsText: string enableSearch: boolean searchStrategy: string