fix: 优化后端服务不可用时的错误处理,移除本地模拟回复逻辑
This commit is contained in:
@ -19,7 +19,7 @@ import { dialogManager, DialogSession } from "../services/dialogService";
|
||||
import { userInteractionManager } from "../services/userInteraction";
|
||||
import { healthCheck } from "../services/apiClient";
|
||||
|
||||
import type { RunMode } from '../types/api';
|
||||
import type { RunMode } from "../types/api";
|
||||
|
||||
/** 是否使用后端服务(可通过配置控制) */
|
||||
let useBackendService = true;
|
||||
@ -45,7 +45,7 @@ export function setPendingPlanExecution(
|
||||
taskId: string
|
||||
): void {
|
||||
pendingPlanExecution = { panel, planTitle, extensionPath, taskId };
|
||||
console.log('[MessageHandler] 设置待执行计划:', planTitle, 'taskId:', taskId);
|
||||
console.log("[MessageHandler] 设置待执行计划:", planTitle, "taskId:", taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,29 +90,22 @@ export async function handleUserMessage(
|
||||
await handleUserMessageWithBackend(panel, text, extensionPath, mode);
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error("后端服务不可用,回退到本地模式:", error);
|
||||
// 后端不可用时,使用本地模拟回复
|
||||
}
|
||||
}
|
||||
|
||||
// 本地模拟回复(后端不可用时的 fallback)
|
||||
console.log("使用本地模拟回复");
|
||||
const reply = getMockReply(text);
|
||||
|
||||
// 记录AI回复到历史(允许失败)
|
||||
try {
|
||||
const historyManager = ChatHistoryManager.getInstance();
|
||||
await historyManager.addAiMessage(reply);
|
||||
} catch (error) {
|
||||
console.warn("记录AI回复历史失败:", error);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
console.error("后端服务不可用:", error);
|
||||
panel.webview.postMessage({
|
||||
command: "receiveMessage",
|
||||
text: reply,
|
||||
command: "updateStatus",
|
||||
text: "后端服务不可用",
|
||||
type: "error",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有 extensionPath,显示错误
|
||||
panel.webview.postMessage({
|
||||
command: "updateStatus",
|
||||
text: "无法处理消息:缺少必要参数",
|
||||
type: "error",
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +122,7 @@ async function handleUserMessageWithBackend(
|
||||
if (!currentSession || !currentSession.active) {
|
||||
currentSession = dialogManager.createSession(extensionPath, reuseTaskId);
|
||||
if (reuseTaskId) {
|
||||
console.log('[MessageHandler] 复用 taskId 创建会话:', reuseTaskId);
|
||||
console.log("[MessageHandler] 复用 taskId 创建会话:", reuseTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +136,9 @@ async function handleUserMessageWithBackend(
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
currentSession!.sendMessage(text, {
|
||||
currentSession!.sendMessage(
|
||||
text,
|
||||
{
|
||||
onText: (fullText, isStreaming) => {
|
||||
// 不再单独处理文本,统一通过 onSegmentUpdate 处理
|
||||
},
|
||||
@ -189,24 +184,27 @@ async function handleUserMessageWithBackend(
|
||||
});
|
||||
|
||||
// 最后一次发送完整的段落
|
||||
console.log('[MessageHandler] 对话完成, 段落数:', segments.length);
|
||||
console.log('[MessageHandler] segments 内容:', JSON.stringify(segments));
|
||||
console.log("[MessageHandler] 对话完成, 段落数:", segments.length);
|
||||
console.log(
|
||||
"[MessageHandler] segments 内容:",
|
||||
JSON.stringify(segments)
|
||||
);
|
||||
|
||||
const result = await panel.webview.postMessage({
|
||||
command: "updateSegments",
|
||||
segments: segments,
|
||||
isComplete: true,
|
||||
});
|
||||
console.log('[MessageHandler] postMessage 返回值:', result);
|
||||
console.log("[MessageHandler] postMessage 返回值:", result);
|
||||
|
||||
// 保存完整的 segments 到历史记录
|
||||
try {
|
||||
// 将完整的 segments 保存到一条 AI 消息中
|
||||
// 这样加载时可以完整还原对话样式
|
||||
const textContent = segments
|
||||
.filter(s => s.type === 'text' && s.content)
|
||||
.map(s => s.content)
|
||||
.join('\n');
|
||||
.filter((s) => s.type === "text" && s.content)
|
||||
.map((s) => s.content)
|
||||
.join("\n");
|
||||
|
||||
await historyManager.addAiMessage(textContent, undefined, segments);
|
||||
} catch (error) {
|
||||
@ -215,9 +213,19 @@ async function handleUserMessageWithBackend(
|
||||
|
||||
// 检查是否有待执行的计划(Plan 模式确认后自动执行)
|
||||
if (pendingPlanExecution) {
|
||||
const { panel: execPanel, planTitle, extensionPath: execPath, taskId: reuseTaskId } = pendingPlanExecution;
|
||||
const {
|
||||
panel: execPanel,
|
||||
planTitle,
|
||||
extensionPath: execPath,
|
||||
taskId: reuseTaskId,
|
||||
} = pendingPlanExecution;
|
||||
pendingPlanExecution = null;
|
||||
console.log('[MessageHandler] 自动执行计划:', planTitle, '复用 taskId:', reuseTaskId);
|
||||
console.log(
|
||||
"[MessageHandler] 自动执行计划:",
|
||||
planTitle,
|
||||
"复用 taskId:",
|
||||
reuseTaskId
|
||||
);
|
||||
|
||||
// 延迟一小段时间确保当前对话完全结束
|
||||
setTimeout(async () => {
|
||||
@ -227,11 +235,11 @@ async function handleUserMessageWithBackend(
|
||||
execPanel,
|
||||
`请按照刚才的计划执行:${planTitle}`,
|
||||
execPath,
|
||||
'agent',
|
||||
"agent",
|
||||
reuseTaskId // 复用 Plan 模式的 taskId
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('[MessageHandler] 自动执行计划失败:', err);
|
||||
console.error("[MessageHandler] 自动执行计划失败:", err);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
@ -253,7 +261,9 @@ async function handleUserMessageWithBackend(
|
||||
onNotification: (message) => {
|
||||
vscode.window.showInformationMessage(message);
|
||||
},
|
||||
}, mode);
|
||||
},
|
||||
mode
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -298,52 +308,52 @@ export async function handlePlanAction(
|
||||
planTitle: string,
|
||||
extensionPath: string
|
||||
): Promise<void> {
|
||||
console.log('[handlePlanAction] action:', action, 'planTitle:', planTitle);
|
||||
console.log("[handlePlanAction] action:", action, "planTitle:", planTitle);
|
||||
|
||||
switch (action) {
|
||||
case 'confirm':
|
||||
case "confirm":
|
||||
// 确认执行:切换到 Agent 模式并发送执行消息
|
||||
panel.webview.postMessage({
|
||||
command: 'switchMode',
|
||||
mode: 'agent'
|
||||
command: "switchMode",
|
||||
mode: "agent",
|
||||
});
|
||||
// 发送执行消息
|
||||
await handleUserMessage(
|
||||
panel,
|
||||
`请按照刚才的计划执行:${planTitle}`,
|
||||
extensionPath,
|
||||
'agent'
|
||||
"agent"
|
||||
);
|
||||
break;
|
||||
|
||||
case 'modify':
|
||||
case "modify":
|
||||
// 修改计划:提示用户输入修改建议
|
||||
const modification = await vscode.window.showInputBox({
|
||||
prompt: '请输入您对计划的修改建议',
|
||||
placeHolder: '例如:第2步需要先检查文件是否存在...',
|
||||
ignoreFocusOut: true
|
||||
prompt: "请输入您对计划的修改建议",
|
||||
placeHolder: "例如:第2步需要先检查文件是否存在...",
|
||||
ignoreFocusOut: true,
|
||||
});
|
||||
if (modification) {
|
||||
await handleUserMessage(
|
||||
panel,
|
||||
`请根据以下建议修改计划:${modification}`,
|
||||
extensionPath,
|
||||
'plan'
|
||||
"plan"
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cancel':
|
||||
case "cancel":
|
||||
// 取消计划:通知用户
|
||||
panel.webview.postMessage({
|
||||
command: 'addMessage',
|
||||
text: '计划已取消。',
|
||||
sender: 'bot'
|
||||
command: "addMessage",
|
||||
text: "计划已取消。",
|
||||
sender: "bot",
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn('[handlePlanAction] 未知操作:', action);
|
||||
console.warn("[handlePlanAction] 未知操作:", action);
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,7 +392,9 @@ function parseFileOperation(text: string): {
|
||||
}
|
||||
|
||||
// 匹配重命名文件:将 xxx.ts 重命名为 yyy.ts 或 把 xxx.ts 改名为 yyy.ts(优先匹配,避免被修改匹配)
|
||||
const renameMatch = lowerText.match(/(?:将|把)\s*(.+?\.\w+)\s*(?:重命名|改名)\s*(?:为|成)\s*(.+?\.\w+)/);
|
||||
const renameMatch = lowerText.match(
|
||||
/(?:将|把)\s*(.+?\.\w+)\s*(?:重命名|改名)\s*(?:为|成)\s*(.+?\.\w+)/
|
||||
);
|
||||
if (renameMatch) {
|
||||
const oldPath = renameMatch[1].trim();
|
||||
const newPath = renameMatch[2].trim();
|
||||
@ -397,7 +409,9 @@ function parseFileOperation(text: string): {
|
||||
// 格式1: 在 xxx.ts 中将 "aaa" 替换为 "bbb"
|
||||
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
|
||||
// 格式3: 将 xxx.ts 文件 'aaa' 替换为 'bbb'
|
||||
const replaceMatch1 = lowerText.match(/在\s*(.+?\.\w+)\s*(?:文件)?中?\s*(?:将|把)\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/);
|
||||
const replaceMatch1 = lowerText.match(
|
||||
/在\s*(.+?\.\w+)\s*(?:文件)?中?\s*(?:将|把)\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/
|
||||
);
|
||||
if (replaceMatch1) {
|
||||
const filePath = replaceMatch1[1].trim();
|
||||
const searchText = replaceMatch1[2].trim();
|
||||
@ -411,7 +425,9 @@ function parseFileOperation(text: string): {
|
||||
}
|
||||
|
||||
// 格式2: 将 xxx.ts 文件 "aaa" 替换为 "bbb"
|
||||
const replaceMatch2 = lowerText.match(/(?:将|把)\s*(.+?\.\w+)\s*(?:文件)?\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/);
|
||||
const replaceMatch2 = lowerText.match(
|
||||
/(?:将|把)\s*(.+?\.\w+)\s*(?:文件)?\s*["'](.+?)["']\s*替换\s*(?:为|成)\s*["'](.+?)["']/
|
||||
);
|
||||
if (replaceMatch2) {
|
||||
const filePath = replaceMatch2[1].trim();
|
||||
const searchText = replaceMatch2[2].trim();
|
||||
@ -739,41 +755,6 @@ export async function handleReplaceInFile(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模拟回复
|
||||
*/
|
||||
function getMockReply(question: string): string {
|
||||
const replies = [
|
||||
`已收到您的问题:"${question}"
|
||||
|
||||
这是一个演示版本,实际需要连接AI服务。
|
||||
|
||||
示例回复:这是一个计数器模板:
|
||||
\`\`\`verilog
|
||||
module counter (
|
||||
input clk,
|
||||
input rst_n,
|
||||
output reg [3:0] count
|
||||
);
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) count <= 0;
|
||||
else count <= count + 1;
|
||||
end
|
||||
endmodule
|
||||
\`\`\``,
|
||||
|
||||
`感谢提问!关于"${question}",在真实版本中我会:
|
||||
1. 分析您的代码上下文
|
||||
2. 提供优化建议
|
||||
3. 生成完整代码
|
||||
4. 解释设计原理
|
||||
|
||||
当前是演示版,请点击侧边栏按钮快速生成代码。`,
|
||||
];
|
||||
|
||||
return replies[Math.floor(Math.random() * replies.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将代码插入到编辑器
|
||||
*/
|
||||
@ -866,7 +847,8 @@ async function handleVCDGeneration(
|
||||
|
||||
if (!projectCheck.hasTestbench) {
|
||||
errorMsg += "• ❌ 缺少 testbench 文件\n";
|
||||
errorMsg += "\n提示: testbench 文件应包含 $dumpfile 和 $dumpvars 语句来生成 VCD 文件。\n";
|
||||
errorMsg +=
|
||||
"\n提示: testbench 文件应包含 $dumpfile 和 $dumpvars 语句来生成 VCD 文件。\n";
|
||||
} else {
|
||||
errorMsg += `• ✅ Testbench: ${projectCheck.testbenchFile}\n`;
|
||||
}
|
||||
@ -910,9 +892,7 @@ async function handleVCDGeneration(
|
||||
fileName: fileName,
|
||||
});
|
||||
|
||||
vscode.window.showInformationMessage(
|
||||
`VCD 文件生成成功: ${fileName}`
|
||||
);
|
||||
vscode.window.showInformationMessage(`VCD 文件生成成功: ${fileName}`);
|
||||
} else {
|
||||
panel.webview.postMessage({
|
||||
command: "receiveMessage",
|
||||
|
||||
Reference in New Issue
Block a user