Appel de fonction
Décrivez vos fonctions dans la requête et le modèle répond avec les appels qu'il veut faire. Votre code les exécute et renvoie les résultats : l'API renvoie des appels d'outil et n'exécute aucun outil elle-même.
POST https://api.shannon-ai.com/v1/chat/completions
Les exemples de cette page utilisent /v1/chat/completions. La même boucle fonctionne sur /v1/responses et /v1/messages ; là où un format écrit une chose différemment, les sections ci-dessous montrent les trois.
import json
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}]
def get_weather(city, unit="celsius"):
# Your code: ask a weather service here.
return {"city": city, "temperature": 18, "unit": unit, "conditions": "cloudy"}
messages = [{"role": "user", "content": "What is the weather in Paris right now?"}]
answer = None
for _ in range(5): # the API sets no limit on rounds: set your own
response = client.chat.completions.create(
model="Kimi-K3-3BIT-REAP",
messages=messages,
tools=tools,
)
choice = response.choices[0]
if choice.finish_reason != "tool_calls":
answer = choice.message.content
break
messages.append(choice.message) # the assistant turn with its tool_calls
for call in choice.message.tool_calls:
args = json.loads(call.function.arguments) # arguments is a JSON string
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
print(answer) import OpenAI from "openai";
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1" });
const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a city.",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name, for example Paris" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
},
},
}];
// Your code: ask a weather service here.
function getWeather({ city, unit = "celsius" }) {
return { city, temperature: 18, unit, conditions: "cloudy" };
}
const messages = [{ role: "user", content: "What is the weather in Paris right now?" }];
let answer = null;
// The API sets no limit on rounds: set your own.
for (let round = 0; round < 5; round++) {
const response = await client.chat.completions.create({
model: "Kimi-K3-3BIT-REAP",
messages,
tools,
});
const choice = response.choices[0];
if (choice.finish_reason !== "tool_calls") {
answer = choice.message.content;
break;
}
messages.push(choice.message); // the assistant turn with its tool_calls
for (const call of choice.message.tool_calls) {
const args = JSON.parse(call.function.arguments); // arguments is a JSON string
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(getWeather(args)),
});
}
}
console.log(answer); # 1. Send the conversation with the tools.
curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
}'
# 2. Run the function yourself, then send the assistant turn and the result.
# Use the id of the call you received.
curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{"role": "assistant", "content": null, "tool_calls": [{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}
}]},
{"role": "tool", "tool_call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
}' La première réponse ne répond pas à la question. Elle demande un appel :
{
"id": "chatcmpl-5b0c1d8e7f6a4b3c9d2e1f0a8b7c6d5e",
"object": "chat.completion",
"created": 1791590400,
"model": "Kimi-K3-3BIT-REAP",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"reasoning_content": "The user asks for the current weather in Paris. I should call get_weather.",
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 312,
"completion_tokens": 58,
"total_tokens": 370,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 31}
}
} Après l'envoi du résultat, la réponse suivante est une réponse ordinaire : message.content contient le texte et finish_reason vaut stop.
La boucle, étape par étape
| Étape | Qui | Ce qui se passe |
|---|---|---|
| 1 | Vous | Envoyez messages avec tools, la liste des fonctions que le modèle peut demander. |
| 2 | Modèle | La réponse porte tool_calls et finish_reason: "tool_calls". Chaque appel nomme une fonction et donne ses arguments. |
| 3 | Vous | Analysez les arguments de chaque appel et exécutez votre fonction. |
| 4 | Vous | Ajoutez à messages le message de l'assistant et un message tool par appel, puis renvoyez la requête avec les mêmes tools. |
| 5 | Modèle | Le modèle répond par du texte avec finish_reason: "stop", ou demande d'autres appels. Dans ce cas, retournez à l'étape 3. |
Une requête est un tour de modèle. L'API ne conserve aucune conversation entre les requêtes et ne fixe aucun nombre de tours : votre code détient messages et décide quand s'arrêter.
Définir les outils
tools est un tableau avec une entrée par fonction. Chaque modèle du catalogue l'accepte ; GET /v1/models indique capabilities.tools: true pour chaque id.
| Champ | Type | Description |
|---|---|---|
type | string | Toujours function. |
function.name | string | Le nom que le modèle utilise pour demander cette fonction. Obligatoire. |
function.description | string | Ce que fait la fonction, en langage clair. |
function.parameters | object | Les arguments sous forme de JSON Schema avec type: "object" au sommet. Voir Mots-clés du schéma ci-dessous. |
Chaque point de terminaison a sa propre écriture d'un outil. Envoyez celle qui correspond au point de terminaison que vous appelez :
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
} {
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
} {
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
} tool_choice
tool_choice indique si le modèle peut, ou doit, appeler un outil. Il n'est lu qu'avec tools.
| Valeur | Description | Appliqué par |
|---|---|---|
"auto" | Le modèle décide s'il répond par du texte ou appelle un outil. C'est la valeur par défaut. | Tous les modèles |
"required" | La réponse appelle au moins un outil. | Modèles open-weight hébergés |
{"type": "function", "function": {"name": "get_weather"}} | La réponse appelle cette fonction. Le nom doit être celui d'une des fonctions de tools. Les appels à d'autres fonctions sont retirés de la réponse. | Modèles open-weight hébergés |
Les modèles Shannon (shannon-*) décident à chaque requête, comme avec auto.
Avec required ou une fonction nommée, lisez finish_reason avant de lire tool_calls. Quand le modèle ne produit aucun appel, la réponse est un texte avec finish_reason: "stop".
| Signification | /v1/chat/completions | /v1/responses | /v1/messages |
|---|---|---|---|
| Le modèle décide | "auto" | "auto" | {"type": "auto"} |
| Appeler au moins un outil | "required" | "required" | {"type": "any"} |
| Appeler une fonction nommée | {"type": "function", "function": {"name": "get_weather"}} | {"type": "function", "name": "get_weather"} | {"type": "tool", "name": "get_weather"} |
Acceptés pour la compatibilité : tool_choice: "none" et parallel_tool_calls. Pour un tour sans appel d'outil, envoyez la requête sans tools.
Appels d'outil dans la réponse
| Champ | Type | Description |
|---|---|---|
finish_reason | string | tool_calls quand la réponse porte au moins un appel. |
message.tool_calls | array | Une entrée par appel. Le champ n'est présent que si le modèle a demandé un appel. |
tool_calls[].id | string | call_ suivi de 32 caractères hexadécimaux. Renvoyez-le comme tool_call_id avec le résultat. |
tool_calls[].type | string | Toujours function. |
tool_calls[].function.name | string | La fonction à exécuter. |
tool_calls[].function.arguments | string | Les arguments sous forme de chaîne JSON, "{}" s'il n'y en a pas. Analysez-la avant de l'utiliser. |
message.content | string | null | null quand la réponse ne contient que des appels. Les modèles open-weight hébergés peuvent renvoyer du texte à côté des appels ; les modèles Shannon renvoient null. |
Le même appel dans chaque format. Sur /v1/responses, c'est un élément function_call de output, avec arguments sous forme de chaîne JSON. Sur /v1/messages, c'est un bloc de contenu tool_use dont input est un objet JSON, son id commence par toolu_, et stop_reason vaut tool_use.
{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
}
} {
"output": [
{
"id": "fc_3d2c1b0a9f8e4d7c6b5a4f3e2d1c0b9a",
"type": "function_call",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}",
"status": "completed"
}
]
} {
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"input": {"city": "Paris"}
}
]
} Plusieurs appels en un tour
Une réponse peut porter plusieurs appels. Exécutez-les tous et envoyez un résultat par appel, chacun avec son propre tool_call_id, dans la requête suivante.
Les modèles open-weight hébergés renvoient chaque appel distinct une seule fois : deux appels avec le même nom et les mêmes arguments arrivent comme un seul.
Appels d'outil en streaming
Avec stream: true, un appel arrive d'un seul bloc : le chunk contient la chaîne arguments complète, il n'y a donc aucun fragment à assembler. index compte les appels du tour à partir de 0.
{
"id": "chatcmpl-5b0c1d8e7f6a4b3c9d2e1f0a8b7c6d5e",
"object": "chat.completion.chunk",
"created": 1791590400,
"model": "Kimi-K3-3BIT-REAP",
"choices": [
{
"index": 0,
"delta": {
"tool_calls": [
{
"index": 0,
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
"finish_reason": null
}
]
} | Point de terminaison | Ce qui arrive |
|---|---|
/v1/chat/completions | Un chat.completion.chunk par appel, avec l'appel dans delta.tool_calls[0]. Le dernier chunk a finish_reason: "tool_calls" et usage. |
/v1/responses | Par appel : response.output_item.added (un élément function_call avec des arguments vides), response.function_call_arguments.delta (la chaîne entière), response.function_call_arguments.done, response.output_item.done. |
/v1/messages | Par appel : content_block_start (un bloc tool_use avec un input vide), un content_block_delta dont input_json_delta.partial_json est tout le texte des arguments, content_block_stop. message_delta porte ensuite stop_reason: "tool_use". |
Sur shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro et shannon-coder-1, une requête en streaming qui porte tools livre le texte de sa réponse dans un seul delta content à la fin du tour. Diffusion en continu
Renvoyer les résultats
La deuxième requête répète la conversation et ajoute deux choses : le tour de l'assistant qui a demandé les appels, et un résultat par appel.
{
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
} {
"input": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"type": "function_call",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
},
{
"type": "function_call_output",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"output": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
} {
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"input": {"city": "Paris"}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
}
]
} - Renvoyez le tour de l'assistant tel que vous l'avez reçu, avant les résultats. Chaque résultat est associé à son appel par id :
tool_call_idsur/v1/chat/completions,call_idsur/v1/responses,tool_use_idsur/v1/messages. - Un résultat est du texte. Sérialisez un objet en chaîne JSON pour
content(outputsur/v1/responses). Sur/v1/messages, lecontentd'un bloctool_resultest une chaîne ou une liste de blocs de texte. - Dans le tour de l'assistant,
function.argumentsreste une chaîne JSON, exactement comme elle est arrivée. - La conversation peut se terminer par un résultat d'outil. Aucun nouveau message utilisateur n'est nécessaire : le modèle continue à partir du résultat.
- Quand votre fonction échoue, envoyez l'échec comme texte du résultat. Le modèle peut alors le signaler ou demander un autre appel.
Mots-clés du schéma
parameters (input_schema sur /v1/messages) est un JSON Schema. La famille Shannon 3 (shannon-3, shannon-3-pro, shannon-3.1, shannon-3.1-pro) et les modèles open-weight hébergés le reçoivent tel que vous l'envoyez.
shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro et shannon-coder-1 reçoivent ce sous-ensemble, sur les trois points de terminaison :
| Traitement | Mots-clés |
|---|---|
| Lus tels qu'envoyés | Tous les mots-clés qui ne sont pas nommés dans les lignes ci-dessous, par exemple type, properties, required, items, enum, description, minimum, maximum, anyOf et allOf, à chaque niveau du schéma. |
format | Lu pour les valeurs int32, int64, float, double, byte, date, date-time et enum. |
oneOf | Lu comme anyOf. |
| Ne fait pas partie du schéma que reçoivent ces modèles | $schema, $id, $ref, $defs, $comment, definitions, additionalProperties, unevaluatedProperties, patternProperties, propertyNames, examples, default, title, const, not, if, then, else, contains, minContains, maxContains, dependentRequired, dependentSchemas, dependencies, readOnly, writeOnly, deprecated, contentEncoding, contentMediaType, contentSchema, multipleOf, uniqueItems ; exclusiveMinimum et exclusiveMaximum quand ce sont des nombres ; les autres valeurs de format. |
Comportement et limites
- L'API ne fixe ni nombre maximal d'outils ni nombre maximal de tours.
- Un corps de requête peut atteindre 32 MiB, et la conversation avec ses résultats doit tenir dans la fenêtre de contexte du modèle.
- Quand une requête porte
tools,shannon-1.6-lite,shannon-1.6-pro,shannon-2-liteetshannon-2-proutilisent unetemperaturepar défaut de 0.2. Envoyeztemperaturepour définir une autre valeur. - Chaque tour est une requête à part entière : il puise dans votre solde et compte pour la protection anti-flood comme toute autre requête. Limites et solde
Erreurs
Un appel d'outil est une réponse normale avec le statut 200. Ces erreurs concernent la requête elle-même :
| Statut | Type | Message | Quand |
|---|---|---|---|
400 | invalid_request_error | unknown model: <id> | model n'est pas un id de la liste publiée. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | tools n'est pas un tableau, ou un autre champ a un mauvais type JSON. |
413 | invalid_request_error | Failed to buffer the request body: … | Le corps de la requête dépasse 32 MiB. L'erreur porte code: "request_too_large". |
Le modèle écrit les arguments. Vérifiez-les par rapport à votre schéma avant d'exécuter la fonction, comme pour toute entrée extérieure.
Les erreurs d'authentification, de solde et de serveur sont les mêmes que pour toute requête. Gestion des erreurs