Documentation

Documentation

Complétions de chat

Le point de terminaison /v1/chat/completions de Kunavo utilise le protocole OpenAI Chat Completions pour les familles Claude et GPT. Le flux continu, les outils et la sortie JSON fonctionnent avec le même SDK ; le rôle de reasoning_effort dépend de la famille, et cette page précise lequel.

Point de terminaison : POST /v1/chat/completions. Le format des requêtes et des réponses correspond exactement à celui de l’API OpenAI Chat Completions, y compris pour le flux continu et les champs facultatifs tool_calls / reasoning_tokens.

Appel de base

resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[
        {"role": "system", "content": "You are a senior staff engineer."},
        {"role": "user", "content": "Pros and cons of postgres LISTEN/NOTIFY for a job queue?"},
    ],
    max_tokens=800,
)
print(resp.choices[0].message.content)

Paramètres

Tous les paramètres OpenAI standard sont acceptés. Certains ne sont pertinents que pour certains fournisseurs : le traducteur transmet ceux pris en charge par chaque fournisseur en amont.

ParamètreTypeRemarques
modelchaîne (obligatoire)Tout identifiant activé provenant de /v1/models.
messagestableau (obligatoire)Format standard des messages OpenAI.
temperature0..2Température d’échantillonnage. Valeur par défaut : 1. Sans effet sur les modèles répertoriés sous ce tableau.
top_p0..1Échantillonnage par noyau. Même exception que pour temperature.
max_tokensintLimite de sortie ; max_completion_tokens prévaut si les deux sont envoyés. Sur les modèles GPT, les jetons de raisonnement sont inclus dans cette limite. Une requête Claude qui ne transmet ni l’un ni l’autre reçoit une valeur de 4,096.
streamboolDiffuser les fragments en SSE. Voir ci-dessous.
toolsarrayDéfinitions de fonctions et d’outils. Claude et GPT prennent tous deux en charge l’utilisation d’outils.
tool_choiceauto|none|namedForcer un outil spécifique ou laisser le modèle décider.
response_formatobjectSortie JSON. Pour Claude, json_schema est appliqué et json_object ne l’est pas — consultez Sortie JSON pour chaque famille.
reasoning_effortstringModèles GPT uniquement — transmis sous la forme reasoning.effort. Non transmis pour Claude. Voir Raisonnement.
seedintÉchantillonnage déterministe lorsque pris en charge.
stopstring|arraySéquences d’arrêt strictes.
Avec Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7 et Claude Sonnet 5, les paramètres temperature, top_p et top_k ne sont pas acceptés. Kunavo supprime ces trois paramètres des requêtes envoyées à ces modèles au lieu de vous renvoyer l’erreur 400 du fournisseur ; ils n’ont donc aucun effet sur ces modèles.

Diffusion en continu

Définissez stream=True. Kunavo émet des événements envoyés par le serveur au format exact d’OpenAI : chaque fragment est un chat.completion.chunk avec choices[0].delta.content. La charge utile finale d’utilisation arrive avec data: [DONE].

stream = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "Explain B-trees in one paragraph."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
La diffusion en continu fonctionne pour tous les modèles de chat. Pour Claude (fournisseur en amont utilisant le protocole Anthropic), nous traduisons à la volée le flux d’événements Anthropic en deltas OpenAI — votre SDK ne voit pas la différence.

Appels d’outils / de fonctions

Les appels d’outils fonctionnent avec tous les fournisseurs. Définissez les outils sous forme de schéma JSON ; le modèle renvoie tool_calls dans son message ; vous l’exécutez, puis renvoyez les résultats sous forme de messages role: "tool".

tools = [{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Get current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "unit": {"type": "string", "enum": ["c", "f"]},
            },
            "required": ["city"],
        },
    },
}]

resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
    tools=tools,
    tool_choice="auto",
)

# Inspect tool calls the model wants to make
for call in resp.choices[0].message.tool_calls or []:
    print(call.function.name, call.function.arguments)

Sortie JSON (response_format)

response_format demande une sortie JSON. Les garanties dépendent de la famille de modèles, car la requête de chaque famille est traduite selon un mécanisme amont différent :

Famillejson_schemajson_object
ClaudeAppliqué. Transmis sous forme de sorties structurées Anthropic (output_config.format) ; la réponse est contrainte par votre schéma. Vérifié sur chaque modèle Claude proposé par Kunavo, le 2026-09-24.Non appliqué. Anthropic ne propose pas de mode JSON sans schéma ; la demande est donc acceptée puis ignorée — la réponse peut commencer par du texte ou un bloc de code.
GPTTransmis sous la forme text.format de l’API Responses, mais le fournisseur actuel ne l’applique pas à chaque requête. Validez avant d’analyser.Transmis sous la forme text.format.
import json

resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "Jane Doe <jane@example.com> wants the Pro plan."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "signup",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "email": {"type": "string"},
                    "plan": {"type": "string", "enum": ["free", "pro", "enterprise"]},
                },
                "required": ["name", "email", "plan"],
                "additionalProperties": False,
            },
        },
    },
)
signup = json.loads(resp.choices[0].message.content)  # constrained to the schema

Quatre points à connaître sur Claude :

  • Le schéma est transmis à Anthropic presque tel que vous l’avez écrit. Anthropic exige additionalProperties: false sur les objets ; Kunavo l’ajoute donc à tout objet qui répertorie ses propriétés et ne le définit pas — c’est ce que produit le model_json_schema() de Pydantic. Anthropic rejette toujours, avec une erreur 400 précisant le problème : un objet laissé ouvert (additionalProperties défini sur une valeur autre que false, ou sans propriétés répertoriées et avec additionalProperties non défini) et oneOf — utilisez anyOf. La page d’Anthropic sur les sorties structurées répertorie les autres cas.
  • strict et name sont ignorés : Anthropic applique chaque schéma qu’il accepte, avec ou sans mode strict.
  • Un schéma ne peut pas être combiné avec un dernier message assistant (un préremplissage) ; Anthropic renvoie une erreur 400.
  • Lorsque max_tokens est épuisé au milieu d’un objet, le JSON est tronqué et finish_reason vaut "length".

Entrée visuelle / multimodale

Les modèles dotés de la capacité vision acceptent des blocs de contenu image. Utilisez une URL HTTPS ou un URI de données base64 data:.

# Pass an image URL or a base64 data URI as part of a multimodal message
resp = client.chat.completions.create(
    model="claude-haiku-4-5",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What's in this image?"},
            {"type": "image_url", "image_url": {
                "url": "https://example.com/cat.jpg"
            }},
        ],
    }],
)
print(resp.choices[0].message.content)

Les modèles compatibles avec la vision du catalogue incluent Claude Opus 5.5 — consultez la liste des modèles pour voir les autres.

Pour une image HTTPS, Kunavo télécharge lui-même le fichier et le transmet au modèle en ligne, car certains fournisseurs en amont ignorent une simple URL d’image. Limites : 16 images par requête, 5 MB par image, 20 MB au total, 10 secondes par téléchargement. Si une image ne peut pas être récupérée, une erreur la nommant est renvoyée au lieu de l’ignorer.

Raisonnement

Le rôle d’un paramètre de raisonnement sur ce point de terminaison dépend de la famille, car la requête de chacune est traduite vers une API en amont différente :

Famillereasoning_effortJetons de raisonnement dans l’utilisation
GPTTransmis sous la forme reasoning.effort de l’API Responses, valeur pour valeur, sur GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra et GPT-5.5. Les valeurs acceptées sont celles du modèle ; sur GPT-6 Sol et GPT-6 Luna, none désactive le raisonnement et max correspond au niveau maximal.Comptabilisés dans completion_tokens et indiqués séparément sous completion_tokens_details.reasoning_tokens, facturés au tarif de sortie.
ClaudeNon transmis. La requête Anthropic est construite à partir d’une liste fixe de champs — model, max_tokens, messages, stream, temperature, top_p, top_k, stop, tools, tool_choice et response_format — ; reasoning_effort, thinking et tout autre champ absent de cette liste sont donc supprimés ici.Aucun détail séparé. Anthropic comptabilise tout raisonnement dans son output_tokens, indiqué par completion_tokens.
# GPT models: reasoning_effort is forwarded as the Responses API's
# reasoning.effort. Reasoning tokens are part of completion_tokens.
resp = client.chat.completions.create(
    model="gpt-6-sol",
    messages=[{"role": "user", "content": "Plan a 3-week MLOps migration."}],
    reasoning_effort="high",
)
details = resp.usage.completion_tokens_details   # absent when no reasoning ran
print(details.reasoning_tokens if details else 0)
Le raisonnement étendu sur Claude passe par l’API Messages native, qui transmet thinking à Anthropic sans traduction.

Mise en cache des prompts

