Skip to content

CrewAI: using the VIDAI Control Plane as the backend

CrewAI agents each take an llm= argument. Point that LLM object at the control plane's base URL and every agent, task, and delegation on the crew runs through it.

TL;DR

from crewai import Agent, Crew, Task, LLM

llm = LLM(
    model="openai/gpt-4o-mini",
    base_url="https://your-vidai-server.example.com/v1",
    api_key="your-vidai-key",
)

researcher = Agent(role="Researcher", goal="Find facts", llm=llm)
task = Task(description="Summarise CrewAI in one line", agent=researcher)
Crew(agents=[researcher], tasks=[task]).kickoff()

Prerequisites

  • pip install crewai (any recent version — LLM has been the configured-model entry point since 0.30).
  • Control plane base URL and an API key from the API Keys page.
  • A model registered on the Models page. The model= string you pass to LLM is what the control plane resolves.

Configuring a single crew

The LLM object is standalone. Build it once, hand it to every agent that should share it:

from crewai import Agent, Crew, Task, LLM

vidai = LLM(
    model="openai/gpt-4o-mini",
    base_url="https://your-vidai-server.example.com/v1",
    api_key="your-vidai-key",
    temperature=0.2,
)

planner = Agent(role="Planner", goal="Plan the trip", llm=vidai)
writer = Agent(role="Writer", goal="Write the itinerary", llm=vidai)

plan = Task(description="Plan a 3-day Tokyo trip", agent=planner)
write = Task(description="Turn the plan into an itinerary", agent=writer, context=[plan])

Crew(agents=[planner, writer], tasks=[plan, write]).kickoff()

The openai/ prefix on the model string tells CrewAI's underlying router to use the OpenAI wire shape — which is what the control plane speaks on /v1/chat/completions. Any model you've registered on the Models page is callable as openai/<your-model-name> from here.

Mixing models per agent

Different agents can use different LLM objects — cheap model for the planner, richer model for the writer:

cheap = LLM(model="openai/gpt-4o-mini",   base_url=..., api_key=...)
richer = LLM(model="openai/claude-sonnet-4.5", base_url=..., api_key=...)

planner = Agent(role="Planner", goal="...", llm=cheap)
writer  = Agent(role="Writer",  goal="...", llm=richer)

Both endpoints go through the same control plane; you set routing rules on the Routing page once and they apply across every agent.

Environment-based config

If you'd rather keep secrets and endpoints in env:

export OPENAI_API_BASE=https://your-vidai-server.example.com/v1
export OPENAI_API_KEY=your-vidai-key
from crewai import Agent, LLM

# LLM picks up the env vars when base_url/api_key aren't passed.
llm = LLM(model="openai/gpt-4o-mini")

Tools and delegation

CrewAI's tool calls and inter-agent delegation both use the same llm= you configured on the agent. No extra setup — every tool call and every delegated sub-task is a control-plane request that shows up in Request Logs.

Structured output

CrewAI's output_json= and output_pydantic= fields on Task work through the underlying model's JSON mode / structured output support. Pass them as normal; the control plane forwards the request unchanged:

from pydantic import BaseModel

class Itinerary(BaseModel):
    day: int
    plan: str

task = Task(
    description="Plan day 1 of a Tokyo trip",
    agent=planner,
    output_pydantic=Itinerary,
)

Verify it works

from crewai import Agent, Crew, Task, LLM

llm = LLM(
    model="openai/gpt-4o-mini",
    base_url="https://your-vidai-server.example.com/v1",
    api_key="your-vidai-key",
)

probe = Agent(role="Probe", goal="Respond briefly", llm=llm)
task = Task(description="Reply with exactly: ok", agent=probe)
result = Crew(agents=[probe], tasks=[task]).kickoff()
print(result)

Expect ok (or a very short variant). A new row appears on the control plane's Request Logs attributed to the API key you passed.

If something's off

Raise an issue at github.com/vidaiUK/vidai-quickstart/issues with your LLM config and the crew that reproduces the issue. We'll get it sorted.

Where to go next