Docs › Python & Node: LangGraph, OpenAI Agents SDK and any script

Python & Node: LangGraph, OpenAI Agents SDK and any script

Two single-file clients with no dependencies. Both fail open: if RunVouch is unreachable your code runs unmonitored and prints one warning.

Python

# curl -fsSL https://runvouch.com/runvouch.py -o runvouch.py
import os, runvouch
runvouch.agent("nightly-etl", cadence_s=86400, cap_run_cost=2, evidence_required=True)

with runvouch.vouch("nightly-etl", evidence=lambda: {"parquet": os.path.getsize("out.parquet") > 0}) as run:
    for f in files:
        run.tool("summarize", {"file": f}, cost=0.004)   # identical inputs 8x → RETRY_STORM
        summarize(f)

An exception inside the block ends the run as fail with the error text in the alert; a clean exit ends it as ok and evaluates your evidence.

LangGraph

from langchain_core.callbacks import BaseCallbackHandler
import runvouch

class Vouch(BaseCallbackHandler):
    def __init__(self, run): self.run = run
    def on_tool_start(self, serialized, input_str, **kw):
        self.run.tool(serialized.get("name", "tool"), input_str)
    def on_llm_end(self, response, **kw):
        u = (response.llm_output or {}).get("token_usage", {})
        self.run.tool("llm", None, tokens=u.get("total_tokens", 0), cost=u.get("total_cost", 0))

with runvouch.vouch("research-graph", evidence=lambda: {"report": open("report.md").read() != ""}) as run:
    graph.invoke(state, config={"callbacks": [Vouch(run)]})

OpenAI Agents SDK

from agents import Runner
import runvouch

with runvouch.vouch("inbox-agent", evidence=lambda: {"replied": replied_count > 0}) as run:
    result = Runner.run_sync(agent, "Triage today's inbox")
    run.tool("openai.run", None, tokens=result.context_wrapper.usage.total_tokens)

Per-tool reporting is optional; per-run cost and evidence are enough for daily caps, drift and "green ≠ done".

Node

// curl -fsSL https://runvouch.com/runvouch.js -o runvouch.js   (Node 18+)
const rv = require('./runvouch');
await rv.agent('nightly-report', { cadence_s: 86400, cap_run_cost: 2, evidence_required: true });

await rv.vouch('nightly-report', async (run) => {
  for (const page of pages) { await run.tool('fetch', { url: page }); await scrape(page); }
}, { evidence: async () => ({ report: fs.existsSync('out/report.html') }) });

CrewAI, AutoGen, anything else

Wrap the entry point with rv run (no code changes) or use the two HTTP calls: POST /v1/runs/start and POST /v1/runs/end — see the API. Cost from OpenRouter/OpenAI/Anthropic responses goes into cost on the end call.


Need a key? Get one free · Stuck? contact