Function Calling
Function calling enables the language model to invoke external functions during a conversation, allowing it to retrieve real-time information or perform operations.
Supported Models
The following models support function calling:
deepseek-r1-70bgpt-oss-120bLlama-3.3-70B-Instructmistral-small3.2qwen3-30bqwen3-coder-30b
Web Search Example
Here's a complete example using DuckDuckGo web search with the Regolo API:
from ddgs import DDGS
import logging
import os
import requests
import json
logger = logging.getLogger(__name__)
REGOLO_BASE_URL = "https://api.regolo.ai/v1"
API_KEY = os.getenv("REGOLO_API_KEY", "YOUR_REGOLO_KEY")
def duckduckgo_search(query: str, max_results: int = 10, region: str = "wt-wt") -> str:
"""
Search the web using DuckDuckGo and return formatted results.
Args:
query: Search query string
max_results: Maximum number of results (default: 10)
region: Region code for localized results (default: "wt-wt" for worldwide)
Returns:
Formatted markdown string with search results
"""
try:
logger.info(f"DUCKDUCKGO: Searching for '{query}' (max_results={max_results})")
with DDGS() as ddgs:
# ddgs package uses 'query' parameter
results = list(ddgs.text(query=query, max_results=max_results, region=region))
if not results:
return "No search results found."
# Format results as markdown
formatted = "## Search Results\n\n"
for result in results:
title = result.get("title", "No title")
url = result.get("href", "")
body = result.get("body", "")
formatted += f"[{title}]({url})\n"
formatted += f"{body}\n\n"
logger.info(f"DUCKDUCKGO: Found {len(results)} results")
return formatted.strip()
except Exception as e:
logger.error(f"DUCKDUCKGO: Error: {e}")
return f"Error performing search: {str(e)}"
tools = [
{
"type": "function",
"function": {
"name": "duckduckgo_search",
"description": (
"Search the web for current information using DuckDuckGo. "
"Use this when you need up-to-date information, news, facts, "
"or any information that may change over time."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query string"
},
"max_results": {
"type": "integer",
"description": "Maximum number of results to return (1-20)",
"minimum": 1,
"maximum": 20,
"default": 10
},
"region": {
"type": "string",
"description": "Region code for localized results (e.g., 'wt-wt' for worldwide, 'us-en' for US English)",
"default": "wt-wt"
}
},
"required": ["query"]
}
}
}
]
# Create messages list
messages = [
{"role": "user", "content": "Find me some news about AI"}
]
# Initial request with tools
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "auto"
}
response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=payload
)
message = response.json()["choices"][0]["message"]
# Handle function call
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
if tool_call["function"]["name"] == "duckduckgo_search":
args = json.loads(tool_call["function"]["arguments"])
search_results = duckduckgo_search(
query=args.get("query"),
max_results=args.get("max_results", 10),
region=args.get("region", "wt-wt")
)
# Add assistant message with tool_calls and tool response
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": tool_call["function"]["name"],
"content": search_results
})
# Make follow-up call with updated messages
followup_payload = {
"model": "gpt-oss-120b",
"messages": messages
}
followup_response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=followup_payload
)
followup_response.raise_for_status()
final_answer = followup_response.json()["choices"][0]["message"]["content"]
print(final_answer)
else:
print(message.get("content", ""))
# Create messages list
messages = [
{"role": "user", "content": "Find me some news about AI"}
]
# Initial request with tools
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "auto"
}
response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=payload
)
# Show full JSON response
print(response.json())
message = response.json()["choices"][0]["message"]
# Handle function call
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
if tool_call["function"]["name"] == "duckduckgo_search":
args = json.loads(tool_call["function"]["arguments"])
search_results = duckduckgo_search(
query=args.get("query"),
max_results=args.get("max_results", 10),
region=args.get("region", "wt-wt")
)
# Add assistant message with tool_calls and tool response
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": tool_call["function"]["name"],
"content": search_results
})
# Make follow-up call with updated messages
followup_payload = {
"model": "gpt-oss-120b",
"messages": messages
}
followup_response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=followup_payload
)
followup_response.raise_for_status()
# Show full JSON response
print(followup_response.json())
else:
print(message.get("content", ""))
# Create messages list
messages = [
{"role": "user", "content": "Find me some news about AI"}
]
# Initial request with tools
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "auto"
}
response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=payload
)
message = response.json()["choices"][0]["message"]
# Handle function call
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
if tool_call["function"]["name"] == "duckduckgo_search":
args = json.loads(tool_call["function"]["arguments"])
search_results = duckduckgo_search(
query=args.get("query"),
max_results=args.get("max_results", 10),
region=args.get("region", "wt-wt")
)
# Add assistant message with tool_calls and tool response
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": tool_call["function"]["name"],
"content": search_results
})
# Make follow-up call with streaming enabled
followup_payload = {
"model": "gpt-oss-120b",
"messages": messages,
"stream": True
}
followup_response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=followup_payload,
stream=True
)
# Extract only content from streamed JSON
for line in followup_response.iter_lines():
if line:
decoded_line = line.decode('utf-8')
if decoded_line.startswith('data: '):
json_str = decoded_line[6:]
if json_str.strip() == '[DONE]':
break
try:
json_data = json.loads(json_str)
if 'choices' in json_data:
delta = json_data['choices'][0].get('delta', {})
content = delta.get('content', '')
if content:
print(content, end='', flush=True)
except json.JSONDecodeError:
pass
else:
print(message.get("content", ""))
# Create messages list
messages = [
{"role": "user", "content": "Find me some news about AI"}
]
# Initial request with tools
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "auto"
}
response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=payload
)
# Show full JSON response
print(response.json())
message = response.json()["choices"][0]["message"]
# Handle function call
if message.get("tool_calls"):
for tool_call in message["tool_calls"]:
if tool_call["function"]["name"] == "duckduckgo_search":
args = json.loads(tool_call["function"]["arguments"])
search_results = duckduckgo_search(
query=args.get("query"),
max_results=args.get("max_results", 10),
region=args.get("region", "wt-wt")
)
# Add assistant message with tool_calls and tool response
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": tool_call["function"]["name"],
"content": search_results
})
# Make follow-up call with streaming enabled
followup_payload = {
"model": "gpt-oss-120b",
"messages": messages,
"stream": True
}
followup_response = requests.post(
f"{REGOLO_BASE_URL}/chat/completions",
headers=headers,
json=followup_payload,
stream=True
)
# Show full JSON response
for line in followup_response.iter_lines():
if line:
print(line.decode('utf-8'))
else:
print(message.get("content", ""))
Tool Choice
By default, the model will determine when and how many tools to use. You can force specific behavior with the tool_choice parameter.
Auto (Default)
The model decides whether to call zero, one, or multiple functions:
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "auto" # Default behavior
}
Required
Force the model to call one or more functions:
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "required"
}
Forced Function
Force the model to call exactly one specific function:
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": {
"type": "function",
"function": {"name": "duckduckgo_search"}
}
}
Allowed Tools
Restrict the tool calls the model can make to a subset of the tools available. This is useful when you want to make only a subset of tools available across model requests without modifying the list of tools you pass in, maximizing savings from prompt caching.
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools, # All available tools
"tool_choice": {
"type": "allowed_tools",
"mode": "auto",
"tools": [
{"type": "function", "name": "duckduckgo_search"}
]
}
}
None
Disable function calling entirely, imitating the behavior of passing no functions:
payload = {
"model": "gpt-oss-120b",
"messages": messages,
"tools": tools,
"tool_choice": "none"
}