Files
agent-chat-ui/agent/open-code/nodes/planner.ts

93 lines
3.0 KiB
TypeScript
Raw Normal View History

2025-03-06 20:04:55 -08:00
import { v4 as uuidv4 } from "uuid";
2025-03-07 10:47:08 -08:00
import { AIMessage, ToolMessage } from "@langchain/langgraph-sdk";
2025-03-06 20:04:55 -08:00
import { OpenCodeState, OpenCodeUpdate } from "../types";
2025-03-07 10:47:08 -08:00
import { DO_NOT_RENDER_ID_PREFIX } from "@/lib/ensure-tool-responses";
import { LangGraphRunnableConfig } from "@langchain/langgraph";
import ComponentMap from "../../uis";
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
2025-03-06 20:04:55 -08:00
2025-03-07 12:43:22 -08:00
const PLAN = [
2025-03-07 10:47:08 -08:00
"Set up project scaffolding using Create React App and implement basic folder structure for components, styles, and utilities.",
"Create reusable UI components for TodoItem, including styling with CSS modules.",
"Implement state management using React Context to handle todo items, including actions for adding, updating, and deleting todos.",
"Add form functionality for creating new todos with input validation and error handling.",
"Create filtering and sorting capabilities to allow users to view completed, active, or all todos.",
"Implement local storage integration to persist todo items between page refreshes.",
];
export async function planner(
2025-03-07 11:21:49 -08:00
state: OpenCodeState,
2025-03-07 10:47:08 -08:00
config: LangGraphRunnableConfig,
): Promise<OpenCodeUpdate> {
const ui = typedUi<typeof ComponentMap>(config);
2025-03-07 12:43:22 -08:00
const lastUpdateCodeToolCall = state.messages.findLast(
(m) =>
m.getType() === "ai" &&
(m as unknown as AIMessage).tool_calls?.some(
(tc) => tc.name === "update_file",
),
) as AIMessage | undefined;
2025-03-07 11:21:49 -08:00
const lastPlanToolCall = state.messages.findLast(
(m) =>
m.getType() === "ai" &&
(m as unknown as AIMessage).tool_calls?.some((tc) => tc.name === "plan"),
) as AIMessage | undefined;
2025-03-07 12:43:22 -08:00
const planToolCallArgs = lastPlanToolCall?.tool_calls?.[0]?.args as Record<
string,
any
>;
const executedPlans: string[] = planToolCallArgs?.executedPlans ?? [];
let remainingPlans: string[] = planToolCallArgs?.remainingPlans ?? PLAN;
const executedPlanItem = lastUpdateCodeToolCall?.tool_calls?.[0]?.args
?.executed_plan_item as string | undefined;
if (executedPlanItem) {
executedPlans.push(executedPlanItem);
remainingPlans = remainingPlans.filter((p) => p !== executedPlanItem);
}
const content = executedPlanItem
? `I've updated the plan list based on the executed plans.`
: `I've come up with a detailed plan for building the todo app.`;
2025-03-07 11:21:49 -08:00
2025-03-07 10:47:08 -08:00
const toolCallId = uuidv4();
2025-03-06 20:04:55 -08:00
const aiMessage: AIMessage = {
type: "ai",
id: uuidv4(),
2025-03-07 11:21:49 -08:00
content,
2025-03-06 20:04:55 -08:00
tool_calls: [
{
2025-03-07 10:47:08 -08:00
name: "plan",
2025-03-06 20:04:55 -08:00
args: {
2025-03-07 12:43:22 -08:00
executedPlans,
remainingPlans,
2025-03-06 20:04:55 -08:00
},
2025-03-07 10:47:08 -08:00
id: toolCallId,
2025-03-06 20:04:55 -08:00
type: "tool_call",
2025-03-07 10:47:08 -08:00
},
],
};
2025-03-07 21:26:48 +01:00
const msg = ui.create("code-plan", {
2025-03-07 10:47:08 -08:00
toolCallId,
2025-03-07 11:21:49 -08:00
executedPlans,
remainingPlans,
2025-03-07 10:47:08 -08:00
});
2025-03-07 21:26:48 +01:00
msg.additional_kwargs["message_id"] = aiMessage.id;
2025-03-07 10:47:08 -08:00
const toolMessage: ToolMessage = {
type: "tool",
id: `${DO_NOT_RENDER_ID_PREFIX}${uuidv4()}`,
tool_call_id: toolCallId,
content: "User has approved the plan.",
};
2025-03-06 20:04:55 -08:00
2025-03-07 10:47:08 -08:00
return {
messages: [aiMessage, toolMessage],
2025-03-07 21:26:48 +01:00
ui: [msg],
2025-03-07 10:47:08 -08:00
timestamp: Date.now(),
};
}