ned.watch ← integrations

Integration

LangGraph and LangChain

Know when your LangGraph agent silently stops.

from nedwatch.integrations.langchain import NedCallbackHandler      # pip install ned-watch; set NED_CALLBACK_URL
graph.invoke(inputs, config={"callbacks": [NedCallbackHandler(every="1h", max="20m")]})

The handler watches the run you invoke (the graph or chain at the top), not each node inside it:

eventwhat Ned gets
the top-level run startsthe deadman is armed; an overrun run starts (with max=)
it endsthe overrun run finishes; the deadman checks in
it raisesno check-in (the deadman fires when every passes); the overrun run is finished as failed with the error, so Ned fires at once

The watch name defaults to the compiled graph's name (builder.compile(name="research-agent")), else the script name. Pass a name to choose: NedCallbackHandler("research-agent", every="1h").

Options

NedCallbackHandler(
    name=None,              # watch name
    every="1h",             # deadman: fire if no successful run for this long (default when max isn't set)
    max=None,               # overrun: fire if one run takes longer than this
    on_error="report",      # "report" (failed + error) | "leave_open" | "finish"
    heartbeat=False,        # True: each LLM reply / tool result also checks in (throttled to one per 30 s)
    ned=None,               # a nedwatch.Ned, to set the key, region or callback in code
)

heartbeat=True suits long-lived agents that run for hours: the deadman then means "no progress for every". It can hide a run that does some work and then fails, so leave it off for scheduled runs.

Use AsyncNedCallbackHandler with ainvoke / astream to stay on the event loop (the sync handler also works there; LangChain runs it in a thread).

A check-in node instead

If you'd rather see it in the graph, add a node on the success path:

from nedwatch.integrations.langchain import checkin_node
builder.add_node("ned", checkin_node("research-agent", every="1h"))
builder.add_edge("summarise", "ned")
builder.add_edge("ned", END)

It checks in and changes no state.

Plain LangChain

The same handler works on any runnable: chain.invoke(x, config={"callbacks": [NedCallbackHandler("summariser")]}). New agents are tagged langgraph when LangGraph is imported, else langchain.

Example

examples/langgraph/ has a runnable graph (with a stub model) and tests that run it against a local Ned Watch.