优化进度事件处理,添加请求持续时间统计

This commit is contained in:
2026-05-13 17:33:40 +08:00
parent 319b3c8ea5
commit 3021fc42ec
2 changed files with 70 additions and 18 deletions
+3 -1
View File
@@ -1,4 +1,6 @@
node_modules/
.opencode/node_modules/
.local.env
.vscode
.vscode
data/
logs/
+67 -17
View File
@@ -240,7 +240,6 @@ export const buildChatRouter = (
}),
);
}
res.write(toSse("done", { session_id: clientSessionId }));
}
}
} finally {
@@ -341,6 +340,16 @@ type StreamPromptOptions = {
write: (event: string, data: Record<string, unknown>) => void;
};
type ProgressStatus = "running" | "completed" | "error";
type ProgressPayload = {
id: string;
phase: string;
status: ProgressStatus;
title: string;
detail?: string;
};
const streamPromptResponse = async ({
runtime,
opencodeSessionId,
@@ -353,6 +362,9 @@ const streamPromptResponse = async ({
}: StreamPromptOptions): Promise<{ aborted: boolean; failed: boolean }> => {
const eventStream = await runtime.subscribeEvents();
const iterator = eventStream[Symbol.asyncIterator]();
const requestStartedAt = Date.now();
const progressStartedAtMap = new Map<string, number>();
const finalizedProgressIds = new Set<string>();
const emittedToolParts = new Set<string>();
const partTypes = new Map<string, Part["type"]>();
const pendingPartTextDeltas = new Map<string, string[]>();
@@ -375,8 +387,48 @@ const streamPromptResponse = async ({
})
: null;
write("progress", {
session_id: clientSessionId,
const emitProgress = ({ id, phase, status, title, detail }: ProgressPayload) => {
if (status === "running" && finalizedProgressIds.has(id)) {
return;
}
const now = Date.now();
const startedAt = progressStartedAtMap.get(id) ?? now;
if (!progressStartedAtMap.has(id)) {
progressStartedAtMap.set(id, startedAt);
}
if (status === "running") {
write("progress", {
session_id: clientSessionId,
id,
phase,
status,
title,
detail,
started_at: startedAt,
elapsed_ms: Math.max(0, now - startedAt),
});
return;
}
const durationMs = Math.max(0, now - startedAt);
finalizedProgressIds.add(id);
progressStartedAtMap.delete(id);
write("progress", {
session_id: clientSessionId,
id,
phase,
status,
title,
detail,
started_at: startedAt,
ended_at: now,
duration_ms: durationMs,
});
};
emitProgress({
id: "request-received",
phase: "start",
status: "running",
@@ -438,8 +490,7 @@ const streamPromptResponse = async ({
}
if (event.type === "session.status") {
write("progress", {
session_id: clientSessionId,
emitProgress({
id: "session-status",
phase: "session",
status: event.properties.status.type === "idle" ? "completed" : "running",
@@ -515,8 +566,7 @@ const streamPromptResponse = async ({
reasoningDeltas.get(part.id) ?? [],
part.time.end,
);
write("progress", {
session_id: clientSessionId,
emitProgress({
id: part.id,
phase: "planning",
status: part.time.end ? "completed" : "running",
@@ -530,8 +580,7 @@ const streamPromptResponse = async ({
const isToolFinalState =
part.state.status === "completed" || part.state.status === "error";
write("progress", {
session_id: clientSessionId,
emitProgress({
id: part.id,
phase: "tool",
status: normalizeToolStatus(part.state.status),
@@ -587,8 +636,7 @@ const streamPromptResponse = async ({
const completed = event.properties.todos.filter(
(todo) => todo.status === "completed",
).length;
write("progress", {
session_id: clientSessionId,
emitProgress({
id: "todo-progress",
phase: "planning",
status: completed === event.properties.todos.length ? "completed" : "running",
@@ -607,6 +655,7 @@ const streamPromptResponse = async ({
? getErrorMessage(event.properties.error)
: "opencode session error",
detail: event.properties.error?.name,
total_duration_ms: Math.max(0, Date.now() - requestStartedAt),
});
failed = true;
done = true;
@@ -614,8 +663,7 @@ const streamPromptResponse = async ({
}
if (event.type === "session.idle") {
write("progress", {
session_id: clientSessionId,
emitProgress({
id: "session-status",
phase: "session",
status: "completed",
@@ -641,16 +689,14 @@ const streamPromptResponse = async ({
if (!emittedText) {
await emitFallbackMessage(runtime, opencodeSessionId, clientSessionId, write);
}
write("progress", {
session_id: clientSessionId,
emitProgress({
id: "request-received",
phase: "start",
status: "completed",
title: "请求处理完成",
detail: "本次请求的分析、工具执行和结果整理流程已经完成。",
});
write("progress", {
session_id: clientSessionId,
emitProgress({
id: "request-completed",
phase: "complete",
status: "completed",
@@ -659,6 +705,10 @@ const streamPromptResponse = async ({
? "最终回答已生成并推送到前端。"
: "已完成分析,并通过兜底消息补发最终回答内容。",
});
write("done", {
session_id: clientSessionId,
total_duration_ms: Math.max(0, Date.now() - requestStartedAt),
});
return { aborted: false, failed: false };
} finally {
await iterator.return?.(undefined);