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

# CrewAI

> Durable execution, policy, and approvals for CrewAI agents

A CrewAI crew runs on Rebuno with two seams. Model calls go through a
Rebuno-aware gateway, which records each one as an `llm_call` step, and tools
are declared with `@tool`, so every tool call is recorded as a `tool_call` step.
On a re-dispatch the agent runs from the top and recorded steps replay instead
of calling the model or the tool again.

## Install

```bash theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
pip install rebuno crewai
```

## Model calls

CrewAI builds its own HTTP client, so `http_client()` can't be passed in.
Instead, point the model's `base_url` at a gateway that implements the
[LLM call contract](/llm-calls#implement-interception), and forward the dispatch
in headers so the gateway can record the call under it:

```python theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
import os

from crewai import LLM
from rebuno import execution

ctx = execution()
llm = LLM(
    model="openai/gpt-5.5",
    base_url=os.environ["GATEWAY_URL"],
    api_key=os.environ["GATEWAY_KEY"],
    extra_headers={
        "rebuno-execution-id": ctx.id,
        "rebuno-dispatch-id": ctx.dispatch_id,
        "rebuno-dispatch-attempt": str(ctx.dispatch_attempt),
        "rebuno-agent-id": ctx.agent_id,
        "rebuno-agent-secret": os.environ["REBUNO_AGENT_SECRET"],
    },
)
```

`execution()` reads the current dispatch, so build the `LLM` inside the handler.
[`examples/gateway/litellm_proxy.py`](https://github.com/rebuno/rebuno/blob/main/examples/gateway/litellm_proxy.py)
is a LiteLLM proxy callback that implements the gateway.

## Tools

Apply CrewAI's `tool` decorator over Rebuno's `@tool`. Rebuno's wrapper keeps
the function's signature and docstring, so CrewAI builds the tool schema from it
as usual:

```python theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
from crewai.tools import tool as crewai_tool
from rebuno import tool


@crewai_tool("lookup_orders")
@tool("lookup_orders", idempotency="safe_to_retry")
async def lookup_orders(customer_id: str) -> dict:
    """Look up a customer's recent orders."""
    ...


@crewai_tool("send_email")
@tool("send_email", idempotency="at_most_once")
async def send_email(body: str) -> dict:
    """Email the support summary."""
    ...
```

Mark anything with a side effect, such as sending an email or creating a
ticket, `at_most_once`. See [idempotency](/sdk/python/tools#idempotency).

## The agent

CrewAI's `Agent` and Rebuno's `Agent` share a name, so import one under an
alias:

```python theme={"theme":{"light":"min-light","dark":"material-theme-ocean"}}
from crewai import Agent as CrewAgent
from crewai import Crew, Task
from rebuno import Agent


async def process(query: str) -> dict:
    llm = ...  # the gateway LLM above
    specialist = CrewAgent(
        role="support specialist",
        goal=query,
        backstory="You investigate customer issues and email support summaries.",
        llm=llm,
        tools=[lookup_orders, send_email],
    )
    task = Task(description=query, expected_output="a short brief", agent=specialist)
    result = await Crew(agents=[specialist], tasks=[task]).kickoff_async()
    return {"answer": str(result)}


agent = Agent("support")

if __name__ == "__main__":
    agent.run(process)
```

## What Rebuno adds

* **Policy.** Every model and tool call is checked against [policy](/policy)
  before it runs. A denied tool returns the rule's reason to the model as the
  tool result, so the agent can take a different path.
* **Approvals.** A tool that requires approval parks the execution. Once it's
  approved, the agent is dispatched again, the earlier steps replay, and the
  approved call runs.
* **Recovery.** If the worker dies partway through, the next dispatch replays
  every completed step and continues from the first one that didn't finish.
  Model calls that already ran are not paid for twice.

## Full example

[`examples/frameworks/python/crewai_agent.py`](https://github.com/rebuno/rebuno/blob/main/examples/frameworks/python/crewai_agent.py)
is a support agent that investigates a customer issue, creates a ticket, and
emails a summary after approval.
