Language
Reviews
Add human-in-the-loop approval steps to your workflows.
Reviews pause graph execution at a node until a human approves it. Reviewers can see the node's output, fill out a form, and approve or reject. The review response is then available to downstream nodes via context.reviews.
Enabling review on a node
Add review: true for simple approval, or use a full review config block:
node check_output {
type: code
label: "Check output"
outputSchema: @json {
{
"type": "object",
"required": ["draft"],
"properties": { "draft": { "type": "string" } },
"additionalProperties": false
}
}
code: @ts {
return { draft: "Generated content here..." }
}
review: {
enabled: true
title: "Review before sending"
description: "Approve the draft or request changes."
schema: @json {
{
"type": "object",
"required": ["approved"],
"properties": {
"approved": { "type": "boolean", "title": "Approve" },
"feedback": { "type": "string", "title": "Feedback" }
},
"additionalProperties": false
}
}
}
}Review config fields
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Enable or disable the review step. |
title | string | No | Title shown to the reviewer. |
description | string | No | Description shown to the reviewer. |
schema | @json { } block | No | JSON Schema for the review form. |
content | string | No | Additional content shown to the reviewer. |
Accessing review data
In @ts { } blocks, use context.reviews.<nodeName> to access the reviewer's response:
@ts {
// In a downstream node or the reviewed node itself
const approved = context.reviews.check_output?.approved
const feedback = context.reviews.check_output?.feedback ?? ""
}The LSP types context.reviews from the review schema, so you get autocomplete. See Context and Type Safety for details.
Full example: draft, review, route
This graph drafts an email with an LLM, pauses for human review, then routes to send or revise based on the reviewer's response.
graph email_with_review {
label: "Email with Review"
root {
type: code
label: "Entry"
inputSchema: @json {
{
"type": "object",
"required": ["name", "email", "topic"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"topic": { "type": "string" }
},
"additionalProperties": false
}
}
outputSchema: @json {
{
"type": "object",
"required": ["name", "email", "topic"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"topic": { "type": "string" }
},
"additionalProperties": false
}
}
code: @ts {
const { name, email, topic } = context.nodes.root.input
return { name: name?.trim() ?? "", email: email?.trim() ?? "", topic: topic?.trim() ?? "" }
}
}
node draft {
type: ai
kind: object
label: "Draft email"
outputSchema: @json {
{
"type": "object",
"required": ["subject", "body"],
"properties": {
"subject": { "type": "string" },
"body": { "type": "string" }
},
"additionalProperties": false
}
}
model: "gpt-4o-mini"
prompt: @ts {
return `Draft a professional email to ${context.nodes.root.output.name} about: ${context.nodes.root.output.topic}`
}
}
node review_draft {
type: code
label: "Review draft"
outputSchema: @json {
{
"type": "object",
"required": ["subject", "body"],
"properties": {
"subject": { "type": "string" },
"body": { "type": "string" }
},
"additionalProperties": false
}
}
code: @ts { return context.nodes.draft.output }
review: {
enabled: true
title: "Review email"
description: "Approve or request changes."
schema: @json {
{
"type": "object",
"required": ["approved"],
"properties": {
"approved": { "type": "boolean", "title": "Send as-is" },
"feedback": { "type": "string", "title": "Changes requested" }
},
"additionalProperties": false
}
}
}
}
node route {
type: switch
label: "Route"
cases: ["send", "revise"]
router: @ts {
return context.reviews.review_draft?.approved ? "send" : "revise"
}
}
node send {
type: email
label: "Send"
from: @ts { return "[email protected]" }
to: @ts { return context.nodes.root.output.email }
subject: @ts { return context.nodes.draft.output.subject }
text: @ts { return context.nodes.draft.output.body }
}
node revise {
type: ai
kind: object
label: "Revise"
outputSchema: @json {
{
"type": "object",
"required": ["subject", "body"],
"properties": {
"subject": { "type": "string" },
"body": { "type": "string" }
},
"additionalProperties": false
}
}
model: "gpt-4o-mini"
prompt: @ts {
const draft = context.nodes.draft.output
const feedback = context.reviews.review_draft?.feedback ?? ""
return `Revise this email. Subject: ${draft.subject}. Body: ${draft.body}. Feedback: ${feedback}`
}
}
node send_revised {
type: email
label: "Send revised"
from: @ts { return "[email protected]" }
to: @ts { return context.nodes.root.output.email }
subject: @ts { return context.nodes.revise.output.subject }
text: @ts { return context.nodes.revise.output.body }
}
flow {
root -> draft
draft -> review_draft
review_draft -> route
route -["send"]-> send
route -["revise"]-> revise
revise -> send_revised
}
}Further reading
- Context and Type Safety: how
context.reviewsis typed from the review schema - Node types: switch node configuration