Documentazione

Documentazione

completamenti chat

L'endpoint /v1/chat/completions di Kunavo usa il protocollo Chat Completions di OpenAI per le famiglie Claude e GPT. Streaming, strumenti e output JSON funzionano con lo stesso SDK; la funzione di reasoning_effort dipende dalla famiglia e questa pagina spiega quale sia.

Endpoint: POST /v1/chat/completions. La forma della richiesta e della risposta corrisponde esattamente all'API Chat Completions di OpenAI, compresi lo streaming e i campi facoltativi tool_calls / reasoning_tokens.

Chiamata di 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)

Parametri

Sono accettati tutti i parametri standard di OpenAI. Alcuni hanno senso solo per determinati fornitori: il traduttore inoltra quelli supportati da ciascun fornitore upstream.

ParametroTipoNote
modelstringa (obbligatorio)Qualsiasi slug abilitato di /v1/models.
messagesarray (obbligatorio)Formato standard dei messaggi OpenAI.
temperature0..2Temperatura di campionamento. Valore predefinito: 1. Non ha effetto sui modelli elencati sotto questa tabella.
top_p0..1Campionamento per nucleo. Vale la stessa eccezione indicata per temperature.
max_tokensintLimite dell'output; se vengono inviati entrambi, prevale max_completion_tokens. Nei modelli GPT, i token di ragionamento vengono conteggiati nel limite. Se una richiesta Claude non specifica nessuno dei due, il valore è 4,096.
streamboolTrasmette i segmenti in streaming tramite SSE. Vedi sotto.
toolsarrayDefinizioni di funzioni/strumenti. Claude e GPT supportano entrambi l'uso degli strumenti.
tool_choiceauto|none|namedForza uno strumento specifico oppure lascia decidere al modello.
response_formatobjectOutput JSON. In Claude viene applicato un vincolo tramite json_schema, ma non tramite json_object: consulta Output JSON per i dettagli di ogni famiglia.
reasoning_effortstringSolo modelli GPT: viene inoltrato come reasoning.effort. Non viene inoltrato per Claude. Vedi Ragionamento.
seedintCampionamento deterministico, ove supportato.
stopstring|arraySequenze di arresto rigide.
Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7 e Claude Sonnet 5 non accettano temperature, top_p o top_k. Kunavo rimuove questi tre campi dalle richieste inviate a tali modelli, anziché inoltrare il 400 del fornitore; non hanno quindi alcun effetto su quei modelli.

Streaming

Imposta stream=True. Kunavo emette eventi inviati dal server nel formato esatto di OpenAI: ogni segmento è un chat.completion.chunk con choices[0].delta.content. Il payload finale di utilizzo arriva con 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)
Lo streaming funziona con tutti i modelli chat. Per Claude (con upstream basato sul protocollo Anthropic), trasformiamo al volo il flusso di eventi Anthropic in delta OpenAI: il tuo SDK non si accorge della differenza.

Chiamata agli strumenti / alle funzioni

La chiamata agli strumenti funziona con tutti i fornitori. Definisci gli strumenti tramite uno schema JSON; il modello restituisce tool_calls nel messaggio; esegui lo strumento e reinviane i risultati come messaggi 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)

Output JSON (response_format)

response_format richiede JSON. Le garanzie dipendono dalla famiglia di modelli, perché la richiesta di ciascuna famiglia viene tradotta in un meccanismo upstream diverso:

Famigliajson_schemajson_object
ClaudeApplicato. Inviato come output strutturato di Anthropic (output_config.format); la risposta è vincolata al tuo schema. Verificato su tutti i modelli Claude offerti da Kunavo, 2026-09-24.Non applicato. Anthropic non dispone di una modalità JSON senza schema, quindi il campo viene accettato e ignorato: la risposta può iniziare con prosa o con un blocco di codice.
GPTInoltrato come text.format della Responses API, ma l'upstream attuale non lo applica a tutte le richieste. Convalida il risultato prima di analizzarlo.Inoltrato come 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

Quattro aspetti da conoscere su Claude:

  • Lo schema viene inviato ad Anthropic quasi esattamente come l'hai scritto. Anthropic richiede additionalProperties: false per gli oggetti, quindi Kunavo lo aggiunge agli oggetti con proprietà elencate e lo lascia non impostato se lo produce Pydantic tramite model_json_schema(). Anthropic continua a rifiutare questi casi con un errore 400 che indica il problema: un oggetto lasciato aperto (additionalProperties impostato su un valore diverso da false oppure, se non sono elencate proprietà, additionalProperties non impostato) e oneOf — usa anyOf. La pagina di Anthropic sugli output strutturati elenca gli altri casi.
  • strict e name vengono ignorati: Anthropic applica tutti gli schemi che accetta, in modalità strict o meno.
  • Uno schema non può essere combinato con un messaggio assistant finale (un prefill); Anthropic risponde con un errore 400.
  • Se max_tokens si esaurisce nel mezzo di un oggetto, il JSON viene troncato e finish_reason è "length".

