improve components

This commit is contained in:
bracesproul
2025-03-07 12:43:22 -08:00
parent 5d4e33bb52
commit 0607f042e7
5 changed files with 68 additions and 28 deletions

View File

@@ -5,7 +5,6 @@ import { OpenCodeState, OpenCodeUpdate } from "../types";
import { LangGraphRunnableConfig } from "@langchain/langgraph";
import ComponentMap from "../../uis";
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
import { PLAN } from "./planner";
export async function executor(
state: OpenCodeState,
@@ -18,11 +17,25 @@ export async function executor(
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 numOfExecutedPlanItems: number =
planToolCallArgs?.executedPlans?.length ?? 0;
const planToolCallArgs = lastPlanToolCall?.tool_calls?.[0]?.args as Record<
string,
any
>;
const nextPlanItem = planToolCallArgs?.remainingPlans?.[0] as
| string
| undefined;
const numOfExecutedPlanItems = planToolCallArgs?.executedPlans?.length ?? 0;
const planItem = PLAN[numOfExecutedPlanItems - 1];
if (!nextPlanItem) {
// All plans have been executed
const successfullyFinishedMsg: AIMessage = {
type: "ai",
id: uuidv4(),
content:
"Successfully completed all the steps in the plan. Please let me know if you need anything else!",
};
return { messages: [successfullyFinishedMsg] };
}
let updateFileContents = "";
switch (numOfExecutedPlanItems) {
@@ -79,9 +92,8 @@ export async function executor(
{
name: "update_file",
args: {
args: {
new_file_content: updateFileContents,
},
new_file_content: updateFileContents as any,
executed_plan_item: nextPlanItem as any,
},
id: toolCallId,
type: "tool_call",
@@ -92,7 +104,7 @@ export async function executor(
ui.write("proposed-change", {
toolCallId,
change: updateFileContents,
planItem,
planItem: nextPlanItem,
});
return {

View File

@@ -6,7 +6,7 @@ import { LangGraphRunnableConfig } from "@langchain/langgraph";
import ComponentMap from "../../uis";
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
export const PLAN = [
const PLAN = [
"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.",
@@ -21,19 +21,36 @@ export async function planner(
): Promise<OpenCodeUpdate> {
const ui = typedUi<typeof ComponentMap>(config);
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;
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 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.`;
const toolCallId = uuidv4();
const aiMessage: AIMessage = {
@@ -44,10 +61,8 @@ export async function planner(
{
name: "plan",
args: {
args: {
executedPlans,
remainingPlans,
},
executedPlans,
remainingPlans,
},
id: toolCallId,
type: "tool_call",

View File

@@ -8,9 +8,9 @@ interface PlanProps {
export default function Plan(props: PlanProps) {
return (
<div className="flex flex-col gap-4 w-full max-w-xl p-6 border-[1px] rounded-xl border-slate-500">
<div className="flex flex-col gap-4 w-full max-w-4xl p-6 border-[1px] rounded-xl border-slate-500">
<h2 className="text-2xl font-semibold text-center mb-2">Code Plan</h2>
<div className="grid grid-cols-2 divide-x divide-slate-300">
<div className="grid grid-cols-2 divide-x divide-slate-300 w-full">
<div className="flex flex-col gap-2 pr-6">
<h3 className="text-lg font-medium mb-4 text-slate-700">
Executed Plans

View File

@@ -7,7 +7,8 @@ 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";
import { useEffect, useState } from "react";
import { getToolResponse } from "../../utils/get-tool-response";
interface ProposedChangeProps {
toolCallId: string;
@@ -15,6 +16,9 @@ interface ProposedChangeProps {
planItem: string;
}
const ACCEPTED_CHANGE_CONTENT =
"User accepted the proposed change. Please continue.";
export default function ProposedChange(props: ProposedChangeProps) {
const [isAccepted, setIsAccepted] = useState(false);
@@ -27,7 +31,6 @@ export default function ProposedChange(props: ProposedChangeProps) {
alert("Rejected. (just kidding, you can't reject me silly!)");
};
const handleAccept = () => {
const content = "User accepted the proposed change. Please continue.";
thread.submit({
messages: [
{
@@ -35,7 +38,7 @@ export default function ProposedChange(props: ProposedChangeProps) {
tool_call_id: props.toolCallId,
id: `${DO_NOT_RENDER_ID_PREFIX}${uuidv4()}`,
name: "buy-stock",
content,
content: ACCEPTED_CHANGE_CONTENT,
},
{
type: "human",
@@ -47,9 +50,17 @@ export default function ProposedChange(props: ProposedChangeProps) {
setIsAccepted(true);
};
useEffect(() => {
if (typeof window === "undefined" || isAccepted) return;
const toolResponse = getToolResponse(props.toolCallId, thread);
if (toolResponse && toolResponse.content === ACCEPTED_CHANGE_CONTENT) {
setIsAccepted(true);
}
}, []);
if (isAccepted) {
return (
<div className="flex flex-col gap-4 w-full max-w-xl p-4 border-[1px] rounded-xl border-green-300">
<div className="flex flex-col gap-4 w-full max-w-4xl p-4 border-[1px] rounded-xl border-green-300">
<div className="flex flex-col items-start justify-start gap-2">
<p className="text-lg font-medium">Accepted Change</p>
<p className="text-sm font-mono">{props.planItem}</p>