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:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user