# Chat con IA

Respuestas de chat de los modelos de lenguaje de la plataforma.

URL base: `https://ai.inovacc.dev`

## Descripción general

El chat con IA da a tu aplicación respuestas conversacionales de los modelos de lenguaje que Inovacc ejecuta para tu organización. El endpoint, `POST /v1/chat/completions`, usa el formato de red de chat-completions de OpenAI, por lo que una biblioteca cliente de OpenAI existente puede llamarlo cambiando la URL base a `https://ai.inovacc.dev/v1` y agregando dos encabezados. Envías una lista de mensajes y recibes la respuesta del asistente, ya sea como un solo documento JSON o como un flujo de eventos mientras se va escribiendo.

El problema que resuelve es el acceso sin infraestructura intermedia: tu organización no tiene cuentas, claves ni contratos con proveedores de modelos. Inovacc decide qué modelo atiende cada una de tus rutas, aplica los límites y presupuestos de tu organización antes de realizar una llamada, y registra el uso de cada llamada para que puedas ver cuánto gasta cada clave y cada ruta.

Usa el chat con IA cuando la respuesta sea texto libre: un asistente, un resumen, un borrador, una extracción a JSON, una respuesta que llama a tus herramientas. Otro producto encaja mejor cuando:

- necesitas un veredicto estructurado frente a criterios (una puntuación, una elección, un sí o un no, con confianza): usa el [Motor de decisiones](/es/products/decision/);
- la respuesta debe salir de tus propios documentos, con citas: consulta una colección en la nube de [Conocimiento y búsqueda vectorial](/es/products/knowledge/);
- necesitas vectores en lugar de texto: usa los [Embeddings de IA](/es/products/ai.embeddings/).

## Conceptos

**Rutas, no modelos.** Nunca nombras un modelo. A tu organización se le asignan **rutas**: ids opacos como `r00`, configurados por Inovacc solo para tu organización. Una ruta decide qué modelo responde, su techo de salida y los campos que acepta. `GET /v1/models` lista las rutas que tu organización puede usar, cada una con el `endpoint` al que sirve; el `id` es siempre el id de la ruta, nunca el nombre de lo que la respalda. Envía el id como el campo `model`; envía `"auto"`, o deja fuera `model`, y responde la ruta predeterminada de tu organización. Una ruta sirve exactamente un endpoint: una ruta de chat invocada desde `/v1/embeddings` o `/v1/run` se rechaza igual que una ruta que no tienes permitida.

**Campos.** Los campos comunes de OpenAI siempre se aceptan: `messages`, `model`, `stream`, `stream_options`, `max_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `stop`, las penalizaciones, `logit_bias`, `logprobs`, `tools`, `tool_choice`, `response_format`, `seed`, `reasoning_effort` y algunos más. Siete campos (`n`, `service_tier`, `store`, `metadata`, `modalities`, `audio`, `web_search_options`) se aceptan solo cuando se han concedido a tu organización. Cualquier otro campo se rechaza por su nombre. Algunas rutas aceptan un conjunto más reducido (`messages`, `model`, `stream`, `stream_options`, los dos límites de salida, `temperature`, `top_p`); en ellas, `tools` y los demás campos se rechazan para esa ruta.

**Mensajes e imágenes.** `content` es una cadena, o un arreglo de partes de texto y partes `image_url`. Una imagen es una URL `data:` (PNG, JPEG, WebP o GIF, en base64) o una URL `https://`; una ruta cuyo modelo no puede obtener una URL rechaza la forma `https://`. Una solicitud lleva como máximo 20 imágenes, y cada una cuenta como 1,600 tokens de entrada estimados.

**Techo de salida.** `max_tokens` y `max_completion_tokens` solo pueden bajar el techo que fijan la ruta y tu organización; un valor mayor se recorta, nunca se rechaza. Los tokens de razonamiento que gasta un modelo están dentro de `completion_tokens`.

**Streaming.** Con `"stream": true` la respuesta es un `text/event-stream` de eventos `chat.completion.chunk` de OpenAI, cada uno una línea `data:`, que termina con `data: [DONE]`. Configura `stream_options.include_usage` para recibir un fragmento final con los conteos de tokens.

**Encabezados de uso.** Una respuesta sin streaming lleva `x-route-id`, `x-usage-input-tokens` y `x-usage-output-tokens`, y toda respuesta lleva `x-operation-id`.

## Cómo funciona

