Recorder

LangChain and LangGraph

One callback handler records every model call. LangGraph passes the node name in the callback metadata, so name your graph nodes with the blueprint's step ids.

  1. Copy harness_recorder.py into your project.
  2. Add the callback handler below to the graph's config.
  3. Record decisions in your conditional-edge functions with run.decision(gate_id, branch).

Callback handler

from langchain_core.callbacks import BaseCallbackHandler
from harness_recorder import Recorder, usage_from

class HarnessCallback(BaseCallbackHandler):
    def __init__(self, run):
        self.run, self.open = run, {}
    def on_chat_model_start(self, serialized, messages, *, run_id, metadata=None, **kw):
        meta = metadata or {}
        if meta.get("langgraph_node"):
            step = self.run.agent(meta["langgraph_node"], model=meta.get("ls_model_name"))
            step.__enter__(); self.open[run_id] = step
    def on_llm_end(self, response, *, run_id, **kw):
        step = self.open.pop(run_id, None)
        if step:
            step.model_call(**usage_from(response.generations[0][0].message)); step.__exit__(None, None, None)

run = Recorder("support-triage", "2.1.0").start_run(ticket_id)
graph.invoke(state, config={"callbacks": [HarnessCallback(run)]})
run.finish("completed")

Where the log lands

Wherever your service logs go (stdout), or a folder.

How it reaches the harness

You download the logs with your own access, or upload them.

LangChain and LangGraph can also emit OpenTelemetry through an instrumentation library; then use the OpenTelemetry panel instead.

Read the full guide on GitHub

Add the recorder

20 platform guides in total.