From d2468cd6fcc887c98e2f6a547e95039a44e5db9f Mon Sep 17 00:00:00 2001 From: thzxx Date: Sat, 18 Apr 2026 10:08:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=85=A8=E9=9D=A2=E6=B8=85?= =?UTF-8?q?=E7=90=86=E6=AD=BB=E4=BB=A3=E7=A0=81=20=E2=80=94=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20348=20=E8=A1=8C=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除文件: - training-export.ts: 训练数据导出(零引用,UI 已移除) 删除函数: - workspace.ts: isProcessAlive, getActiveProcessCount, execWorkspaceCommand, terminateWorkspaceCommand - sqlite.ts: deleteMessagesBySession, getSkill, closeDatabase - tool-registry.ts: isToolEnabled - cron-manager.ts: clearAllTimers - workspace-panel.ts: setOnToolTerminated, setWorkspaceDirPath 删除 UI: - index.html: 训练数据导出按钮及说明 - settings-modal.ts: 训练数据导出事件绑定 --- src/main/db/sqlite.ts | 16 --- src/main/workspace.ts | 158 --------------------- src/renderer/components/settings-modal.ts | 9 -- src/renderer/components/workspace-panel.ts | 18 --- src/renderer/index.html | 5 - src/renderer/services/cron-manager.ts | 9 -- src/renderer/services/tool-registry.ts | 3 - src/renderer/services/training-export.ts | 130 ----------------- 8 files changed, 348 deletions(-) delete mode 100644 src/renderer/services/training-export.ts diff --git a/src/main/db/sqlite.ts b/src/main/db/sqlite.ts index 2dfb948..1b77a32 100644 --- a/src/main/db/sqlite.ts +++ b/src/main/db/sqlite.ts @@ -377,10 +377,6 @@ export function getMessagesBySession(sessionId: string): MessageRow[] { return queryAll(getDb(), 'SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC', [sessionId]) as unknown as MessageRow[]; } -export function deleteMessagesBySession(sessionId: string): void { - runExec(getDb(), 'DELETE FROM messages WHERE session_id = ?', [sessionId]); - persist(); -} // ─── Memories CRUD ─── @@ -531,9 +527,6 @@ export function saveSkill(skill: SkillRow): string { return skill.id; } -export function getSkill(id: string): SkillRow | null { - return queryOne(getDb(), 'SELECT * FROM skills WHERE id = ?', [id]) as unknown as SkillRow | null; -} export function getAllSkills(): SkillRow[] { return queryAll(getDb(), 'SELECT * FROM skills ORDER BY success_count DESC, updated_at DESC') as unknown as SkillRow[]; @@ -626,12 +619,3 @@ export function importSessions(data: ExportData): { imported: number; skipped: n return { imported, skipped }; } -// ─── Close ─── - -export function closeDatabase(): void { - if (db) { - persist(); - db.close(); - db = null; - } -} diff --git a/src/main/workspace.ts b/src/main/workspace.ts index 5b432ab..1853e9e 100644 --- a/src/main/workspace.ts +++ b/src/main/workspace.ts @@ -199,161 +199,3 @@ export function listWorkspaceDir(dirPath?: string): { success: boolean; entries? return { success: false, error: (err as Error).message }; } } - -/** 检查进程是否还活着 */ -export function isProcessAlive(id: string): boolean { - return activeProcesses.has(id); -} - -/** 获取活跃进程数量 */ -export function getActiveProcessCount(): number { - return activeProcesses.size; -} - -/** - * 执行命令并收集完整输出(无超时限制) - * 用于 AI Tool Calling 集成,通过 IPC invoke/handle 调用 - * 返回完整的 stdout + stderr + exitCode - */ -export function execWorkspaceCommand( - command: string, - cwd?: string, - onOutput?: (type: 'stdout' | 'stderr', data: string) => void -): Promise<{ success: boolean; stdout: string; stderr: string; exitCode: number | null; duration: number; userTerminated: boolean; truncated: boolean }> { - return new Promise((resolve) => { - const workDir = cwd ? path.resolve(cwd) : _workspaceDir; - const cmdId = `_tool_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; - - // 安全检查 - const cmdCheck = checkCommandAllowed(command); - if (!cmdCheck.ok) { - sendLog('warn', `🔧 工具命令被拦截`, `${command.slice(0, 100)} | ${cmdCheck.reason}`); - resolve({ success: false, stdout: '', stderr: cmdCheck.reason!, exitCode: 1, duration: 0, userTerminated: false, truncated: false }); - return; - } - - const dirCheck = checkPathAllowed(workDir, 'read'); - if (!dirCheck.ok) { - sendLog('warn', `🔧 工具目录被拦截`, `${workDir} | ${dirCheck.reason}`); - resolve({ success: false, stdout: '', stderr: dirCheck.reason!, exitCode: 1, duration: 0, userTerminated: false, truncated: false }); - return; - } - - if (!fs.existsSync(workDir)) { - resolve({ success: false, stdout: '', stderr: `工作目录不存在: ${workDir}`, exitCode: 1, duration: 0, userTerminated: false, truncated: false }); - return; - } - - sendLog('info', `🔧 execWorkspaceCommand`, `${command.slice(0, 200)} | cwd: ${workDir}`); - - const startTime = Date.now(); - const MAX_OUTPUT = 5 * 1024 * 1024; // 5MB 输出上限 - let stdoutBuf = ''; - let stderrBuf = ''; - let truncated = false; - - try { - const isWin = process.platform === 'win32'; - const shell = isWin ? 'cmd.exe' : 'bash'; - const actualCmd = isWin ? `chcp 65001 >nul && ${command}` : command; - const shellArgs = isWin ? ['/c', actualCmd] : ['-c', actualCmd]; - const proc = spawn(shell, shellArgs, { - cwd: workDir, - env: { - ...process.env, - ...(isWin ? {} : { TERM: 'xterm-256color' }), - LANG: 'en_US.UTF-8', - LC_ALL: 'en_US.UTF-8', - PYTHONIOENCODING: 'utf-8' - }, - stdio: ['pipe', 'pipe', 'pipe'] - }); - - activeProcesses.set(cmdId, proc); - - proc.stdout.on('data', (data: Buffer) => { - const str = data.toString('utf-8'); - if (stdoutBuf.length < MAX_OUTPUT) { - stdoutBuf += str; - if (stdoutBuf.length > MAX_OUTPUT) { - stdoutBuf = stdoutBuf.slice(0, MAX_OUTPUT); - truncated = true; - } - } else { - truncated = true; - } - // 实时推送到渲染进程 - onOutput?.('stdout', str); - }); - - proc.stderr.on('data', (data: Buffer) => { - const str = data.toString('utf-8'); - if (stderrBuf.length < MAX_OUTPUT) { - stderrBuf += str; - if (stderrBuf.length > MAX_OUTPUT) { - stderrBuf = stderrBuf.slice(0, MAX_OUTPUT); - truncated = true; - } - } else { - truncated = true; - } - // 实时推送到渲染进程 - onOutput?.('stderr', str); - }); - - proc.on('close', (code) => { - activeProcesses.delete(cmdId); - const duration = Date.now() - startTime; - sendLog( - code === 0 ? 'success' : 'warn', - `🔧 execWorkspaceCommand 完成`, - `code: ${code} | ${duration}ms | stdout: ${stdoutBuf.length}B | stderr: ${stderrBuf.length}B` - ); - resolve({ - success: code === 0, - stdout: stdoutBuf, - stderr: stderrBuf, - exitCode: code, - duration, - userTerminated: false, - truncated - }); - }); - - proc.on('error', (err) => { - activeProcesses.delete(cmdId); - const duration = Date.now() - startTime; - sendLog('error', `🔧 execWorkspaceCommand 失败`, `${cmdId} | ${err.message}`); - resolve({ - success: false, - stdout: stdoutBuf, - stderr: stderrBuf + (stderrBuf ? '\n' : '') + `进程启动失败: ${err.message}`, - exitCode: 1, - duration, - userTerminated: false, - truncated - }); - }); - } catch (err) { - const duration = Date.now() - startTime; - sendLog('error', `🔧 execWorkspaceCommand 异常`, (err as Error).message); - resolve({ - success: false, - stdout: stdoutBuf, - stderr: (err as Error).message, - exitCode: 1, - duration, - userTerminated: false, - truncated: false - }); - } - }); -} - -/** - * 终止 execWorkspaceCommand 启动的进程 - * @returns 是否成功终止 - */ -export function terminateWorkspaceCommand(commandId: string): boolean { - return killProcess(commandId); -} diff --git a/src/renderer/components/settings-modal.ts b/src/renderer/components/settings-modal.ts index f2236d1..90a7d97 100644 --- a/src/renderer/components/settings-modal.ts +++ b/src/renderer/components/settings-modal.ts @@ -151,15 +151,6 @@ export function initSettingsModal(): void { logSetting('检查间隔', `${ms / 60000} 分钟`); }); - // ── v5.0 训练数据导出 ── - document.querySelector('#btnExportTraining')!.addEventListener('click', async () => { - const { saveTrainingData } = await import('../services/training-export.js'); - showToast('正在导出训练数据...', 'info'); - const ok = await saveTrainingData(); - if (ok) showToast('训练数据导出成功', 'success'); - else showToast('导出取消或无数据', 'warning'); - }); - // ── v5.0 Cron 定时任务 ── document.querySelector('#btnAddCronTask')!.addEventListener('click', async () => { const name = (document.querySelector('#inputCronName') as HTMLInputElement).value.trim(); diff --git a/src/renderer/components/workspace-panel.ts b/src/renderer/components/workspace-panel.ts index 0b72a84..05823a3 100644 --- a/src/renderer/components/workspace-panel.ts +++ b/src/renderer/components/workspace-panel.ts @@ -44,10 +44,6 @@ let currentAiCommand: string | null = null; /** 终止通知回调(由外部设置) */ let onToolTerminated: ((command: string) => void) | null = null; -/** 设置终止通知回调 */ -export function setOnToolTerminated(cb: (command: string) => void): void { - onToolTerminated = cb; -} function genId(): string { return `ws_${Date.now()}_${++_counter}`; @@ -628,17 +624,3 @@ export function getWorkspaceDirPath(): string { return workspaceDir; } -/** 设置工作空间目录 */ -export async function setWorkspaceDirPath(dir: string): Promise { - const bridge = window.metonaDesktop; - if (!bridge?.isDesktop) return false; - - const result = await bridge.workspace.setDir(dir); - if (result.success && result.dir) { - workspaceDir = result.dir; - currentFileDir = workspaceDir; - logInfo('工作空间目录已更新', workspaceDir); - return true; - } - return false; -} diff --git a/src/renderer/index.html b/src/renderer/index.html index 241d7ec..5f08a39 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -347,11 +347,6 @@ -
- - -

