How do I trace an agent built with LangChain and deployed to a FastAPI service?
Rius traces a LangChain agent in a FastAPI service when you call rius.init() once at process start, before you build and run your chains. A long-running FastAPI server needs no manual flush in Python, because the SDK drains pending spans at clean shutdown. The service_name you pass to init() is the name your agent appears under in Rius.
How it works
- LangChain is traced through its callback system. The Rius LangChain integration hooks LangChain’s callbacks, so anything that runs through them is traced: LCEL chains, agents and LangGraph nodes. Each step becomes a span typed by what it is: chains as CHAIN, tools as TOOL, retrievers as RETRIEVER, and model calls as LLM spans with model, token usage and messages. Rius computes cost on its side. The nesting follows your chain structure.
- Order matters. The integration attaches when
init()runs, so callinit()before you build and run chains. In a FastAPI service that means app startup, not inside a request handler. - Export runs off the request path. Spans are queued in memory and exported in batches from a background thread. Span creation never waits on the network, so a request is not slowed by tracing.
- Shutdown is mostly automatic. For a long-running server such as FastAPI, Python needs nothing from you: batches export continuously, and the exit hook drains the rest on a clean shutdown. Calling
shutdown()yourself also sends the finalstoppedheartbeat, which is how Rius tells a clean exit from a crash. - The agent name comes from
service_name.RIUS_SERVICE_NAMEsets the same value, andRIUS_AGENT_NAMEoverrides the name the agent is grouped under. It defaults to the service name.
Set it up
-
Install the SDK with the LangChain extra. The extra installs the instrumentation, not LangChain, which your service already has. LangChain integration covers the full setup.
pip install "glassflow-rius[langchain]" langchain -
Call
init()in the FastAPI lifespan, before the agent is built, and callshutdown()after theyield. Replacebuild_agentwith your own function that creates the chain or agent, and pick a stable, descriptive service name.from contextlib import asynccontextmanager from fastapi import FastAPI import rius @asynccontextmanager async def lifespan(app: FastAPI): client = rius.init(api_key="ri_...", service_name="support-agent") app.state.agent = build_agent() # your own function yield client.shutdown() # drains spans and sends the final stopped heartbeat app = FastAPI(lifespan=lifespan) -
Run your endpoints as before. Do not call
client.flush()inside request handlers. It blocks until the export completes, which is the stall the background exporter exists to avoid. Flush only at process boundaries. -
Choose the name deliberately. Use
service_name, or setRIUS_SERVICE_NAMEorRIUS_AGENT_NAMEin the environment. Keep it the same across deploys so the agent stays one entry. To compare releases on the Agents page, also setRIUS_MAIN_AGENT_VERSION, which is the version that page compares. -
Send one request that calls a model, then open the trace in the console. You should see the chain as nested spans, with the model-call span carrying model, tokens and cost.
-
If you run several workers with gunicorn or multiprocessing, you need nothing at fork time. The exporter re-creates its background thread in each forked child. Each worker shows as its own instance. Call
flush()only if workers are killed hard, since an unclean shutdown can lose the last batch.
What you get
- A nested trace for every request, mirroring your chain or graph, with a span for each chain step, tool call, retrieval and model call.
- Tokens and cost per model call. Cost is a list-price estimate in US dollars that Rius computes, so you never send a cost value.
- One agent entry named after your service, with its instances on the Agents view because the heartbeat is on by default and your API key is set. A worker shows as running while it sends heartbeats, and a clean
shutdown()marks it stopped rather than gone. - No change to how requests are served. Exporter failures are logged and never raise into your application code.
- More on batching, retries and heartbeat delivery in Reliability.
When something else fits
- If you deploy the same agent to a serverless runtime instead, call
client.flush()at the end of every invocation and setheartbeat=False, because the runtime freezes the process between calls. - For a CLI or batch job built on LangChain, call
client.flush()before the process exits. - If your agent uses a different framework, pick its setup from the Integrations list.