Input visivo / multimodale

I modelli con la funzionalità vision accettano blocchi di contenuto immagine. Usa un URL HTTPS oppure un URI 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)

Tra i modelli con funzionalità visiva nel catalogo figurano Claude Opus 5.5: consulta l'elenco dei modelli per gli altri.

Per un'immagine https, Kunavo scarica il file direttamente e lo invia al modello in linea, perché alcuni fornitori upstream ignorano un semplice URL dell'immagine. Limiti: 16 immagini per richiesta, 5 MB per immagine, 20 MB in totale, 10 secondi per download. Se non è possibile recuperare un'immagine, viene restituito un errore che la identifica, invece di ignorarla.

Ragionamento

La funzione di un controllo del ragionamento su questo endpoint dipende dalla famiglia, perché la richiesta di ciascuna famiglia viene tradotta in un'API upstream diversa:

Famigliareasoning_effortToken di ragionamento nell'utilizzo
GPTInoltrato come reasoning.effort della Responses API, mantenendone invariato il valore, su GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra e GPT-5.5. I valori accettati sono quelli previsti dal modello; su GPT-6 Sol e GPT-6 Luna, none disattiva il ragionamento e max è il livello massimo.Conteggiati in completion_tokens e indicati separatamente come completion_tokens_details.reasoning_tokens, con addebito alla tariffa di output.
ClaudeNon inoltrato. La richiesta Anthropic viene costruita da un elenco fisso di campi — model, max_tokens, messages, stream, temperature, top_p, top_k, stop, tools, tool_choice e response_format — quindi reasoning_effort, thinking e ogni altro campo al di fuori dell'elenco vengono eliminati qui.Nessuna suddivisione. Anthropic conteggia ogni contenuto di ragionamento all'interno del proprio output_tokens, indicato da 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)
Il thinking esteso su Claude passa attraverso la Messages API nativa, che inoltra thinking ad Anthropic senza traduzioni.

caching dei prompt

Un prompt di sistema lungo, un documento di riferimento, un blocco few-shot: qualsiasi prefisso stabile può essere memorizzato nella cache a monte e riutilizzato nelle chiamate successive a una frazione del costo di input. Le cache hit compaiono nell’oggetto usage come prompt_tokens_details.cached_tokens.

I modelli GPT memorizzano automaticamente nella cache: non serve modificare la richiesta. cached_tokens è un sottoinsieme di prompt_tokens e viene fatturato a una tariffa ridotta per la lettura dalla cache.

Claude memorizza nella cache solo fino a un breakpoint cache_control e, su un prompt lungo, questo endpoint imposta i breakpoint al posto tuo: sul prompt di sistema, sulle definizioni degli strumenti e sull’ultimo messaggio non appena la conversazione contiene un turno dell’assistente. Per scegliere tu il prefisso, applica cache_control a un blocco di contenuto, come qui sotto; Kunavo mantiene i tuoi breakpoint e aggiunge i propri solo nelle posizioni che hai lasciato libere:

# 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)
Per avere il pieno controllo della memorizzazione nella cache dei prompt di Claude — memorizzazione del prompt di sistema e delle definizioni degli strumenti, breakpoint multipli e campi usage nativi cache_creation_input_tokens / cache_read_input_tokens — usa la Messages API nativa. Anche questo endpoint memorizza nella cache il prompt di sistema e le definizioni degli strumenti, accetta più breakpoint in una sola richiesta e riporta le scritture nella cache come cache_creation_input_tokens e le letture dalla cache come prompt_tokens_details.cached_tokens; ciò che fa solo l’endpoint nativo è inoltrare il corpo della richiesta senza aggiungere alcun breakpoint.

Oggetto usage

Ogni risposta (e il chunk finale dello streaming) include un oggetto usage:

CampoSignificato
prompt_tokensToken di input fatturati, inclusi quelli memorizzati nella cache.
prompt_tokens_details.cached_tokensInput memorizzato nella cache: un sottoinsieme di prompt_tokens, fatturato a una tariffa ridotta per la lettura dalla cache.
completion_tokensToken di output fatturati. Nei modelli GPT includono i token di ragionamento; in Claude corrispondono a output_tokens di Anthropic.
completion_tokens_details.reasoning_tokensModelli GPT: la quota di completion_tokens usata per il ragionamento. Assente se pari a zero e non riportata per Claude.
total_tokensprompt_tokens + completion_tokens.

Compatibilità di Ollama con OpenAI — /v1/chat/completions

Ollama rende disponibili i suoi modelli locali tramite un’API compatibile con OpenAI all’indirizzo http://localhost:11434/v1, implementando lo stesso contratto /chat/completions documentato in questa pagina. I due servizi sono quindi intercambiabili lato client: qualsiasi applicazione sviluppata per l’API v1 di Ollama funziona con quella di Kunavo cambiando l’URL di base e il modello.

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."}],
)

