Skip to content
Pyrula
Pre-release: the packages are not on PyPI yet, so pip install does not resolve. Build from source to try it today.

Pyrula

Every step, model call and tool call is journaled, so a run resumes after a crash, reruns with a change, and replays as a test.

Terminal window
pip install "pyrula[agents]"
pyrula init
pyrula dev app.py

The scaffold talks to a scripted model, so it needs no key, broker or account. Until release, install is a source build (Install).

On your machineWith Pyrula Cloud
Runpyrula dev journals every step, model and tool call. A completed step is never re-run after a crash.Every worker’s runs in one timeline (Connect).
What-ifpyrula case run --prompt-file or --model: model calls run live, recorded tool results answer the rest.Fork a finished run and change its input, a step result, the model or the prompt. Writes denied, budget on (What-if runs).
Casepyrula dev keeps the newest finished run in .pyrula/last/.Save a run or a what-if as a case, stored in your fixture store (Saved cases).
CIpytest --pyrula-cases cases: no model call, no network.pyrula ci run or the replay-check GitHub Action (Replay checks in CI).

Record once, iterate free walks the left column.

from pyrula.agents import AgentContext, AgentLimits, ToolContext, agent, tool
@tool(read_only=True)
async def lookup_order(ctx: ToolContext, order_id: str) -> dict:
return {"status": "delivered", "total_cents": 25_000}
@tool(requires_approval=lambda args: args["cents"] > 10_000, approval_timeout=3600)
async def refund(ctx: ToolContext, order_id: str, cents: int) -> str:
return f"refunded {cents} cents on {order_id}"
@agent(limits=AgentLimits(max_cost_usd=2.50, max_total_tokens=200_000))
async def support(ctx: AgentContext, message: str) -> str:
return await ctx.llm.run([{"role": "user", "content": message}], tools=[lookup_order, refund])
  • A refund over $100 parks the run until a person answers from the dashboard or the resume endpoint; an hour with no answer denies it (Approval). pyrula dev (embedded mode) refuses approval_timeout, so run a worker for this agent. Pydantic AI tools get no approval. examples/tool_approval.py runs an approval and a rejection.
  • The caps are checked before each live model call, so a run can overshoot by one in-flight call per concurrent caller (Spend caps).
  • pyrula runs terminate <run_id> ends a run with no worker (Cancel and terminate).
  • In a what-if or case run, refund is a write and is refused unless recorded or allowed.

Coming from Pydantic AI, LangGraph, Celery, Temporal or FastAPI? Each page maps that tool’s concepts to Pyrula’s. For coding agents: llms.txt.

View this page as Markdown