Skip to Content

How do I trace an OpenAI Agents SDK agent deployed to Vercel functions?

Rius traces an OpenAI Agents SDK agent on Vercel functions when you call rius.init once at module level, wrap the entrypoint and tools with observe, and flush spans at the end of every invocation. Model calls arrive as generations with tokens and cost. The flush matters because a serverless runtime freezes the process between invocations, so spans still queued can go unsent. Use underscores in the framework’s Agent(name=...).

How it works

  • Model calls. The framework drives the OpenAI client underneath, so the openai integration records each model call with model, tokens, cost and time to first token. You add no framework-specific code for that. See the OpenAI Agents SDK page for the full coverage table.
  • Run structure. The agent loop and tool executions run inside the framework. An observe wrapper with the AGENT kind on your entrypoint, and one with the TOOL kind on each tool body, puts that structure in the trace.
  • Export. Spans are queued in memory and sent in batches from a background thread. A serverless runtime suspends the process between invocations, so that thread exports nothing while frozen, and the exit hook that normally drains the queue may never run. An explicit flush() at the end of each invocation sends what is queued. The Reliability page covers the pipeline in detail.
  • Heartbeat. The SDK pings Rius for the process’s whole lifetime by default. A suspended process cannot keep pinging, so in serverless runtimes you turn it off.

Set it up

  1. Install the SDK with the OpenAI extra, and the framework. The integrations overview covers installing in full.
pip install "glassflow-rius[openai]" openai-agents
  1. Call rius.init once, at the top of the module that defines your agent, before the agent and the handler. Code at module level runs once per process, not on every request. Pass heartbeat=False, and keep the returned client so you can flush it. Disable the framework’s own tracing with set_tracing_disabled(True) if Rius should be the single telemetry path.

  2. Wrap the entrypoint with observe as an AGENT span, and each tool body with observe as a TOOL span. Keep @function_tool outermost on tools.

  3. Flush in a finally block at the end of every invocation, so spans leave even when the run raises.

import rius from agents import Agent, Runner, function_tool, set_tracing_disabled from rius import SpanKind client = rius.init(api_key="ri_...", service_name="my-agent", heartbeat=False) set_tracing_disabled(True) @function_tool @rius.observe(kind=SpanKind.TOOL) def account_status(email: str) -> str: """Fetch a customer's account standing by email.""" return lookup(email) support_agent = Agent( name="support_agent", instructions="Answer support questions; check accounts with the tool.", tools=[account_status], model="gpt-5-mini", ) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) def handle_ticket(ticket: str) -> str: result = Runner.run_sync(support_agent, ticket) return str(result.final_output) def handle_request(ticket: str) -> str: # Call this from your Vercel function's handler. try: return handle_ticket(ticket) finally: client.flush(timeout_millis=30_000)
  1. Name things deliberately.

    • The agent. Use underscores in Agent(name=...). Hyphens are rewritten in the generated handoff tool name, and the framework warns on every run.
    • The root span. The name you give observe is how the run appears in the trace, here handle-ticket.
    • The service. service_name is the name the agent is grouped under by default. Set RIUS_AGENT_NAME if you want a different grouping name.
  2. Group turns of one conversation with rius.session(), reusing your conversation or ticket id.

Flush once per invocation, at the end. Do not flush inside the agent loop or other hot paths, because flush() blocks until the export completes.

What you get

  • A root AGENT span per request, with an LLM generation for each model call showing model, tokens, cost and time to first token, and a TOOL span for each tool call.
  • Spans that leave the process before the runtime freezes it, so a short invocation does not lose its trace.
  • Tool spans you named yourself, under a root named after your entrypoint.
  • Cost as a list-price estimate in US dollars, computed by Rius from the model and token counts.
  • Because the heartbeat is off, the agent is marked no heartbeat and lists no Instances. Its traces still appear.
  • Handoffs produce no span of their own. They show as consecutive generations from different agents, so add an observe wrapper per agent entry if you need the boundaries.
  • Streamed runs carry token counts only when usage is enabled on the underlying OpenAI client. See the OpenAI Agents SDK page for the include_usage note.

When something else fits

  • The OpenAI Agents SDK integration is Python only. If your Vercel functions are written in TypeScript, the Integrations page lists what each language supports, and calls with no integration are traced with manual generations.
  • If your agent runs in a long-lived process rather than a function, keep the heartbeat on and flush at graceful shutdown instead, as the Reliability page describes.

Still stuck? Get help

Last updated on