Tool calling (Function/Tool Calling) lets LLMs interact with external systems through defined API contracts. When a model chooses a tool, it produces structured arguments that follow the JSON Schema. The client runs the tool and returns its result to the model, enabling complex orchestration and system integration.
Key fields in a tool-calling request:
{"type":"function","function":{name, description, parameters}}."auto": Let the model decide whether to call a tool."required": Require the model to call at least one tool."none": Disable tool calling.{"type":"function","function":{"name":"xxx"}}: Force a call to the specified function.Key response fields:
choices[].message.content: The model’s natural-language response.choices[].message.tool_calls: Array of tool calls.choices[].finish_reason: Reason generation ended.id, created, usage: Request metadata and token statistics.Note: Token Market has no
/v1/anthropicendpoint. Anthropic-compatible and OpenAI-compatible requests use the unified/v1gateway; code examples use the gateway/messagespath.
{name, description, input_schema}.The unified /v1 gateway accepts the following request headers:
| Header | Usage |
|---|---|
Authorization: Bearer <API_KEY> | Generic Bearer authentication. |
x-api-key: <API_KEY> | The API Key header used by the official Anthropic SDK; it can replace Authorization. |
anthropic-version: 2023-06-01 | The version header used by the official Anthropic SDK; supported for Anthropic-format requests. |
Authorization and x-api-key are alternative authentication headers; do not send both unless your client requires it. When using the official Anthropic SDK, keep its default x-api-key and anthropic-version headers.
content[]: Array of response content blocks. Supported block types include:
{"type":"text","text":"..."}: natural-language text.{"type":"tool_use","id":"...","name":"...","input":{...}}: tool call.Tool-calling capability depends on the current model and channel. Model Market shows model types, input/output modalities, context, pricing, and channel information, but it does not provide a permanent tool-calling support list or an endpoint for detailed information about a specific model. Copy the real model ID from Model Market and validate tool calling with a minimal request; do not use /v1/anthropic or an invented model-details path.
To use Token Market with coding tools, see Use Token Market with Coding Tools.
#!/usr/bin/env python3
import os
import sys
import json
from openai import OpenAI
# Define the tool
def search_books(search_terms):
query = " ".join(search_terms).lower()
catalog = [
{"id": 4300, "title": "Ulysses", "authors": [{"name": "Joyce, James"}]},
{"id": 2814, "title": "Dubliners", "authors": [{"name": "Joyce, James"}]},
{"id": 4217, "title": "A Portrait of the Artist as a Young Man", "authors": [{"name": "Joyce, James"}]},
{"id": 766, "title": "Chamber Music", "authors": [{"name": "Joyce, James"}]},
]
if "joyce" in query:
return catalog
return []
def main() -> int:
base_url = os.getenv("BASE_URL", "https://api.tokensmarket.ai/v1")
api_key = os.getenv("API_KEY", "<YOUR_TOKEN_MARKET_API_KEY>")
client = OpenAI(base_url=base_url, api_key=api_key)
tools = [
{
"type": "function",
"function": {
"name": "search_books",
"description": "Search a local book catalog by keyword (static example)",
"parameters": {
"type": "object",
"properties": {
"search_terms": {
"type": "array",
"items": {"type": "string"},
"description": "List of search terms"
}
},
"required": ["search_terms"],
"additionalProperties": False,
},
},
}
]
messages = [
{"role": "user", "content": "Which books did Joyce write? List the titles."}
]
# Send the request with tools
first = client.chat.completions.create(
model="glm-5.1",
messages=messages,
tools=tools,
tool_choice={"type": "function", "function": {"name": "search_books"}},
max_tokens=2048,
)
choice = first.choices[0]
if not choice.message.tool_calls:
print("[ERROR] model did not return tool_calls", file=sys.stderr)
return 3
# The OpenAI SDK message object can be appended directly to the conversation.
messages.append(choice.message)
# Execute the tool and append its result
for tc in (choice.message.tool_calls or []):
args = json.loads(tc.function.arguments or "{}")
res = search_books(args.get("search_terms", []))
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(res, ensure_ascii=False),
})
# Send the tool result back to the model
second = client.chat.completions.create(
model="glm-5.1",
messages=messages,
tools=tools,
max_tokens=2048,
)
final_text = (second.choices[0].message.content or "").strip()
print({"model": second.model, "final_text": final_text})
return 0
if __name__ == "__main__":
raise SystemExit(main())
Agent workflows often use stream: true. Tool calls then arrive over multiple chunks instead of as one complete parameter object. The server may split the tool-call id, name, and function.arguments; an arguments string can be split in the middle, so do not call json.loads on each chunk.
stream = client.chat.completions.create(
model="glm-5.1",
messages=messages,
tools=tools,
stream=True,
)
tool_calls_by_index = {}
for chunk in stream:
for choice in chunk.choices:
for delta in choice.delta.tool_calls or []:
call = tool_calls_by_index.setdefault(
delta.index,
{"id": "", "name": "", "arguments": ""},
)
if delta.id:
call["id"] = delta.id
if delta.function:
if delta.function.name:
call["name"] = delta.function.name
call["arguments"] += delta.function.arguments or ""
# Parse complete arguments and execute tools only after the stream ends.
for call in tool_calls_by_index.values():
args = json.loads(call["arguments"] or "{}")
result = search_books(args.get("search_terms", []))
For streaming requests, use delta.tool_calls[*].index to distinguish parallel tool calls and append fragments for the same index. Execute a tool only after its complete call has arrived, then append the reconstructed assistant tool-call message and tool result to messages before sending the next request. Handle text deltas separately from tool-argument deltas.
#!/usr/bin/env python3
import os
import sys
import json
import requests
# Define the tool
def search_books(search_terms):
query = " ".join(search_terms).lower()
catalog = [
{"id": 4300, "title": "Ulysses", "authors": [{"name": "Joyce, James"}]},
{"id": 2814, "title": "Dubliners", "authors": [{"name": "Joyce, James"}]},
{"id": 4217, "title": "A Portrait of the Artist as a Young Man", "authors": [{"name": "Joyce, James"}]},
{"id": 766, "title": "Chamber Music", "authors": [{"name": "Joyce, James"}]},
]
if "joyce" in query or "Joyce" in query:
return catalog
return []
def main() -> int:
base_url = os.getenv("BASE_URL", "https://api.tokensmarket.ai/v1")
api_key = os.getenv("API_KEY", "<YOUR_TOKEN_MARKET_API_KEY>")
url = f"{base_url}/messages"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"anthropic-version": "2023-06-01",
}
# The official Anthropic SDK uses "x-api-key": api_key instead of Authorization.
# Use one authentication header, not both.
tools = [
{
"name": "search_books",
"description": "Search a local book catalog by keyword (static example)",
"input_schema": {
"type": "object",
"properties": {
"search_terms": {
"type": "array",
"items": {"type": "string"},
"description": "List of search terms"
}
},
"required": ["search_terms"],
"additionalProperties": False,
},
}
]
messages = [
{
"role": "user",
"content": [{
"type": "text",
"text": (
"Follow these requirements exactly:\n"
"1) You must call search_books with search_terms=['Joyce'];\n"
"2) After receiving the result, output only the list of book titles.\n"
"Do not answer directly without calling the tool."
)
}]
}
]
r1 = requests.post(
url,
headers=headers,
data=json.dumps({
"model": "glm-5.1",
"max_tokens": 2048,
"tools": tools,
"messages": messages,
}),
timeout=60,
)
r1.raise_for_status()
data1 = r1.json()
tool_use_block = None
for block in data1.get("content", []) or []:
if block.get("type") == "tool_use" and block.get("name") == "search_books":
tool_use_block = block
break
if not tool_use_block:
texts = []
for b in data1.get("content", []) or []:
if b.get("type") == "text" and "text" in b:
texts.append(b["text"])
final_text = "".join(texts).strip()
print({"model": data1.get("model"), "final_text": final_text})
return 0
args = tool_use_block.get("input", {})
result = search_books(args.get("search_terms", []))
# Return the previous assistant tool_use unchanged, then append the user tool_result
messages2 = [
messages[0],
{"role": "assistant", "content": [tool_use_block]},
{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_use_block["id"],
"content": json.dumps(result, ensure_ascii=False),
}],
},
]
r2 = requests.post(
url,
headers=headers,
data=json.dumps({
"model": "glm-5.1",
"max_tokens": 2048,
"tools": tools,
"messages": messages2,
}),
timeout=60,
)
r2.raise_for_status()
data2 = r2.json()
texts = []
for b in data2.get("content", []) or []:
if b.get("type") == "text" and "text" in b:
texts.append(b["text"])
final_text = "".join(texts).strip()
print({"model": data2.get("model"), "final_text": final_text})
return 0
if __name__ == "__main__":
raise SystemExit(main())