Un long prompt système, un document de référence, un bloc de quelques exemples : tout préfixe stable peut être mis en cache en amont et réutilisé lors des appels suivants à une fraction du prix d’entrée. Les accès au cache apparaissent dans l’objet d’utilisation sous prompt_tokens_details.cached_tokens.

Les modèles GPT mettent automatiquement le contenu en cache : aucune modification de la requête n’est nécessaire. cached_tokens est un sous-ensemble de prompt_tokens et est facturé à un tarif réduit de lecture du cache.

Claude ne met en cache que jusqu’à un point de rupture cache_control, et pour un prompt long, ce point de terminaison définit les points de rupture à votre place : sur le prompt système, sur les définitions d’outils et sur le dernier message dès que la conversation contient un tour de l’assistant. Pour choisir vous-même le préfixe, ajoutez cache_control à un bloc de contenu, comme ci-dessous ; Kunavo conserve vos points de rupture et n’ajoute les siens qu’aux positions que vous avez laissées libres :

# Claude caches prefixes you mark with cache_control. Attach it to a content
# block; later calls reusing that prefix read it at ~10% of the input price.
resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": LONG_DOCUMENT,            # the stable, reused prefix
                "cache_control": {"type": "ephemeral"},
            },
            {"type": "text", "text": "Summarize the document above."},
        ],
    }],
)
print(resp.usage.prompt_tokens_details.cached_tokens)
Pour maîtriser entièrement la mise en cache des prompts Claude — mise en cache du prompt système et des définitions d’outils, plusieurs points de rupture et champs d’utilisation natifs cache_creation_input_tokens / cache_read_input_tokens — utilisez la Messages API native. Ce point de terminaison met lui aussi en cache le prompt système et les définitions d’outils, accepte plusieurs points de rupture dans une même requête et indique les écritures en cache sous cache_creation_input_tokens et les lectures du cache sous prompt_tokens_details.cached_tokens ; ce que seul le point de terminaison natif fait, c’est transmettre votre corps de requête sans y ajouter de point de rupture.

Objet d’utilisation

Chaque réponse (ainsi que le dernier fragment du flux) inclut un objet usage :

ChampSignification
prompt_tokensJetons d’entrée facturés, jetons mis en cache inclus.
prompt_tokens_details.cached_tokensEntrée mise en cache, sous-ensemble de prompt_tokens, facturée à un tarif réduit de lecture du cache.
completion_tokensTokens de sortie facturés. Pour les modèles GPT, cela inclut les tokens de raisonnement ; pour Claude, il s’agit de output_tokens d’Anthropic.
completion_tokens_details.reasoning_tokensModèles GPT : part de raisonnement de completion_tokens. Absent si la valeur est zéro, et non signalé pour Claude.
total_tokensprompt_tokens + completion_tokens.

Compatibilité d’Ollama avec OpenAI — /v1/chat/completions

Ollama expose ses modèles locaux via une API compatible avec OpenAI à l’adresse http://localhost:11434/v1, en implémentant le même contrat /chat/completions décrit sur cette page. Les deux sont donc interchangeables côté client : tout ce qui fonctionne avec l’API v1 d’Ollama fonctionne avec celle de Kunavo en changeant l’URL de base et le modèle.

from openai import OpenAI

# Before — Ollama's OpenAI-compatible API, local models
client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama",                       # ignored locally
)
resp = client.chat.completions.create(
    model="llama3.2",
    messages=[{"role": "user", "content": "Summarize this changelog."}],
)

# After — the same call against hosted frontier models.
# Two lines changed. Everything below them is untouched.
client = OpenAI(
    base_url="https://api.kunavo.com/v1",   # <- was localhost:11434/v1
    api_key=os.environ["KUNAVO_API_KEY"],
)
resp = client.chat.completions.create(
    model="claude-sonnet-5",  # <- was llama3.2
    messages=[{"role": "user", "content": "Summarize this changelog."}],
)

Les éléments qui posent généralement problème lors du passage d’un fournisseur à un autre ne posent pas problème ici, car le format sur le réseau est identique :

