Event-sourced recording + deterministic replay + time‑travel debugger for LLM agents.
Project description
Timewarp — Deterministic Replay & Time‑Travel Debugger for LLM Agent Workflows
Record every step. Rewind any step. Reproduce any run.
Timewarp adds event‑sourced logging and deterministic replay to agent frameworks (LangGraph first, LangChain optional), plus an interactive REPL debugger for step‑through, diffs, and what‑if edits. It fills a well‑documented gap: mainstream tools visualize traces but don’t let you replay them exactly.
What’s Included (v0.1 core)
- Core models and helpers
timewarp.events: Pydantic v2 models (Run,Event,BlobRef), hashing, redactiontimewarp.codec: Canonical JSON (orjson), Zstandard compressiontimewarp.determinism: RNG snapshot/restore
- Local store
timewarp.store.LocalStore: SQLite (WAL) for runs/events + filesystem blobs- Deterministic blob layout:
runs/<run_id>/events/<step>/<kind>.bin(zstd) - Connection PRAGMAs applied per-connection:
journal_mode=WAL,synchronous=NORMAL, configurable busy timeout - Monotonic steps: per-run event
stepmust strictly increase; single-writer-per-run is recommended for correctness
- LangGraph recording
timewarp.langgraph.LangGraphRecorder: streamsupdates|values|messages, recordsLLM|TOOL|DECISION|HITL|SNAPSHOTevents- Labels include
thread_id,namespace,node,checkpoint_id,anchor_id - Privacy redaction via
privacy_marks
- Diff engine
- Anchor‑aware alignment + windowed realignment; DeepDiff/text diffs; first divergence
- Delta debugging: minimal failing window via
diff --bisect
- Replay scaffolding
PlaybackLLM/PlaybackToolinject recorded outputs with prompt/args validationLangGraphReplayer.resume()re‑executes from nearest checkpoint using recorded outputs- What‑if overrides supported (one‑shot per step)
- CLI
timewarp list|events|tools|diff|debug, plusresume,inject, andfsck(see below)timewarp-replinteractive debugger for browsing timelines, inspecting prompts/tools/memory, deterministic resume, and recording what‑if forksexport langsmith <run_id>to serialize runs/events for external tooling
- Telemetry (optional)
- OpenTelemetry spans per event; replay spans link to originals via Span Links
- Attributes use
tw.*keys:tw.run_id,tw.step,tw.action_type,tw.actor,tw.replay,tw.namespace,tw.thread_id,tw.checkpoint_id,tw.anchor_id,tw.branch_of,tw.hash.output|state|prompt
Install & Dev
Requires Python 3.11+.
Install from PyPI:
pip install timewarp-llm
# optional extras
pip install langgraph langchain-core # optional runtime dependencies for recording/replay
pip install 'timewarp-llm[otel]'
pip install 'timewarp-llm[dspy]' # optional DSPy optimizers
# CLI entry points
timewarp --help
timewarp-repl --help
uv venv && uv pip install -e .[dev]
ruff format && ruff check --fix
mypy --strict
pytest -q
Developer notes: see `docs/DEV.md` for CLI helper modules, canonical JSON path, store insert/observability details, and provenance consistency.
Optional dependencies
- LangGraph/LC core:
uv pip install langgraph langchain-core - Telemetry:
uv pip install -e .[otel](installsopentelemetry-*) - DSPy:
uv pip install -e .[dspy](installsdspy) for optional prompt optimization
DSPy Dataset & Optimization (optional)
Timewarp can export per-agent datasets from recorded runs and optionally run DSPy optimizers to produce improved prompt specifications.
Build a dataset from a run:
timewarp ./timewarp.sqlite3 ./blobs dspy build-dataset <run_id> --out ds.json
Run an optimizer (requires installing the dspy extra):
timewarp ./timewarp.sqlite3 ./blobs dspy optimize ds.json --optimizer bootstrap --out prompts.json
# or
timewarp ./timewarp.sqlite3 ./blobs dspy optimize ds.json --optimizer mipro --out prompts.json
# emit overrides JSON directly consumable by `dspy fork`
timewarp ./timewarp.sqlite3 ./blobs dspy optimize ds.json --emit-overrides --out overrides.json
Notes
- Dataset groups examples by agent (LangGraph node). Each example includes inputs (messages when
available), the agent's memory snapshot at
step-1, the recorded output, and step/thread metadata. - Optimizers are optional. If DSPy is not installed or compilation fails, the CLI emits a heuristic prompt template per agent with basic metrics.
- This is pre‑release functionality and may evolve without backward compatibility guarantees.
Recording a Run (LangGraph)
Quickstart via facade:
from timewarp import wrap, messages_pruner
from examples.langgraph_demo.app import make_graph
graph = make_graph()
rec = wrap(
graph,
project="demo",
name="my-run",
stream_modes=("updates", "messages", "values"),
snapshot_every=20,
snapshot_on=("terminal", "decision"),
state_pruner=messages_pruner(max_len=2000, max_items=200),
enable_record_taps=True, # robust prompt/tool args hashing
event_batch_size=20, # batch appends to reduce SQLite overhead
)
result = rec.invoke({"text": "hi"}, config={"configurable": {"thread_id": "t-1"}})
print("run_id=", rec.last_run_id)
Manual recorder usage:
from pathlib import Path
from timewarp.events import Run
from timewarp.store import LocalStore
from timewarp.langgraph import LangGraphRecorder
from timewarp import messages_pruner
store = LocalStore(db_path=Path("./timewarp.db"), blobs_root=Path("./blobs"))
run = Run(project="demo", name="my-run", framework="langgraph")
rec = LangGraphRecorder(
graph=my_compiled_graph,
store=store,
run=run,
stream_modes=("updates", "values"), # also supports "messages"
stream_subgraphs=True,
snapshot_on={"terminal", "decision"},
state_pruner=messages_pruner(max_len=2000, max_items=200),
)
result = rec.invoke({"text": "hi"}, config={"configurable": {"thread_id": "t-1"}})
Debugging & Diffs
timewarp ./timewarp.db ./blobs list
timewarp ./timewarp.db ./blobs debug <run_id> # basic inspector (legacy)
timewarp ./timewarp.db ./blobs diff <run_a> <run_b> # first divergence / bisect
timewarp ./timewarp.db ./blobs events <run_id> --type LLM --node compose --thread t-1 --json
# NEW: interactive debugger (recommended)
timewarp-repl ./timewarp.sqlite3 ./blobs <run_id> \
--app examples.langgraph_demo.app:make_graph \
--thread t-1 --freeze-time
Interactive Debugger (REPL)
Timewarp ships a richer interactive REPL that unifies timeline browsing, prompt/tools/memory inspection, deterministic replay, what‑if injections, prompt overrides, and diffs.
- Binary:
timewarp-repl(installed by the package) - Programmatic:
timewarp.interactive_debug.launch_debugger(...)
CLI usage
timewarp-repl <db> <blobs> <run_id> [--app module:function] [--thread ID] \
[--freeze-time] [--strict-meta] [--allow-diff] [--overrides overrides.json]
Inside the REPL
Commands
app module:function Load a compiled LangGraph via factory (enables resume/inject)
thread T Set thread_id to use during resume/inject
freeze | unfreeze Toggle freeze-time replay
strict | nonstrict Toggle strict meta checks (provider/model/tools invariants)
allowdiff | disallowdiff Toggle allowing prompt diffs during replay (for overrides)
overrides [file.json] Load per-agent prompt overrides; empty to clear
Views
list [type=.. node=.. thread=.. ns=..] Timeline (filterable)
event STEP Show a single event + blob size hints
llm Show the last LLM before current position
prompt [STEP] Messages/tools head + estimated tokens for an LLM step
tools [STEP] Tools summary across run or details for one LLM step
memory Memory summary by space
memory_show STEP Full memory snapshot up to step
memory_diff A B [key=path] Structural diff between two snapshots (optional dotted key)
Execution
resume [FROM_STEP] Deterministically resume from a checkpoint using recorded outputs
inject STEP output.json [-r|--record]
One-shot override at STEP; optionally record fork immediately
fork_prompts overrides.json [-r|--record]
Prepare/record a fork that applies prompt overrides
diff OTHER_RUN_ID [--bisect] [--window N]
Show first divergence or minimal failing window
Programmatic launch
from timewarp.interactive_debug import launch_debugger
from examples.langgraph_demo.app import make_graph
launch_debugger(
db="./timewarp.sqlite3",
blobs="./blobs",
run_id="<UUID>",
graph=make_graph(), # or pass --app module:function via CLI
thread_id="t-1",
freeze_time=True,
strict_meta=False,
allow_diff=False,
)
Integrity Check (fsck)
Verify that all blobs referenced by a run exist on disk; optionally repair and garbage‑collect orphans. Emits JSON for easy automation.
# Basic verification (JSON output)
timewarp ./timewarp.db ./blobs fsck <run_id>
# Attempt repair by promoting any matching .tmp files to final .bin
timewarp ./timewarp.db ./blobs fsck <run_id> --repair
# Remove blob files on disk that are not referenced by the run (dangerous);
# use a grace period to avoid racing with in-flight writes
timewarp ./timewarp.db ./blobs fsck <run_id> --gc-orphans --grace 5
Output shape:
{"missing": ["runs/<id>/events/12/output.bin", ...],
"repaired": ["runs/<id>/events/12/output.bin", ...],
"orphans_gc": ["runs/<id>/events/99/output.bin.tmp", ...]}
SQLite & Concurrency
- Single-writer-per-run: Timewarp enforces strictly increasing
stepperrun_id. Theeventstable uses(run_id, step)as its primary key andLocalStoreguards with a monotonic check againstMAX(step). Running multiple writers for the samerun_idcan cause UNIQUE violations or out-of-order errors. Recommended: one process per run ID. - PRAGMAs: Each connection applies
journal_mode=WAL,synchronous=NORMAL, a configurablebusy_timeout, and best-effortforeign_keys=ON,temp_store=MEMORY, and ajournal_size_limit. These trade-offs aim for durable, fast appends. - JSON1 indexes (optional): Additional indexes rely on SQLite JSON1 (
json_extract). When JSON1 isn’t available, index creation is skipped and a one-time warning is printed; queries still work but may fall back to table scans. Most modern Python builds ship SQLite with JSON1 enabled. - Blob finalization: Blobs are written to
*.bin.tmpand promoted to final*.binon event append. Reading a blob may also finalize the file if a matching.tmpexists. - Orphan GC:
fsck --gc-orphansapplies a grace window (--grace) to avoid deleting files created moments ago by in-flight writers.
Delta Debugging (Minimal Failing Delta)
Find the smallest contiguous mismatching window between two runs, accounting for anchor‑aware realignment to skip benign reorders:
# Text output
timewarp ./timewarp.db ./blobs diff <run_a> <run_b> --bisect
# JSON output (machine‑readable)
timewarp ./timewarp.db ./blobs diff <run_a> <run_b> --bisect --json
# => {"start_a": <int>, "end_a": <int>, "start_b": <int>, "end_b": <int>, "cause": <str>} | {"result": null}
# Tune anchor lookahead window (default 5)
timewarp ./timewarp.db ./blobs diff <run_a> <run_b> --bisect --window 3
Notes
- Causes: "output hash mismatch" | "anchor mismatch" | "adapter/schema mismatch".
- Benign reorders (by matching anchors) are excluded from the window.
- If all aligned pairs match but lengths differ, the trailing unmatched step is reported with cause "anchor mismatch".
- Exit codes: add
--fail-on-divergenceto return a non‑zero exit code when a divergence is found (applies to text and--jsonmodes).
Deterministic Replay & What‑ifs (CLI)
Provide an app factory that returns a compiled LangGraph (example shipped):
--app examples.langgraph_demo.app:make_graph
Resume deterministically from a prior checkpoint:
timewarp ./timewarp.db ./blobs resume <run_id> --from 42 --thread t-1 --app examples.langgraph_demo.app:make_graph
Inject an alternative output at step N and fork:
timewarp ./timewarp.db ./blobs inject <run_id> 23 \
--output alt_23.json \
--thread t-1 \
--app examples.langgraph_demo.app:make_graph \
--record-fork # execute and persist new branch immediately
Notes
- The CLI binds playback wrappers via lightweight installers that intercept LangChain ChatModel/Tool calls during replay. Your graph runs without network/tool side‑effects in replay mode.
- For forks, you can either prepare and record later, or pass
--record-forkto execute and persist the new branch immediately. The new run is labeled withbranch_ofandoverride_stepfor lineage. - Snapshot knobs:
snapshot_everycontrols cadence;snapshot_oncan include"terminal"and/or"decision"to emit snapshots at run end and after routing decisions. You can also pass astate_prunercallable to trim large fields from state snapshots before persistence. - REPL filters: inside
debug, runlist type=LLM node=compose thread=t-1to view a subset. - Pretty state:
state --prettyprints truncated previews with size hints. - Save patch:
savepatch STEP file.jsonwrites the event’s output JSON for reuse withinject. - Event batching:
event_batch_sizebatches DB writes for throughput. For heavy runs, try50or100.
Tools, Prompt, and Memory Views
Inspect tools available to the model, prompts, and memory/retrieval state reconstructed per step and agent:
# Tools summary across LLM steps
timewarp ./timewarp.sqlite3 ./blobs tools <run_id>
# Tools detail for a specific LLM step
timewarp ./timewarp.sqlite3 ./blobs tools <run_id> --step 42 --json
# Memory snapshots (per agent/space)
timewarp ./timewarp.sqlite3 ./blobs memory summary <run_id> --step 120
timewarp ./timewarp.sqlite3 ./blobs memory show <run_id> --step 120 --space planner --json
timewarp ./timewarp.sqlite3 ./blobs memory diff <run_id> 100 140 --space planner --scope working --key messages.0
Tips in the interactive REPL
> tools # summary across LLM steps
> tools 42 # detail for step 42
> prompt 42 # prompt parts (messages + tools), hashes, token estimate
> memory # summary by agent at current step
> memory_show 120 # snapshot at step 120
> memory_diff 120 140 key=messages.0 # structural diff; optional dot path
Details
- Available tools: extracted from recorded prompt parts when present;
tools_digestshows a stable hash when details aren’t recorded. - Called tools: correlated by
thread_idand node, scanning forward until the next LLM event on the same thread. - Token estimate: provider-agnostic heuristic (≈ chars/4) to quickly gauge prompt size; for precise tokens/costs integrate a tokenizer.
- Privacy: printed payloads respect
privacy_marksredaction.
Capturing LangGraph memory from values
To synthesize memory from LangGraph values stream, configure the recorder with memory_paths:
from timewarp.langgraph import LangGraphRecorder
rec = LangGraphRecorder(
graph=graph,
store=store,
run=run,
stream_modes=("updates", "values"),
memory_paths=("messages", "history", "scratch", "artifacts", "memory"),
)
Each new/changed key under these paths emits a MEMORY event (mem_provider="LangGraphState") with stable hashes.item, labels.anchor_id, and inferred mem_scope from the path name. The CLI memory command reconstructs per-agent snapshots from these events.
Record‑time taps (determinism)
For stronger determinism checks, Timewarp can compute and store hashes.prompt and hashes.args at call sites (LangChain core). When using installers directly, start a recording session to scope staged hashes to the current run:
from timewarp.bindings import begin_recording_session, bind_langgraph_record
# Assuming you are using LangGraphRecorder with a concrete Run object
end_session = begin_recording_session(run.run_id)
teardown = bind_langgraph_record()
try:
# run your graph under the recorder
...
finally:
# Ensure both session and patches are cleaned up
end_session()
teardown()
- The
wrap(...)facade auto‑enables record taps withenable_record_taps=Trueand管理 the session lifecycle for you. - When not using
wrap(...), preferbegin_recording_session(...)to avoid any cross‑run leakage; global fallbacks are removed in dev.
Telemetry
Enable OpenTelemetry by installing the extras and configuring an exporter in your app. Timewarp emits spans per event; replay spans use Span Links pointing to original spans. Attributes use the tw.* namespace.
Examples
- Example LangGraph factory:
examples/langgraph_demo/app.pyprovidesmake_graph()for quick--appusage in CLI. - CLI implementation: the console entrypoint is
timewarp.cli:main, which dispatches to a decomposed CLI package undertimewarp/cli/(commands/helpers). Commands remain stable across versions. - Freeze-time example:
examples/langgraph_demo/time_freeze_app.pyprovidesmake_graph_time()that writestimewarp.determinism.now()into state so you can verify identical timestamps on replay with--freeze-time. - Parallel branches example:
examples/langgraph_demo/parallel_app.pydemonstrates fan-out and join with DECISION anchors.
Multi‑Agent Demo
A realistic multi‑agent LangGraph with mock tools, a fake LLM, human‑in‑the‑loop (HITL), subgraphs, and dynamic routing:
- File:
examples/langgraph_demo/multi_agent_full.py - Features:
- LLM events with prompt hashing via record‑time taps (FakeListChatModel)
- TOOL events with MCP‑style metadata (
tool_name,mcp_server,mcp_transport) and args hashing - Human‑in‑the‑loop using
langgraph.types.interrupt+Command(goto=...) - Subgraph for review (
draft_writer→light_edit) and dynamic routing to skip review - Snapshots on terminal + decisions; message‑rich state for pruning/pretty printing
- Run it to record a run and demonstrate resume + what‑if:
uv run python -m examples.langgraph_demo.multi_agent_full
# Inspect with CLI (defaults: ./timewarp.sqlite3 ./blobs)
uv run timewarp ./timewarp.sqlite3 ./blobs list
uv run timewarp ./timewarp.sqlite3 ./blobs debug <run_id>
End‑to‑End Script (Record → Resume → Fork → Diff)
For a single, repeatable flow that exercises most features, use:
- File:
examples/langgraph_demo/run_all.py - What it does:
- Builds the multi‑agent graph and records a baseline run
- Resumes deterministically from the nearest checkpoint (no side‑effects)
- Forks the run by overriding the first TOOL/LLM output (what‑if), records the branch
- Computes first divergence and minimal failing window between base and fork
- Uses a dedicated store by default to avoid local schema drift:
- DB:
tw_runs/demo.sqlite3 - Blobs:
tw_runs/blobs/
- DB:
- Run:
uv run python -m examples.langgraph_demo.run_all
# => prints JSON with run IDs, first divergence, and minimal window
Notes
- Ensure optional deps installed:
uv pip install langgraph langchain-core. - For CLI
resume/inject, pass your app factory (e.g.,examples.langgraph_demo.app:make_graphorexamples.langgraph_demo.multi_agent_full:make_graph_multi). - Full multi‑agent example:
examples/langgraph_demo/multi_agent_full.pyexercises LLM, TOOL, DECISION, HITL, SNAPSHOT, subgraphs, parallel fan‑out with reducers, and async paths. - Tests exercise recorder, diff alignment, replay state reconstruction, and playback installers.
Full Multi‑Agent Demo
Record a representative multi‑agent workflow and exercise the debugger end‑to‑end:
python -m examples.langgraph_demo.multi_agent_full
# prints: Recorded run_id: <UUID>
# also records a what‑if fork and, if supported, an async run
The script builds a graph with:
- LLM nodes (
planner,review:draft_writer) and staged prompt hashes. - TOOL node (
tooling) with MCP‑like metadata and privacy redaction on kwargs. - Parallel branches (
planner,tooling, optionaltooling_async) merged via a reducer onartifactsto avoid concurrent update conflicts. - A
humanHITL interrupt, DECISION events on routing, and periodic/terminal snapshots. - A
reviewsubgraph that streams whenstream_subgraphs=True.
After recording, explore via CLI (defaults write to ./timewarp.sqlite3 and ./blobs):
timewarp ./timewarp.sqlite3 ./blobs list
timewarp ./timewarp.sqlite3 ./blobs debug <run_id>
Resume deterministically and run a what‑if injection (using this demo’s factory):
timewarp ./timewarp.sqlite3 ./blobs resume <run_id> \
--app examples.langgraph_demo.multi_agent_full:make_graph_multi \
--thread t-demo --freeze-time
timewarp ./timewarp.sqlite3 ./blobs inject <run_id> <step> \
--output alt.json \
--app examples.langgraph_demo.multi_agent_full:make_graph_multi \
--thread t-demo --record-fork --freeze-time
Prompt overrides (DSPy-style)
Provide a JSON mapping of agent/node name to an override spec. A spec can be a string (treated as a system message or appended to a raw prompt) or an object with a mode and text.
Example overrides.json
{
"planner": { "mode": "prepend_system", "text": "Be concise and accurate." },
"review": "Prefer bullet points"
}
``
Apply overrides during a non-recorded resume (tolerate prompt-hash diffs with --allow-diff):
timewarp ./timewarp.sqlite3 ./blobs resume <run_id>
--app examples.langgraph_demo.multi_agent_full:make_graph_multi
--thread t-demo
--prompt-overrides ./overrides.json
--allow-diff
Fork and record a branch with overrides using inject:
timewarp ./timewarp.sqlite3 ./blobs inject <run_id> 0
--prompt-overrides ./overrides.json
--app examples.langgraph_demo.multi_agent_full:make_graph_multi
--thread t-demo
--allow-diff
--record-fork
Or use the dedicated helper:
timewarp ./timewarp.sqlite3 ./blobs dspy fork <run_id>
--app examples.langgraph_demo.multi_agent_full:make_graph_multi
--overrides ./overrides.json
--thread t-demo
--allow-diff
--record-fork
Each forked run is labeled with `branch_of=<baseline>` and `override_step=prompt_overrides`, so you can diff the branch against the baseline:
timewarp ./timewarp.sqlite3 ./blobs diff <baseline_run_id> <fork_run_id> --json
Event Filters Cheatsheet
------------------------
Focus on specific slices of the run quickly:
Only TOOL events (MCP) from the tooling node
timewarp ./timewarp.sqlite3 ./blobs events <run_id>
--type TOOL --tool-kind MCP --node tooling --json
Only LLM events from the planner node
timewarp ./timewarp.sqlite3 ./blobs events <run_id> --type LLM --node planner --json
HITL interrupts from the human node
timewarp ./timewarp.sqlite3 ./blobs events <run_id> --type HITL --node human --json
LLM events emitted inside the review subgraph (match by namespace)
timewarp ./timewarp.sqlite3 ./blobs events <run_id> --type LLM --namespace review --json
All DECISION anchors, useful to understand routing and joins
timewarp ./timewarp.sqlite3 ./blobs events <run_id> --type DECISION --json
Notes
-----
- The demo records one sync run (no async nodes) to keep `.invoke()` compatible,
then tries an async run with `make_graph_multi(include_async=True)` using `.ainvoke()`.
- If your environment does not provide `graph.astream`, the async run is skipped.
- When using `wrap(...)` without an explicit `LocalStore`, the default DB path is
`./timewarp.sqlite3` (examples above use that). Earlier examples may reference
`./timewarp.db`; both are supported if you pass matching paths on the CLI.
MCP Example (optional)
----------------------
When `langgraph` and `langchain-mcp-adapters` are available, you can run the MCP demo app:
Record a run using the MCP example app
python - <<'PY' from pathlib import Path from timewarp.store import LocalStore from timewarp.events import Run from timewarp.langgraph import LangGraphRecorder from examples.langgraph_demo.mcp_app import make_graph_mcp
store = LocalStore(db_path=Path('./timewarp.db'), blobs_root=Path('./blobs')) graph = make_graph_mcp() run = Run(project='demo', name='mcp', framework='langgraph') rec = LangGraphRecorder(graph=graph, store=store, run=run, stream_modes=("messages","updates"), stream_subgraphs=True) _ = rec.invoke({"text":"hi"}, config={"configurable": {"thread_id": "t-1"}}) print('run_id=', run.run_id) PY
View TOOL events with MCP metadata
timewarp ./timewarp.db ./blobs events <run_id> --type TOOL --tool-kind MCP --json
Note: MCP metadata is best-effort and dependent on adapter/provider behavior. In environments
where the stream does not emit tool metadata, you may not observe TOOL events for MCP calls.
HITL & Privacy Docs
-------------------
- HITL patterns with LangGraph (DECISION anchors, snapshots, CLI tips): see `docs/hitl.md`.
- Privacy marks and redaction strategies (`redact`, `mask4`) with examples: see `docs/privacy.md`.
Time Provider & Freeze-Time
---------------------------
Use `timewarp.determinism.now()` in your graphs to obtain a deterministic clock.
Recording uses `now()` for `Run.started_at` and all `Event.ts`. During replay, you can
freeze time to the recorded event timestamps.
Programmatic replay:
from timewarp.replay import LangGraphReplayer
replayer = LangGraphReplayer(graph=my_graph, store=store) from timewarp.bindings import bind_langgraph_playback
Define an installer with the standard 3‑arg signature
def installer(llm, tool, memory) -> None: bind_langgraph_playback(my_graph, llm, tool, memory)
session = replayer.resume( run_id, from_step=None, thread_id="t-1", install_wrappers=installer, freeze_time=True )
CLI replay with frozen time:
timewarp ./timewarp.db ./blobs resume <run_id> --app examples.langgraph_demo.time_freeze_app:make_graph_time --thread t-1 --freeze-time
timewarp ./timewarp.db ./blobs inject <run_id> --output alt.json
--app examples.langgraph_demo.time_freeze_app:make_graph_time --thread t-1 --freeze-time --record-fork
Example graph writes the ISO timestamp to state (key `now_iso`). With `--freeze-time`,
replay preserves the exact value that was recorded.
Replay Convenience Facade
-------------------------
You can also resume deterministically via a one-call facade:
from timewarp import Replay
session = Replay.resume( store, app_factory="examples.langgraph_demo.app:make_graph", run_id=, from_step=42, thread_id="t-1", strict_meta=True, freeze_time=True, ) print(session.result)
Exporters
---------
Use the CLI to export a run in a LangSmith-friendly JSON bundle:
timewarp ./timewarp.db ./blobs export langsmith <run_id> --include-blobs
The module `timewarp.exporters.langsmith` also exposes `serialize_run(...)` and `export_run(...)` for programmatic use.
OpenTelemetry Quickstart
------------------------
See `docs/otel-quickstart.md` for a minimal setup to emit spans per event and link replay spans to recorded ones.
CLI Internals (Contributors)
----------------------------
- Entry point: `timewarp.cli:main` dispatches to a decomposed CLI under `timewarp/cli/`.
- Commands: `timewarp/cli/commands/*` implement subcommands (list, events, tools, diff, resume, inject, export, fsck, debug).
- Helpers: `timewarp/cli/helpers/*` contains small utilities used by the CLI only:
- `jsonio`: `print_json`, `dumps_text`, `loads_file` (orjson‑backed).
- `state`: `format_state_pretty`, `dump_event_output_to_file`.
- `events`: `filter_events`.
- `filters`: `parse_list_filters`.
- Stability: these helper modules are implementation details for the CLI and are not part of the public API; they may change between versions.
- Programmatic use: prefer the core modules and top‑level exports (`timewarp.events`, `timewarp.store`, `timewarp.diff`, `timewarp.replay`, `timewarp.langgraph`, etc.).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file timewarp_llm-0.2.1.tar.gz.
File metadata
- Download URL: timewarp_llm-0.2.1.tar.gz
- Upload date:
- Size: 149.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
015c9e7aa287a6a23a825d268891e5df6d347075b3faaec1a759ff8098989548
|
|
| MD5 |
487938dbd70b5ab15e09404c2316b708
|
|
| BLAKE2b-256 |
3cdf3f14cdf0d26adf9ed13fd186514900c90f790d6cff7c9757b01efc125257
|
Provenance
The following attestation bundles were made for timewarp_llm-0.2.1.tar.gz:
Publisher:
release.yml on aleks-apostle/timewarp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timewarp_llm-0.2.1.tar.gz -
Subject digest:
015c9e7aa287a6a23a825d268891e5df6d347075b3faaec1a759ff8098989548 - Sigstore transparency entry: 487478227
- Sigstore integration time:
-
Permalink:
aleks-apostle/timewarp@0d3e26bb76c227fb9c79c5c8d047f706a6ec5f82 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/aleks-apostle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0d3e26bb76c227fb9c79c5c8d047f706a6ec5f82 -
Trigger Event:
push
-
Statement type:
File details
Details for the file timewarp_llm-0.2.1-py3-none-any.whl.
File metadata
- Download URL: timewarp_llm-0.2.1-py3-none-any.whl
- Upload date:
- Size: 127.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
788d681df56fe2e46f7b324d5ebdfb7e9639ec5ccfee5a0e39bfc8651a2f835d
|
|
| MD5 |
96f934f75ef974df61849317f8bc1241
|
|
| BLAKE2b-256 |
9b4c6da897a03d7399556f7056189187a2348e9c72f1c01e29dee777d4272d8b
|
Provenance
The following attestation bundles were made for timewarp_llm-0.2.1-py3-none-any.whl:
Publisher:
release.yml on aleks-apostle/timewarp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
timewarp_llm-0.2.1-py3-none-any.whl -
Subject digest:
788d681df56fe2e46f7b324d5ebdfb7e9639ec5ccfee5a0e39bfc8651a2f835d - Sigstore transparency entry: 487478237
- Sigstore integration time:
-
Permalink:
aleks-apostle/timewarp@0d3e26bb76c227fb9c79c5c8d047f706a6ec5f82 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/aleks-apostle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0d3e26bb76c227fb9c79c5c8d047f706a6ec5f82 -
Trigger Event:
push
-
Statement type: