Skip to content

LangGraph

LangGraph builds stateful, multi-step agents as graphs. It emits OpenTelemetry traces through the LangSmith SDK (bundled with langchain). When you call logfire.configure(), Logfire installs the global OpenTelemetry tracer provider, and the LangSmith tracer detects that provider and uses it — so your graph’s spans flow straight into Logfire with no exporter, endpoint, or API key configuration.

Installation

Terminal
pip install logfire langchain langgraph langchain-openai

Usage

Set the three LANGSMITH_* environment variables before importing langchain/langgraph, then call logfire.configure():

import os

# Must be set before importing langchain/langgraph
os.environ['LANGSMITH_OTEL_ENABLED'] = 'true'
os.environ['LANGSMITH_OTEL_ONLY'] = 'true'  # OTel only; no LangSmith backend, no API key needed
os.environ['LANGSMITH_TRACING'] = 'true'

from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode

import logfire

logfire.configure()  # sets the global OTel tracer provider that LangSmith detects


tool_calls: list[str] = []


@tool
def lookup_incident(incident_id: str) -> str:
    """Look up the current status and owner of an incident by ID."""
    tool_calls.append(incident_id)
    return f'{incident_id} is resolved; owner=platform-observability'


llm = ChatOpenAI(model='gpt-5-mini', temperature=0)
llm_with_tool = llm.bind_tools([lookup_incident], tool_choice='lookup_incident')


def call_tool_model(state: MessagesState) -> dict:
    return {'messages': [llm_with_tool.invoke(state['messages'])]}


def call_final_model(state: MessagesState) -> dict:
    return {'messages': [llm.invoke(state['messages'])]}


builder = StateGraph(MessagesState)
builder.add_node('agent', call_tool_model)
builder.add_node('tools', ToolNode([lookup_incident]))
builder.add_node('respond', call_final_model)
builder.add_edge(START, 'agent')
builder.add_edge('agent', 'tools')
builder.add_edge('tools', 'respond')
builder.add_edge('respond', END)
graph = builder.compile()

result = graph.invoke(
    {
        'messages': [
            HumanMessage(
                content="Use lookup_incident with incident_id='incident-42', then report the status and owner."
            )
        ]
    }
)
if tool_calls != ['incident-42']:
    raise RuntimeError(f'expected one tool call for incident-42, received {tool_calls}')
print(result['messages'][-1].content)

The example fails unless the graph executes its native ToolNode. You’ll see a trace in Logfire with a span for the graph run, a span per node, and the underlying LLM and lookup_incident tool calls nested beneath them. LangGraph agents also appear in the specialized Agents view with per-run token counts; the support matrix shows which columns each view populates.

Managed prompts

Keep your nodes’ prompts in Prompt Management and fetch them at runtime:

Terminal
pip install 'logfire[variables]'
from pydantic import BaseModel

import logfire

logfire.configure()


class JokeInputs(BaseModel):
    topic: str


prompt_var = logfire.template_var(
    name='prompt__joke',
    type=str,
    default='Tell me a joke about {{topic}}',
    inputs_type=JokeInputs,
)


def tell_joke(state):
    with prompt_var.get(JokeInputs(topic=state['topic']), label='production') as resolved:
        prompt = resolved.value
    response = llm.invoke(prompt)  # llm defined as in the example above
    return {'joke': response.content}

See Use Prompts in Your Application for the full workflow.