Local Visualization (Current)
Conductr Local is the current local visualizer for Railtracks. It reads the JSONL event stream that every run records, and lets you browse runs, LLM calls, middleware decisions, and raw events, with filtering, sorting, and pagination on every table. It is installed separately from the legacy visualizer, so both can be used in the same project.

Still in beta
The current visualizer is still in beta, which is why it's started with
--beta. Its UI and /api/v2 contract are under active development. Response
fields, filters, and other behavior can change between releases without
notice.
Install and start
Install the optional visualization dependencies:
Initialize Railtracks from your project root, install the beta UI, and start the server:
railtracks init
railtracks update --beta
railtracks viz --beta
railtracks initcreates the.railtracksdirectory and installs the legacy UI.railtracks update --betainstalls the beta build alongside it in.railtracks/beta-ui.- If the beta UI is missing,
railtracks viz --betadownloads it automatically before starting the server.
The server opens at http://localhost:3031. Its API uses /api/v2/...
routes, and interactive API documentation is available at
http://localhost:3031/docs.
The page doesn't update on its own. After a new run, press Refresh at the top right of any page to load it.
Agent Traces
Agent Traces is the home page. Each row is one run of a flow, with its status, entry point, the middleware it passed through, cost, tokens (input and output), duration, and start time.
- The tiles at the top summarize every run that matches the current filters. Click Failures or Blocked to show only those runs, and click again to clear the filter.
- Narrow the list by date range (24h, 7 days, 30 days, All, or a custom range), flow name, entry point, or status.
- Show, hide, and resize columns with Columns. Your layout is remembered in the browser.
A run is Running while it's still in progress. Once it ends, it's Completed or Failed, or Blocked when a guardrail or verifier stopped it. Click a middleware icon in a row to open that middleware's decisions for the run.
Session details
Click a run to open its session. The tree on the left shows every node the run called, nested under the node that called it, with each one's latency. Select a node to see its cost, tokens, inputs, and outputs on the right. For an agent, that's the full message history it sent to the model.

When a node made more than one LLM call, a table lists each call's model, tokens, cost, and latency. Select a call to see exactly what that call sent.
Nodes that raised an exception are marked Failed. To read the exception
itself, open the run's events (see Event Logs) and select its
node.failure event.

Buttons at the top of the session:
- Visualizer switches to a graph of the run. Click a node to inspect it, and use Session Details to switch back.
- Events opens Event Logs filtered to this run.
- The copy button copies the session ID.

LLM Traces
LLM Traces lists every model call across all runs, with the flow, agent, model, tokens, cost, and latency. The tiles show the call count, errors, total tokens and cost, average latency, and the slowest call. Click Errors to show only failed calls. Filter by flow, agent, or model.

Click a call to see its messages, its output, the provider, and the error if it failed. Open in session jumps to the node that made the call.

Middleware
Middleware rolls up every middleware across all runs. One row is one middleware: how often it ran, how many decisions it made, how many calls it blocked or interrupted, and how many sessions it appeared in.

Kind is what the middleware does:
| Kind | Created with |
|---|---|
| Input guard | @rt.input_guard |
| Output guard | @rt.output_guard |
| Request transform | @rt.pre_llm |
| Response transform | @rt.post_llm |
| Result hook | @rt.post_node |
| LLM wrapper | @rt.wrap_llm |
| Node wrapper | @rt.wrap_node |
| Verifier | pre_verifier, post_verifier |
Band is where it was attached: Node for middleware= and LLM for
model_middleware=.
Click Blocks to show only middleware that blocked something. The Framework switch also shows the middleware Railtracks adds internally for observability, which is hidden by default.
Click a row for its full roll-up, including when it was first and last seen and the reason it gave for its latest decision. View events opens its events in Event Logs.

Event Logs
Event Logs is the raw event stream: every event Railtracks recorded, newest
first. Search the payloads, or filter by namespace (such as node, llm, or
middleware), event type, and flow. Click Failures to show only events
that reported an exception.

Click an event to see its full payload. For a failure, that includes the exception's name and message. Open in session jumps to the run, with the event's node selected.

Middleware events carry each decision. Here, a pre_verifier declined a refund
and recorded why, along with the arguments it was asked to approve:

Evaluations
Evaluations aren't in the current visualizer yet. Its Evaluations page is a placeholder. To browse evaluation results, use the legacy visualizer as described in Evaluation Visualization.
Record event-stream data
Events are recorded automatically to .railtracks/data/events with no setup required. The current visualizer reads from the
same directory.
Customize event writers
To use your own writer set (or add more alongside the default), call configure_writers(...). You can call it at any time: the writers it replaces are shut down, and the new ones receive every event after the call.
from railtracks.observability import JsonlWriter, configure_writers
configure_writers([JsonlWriter()])
configure_writers
configure_writers replaces the auto-registered default, so if you'd like
to still have the local event files, you need to pass JsonlWriter() as
well as any new or custom writers.
A custom writer is any object with plain (not async) start(), write(event) and shutdown() methods, matching the Writer protocol in railtracks.observability. Railtracks calls them on the thread that emits each event, one call at a time per writer, so keep write quick. configure_writers raises a TypeError if any of these methods is async.
Use another event directory
Set RAILTRACKS_EVENTS_DIR in both the process recording events and the
visualizer process. Relative paths are resolved from the current working
directory. This redirects the auto-injected writer without any code change.
export RAILTRACKS_EVENTS_DIR=./saved-events
python my_agent.py
railtracks viz --beta
Choose what context events record
rt.context calls are recorded as context.* events. Set RAILTRACKS_CONTEXT_EVENTS to 0 for no context events, 1 for keys without values, or 2 (the default) for keys and values. Every other event is recorded either way. See Global Context for details.
Deployed environments with no writable disk
Set RAILTRACKS_DISABLE_EVENTS=True on hosts where Railtracks can't (or
shouldn't) write to disk. It skips both the auto-registered event
writer and the legacy save_state session dump, regardless of what
save_state= is set to.
For hosted observability in these environments, use Conductr.
save_state is deprecated
save_state=True still writes .railtracks/data/sessions/*.json for the
legacy visualizer this release, but passing the argument at all now
emits a DeprecationWarning. The file dump is being replaced by the
event stream (.railtracks/data/events/). Default: True this release,
flips to False next release. Remove the argument to let the framework
default take over.
Running from multiple directories?
Run railtracks init once from your project root, at the same level as
your .git directory. Railtracks walks up from the current directory to
locate that project's .railtracks directory.
For a fixed location, set RAILTRACKS_HOME to the parent directory where
.railtracks should live. RAILTRACKS_HOME takes priority over directory
traversal.
Debug the API
Add --debug to emit structured diagnostics:
Debug mode writes newline-delimited JSON records to stderr for v2 requests, DuckDB queries, and event-store connection changes.
Debug logs can contain user input
Request records include query parameter values, such as search terms. Review or redact debug output before sharing it or sending it to a log collector.
Update the UI
The legacy and current builds are updated independently. Refresh only the
current build, in .railtracks/beta-ui, with: