Documentación
completados de chat
El endpoint /v1/chat/completions de Kunavo usa el protocolo Chat Completions de OpenAI para las familias Claude y GPT. El streaming, las herramientas y la salida JSON funcionan con el mismo SDK; el efecto de reasoning_effort depende de la familia, y esta página especifica cuál es.
Endpoint: POST /v1/chat/completions. Los formatos de solicitud y respuesta coinciden exactamente con la API Chat Completions de OpenAI, incluidos el streaming y los campos opcionales tool_calls / reasoning_tokens.
Llamada básica
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)Parámetros
Se aceptan todos los parámetros estándar de OpenAI. Algunos solo son pertinentes para ciertos proveedores: el traductor transmite los que admite cada proveedor upstream.
| Parámetro | Tipo | Notas |
|---|---|---|
model | cadena (obligatorio) | Cualquier identificador habilitado de /v1/models. |
messages | matriz (obligatorio) | Formato estándar de mensajes de OpenAI. |
temperature | 0..2 | Temperatura de muestreo. Valor predeterminado: 1. No tiene efecto en los modelos enumerados debajo de esta tabla. |
top_p | 0..1 | Muestreo de núcleo. Se aplica la misma excepción que para temperature. |
max_tokens | int | Límite de salida; max_ tiene prioridad si se envían ambos. En los modelos GPT, los tokens de razonamiento cuentan para este límite. Si una solicitud a Claude no incluye ninguno de los dos, el valor es 4,096. |
stream | bool | Transmite fragmentos como SSE. Consulta más abajo. |
tools | array | Definiciones de funciones/ |
tool_choice | auto| | Fuerza el uso de una herramienta específica o permite que el modelo decida. |
response_ | object | Salida JSON. En Claude se exige un json_schema, pero no un json_object; consulta Salida JSON para ver los detalles de cada familia. |
reasoning_ | string | Solo para modelos GPT: se reenvía como reasoning.. No se reenvía para Claude. Consulta Razonamiento. |
seed | int | Muestreo determinista cuando se admite. |
stop | string|array | Secuencias de parada estrictas. |
temperature, top_p ni top_k. Kunavo elimina esos tres campos de las solicitudes a esos modelos en lugar de devolverte el error 400 del proveedor; por tanto, no tienen efecto en ellos.Streaming
Establece stream=True. Kunavo emite eventos enviados por el servidor en el formato exacto de OpenAI: cada fragmento es un chat.completion.chunk con choices[0].delta.content. La carga útil final de uso llega 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)Llamadas a herramientas y funciones
Las llamadas a herramientas funcionan con todos los proveedores. Define las herramientas como esquemas JSON; el modelo devuelve tool_calls en su mensaje; las ejecutas y devuelves los resultados en mensajes 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)Salida JSON (response_format)
response_format solicita una respuesta en JSON. Lo que garantiza depende de la familia del modelo, porque la solicitud de cada familia se traduce a un mecanismo distinto del proveedor upstream:
| Familia | json_schema | json_object |
|---|---|---|
| Claude | Obligatorio. Se envía como salida estructurada de Anthropic (output_); la respuesta se ajusta a tu esquema. Verificado en todos los modelos Claude que ofrece Kunavo, 2026-09-24. | No obligatorio. Anthropic no tiene un modo JSON sin esquema, así que el campo se acepta y se ignora; la respuesta puede empezar con prosa o un bloque de código. |
| GPT | Se reenvía como text.format de la API Responses, pero el proveedor upstream actual no lo aplica en todas las solicitudes. Valídalo antes de analizar la respuesta. | Se reenvía como 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 schemaCuatro aspectos que debes conocer sobre Claude:
- El esquema se envía a Anthropic casi tal como lo escribiste. Anthropic exige
additionalProperties: falseen los objetos, por lo que Kunavo lo añade a cualquier objeto que enumere sus propiedades y lo deja sin definir, como hace Pydantic conmodel_json_schema(). Anthropic sigue rechazando lo siguiente con un error 400 que identifica el problema: un objeto abierto (additionalPropertiesdefinido con cualquier valor distinto defalse, o sin propiedades enumeradas y conadditionalPropertiessin definir) yoneOf; usaanyOf. La página de salida estructurada de Anthropic enumera los demás casos. - Se ignoran
strictyname: Anthropic aplica todos los esquemas que acepta, tanto si son estrictos como si no. - No se puede combinar un esquema con un mensaje final
assistant(un prefill); Anthropic responde con un error 400. - Si
max_tokensse agota a mitad de un objeto, el JSON queda truncado yfinish_reasones"length".
Entrada visual y multimodal
Los modelos con la capacidad vision aceptan bloques de contenido de imagen. Usa una URL HTTPS o una 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)Los modelos con capacidad de visión del catálogo incluyen Claude Opus 5.5; consulta la lista de modelos para ver los demás.
Para una imagen HTTPS, Kunavo descarga el archivo y se lo entrega al modelo en línea, porque algunos proveedores upstream ignoran una URL de imagen sin procesar. Límites: 16 imágenes por solicitud, 5 MB por imagen, 20 MB en total y 10 segundos por descarga. Si no se puede obtener una imagen, se devuelve un error que la identifica en lugar de omitirla.
Razonamiento
El efecto de un control de razonamiento en este endpoint depende de la familia, porque la solicitud de cada familia se traduce a una API distinta del proveedor upstream:
| Familia | reasoning_ | Tokens de razonamiento en el uso |
|---|---|---|
| GPT | Se reenvía a GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra y GPT-5.5 como reasoning. de la API Responses, sin alterar el valor. Los valores aceptados son los propios del modelo: en GPT-6 Sol y GPT-6 Luna, none desactiva el razonamiento y max es el nivel máximo. | Contabilizados dentro de completion_ y desglosados como completion_; se facturan a la tarifa de salida. |
| Claude | No se reenvía. La solicitud de Anthropic se construye a partir de una lista fija de campos: model, max_tokens, messages, stream, temperature, top_p, top_k, stop, tools, tool_choice y response_. Por tanto, reasoning_, thinking y cualquier otro campo ajeno a esa lista se descartan aquí. | Sin desglose. Anthropic cuenta todo el razonamiento dentro de su output_, que es lo que informa completion_. |
# 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)thinking a Anthropic sin traducir.caché de prompts
Un prompt de sistema extenso, un documento de referencia o un bloque de ejemplos: cualquier prefijo estable puede almacenarse en caché en el proveedor y reutilizarse en llamadas posteriores por una fracción del precio de entrada. Los aciertos de caché aparecen en el objeto de uso como prompt_tokens_details.cached_tokens.
Los modelos GPT almacenan en caché automáticamente; no hace falta modificar la solicitud. cached_tokens es un subconjunto de prompt_tokens y se factura a una tarifa reducida de lectura de caché.
Claude solo almacena en caché hasta un punto de interrupción cache_control, y en un prompt largo este endpoint establece los puntos de interrupción por ti: en el prompt de sistema, en las definiciones de herramientas y en el último mensaje en cuanto la conversación contiene un turno del asistente. Para elegir tú el prefijo, adjunta cache_control a un bloque de contenido, como se muestra abajo; Kunavo conserva tus puntos de interrupción y solo añade los suyos en las posiciones que dejaste vacías:
# 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)cache_creation_input_tokens / cache_read_input_tokens— usa la API nativa Messages. Este endpoint también almacena en caché el prompt de sistema y las definiciones de herramientas, admite varios puntos de interrupción en una misma solicitud e informa las escrituras en caché como cache_creation_input_tokens y las lecturas de caché como prompt_tokens_details.cached_tokens; lo que solo el endpoint nativo hace es transmitir el cuerpo de tu solicitud sin añadir ningún punto de interrupción.Objeto de uso
Cada respuesta (y el fragmento final de la transmisión) incluye un objeto usage:
| Campo | Significado |
|---|---|
prompt_ | Tokens de entrada facturados, incluidos los tokens almacenados en caché. |
prompt_ | Entrada almacenada en caché: un subconjunto de prompt_ |
completion_ | Tokens de salida facturados. En los modelos GPT esto incluye los tokens de razonamiento; en Claude es output_ |
completion_ | Modelos GPT: la parte de completion_ |
total_tokens | prompt_ |
Compatibilidad de Ollama con OpenAI — /v1/chat/completions
Ollama ofrece sus modelos locales mediante una API compatible con OpenAI en http://localhost:11434/v1, que implementa el mismo contrato /chat/completions documentado en esta página. Así, ambas opciones son intercambiables desde el cliente: cualquier código escrito para la API v1 de Ollama funciona con la de Kunavo con solo cambiar la URL base y el modelo.
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."}],
)Los elementos que suelen fallar al cambiar de proveedor aquí no fallan, porque el formato en la red es el mismo:
| Función | Ollama v1 | Kunavo v1 |
|---|---|---|
| Ruta del endpoint | /v1/ | /v1/ |
| Streaming | SSE, chat. | Idénticos: misma estructura de delta |
| Llamadas a herramientas | tools / tool_calls | Idénticas en Claude y GPT |
| Entrada visual | Bloques image_url en modelos locales multimodales | Los mismos bloques; URL o URI de datos en base64 |
| api_key | Marcador de posición, ignorado | Una clave real de Kunavo |
| model | Una etiqueta descargada (llama3.2) | Un identificador alojado (claude-sonnet-5) |
| extras de uso | Solo recuentos de tokens | Añade cached_ |
| Más allá del chat | completions, embeddings | Además, endpoints de imágenes, vídeo y audio |
base_url, model y la clave desde variables de entorno, y cambia según el entorno: modelos locales para iteraciones sin conexión y modelos alojados cuando importa la calidad, con una sola ruta de código. La guía completa, que incluye lo que la interfaz OpenAI de Ollama implementa y lo que no, está en la guía de la API de Ollama compatible con OpenAI.¿Dónde está la documentación oficial del endpoint /v1/chat/completions compatible con OpenAI de Ollama?
Ollama documenta su capa de compatibilidad con OpenAI en su propio repositorio, en docs/openai.md, que es la referencia oficial de los campos de OpenAI que implementa el servidor local. Esta página es la referencia equivalente para el endpoint alojado /v1/chat/completions de Kunavo: el mismo contrato de Chat Completions de OpenAI, documentado campo por campo más arriba, para que puedas comparar directamente ambas interfaces. En resumen, los formatos de solicitud y respuesta son iguales: Ollama lo ofrece en http://localhost:11434/v1 para modelos descargados localmente, y Kunavo en https://api.kunavo.com/v1 para modelos Claude y GPT alojados.
¿Es /v1/chat/completions de Kunavo compatible con la API de OpenAI de Ollama?
Sí: ambos implementan el mismo contrato de Chat Completions de OpenAI, por lo que un cliente diseñado para Ollama en http://localhost:11434/v1 funciona sin cambios en https://api.kunavo.com/v1. Los campos de solicitud (messages, temperature, max_tokens, stream, tools, tool_choice, response_format, stop, seed) y los campos de respuesta (choices[].message, choices[].delta en streaming, usage) coinciden. Las diferencias son las esperables en modelos alojados: api_key es una clave real, no un marcador de posición; model recibe un identificador alojado, como claude-sonnet-5, en lugar de una etiqueta descargada, como llama3.2; y usage incluye cached_tokens y reasoning_tokens, que los modelos locales no informan.
¿Cómo migro una aplicación de Ollama a Claude o GPT alojado?
Cambia dos líneas: establece base_url de http://localhost:11434/v1 a https://api.kunavo.com/v1 y establece model con el identificador alojado en lugar de la etiqueta local. Usa una clave real de Kunavo en lugar del marcador de posición que Ollama ignora. No hace falta cambiar el código de streaming, llamadas a herramientas ni visión, porque los formatos SSE y tool_calls son idénticos.
¿Puedo seguir usando Ollama en desarrollo y modelos alojados en producción?
Sí, es una configuración habitual: lee la URL base, el modelo y la clave de las variables de entorno y cámbialos según el entorno. Como ambos extremos usan el mismo protocolo OpenAI, no hay que mantener una segunda ruta de código: Llama o Qwen locales para iterar sin conexión, y Claude o GPT alojados cuando importa la calidad. Las definiciones de herramientas y los controladores de streaming se comparten literalmente.
¿Qué endpoints de OpenAI añade Kunavo a la interfaz compatible de Ollama?
La interfaz de Ollama compatible con OpenAI cubre /chat/completions, /completions, /embeddings y /models. Kunavo añade endpoints multimedia con la misma clave: /v1/images/generations, /v1/images/edits, /v1/video/generations, /v1/videos y /v1/audio/music, además de Claude en el endpoint nativo de Anthropic /v1/messages. Hay dos salvedades claras en sentido contrario: /v1/audio/speech, /v1/audio/transcriptions y /v1/embeddings están implementados como formatos de transmisión, pero actualmente no tienen ningún modelo habilitado, por lo que no funcionarán por ahora; y, a diferencia de Ollama, Kunavo no ejecuta modelos localmente. GET /v1/models es siempre la referencia definitiva de lo que se puede invocar.