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.
- Copy
harness_recorder.pyinto your project. - Add the callback handler below to the graph's config.
- 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.