refactor: 移除人格模式、后台检查、系统诊断、定时任务功能

This commit is contained in:
Metona Dev
2026-04-24 11:29:52 +08:00
parent 5cbb71cf50
commit f7fb47939d
5 changed files with 0 additions and 585 deletions
-297
View File
@@ -15,16 +15,6 @@ import { OllamaAPI } from '../api/ollama.js';
import { ChatDB } from '../db/chat-db.js';
import type { ChatSession } from '../types.js';
/** v4.2 人格模式 system prompt 映射 */
export const PERSONALITY_SYSTEM_PROMPTS: Record<string, string> = {
default: '',
coder: '你是一个专业的编程助手。你注重代码质量、可读性和最佳实践。回答时优先给出可运行的代码,其次才是解释。遇到问题先分析根因再给方案。使用 type hints、合理的命名和注释。代码风格偏向简洁紧凑。',
writer: '你是一个专业的写作助手。你的文字流畅、有文采,注重结构和节奏。擅长技术文档、文章、文案、邮件等各种文体。回答时先理解写作目的和受众,再给出合适的风格和内容。',
analyst: '你是一个精确的数据分析助手。你注重数据的准确性和逻辑的严密性。回答时先明确数据来源和假设,再给出分析过程和结论。偏好用数字和事实说话,避免模糊表述。',
translator: '你是一个专业的翻译助手。你精通多语言互译,注重信达雅。翻译时保留原文的语气和风格,专业术语保持一致。遇到歧义时给出多种翻译并标注区别。',
reviewer: '你是一个严格的代码审查助手。你关注:正确性(逻辑错误、边界情况)、安全性(注入、泄露)、性能(时间/空间复杂度)、可维护性(命名、结构、注释)。审查时先给出总体评价,再逐项列出问题并给出修复建议。',
};
let settingsModalEl: HTMLElement;
export function initSettingsModal(): void {
@@ -84,84 +74,6 @@ export function initSettingsModal(): void {
}, 500);
document.querySelector('#inputMaxTurns')!.addEventListener('input', saveMaxTurns);
// ── v4.2 人格模式切换 ──
const PERSONALITY_TEMPERATURES: Record<string, number> = {
default: -1, // 使用用户设置
coder: 0.3,
writer: 0.8,
analyst: 0.2,
translator: 0.4,
reviewer: 0.3,
};
document.querySelectorAll('.personality-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const personality = (btn as HTMLElement).dataset.personality || 'default';
state.set('personality', personality);
const db = state.get<ChatDB | null>(KEYS.DB);
if (db) await db.saveSetting('personality', personality);
// 更新按钮样式
document.querySelectorAll('.personality-btn').forEach(b => {
(b as HTMLElement).style.border = '2px solid var(--border-subtle)';
b.classList.remove('active');
});
(btn as HTMLElement).style.border = '2px solid var(--primary)';
btn.classList.add('active');
// 如果人格有推荐温度,自动调整
const recommendedTemp = PERSONALITY_TEMPERATURES[personality];
if (recommendedTemp >= 0) {
const tempSlider = document.querySelector('#inputTemperature') as HTMLInputElement;
tempSlider.value = String(recommendedTemp);
document.querySelector('#tempValue')!.textContent = recommendedTemp.toFixed(1);
state.set('temperature', recommendedTemp);
if (db) await db.saveSetting('temperature', recommendedTemp);
}
const names: Record<string, string> = {
default: '通用助手', coder: '编程助手', writer: '写作助手',
analyst: '数据分析', translator: '翻译助手', reviewer: '代码审查'
};
showToast(`已切换到 ${names[personality] || personality}`, 'success');
logSetting('人格模式', names[personality] || personality);
});
});
// ── v4.3 诊断系统 ──
document.querySelector('#btnDoctor')!.addEventListener('click', runDoctor);
// ── v4.3 Heartbeat 后台检查 ──
const toggleHB = document.querySelector<HTMLInputElement>('#toggleHeartbeat')!;
const selectHBInterval = document.querySelector<HTMLSelectElement>('#selectHeartbeatInterval')!;
toggleHB.addEventListener('change', async () => {
const enabled = toggleHB.checked;
state.set('heartbeatEnabled', enabled);
const db = state.get<ChatDB | null>(KEYS.DB);
if (db) await db.saveSetting('heartbeatEnabled', enabled);
if (enabled) {
startHeartbeat(parseInt(selectHBInterval.value));
showToast('后台检查已启用', 'success');
} else {
stopHeartbeat();
showToast('后台检查已关闭', 'info');
}
logSetting('后台检查', enabled ? '已启用' : '已关闭');
});
selectHBInterval.addEventListener('change', async () => {
const ms = parseInt(selectHBInterval.value);
state.set('heartbeatInterval', ms);
const db = state.get<ChatDB | null>(KEYS.DB);
if (db) await db.saveSetting('heartbeatInterval', ms);
if (state.get<boolean>('heartbeatEnabled', false)) {
stopHeartbeat();
startHeartbeat(ms);
}
logSetting('检查间隔', `${ms / 60000} 分钟`);
});
// ── v5.0 MCP 服务器管理 ──
async function renderMCPServerList(): Promise<void> {
const { getMCPServers, getMCPServerStatuses, toggleMCPServer, removeMCPServer } = await import('../services/mcp-client.js');
@@ -234,33 +146,6 @@ export function initSettingsModal(): void {
setTimeout(renderMCPServerList, 100);
});
// ── v5.0 Cron 定时任务 ──
document.querySelector('#btnAddCronTask')!.addEventListener('click', async () => {
const name = (document.querySelector('#inputCronName') as HTMLInputElement).value.trim();
const type = (document.querySelector('#selectCronType') as HTMLSelectElement).value as 'oneshot' | 'recurring';
const schedule = (document.querySelector('#inputCronSchedule') as HTMLInputElement).value.trim();
const message = (document.querySelector('#inputCronMessage') as HTMLInputElement).value.trim();
if (!name || !schedule || !message) {
showToast('请填写任务名称、时间和描述', 'warning');
return;
}
const { addCronTask } = await import('../services/cron-manager.js');
await addCronTask({ name, type, schedule, message, enabled: true });
// 清空输入框
(document.querySelector('#inputCronName') as HTMLInputElement).value = '';
(document.querySelector('#inputCronSchedule') as HTMLInputElement).value = '';
(document.querySelector('#inputCronMessage') as HTMLInputElement).value = '';
showToast(`定时任务已添加: ${name}`, 'success');
renderCronTaskList();
});
// 初始化 cron 列表
renderCronTaskList();
document.querySelector('#btnReleaseVRAM')!.addEventListener('click', async () => {
const api = state.get<OllamaAPI>(KEYS.API);
const model = (document.querySelector('#modelSelect') as HTMLSelectElement).value;
@@ -547,185 +432,3 @@ async function importSessions(filePath: string): Promise<void> {
}
}
// ══════════════════════════════════════════════
// v4.3 诊断系统
// ══════════════════════════════════════════════
async function runDoctor(): Promise<void> {
const resultsEl = document.querySelector('#doctorResults')!;
resultsEl.style.display = '';
resultsEl.innerHTML = '<div>🩺 正在诊断...</div>';
const checks: Array<{ name: string; status: 'ok' | 'warn' | 'error'; detail: string }> = [];
// 1. Ollama 连接
const api = state.get<OllamaAPI>(KEYS.API);
try {
const version = await api!.getVersion();
checks.push({ name: 'Ollama 连接', status: 'ok', detail: `v${version.version}` });
} catch (err) {
checks.push({ name: 'Ollama 连接', status: 'error', detail: `无法连接: ${(err as Error).message}` });
}
// 2. 模型可用性
try {
const models = await api!.listModels();
const count = models.models?.length || 0;
checks.push({ name: '已安装模型', status: count > 0 ? 'ok' : 'warn', detail: `${count} 个模型` });
} catch {
checks.push({ name: '已安装模型', status: 'error', detail: '无法获取模型列表' });
}
// 3. 当前模型能力
const model = (document.querySelector('#modelSelect') as HTMLSelectElement)?.value;
if (model) {
try {
const detail = await api!.showModel(model);
const caps = detail.capabilities || [];
const hasTools = caps.includes('tools');
const hasVision = caps.includes('vision');
const hasThink = caps.includes('thinking');
checks.push({
name: `模型能力 (${model})`,
status: hasTools ? 'ok' : 'warn',
detail: `Tool Calling: ${hasTools ? '✅' : '❌'} | Vision: ${hasVision ? '✅' : '❌'} | Think: ${hasThink ? '✅' : '❌'}`
});
} catch {
checks.push({ name: '模型能力', status: 'warn', detail: '无法检测模型能力' });
}
} else {
checks.push({ name: '当前模型', status: 'warn', detail: '未选择模型' });
}
// 4. SQLite 数据库
const db = state.get<ChatDB | null>(KEYS.DB);
if (db) {
try {
const sessions = await db.getAllSessions();
checks.push({ name: 'SQLite 数据库', status: 'ok', detail: `${sessions.length} 个会话` });
} catch (err) {
checks.push({ name: 'SQLite 数据库', status: 'error', detail: `错误: ${(err as Error).message}` });
}
} else {
checks.push({ name: 'SQLite 数据库', status: 'error', detail: '未初始化' });
}
// 5. 技能系统
try {
if (db) {
checks.push({ name: '技能系统', status: 'ok', detail: '已就绪' });
}
} catch {
checks.push({ name: '技能系统', status: 'warn', detail: '无法获取技能列表' });
}
// 6. 版本信息
const bridge = window.metonaDesktop;
if (bridge?.info) {
try {
const info = await bridge.info();
checks.push({ name: '应用信息', status: 'ok', detail: `v${info.version} | Electron ${info.electronVersion} | ${info.platform} ${info.arch}` });
} catch {
checks.push({ name: '应用信息', status: 'warn', detail: '无法获取' });
}
}
// 渲染结果
const statusIcon = (s: string) => s === 'ok' ? '✅' : s === 'warn' ? '⚠️' : '❌';
const allOk = checks.every(c => c.status === 'ok');
resultsEl.innerHTML = `
<div style="margin-bottom:6px;font-weight:600;">${allOk ? '✅ 所有检查通过' : '⚠️ 发现问题'}</div>
${checks.map(c => `<div style="margin:4px 0;">${statusIcon(c.status)} <strong>${c.name}</strong>: ${c.detail}</div>`).join('')}
`;
logInfo('系统诊断完成', `${checks.filter(c => c.status === 'ok').length}/${checks.length} 项通过`);
}
// ══════════════════════════════════════════════
// v4.3 Heartbeat 后台检查
// ══════════════════════════════════════════════
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
export function startHeartbeat(intervalMs: number): void {
stopHeartbeat();
heartbeatTimer = setInterval(async () => {
const api = state.get<OllamaAPI>(KEYS.API);
if (!api) return;
try {
await api.getVersion();
// 连接正常,静默
} catch {
// 连接异常,通知用户
const bridge = window.metonaDesktop;
if (bridge?.notify) {
bridge.notify('Metona Ollama', '⚠️ Ollama 服务连接异常,请检查是否正在运行');
}
logWarn('Heartbeat: Ollama 连接异常');
}
}, intervalMs);
logInfo(`Heartbeat 已启动`, `间隔: ${intervalMs / 60000} 分钟`);
}
export function stopHeartbeat(): void {
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
}
/** 应用启动时恢复 heartbeat */
export function restoreHeartbeat(): void {
const enabled = state.get<boolean>('heartbeatEnabled', false);
const interval = state.get<number>('heartbeatInterval', 3600000);
if (enabled) {
startHeartbeat(interval);
// 同步 UI 状态
const toggle = document.querySelector<HTMLInputElement>('#toggleHeartbeat');
if (toggle) toggle.checked = true;
const select = document.querySelector<HTMLSelectElement>('#selectHeartbeatInterval');
if (select) select.value = String(interval);
}
}
/** 渲染定时任务列表 */
async function renderCronTaskList(): Promise<void> {
const container = document.querySelector('#cronTaskList');
if (!container) return;
const { getCronTasks, removeCronTask, toggleCronTask } = await import('../services/cron-manager.js');
const tasks = await getCronTasks();
if (tasks.length === 0) {
container.innerHTML = '<p class="text-muted">无定时任务</p>';
return;
}
container.innerHTML = tasks.map(t => `
<div style="display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid var(--border-subtle);">
<span>${t.enabled ? '✅' : '⏸️'}</span>
<span style="flex:1;font-weight:500;">${t.name}</span>
<span class="text-muted" style="font-size:11px;">${t.type === 'oneshot' ? '一次性' : '周期'}</span>
<button class="btn btn-sm" onclick="window.__toggleCron('${t.id}')" style="padding:2px 6px;font-size:11px;">${t.enabled ? '禁用' : '启用'}</button>
<button class="btn btn-sm btn-danger" onclick="window.__deleteCron('${t.id}')" style="padding:2px 6px;font-size:11px;">删除</button>
</div>
`).join('');
}
// 全局方法供内联 onclick 调用
interface WindowWithCron extends Window {
__toggleCron?: (id: string) => Promise<void>;
__deleteCron?: (id: string) => Promise<void>;
}
(window as WindowWithCron).__toggleCron = async (id: string) => {
const { toggleCronTask } = await import('../services/cron-manager.js');
await toggleCronTask(id);
renderCronTaskList();
};
(window as WindowWithCron).__deleteCron = async (id: string) => {
const { removeCronTask } = await import('../services/cron-manager.js');
await removeCronTask(id);
renderCronTaskList();
};