138 lines
4.6 KiB
Markdown
138 lines
4.6 KiB
Markdown
# Conol.ai Tool Use Protocol
|
|
|
|
Gateway v6.0+ supports OpenAI-compatible tool calling through conol.ai's SSE API.
|
|
|
|
## Sending Tools to Conol.ai
|
|
|
|
Tools are passed in the session creation body as an Anthropic-format array:
|
|
|
|
```python
|
|
session_body = {
|
|
"source": {"type": "home"},
|
|
"messages": conol_msgs,
|
|
"timezone": "Europe/Kyiv",
|
|
"agentModel": model,
|
|
"agentEffort": "low",
|
|
"tools": [
|
|
{
|
|
"name": "get_weather",
|
|
"description": "Get current weather for a city",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {"city": {"type": "string"}},
|
|
"required": ["city"],
|
|
},
|
|
}
|
|
],
|
|
}
|
|
```
|
|
|
|
Both Anthropic-format (`name`, `input_schema`) and OpenAI-format (`type: "function"`, `function: {name, parameters}`) are accepted by conol.ai. The gateway converts OpenAI format to Anthropic before sending.
|
|
|
|
## Receiving Tool Calls from SSE
|
|
|
|
Conol.ai emits tool calls as SSE `history_delta` events with `tool_call` log entries:
|
|
|
|
```json
|
|
{
|
|
"type": "history_delta",
|
|
"stages": [{
|
|
"id": "...",
|
|
"logs": [{
|
|
"role": "assistant",
|
|
"type": "tool_call",
|
|
"content": [{
|
|
"name": "WebSearch",
|
|
"type": "tool_call",
|
|
"input": {"query": "Paris weather"},
|
|
"result": [{"text": "83°F, Overcast..."}]
|
|
}]
|
|
}]
|
|
}]
|
|
}
|
|
```
|
|
|
|
Key fields:
|
|
- `name` — tool name (string)
|
|
- `input` — JSON arguments (dict)
|
|
- `result` — optional: array of `{"text": "..."}` objects with tool output
|
|
|
|
## Converting to OpenAI Format
|
|
|
|
```python
|
|
def conol_to_openai_tool_calls(events):
|
|
tool_calls = []
|
|
for ev in events:
|
|
if ev.tool_name:
|
|
tool_calls.append({
|
|
"id": f"call_{uuid.uuid4().hex[:8]}",
|
|
"type": "function",
|
|
"function": {
|
|
"name": ev.tool_name,
|
|
"arguments": json.dumps(ev.tool_input, ensure_ascii=False),
|
|
},
|
|
})
|
|
return tool_calls
|
|
```
|
|
|
|
## Built-in Tools (Auto-called by conol.ai)
|
|
|
|
Conol.ai models have built-in tools that fire automatically even when no user tools are provided:
|
|
|
|
| Tool | Purpose |
|
|
|------|---------|
|
|
| `WebSearch` | Web search via Exa.ai |
|
|
| `Fetch` | Fetch URL content with prompt |
|
|
| `SetIntention` | Internal meta-tool for task routing |
|
|
| `WriteNote` | Create Obsidian-style notes |
|
|
| `ReadNote` | Read persisted notes |
|
|
|
|
These appear in SSE as `tool_call` logs alongside any user-provided tools.
|
|
|
|
## Streaming Tool Calls
|
|
|
|
In streaming mode, tool calls are emitted as SSE chunks with `delta.tool_calls[]`:
|
|
|
|
```json
|
|
{
|
|
"object": "chat.completion.chunk",
|
|
"choices": [{
|
|
"delta": {"tool_calls": [{"id": "call_abc", "type": "function", "function": {"name": "WebSearch", "arguments": "{\"query\":\"...\"}"}}]},
|
|
"finish_reason": null
|
|
}]
|
|
}
|
|
```
|
|
|
|
Then final chunk with `finish_reason: "tool_calls"` or `"stop"`.
|
|
|
|
## Tool Result Loop (Tool → Model → Response)
|
|
|
|
To continue a conversation after tool calls, send tool results back as messages with `role: "tool"`:
|
|
|
|
```json
|
|
{
|
|
"model": "gpt-5.6-luna",
|
|
"messages": [
|
|
{"role": "user", "content": "Weather in Paris?"},
|
|
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_1", "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}]},
|
|
{"role": "tool", "tool_call_id": "call_1", "content": "83°F, Overcast"}
|
|
]
|
|
}
|
|
```
|
|
|
|
The gateway converts `role: "tool"` messages to text: `[Tool result #call_1]: 83°F, Overcast`.
|
|
|
|
## Pitfalls
|
|
|
|
### Duplicate Tool Calls
|
|
Conol.ai sometimes emits duplicate `tool_call` events in the SSE stream (same name, same arguments, different stage IDs). The gateway currently returns all of them. For production use, deduplicate by `(name, json.dumps(input))` key.
|
|
|
|
### Tool + Text in Same Response
|
|
Conol.ai models may return BOTH text content AND tool calls in the same response. OpenAI convention is `content: null` when tool_calls are present. The gateway currently returns both — downstream clients expecting strict OpenAI protocol may need `content: null` when `tool_calls` is non-empty.
|
|
|
|
### Tool Choice
|
|
`tool_choice: "auto"/"none"/"required"` is passed but conol.ai does not enforce strict tool choice — it always has its built-in tools available. For `tool_choice: "none"`, filter tool calls out in the gateway.
|
|
|
|
### Streaming + Tool Calls
|
|
When streaming with tools, conol.ai sends thinking text first, then tool calls, then final text. The gateway collects all events and emits tool calls first, then text. This causes a delay before the first chunk — the full response must arrive before streaming begins. Future improvement: buffer and emit as events arrive.
|