perf(webchat): skip unnecessary full history reloads on final events (#20588)

Co-authored-by: amzzzzzzz <154392693+amzzzzzzz@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-02-22 21:16:05 +01:00
parent f2e9986813
commit dc6afeb4f8
4 changed files with 66 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ import {
} from "./app-settings.ts";
import { handleAgentEvent, resetToolStream, type AgentEventPayload } from "./app-tool-stream.ts";
import type { OpenClawApp } from "./app.ts";
import { shouldReloadHistoryForFinalEvent } from "./chat-event-reload.ts";
import { loadAgents } from "./controllers/agents.ts";
import { loadAssistantIdentity } from "./controllers/assistant-identity.ts";
import { loadChatHistory } from "./controllers/chat.ts";
@@ -256,7 +257,7 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {
}
}
}
if (state === "final") {
if (state === "final" && shouldReloadHistoryForFinalEvent(payload)) {
void loadChatHistory(host as unknown as OpenClawApp);
}
return;

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { shouldReloadHistoryForFinalEvent } from "./chat-event-reload.ts";
describe("shouldReloadHistoryForFinalEvent", () => {
it("returns false for non-final events", () => {
expect(
shouldReloadHistoryForFinalEvent({
runId: "run-1",
sessionKey: "main",
state: "delta",
message: { role: "assistant", content: [{ type: "text", text: "x" }] },
}),
).toBe(false);
});
it("returns true when final event has no message payload", () => {
expect(
shouldReloadHistoryForFinalEvent({
runId: "run-1",
sessionKey: "main",
state: "final",
}),
).toBe(true);
});
it("returns false when final event includes assistant payload", () => {
expect(
shouldReloadHistoryForFinalEvent({
runId: "run-1",
sessionKey: "main",
state: "final",
message: { role: "assistant", content: [{ type: "text", text: "done" }] },
}),
).toBe(false);
});
it("returns true when final event message role is non-assistant", () => {
expect(
shouldReloadHistoryForFinalEvent({
runId: "run-1",
sessionKey: "main",
state: "final",
message: { role: "user", content: [{ type: "text", text: "echo" }] },
}),
).toBe(true);
});
});

View File

@@ -0,0 +1,16 @@
import type { ChatEventPayload } from "./controllers/chat.ts";
export function shouldReloadHistoryForFinalEvent(payload?: ChatEventPayload): boolean {
if (!payload || payload.state !== "final") {
return false;
}
if (!payload.message || typeof payload.message !== "object") {
return true;
}
const message = payload.message as Record<string, unknown>;
const role = typeof message.role === "string" ? message.role.toLowerCase() : "";
if (role && role !== "assistant") {
return true;
}
return false;
}