feat: 升级至 v0.3.15 — MiMo 多模态 + 工作空间数据库继承 + 引导窗口上下文配置 + TS 错误修复

- feat(mimo): 适配器支持图片输入,将 images 转为 OpenAI 兼容 content parts 数组
- feat(workspace): 切换工作空间时支持继承数据库 agent.db(SQLite backup API 原子导出,自动 checkpoint WAL)
- feat(onboarding): 引导弹窗 LLM 配置加上下文长度字段(DeepSeek/Agnes=1000000、MiMo=131072、Ollama=null)
- fix(types): 修复 16 个 TypeScript 编译错误(setConfigLoaded 接口声明、10 个 IPC 返回类型加 error 字段、MUI TextField readOnly slot 迁移)
- chore(workspace): 移除死代码 traces 目录(trace 数据实际存储于数据库 sessions.metadata 字段,traces 目录从未被任何代码读写)
- docs(mimo): API 文档补充多模态输入章节和 curl/Python 代码示例
- chore: 版本号 0.3.14 → 0.3.15
This commit is contained in:
2026-07-21 21:05:50 +08:00
parent c0a26ce053
commit 516d8a728f
12 changed files with 318 additions and 33 deletions
+47 -1
View File
@@ -20,9 +20,24 @@ export function OnboardingWizard(): React.JSX.Element | null {
const [apiKey, setApiKey] = useState('');
const [showKey, setShowKey] = useState(false);
const [workspacePath, setWorkspacePath] = useState('');
// 上下文窗口(联动 Provider:ollama 可空=由模型决定;其他 provider 默认值见 DEFAULT_CTX
const [contextWindow, setContextWindow] = useState<number | null>(null);
if (onboardingCompleted) return null;
// 各 Provider 上下文窗口默认值(与 SettingsModal 保持一致)
// ollama 返回 null(由模型决定),其他 provider 返回正整数
const DEFAULT_CTX: Record<string, number | null> = {
deepseek: 1_000_000,
agnes: 1_000_000,
mimo: 131_072,
ollama: null,
};
// 上下文窗口校验:ollama 允许空,最小 512;其他 provider 最小 4096
const ctxMin = provider === 'ollama' ? 512 : 4096;
const ctxError =
contextWindow != null && (!Number.isFinite(contextWindow) || contextWindow < ctxMin);
const handleNext = async () => {
if (step < STEPS.length - 1) { setStep(step + 1); return; }
try {
@@ -39,6 +54,14 @@ export function OnboardingWizard(): React.JSX.Element | null {
if (model.trim()) entries.push({ key: 'llm.model', value: model.trim() });
if (apiKey.trim()) entries.push({ key: 'llm.apiKey', value: apiKey.trim() });
if (workspacePath.trim()) entries.push({ key: 'workspace.path', value: workspacePath.trim() });
// 上下文窗口:根据 Provider 落库到对应 key
// - ollama: ollama.numCtx(允许 null=由模型决定)
// - deepseek/agnes/mimo: {provider}.contextWindow(必须有值且 >= 4096
if (provider === 'ollama') {
entries.push({ key: 'ollama.numCtx', value: contextWindow });
} else if (provider && contextWindow != null && contextWindow >= 4096) {
entries.push({ key: `${provider}.contextWindow`, value: contextWindow });
}
entries.push({ key: 'onboarding.completed', value: true });
const r = await window.metona.config.setBatch(entries);
@@ -48,6 +71,10 @@ export function OnboardingWizard(): React.JSX.Element | null {
return;
}
useAgentStore.getState().setProvider(provider.trim() || 'deepseek', model.trim() || '');
// 同步 contextWindow 到 Agent Store(与 SettingsModal 行为一致)
if (contextWindow != null && contextWindow >= ctxMin) {
useAgentStore.setState({ contextWindow });
}
// P1-6 修复: Onboarding 完成后标记配置已加载
useAgentStore.getState().setConfigLoaded(true);
}
@@ -81,7 +108,12 @@ export function OnboardingWizard(): React.JSX.Element | null {
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 2 }}> Provider API Base URL </Typography>
<Stack spacing={2}>
<FormControl size="small"><InputLabel>Provider</InputLabel>
<Select value={provider} label="Provider" onChange={(e) => setProvider(e.target.value)}>
<Select value={provider} label="Provider" onChange={(e) => {
const v = e.target.value;
setProvider(v);
// 联动默认上下文窗口(与 SettingsModal 默认值一致)
setContextWindow(DEFAULT_CTX[v] ?? null);
}}>
<MenuItem value="deepseek">DeepSeek</MenuItem>
<MenuItem value="agnes">Agnes AI</MenuItem>
<MenuItem value="mimo">MiMo ()</MenuItem>
@@ -93,6 +125,20 @@ export function OnboardingWizard(): React.JSX.Element | null {
<TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-...(本地模型可留空)"
slotProps={{ input: { endAdornment: <InputAdornment position="end"><IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton></InputAdornment> } }}
/>
<TextField
size="small"
label={provider === 'ollama' ? '上下文长度 (num_ctx)' : '上下文窗口 (contextWindow)'}
type="number"
value={contextWindow ?? ''}
onChange={(e) => {
const v = e.target.value;
setContextWindow(v === '' ? null : Number(v));
}}
placeholder={provider === 'ollama' ? '默认由模型决定(如 2048、4096、128000' : '如 64000、128000、1000000'}
slotProps={{ htmlInput: { min: ctxMin, step: ctxMin } }}
error={ctxError}
helperText={ctxError ? `最小值为 ${ctxMin}` : (provider === 'ollama' ? ' ' : '用于上下文压缩判断,不传给 API')}
/>
</Stack>
</Box>
)}