@copilotkit/runtime
Version:
<div align="center"> <a href="https://copilotkit.ai" target="_blank"> <img src="https://github.com/copilotkit/copilotkit/raw/main/assets/banner.png" alt="CopilotKit Logo"> </a>
191 lines (172 loc) • 6.04 kB
text/typescript
import {
RunAgentInput,
EventType,
CustomEvent,
TextMessageStartEvent,
TextMessageContentEvent,
TextMessageEndEvent,
ToolCallStartEvent,
ToolCallArgsEvent,
ToolCallEndEvent,
} from "@ag-ui/client";
import { map } from "rxjs";
import { LangGraphEventTypes } from "../../../agents/langgraph/events";
import { RawEvent } from "@ag-ui/core";
import {
LangGraphAgent as AGUILangGraphAgent,
type LangGraphAgentConfig,
ProcessedEvents,
} from "@ag-ui/langgraph";
import { Message as LangGraphMessage } from "@langchain/langgraph-sdk/dist/types.messages";
export interface PredictStateTool {
tool: string;
state_key: string;
tool_argument: string;
}
export type State = Record<string, any>;
export type TextMessageEvents =
| TextMessageStartEvent
| TextMessageContentEvent
| TextMessageEndEvent;
export type ToolCallEvents = ToolCallStartEvent | ToolCallArgsEvent | ToolCallEndEvent;
export enum CustomEventNames {
CopilotKitManuallyEmitMessage = "copilotkit_manually_emit_message",
CopilotKitManuallyEmitToolCall = "copilotkit_manually_emit_tool_call",
CopilotKitManuallyEmitIntermediateState = "copilotkit_manually_emit_intermediate_state",
CopilotKitExit = "copilotkit_exit",
}
export class LangGraphAgent extends AGUILangGraphAgent {
constructor(config: LangGraphAgentConfig) {
super(config);
}
dispatchEvent(event: ProcessedEvents) {
if (event.type === EventType.CUSTOM) {
// const event = processedEvent as unknown as CustomEvent;
const customEvent = event as unknown as CustomEvent;
if (customEvent.name === CustomEventNames.CopilotKitManuallyEmitMessage) {
this.subscriber.next({
type: EventType.TEXT_MESSAGE_START,
role: "assistant",
messageId: customEvent.value.message_id,
rawEvent: event,
});
this.subscriber.next({
type: EventType.TEXT_MESSAGE_CONTENT,
messageId: customEvent.value.message_id,
delta: customEvent.value.message,
rawEvent: event,
});
this.subscriber.next({
type: EventType.TEXT_MESSAGE_END,
messageId: customEvent.value.message_id,
rawEvent: event,
});
return true;
}
if (customEvent.name === CustomEventNames.CopilotKitManuallyEmitToolCall) {
this.subscriber.next({
type: EventType.TOOL_CALL_START,
toolCallId: customEvent.value.id,
toolCallName: customEvent.value.name,
parentMessageId: customEvent.value.id,
rawEvent: event,
});
this.subscriber.next({
type: EventType.TOOL_CALL_ARGS,
toolCallId: customEvent.value.id,
delta: customEvent.value.args,
rawEvent: event,
});
this.subscriber.next({
type: EventType.TOOL_CALL_END,
toolCallId: customEvent.value.id,
rawEvent: event,
});
return true;
}
if (customEvent.name === CustomEventNames.CopilotKitManuallyEmitIntermediateState) {
this.activeRun.manuallyEmittedState = customEvent.value;
this.dispatchEvent({
type: EventType.STATE_SNAPSHOT,
snapshot: this.getStateSnapshot(this.activeRun.manuallyEmittedState),
rawEvent: event,
});
return true;
}
if (customEvent.name === CustomEventNames.CopilotKitExit) {
this.subscriber.next({
type: EventType.CUSTOM,
name: "Exit",
value: true,
});
return true;
}
}
// Intercept all text message and tool call events and check if should disable
const rawEvent = (event as ToolCallEvents | TextMessageEvents).rawEvent;
if (!rawEvent) {
this.subscriber.next(event);
return true;
}
const isMessageEvent =
event.type === EventType.TEXT_MESSAGE_START ||
event.type === EventType.TEXT_MESSAGE_CONTENT ||
event.type === EventType.TEXT_MESSAGE_END;
const isToolEvent =
event.type === EventType.TOOL_CALL_START ||
event.type === EventType.TOOL_CALL_ARGS ||
event.type === EventType.TOOL_CALL_END;
if ("copilotkit:emit-tool-calls" in (rawEvent.metadata || {})) {
if (rawEvent.metadata["copilotkit:emit-tool-calls"] === false && isToolEvent) {
return false;
}
}
if ("copilotkit:emit-messages" in (rawEvent.metadata || {})) {
if (rawEvent.metadata["copilotkit:emit-messages"] === false && isMessageEvent) {
return false;
}
}
this.subscriber.next(event);
return true;
}
// @ts-ignore
run(input: RunAgentInput) {
return super.run(input).pipe(
map((processedEvent) => {
// Turn raw event into emit state snapshot from tool call event
if (processedEvent.type === EventType.RAW) {
// Get the LangGraph event from the AGUI event.
const event = (processedEvent as RawEvent).event ?? (processedEvent as RawEvent).rawEvent;
const eventType = event.event;
const toolCallData = event.data?.chunk?.tool_call_chunks?.[0];
const toolCallUsedToPredictState = event.metadata?.[
"copilotkit:emit-intermediate-state"
]?.some(
(predictStateTool: PredictStateTool) => predictStateTool.tool === toolCallData?.name,
);
if (eventType === LangGraphEventTypes.OnChatModelStream && toolCallUsedToPredictState) {
return {
type: EventType.CUSTOM,
name: "PredictState",
value: event.metadata["copilotkit:emit-intermediate-state"],
};
}
}
return processedEvent;
}),
);
}
langGraphDefaultMergeState(state: State, messages: LangGraphMessage[], tools: any): State {
const { tools: returnedTools, ...rest } = super.langGraphDefaultMergeState(
state,
messages,
tools,
);
return {
...rest,
copilotkit: {
actions: returnedTools,
},
};
}
}