feat: 自动更新
This commit is contained in:
@@ -17,6 +17,108 @@ export interface AiProviderFormState {
|
||||
redactionPolicy: string;
|
||||
}
|
||||
|
||||
export interface AiProviderKindDefaults {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: AiProviderKind;
|
||||
baseUrl: string;
|
||||
apiKeyRef: string;
|
||||
modelsText: string;
|
||||
defaultModel: string;
|
||||
relayMode: AiRelayMode;
|
||||
timeoutMs: string;
|
||||
redactionPolicy: string;
|
||||
requirement: string;
|
||||
advancedNote: string;
|
||||
}
|
||||
|
||||
export const aiProviderKindDefaults: Record<AiProviderKind, AiProviderKindDefaults> = {
|
||||
"openai-compatible": {
|
||||
id: "ai.openai-compatible",
|
||||
name: "OpenAI Compatible",
|
||||
kind: "openai-compatible",
|
||||
baseUrl: "https://relay.example.test/v1",
|
||||
apiKeyRef: "secret://providers/openai-compatible",
|
||||
modelsText: "gpt-5.6-luna",
|
||||
defaultModel: "gpt-5.6-luna",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "需要平台侧 API key / secret 引用;Base URL 可按网关修改。",
|
||||
advancedNote: "OpenAI 兼容网关通常只差 Base URL,模型可以保存后再发现。"
|
||||
},
|
||||
openai: {
|
||||
id: "ai.openai",
|
||||
name: "OpenAI",
|
||||
kind: "openai",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKeyRef: "secret://providers/openai",
|
||||
modelsText: "gpt-5.6-terra, gpt-5.6-luna",
|
||||
defaultModel: "gpt-5.6-terra",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "官方 SDK/API 使用 API key;平台保存 secret 引用,不在页面保存真实密钥。",
|
||||
advancedNote: "官方 OpenAI API 使用 Bearer API key 与 /v1 endpoint;模型列表可按账号权限调整。"
|
||||
},
|
||||
claude: {
|
||||
id: "ai.claude",
|
||||
name: "Claude",
|
||||
kind: "claude",
|
||||
baseUrl: "https://api.anthropic.com/v1",
|
||||
apiKeyRef: "secret://providers/anthropic",
|
||||
modelsText: "claude-sonnet-5, claude-haiku-4-5-20251001",
|
||||
defaultModel: "claude-sonnet-5",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "Anthropic 请求使用 API key;版本头由平台适配层处理。",
|
||||
advancedNote: "保持平台中介调用,避免插件或前端接触 Anthropic key。"
|
||||
},
|
||||
gemini: {
|
||||
id: "ai.gemini",
|
||||
name: "Gemini",
|
||||
kind: "gemini",
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
||||
apiKeyRef: "secret://providers/gemini",
|
||||
modelsText: "gemini-3.5-flash, gemini-2.5-flash",
|
||||
defaultModel: "gemini-3.5-flash",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "Google Gemini 入门使用 API key;平台保存 secret 引用。",
|
||||
advancedNote: "Base URL 使用 Google Generative Language API;模型名按项目可用模型调整。"
|
||||
},
|
||||
ollama: {
|
||||
id: "ai.ollama",
|
||||
name: "Ollama Local",
|
||||
kind: "ollama",
|
||||
baseUrl: "http://127.0.0.1:11434/v1",
|
||||
apiKeyRef: "",
|
||||
modelsText: "gpt-oss:20b",
|
||||
defaultModel: "gpt-oss:20b",
|
||||
relayMode: "local",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "本地 Ollama 默认不需要 API key;重点是本机/内网 Base URL 和模型名。",
|
||||
advancedNote: "Ollama OpenAI 兼容接口会忽略 dummy key;平台本地模式不要求用户填写密钥引用。"
|
||||
},
|
||||
custom: {
|
||||
id: "ai.custom",
|
||||
name: "Custom Provider",
|
||||
kind: "custom",
|
||||
baseUrl: "https://provider.example.test/v1",
|
||||
apiKeyRef: "secret://providers/custom",
|
||||
modelsText: "custom-model",
|
||||
defaultModel: "custom-model",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default",
|
||||
requirement: "自定义服务至少需要平台 secret 引用、Base URL 和一个模型名。",
|
||||
advancedNote: "用于非标准协议或私有网关;保存前请确认服务兼容平台适配层。"
|
||||
}
|
||||
};
|
||||
|
||||
export interface AiProviderMetrics {
|
||||
total: number;
|
||||
active: number;
|
||||
@@ -41,18 +143,7 @@ export interface AiProviderPageInitialState {
|
||||
}
|
||||
|
||||
export function emptyAiProviderForm(): AiProviderFormState {
|
||||
return {
|
||||
id: "",
|
||||
name: "",
|
||||
kind: "openai-compatible",
|
||||
baseUrl: "",
|
||||
apiKeyRef: "secret://providers/",
|
||||
modelsText: "",
|
||||
defaultModel: "",
|
||||
relayMode: "relay",
|
||||
timeoutMs: "30000",
|
||||
redactionPolicy: "default"
|
||||
};
|
||||
return aiProviderFormFromDefaults("openai");
|
||||
}
|
||||
|
||||
export function aiProviderToForm(provider?: AiProviderResponse): AiProviderFormState {
|
||||
@@ -73,6 +164,67 @@ export function aiProviderToForm(provider?: AiProviderResponse): AiProviderFormS
|
||||
};
|
||||
}
|
||||
|
||||
export function aiProviderFormFromDefaults(kind: AiProviderKind): AiProviderFormState {
|
||||
const defaults = aiProviderKindDefaults[kind];
|
||||
return {
|
||||
id: "",
|
||||
name: defaults.name,
|
||||
kind: defaults.kind,
|
||||
baseUrl: defaults.baseUrl,
|
||||
apiKeyRef: defaults.apiKeyRef,
|
||||
modelsText: defaults.modelsText,
|
||||
defaultModel: defaults.defaultModel,
|
||||
relayMode: defaults.relayMode,
|
||||
timeoutMs: defaults.timeoutMs,
|
||||
redactionPolicy: defaults.redactionPolicy
|
||||
};
|
||||
}
|
||||
|
||||
export function applyAiProviderKindDefaults(current: AiProviderFormState, kind: AiProviderKind): AiProviderFormState {
|
||||
const defaults = aiProviderKindDefaults[kind];
|
||||
return {
|
||||
...current,
|
||||
id: current.id,
|
||||
name: defaults.name,
|
||||
kind: defaults.kind,
|
||||
baseUrl: defaults.baseUrl,
|
||||
apiKeyRef: defaults.apiKeyRef,
|
||||
modelsText: defaults.modelsText,
|
||||
defaultModel: defaults.defaultModel,
|
||||
relayMode: defaults.relayMode,
|
||||
timeoutMs: defaults.timeoutMs,
|
||||
redactionPolicy: defaults.redactionPolicy
|
||||
};
|
||||
}
|
||||
|
||||
export function completeAiProviderForm(form: AiProviderFormState): AiProviderFormState {
|
||||
const defaults = aiProviderKindDefaults[form.kind];
|
||||
const modelsText = form.modelsText.trim() || defaults.modelsText;
|
||||
const models = modelsText
|
||||
.split(",")
|
||||
.map((model) => model.trim())
|
||||
.filter(Boolean);
|
||||
return {
|
||||
...form,
|
||||
id: generatedAiProviderId(form),
|
||||
name: form.name.trim() || defaults.name,
|
||||
baseUrl: form.baseUrl.trim() || defaults.baseUrl,
|
||||
apiKeyRef: form.apiKeyRef.trim() || defaults.apiKeyRef,
|
||||
modelsText,
|
||||
defaultModel: form.defaultModel.trim() || models[0] || defaults.defaultModel,
|
||||
relayMode: form.relayMode || defaults.relayMode,
|
||||
timeoutMs: form.timeoutMs.trim() || defaults.timeoutMs,
|
||||
redactionPolicy: form.redactionPolicy.trim() || defaults.redactionPolicy
|
||||
};
|
||||
}
|
||||
|
||||
export function generatedAiProviderId(form: AiProviderFormState): string {
|
||||
if (form.id.trim()) {
|
||||
return form.id.trim();
|
||||
}
|
||||
return aiProviderKindDefaults[form.kind].id;
|
||||
}
|
||||
|
||||
export function summarizeAiProviders(providers: AiProviderResponse[]): AiProviderMetrics {
|
||||
return providers.reduce<AiProviderMetrics>(
|
||||
(metrics, provider) => ({
|
||||
|
||||
Reference in New Issue
Block a user