FonctionnalitéOllama v1Kunavo v1
Chemin du point de terminaison/v1/chat/completions/v1/chat/completions
Diffusion en continuSSE, chat.completion.chunkIdentique — même structure de delta
Appels d’outilstools / tool_callsIdentique pour Claude et GPT
Entrée visuelleBlocs image_url sur les modèles locaux multimodauxMêmes blocs ; URL ou URI de données base64
api_keyEspace réservé, ignoréUne véritable clé Kunavo
modelUne étiquette téléchargée (llama3.2)Un identifiant hébergé (claude-sonnet-5)
données supplémentaires d’utilisationDécomptes de jetons uniquementAjoute cached_tokens et reasoning_tokens
Au-delà du chatcompletions, embeddingsAvec, en plus, des points de terminaison pour les images, la vidéo et l’audio
Vous utilisez les deux ? Lisez base_url, model et la clé depuis des variables d’environnement, puis choisissez selon l’environnement : modèles locaux pour les itérations hors ligne, modèles hébergés lorsque la qualité compte, avec un seul chemin de code. Le guide détaillé, qui précise ce que l’interface OpenAI d’Ollama implémente ou non, se trouve dans le guide de l’API d’Ollama compatible avec OpenAI.

Où trouver la documentation officielle d’Ollama sur son point de terminaison /v1/chat/completions compatible OpenAI ?

Ollama documente sa couche de compatibilité OpenAI dans son propre dépôt, sous docs/openai.md ; cette documentation fait autorité quant aux champs OpenAI pris en charge par le serveur local. Cette page est l’équivalent pour le point de terminaison /v1/chat/completions hébergé de Kunavo : le même contrat OpenAI Chat Completions, documenté champ par champ ci-dessus, qui vous permet de comparer directement les deux interfaces. En bref, les formats de requête et de réponse sont identiques : Ollama le propose à l’adresse http://localhost:11434/v1 avec des modèles téléchargés localement, Kunavo à l’adresse https://api.kunavo.com/v1 avec des modèles Claude et GPT hébergés.

Le point de terminaison /v1/chat/completions de Kunavo est-il compatible avec l’API OpenAI d’Ollama ?

Oui — les deux implémentent le même contrat OpenAI Chat Completions ; un client conçu pour Ollama à l’adresse http://localhost:11434/v1 fonctionne donc sans modification à l’adresse https://api.kunavo.com/v1. Les champs de requête (messages, temperature, max_tokens, stream, tools, tool_choice, response_format, stop, seed) et de réponse (choices[].message, choices[].delta dans les flux, usage) correspondent. Les différences sont celles que l’on peut attendre de modèles hébergés : api_key est une véritable clé, et non un espace réservé ; model prend un identifiant hébergé comme claude-sonnet-5, et non une balise téléchargée comme llama3.2 ; enfin, usage contient cached_tokens et reasoning_tokens, que les modèles locaux ne signalent pas.

Comment migrer une application Ollama vers Claude ou GPT hébergés ?

Modifiez deux lignes : définissez base_url sur https://api.kunavo.com/v1 au lieu de http://localhost:11434/v1, puis définissez model sur un identifiant hébergé au lieu de la balise locale. Fournissez une véritable clé Kunavo au lieu de l’espace réservé ignoré par Ollama. Aucun changement n’est nécessaire au code de flux continu, d’appel d’outils ou de vision, car les formats SSE et tool_calls sont identiques.

Puis-je conserver Ollama pour le développement et utiliser des modèles hébergés en production ?

Oui, c’est une configuration courante : lisez l’URL de base, le modèle et la clé depuis des variables d’environnement, puis changez-les selon l’environnement. Les deux côtés utilisant le même protocole OpenAI, aucun deuxième chemin de code n’est à maintenir : Llama ou Qwen en local pour les itérations hors ligne, Claude ou GPT hébergés lorsque la qualité compte. Les définitions d’outils et les gestionnaires de flux continu sont réutilisés à l’identique.

Quels points de terminaison OpenAI Kunavo ajoute-t-il à l’interface compatible d’Ollama ?

L’interface compatible OpenAI d’Ollama couvre /chat/completions, /completions, /embeddings et /models. Kunavo ajoute les points de terminaison multimédias avec la même clé : /v1/images/generations, /v1/images/edits, /v1/video/generations, /v1/videos et /v1/audio/music, ainsi que Claude sur le point de terminaison natif Anthropic /v1/messages. Deux réserves à signaler : /v1/audio/speech, /v1/audio/transcriptions et /v1/embeddings sont implémentés en tant que formats filaires, mais aucun modèle n’est actuellement activé derrière eux ; ils ne fonctionneront donc pas pour le moment. Par ailleurs, contrairement à Ollama, Kunavo n’exécute pas de modèles localement. GET /v1/models fait toujours autorité quant aux modèles appelables.

Étapes suivantes