Toda llamada lleva `Authorization: Bearer <key>` y un `X-Operation-Id`. Tu organización se obtiene de la clave. El id de operación es tu clave de idempotencia: de 8 a 128 letras, dígitos, `_` o `-`. El servicio verifica, en orden: el id de operación, la clave, si ese id de operación ya se usó, el cuerpo y sus campos, la ruta, los mensajes, el tamaño de entrada estimado (una cuarta parte de los caracteres de todo el cuerpo de la solicitud, más la estimación de imágenes) y luego reserva la llamada contra las cuotas de tu organización. Solo cuando todo eso se cumple se llama a un modelo.

El `id` de la respuesta es siempre `chatcmpl-` seguido de tu id de operación, y su `model` es siempre el id de la ruta. Una respuesta cuyo presupuesto de salida completo se fue en razonamiento sigue siendo `200`, con `content` vacío y `finish_reason: "length"`; una respuesta detenida por el filtro de seguridad del modelo es `200` con `finish_reason: "content_filter"`.

Una respuesta exitosa sin streaming se almacena durante 24 horas bajo su id de operación. Si envías de nuevo el mismo id, recibes la respuesta almacenada, con `x-idempotent-replay: true`, sin una segunda llamada al modelo, un segundo cobro ni un cambio de cuota. Un error nunca se almacena, así que reintentar una llamada fallida con el mismo id la ejecuta de nuevo. Mientras una llamada sigue en ejecución, una segunda llamada con su id recibe `409 operation_in_progress`.

Un stream nunca se almacena. Si el modelo falla a mitad de camino, el stream termina con un evento de error (`upstream_error`) y `data: [DONE]`; un stream que termina sin `[DONE]` se cortó y está incompleto. Lee `delta.content` en cada fragmento, incluidos el primero y el que lleva `finish_reason`.

Lo que se mide son tokens: de entrada y de salida, según los reporta el modelo, o la estimación cuando no hay un conteo disponible. `GET /v1/usage/summary` suma el uso de tu organización por clave, ruta, día u hora, y `GET /v1/capabilities` le dice a un agente lo que tu organización puede invocar; ninguno se cobra.

## Primeros pasos

Necesitas una clave de API y al menos una ruta de chat habilitada para tu organización. Las claves se crean en la consola de Inovacc; consulta [Autenticación](/es/guides/authentication/).

