fix: 切换 Provider 后不生效 + Ollama 模式 API Key 输入框仍显示
1. reloadAdapter 加 try-catch: 配置中间状态 createAdapter 抛异常时不阻断 IPC, 记录日志即可 2. sendMessage 时兜底调用 reloadAdapter: 确保每次发消息用最新 adapter, 不依赖 config:set 的逐字段 reload 3. Ollama 模式隐藏 API Key 输入框, 切换到 Ollama 时自动清空旧 key 4. 切换 Provider 时自动填充默认 URL(仅旧 URL 为默认值或空时)
This commit is contained in:
@@ -52,6 +52,9 @@ export function registerAllIPCHandlers(
|
|||||||
ipcMain.handle('agent:sendMessage', async (_event, userMessage: MetonaMessage, sessionId: string) => {
|
ipcMain.handle('agent:sendMessage', async (_event, userMessage: MetonaMessage, sessionId: string) => {
|
||||||
log.info('[AGENT] sendMessage:', sessionId, userMessage.content.slice(0, 80));
|
log.info('[AGENT] sendMessage:', sessionId, userMessage.content.slice(0, 80));
|
||||||
|
|
||||||
|
// 发送消息前确保 Adapter 使用最新配置(防止 config:set 中间状态导致 reload 失败)
|
||||||
|
reloadAdapter();
|
||||||
|
|
||||||
// TRACE 层:开始录制
|
// TRACE 层:开始录制
|
||||||
sessionRecorder.startRecording(sessionId);
|
sessionRecorder.startRecording(sessionId);
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ async function initialize(): Promise<void> {
|
|||||||
|
|
||||||
// ===== 热重载 Adapter 回调(设置变更时触发)=====
|
// ===== 热重载 Adapter 回调(设置变更时触发)=====
|
||||||
const reloadAdapter = () => {
|
const reloadAdapter = () => {
|
||||||
|
try {
|
||||||
const newAdapter = createAdapter();
|
const newAdapter = createAdapter();
|
||||||
agentLoop.setAdapter(newAdapter);
|
agentLoop.setAdapter(newAdapter);
|
||||||
// Provider 切换时同步 contextLength:仅 Ollama 使用 numCtx 作为有效上下文窗口
|
// Provider 切换时同步 contextLength:仅 Ollama 使用 numCtx 作为有效上下文窗口
|
||||||
@@ -240,6 +241,10 @@ async function initialize(): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
agentLoop.updateConfig({ contextLength: undefined });
|
agentLoop.updateConfig({ contextLength: undefined });
|
||||||
}
|
}
|
||||||
|
log.info(`[CONFIG] Adapter reloaded: provider=${provider}`);
|
||||||
|
} catch (err) {
|
||||||
|
log.error(`[CONFIG] Failed to reload adapter: ${(err as Error).message}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== 窗口管理 =====
|
// ===== 窗口管理 =====
|
||||||
|
|||||||
@@ -118,11 +118,26 @@ function LLMSettings() {
|
|||||||
}
|
}
|
||||||
}, [provider, numCtx]);
|
}, [provider, numCtx]);
|
||||||
|
|
||||||
|
// 切换 Provider 时自动填充默认 URL + 清空 Ollama 的 apiKey
|
||||||
|
const handleProviderChange = (newProvider: string) => {
|
||||||
|
setProvider(newProvider);
|
||||||
|
// Ollama 不需要 API Key,切换到 Ollama 时清空旧 key
|
||||||
|
if (newProvider === 'ollama' && apiKey) {
|
||||||
|
setApiKey('');
|
||||||
|
}
|
||||||
|
// 自动填充默认 URL(仅在 URL 为空或与旧 provider 默认 URL 匹配时)
|
||||||
|
const currentUrl = baseURL.trim();
|
||||||
|
const isDefaultUrl = Object.values(PROVIDER_URLS).includes(currentUrl);
|
||||||
|
if (isDefaultUrl || !currentUrl) {
|
||||||
|
setBaseURL(PROVIDER_URLS[newProvider] ?? '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>LLM 配置</Typography>
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>LLM 配置</Typography>
|
||||||
<FormControl size="small"><InputLabel>Provider</InputLabel>
|
<FormControl size="small"><InputLabel>Provider</InputLabel>
|
||||||
<Select value={provider} label="Provider" onChange={(e) => setProvider(e.target.value)}>
|
<Select value={provider} label="Provider" onChange={(e) => handleProviderChange(e.target.value)}>
|
||||||
<MenuItem value="deepseek">DeepSeek</MenuItem>
|
<MenuItem value="deepseek">DeepSeek</MenuItem>
|
||||||
<MenuItem value="agnes">Agnes AI</MenuItem>
|
<MenuItem value="agnes">Agnes AI</MenuItem>
|
||||||
<MenuItem value="ollama">Ollama (本地)</MenuItem>
|
<MenuItem value="ollama">Ollama (本地)</MenuItem>
|
||||||
@@ -130,9 +145,11 @@ function LLMSettings() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<TextField size="small" label="API Base URL" value={baseURL} onChange={(e) => setBaseURL(e.target.value)} placeholder="如 https://api.deepseek.com" />
|
<TextField size="small" label="API Base URL" value={baseURL} onChange={(e) => setBaseURL(e.target.value)} placeholder="如 https://api.deepseek.com" />
|
||||||
<TextField size="small" label="模型名称" value={model} onChange={(e) => setModel(e.target.value)} placeholder="如 deepseek-v4-pro、qwen3:latest" />
|
<TextField size="small" label="模型名称" value={model} onChange={(e) => setModel(e.target.value)} placeholder="如 deepseek-v4-pro、qwen3:latest" />
|
||||||
<TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-...(本地模型可留空)"
|
{provider !== 'ollama' && (
|
||||||
|
<TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-..."
|
||||||
slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }}
|
slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
{provider === 'ollama' && (
|
{provider === 'ollama' && (
|
||||||
<TextField
|
<TextField
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
Reference in New Issue
Block a user