CrewAI
CrewAI gets a governed native LLM object pointed at the Agent Fabric LLM proxy. The adapter translates the governed connection into CrewAI’s own model-string and kwarg conventions for you.
What you get
- A native
crewai.BaseLLM— concretelyOpenAICompletion, CrewAI’s native OpenAI provider.crewai.LLM’s own__new__factory routes anopenai/-prefixed model with an explicitbase_urlto that provider rather than returning anLLMinstance itself. The proxy auth and attribution headers are set. - The
openai/model prefix and CrewAI’s kwarg names handled automatically. - Supported at
connection_kwargs(). CrewAI’s native provider builds its own HTTP client rather than using the SDK’s, so there is no run correlation ID anddonkey.last_callis not populated (see Notes).
Install
pip install "donkey-kit[crewai]"Quickstart
Python
from donkey_kit.integrations.crewai import llm
model = llm("gpt-4o")model is a real crewai.BaseLLM instance (OpenAICompletion). The adapter
prefixes the model string with openai/ (openai/gpt-4o) — you don’t add it
yourself. That prefix, together with the proxy base_url, is what routes
crewai.LLM’s factory to its native OpenAI provider; the provider strips the
prefix again, so the proxy receives the bare model id (gpt-4o).
Three ways to construct
1. Off a shared Donkey instance:
from donkey_kit import Donkey
async with Donkey.from_env() as donkey:
model = donkey.crewai.llm("gpt-4o")2. Module-level factory (shortest):
from donkey_kit.integrations.crewai import llm
model = llm("gpt-4o")3. Governed kwargs, native constructor:
from donkey_kit import Donkey
from crewai import LLM
async with Donkey.from_env() as donkey:
model = LLM(model="openai/gpt-4o", **donkey.crewai.connection_kwargs())Manual equivalent
from crewai import LLM
model = LLM(
model="openai/gpt-4o",
base_url=..., # from DONKEY_LLM_PROXY_URL, no /v1 suffix
api_key=...,
extra_headers=..., # client_id / client_secret header pair
max_retries=0, # don't let the OpenAI client retry a refusal
)connection_kwargs() fills in base_url, api_key, extra_headers (the
client_id / client_secret header pair), and max_retries=0 for you. When CrewAI is installed it
also adds an interceptor: with one set, CrewAI’s provider builds HTTP clients
that don’t follow redirects, and the interceptor removes the credential headers
from any request to an origin other than the proxy (see
Credentials go only to checked endpoints).
A base_url / api_base passed to llm() must pass the
https:// rule.
Notes
- No run correlation ID. CrewAI sends requests through its native OpenAI
provider’s own HTTP client rather than the SDK’s shared one, so the
correlation ID bound by
donkey.run()doesn’t reach the request. The auth and attribution headers are still sent on every request. The conformance suite checks this as a documented behaviour. jwtmode isn’t supported. The JWT is added only by the SDK’s shared client, which CrewAI’s provider doesn’t use, sodonkey.crewai.llm()andconnection_kwargs()raiseConfigErrorinjwtmode. Use client-id auth with CrewAI; see thejwtmode note.- A budget refusal is sent 3 times. CrewAI wraps every LLM call in its own
rate-limit retry (3 attempts, with a 1s then 2s wait) and treats any
429as a rate limit. On the proxy a429is a budget refusal, so CrewAI re-sends it twice before raising. CrewAI has no setting to turn this retry off. The OpenAI client underneath hasmax_retries=0, and transient5xxerrors are not retried at all, because the SDK’s transport isn’t used. - Printing the model shows the API key.
OpenAICompletion’s ownrepr()/str()includeapi_key. Don’t print or log it. donkey.last_callis unavailable. Because the response is handled by CrewAI’s own client, gateway identity, routing, and usage fields can’t be observed. When every adapter resolved on aDonkeyis like this one,donkey.last_callreportsstatus == LastCallStatus.UNAVAILABLEandavailable == False, and names the resolved adapters insurface.
See the error taxonomy for how proxy rejections surface through CrewAI.