Files
agent-chat-ui/src/providers/Runtime.tsx

79 lines
2.3 KiB
TypeScript
Raw Normal View History

2025-02-28 14:15:37 -08:00
import { ReactNode, useEffect } from "react";
2025-02-26 17:09:16 -08:00
import {
useExternalStoreRuntime,
AppendMessage,
AssistantRuntimeProvider,
} from "@assistant-ui/react";
2025-02-28 14:15:37 -08:00
import { HumanMessage, Message, ToolMessage } from "@langchain/langgraph-sdk";
2025-02-27 14:08:24 -08:00
import { useStreamContext } from "./Stream";
import { convertLangChainMessages } from "./convert-messages";
2025-02-26 17:09:16 -08:00
2025-02-28 14:15:37 -08:00
function ensureToolCallsHaveResponses(messages: Message[]): Message[] {
const newMessages: ToolMessage[] = [];
messages.forEach((message, index) => {
if (message.type !== "ai" || message.tool_calls?.length === 0) {
// If it's not an AI message, or it doesn't have tool calls, we can ignore.
return;
}
// If it has tool calls, ensure the message which follows this is a tool message
const followingMessage = messages[index + 1];
if (followingMessage && followingMessage.type === "tool") {
// Following message is a tool message, so we can ignore.
return;
}
// Since the following message is not a tool message, we must create a new tool message
newMessages.push(
...(message.tool_calls?.map((tc) => ({
type: "tool" as const,
tool_call_id: tc.id ?? "",
id: tc.id ?? "",
name: tc.name,
content: "Successfully handled tool call.",
})) ?? [])
2025-02-28 14:15:37 -08:00
);
});
return newMessages;
}
2025-02-26 17:09:16 -08:00
export function RuntimeProvider({
children,
}: Readonly<{
children: ReactNode;
}>) {
2025-02-27 14:08:24 -08:00
const stream = useStreamContext();
2025-02-26 17:09:16 -08:00
const onNew = async (message: AppendMessage) => {
if (message.content[0]?.type !== "text")
throw new Error("Only text messages are supported");
2025-02-27 14:08:24 -08:00
2025-02-26 17:09:16 -08:00
const input = message.content[0].text;
2025-02-27 14:08:24 -08:00
const humanMessage: HumanMessage = { type: "human", content: input };
2025-02-28 14:15:37 -08:00
const newMessages = [
...ensureToolCallsHaveResponses(stream.messages),
humanMessage,
];
console.log("Sending new messages", newMessages);
stream.submit({ messages: newMessages }, { streamMode: ["values"] });
2025-02-26 17:09:16 -08:00
};
2025-02-27 14:08:24 -08:00
2025-02-28 14:15:37 -08:00
useEffect(() => {
console.log("useEffect - stream.messages", stream.messages);
}, [stream.messages]);
2025-02-26 17:09:16 -08:00
const runtime = useExternalStoreRuntime({
2025-02-27 14:08:24 -08:00
isRunning: stream.isLoading,
messages: stream.messages,
convertMessage: convertLangChainMessages,
2025-02-26 17:09:16 -08:00
onNew,
});
2025-02-27 14:08:24 -08:00
2025-02-26 17:09:16 -08:00
return (
<AssistantRuntimeProvider runtime={runtime}>
{children}
</AssistantRuntimeProvider>
);
2025-02-27 14:08:24 -08:00
}