优化进度事件处理,添加请求持续时间统计
This commit is contained in:
@@ -2,3 +2,5 @@ node_modules/
|
|||||||
.opencode/node_modules/
|
.opencode/node_modules/
|
||||||
.local.env
|
.local.env
|
||||||
.vscode
|
.vscode
|
||||||
|
data/
|
||||||
|
logs/
|
||||||
|
|||||||
+65
-15
@@ -240,7 +240,6 @@ export const buildChatRouter = (
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
res.write(toSse("done", { session_id: clientSessionId }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -341,6 +340,16 @@ type StreamPromptOptions = {
|
|||||||
write: (event: string, data: Record<string, unknown>) => void;
|
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 ({
|
const streamPromptResponse = async ({
|
||||||
runtime,
|
runtime,
|
||||||
opencodeSessionId,
|
opencodeSessionId,
|
||||||
@@ -353,6 +362,9 @@ const streamPromptResponse = async ({
|
|||||||
}: StreamPromptOptions): Promise<{ aborted: boolean; failed: boolean }> => {
|
}: StreamPromptOptions): Promise<{ aborted: boolean; failed: boolean }> => {
|
||||||
const eventStream = await runtime.subscribeEvents();
|
const eventStream = await runtime.subscribeEvents();
|
||||||
const iterator = eventStream[Symbol.asyncIterator]();
|
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 emittedToolParts = new Set<string>();
|
||||||
const partTypes = new Map<string, Part["type"]>();
|
const partTypes = new Map<string, Part["type"]>();
|
||||||
const pendingPartTextDeltas = new Map<string, string[]>();
|
const pendingPartTextDeltas = new Map<string, string[]>();
|
||||||
@@ -375,8 +387,48 @@ const streamPromptResponse = async ({
|
|||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
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", {
|
write("progress", {
|
||||||
session_id: clientSessionId,
|
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",
|
id: "request-received",
|
||||||
phase: "start",
|
phase: "start",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -438,8 +490,7 @@ const streamPromptResponse = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.status") {
|
if (event.type === "session.status") {
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: "session-status",
|
id: "session-status",
|
||||||
phase: "session",
|
phase: "session",
|
||||||
status: event.properties.status.type === "idle" ? "completed" : "running",
|
status: event.properties.status.type === "idle" ? "completed" : "running",
|
||||||
@@ -515,8 +566,7 @@ const streamPromptResponse = async ({
|
|||||||
reasoningDeltas.get(part.id) ?? [],
|
reasoningDeltas.get(part.id) ?? [],
|
||||||
part.time.end,
|
part.time.end,
|
||||||
);
|
);
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: part.id,
|
id: part.id,
|
||||||
phase: "planning",
|
phase: "planning",
|
||||||
status: part.time.end ? "completed" : "running",
|
status: part.time.end ? "completed" : "running",
|
||||||
@@ -530,8 +580,7 @@ const streamPromptResponse = async ({
|
|||||||
const isToolFinalState =
|
const isToolFinalState =
|
||||||
part.state.status === "completed" || part.state.status === "error";
|
part.state.status === "completed" || part.state.status === "error";
|
||||||
|
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: part.id,
|
id: part.id,
|
||||||
phase: "tool",
|
phase: "tool",
|
||||||
status: normalizeToolStatus(part.state.status),
|
status: normalizeToolStatus(part.state.status),
|
||||||
@@ -587,8 +636,7 @@ const streamPromptResponse = async ({
|
|||||||
const completed = event.properties.todos.filter(
|
const completed = event.properties.todos.filter(
|
||||||
(todo) => todo.status === "completed",
|
(todo) => todo.status === "completed",
|
||||||
).length;
|
).length;
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: "todo-progress",
|
id: "todo-progress",
|
||||||
phase: "planning",
|
phase: "planning",
|
||||||
status: completed === event.properties.todos.length ? "completed" : "running",
|
status: completed === event.properties.todos.length ? "completed" : "running",
|
||||||
@@ -607,6 +655,7 @@ const streamPromptResponse = async ({
|
|||||||
? getErrorMessage(event.properties.error)
|
? getErrorMessage(event.properties.error)
|
||||||
: "opencode session error",
|
: "opencode session error",
|
||||||
detail: event.properties.error?.name,
|
detail: event.properties.error?.name,
|
||||||
|
total_duration_ms: Math.max(0, Date.now() - requestStartedAt),
|
||||||
});
|
});
|
||||||
failed = true;
|
failed = true;
|
||||||
done = true;
|
done = true;
|
||||||
@@ -614,8 +663,7 @@ const streamPromptResponse = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.idle") {
|
if (event.type === "session.idle") {
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: "session-status",
|
id: "session-status",
|
||||||
phase: "session",
|
phase: "session",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
@@ -641,16 +689,14 @@ const streamPromptResponse = async ({
|
|||||||
if (!emittedText) {
|
if (!emittedText) {
|
||||||
await emitFallbackMessage(runtime, opencodeSessionId, clientSessionId, write);
|
await emitFallbackMessage(runtime, opencodeSessionId, clientSessionId, write);
|
||||||
}
|
}
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: "request-received",
|
id: "request-received",
|
||||||
phase: "start",
|
phase: "start",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
title: "请求处理完成",
|
title: "请求处理完成",
|
||||||
detail: "本次请求的分析、工具执行和结果整理流程已经完成。",
|
detail: "本次请求的分析、工具执行和结果整理流程已经完成。",
|
||||||
});
|
});
|
||||||
write("progress", {
|
emitProgress({
|
||||||
session_id: clientSessionId,
|
|
||||||
id: "request-completed",
|
id: "request-completed",
|
||||||
phase: "complete",
|
phase: "complete",
|
||||||
status: "completed",
|
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 };
|
return { aborted: false, failed: false };
|
||||||
} finally {
|
} finally {
|
||||||
await iterator.return?.(undefined);
|
await iterator.return?.(undefined);
|
||||||
|
|||||||
Reference in New Issue
Block a user