From c338522a347ae87f548be01899f76b08265c6977 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Fri, 7 Mar 2025 11:21:49 -0800 Subject: [PATCH] cr --- agent/open-code/nodes/executor.ts | 16 ++-- agent/open-code/nodes/planner.ts | 24 +++++- agent/uis/open-code/plan/index.tsx | 34 ++++++-- agent/uis/open-code/proposed-change/index.tsx | 85 +++++++++++++++++-- 4 files changed, 132 insertions(+), 27 deletions(-) diff --git a/agent/open-code/nodes/executor.ts b/agent/open-code/nodes/executor.ts index a9e50c9..6276e1e 100644 --- a/agent/open-code/nodes/executor.ts +++ b/agent/open-code/nodes/executor.ts @@ -13,17 +13,19 @@ export async function executor( ): Promise { const ui = typedUi(config); - const numOfUpdateFileCalls = state.messages.filter( + const lastPlanToolCall = state.messages.findLast( (m) => m.getType() === "ai" && - (m as unknown as AIMessage).tool_calls?.some( - (tc) => tc.name === "update_file", - ), - ).length; - const planItem = PLAN[numOfUpdateFileCalls - 1]; + (m as unknown as AIMessage).tool_calls?.some((tc) => tc.name === "plan"), + ) as AIMessage | undefined; + const planToolCallArgs = lastPlanToolCall?.tool_calls?.[0]?.args?.args; + const numOfExecutedPlanItems: number = + planToolCallArgs?.executedPlans?.length ?? 0; + + const planItem = PLAN[numOfExecutedPlanItems - 1]; let updateFileContents = ""; - switch (numOfUpdateFileCalls) { + switch (numOfExecutedPlanItems) { case 0: updateFileContents = await fs.readFile( "agent/open-code/nodes/plan-code/step-1.txt", diff --git a/agent/open-code/nodes/planner.ts b/agent/open-code/nodes/planner.ts index 29a2361..2c5f94b 100644 --- a/agent/open-code/nodes/planner.ts +++ b/agent/open-code/nodes/planner.ts @@ -16,22 +16,37 @@ export const PLAN = [ ]; export async function planner( - _state: OpenCodeState, + state: OpenCodeState, config: LangGraphRunnableConfig, ): Promise { const ui = typedUi(config); + const lastPlanToolCall = state.messages.findLast( + (m) => + m.getType() === "ai" && + (m as unknown as AIMessage).tool_calls?.some((tc) => tc.name === "plan"), + ) as AIMessage | undefined; + const planToolCallArgs = lastPlanToolCall?.tool_calls?.[0]?.args?.args; + const executedPlans = planToolCallArgs?.executedPlans ?? []; + const remainingPlans = planToolCallArgs?.remainingPlans ?? PLAN; + + const content = + executedPlans.length > 0 + ? `I've updated the plan list based on the executed plans.` + : `I've come up with a detailed plan for building the todo app.`; + const toolCallId = uuidv4(); const aiMessage: AIMessage = { type: "ai", id: uuidv4(), - content: "I've come up with a detailed plan for building the todo app.", + content, tool_calls: [ { name: "plan", args: { args: { - plan: PLAN, + executedPlans, + remainingPlans, }, }, id: toolCallId, @@ -42,7 +57,8 @@ export async function planner( ui.write("code-plan", { toolCallId, - plan: PLAN, + executedPlans, + remainingPlans, }); const toolMessage: ToolMessage = { diff --git a/agent/uis/open-code/plan/index.tsx b/agent/uis/open-code/plan/index.tsx index c1f2bba..46a6166 100644 --- a/agent/uis/open-code/plan/index.tsx +++ b/agent/uis/open-code/plan/index.tsx @@ -2,19 +2,35 @@ import "./index.css"; interface PlanProps { toolCallId: string; - plan: string[]; + executedPlans: string[]; + remainingPlans: string[]; } export default function Plan(props: PlanProps) { return ( -
-

Plan

-
- {props.plan.map((step, index) => ( -

- {index + 1}. {step} -

- ))} +
+

Code Plan

+
+
+

+ Executed Plans +

+ {props.executedPlans.map((step, index) => ( +

+ {index + 1}. {step} +

+ ))} +
+
+

+ Remaining Plans +

+ {props.remainingPlans.map((step, index) => ( +

+ {props.executedPlans.length + index + 1}. {step} +

+ ))} +
); diff --git a/agent/uis/open-code/proposed-change/index.tsx b/agent/uis/open-code/proposed-change/index.tsx index d47df48..7a41980 100644 --- a/agent/uis/open-code/proposed-change/index.tsx +++ b/agent/uis/open-code/proposed-change/index.tsx @@ -1,8 +1,13 @@ -import { Button } from "@/components/ui/button"; import "./index.css"; +import { v4 as uuidv4 } from "uuid"; +import { Button } from "@/components/ui/button"; import ReactMarkdown from "react-markdown"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; +import { UIMessage, useStreamContext } from "@langchain/langgraph-sdk/react-ui"; +import { Message } from "@langchain/langgraph-sdk"; +import { DO_NOT_RENDER_ID_PREFIX } from "@/lib/ensure-tool-responses"; +import { useState } from "react"; interface ProposedChangeProps { toolCallId: string; @@ -11,12 +16,72 @@ interface ProposedChangeProps { } export default function ProposedChange(props: ProposedChangeProps) { - const handleReject = () => {}; - const handleAccept = () => {}; + const [isAccepted, setIsAccepted] = useState(false); + + const thread = useStreamContext< + { messages: Message[]; ui: UIMessage[] }, + { MetaType: { ui: UIMessage | undefined } } + >(); + + const handleReject = () => { + alert("Rejected. (just kidding, you can't reject me silly!)"); + }; + const handleAccept = () => { + const content = "User accepted the proposed change. Please continue."; + thread.submit({ + messages: [ + { + type: "tool", + tool_call_id: props.toolCallId, + id: `${DO_NOT_RENDER_ID_PREFIX}${uuidv4()}`, + name: "buy-stock", + content, + }, + { + type: "human", + content: `Accepted change.`, + }, + ], + }); + + setIsAccepted(true); + }; + + if (isAccepted) { + return ( +
+
+

Accepted Change

+

{props.planItem}

+
+ + ) : ( + {children} + ); + }, + }} + /> +
+ ); + } return ( -
-

Proposed Change

+
+
+

Proposed Change

+

{props.planItem}

+
- - +
);