mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
96885b5959
Flatten all packages from packages/v1/* and packages/v2/* into packages/* — every package now lives directly under the @copilotkit/ scope with no v1/v2 subdirectories. - Move all v1 packages (react-core, react-ui, runtime, shared, etc.) from packages/v1/* to packages/* - Absorb v2 react code into packages/react-core/src/v2/ (exported via /v2 subpath) - Absorb v2 agent code into packages/runtime/src/agent/ (exported via /v2 subpath) - Move v2 packages (core, angular, demo-agents, etc.) to packages/* - Replace all @copilotkitnext/* imports with @copilotkit/* equivalents - Keep @copilotkitnext/angular as the sole exception (angular remains on next) - Update CI workflows, renovate config, release scripts for flat structure - No public API surface changes — all exports fields are preserved Co-authored-by: Alem Tuzlak <t.zlak@hotmail.com> Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
155 lines
4.2 KiB
TypeScript
155 lines
4.2 KiB
TypeScript
import { BaseEvent, EventType, RunErrorEvent } from "@ag-ui/client";
|
|
import { randomUUID } from "./utils";
|
|
|
|
interface FinalizeRunOptions {
|
|
stopRequested?: boolean;
|
|
interruptionMessage?: string;
|
|
}
|
|
|
|
const defaultStopMessage = "Run stopped by user";
|
|
const defaultAbruptEndMessage = "Run ended without emitting a terminal event";
|
|
|
|
export function finalizeRunEvents(
|
|
events: BaseEvent[],
|
|
options: FinalizeRunOptions = {},
|
|
): BaseEvent[] {
|
|
const { stopRequested = false, interruptionMessage } = options;
|
|
|
|
const resolvedStopMessage = interruptionMessage ?? defaultStopMessage;
|
|
const resolvedAbruptMessage =
|
|
interruptionMessage && interruptionMessage !== defaultStopMessage
|
|
? interruptionMessage
|
|
: defaultAbruptEndMessage;
|
|
|
|
const appended: BaseEvent[] = [];
|
|
|
|
const openMessageIds = new Set<string>();
|
|
const openToolCalls = new Map<
|
|
string,
|
|
{
|
|
hasEnd: boolean;
|
|
hasResult: boolean;
|
|
}
|
|
>();
|
|
|
|
for (const event of events) {
|
|
switch (event.type) {
|
|
case EventType.TEXT_MESSAGE_START: {
|
|
const messageId = (event as { messageId?: string }).messageId;
|
|
if (typeof messageId === "string") {
|
|
openMessageIds.add(messageId);
|
|
}
|
|
break;
|
|
}
|
|
case EventType.TEXT_MESSAGE_END: {
|
|
const messageId = (event as { messageId?: string }).messageId;
|
|
if (typeof messageId === "string") {
|
|
openMessageIds.delete(messageId);
|
|
}
|
|
break;
|
|
}
|
|
case EventType.TOOL_CALL_START: {
|
|
const toolCallId = (event as { toolCallId?: string }).toolCallId;
|
|
if (typeof toolCallId === "string") {
|
|
openToolCalls.set(toolCallId, {
|
|
hasEnd: false,
|
|
hasResult: false,
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case EventType.TOOL_CALL_END: {
|
|
const toolCallId = (event as { toolCallId?: string }).toolCallId;
|
|
const info = toolCallId ? openToolCalls.get(toolCallId) : undefined;
|
|
if (info) {
|
|
info.hasEnd = true;
|
|
}
|
|
break;
|
|
}
|
|
case EventType.TOOL_CALL_RESULT: {
|
|
const toolCallId = (event as { toolCallId?: string }).toolCallId;
|
|
const info = toolCallId ? openToolCalls.get(toolCallId) : undefined;
|
|
if (info) {
|
|
info.hasResult = true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const hasRunFinished = events.some(
|
|
(event) => event.type === EventType.RUN_FINISHED,
|
|
);
|
|
const hasRunError = events.some(
|
|
(event) => event.type === EventType.RUN_ERROR,
|
|
);
|
|
const hasTerminalEvent = hasRunFinished || hasRunError;
|
|
const terminalEventMissing = !hasTerminalEvent;
|
|
|
|
for (const messageId of openMessageIds) {
|
|
const endEvent = {
|
|
type: EventType.TEXT_MESSAGE_END,
|
|
messageId,
|
|
} as BaseEvent;
|
|
events.push(endEvent);
|
|
appended.push(endEvent);
|
|
}
|
|
|
|
for (const [toolCallId, info] of openToolCalls) {
|
|
if (!info.hasEnd) {
|
|
const endEvent = {
|
|
type: EventType.TOOL_CALL_END,
|
|
toolCallId,
|
|
} as BaseEvent;
|
|
events.push(endEvent);
|
|
appended.push(endEvent);
|
|
}
|
|
|
|
if (terminalEventMissing && !info.hasResult) {
|
|
const resultEvent = {
|
|
type: EventType.TOOL_CALL_RESULT,
|
|
toolCallId,
|
|
messageId: `${toolCallId ?? randomUUID()}-result`,
|
|
role: "tool",
|
|
content: JSON.stringify(
|
|
stopRequested
|
|
? {
|
|
status: "stopped",
|
|
reason: "stop_requested",
|
|
message: resolvedStopMessage,
|
|
}
|
|
: {
|
|
status: "error",
|
|
reason: "missing_terminal_event",
|
|
message: resolvedAbruptMessage,
|
|
},
|
|
),
|
|
} as BaseEvent;
|
|
events.push(resultEvent);
|
|
appended.push(resultEvent);
|
|
}
|
|
}
|
|
|
|
if (terminalEventMissing) {
|
|
if (stopRequested) {
|
|
const finishedEvent = {
|
|
type: EventType.RUN_FINISHED,
|
|
} as BaseEvent;
|
|
events.push(finishedEvent);
|
|
appended.push(finishedEvent);
|
|
} else {
|
|
const errorEvent: RunErrorEvent = {
|
|
type: EventType.RUN_ERROR,
|
|
message: resolvedAbruptMessage,
|
|
code: "INCOMPLETE_STREAM",
|
|
};
|
|
events.push(errorEvent);
|
|
appended.push(errorEvent);
|
|
}
|
|
}
|
|
|
|
return appended;
|
|
}
|