1. **Lista tus rutas.** Llama a `GET /v1/models`. Cada entrada con `"endpoint": "/v1/chat/completions"` es una ruta con la que puedes chatear; anota su `id`.
2. **Envía un primer mensaje.** Llama a `POST /v1/chat/completions` con ese id como `model`, un mensaje `user` y un `X-Operation-Id` nuevo (consulta [los ejemplos](#example)). El `id` de la respuesta termina con tu id de operación y `x-usage-input-tokens` muestra lo que se contó.
3. **Reinténtalo.** Envía la misma solicitud con el mismo id de operación. La respuesta es idéntica y lleva `x-idempotent-replay: true`: nada se ejecutó dos veces.
4. **Usa streaming.** Agrega `"stream": true` y `"stream_options": {"include_usage": true}` con un id de operación nuevo. Los fragmentos llegan mientras se escribe la respuesta, y el último antes de `[DONE]` lleva `usage`.
5. **Revisa el gasto.** Llama a `GET /v1/usage/summary?group_by=route` para ver las llamadas y los tokens que acabas de generar.

## Casos de uso

**Un asistente de soporte.** Tu widget de ayuda transmite la respuesta por streaming para que la persona la vea aparecer de inmediato. Cada turno del usuario es una llamada con un id de operación nuevo; tu servidor conserva la conversación y envía los mensajes recientes cada vez. Si la conexión se cae antes de `[DONE]`, el widget muestra la respuesta como cortada y ofrece preguntar de nuevo.

**Extracción estructurada en lote.** Un trabajo nocturno lee documentos entrantes y pide un objeto JSON con `response_format`. Cada documento recibe un id de operación derivado de su propio id, de modo que cuando el trabajo se reinicia tras una caída, los documentos terminados regresan desde las respuestas almacenadas en segundos y no se cobran de nuevo.

**Un agente que llama a tus herramientas.** En una ruta que acepta `tools`, el modelo responde con `tool_calls`; tu código ejecuta la herramienta y envía el resultado de vuelta como un mensaje `tool`. Cada paso tiene su propio id de operación, así que un paso reintentado nunca ejecuta una herramienta dos veces del lado del modelo.

## Límites y precios

| Límite | Valor |
|---|---|
| Cuerpo de la solicitud | 1 MiB por defecto; tu organización puede configurarse entre 1 KiB y 20 MiB |
| Imágenes por solicitud | 20 |
| Costo de entrada estimado de una imagen | 1,600 tokens |
| Tokens de entrada por solicitud | el tope de tu organización (`400 input_too_large` por encima de él) |
| Tokens de salida por solicitud | el menor entre el techo de la ruta y el de tu organización |
| Tiempo hasta la primera respuesta del modelo | 60 segundos |
| Respuesta almacenada para una repetición | 24 horas |
| Una llamada mantenida como en curso | 5 minutos como máximo |
| Solicitudes por minuto y por día; tokens y costo por día y por mes; llamadas concurrentes; presupuestos diarios | según lo configurado para tu organización |

**Precios:** a consultar. La unidad de precio son los **tokens**.

## Errores

| Estado | Código | Qué significa y qué hacer |
|---|---|---|
| 400 | `missing_operation_id`, `invalid_operation_id` | Envía `X-Operation-Id` con 8 a 128 letras, dígitos, `_` o `-`. |
| 400 | `invalid_body` | El cuerpo no es un objeto JSON, o `user` no es una cadena. Corrige el cuerpo. |
| 400 | `invalid_messages` | `messages` está vacío o un mensaje está mal formado. |
| 400 | `unsupported_field` | Un campo de nivel superior que este endpoint no acepta; quítalo. |
| 400 | `unsupported_field_for_route` | La ruta acepta un conjunto más reducido de campos, o no puede obtener una URL de imagen; quita el campo o envía la imagen como `data:`. |
| 400 | `model_not_allowed` | El valor de `model` no es un id de ruta válido. Usa un id de `GET /v1/models`. |
| 400 | `input_too_large` | La entrada supera el tope por solicitud de tu organización, o hay más de 20 imágenes. Acórtala. |
| 401 | `missing_credentials`, `invalid_credentials` | Envía una clave válida. |
| 403 | `key_disabled`, `organization_disabled` | La clave o la organización fue deshabilitada; contacta a tu administrador. |
| 403 | `field_not_allowed` | Un campo que no se ha concedido a tu organización. |
| 403 | `route_forbidden` | La ruta no es una que tu organización pueda usar en este endpoint. |
| 409 | `operation_in_progress` | Ese id de operación sigue en ejecución; espera y reintenta. |
| 413 | `request_too_large` | El cuerpo supera tu tope de tamaño. |
| 429 | `rate_limited` | Demasiadas solicitudes; espera `Retry-After` segundos. |
| 429 | `quota_exceeded`, `concurrency_limited` | Se agotó una asignación de tokens, costo o concurrencia; sin `Retry-After`. |
| 429 | `budget_exhausted` | Se gastó un presupuesto diario; se restablece a las 00:00 UTC (`Retry-After`). |
| 502 | `upstream_error` | El modelo no respondió de forma utilizable; reintenta con el mismo id de operación. |
| 503 | `service_unavailable`, `route_unavailable` | Reintenta más tarde; `route_unavailable` requiere que Inovacc corrija tu ruta. |
| 504 | `timeout_error` | El modelo no empezó a responder en 60 segundos; reintenta. |

El envoltorio y cada tipo están en [Errores](/es/guides/errors/).

## Buenas prácticas

- **Un id de operación por solicitud lógica**, reutilizado solo para reintentar esa misma solicitud. Un id reutilizado devuelve la primera respuesta sin importar lo que diga el cuerpo nuevo.
- **Reintenta en `502`, `503`, `504` y en errores de red con el mismo id de operación**, con espera creciente. En `429 rate_limited` y `budget_exhausted`, espera los segundos de `Retry-After`.
- **Configura `max_tokens`** con lo que necesitas: reduce la reserva hecha contra tus cuotas y el costo de una respuesta descontrolada.
- **Vigila `X-Budget-Warning`**: aparece cuando tu organización supera el umbral de aviso de un presupuesto, antes de que se rechacen las llamadas.
- **Usa `stream` para personas, no para trabajos**: una respuesta con streaming no se puede repetir, una sin streaming sí.
- **Envía las imágenes como URL `data:`** cuando no sepas si el modelo de la ruta puede obtener una URL, y mantenlas dentro del límite de 20 imágenes.
- **Nunca envíes una clave secreta a un navegador**: llama al chat con IA desde tu servidor.

## Relacionados

- [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/)
- [Motor de decisiones](/es/products/decision/): veredictos estructurados con la misma clave
- [Conocimiento y búsqueda vectorial](/es/products/knowledge/): respuestas de tus documentos, con citas
- [Embeddings de IA](/es/products/ai.embeddings/)

## Endpoints

- POST /v1/chat/completions
- GET /v1/models
- POST /v1/run

## Ejemplo

```sh
curl -X POST "https://ai.inovacc.dev/v1/chat/completions" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}'
```
