mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
fix(llm): let custom OpenAI-compatible providers opt out of API Key requirement (#89)
This commit is contained in:
parent
88fb28a22a
commit
d2886bdaa3
@ -12,5 +12,6 @@ public class CreateCustomProviderRequest {
|
||||
private String apiKeyPrefix;
|
||||
private String protocol;
|
||||
private String chatModel;
|
||||
private Boolean requireApiKey;
|
||||
private List<ModelInfoDTO> models;
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ public class ProviderConfigRequest {
|
||||
private String protocol;
|
||||
private String chatModel;
|
||||
private Map<String, Object> 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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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: '池内尝试顺序',
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -29,6 +29,16 @@
|
||||
/>
|
||||
<div class="field-hint">{{ baseUrlHint }}</div>
|
||||
</div>
|
||||
<div v-if="editingProvider?.authType !== 'oauth' && form.protocol === 'openai-compatible'" class="form-group">
|
||||
<div class="search-toggle-row">
|
||||
<label class="form-label" style="margin-bottom: 0">{{ t('settings.model.fields.requireApiKey') }}</label>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="form.requireApiKey" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-hint">{{ t('settings.model.requireApiKeyHint') }}</div>
|
||||
</div>
|
||||
<!-- OAuth 登录区域(auth_type === 'oauth' 时显示) -->
|
||||
<div v-if="editingProvider?.authType === 'oauth'" class="form-group full-width oauth-group">
|
||||
<label class="form-label">{{ t('settings.model.oauthTitle') }}</label>
|
||||
@ -65,7 +75,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- API Key 输入区域(非 OAuth 时显示) -->
|
||||
<div v-else class="form-group">
|
||||
<div v-else-if="form.protocol !== 'openai-compatible' || form.requireApiKey" class="form-group">
|
||||
<label class="form-label">{{ t('settings.model.apiKey') }}</label>
|
||||
<input
|
||||
v-model="form.apiKey"
|
||||
@ -76,7 +86,7 @@
|
||||
/>
|
||||
<div class="field-hint">{{ t('settings.model.leaveBlankKeep') }}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="editingProvider?.authType !== 'oauth' && (form.protocol !== 'openai-compatible' || form.requireApiKey)" class="form-group">
|
||||
<label class="form-label">{{ t('settings.model.fields.apiKeyPrefix') }}</label>
|
||||
<input v-model="form.apiKeyPrefix" class="form-input" />
|
||||
</div>
|
||||
@ -177,6 +187,7 @@ defineProps<{
|
||||
apiKeyPrefix: string
|
||||
protocol: string
|
||||
chatModel: string
|
||||
requireApiKey: boolean
|
||||
generateKwargsText: string
|
||||
enableSearch: boolean
|
||||
searchStrategy: string
|
||||
|
||||
Loading…
Reference in New Issue
Block a user