mirror of
https://github.com/yuangyaa/openclaw.git
synced 2026-07-14 11:07:29 +08:00
* fix(discord): defer component interactions to prevent timeout Discord requires interaction responses within 3 seconds. Button clicks were routed through the LLM pipeline before responding, exceeding this window and showing 'This interaction failed' to users. Now immediately defers the interaction, then processes the agent response asynchronously. Fixes #16262 * fix: harden deferred interaction replies and silent chat finals (#16287) (thanks @robbyczgw-cla) --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import { Container, Spacer, Text } from "@mariozechner/pi-tui";
|
|
import { theme } from "../theme/theme.js";
|
|
import { AssistantMessageComponent } from "./assistant-message.js";
|
|
import { ToolExecutionComponent } from "./tool-execution.js";
|
|
import { UserMessageComponent } from "./user-message.js";
|
|
|
|
export class ChatLog extends Container {
|
|
private toolById = new Map<string, ToolExecutionComponent>();
|
|
private streamingRuns = new Map<string, AssistantMessageComponent>();
|
|
private toolsExpanded = false;
|
|
|
|
clearAll() {
|
|
this.clear();
|
|
this.toolById.clear();
|
|
this.streamingRuns.clear();
|
|
}
|
|
|
|
addSystem(text: string) {
|
|
this.addChild(new Spacer(1));
|
|
this.addChild(new Text(theme.system(text), 1, 0));
|
|
}
|
|
|
|
addUser(text: string) {
|
|
this.addChild(new UserMessageComponent(text));
|
|
}
|
|
|
|
private resolveRunId(runId?: string) {
|
|
return runId ?? "default";
|
|
}
|
|
|
|
startAssistant(text: string, runId?: string) {
|
|
const component = new AssistantMessageComponent(text);
|
|
this.streamingRuns.set(this.resolveRunId(runId), component);
|
|
this.addChild(component);
|
|
return component;
|
|
}
|
|
|
|
updateAssistant(text: string, runId?: string) {
|
|
const effectiveRunId = this.resolveRunId(runId);
|
|
const existing = this.streamingRuns.get(effectiveRunId);
|
|
if (!existing) {
|
|
this.startAssistant(text, runId);
|
|
return;
|
|
}
|
|
existing.setText(text);
|
|
}
|
|
|
|
finalizeAssistant(text: string, runId?: string) {
|
|
const effectiveRunId = this.resolveRunId(runId);
|
|
const existing = this.streamingRuns.get(effectiveRunId);
|
|
if (existing) {
|
|
existing.setText(text);
|
|
this.streamingRuns.delete(effectiveRunId);
|
|
return;
|
|
}
|
|
this.addChild(new AssistantMessageComponent(text));
|
|
}
|
|
|
|
dropAssistant(runId?: string) {
|
|
const effectiveRunId = this.resolveRunId(runId);
|
|
const existing = this.streamingRuns.get(effectiveRunId);
|
|
if (!existing) {
|
|
return;
|
|
}
|
|
this.removeChild(existing);
|
|
this.streamingRuns.delete(effectiveRunId);
|
|
}
|
|
|
|
startTool(toolCallId: string, toolName: string, args: unknown) {
|
|
const existing = this.toolById.get(toolCallId);
|
|
if (existing) {
|
|
existing.setArgs(args);
|
|
return existing;
|
|
}
|
|
const component = new ToolExecutionComponent(toolName, args);
|
|
component.setExpanded(this.toolsExpanded);
|
|
this.toolById.set(toolCallId, component);
|
|
this.addChild(component);
|
|
return component;
|
|
}
|
|
|
|
updateToolArgs(toolCallId: string, args: unknown) {
|
|
const existing = this.toolById.get(toolCallId);
|
|
if (!existing) {
|
|
return;
|
|
}
|
|
existing.setArgs(args);
|
|
}
|
|
|
|
updateToolResult(
|
|
toolCallId: string,
|
|
result: unknown,
|
|
opts?: { isError?: boolean; partial?: boolean },
|
|
) {
|
|
const existing = this.toolById.get(toolCallId);
|
|
if (!existing) {
|
|
return;
|
|
}
|
|
if (opts?.partial) {
|
|
existing.setPartialResult(result as Record<string, unknown>);
|
|
return;
|
|
}
|
|
existing.setResult(result as Record<string, unknown>, {
|
|
isError: opts?.isError,
|
|
});
|
|
}
|
|
|
|
setToolsExpanded(expanded: boolean) {
|
|
this.toolsExpanded = expanded;
|
|
for (const tool of this.toolById.values()) {
|
|
tool.setExpanded(expanded);
|
|
}
|
|
}
|
|
}
|