Workflows
Workflows are functions decorated with HTTP triggers. They define your API endpoints and orchestrate agents.
Basic Workflow
@post("/api/chat")
workflow Chat(message: string) -> { reply: string } {
reply, err = Assistant.ask(message)
if err != nil {
return { reply: "Something went wrong." }
}
return { reply: reply }
}HTTP Decorators
@post — POST endpoint
@post("/api/analyze")
workflow Analyze(text: string) -> { result: string } {
// ...
}@webhook — Webhook endpoint
@webhook("/hooks/github")
workflow GithubWebhook(payload: string) -> { status: string } {
// Process webhook
return { status: "ok" }
}@webui — Custom UI metadata
@webui(title: "Text Analyzer", description: "Analyze text with AI")
@post("/api/analyze")
workflow Analyze(text: string) -> { result: string } {
// ...
}Input Parameters
Workflow parameters map to the JSON body of the HTTP request:
@post("/api/process")
workflow Process(
text: string,
language: string,
max_length: int
) -> { result: string } {
// Access parameters directly
io.println("Processing ${language} text...")
// ...
}Request body:
{
"text": "Hello world",
"language": "en",
"max_length": 100
}File Uploads
Use the file type for file upload support:
@post("/api/summarize")
workflow Summarize(document: file, context: string) -> { summary: string } {
content, err = io.read_file(document)
if err != nil {
return { summary: "Failed to read file." }
}
// Process content...
}The auto-generated UI will render a file upload input.
Streaming Workflows
Return stream for SSE responses:
@post("/chat")
workflow Chat(message: string, session_id: string) -> stream {
return Assistant.stream(message, session: session_id)
}See Streaming for details.
Serving Workflows
Register workflows with http.Server:
fn main() {
server = http.Server([Chat, Analyze, Summarize])
io.println("Server running on :8080")
server.listen(8080)
}Every workflow gets its API endpoint (e.g., POST /api/chat). To view workflows in a web UI:
haira webui -c localhost:8080 -p 3000Multi-Step Workflows
Use step blocks for named stages:
@post("/api/analyze")
workflow Analyze(text: string) -> { result: string } {
step "Validate input" {
if len(text) == 0 {
return { result: "Empty input." }
}
}
step "Analyze text" {
analysis, err = Analyst.ask("Analyze: ${text}")
if err != nil {
return { result: "Analysis failed." }
}
}
return { result: analysis }
}Step Retry
@retry adds automatic retry with backoff:
@retry(max: 10, delay: 5000, backoff: "exponential")
step "Call external API" {
result = http.get(url)
}Verification Loops
Use verify { assert ... } inside steps to define assertions that trigger retries automatically:
@retry(max: 3)
step "Generate analysis" {
analysis, err = Analyst.ask("Analyze: ${data}")
if err != nil { return { result: "Failed." } }
verify {
assert len(analysis) > 100
assert strings.contains(analysis, "conclusion")
}
}When an assertion fails inside a @retry step, the step automatically retries. Without @retry, assertion failures return an error.
Lifecycle Hooks
Workflows support lifecycle hooks for error handling and completion logic:
onerror — Handle errors
@post("/api/process")
workflow Process(text: string) -> { result: string } {
onerror err {
io.println("Workflow failed: ${err}")
return { result: "Error: could not process" }
}
result, err = Analyzer.ask(text)
if err != nil { return { result: "AI error" } }
return { result: result }
}onsuccess — Run on completion
@post("/api/analyze")
workflow Analyze(text: string) -> { summary: string } {
onsuccess {
io.println("Analysis completed successfully")
}
summary, err = Summarizer.ask(text)
if err != nil { return { summary: "Failed" } }
return { summary: summary }
}oncancel — Run on cancellation
@post("/api/long-task")
workflow LongTask(input: string) -> { result: string } {
oncancel {
io.println("Task was cancelled, cleaning up...")
}
// ... long-running work
return { result: "done" }
}Additional HTTP Decorators
@get — GET endpoint
@get("/api/status")
workflow Status() -> { status: string } {
return { status: "ok" }
}@put — PUT endpoint
@put("/api/users")
workflow UpdateUser(id: int, name: string) -> { success: bool } {
// update logic
return { success: true }
}@delete — DELETE endpoint
@delete("/api/users")
workflow DeleteUser(id: int) -> { deleted: bool } {
// delete logic
return { deleted: true }
}Orchestrating Multiple Agents
@post("/api/research")
workflow Research(topic: string) -> { report: string } {
// Gather data in parallel
data = spawn {
Researcher.ask("Find facts about: ${topic}")
Researcher.ask("Find recent news about: ${topic}")
}
// Synthesize
combined = string.join(data, "\n\n")
report, err = Writer.ask("Write a report from: ${combined}")
if err != nil {
return { report: "Failed to generate report." }
}
return { report: report }
}