CAMEL-AI¶
Available · FunctionTool per spec · approval from the
executor's port · pip install "agent-adapter[camel]" (camel-ai) · own environment (needs
mcp<2 and pins pydantic<=2.12.0, while all needs mcp>=2.3 and pydantic>=2.12.5; not in all)
Functions¶
Function (agent_adapter.integrations.camel) |
Returns |
|---|---|
camel_tool(spec, *, executor=None, strict=True) |
one camel.toolkits.FunctionTool running spec through the executor |
camel_tools(registry, *, executor=None, strict=True) |
one per registry tool exported to camel |
openai_tool_schema(spec, *, strict=True) |
the Chat Completions tool dict CAMEL sends to the model |
INTEGRATION |
get_integration("camel"); bind_approval(port) is None |
The schema is the OpenAI transform of the neutral schema. A spec
that cannot be strict is sent with strict: false. camel_tool refuses
(IntegrationError) a CAMEL outside the extra's range or without FunctionTool.async_call.
Wiring¶
def build_agent(model: Any) -> ChatAgent:
# The executor's port decides send_invoice (confirm=True): CAMEL has no gate of its own.
executor = Executor(registry, approval=CallbackApproval(reviewer))
return ChatAgent(model=model, tools=camel_tools(registry, executor=executor))
async def ask(agent: ChatAgent, prompt: str) -> list[tuple[str, Any]]:
response = await agent.astep(prompt) # astep awaits the executor; step uses run_sync
return [(call.tool_name, call.result) for call in response.info["tool_calls"]]
How a call runs¶
| CAMEL entry | Bridge path |
|---|---|
await agent.astep(...) → tool.async_call(**args) |
awaits executor.run_spec on CAMEL's loop |
agent.step(...) → tool(**args) |
run_sync over the same call (a worker thread inside a running loop) |
sequenceDiagram
participant C as ChatAgent
participant T as FunctionTool
participant Ex as Executor
participant P as ApprovalPort
C->>T: async_call(**args)
T->>Ex: run_spec(spec, args)
Ex->>P: confirm tool: request(...)
P-->>Ex: decision
Ex-->>T: value, or a model-visible error
T-->>C: result (ToolCallingRecord.result)
Approval¶
CAMEL 0.2.90 has no tool-confirmation gate and hands a tool no call id. So nothing CAMEL
does can approve a confirm tool: the executor's own port decides every one. With
no port, the executor denies it.
| Case | What happens |
|---|---|
confirm rule says yes |
the executor asks its port (call id: one the executor makes) |
| No port on the executor | denied: {"error": {"kind": "approval-denied", ...}} |
| An approval edits arguments | the executor applies the edits |
| A CAMEL toolkit or human-in-the-loop tool "approves" | not an approval: it can only add a gate in front |
Trust assumptions
The executor's port is the only approval. Do not wrap a confirm tool in your own
CAMEL confirmation and drop the port: the executor would then deny it.
Calls and errors¶
| Item | Behaviour |
|---|---|
| Call id | none from CAMEL; the executor makes one per call |
| Arguments | keyword only; positional ones raise ToolUsageError |
ToolArgsInvalid, ToolError, ApprovalDenied, GuardrailBlocked, RunLimitExceeded |
returned as {"error": {"kind", "message"}} |
Other errors (ToolFailed, …) |
raised; CAMEL records Tool execution failed: … with the error's message |
CancelledError |
propagates (astep); a step worker thread runs on until the call ends |
| Schema warnings | none: the bridge checks the parameters are a JSON Schema without CAMEL's per-field warnings |
Files¶
| File | Role |
|---|---|
agent_adapter/integrations/camel.py |
camel_tool, camel_tools, openai_tool_schema, INTEGRATION |
examples/camel_tools.py |
the wiring above, with a scripted CAMEL model |
tests/integrations/test_camel_contract.py |
contract kit C1-C8 and the bridge's edge cases |