// ralph-loop.tsx
import { createSmithers, Task, Sequence, Ralph } from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
const { Workflow, smithers } = createSmithers({
code: z.object({
source: z.string(),
language: z.string(),
}),
review: z.object({
approved: z.boolean(),
feedback: z.string(),
}),
finalOutput: z.object({
source: z.string(),
iterations: z.number(),
}),
});
const coder = new Agent({
model: anthropic("claude-sonnet-4-5-20250929"),
instructions:
"You are an expert programmer. Write or revise code based on the given requirements and feedback.",
});
const reviewer = new Agent({
model: anthropic("claude-sonnet-4-5-20250929"),
instructions:
"You are a strict code reviewer. Evaluate the code for correctness, style, and edge cases. Set approved to true only if the code is production-ready.",
});
export default smithers((ctx) => {
const latestReview = ctx.outputMaybe("review", { nodeId: "review" });
const latestCode = ctx.outputMaybe("code", { nodeId: "write" });
return (
<Workflow name="ralph-loop">
<Sequence>
<Ralph
id="revision-loop"
until={latestReview?.approved === true}
maxIterations={5}
onMaxReached="return-last"
>
<Sequence>
<Task id="write" output="code" agent={coder}>
Write a TypeScript function that debounces an input function.
{latestReview
? ` Revise based on this feedback: ${latestReview.feedback}`
: ""}
</Task>
<Task id="review" output="review" agent={reviewer}>
Review this code for correctness and edge cases:
{"\n\n```" + (latestCode?.language ?? "ts") + "\n"}
{latestCode?.source ?? "// no code yet"}
{"\n```"}
</Task>
</Sequence>
</Ralph>
{/* After the loop ends, emit the final result */}
<Task id="final" output="finalOutput">
{{
source: latestCode?.source ?? "",
iterations: ctx.iterationCount("code", "write"),
}}
</Task>
</Sequence>
</Workflow>
);
});