diff --git a/src/content/docs-es/choose-a-model.md b/src/content/docs-es/choose-a-model.md
index 50c664fa..d299596f 100644
--- a/src/content/docs-es/choose-a-model.md
+++ b/src/content/docs-es/choose-a-model.md
@@ -29,7 +29,8 @@ Si no sabes cuál coger, busca en la primera columna lo que quieres hacer.
| Lo mismo, pero sin el tier premium | `glm5.3-flash` | Mismo contexto de 1M y cuota generosa |
| Que conteste rápido | `qwen3.8-flash` | Menos profundidad, mucha menos espera |
| Pasarle un audio al modelo directamente | `mimo-v2.6-flash` | Oye audio de forma nativa |
-| Describir o analizar una imagen | `deepseek-v4-flash` | Cualquiera menos `glm5.3` sirve; este es el mejor |
+| Pasarle un vídeo al modelo | `glm5.3` | Es el único que ve vídeo. Necesita el tier premium |
+| Describir o analizar una imagen | `deepseek-v4-flash` | Cualquier modelo de chat sirve; este es el mejor |
| Probar cosas sin gastar cuota | `gemma4` | No tiene contador de tokens |
| Montar un buscador o un RAG | `qwen3-embedding` y después `rerank` | Primero recuperas por similitud, luego reordenas por relevancia |
| Convertir texto en audio | `kokoro` | 67 voces, dos de ellas en español |
@@ -42,7 +43,7 @@ Si no sabes cuál coger, busca en la primera columna lo que quieres hacer.
| id | Para qué | Contexto | Acepta | Cuota |
|---|---|---|---|---|
| `deepseek-v4-flash` | Chat y razonamiento general | 1M | texto · imagen | 3B tokens/mes |
-| `glm5.3` | Agentes de código y tareas largas | 1M | texto | 3B tokens/periodo de facturación |
+| `glm5.3` | Agentes de código y tareas largas | 1M | texto · imagen · audio · vídeo | 3B tokens/periodo de facturación |
| `glm5.3-flash` | Agentes de código, sin premium | 1M | texto · imagen | 2B tokens/mes |
| `qwen3.8-flash` | Respuestas rápidas | 1M | texto · imagen | 500M tokens/mes |
| `mimo-v2.6-flash` | El MiMo más nuevo, omnimodal | 1M | texto · imagen · audio | 1.0B tokens/mes |
diff --git a/src/content/docs-es/claude-code.mdx b/src/content/docs-es/claude-code.mdx
index b2a94b28..6640596a 100644
--- a/src/content/docs-es/claude-code.mdx
+++ b/src/content/docs-es/claude-code.mdx
@@ -189,8 +189,6 @@ Depende del camino, porque el modelo hace cosas distintas en cada uno.
**Con la pasarela**, el modelo lleva la sesión entera: `glm5.3-flash`, o `glm5.3` si tienes el tier premium. Los dos tienen 1M de tokens de contexto, que es lo que pide una sesión larga de agente.
-En cualquiera de los dos, evita `glm5.3` para tareas que impliquen leer capturas de pantalla: no acepta imágenes.
-
## Problemas conocidos
- **Delegando, la salida de OpenCode entra en tu conversación.** Si le pides un resumen de algo enorme, lo que devuelva ocupa contexto en Claude Code. Pídele resúmenes, no volcados.
diff --git a/src/content/docs-es/cline.mdx b/src/content/docs-es/cline.mdx
index 9cfdf2b2..cfe5f534 100644
--- a/src/content/docs-es/cline.mdx
+++ b/src/content/docs-es/cline.mdx
@@ -46,7 +46,7 @@ Cline lo pregunta porque con un proveedor genérico no tiene forma de averiguarl
| `glm5.3-flash` | 1000000 | sí | sí |
| `deepseek-v4-flash` | 1000000 | sí | sí |
| `qwen3.8-flash` | 1000000 | sí | sí |
-| `glm5.3` | 1000000 | no | sí |
+| `glm5.3` | 1000000 | sí | sí |
Si marcas imágenes en un modelo que no las acepta, Cline intentará mandarle capturas y la petición fallará.
diff --git a/src/content/docs-es/hermes.mdx b/src/content/docs-es/hermes.mdx
index 65558cab..3d3b41e0 100644
--- a/src/content/docs-es/hermes.mdx
+++ b/src/content/docs-es/hermes.mdx
@@ -90,7 +90,7 @@ hermes model
`glm5.3-flash` si vas a pedirle tareas largas o que delegue en subagentes, por su contexto de 1M de tokens. `deepseek-v4-flash` para conversación y tareas normales, que además tiene la cuota más amplia del clúster.
-Si lo vas a usar para mandarle fotos por Telegram, cualquiera menos `glm5.3`, que es el único que no acepta imágenes.
+Si lo vas a usar para mandarle fotos por Telegram, cualquiera sirve: todos los modelos de chat aceptan imágenes.
diff --git a/src/content/docs-es/models.mdx b/src/content/docs-es/models.mdx
index 2f5461a4..c0fc63f0 100644
--- a/src/content/docs-es/models.mdx
+++ b/src/content/docs-es/models.mdx
@@ -18,15 +18,15 @@ OpenAI y la misma `base URL`.
id="glm-5-3"
name="glm5.3"
tag="753B MoE"
- leftLabel="generación de texto y chat · coding agéntico"
+ leftLabel="generación de texto multimodal y chat · coding agéntico"
rightLabel="capacidades"
- description="Tier premium: solo se puede llamar con una key de la membresía premium GLM 5.3. Modelo MoE de ~753B parámetros, enfocado a coding y a tareas agénticas de largo recorrido. Contexto de 1M tokens. Entra texto y sale texto: no acepta imágenes. Tool calling y razonamiento (emite una traza de razonamiento). Cuota de 3.000M tokens por miembro, y el contador vuelve a cero cuando empieza tu periodo de facturación. Además tiene un tope de 400M tokens por ventana móvil de 4h, que es el límite con el que topa antes una sesión intensiva de agente de código."
+ description="Tier premium: solo se puede llamar con una key de la membresía premium GLM 5.3. Modelo MoE de ~753B parámetros, enfocado a coding y a tareas agénticas de largo recorrido. Contexto de 1M tokens. Multimodal: acepta imágenes, audio y vídeo además de texto. Tool calling y razonamiento (emite una traza de razonamiento). Cuota de 3.000M tokens por miembro, y el contador vuelve a cero cuando empieza tu periodo de facturación. Además tiene un tope de 400M tokens por ventana móvil de 4h, que es el límite con el que topa antes una sesión intensiva de agente de código."
specs={[
{ label: 'Tipo', value: 'MoE (~753B total)' },
- { label: 'Cuantización', value: 'FP8' },
+ { label: 'Cuantización', value: 'NVFP4' },
{ label: 'Atención', value: 'Sparse attention' },
{ label: 'Contexto', value: '1M tokens' },
- { label: 'Modalidades de entrada', value: 'texto' },
+ { label: 'Modalidades de entrada', value: 'texto · imagen · audio · vídeo' },
{ label: 'Modalidades de salida', value: 'texto' },
{ label: 'Cuota / periodo de facturación', value: '3.000M tokens / miembro' },
{ label: 'Ventana móvil de 4h', value: '400M tokens' },
@@ -34,6 +34,9 @@ OpenAI y la misma `base URL`.
items={[
'Tool calling (function calling)',
'Modo razonamiento (traza de razonamiento)',
+ 'Visión (entrada de imagen)',
+ 'Audio (entrada de audio)',
+ 'Vídeo (entrada de vídeo)',
'Coding y tareas agénticas de largo recorrido',
'Contexto de 1M tokens',
'Generación en streaming (SSE)',
diff --git a/src/content/docs-es/opencode.mdx b/src/content/docs-es/opencode.mdx
index 7df22c7a..acefbb3d 100644
--- a/src/content/docs-es/opencode.mdx
+++ b/src/content/docs-es/opencode.mdx
@@ -63,7 +63,7 @@ Escribe esto en `~/.config/opencode/opencode.json` para tenerlo en todos tus pro
"glm5.3": {
"name": "GLM 5.3 (premium)",
"limit": { "context": 1048576, "output": 32768 },
- "modalities": { "input": ["text"], "output": ["text"] }
+ "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }
}
}
}
diff --git a/src/content/docs-es/pi.mdx b/src/content/docs-es/pi.mdx
index b90f1c25..4806f31c 100644
--- a/src/content/docs-es/pi.mdx
+++ b/src/content/docs-es/pi.mdx
@@ -88,7 +88,7 @@ En `~/.pi/agent/models.json`:
"id": "glm5.3",
"name": "GLM 5.3 (premium)",
"reasoning": true,
- "input": ["text"],
+ "input": ["text", "image"],
"contextWindow": 1048576,
"maxTokens": 32768
}
@@ -136,7 +136,7 @@ Pídele algo corto. Si contesta, está saliendo por el clúster. Puedes cambiar
- **La clave va escrita en el fichero.** `models.json` vive en tu carpeta personal, así que no suele acabar en un repositorio, pero tenlo en cuenta si sincronizas tu configuración entre máquinas.
- **`maxTokens` no es el contexto.** Es el techo de cada respuesta. El razonamiento sale de ese mismo presupuesto, así que si pides respuestas largas y razonadas, súbelo.
- **Los modelos que declares son los que verás.** Pi no le pregunta al clúster qué hay disponible: muestra lo que haya en la lista.
-- **`mimo-v2.6-flash` entra sin su audio.** El esquema de Pi acepta `"text"` e `"image"` en `input` y nada más, y un tercer valor no tumba solo a ese modelo: Pi rechaza el fichero entero, con todos los demás proveedores que haya dentro. El modelo sigue oyendo audio por la API, pero no desde Pi.
+- **`mimo-v2.6-flash` entra sin su audio, y `glm5.3` sin su audio ni su vídeo.** El esquema de Pi acepta `"text"` e `"image"` en `input` y nada más, y un tercer valor no tumba solo a ese modelo: Pi rechaza el fichero entero, con todos los demás proveedores que haya dentro. Los dos los siguen aceptando por la API, pero no desde Pi.
- **`glm5.3` necesita una clave del tier premium.** Está en la lista para que quien lo tenga lo vea. Sin ese tier la respuesta es un `401`, explicado en [Elige tu modelo](/es/docs/choose-a-model).
diff --git a/src/content/docs-es/vscode.mdx b/src/content/docs-es/vscode.mdx
index ff84a133..8c379bce 100644
--- a/src/content/docs-es/vscode.mdx
+++ b/src/content/docs-es/vscode.mdx
@@ -108,7 +108,7 @@ Se abre un fichero `chatLanguageModels.json`. Déjalo así:
"name": "GLM 5.3 (premium)",
"url": "https://api.nan.builders/v1/chat/completions",
"toolCalling": true,
- "vision": false,
+ "vision": true,
"maxInputTokens": 1015808,
"maxOutputTokens": 32768
}
@@ -137,7 +137,7 @@ Abre el chat, despliega el selector de modelos y elige uno de los que acabas de
`glm5.3-flash` para el modo agente, que es donde importa el contexto largo. `deepseek-v4-flash` para preguntas de chat. `qwen3.8-flash` si prefieres que conteste rápido.
-`glm5.3` solo responde si tienes el tier premium. Va en el bloque con `"vision": false`, que no es una preferencia: es el único modelo que no acepta imágenes.
+`glm5.3` solo responde si tienes el tier premium.
diff --git a/src/content/docs/choose-a-model.md b/src/content/docs/choose-a-model.md
index ca794551..1293df4a 100644
--- a/src/content/docs/choose-a-model.md
+++ b/src/content/docs/choose-a-model.md
@@ -29,7 +29,8 @@ If you do not know which one to pick, look for what you want to do in the first
| The same, but without the premium tier | `glm5.3-flash` | Same 1M context and a generous quota |
| Get an answer fast | `qwen3.8-flash` | Less depth, much less waiting |
| Hand the model an audio file directly | `mimo-v2.6-flash` | It hears audio natively |
-| Describe or analyze an image | `deepseek-v4-flash` | Any of them except `glm5.3` will do; this is the best |
+| Hand the model a video | `glm5.3` | The only one that watches video. Needs the premium tier |
+| Describe or analyze an image | `deepseek-v4-flash` | Any chat model will do; this is the best |
| Try things without spending quota | `gemma4` | It has no token counter |
| Build a search engine or a RAG | `qwen3-embedding` and then `rerank` | First you retrieve by similarity, then you reorder by relevance |
| Turn text into audio | `kokoro` | 67 voices, two of them Spanish |
@@ -42,7 +43,7 @@ If you do not know which one to pick, look for what you want to do in the first
| id | What for | Context | Accepts | Quota |
|---|---|---|---|---|
| `deepseek-v4-flash` | General chat and reasoning | 1M | text · image | 3B tokens/month |
-| `glm5.3` | Coding agents and long tasks | 1M | text | 3B tokens/billing period |
+| `glm5.3` | Coding agents and long tasks | 1M | text · image · audio · video | 3B tokens/billing period |
| `glm5.3-flash` | Coding agents, without premium | 1M | text · image | 2B tokens/month |
| `qwen3.8-flash` | Fast answers | 1M | text · image | 500M tokens/month |
| `mimo-v2.6-flash` | The newest MiMo, omnimodal | 1M | text · image · audio | 1.0B tokens/month |
diff --git a/src/content/docs/claude-code.mdx b/src/content/docs/claude-code.mdx
index 5fc5cbbf..833ffb29 100644
--- a/src/content/docs/claude-code.mdx
+++ b/src/content/docs/claude-code.mdx
@@ -189,8 +189,6 @@ It depends on the path, because the model does different jobs in each.
**With the gateway**, the model runs the entire session: `glm5.3-flash`, or `glm5.3` if you have the premium tier. Both have 1M tokens of context, which is what a long agent session asks for.
-On either path, avoid `glm5.3` for tasks that involve reading screenshots: it does not accept images.
-
## Known issues
- **When delegating, OpenCode's output lands in your conversation.** If you ask it to summarize something enormous, whatever it returns takes up context in Claude Code. Ask it for summaries, not dumps.
diff --git a/src/content/docs/cline.mdx b/src/content/docs/cline.mdx
index b02e3fd4..31a9ace3 100644
--- a/src/content/docs/cline.mdx
+++ b/src/content/docs/cline.mdx
@@ -46,7 +46,7 @@ Cline asks because with a generic provider it has no way of finding out:
| `glm5.3-flash` | 1000000 | yes | yes |
| `deepseek-v4-flash` | 1000000 | yes | yes |
| `qwen3.8-flash` | 1000000 | yes | yes |
-| `glm5.3` | 1000000 | no | yes |
+| `glm5.3` | 1000000 | yes | yes |
If you tick images on a model that does not accept them, Cline will try to send it screenshots and the request will fail.
diff --git a/src/content/docs/hermes.mdx b/src/content/docs/hermes.mdx
index 91c9fb27..17246756 100644
--- a/src/content/docs/hermes.mdx
+++ b/src/content/docs/hermes.mdx
@@ -90,7 +90,7 @@ hermes model
`glm5.3-flash` if you are going to give it long tasks or have it delegate to subagents, for its 1M token context. `deepseek-v4-flash` for conversation and normal tasks, which also has the widest quota on the cluster.
-If you are going to send it photos over Telegram, any of them except `glm5.3`, which is the only one that does not accept images.
+If you are going to send it photos over Telegram, any of them will do: every chat model accepts images.
diff --git a/src/content/docs/models.mdx b/src/content/docs/models.mdx
index 95608ddb..282c7d20 100644
--- a/src/content/docs/models.mdx
+++ b/src/content/docs/models.mdx
@@ -18,15 +18,15 @@ with the same `base URL`.
id="glm-5-3"
name="glm5.3"
tag="753B MoE"
- leftLabel="text generation & chat · agentic coding"
+ leftLabel="multimodal text generation & chat · agentic coding"
rightLabel="capabilities"
- description="Premium tier: callable only with a key on the GLM 5.3 premium membership. ~753B parameter MoE model, focused on coding and long-horizon agentic tasks. 1M token context. Text in, text out: it does not take images. Tool calling and reasoning (emits a reasoning trace). 3,000M token quota per member, and the counter goes back to zero when your billing period starts. Also capped at 400M tokens per rolling 4h window, which is the limit a heavy coding-agent run reaches first."
+ description="Premium tier: callable only with a key on the GLM 5.3 premium membership. ~753B parameter MoE model, focused on coding and long-horizon agentic tasks. 1M token context. Multimodal: it takes images, audio and video as well as text. Tool calling and reasoning (emits a reasoning trace). 3,000M token quota per member, and the counter goes back to zero when your billing period starts. Also capped at 400M tokens per rolling 4h window, which is the limit a heavy coding-agent run reaches first."
specs={[
{ label: 'Type', value: 'MoE (~753B total)' },
- { label: 'Quantization', value: 'FP8' },
+ { label: 'Quantization', value: 'NVFP4' },
{ label: 'Attention', value: 'Sparse attention' },
{ label: 'Context', value: '1M tokens' },
- { label: 'Input modalities', value: 'text' },
+ { label: 'Input modalities', value: 'text · image · audio · video' },
{ label: 'Output modalities', value: 'text' },
{ label: 'Allowance / billing period', value: '3,000M tokens / member' },
{ label: 'Rolling 4h window', value: '400M tokens' },
@@ -34,6 +34,9 @@ with the same `base URL`.
items={[
'Tool calling (function calling)',
'Reasoning control (`reasoning_effort`: low · medium · high · max)',
+ 'Vision (image input)',
+ 'Audio (audio input)',
+ 'Video (video input)',
'Coding and long-horizon agentic tasks',
'1M token context',
'Streaming generation (SSE)',
diff --git a/src/content/docs/opencode.mdx b/src/content/docs/opencode.mdx
index db3d6276..97677ac2 100644
--- a/src/content/docs/opencode.mdx
+++ b/src/content/docs/opencode.mdx
@@ -63,7 +63,7 @@ Write this into `~/.config/opencode/opencode.json` to have it in every project,
"glm5.3": {
"name": "GLM 5.3 (premium)",
"limit": { "context": 1048576, "output": 32768 },
- "modalities": { "input": ["text"], "output": ["text"] }
+ "modalities": { "input": ["text", "image", "audio", "video"], "output": ["text"] }
}
}
}
diff --git a/src/content/docs/pi.mdx b/src/content/docs/pi.mdx
index e63c96d6..7a32da23 100644
--- a/src/content/docs/pi.mdx
+++ b/src/content/docs/pi.mdx
@@ -88,7 +88,7 @@ In `~/.pi/agent/models.json`:
"id": "glm5.3",
"name": "GLM 5.3 (premium)",
"reasoning": true,
- "input": ["text"],
+ "input": ["text", "image"],
"contextWindow": 1048576,
"maxTokens": 32768
}
@@ -136,7 +136,7 @@ Ask it for something short. If it answers, it is going out through the cluster.
- **The key is written in the file.** `models.json` lives in your home directory, so it does not usually end up in a repository, but keep it in mind if you sync your configuration between machines.
- **`maxTokens` is not the context.** It is the ceiling for each answer. Reasoning comes out of that same budget, so if you ask for long reasoned answers, raise it.
- **The models you declare are the ones you get.** Pi does not ask the cluster what is available: it shows whatever is on the list.
-- **`mimo-v2.6-flash` goes in without its audio.** Pi's schema accepts `"text"` and `"image"` for `input` and nothing else, and a third value does not fail that one model: Pi refuses the whole file, every other provider in it included. The model still hears audio through the API, just not from inside Pi.
+- **`mimo-v2.6-flash` goes in without its audio, and `glm5.3` without its audio and video.** Pi's schema accepts `"text"` and `"image"` for `input` and nothing else, and a third value does not fail that one model: Pi refuses the whole file, every other provider in it included. Both models still take them through the API, just not from inside Pi.
- **`glm5.3` needs a key on the premium tier.** It is on the list so that a member who has it can see it. Without that tier the answer is a `401`, which is explained in [Choose your model](/docs/choose-a-model).
diff --git a/src/content/docs/vscode.mdx b/src/content/docs/vscode.mdx
index 292e278e..a42e8257 100644
--- a/src/content/docs/vscode.mdx
+++ b/src/content/docs/vscode.mdx
@@ -108,7 +108,7 @@ A `chatLanguageModels.json` file opens. Leave it like this:
"name": "GLM 5.3 (premium)",
"url": "https://api.nan.builders/v1/chat/completions",
"toolCalling": true,
- "vision": false,
+ "vision": true,
"maxInputTokens": 1015808,
"maxOutputTokens": 32768
}
@@ -137,7 +137,7 @@ Open the chat, drop down the model picker and choose one of the ones you just ad
`glm5.3-flash` for agent mode, which is where long context matters. `deepseek-v4-flash` for chat questions. `qwen3.8-flash` if you prefer fast answers.
-`glm5.3` only answers if you have the premium tier. It is in the block with `"vision": false`, which is not a preference: it is the one model that does not accept images.
+`glm5.3` only answers if you have the premium tier.
diff --git a/src/data/modelos.json b/src/data/modelos.json
index 0b094816..de606535 100644
--- a/src/data/modelos.json
+++ b/src/data/modelos.json
@@ -29,7 +29,7 @@
{
"id": "glm5.3",
"by": "Z.ai",
- "specs": "753B MoE · FP8 · sparse attention · 1M context · tool calling · reasoning",
+ "specs": "753B MoE · NVFP4 · sparse attention · 1M context · multimodal · tool calling · reasoning",
"cuota": "3B tokens/periodo de facturación",
"frontier": true,
"premium": true
diff --git a/src/data/openapi.json b/src/data/openapi.json
index 4c49298b..0a75d773 100644
--- a/src/data/openapi.json
+++ b/src/data/openapi.json
@@ -3,7 +3,7 @@
"info": {
"title": "NaN API",
"version": "1.0.0",
- "description": "Open models on a shared EU inference cluster. Zero logs.\n\nThe NaN API is OpenAI-compatible: predictable, resource-oriented URLs, JSON request and response bodies, and standard HTTP verbs and status codes. Point any OpenAI SDK at our base URL and your existing code keeps working. Change the base URL and the API key, and that's it.\n\nOne schema across every model, so you only learn the API once. Change the `model` field to switch models; everything else stays the same.\n\n- Base URL: `https://api.nan.builders/v1`\n- OpenAPI spec: this document. Import it into Postman, Insomnia, or your own tooling.\n\nIf you use the [Helmcode](https://helmcode.com) enterprise service, the base URL is `https://api.helmcode.com/v1` instead. Every other endpoint is identical.\n\n## Authentication\n\nEvery request authenticates with an API key, sent as a Bearer token:\n\n```\nAuthorization: Bearer $NAN_API_KEY\n```\n\nYou must be a NaN community member. Generate your key from user settings, under \"API Keys\", on the [platform](https://cloud.nan.builders/). The key is personal and non-transferable. Keep it secret: never embed one in client-side code or commit it to source control. Requests must go over HTTPS; calls over plain HTTP fail.\n\n## Making requests\n\nThe API is OpenAI-compatible, so point an official OpenAI SDK at our base URL and change nothing else:\n\n```python\nfrom openai import OpenAI\n\nclient = OpenAI(\n api_key=\"$NAN_API_KEY\",\n base_url=\"https://api.nan.builders/v1\",\n)\n\nresp = client.chat.completions.create(\n model=\"deepseek-v4-flash\",\n messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n)\nprint(resp.choices[0].message.content)\n```\n\n## Streaming\n\nChat responses can stream token-by-token. Set `\"stream\": true` on `/chat/completions` and the response arrives as Server-Sent Events: each event is a `data:` line carrying a `chat.completion.chunk`, with the new text in `choices[0].delta.content`. A final `data: [DONE]` line ends the stream. `/chat/completions` streams incrementally on every chat model. `/responses` streams incrementally only on `deepseek-v4-flash`; on `qwen3.6` and `gemma4` it holds the whole answer and sends it in one burst at the end.\n\n## Rate limits\n\n{{RATE_LIMITS}}\n\nImage endpoints run on their own budget, separate from the model endpoints: 20 requests per minute and 100 requests per month. The usage endpoint is metered separately too: {{USAGE_RATE_LIMIT}} requests per minute per member. Exceed any limit and you get a `429`.\n\n## Errors\n\nNaN uses conventional HTTP status codes: `2xx` on success, `4xx` for a problem with the request (a missing parameter, an invalid key, an unavailable model) and `5xx` for a server-side error. Every error returns a JSON body in the OpenAI shape:\n\n```json\n{\n \"error\": {\n \"message\": \"The model 'foo' does not exist.\",\n \"type\": \"invalid_request_error\",\n \"param\": \"model\",\n \"code\": \"model_not_found\"\n }\n}\n```\n\n`message` is human-readable, `param` names the offending field when applicable, and `code` is a short machine-readable string you can branch on.\n\n| Status | Meaning | `code` |\n| --- | --- | --- |\n| `400` | Invalid or malformed parameter (`param` says which); content blocked by the safety filter; a `max_tokens` above what the model can generate (a generic \"Invalid request\" message today); or a request that does not fit the model's context window. On the models hosted on the cluster (`qwen3.6`, `gemma4`) the overflow message is \"Context length exceeded for model '...'\" with the model's limit; on the other models it is usually that text, but it can arrive as the generic \"Invalid request. Check your request parameters.\" | `\"400\"` (with `type: invalid_request_error`) · `content_policy_violation` |\n| `401` | Missing or invalid API key, or a key whose tier does not reach the requested model (`glm5.3`): \"This API key does not have access to the requested model\", `type: auth_error`. Measured 2026-09-12. | `invalid_api_key` |\n| `402` | The token allowance is spent on a model that carries one. Not retryable: the counter returns to zero when that model's quota period does, the calendar month for the models counted per month and your billing period for `glm5.3`. | `monthly_cap_reached` |\n| `403` | Your tier can't access this endpoint. Image generation requires inference membership. A model your tier cannot reach answers `401`, not this. | `tier_restricted` |\n| `404` | The requested model doesn't exist. | `model_not_found` |\n| `429` | Rate limit hit (`rpm_limit`, `max_parallel_requests`), the rolling 4h token budget of `glm5.3`, or a quota exhausted. | `rate_limit_exceeded` · `insufficient_quota` · `quota_exceeded` |\n| `500` | Something went wrong on our side (includes upstream model errors). | (none) |\n| `524` | Timeout, typical with large audio files on `/audio/transcriptions`. | (none) |\n\nRetry `429` and `5xx` responses with exponential backoff. Don't retry `400`, `401`, `403`, or `404` blindly (a context overflow is not retried automatically either: shorten the input or pick a model with a larger window): they'll fail the same way every time until you change the request. `402` cannot be fixed by repetition either: it clears when that model's quota period resets.\n\n## Model catalog\n\nEvery endpoint takes a `model` id. Capabilities vary by model:\n\n| Model | Use for | Capabilities |\n| --- | --- | --- |\n| `deepseek-v4-flash` | Chat, vision, reasoning | Streaming, tool calling, reasoning, image input, 1M-token context. 3B tokens/month per member |\n| `mimo-v2.6-flash` | Chat, vision, audio | Streaming, tool calling, reasoning, image input, audio input, 1M-token context. 1.0B tokens/month per member |\n| `qwen3.8-flash` | Chat, vision, agents | Streaming, tool calling, reasoning (on by default), vision, 1M-token context. 500M tokens/month per member |\n| `glm5.3-flash` | Chat, vision, agents | Streaming, tool calling, reasoning, vision, 1M-token context. 2B tokens/month per member |\n| `qwen3.6` | Chat, agents | Streaming, tool calling, vision, reasoning (opt-out, returns `reasoning_content`) |\n| `gemma4` | Chat, vision, agents | Streaming, tool calling, vision, reasoning (opt-in) |\n| `glm5.3` | Coding, long-horizon agents | Streaming, tool calling, reasoning trace, text-only input, 1M-token context. Premium tier only |\n| `qwen3-embedding` | Embeddings | 4096-dimension vectors |\n| `rerank` | RAG reranking | Qwen3-Reranker-8B, 100+ languages |\n| `kokoro` | Text-to-speech | Multiple voices and audio formats |\n| `whisper` | Speech-to-text | Transcription with word/segment timestamps |\n| `flux-2-klein` | Image generation | Text-to-image and image-to-image |\n| `qwen-image-2.1` | Image generation (text→image) | 512-1280 px, 1-4 per request, seed 0-2147483647. 100 images/month per member (shared pool with flux-2-klein) |\n\n`glm5.3` is served only to keys on the GLM 5.3 premium tier; every other model is available to any inference member. Call [List models](#tag/Models) for the exact set available to your key.\n\n## Versioning & compatibility\n\nThe API tracks the OpenAI API surface, so OpenAI SDKs and tools work against `https://api.nan.builders/v1` unchanged. This reference documents the stable public `/v1` endpoints, and we add capabilities without breaking existing fields.",
+ "description": "Open models on a shared EU inference cluster. Zero logs.\n\nThe NaN API is OpenAI-compatible: predictable, resource-oriented URLs, JSON request and response bodies, and standard HTTP verbs and status codes. Point any OpenAI SDK at our base URL and your existing code keeps working. Change the base URL and the API key, and that's it.\n\nOne schema across every model, so you only learn the API once. Change the `model` field to switch models; everything else stays the same.\n\n- Base URL: `https://api.nan.builders/v1`\n- OpenAPI spec: this document. Import it into Postman, Insomnia, or your own tooling.\n\nIf you use the [Helmcode](https://helmcode.com) enterprise service, the base URL is `https://api.helmcode.com/v1` instead. Every other endpoint is identical.\n\n## Authentication\n\nEvery request authenticates with an API key, sent as a Bearer token:\n\n```\nAuthorization: Bearer $NAN_API_KEY\n```\n\nYou must be a NaN community member. Generate your key from user settings, under \"API Keys\", on the [platform](https://cloud.nan.builders/). The key is personal and non-transferable. Keep it secret: never embed one in client-side code or commit it to source control. Requests must go over HTTPS; calls over plain HTTP fail.\n\n## Making requests\n\nThe API is OpenAI-compatible, so point an official OpenAI SDK at our base URL and change nothing else:\n\n```python\nfrom openai import OpenAI\n\nclient = OpenAI(\n api_key=\"$NAN_API_KEY\",\n base_url=\"https://api.nan.builders/v1\",\n)\n\nresp = client.chat.completions.create(\n model=\"deepseek-v4-flash\",\n messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n)\nprint(resp.choices[0].message.content)\n```\n\n## Streaming\n\nChat responses can stream token-by-token. Set `\"stream\": true` on `/chat/completions` and the response arrives as Server-Sent Events: each event is a `data:` line carrying a `chat.completion.chunk`, with the new text in `choices[0].delta.content`. A final `data: [DONE]` line ends the stream. `/chat/completions` streams incrementally on every chat model. `/responses` streams incrementally only on `deepseek-v4-flash`; on `qwen3.6` and `gemma4` it holds the whole answer and sends it in one burst at the end.\n\n## Rate limits\n\n{{RATE_LIMITS}}\n\nImage endpoints run on their own budget, separate from the model endpoints: 20 requests per minute and 100 requests per month. The usage endpoint is metered separately too: {{USAGE_RATE_LIMIT}} requests per minute per member. Exceed any limit and you get a `429`.\n\n## Errors\n\nNaN uses conventional HTTP status codes: `2xx` on success, `4xx` for a problem with the request (a missing parameter, an invalid key, an unavailable model) and `5xx` for a server-side error. Every error returns a JSON body in the OpenAI shape:\n\n```json\n{\n \"error\": {\n \"message\": \"The model 'foo' does not exist.\",\n \"type\": \"invalid_request_error\",\n \"param\": \"model\",\n \"code\": \"model_not_found\"\n }\n}\n```\n\n`message` is human-readable, `param` names the offending field when applicable, and `code` is a short machine-readable string you can branch on.\n\n| Status | Meaning | `code` |\n| --- | --- | --- |\n| `400` | Invalid or malformed parameter (`param` says which); content blocked by the safety filter; a `max_tokens` above what the model can generate (a generic \"Invalid request\" message today); or a request that does not fit the model's context window. On the models hosted on the cluster (`qwen3.6`, `gemma4`) the overflow message is \"Context length exceeded for model '...'\" with the model's limit; on the other models it is usually that text, but it can arrive as the generic \"Invalid request. Check your request parameters.\" | `\"400\"` (with `type: invalid_request_error`) · `content_policy_violation` |\n| `401` | Missing or invalid API key, or a key whose tier does not reach the requested model (`glm5.3`): \"This API key does not have access to the requested model\", `type: auth_error`. Measured 2026-09-12. | `invalid_api_key` |\n| `402` | The token allowance is spent on a model that carries one. Not retryable: the counter returns to zero when that model's quota period does, the calendar month for the models counted per month and your billing period for `glm5.3`. | `monthly_cap_reached` |\n| `403` | Your tier can't access this endpoint. Image generation requires inference membership. A model your tier cannot reach answers `401`, not this. | `tier_restricted` |\n| `404` | The requested model doesn't exist. | `model_not_found` |\n| `429` | Rate limit hit (`rpm_limit`, `max_parallel_requests`), the rolling 4h token budget of `glm5.3`, or a quota exhausted. | `rate_limit_exceeded` · `insufficient_quota` · `quota_exceeded` |\n| `500` | Something went wrong on our side (includes upstream model errors). | (none) |\n| `524` | Timeout, typical with large audio files on `/audio/transcriptions`. | (none) |\n\nRetry `429` and `5xx` responses with exponential backoff. Don't retry `400`, `401`, `403`, or `404` blindly (a context overflow is not retried automatically either: shorten the input or pick a model with a larger window): they'll fail the same way every time until you change the request. `402` cannot be fixed by repetition either: it clears when that model's quota period resets.\n\n## Model catalog\n\nEvery endpoint takes a `model` id. Capabilities vary by model:\n\n| Model | Use for | Capabilities |\n| --- | --- | --- |\n| `deepseek-v4-flash` | Chat, vision, reasoning | Streaming, tool calling, reasoning, image input, 1M-token context. 3B tokens/month per member |\n| `mimo-v2.6-flash` | Chat, vision, audio | Streaming, tool calling, reasoning, image input, audio input, 1M-token context. 1.0B tokens/month per member |\n| `qwen3.8-flash` | Chat, vision, agents | Streaming, tool calling, reasoning (on by default), vision, 1M-token context. 500M tokens/month per member |\n| `glm5.3-flash` | Chat, vision, agents | Streaming, tool calling, reasoning, vision, 1M-token context. 2B tokens/month per member |\n| `qwen3.6` | Chat, agents | Streaming, tool calling, vision, reasoning (opt-out, returns `reasoning_content`) |\n| `gemma4` | Chat, vision, agents | Streaming, tool calling, vision, reasoning (opt-in) |\n| `glm5.3` | Coding, long-horizon agents | Streaming, tool calling, reasoning trace, image, audio and video input, 1M-token context. Premium tier only |\n| `qwen3-embedding` | Embeddings | 4096-dimension vectors |\n| `rerank` | RAG reranking | Qwen3-Reranker-8B, 100+ languages |\n| `kokoro` | Text-to-speech | Multiple voices and audio formats |\n| `whisper` | Speech-to-text | Transcription with word/segment timestamps |\n| `flux-2-klein` | Image generation | Text-to-image and image-to-image |\n| `qwen-image-2.1` | Image generation (text→image) | 512-1280 px, 1-4 per request, seed 0-2147483647. 100 images/month per member (shared pool with flux-2-klein) |\n\n`glm5.3` is served only to keys on the GLM 5.3 premium tier; every other model is available to any inference member. Call [List models](#tag/Models) for the exact set available to your key.\n\n## Versioning & compatibility\n\nThe API tracks the OpenAI API surface, so OpenAI SDKs and tools work against `https://api.nan.builders/v1` unchanged. This reference documents the stable public `/v1` endpoints, and we add capabilities without breaking existing fields.",
"contact": {
"name": "NaN",
"url": "https://nan.builders"
@@ -162,7 +162,7 @@
"messages": {
"type": "array",
"minItems": 1,
- "description": "The conversation so far, oldest first. `content` is a string, or an array of parts (`text` + `image_url`) for vision input on `deepseek-v4-flash`, `mimo-v2.6-flash`, `qwen3.8-flash`, `glm5.3-flash`, `qwen3.6` and `gemma4`. `glm5.3` is text only.",
+ "description": "The conversation so far, oldest first. `content` is a string, or an array of parts (`text` + `image_url`) for vision input on every chat model: `deepseek-v4-flash`, `mimo-v2.6-flash`, `qwen3.8-flash`, `glm5.3-flash`, `qwen3.6`, `gemma4` and `glm5.3`.",
"items": {
"$ref": "#/components/schemas/Message"
},
diff --git a/src/lib/modelCatalog.ts b/src/lib/modelCatalog.ts
index 5a041ad7..a64094f6 100644
--- a/src/lib/modelCatalog.ts
+++ b/src/lib/modelCatalog.ts
@@ -22,7 +22,7 @@
export type ModelKind = 'chat' | 'embedding' | 'rerank' | 'tts' | 'stt' | 'image';
-export type Modality = 'text' | 'image' | 'audio';
+export type Modality = 'text' | 'image' | 'audio' | 'video';
/**
* How a model counts against what you pay for.
@@ -79,7 +79,7 @@ export const MODELS: ModelSpec[] = [
by: 'Z.ai',
kind: 'chat',
contextTokens: 1_000_000,
- inputs: ['text'],
+ inputs: ['text', 'image', 'audio', 'video'],
quota: {
kind: 'billingPeriod',
label: { en: '3B tokens / billing period', es: '3B tokens/periodo de facturación' },
@@ -261,8 +261,8 @@ export function quotaLabel(m: ModelSpec, lang: 'en' | 'es'): string {
/** The input modalities as a short list: "text · image". */
export function inputsLabel(m: ModelSpec, lang: 'en' | 'es'): string {
const T = {
- en: { text: 'text', image: 'image', audio: 'audio' },
- es: { text: 'texto', image: 'imagen', audio: 'audio' },
+ en: { text: 'text', image: 'image', audio: 'audio', video: 'video' },
+ es: { text: 'texto', image: 'imagen', audio: 'audio', video: 'vídeo' },
}[lang];
return (m.inputs ?? ['text']).map((i) => T[i]).join(' · ');
}
diff --git a/src/tests/landing/modelsTable.test.ts b/src/tests/landing/modelsTable.test.ts
index b9c66a38..2b6ac752 100644
--- a/src/tests/landing/modelsTable.test.ts
+++ b/src/tests/landing/modelsTable.test.ts
@@ -32,13 +32,15 @@ describe('tabla de modelos — la fila premium', () => {
expect(premium.map((m) => m.id)).toEqual(['glm5.3']);
});
- test('publica el contexto que sirve el cluster y no le atribuye visión', () => {
+ test('publica el contexto que sirve el cluster, NVFP4 y multimodal', () => {
const { specs } = premium[0];
const glm = DEFAULT_RATE_LIMITS.windowedModels.find((m) => m.model === 'glm5.3')!;
expect(specs).toContain(`${formatTokens(glm.contextTokens)} context`);
- // El premium entra solo texto. La visión la dan glm5.3-flash y compañía,
- // y durante un tiempo la tabla se la atribuyó también al 5.3.
- expect(specs).not.toMatch(/multimodal|vision|visión/i);
+ // Since the NVFP4 build the premium reads images. Before that it was text
+ // only and this row had to say so; both facts changed together.
+ expect(specs).toContain('NVFP4');
+ expect(specs).not.toContain('FP8');
+ expect(specs).toMatch(/multimodal/i);
});
test('la cuota va por periodo de facturación, no por mes', () => {
diff --git a/src/tests/lib/docsGlm53.test.ts b/src/tests/lib/docsGlm53.test.ts
index 761d65db..1a952f1c 100644
--- a/src/tests/lib/docsGlm53.test.ts
+++ b/src/tests/lib/docsGlm53.test.ts
@@ -67,14 +67,17 @@ describe('docs/models — the glm5.3 card', () => {
});
/**
- * 5.3 is text in, text out. The card claimed image input for weeks, copied
- * over from the 5.3 announcement; the multimodal one is `glm5.3-flash`.
+ * 5.3 was text only until it moved to an NVFP4 build that reads images, and
+ * the card said "it does not take images" for a month after #39 removed the
+ * vision it had copied from the announcement. Both halves are asserted so a
+ * revert of either one shows up here.
*/
- test('does not claim image input', () => {
+ test('publishes image, audio and video input and the NVFP4 quantization', () => {
const section = glmSection(models);
- expect(section).toContain('Input modalities: text');
- expect(section).not.toContain('Input modalities: text · image');
- expect(section).not.toMatch(/vision|image input/i);
+ expect(section).toContain('Input modalities: text · image · audio · video');
+ expect(section).toContain('Quantization: NVFP4');
+ expect(section).not.toContain('FP8');
+ expect(section).not.toMatch(/does not take images|text in, text out/i);
});
/**
@@ -121,8 +124,8 @@ describe('docs/api — glm5.3 is callable', () => {
expect(row).toContain('1M-token context');
expect(row).toMatch(/premium tier/i);
expect(row).not.toContain('256K');
- // The catalog row listed vision too. It is the one chat model with none.
- expect(row).not.toMatch(/vision/i);
+ expect(row).toMatch(/image, audio and video input/);
+ expect(row).not.toMatch(/text-only/i);
});
test('the 4h window is spelled out, not left as small print', () => {