> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hue.run/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To set up Hue in an application, fetch https://docs.hue.run/guides/agent-setup.md and follow it.
> Hue's project-data MCP server is https://mcp.hue.run/mcp. https://docs.hue.run/mcp only searches these docs and cannot read a Hue project.
> Never ask the user to paste a Hue API key into chat; they store it as HUE_API_KEY themselves.
> Install the exact SDK versions pinned on the page you're following, or on https://docs.hue.run/quickstart.md. Run the latest CLI without a project install as `npx -y @hue-run/sdk@latest`. In a project with `@hue-run/sdk` installed, use `npx hue`. Pin an exact version for reproducible CI runs.
> Use Hue product terms: eval set, case, evaluator, run and scoring run. Older APIs may say dataset, scorer or experiment; prefer the current SDK methods. See https://docs.hue.run/concepts.md.

# LangChain and LangGraph

> Record the model and tool calls of a LangChain or LangGraph agent loop as Hue spans with the Python SDK.

LangChain's `create_agent` and a LangGraph `StateGraph` own the agent loop: the framework calls the model and runs the tools. Hue ships no LangChain instrumentation. Record the loop with the [Python SDK](/sdks/python): a request span around `invoke`, and a middleware that opens a Hue model span around each model call and a tool span around each tool call. Keep your model provider, prompts and tools as they are. `create_agent` and `AgentMiddleware` are LangChain 1.x APIs; this page was verified with `langchain` 1.4.6, `langgraph` 1.2.16 and `hue-run` 0.13.2.

## Record the loop

```python theme={null}
import os

from hue_sdk import create_hue_safe
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware
from langchain_core.messages import AIMessage, ToolMessage, convert_to_openai_messages
from langchain_core.utils.function_calling import convert_to_openai_tool

hue = create_hue_safe(api_key=os.environ.get("HUE_API_KEY"), service_name="support-agent")


class HueTracing(AgentMiddleware):
    def __init__(self, hue, provider):
        super().__init__()
        self.hue, self.provider = hue, provider

    def wrap_model_call(self, request, handler):
        model = getattr(request.model, "model_name", None) or type(request.model).__name__
        sent = {"tools": [convert_to_openai_tool(t) for t in request.tools]}
        if request.system_message is not None:
            sent["system_instructions"] = request.system_message.content
        with self.hue.model(model, provider=self.provider, **sent) as span:
            span.set_input(convert_to_openai_messages(request.messages))
            response = handler(request)
            span.set_output(convert_to_openai_messages(response.result))
            for message in response.result:
                if isinstance(message, AIMessage) and message.usage_metadata:
                    usage = message.usage_metadata
                    span.set_usage(input_tokens=usage["input_tokens"], output_tokens=usage["output_tokens"])
            return response

    def wrap_tool_call(self, request, handler):
        call = request.tool_call
        with self.hue.tool(call["name"], call_id=call.get("id")) as span:
            span.set_input(call["args"])
            result = handler(request)
            if isinstance(result, ToolMessage):
                span.set_output(result.content)
            return result


agent = create_agent(model, tools=tools, system_prompt=prompt, middleware=[HueTracing(hue, "openai")])


def reply(question, session_id, user_id):
    with hue.context(session_id=session_id, user_id=user_id), hue.span("support.reply") as request:
        request.set_input(question)
        state = agent.invoke({"messages": [{"role": "user", "content": question}]})
        answer = state["messages"][-1].content
        request.set_output(answer)
        return answer
```

`model`, `tools` and `prompt` are your existing agent's; pass the provider name you use (`"openai"`, `"anthropic"`). For `ainvoke` and `astream`, implement `awrap_model_call` and `awrap_tool_call` with the same bodies and `await handler(request)`. Each model and tool span becomes a child of the request span: LangGraph runs its nodes in copies of the caller's context, in threads and in asyncio tasks alike. In a hand-built `StateGraph`, open `hue.model` and `hue.tool` inside the nodes that call the model and run tools; the request span around `graph.invoke` is the same.

## What Hue stores

* The request span holds the user's question and the final reply, the session and the user ID; Hue titles the trace from the question.
* Each model span holds the messages the framework sent and the messages it got back, in the OpenAI chat shape `convert_to_openai_messages` produces (`gen_ai.input.messages`, `gen_ai.output.messages`), the tool definitions (`gen_ai.tool.definitions`), the system prompt (`gen_ai.system_instructions`), the model name and the usage. A tool-calling step keeps its `tool_calls` in the output message.
* Each tool span holds the arguments the agent passed and the result it got back, under the tool call ID the model issued, so the tool call and its result are linked in the trace.

Pass `capture_content=False` to record only metadata; see [Content capture](/sdks/python#content-capture). Flush and verify as the Python SDK page describes: [Verify](/sdks/python#verify), and [Async servers](/sdks/python#async-servers) when the agent serves from FastAPI.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.