Skip to Content

What does Rius record when my agent calls CrewAI?

Rius records each model call CrewAI makes as a generation span with the model, token counts, cost and time to first token. It also records the run and tool spans you wrap with observe. Crews and tasks produce no spans of their own, so you wrap the entrypoints you want to see. This works for the Python framework; there is no TypeScript integration for CrewAI.

How it works

CrewAI 1.x drives the OpenAI client natively, so the openai integration captures its model calls. When LiteLLM is installed, CrewAI uses it as a fallback, and you use the litellm integration instead. With a global init(), every installed integration turns on automatically. The CrewAI integration page has the full recipe, and the Integrations overview lists the other frameworks.

The crew structure runs inside the framework, where auto-instrumentation cannot see it. You add that shape with observe wrappers on the entrypoint and on tool bodies.

Here is what lands in the trace:

In CrewAIIn the trace
Your function around crew.kickoff()The root AGENT span, from your observe wrapper
A model callAn LLM generation with model, tokens, cost and time to first token
A toolA TOOL span, from your observe wrapper on the tool body
Crews and tasksNo spans of their own

On each generation, the model name and token counts are what Rius uses to compute cost. You never send a cost value. Costs are list-price estimates in US dollars. Prompt and response content goes out with the spans unless you set RIUS_CAPTURE_CONTENT to false, which strips it at export.

A run that calls one tool looks like this:

handle-ticket AGENT 8.4s ├─ ChatCompletion LLM 3.9s agent decides to call the tool ├─ execute_tool account_status TOOL 0ms └─ ChatCompletion LLM 4.3s agent answers

Set it up

  1. Install the SDK with the OpenAI extra, next to CrewAI. The extra installs the instrumentation, never the library itself.

    pip install "glassflow-rius[openai]" crewai

    If LiteLLM is installed and CrewAI uses it, install pip install "glassflow-rius[litellm]" litellm instead.

  2. Call rius.init(), wrap the tool body with observe using the TOOL kind, and wrap your entrypoint as the AGENT root:

    import rius from crewai import Agent, Crew, Task from crewai.tools import tool from rius import SpanKind rius.init(api_key="ri_...", service_name="my-agent") @tool("account_status") @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( role="Support specialist", goal="Answer support tickets accurately and concisely.", backstory="You resolve product and account questions.", tools=[account_status], llm="gpt-5-mini", ) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) def handle_ticket(ticket: str) -> str: task = Task( description=f"Resolve this support ticket: {ticket}", expected_output="A short, direct answer.", agent=support_agent, ) return str(Crew(agents=[support_agent], tasks=[task]).kickoff())
  3. Keep the decorator order on tools: @tool outermost, observe on the function body. The tool span then parents under the run through ambient context.

  4. Where no terminal is attached, such as CI or containers, set CREWAI_TRACING_ENABLED=false. CrewAI has its own tracing product and asks about it interactively on the first run.

  5. To group one conversation or job, scope each kickoff with rius.session() and reuse your own id.

  6. Run one ticket that calls a tool, then open the trace in the console. You should see the root AGENT span, the LLM generations with model, tokens and cost, and your named TOOL spans.

What you get

  • A generation span for each model call, with model, token counts, cost and time to first token.
  • A root AGENT span named after your entrypoint, so each run is one trace.
  • A TOOL span for each tool body you wrapped, with its duration, placed under the run.
  • Sessions that group the kickoffs of one conversation or job, when you scope them with rius.session().

Known limits, verified against crewai 1.15.18:

  • Crew and task structure produces no spans. You cannot tell from the trace which task a generation belongs to. Wrap per-task entrypoints with observe if you need that.
  • Generation spans carry a generic name, ChatCompletion.
  • Streamed calls carry token counts, and so cost, only when usage is enabled on the underlying OpenAI client. See the include_usage note on the OpenAI integration page.

When something else fits

  • If you need task-level spans without wrapping each task yourself, wrap per-task entrypoints with observe, since CrewAI’s crews and tasks emit no spans.
  • If your agent is TypeScript, there is no CrewAI integration. Trace those calls with manual generations or another listed integration.

Still stuck? Get help

Last updated on