优化进度事件处理,添加请求持续时间统计
This commit is contained in:
+3
-1
@@ -1,4 +1,6 @@
|
||||
node_modules/
|
||||
.opencode/node_modules/
|
||||
.local.env
|
||||
.vscode
|
||||
.vscode
|
||||
data/
|
||||
logs/
|
||||
|
||||
+67
-17
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user