Tool Calls
The SSE protocol for tool calling.
Tool calling (function calling) follows a specific event sequence in the SSE stream. The server performs the tool execution and streams the events. The client receives and renders them.
Protocol Flow
- The server runs a model that decides to call a tool.
- It sends a
tool_callevent over SSE. - The server executes the tool and sends a
tool_resultevent. - Optionally, it continues with
text_deltaevents. - The stream ends with
data: [DONE].
Event Sequence
A typical tool call stream:
data: {"type":"tool_call","tool_name":"get_weather","argument":"{\"city\":\"London\"}","call_id":"call_1"}
data: {"type":"tool_result","call_id":"call_1","output":"Sunny, 18°C in London"}
data: {"type":"text_delta","delta":"The weather in London is sunny, 18°C."}
data: [DONE]Event Types
tool_call
Signals that the model wants to invoke a tool.
interface ToolCallEvent {
type: "tool_call";
tool_name: string; // Name of the tool
argument: string; // JSON string of arguments
call_id?: string; // Unique ID linking to the result
}tool_result
Contains the output of the tool execution.
interface ToolResultEvent {
type: "tool_result";
call_id: string; // Matches the tool_call's call_id
output: string; // Result string
}Multiple Tool Calls
A single response can include multiple tool calls. Each gets its own tool_call and tool_result pair, linked by call_id:
data: {"type":"tool_call","tool_name":"search","argument":"...","call_id":"call_1"}
data: {"type":"tool_result","call_id":"call_1","output":"..."}
data: {"type":"tool_call","tool_name":"calculate","argument":"...","call_id":"call_2"}
data: {"type":"tool_result","call_id":"call_2","output":"..."}
data: {"type":"text_delta","delta":"Based on the results..."}
data: [DONE]The call_id field connects each result to its call. Always include it when using multiple tools.
ToolCallPart Structure
On the client side, tool calls are stored as ToolCallPart in the message's parts array:
interface ToolCallPart {
type: "tool_call";
tool_name: string; // Name of the tool
argument: string; // JSON string of arguments
callId?: string; // Matches call_id from events
result?: string; // Filled by tool_result event
}Server Example (FastAPI)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json
app = FastAPI()
def get_weather(city: str) -> str:
return f"Sunny, 18°C in {city}"
@app.post("/api/chat")
async def chat(request: dict):
message = request["message"]
async def generate():
if "weather" in message.lower():
# 1. Send tool call
yield f'data: {json.dumps({
"type": "tool_call",
"tool_name": "get_weather",
"argument": json.dumps({"city": "London"}),
"call_id": "call_1"
})}\n\n'
# 2. Execute tool
result = get_weather("London")
# 3. Send tool result
yield f'data: {json.dumps({
"type": "tool_result",
"call_id": "call_1",
"output": result
})}\n\n'
# 4. Continue with text
for word in ["The weather", " in London", f" is {result}."]:
yield f'data: {json.dumps({
"type": "text_delta",
"delta": word
})}\n\n'
else:
for word in ["I can", " help with", " weather!"]:
yield f'data: {json.dumps({
"type": "text_delta",
"delta": word
})}\n\n'
yield "data: [DONE]\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")Client-Side Rendering
For handling tool call events and rendering them in React, see Tool Call Rendering.