Skip to main content

langchain-right-rudder

A coherence read for LangChain agents, as an agent middleware.

Fathom is now Right Rudder, by Embedded Risk Analytics. pip install langchain-right-rudder replaces langchain-fathom, and the old class names, state keys and imports keep working in this release.

A long-running agent loses coherence with its own decisions. It renames a field at one step, then writes the old name at a later one. It marks a record done that it never wrote. The run reports success, and the contradiction ships. langchain-right-rudder watches the agent run and, when it finishes, names the step where a later action contradicts an earlier commitment.

The middleware observes the agent and does not change what it does. It maps the tool calls in the message history to a committed-state op stream, sends that stream to the Right Rudder read, and reports the findings. The read is deterministic and needs no model access.

Install

pip install langchain-right-rudder

Use

from langchain.agents import create_agent
from langchain_right_rudder import RightRudderMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[...],
    middleware=[RightRudderMiddleware()],
)

result = agent.invoke({"messages": [...]})

By default the middleware logs any findings through the right-rudder logger. Two other modes fit a test suite or a pipeline.

RightRudderMiddleware(on_finding="raise")   # raise RightRudderCoherenceError when a run is not coherent
RightRudderMiddleware(on_finding="store")   # put the verdict on agent state under the "fathom" key

Under store, the verdict arrives on the result.

result = agent.invoke({"messages": [...]})
verdict = result["fathom"]
verdict["coherent"], verdict["findings"]

The middleware declares that key in its own state schema, so a graph that carries a state schema of its own keeps the verdict rather than dropping it. Version 0.1.0 declared nothing, and an orchestrator such as a deepagents agent dropped the verdict before the caller saw it.

If the run is a rename or a migration, tell the read which token replaced which, so it also reports records left on the old value at the end.

RightRudderMiddleware(supersede=[("guest_id", "customer_id")])

What it finds

Finding The agent...
stale_reference acts on a record it already removed or renamed away
superseded_value writes a value it already replaced
residual ends the run with a record still carrying a value it replaced elsewhere
duplicate_commit adds an entity a collection already holds
post_commit_mutation changes a record after committing it

A coherent run returns coherent and the middleware reports nothing else.

Agents that delegate

The middleware reads the calls of the agent it rides. An orchestrator that hands work to sub-agents runs each sub-agent as its own agent with its own message history, so an orchestrator-level middleware sees the delegation and the orchestrator's own tools, and not what the sub-agents did. Give each sub-agent its own RightRudderMiddleware to cover the whole run. Or attach one RightRudderCapture at the graph root, which follows the orchestrator into its sub-agents and returns the whole run in a single trace, the same trace the right-rudder deepagents adapter reads. On one deepagents research run we measured, the orchestrator's middleware alone returned 3 of the 9 findings a trace across every sub-agent returned.

Your own tool names

The read knows common state-writing tool names. For tools with your own names, map them once and pass the file.

RightRudderMiddleware(mapping_path="tools.json")
{"save_decision": {"op": "set", "kind": "decision", "key": "topic", "value": "text"},
 "confirm_booking": {"op": "commit", "kind": "flight", "key": "booking"}}

The repair

RightRudderMiddleware watches. RightRudderRepairMiddleware acts. It sits between the model and its tools, sends the actions the model just proposed together with what the run has already committed, and gets back one of three decisions.

from langchain_right_rudder import RightRudderRepairMiddleware

agent = create_agent(
    model="gpt-5.5",
    tools=[...],
    middleware=[RightRudderRepairMiddleware()],
)

On proceed every proposed action is consistent with what the agent already committed and nothing changes. On filter the agent keeps its own consistent alternatives and the contradicting calls never reach the tools. On reground none of them survive, so the committed facts go back in front of the model as a system-role message and the model is asked again, once by default.

The repair needs a free key. Get one with right-rudder key you@example.com from the right-rudder package and set RIGHT_RUDDER_API_KEY, or pass key=. The middleware checks for it when you construct it, so a run fails at setup rather than twenty steps in. The two reads stay free without a key.

This middleware changes what your agent does, which is why it carries its own class rather than a flag on the read. Two settings cover the cases where that matters.

RightRudderRepairMiddleware(on_error="raise")  # stop the run rather than skip the repair
RightRudderRepairMiddleware(max_reasks=0)      # never re-ask; filter and proceed only

on_error defaults to "proceed", so an unreachable service or a spent daily limit logs a warning and lets the agent act on its own proposal. A coherence repair should not take down a running agent. Pass "raise" when you are measuring a before-and-after, since a silently skipped repair would contaminate the result.

Every repaired step appends an entry to agent state under fathom_repair, carrying the decision, the proposals evaluated, the finding kinds of anything dropped, and the re-asks spent. That is the log a run needs to report what the repair did.

result = agent.invoke({"messages": [...]})
for step in result["fathom_repair"]:
    print(step["decision"], step["dropped"])

What it costs, in two places. A repair call counts double against the 2,000 calls a day a free key carries. A reground decision spends one extra model call, billed by your own provider. A model turn with no tool calls, and a tool the mapping does not name, cost nothing and pass through untouched.

What we measured

On DBOS's own Hacker News research agent, vendored unchanged and run on gpt-4o-mini through OpenRouter across five topics at ten iterations each, the agent as published repeated 14 of 50 searches and re-read 42 percent of the threads it fetched. With the repair in front of the one step where it proposes its next queries, repeats fell to 0 of 50, re-reads to 12 percent, and distinct threads covered rose 35 percent at the same model and the same iteration count. Across 45 follow-up steps the repair let 23 through untouched, filtered 21, and regrounded 1. Every run, the trace, and the command sit in the coherence census.

Those numbers come from an agent that hands out its proposed next queries explicitly. Your mileage depends on how much of your agent's state the tool mapping can see.

MIT licensed.


If the read caught something in your own run, a star on this repository helps other teams find it.

Metadata

Release files for langchain-right-rudder 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langchain-right-rudder 0.4.0
File Size Uploaded
langchain_right_rudder-0.4.0.tar.gz 23.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for langchain-right-rudder 0.4.0
File Interpreter ABI Platform
langchain_right_rudder-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.5 kB

Release files / langchain_right_rudder-0.4.0.tar.gz

Download URL langchain_right_rudder-0.4.0.tar.gz
Size 23.0 kB
Tags Source
SHA-256 checksum
How to use checksums
b1626a800d66d9248f706ff0deaf635acd6f49b75af0d0b2696250cf61828778
BLAKE2b-256 checksum
How to use checksums
1f34c53c66aa07ab8eca07ddb7c3a9a98c0aa80cb31eed548febad49a47480a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / langchain_right_rudder-0.4.0-py3-none-any.whl

Download URL langchain_right_rudder-0.4.0-py3-none-any.whl
Size 18.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
17b4c48e0284d500f5cb90baa2f7297e450c06d5f3bbbb37077e928e4ec8da9d
BLAKE2b-256 checksum
How to use checksums
d4af9282d929f208778af2f84b37b51e448061b079c53251b1a6c0d0f8450e69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page