pipo. docs

ticket-triage

An http input takes support tickets ({subject, body}), an agent node asks Claude for a category, a priority and a one-line summary (checked against triage.schema.json), and the answer is appended to ./out/triaged.jsonl. agent_budget caps the cost: $0.50 a day for this pipeline and 4,000 tokens per ticket, with a warning at 80%.

Run it with the mock provider (no API key, no cost)#

mock-claude.ts answers like the Anthropic Messages API, classifying by keywords, and reports 400 input and 100 output tokens per call ($0.0009 at the built-in Haiku price). Use a throwaway Pipo home so nothing touches ~/.pipo:

export PIPO_HOME=/tmp/pipo-triage MOCK_CLAUDE_KEY=unused
mkdir -p $PIPO_HOME
cat > $PIPO_HOME/config.yaml <<'EOF'
engine:
  timezone: UTC
  agent_budget: { per_day: 1.00 }   # all pipelines of this home together (spec §3.11)
agents:
  claude_api:
    base_url: http://127.0.0.1:8790
    api_key: env:MOCK_CLAUDE_KEY
EOF
bun examples/ticket-triage/mock-claude.ts &                  # the mock, on port 8790
bun pipo run examples/ticket-triage/ticket-triage.pipo
# in another terminal:
curl -s localhost:8791/in/ticket-triage/tickets -H 'content-type: application/json' \
  -d '{"subject": "Site is down", "body": "Checkout fails for everyone since 9:00"}'

bun pipo status --no-engine shows the day's agent spend against engine.agent_budget.per_day. Lower per_day in the pipeline or in config.yaml to see a budget pause: packets keep being accepted, and the pipeline resumes by itself when the next budget day starts (or at once with pipo resume, which goes over the cap until then).

For real calls, drop base_url and point api_key at a secret reference such as op://vault/anthropic/key (1Password).

Test it#

bun pipo test examples/ticket-triage

fixtures/stubs.json answers the agent node, so the test makes no calls at all.

ticket-triage.pipo#

# Triages support tickets with an agent node, under a daily and a per-ticket budget.
# Runs against the bundled mock of the Claude API (no API key, no cost); see README.md:
#   bun examples/ticket-triage/mock-claude.ts      # then point agents.claude_api.base_url at it in config.yaml
#   bun pipo run examples/ticket-triage/ticket-triage.pipo
#   curl -s localhost:8791/in/ticket-triage/tickets -H 'content-type: application/json' -d '{"subject": "Site is down", "body": "Checkout fails for everyone"}'
pipo: 1
name: ticket-triage
description: Classify each support ticket (category, priority, one-line summary) with an agent, within a budget.

agent_budget:
  per_day: 0.50
  per_packet: 4000
  warn_at: 80%

input:
  via: http
  with:
    listen: 8791
    path: /tickets
  format: json
  validate:
    - type(data.subject) == "string" && len(trim(data.subject)) > 0
    - type(data.body) == "string"
  on_invalid:
    respond: 422
    message: "Rejected: rule '${error.rule}' failed; send {subject, body}"

nodes:
  triage:
    from: input
    agent: claude_api
    with:
      model: claude-haiku-4-5
      prompt: |
        Triage this support ticket. Pick its category and priority and summarise it in one sentence.
        Subject: ${data.subject}
        Body: ${data.body}
      schema: ./triage.schema.json
      timeout: 30s
      max_tokens: 512
    on_error:
      retry: 2
      backoff: exponential
      delay: 1s
      then: dead_letter

output:
  from: triage
  to: file
  with:
    path: ./out/triaged.jsonl
    format: jsonl
    mode: append

delivered:
  check: line_contains
  with: { value: "${meta.packet_id}" }
  within: 5s

Source: examples/ticket-triage.

Generated from the Pipo source code.Pipo is licensed under the AGPLv3.