将 Agent 执行轨迹导出为 SFT 格式,可用于模型微调训练。包含成功的工具调用链和对话记录。

-
diff --git a/src/renderer/services/cron-manager.ts b/src/renderer/services/cron-manager.ts index 897121c..93f22ac 100644 --- a/src/renderer/services/cron-manager.ts +++ b/src/renderer/services/cron-manager.ts @@ -183,12 +183,3 @@ export async function restoreCronTasks(): Promise { logInfo(`定时任务已恢复: ${scheduled} 个任务`); } } - -/** - * 清理所有定时器(应用退出时调用) - */ -export function clearAllTimers(): void { - for (const [id] of activeTimers) { - clearTimer(id); - } -} diff --git a/src/renderer/services/tool-registry.ts b/src/renderer/services/tool-registry.ts index 0d32ec7..f86b496 100644 --- a/src/renderer/services/tool-registry.ts +++ b/src/renderer/services/tool-registry.ts @@ -572,9 +572,6 @@ export function setToolEnabled(toolName: string, enabled: boolean): void { else enabledTools.delete(toolName); } -export function isToolEnabled(toolName: string): boolean { - return enabledTools.has(toolName); -} export function getEnabledToolDefinitions(): ToolDefinition[] { const builtIn = TOOL_DEFINITIONS.filter(def => enabledTools.has(def.function.name)); diff --git a/src/renderer/services/training-export.ts b/src/renderer/services/training-export.ts deleted file mode 100644 index 571ec83..0000000 --- a/src/renderer/services/training-export.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * TrainingExport - 训练数据导出 (v5.0) - * 将 Agent 执行轨迹转换为 SFT 训练数据格式 - */ - -import { state, KEYS } from '../state/state.js'; -import { logInfo, logSuccess, logError } from './log-service.js'; - -interface SFTRecord { - instruction: string; - input: string; - output: string; - tools?: Array<{ name: string; arguments: Record }>; - observations?: string[]; - metadata?: { - session_id: string; - session_title: string; - timestamp: number; - model: string; - eval_count?: number; - total_duration?: number; - }; -} - -/** - * 导出所有会话的训练数据 - * 从 traces + messages 中提取成功的工具调用链 - */ -export async function exportTrainingData(): Promise { - const bridge = (window as any).metonaDesktop; - if (!bridge?.db) return null; - - try { - const sessions = await bridge.db.getAllSessions(); - const records: SFTRecord[] = []; - - for (const session of sessions) { - const messages = await bridge.db.getMessages(session.id); - const traces = await bridge.db.getTraces(session.id); - - // 遍历消息对(user → assistant) - for (let i = 0; i < messages.length - 1; i++) { - const userMsg = messages[i]; - const asstMsg = messages[i + 1]; - - if (userMsg.role !== 'user' || asstMsg.role !== 'assistant') continue; - if (!userMsg.content || !asstMsg.content) continue; - - // 解析工具调用 - let tools: Array<{ name: string; arguments: Record }> | undefined; - let observations: string[] | undefined; - - if (asstMsg.tool_calls) { - try { - const toolCalls = JSON.parse(asstMsg.tool_calls); - if (Array.isArray(toolCalls) && toolCalls.length > 0) { - tools = toolCalls.map((tc: any) => ({ - name: tc.name || tc.function?.name || 'unknown', - arguments: tc.arguments || tc.function?.arguments || {} - })); - } - } catch { /* ignore */ } - } - - // 从 traces 中提取 observation - const sessionTraces = traces.filter(t => - t.created_at >= userMsg.created_at && t.created_at <= asstMsg.created_at - ); - if (sessionTraces.length > 0) { - observations = sessionTraces.map(t => t.observation || '').filter(Boolean); - } - - records.push({ - instruction: userMsg.content, - input: '', - output: asstMsg.content, - ...(tools && { tools }), - ...(observations?.length && { observations }), - metadata: { - session_id: session.id, - session_title: session.title, - timestamp: userMsg.created_at, - model: session.model, - ...(asstMsg.eval_count && { eval_count: asstMsg.eval_count }), - ...(asstMsg.total_duration && { total_duration: asstMsg.total_duration }) - } - }); - - i++; // 跳过 assistant 消息 - } - } - - if (records.length === 0) { - logInfo('训练数据导出: 无可导出数据'); - return null; - } - - logSuccess(`训练数据导出完成: ${records.length} 条记录`); - return records.map(r => JSON.stringify(r)).join('\n'); - } catch (err) { - logError('训练数据导出失败', (err as Error).message); - return null; - } -} - -/** - * 保存训练数据到文件 - */ -export async function saveTrainingData(): Promise { - const bridge = (window as any).metonaDesktop; - if (!bridge?.dialog?.saveFile) return false; - - const data = await exportTrainingData(); - if (!data) return false; - - try { - const filePath = await bridge.dialog.saveFile({ - defaultPath: `metona-training-data-${new Date().toISOString().slice(0, 10)}.jsonl`, - filters: [{ name: 'JSONL', extensions: ['jsonl'] }, { name: 'JSON', extensions: ['json'] }] - }); - if (!filePath) return false; - - await bridge.fs.writeFile(filePath, data); - logSuccess(`训练数据已保存: ${filePath}`); - return true; - } catch (err) { - logError('保存训练数据失败', (err as Error).message); - return false; - } -}