mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-01 03:29:41 +08:00
🐛 fix(ai/volcengine): 修复火山引擎兼容路径并拆分双预设
- OpenAI 兼容 URL 归一化改为保留已有 v3 和 v4 版本段,避免火山与智谱地址被错误补 /v1 - 对误填 /chat/completions 和 /models 的地址先回退到 base URL,再拼接目标端点 - 模型列表与连通性检测复用统一端点解析逻辑,修复火山 Coding Plan 等兼容服务请求 - AI 设置页拆分火山方舟与火山 Coding Plan 两个预设,并按完整路径精确匹配回显 - 修正模型下拉默认值行为,未选模型时保持占位态,避免误用动态列表首项 - 补充 provider 与 service 回归测试,并新增需求追踪文档
This commit is contained in:
@@ -33,7 +33,8 @@ const PROVIDER_PRESETS: ProviderPreset[] = [
|
||||
{ key: 'moonshot', label: 'Kimi', icon: <ExperimentOutlined />, desc: 'Kimi K2.5 (Anthropic 兼容)', color: '#0d9488', backendType: 'anthropic', defaultBaseUrl: 'https://api.moonshot.cn/anthropic', defaultModel: 'moonshot-v1-8k', models: [] },
|
||||
{ key: 'anthropic', label: 'Claude', icon: <ExperimentOutlined />, desc: 'Claude Opus/Sonnet', color: '#d97706', backendType: 'anthropic', defaultBaseUrl: 'https://api.anthropic.com', defaultModel: 'claude-3-5-sonnet-20241022', models: [] },
|
||||
{ key: 'gemini', label: 'Gemini', icon: <CloudOutlined />, desc: 'Gemini 3.1 / 2.5 系列', color: '#059669', backendType: 'gemini', defaultBaseUrl: 'https://generativelanguage.googleapis.com', defaultModel: 'gemini-2.5-flash', models: [] },
|
||||
{ key: 'volcengine', label: '火山引擎', icon: <CloudOutlined />, desc: '火山方舟 / 豆包大模型', color: '#0ea5e9', backendType: 'openai', defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', defaultModel: 'ep-xxxxxx', models: [] },
|
||||
{ key: 'volcengine-ark', label: '火山方舟', icon: <CloudOutlined />, desc: 'Ark 通用推理 / 豆包模型', color: '#0ea5e9', backendType: 'openai', defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', defaultModel: '', models: [] },
|
||||
{ key: 'volcengine-coding', label: '火山 Coding Plan', icon: <CloudOutlined />, desc: 'Ark Code / Coding Plan', color: '#0284c7', backendType: 'openai', defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/coding/v3', defaultModel: '', models: [] },
|
||||
{ key: 'minimax', label: 'MiniMax', icon: <ExperimentOutlined />, desc: 'M2.7 / M2.5 系列 (Anthropic 兼容)', color: '#e11d48', backendType: 'anthropic', defaultBaseUrl: 'https://api.minimaxi.com/anthropic', defaultModel: 'MiniMax-M2.7', models: ['MiniMax-M2.7', 'MiniMax-M2.7-highspeed', 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed', 'MiniMax-M2.1', 'MiniMax-M2.1-highspeed', 'MiniMax-M2'] },
|
||||
{ key: 'ollama', label: 'Ollama', icon: <AppstoreOutlined />, desc: '本地部署开源模型', color: '#78716c', backendType: 'openai', defaultBaseUrl: 'http://localhost:11434/v1', defaultModel: 'llama3', models: [] },
|
||||
{ key: 'custom', label: '自定义', icon: <AppstoreOutlined />, desc: '自定义 API 端点', color: '#64748b', backendType: 'custom', defaultBaseUrl: '', defaultModel: '', models: [] },
|
||||
@@ -50,7 +51,28 @@ const getProviderHostname = (raw?: string): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const getProviderFingerprint = (raw?: string): string => {
|
||||
if (!raw) return '';
|
||||
try {
|
||||
const url = new URL(raw);
|
||||
const normalizedPath = url.pathname.replace(/\/+$/, '').toLowerCase();
|
||||
return `${url.hostname.toLowerCase()}${normalizedPath}`;
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const matchProviderPreset = (provider: Pick<AIProviderConfig, 'type' | 'baseUrl'>): ProviderPreset => {
|
||||
const fingerprint = getProviderFingerprint(provider.baseUrl);
|
||||
const exactPreset = PROVIDER_PRESETS.find(pr =>
|
||||
pr.backendType === provider.type
|
||||
&& fingerprint !== ''
|
||||
&& fingerprint === getProviderFingerprint(pr.defaultBaseUrl)
|
||||
);
|
||||
if (exactPreset) {
|
||||
return exactPreset;
|
||||
}
|
||||
|
||||
const host = getProviderHostname(provider.baseUrl);
|
||||
if (host.endsWith('moonshot.cn')) {
|
||||
return findPreset('moonshot');
|
||||
|
||||
@@ -354,7 +354,7 @@ export const AIChatInput: React.FC<AIChatInputProps> = ({
|
||||
<Select
|
||||
size="small"
|
||||
variant="filled"
|
||||
value={activeProvider.model || (dynamicModels.length > 0 ? dynamicModels[0] : activeProvider.models?.[0])}
|
||||
value={activeProvider.model || undefined}
|
||||
onChange={onModelChange}
|
||||
onDropdownVisibleChange={(open) => { if (open && dynamicModels.length === 0) onFetchModels(); }}
|
||||
loading={loadingModels}
|
||||
|
||||
Reference in New Issue
Block a user