🐛 fix(ai/query-editor/mac-window): 修复模型兼容性并优化即时执行与窗口交互

- AI 兼容性:为 Anthropic Provider 补齐 tools/tool_use/tool_result 协议转换,支持工具调用与流式工具结果解析
- 降级策略:OpenAI 兼容接口在 tools 请求返回 400/422/404 时自动回退为纯文本模式
- 配置修复:调整 MiniMax 预设为 Anthropic 兼容端点并更新默认模型列表
- 状态隔离:AI 聊天面板停止将动态模型列表写回供应商配置,避免污染静态 models 数据
- 编辑器修复:QueryEditor 在 runImmediately 场景下避免重复追加 SQL,改为直接选中并执行
- 交互优化:修复 macOS 原生窗口控制切换与标题栏点击行为,避免窗口按钮状态异常
This commit is contained in:
Syngnat
2026-03-26 17:57:29 +08:00
parent 98e9e5686d
commit b958ff6481
8 changed files with 246 additions and 61 deletions

View File

@@ -1215,9 +1215,6 @@ function App() {
if (target?.closest('[data-no-titlebar-toggle="true"]')) {
return;
}
if (useNativeMacWindowControls) {
return;
}
void handleTitleBarWindowToggle();
};

View File

@@ -333,21 +333,8 @@ export const AIChatPanel: React.FC<AIChatPanelProps> = ({
}
}, [activeProvider?.id]);
useEffect(() => {
if (activeProvider && dynamicModels.length > 0) {
const currentModels = activeProvider.models || [];
if (JSON.stringify(currentModels) !== JSON.stringify(dynamicModels)) {
try {
const Service = (window as any).go?.aiservice?.Service;
const payload = { ...activeProvider, models: dynamicModels };
Service?.AISaveProvider?.(payload);
setActiveProvider(payload);
} catch (e) {
console.warn('Failed to cache models', e);
}
}
}
}, [activeProvider, dynamicModels]);
// dynamicModels 仅在内存中使用,不再写回供应商配置,避免污染静态 models 列表
const fetchDynamicModels = useCallback(async () => {
try {

View File

@@ -34,7 +34,7 @@ const PROVIDER_PRESETS: ProviderPreset[] = [
{ key: 'anthropic', label: 'Claude', icon: <ExperimentOutlined />, desc: 'Claude Opus/Sonnet 4.6', color: '#d97706', backendType: 'anthropic', defaultBaseUrl: 'https://api.anthropic.com', defaultModel: 'claude-sonnet-4-6', models: ['claude-opus-4-6', 'claude-sonnet-4-6'] },
{ 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: ['gemini-3.1-pro', 'gemini-2.5-flash', 'gemini-2.5-pro'] },
{ key: 'volcengine', label: '火山引擎', icon: <CloudOutlined />, desc: '火山方舟 / 豆包大模型', color: '#0ea5e9', backendType: 'openai', defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', defaultModel: 'ep-xxxxxx', models: [] },
{ key: 'minimax', label: 'MiniMax', icon: <ExperimentOutlined />, desc: 'abab6.5 / abab7 系列', color: '#e11d48', backendType: 'openai', defaultBaseUrl: 'https://api.minimax.chat/v1', defaultModel: 'abab7-chat-preview', models: ['abab7-chat-preview', 'abab6.5-chat', 'abab6.5g-chat'] },
{ key: 'minimax', label: 'MiniMax', icon: <ExperimentOutlined />, desc: 'abab6.5 / abab7 系列', color: '#e11d48', backendType: 'anthropic', defaultBaseUrl: 'https://api.minimaxi.com/anthropic', defaultModel: 'MiniMax-Text-01', models: ['MiniMax-Text-01', 'MiniMax-Text-01-vision', 'MiniMax-Text-01-search', 'MiniMax-Text-01-code', 'MiniMax-Text-01-web', 'MiniMax-Text-01-sql', 'MiniMax-Text-01-python', 'MiniMax-Text-01-math', 'MiniMax-Text-01-doc'] },
{ 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: [] },
];

View File

@@ -2040,11 +2040,25 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => {
setCurrentDb(dbName);
}
const editor = editorRef.current;
const monaco = monacoRef.current;
if (editor && monaco) {
let position = editor.getPosition();
const model = editor.getModel();
const existingContent = editor.getValue?.() || '';
// runImmediately 模式下,如果编辑器内容已是待注入的 SQLTabManager 创建时已传入),
// 跳过追加,直接选中全部内容并执行
if (e.detail.runImmediately && existingContent.trim() === sqlText.trim()) {
if (model) {
const lineCount = model.getLineCount();
const maxCol = model.getLineMaxColumn(lineCount);
editor.setSelection(new monaco.Range(1, 1, lineCount, maxCol));
editor.focus();
setTimeout(() => handleRun(), 500);
}
} else {
let position = editor.getPosition();
if (!position && model) {
const lineCount = model.getLineCount();
const maxCol = model.getLineMaxColumn(lineCount);
@@ -2081,6 +2095,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => {
setTimeout(() => handleRun(), 500);
}
}
}
} else {
setQuery((prev: string) => prev ? prev + '\n' + sqlText : sqlText);
message.success('代码已追加');

View File

@@ -151,7 +151,7 @@ const TabManager: React.FC = () => {
id: newTabId,
type: 'query',
title: '新建查询',
query: '',
query: sql,
connectionId: resolvedConnId,
dbName: resolvedDbName
});