Skip to main content

Git for agent trajectories - branch, diff, and bisect LLM agent runs, backed by real re-execution.

Project description

retrial

Git for agent trajectories. Branch, diff, and bisect LLM agent runs — backed by real re-execution, not static logs.

When an agent does something wrong, most debugging is scrolling through logs. retrial lets you go back to step N, change one fact — a tool result, a retrieved doc, an intermediate decision — and re-run your real agent from there to see what it actually would have done differently. With the real model, not a guess. Validated end to end against live claude-opus-4-8.

Why it's different

  • Real re-execution, not log branching. A fork re-enters your live agent loop and makes real model calls from the fork point onward. It does not relabel stored JSON and call it a branch.
  • Zero-export integration. A decorator on your loop, not a JSON schema to hand-build.
  • Narrow and deep. One thing — branch/diff/bisect by real execution — done well.
  • Python-native, local-first. No account, no telemetry, no dashboard. One SQLite file in .retrial/.

Install

pip install retrial

Integrate

retrial needs two things from your loop: the function that calls the model, and the function that runs tool calls. You pass both in — no monkey-patching of your SDK, so every recorded step traces back to a line you wrote.

from retrial import record

@record(session_name="booking-agent")
def run_agent(messages, tools=TOOLS, call_model=call_model, execute_tools=execute_tools):
    while True:
        response = call_model(messages, tools)
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return response
        messages.append({"role": "user", "content": execute_tools(response)})

The one rule: messages must be a parameter, not a list created inside the function. That's what lets a fork seed your loop with edited history and get genuine re-execution. Give the other parameters defaults to fork from the CLI. The loop must be synchronous — @record refuses an async def rather than record something untrue. Then just run it; steps log as the loop runs, with no export step.

A bundled example under examples/ forks, diffs, and bisects with no API key.

Example: fork, then diff

Every step gets a content-hash SHA, addressable by a short prefix like git:

$ retrial log s_a8d4f64945
  4f0c1e2  step 0  model_call  (312ms, 450 tok)
  a1b2c3d  step 1  tool_call   ran search_flight
  9e77b10  step 2  model_call  (288ms, 544 tok)

Fork step 1 with one fact changed — the edit is a JSON patch — and the real model decides again from there:

$ retrial fork a1b2c3d --agent examples.booking_agent:run_agent --edit-file edit.json
Forked into session s_3f9c02ab1e
$ retrial diff s_a8d4f64945 s_3f9c02ab1e
diverged at d66697c
  cause: replace /output/0/content = flight_price 1200

  - A  book_flight    model_call
  + B  check_budget   model_call

final answer
  A  Confirmed: AUS-SFO booked for $450.
  B  That's over the $600 limit. I need approval before booking.

The fork called check_budget, a tool the original never touched — the kind of divergence only real re-execution produces. The original is never mutated; a fork is a new session, so you can branch the same step as many times as you like.

Also

Same machinery — a fork plus a check — pointed at different questions:

  • bisect — which step doomed a failed run? Binary search over resume points, about log2(steps) re-executions.
  • ablate — which recorded facts did a good run actually need? Perturb each and see if the outcome flips. Causal, not heuristic.
  • sweep — fork one step across many values to find a decision threshold in the model's behavior.
  • rerun — re-execute every recorded run against your current code. Your traces are the regression suite; it exits non-zero on a regression, so CI fails without extra plumbing.
  • cost — token and dollar breakdown per step. An unknown model prices as unpriced, never as a guess.

Bisect and ablate are duals and each refuses the other's job: bisect wants a run that failed, ablate a run that worked.

What retrial refuses to do

The whole product rests on the replay being exactly what happened, so retrial raises rather than guesses when it can't verify that:

  • Your loop transforms a tool result before appending it — the patch would land on a value you never saw.
  • An edit invents or drops a tool result the run never produced.
  • A run crashed mid-loop — the message state after its trailing tool call was never observed.
  • Your agent or its model call is async — the session would be marked complete before the loop ran a step.
  • The database was written by a newer retrial, or an imported step's content doesn't match its SHA.

A wrong replay would be worse than no replay. Merge was cut for the same reason: two forks are competing hypotheses, and answers don't merge.

CLI

retrial init                    create .retrial/ + sqlite db
retrial list                    sessions, tree view
retrial log <session>           step-by-step history, SHA per step
retrial show <sha>              full detail on one step
retrial fork <sha> --agent M:F --edit-file e.json
retrial diff <a> <b>            --full to expand shared steps
retrial bisect <session> --check EXPR --agent M:F
retrial ablate <session> --check EXPR --agent M:F
retrial sweep <sha> --values-file v.json --agent M:F
retrial rerun --check EXPR --agent M:F
retrial cost <session>
retrial export <session> > trace.jsonl
retrial import trace.jsonl

SHA prefix matching applies throughout. The store is found by searching upward for .retrial/, the way git finds .git/; override with --db or RETRIAL_DB.

Python API

from retrial import record, fork, diff, bisect, ablate, sweep, rerun, trajectory, Store
from retrial import export, import_

Every function returns a plain dict, so results stay JSON-shaped and printable. The shapes are declared in retrial/types.py and shipped with py.typed, so a typo in a key is a type error, not a KeyError at 3am.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

retrial-0.1.3.tar.gz (121.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

retrial-0.1.3-py3-none-any.whl (68.3 kB view details)

Uploaded Python 3

File details

Details for the file retrial-0.1.3.tar.gz.

File metadata

  • Download URL: retrial-0.1.3.tar.gz
  • Upload date:
  • Size: 121.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for retrial-0.1.3.tar.gz
Algorithm Hash digest
SHA256 1788bc98f2fc510a5cf1a078fa67f9daa7080b31b8032e4b2dc7197f119c385e
MD5 2b119cf825d87d4a677973c9274b4305
BLAKE2b-256 e6f5839a1f8a3620b1fa285816256cf06852047faba501b123a54f59d152be3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrial-0.1.3.tar.gz:

Publisher: publish.yml on ArcKansupada/retrial

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file retrial-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: retrial-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 68.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for retrial-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7028f8667a6c031ba6f25049e8213db64be87c362710c91da0ec52188f8fcb6b
MD5 55714ce4d4440d54bf4835667a5b6bb3
BLAKE2b-256 eb807f7bea3af72b1bbc67def17df83a9208e17bf9be31a64a7c72be5ab5bf0a

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrial-0.1.3-py3-none-any.whl:

Publisher: publish.yml on ArcKansupada/retrial

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page