Gli elementi che di solito si rompono quando si cambia provider qui funzionano, perché il formato sul filo è lo stesso:

FunzionalitàOllama v1Kunavo v1
Percorso dell’endpoint/v1/chat/completions/v1/chat/completions
StreamingSSE, chat.completion.chunkIdentico: stessa struttura delta
Chiamata agli strumentitools / tool_callsIdentica tra Claude e GPT
Input visivoBlocchi image_url sui modelli locali multimodaliStessi blocchi; URL o URI dati base64
api_keySegnaposto, ignoratoUna chiave Kunavo valida
modelUn tag scaricato (llama3.2)Uno slug ospitato (claude-sonnet-5)
Campi usage aggiuntiviSolo conteggi dei tokenAggiunge cached_tokens e reasoning_tokens
Oltre la chatcompletions, embeddingsIn più, endpoint per immagini, video e audio
Usi entrambi? Leggi base_url, model e la chiave dalle variabili d’ambiente e scegli in base all’ambiente: modelli locali per lo sviluppo offline, modelli ospitati quando la qualità è importante, con un unico percorso di codice. La guida completa, che illustra anche cosa supporta e cosa non supporta l’interfaccia OpenAI di Ollama, è disponibile nella guida all’API di Ollama compatibile con OpenAI.

Dove si trova la documentazione ufficiale di Ollama per il suo endpoint /v1/chat/completions compatibile con OpenAI?

Ollama documenta il proprio livello di compatibilità con OpenAI nel suo repository, in docs/openai.md. Questa è la fonte autorevole per sapere quali campi OpenAI implementa il server locale. Questa pagina è il riferimento equivalente per l'endpoint /v1/chat/completions ospitato da Kunavo: lo stesso contratto Chat Completions di OpenAI, documentato sopra campo per campo, che permette di confrontare direttamente le due interfacce. In breve, la forma di richieste e risposte è la stessa: Ollama lo offre all'indirizzo http://localhost:11434/v1 per i modelli scaricati localmente, mentre Kunavo lo offre all'indirizzo https://api.kunavo.com/v1 per i modelli Claude e GPT ospitati.

L'endpoint /v1/chat/completions di Kunavo è compatibile con l'API OpenAI di Ollama?

Sì: entrambi implementano lo stesso contratto Chat Completions di OpenAI, quindi un client sviluppato per Ollama su http://localhost:11434/v1 funziona invariato con https://api.kunavo.com/v1. I campi della richiesta (messages, temperature, max_tokens, stream, tools, tool_choice, response_format, stop, seed) e quelli della risposta (choices[].message, choices[].delta negli stream, usage) corrispondono. Le differenze sono quelle prevedibili per i modelli ospitati: api_key è una chiave effettiva e non un segnaposto; model accetta uno slug ospitato come claude-sonnet-5 anziché un tag scaricato come llama3.2; usage include cached_tokens e reasoning_tokens, che i modelli locali non segnalano.

Come si migra un'app Ollama a Claude o GPT ospitati?

Modifica due righe: imposta base_url da http://localhost:11434/v1 a https://api.kunavo.com/v1 e imposta model sullo slug ospitato invece che sul tag locale. Passa una vera chiave Kunavo al posto del segnaposto ignorato da Ollama. Non serve modificare il codice per lo streaming, la chiamata agli strumenti o la visione, perché le strutture SSE e tool_calls sono identiche.

Posso usare Ollama per lo sviluppo e i modelli ospitati in produzione?

Sì, è una configurazione comune: leggi l'URL di base, il modello e la chiave dalle variabili d'ambiente e selezionali in base all'ambiente. Poiché entrambe le piattaforme usano lo stesso protocollo OpenAI, non serve mantenere un secondo percorso di codice: Llama o Qwen locali per le iterazioni offline, Claude o GPT ospitati quando la qualità è importante. Le definizioni degli strumenti e i gestori dello streaming sono condivisi senza modifiche.

Quali endpoint OpenAI aggiunge Kunavo rispetto all'interfaccia compatibile di Ollama?

L'interfaccia compatibile con OpenAI di Ollama copre /chat/completions, /completions, /embeddings e /models. Kunavo aggiunge gli endpoint multimediali usando la stessa chiave: /v1/images/generations, /v1/images/edits, /v1/video/generations, /v1/videos e /v1/audio/music, oltre a Claude sull'endpoint nativo Anthropic /v1/messages. Due precisazioni necessarie: /v1/audio/speech, /v1/audio/transcriptions e /v1/embeddings sono implementati come formati wire, ma al momento non hanno alcun modello abilitato, quindi oggi non funzioneranno; inoltre, a differenza di Ollama, Kunavo non esegue modelli in locale. GET /v1/models è sempre la fonte di riferimento per verificare quali modelli sono disponibili.

Prossimi passi