CrewAI
CrewAI reads OPENAI_BASE_URL and OPENAI_API_KEY directly from the
environment. The integration is the simplest of any framework: two
env vars.
Minimal example
import os
os.environ["OPENAI_BASE_URL"] = f"{os.environ.get('CURATE_ME_GATEWAY_URL', 'https://api.curate-me.ai')}/v1/openai"
os.environ["OPENAI_API_KEY"] = os.environ["CM_API_KEY"] # CrewAI sends this as Authorization: Bearer
from crewai import Agent, Task, Crew
researcher = Agent(role="Researcher", goal="Find AI governance trends", backstory="Senior analyst.")
task = Task(description="3 key trends in AI governance", expected_output="bulleted list", agent=researcher)
Crew(agents=[researcher], tasks=[task]).kickoff()Full example at
examples/integrations/crewai/basic_crew.py.
Why one env var works
CrewAI uses LiteLLM internally to talk to OpenAI. LiteLLM honours
OPENAI_BASE_URL and treats OPENAI_API_KEY as a bearer token. The
gateway accepts Authorization: Bearer cm_sk_... as an equivalent of
X-CM-API-Key: cm_sk_....
Supported versions
| Library | Tested range |
|---|---|
crewai | >=0.95.0,<2.0.0 |
litellm | >=1.50.0,<2.0.0 (transitive) |
Known limitations
- Per-agent model selection: each
Agent(llm=...)override has to point at the gateway. SetOPENAI_BASE_URLglobally OR passllm=ChatOpenAI(base_url=...)per agent. - CrewAI memory: long-term memory persistence runs entirely on your side; the gateway only sees per-tick LLM calls.
- Tools: standard tools (
SerperDevTool,FileReadTool, etc.) make their own network calls outside the gateway. Only the LLM step is governed. - Custom providers: if you use
litellm’s non-OpenAI provider syntax (e.g.anthropic/claude-3-5-sonnet), CrewAI bypasses theOPENAI_BASE_URLfor that call. Point CrewAI agents explicitly atopenai/gpt-4o(or similar) to keep traffic through Curate-Me.