mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-12 05:13:02 +00:00
fix: bound in-flight history snapshots
This commit is contained in:
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
abortChatRunById,
|
||||
abortChatRunsForProvider,
|
||||
boundInFlightRunSnapshotForChatHistory,
|
||||
isChatStopCommandText,
|
||||
registerChatAbortController,
|
||||
resolveAgentRunExpiresAtMs,
|
||||
@@ -544,4 +545,24 @@ describe("resolveInFlightRunSnapshot", () => {
|
||||
}),
|
||||
).toEqual({ runId: "run-b", text: "b" });
|
||||
});
|
||||
|
||||
it("keeps in-flight text when it fits the chat history budget", () => {
|
||||
expect(
|
||||
boundInFlightRunSnapshotForChatHistory({
|
||||
snapshot: { runId: "run-1", text: "partial" },
|
||||
messages: [],
|
||||
maxBytes: 1_000,
|
||||
}),
|
||||
).toEqual({ runId: "run-1", text: "partial" });
|
||||
});
|
||||
|
||||
it("drops oversized in-flight text but keeps the run id for adoption", () => {
|
||||
expect(
|
||||
boundInFlightRunSnapshotForChatHistory({
|
||||
snapshot: { runId: "run-1", text: "x".repeat(1_000) },
|
||||
messages: [],
|
||||
maxBytes: 100,
|
||||
}),
|
||||
).toEqual({ runId: "run-1", text: "" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { resolveDefaultAgentId } from "../agents/agent-scope-config.js";
|
||||
import { isAbortRequestText } from "../auto-reply/reply/abort-primitives.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { emitAgentEvent } from "../infra/agent-events.js";
|
||||
import { jsonUtf8Bytes } from "../infra/json-utf8-bytes.js";
|
||||
|
||||
const DEFAULT_CHAT_RUN_ABORT_GRACE_MS = 60_000;
|
||||
|
||||
@@ -237,6 +238,25 @@ export function resolveInFlightRunSnapshot(params: {
|
||||
return { runId: best.runId, text: params.chatRunBuffers?.get(best.runId) ?? "" };
|
||||
}
|
||||
|
||||
export function boundInFlightRunSnapshotForChatHistory(params: {
|
||||
snapshot: { runId: string; text: string } | undefined;
|
||||
messages: unknown[];
|
||||
maxBytes: number;
|
||||
}): { runId: string; text: string } | undefined {
|
||||
if (!params.snapshot?.text) {
|
||||
return params.snapshot;
|
||||
}
|
||||
const messagesBytes = jsonUtf8Bytes(params.messages);
|
||||
const snapshotBytes = jsonUtf8Bytes(params.snapshot);
|
||||
if (messagesBytes + snapshotBytes <= params.maxBytes) {
|
||||
return params.snapshot;
|
||||
}
|
||||
// The run id is the recovery contract; buffered partial text is opportunistic.
|
||||
// If it would break the history payload budget, keep adoption and wait for the
|
||||
// next live delta/final instead of sending an oversized chat.history response.
|
||||
return { runId: params.snapshot.runId, text: "" };
|
||||
}
|
||||
|
||||
export type ChatAbortOps = {
|
||||
chatAbortControllers: Map<string, ChatAbortControllerEntry>;
|
||||
chatRunBuffers: Map<string, string>;
|
||||
|
||||
@@ -99,6 +99,7 @@ import {
|
||||
} from "../../utils/message-channel.js";
|
||||
import {
|
||||
abortChatRunById,
|
||||
boundInFlightRunSnapshotForChatHistory,
|
||||
type ChatAbortControllerEntry,
|
||||
type ChatAbortOps,
|
||||
isChatStopCommandText,
|
||||
@@ -2556,6 +2557,11 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
agentId: requestedAgentId,
|
||||
defaultAgentId: resolveDefaultAgentId(cfg),
|
||||
});
|
||||
const boundedInFlightRun = boundInFlightRunSnapshotForChatHistory({
|
||||
snapshot: inFlightRun,
|
||||
messages: bounded.messages,
|
||||
maxBytes: maxHistoryBytes,
|
||||
});
|
||||
respond(true, {
|
||||
sessionKey,
|
||||
sessionId,
|
||||
@@ -2565,7 +2571,7 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
thinkingLevel,
|
||||
fastMode: entry?.fastMode,
|
||||
verboseLevel,
|
||||
...(inFlightRun ? { inFlightRun } : {}),
|
||||
...(boundedInFlightRun ? { inFlightRun: boundedInFlightRun } : {}),
|
||||
});
|
||||
},
|
||||
"chat.message.get": async ({ params, respond, context }) => {
|
||||
|
||||
Reference in New Issue
Block a user