fix(docs): los restos de la migracion, los techos por minuto, la deprecacion del web search y la politica de flux - #75
Open
borjaperfra wants to merge 8 commits into
Open
borjaperfra wants to merge 8 commits into
borjaperfra wants to merge 8 commits into
Conversation
…tados
`%LOCALAPPDATA%\Programs\nan` llegaba a la pagina partido en dos por un
salto de linea de verdad ("Programs" / "an") y `-InstallDir "C:\tools"`
con un tabulador donde iba la barra ("C:<TAB>ools"). Los dos en ingles y
en espanol, desde el #60.
Ninguno es un error de sintaxis en ningun sitio: la linea de PowerShell
corre perfectamente, solo que instala donde no es. Y una ruta de Windows
es justo la cadena que nadie de este equipo vuelve a pegar, asi que
sobrevivieron a la revision y a la traduccion.
Las dos rutas estan comprobadas contra el install.ps1 que se sirve hoy:
$InstallDir = Join-Path $env:LOCALAPPDATA 'Programs\nan', y -InstallDir
es un parametro suyo de verdad.
Los 1150 tests pasaban con el bug puesto. docsSnippets EJECUTA los
snippets, y un snippet que corre es todo lo que pide; estas dos reglas
nuevas miran el residuo en vez del resultado:
- ningun tabulador literal en las paginas, que es en lo que se queda un
\t cuando alguien lo ha interpretado por el camino;
- ningun span de codigo inline sin cerrar, que es la marca que deja un
\n al convertirse en salto de linea: parte el span y deja las dos
lineas con un numero impar de comillas invertidas.
Vistas en rojo antes con los cuatro fallos exactos y ni un falso
positivo en las otras 20 paginas.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…mpilar La pagina se trajo el instalador de PowerShell en el #60 y se quedo con todo lo que estaba escrito de cuando Windows no tenia binario. Resultado: ofrece `irm https://nan.builders/install.ps1 | iex` arriba, dice "hay binarios para macOS, Linux y Windows" un poco mas abajo, y dos pantallas despues manda a instalar Go. - "Es la via para Windows" en Compilar desde el codigo. La seccion se queda, que es la respuesta buena para quien va a tocar el CLI; lo que no puede ser es la via para una plataforma que ya publicamos. - El known issue entero, "En Windows hay que compilar. Las versiones publicadas traen binarios de macOS y Linux". Lo zanja la release: v0.1.19 publica windows_amd64.zip y windows_arm64.zip. Y de paso el known issue que si sigue en pie ofrecia solo el `.tar.gz` para bajarlo a mano, que en Windows tampoco existe. El test no persigue estas cuatro frases sino la forma del fallo: una pagina que instala una plataforma desde un binario publicado y encima le dice que compile. La siguiente plataforma va a llegar igual, anadiendo el texto nuevo y dejando el viejo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cuerpo Cerraba con "el endpoint es compatible con OpenAI tanto en la autenticacion como en el formato del cuerpo". La mitad es verdad y util -la cabecera Bearer es la misma-, y la otra mitad manda al lector a buscar una especificacion que no existe: OpenAI no define ningun /rerank, asi que no hay cuerpo con el que ser compatible. El cuerpo es nuestro. La pagina ya lo sabia. Tres lineas mas arriba, en el comentario del propio snippet, dice que el endpoint "is not part of the standard OpenAI client". Y /search, el otro endpoint nuestro del mismo fichero, ya cerraba bien: "manda el cuerpo JSON con tu key en el Bearer". Esto es igualar rerank a como ya estaba escrito search. El test es la pagina contra si misma: lo que ella marca como fuera del cliente estandar no puede venderse dos parrafos despues como compatible en su payload. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bajo prosa en espanol, el boton decia "Copy", el aviso "Copied to clipboard", el buscador vacio "No results" y los aria-label "Search documentation" y "Documentation". Tiene una causa concreta y esta en el historial: docsCopyIsEnglish.test existe porque paso lo contrario, el espanol se habia colado en las paginas inglesas -"Copiar", "!Copiado al portapapeles!"- y se arreglo escribiendo esas cadenas en ingles. Correcto para /docs, y deja /es/docs justo asi. El layout ya tenia la tabla que lo resuelve, `T`, con su rama `en` y su rama `es`; estas ocho nunca entraron porque viven en los scripts de cliente, no en el markup. El de busqueda ya iba con define:vars, asi que se le pasa `T` y ya esta. El de los bloques de codigo esta empaquetado y tipado, asi que no puede llevar define:vars sin perder el TypeScript: lee sus etiquetas del dataset del propio toast, que este layout pinta siempre. Sin fallback en ingles, a proposito: un fallback es exactamente como las paginas espanolas acabaron diciendo "Copied to clipboard". El precio es que un data-* que falte deja el boton mudo en vez de en otro idioma, que tambien es silencioso, asi que hay test de que el toast lleva los cinco atributos y de que algo lee los cinco. La regla del test no es "en que idioma esta esto" sino "sale de la tabla": un literal que el lector ve, fuera de `T`, tiene un solo idioma sea el que sea. Verificado renderizado en las dos rutas: /docs sirve "Copied to clipboard" y /es/docs "Copiado al portapapeles", y de paso las dos rutas de Windows salen enteras. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fui a mirar si a `glm5.3-flash` y `qwen3.8-flash` les faltaba el TPM y resulta que el problema era mas grande: **ninguno de los cuatro numeros publicados era correcto.** cloud-api no guarda un numero de tokens por minuto. `modelRateLimits` en usage_quota.go guarda la ventana de contexto y un FillsPerMin -cuantas veces por minuto puedes rellenar esa ventana entera- y el techo es el producto. El hook de rate limit lo replica en FALLBACK_MODEL_LIMITS y las dos tablas estan obligadas a coincidir; coinciden. Lo que sale de multiplicar, contra lo que decia la pagina: deepseek-v4-flash 1.048.576 x 6 = 6,3M publicaba 1.5M mimo-v2.5 1.050.000 x 4 = 4,2M publicaba 1.5M qwen3.6 262.144 x 12 = 3,1M publicaba 1.5M gemma4 262.144 x 4 = 1,0M publicaba 1.5M glm5.3-flash 1.048.576 x 11 = 11,5M no aparecia qwen3.8-flash 1.048.576 x 6 = 6,3M no aparecia **El que importa es gemma4: su techo real esta POR DEBAJO del anunciado.** Los otros tres se quedaban cortos, que es feo pero inofensivo; ese prometia de mas, y un miembro que planificara contra la pagina se comia 429 que la pagina decia que no podian pasar. Y `rerank` no tiene 1000 rpm. Esta en `rateLimitExemptModels` junto a kokoro, whisper y qwen3-embedding: **no lleva limite por minuto propio.** El 1000 no sale de ningun sitio del backend. Ahora se publican los cuatro como lista, porque un endpoint ausente de la tabla no se distingue de uno que se nos olvido medir - que es exactamente como el 1000 estuvo ahi meses. **Por que no se vio:** el numero era un `label` escrito a mano. Ahora se guardan los dos factores y el producto se deriva; un producto que nadie multiplica no puede salir mal. El extractor de /api/docs tenia el mismo supuesto metido dentro -metia los cuatro modelos en una fila y cogia el valor de `tpm[0]`-, una forma que solo puede ser correcta mientras todos compartan cifra, y nunca la compartieron. De paso, dos cosas que la prosa decia mal: - "glm5.3 no se rige por minuto en absoluto" es falso: tiene techo como los demas (11,5M), lo que pasa es que la ventana movil muerde mucho antes (400M/4h son 1,67M/min de media). El bloque premium ya lo publica, porque "¿esto va por minuto?" merece respuesta. - Faltaba la jerarquia, que es lo que hacia irresoluble lo de rerank: el limite que notas es el mas estricto de los tres que aplican, y los 60/min de la key cuentan todo lo que llames. El test ancla los FACTORES a cloud-api, no el producto, y exige que todo modelo del catalogo este contabilizado en algun sitio - que es la regla que habria cazado el hueco original. Visto en rojo de las dos formas: cambiando un fills y quitando un modelo de la tabla. Fuentes: helmcode/nan-cloud-api internal/handlers/usage_quota.go y limits_policy.go; helmcode/nan-devops scripts/litellm-ratelimit-hook/ rate_limit_hook.py y docs/GLM52-PREMIUM-TIER.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Se retira la busqueda web, por API y por MCP. Y como la busqueda web es **la unica herramienta que el servidor MCP tiene hoy**, el servidor se va con ella: no tiene sentido avisar solo de una mitad. Sin fecha todavia. El aviso dice lo que se puede decir hoy -sigue funcionando, se anunciara una fecha antes de que pare- y sobre todo dice la decision que el lector tiene delante: no construyas nada nuevo encima. Seis superficies, porque el hecho vive en seis sitios: - `/docs/mcp` y `/es/docs/mcp`, con Callout arriba del todo y la descripcion del frontmatter cambiada, que es lo que se ve en la barra lateral y en los resultados de busqueda antes de abrir la pagina. - La seccion `tool: web search` de Ejemplos, en los dos idiomas. - `/search` y `/mcp` en la spec, con **`deprecated: true`**, que es lo que Scalar pinta como badge y lo que recoge un cliente generado. La prosa sola no vale: nada aguas abajo lee prosa. - Los tags Search y MCP de la spec. - Y los enlaces cruzados desde intro, agent-setup, claude-code, gentle-ai y agents, que es por donde llega la gente que no fue a buscarlo. El test no persigue que alguien se olvide de deprecar. Persigue el fallo que este repo ya ha tenido dos veces: **el cambio entra en ingles y la pagina espanola sigue recomendandolo en voz baja.** Un miembro que lee /es/docs y no se entera es justo el que construye encima. Las paginas que enlazan se buscan escaneando, no por lista, porque el caso que no se puede cubrir a mano es la pagina que todavia no existe. Mira una ventana de lineas y no la linea suelta: en agent-setup el enlace va en `href:` y lo que el lector lee esta en `note:`. Dos cosas que se afinaron al verlo fallar: los marcadores son raices (`deprecad`), porque el espanol flexiona y "deprecada" pasaba; y la comprobacion ignora el frontmatter, porque mientras contaba se podia borrar el Callout entero y todo seguia en verde. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fui a publicar la de flux creyendo que no tenia ninguna. Tenia dos sorpresas. **La primera: flux no pasa por LiteLLM.** Lo sirve cloud-api contra Workers AI, en `internal/imagegen`, asi que no llega al rate_limit_hook y **los 60/min y las 5 a la vez de la key no le aplican**. El comentario de `cmd/server/main.go` lo dice con todas las letras. Mi conclusion anterior -que caia al `defaultRateLimit` de los modelos de chat- estaba mal, y tampoco hace falta ningun cambio en cloud-api: flux ya tiene politica propia y deliberada, solo que en otro subsistema. **La segunda: ya estaba publicada, y mal.** Ejemplos decia "20 peticiones por minuto", una cifra que **no aparece en ninguna parte de la plataforma**. Lo real, de `imagegen.NewRateLimiter(1, 3)`, `imagegen.MonthlyQuota` y `imagegen.MaxVariants`: 1 peticion por segundo, rafaga de 3 100 peticiones por mes natural hasta 4 imagenes por peticion, y una peticion que pida varias sigue costando una Bloque propio en la card de limites y no una fila, porque lo primero que hay que decir de esto es que los limites de la key no le llegan, y eso una fila no lo puede decir. Mas el ritmo en la ficha del modelo y el texto de Ejemplos corregido, en los dos idiomas. El test ancla los tres numeros a su limitador y ademas **exige que flux NO aparezca en las tablas de LiteLLM**: una fila alli afirmaria que los 60/min le alcanzan, y no es verdad. Visto en rojo de las dos formas. Y de paso, `minimax-h3` deja de estar en la lista de "no publicados" por la razon equivocada: es el modelo de video, con allowlist en cloud-api. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cloud-api publica la politica entera en un documento y su propio comentario dice para que: el 400M/4h estaba copiado a mano en cuatro sitios -el hook, el types.ts de cloud-ui, el rateLimits.ts de esta web y la prosa de las docs- "sin que nada pudiera notar que habian divergido". Esta web fue donde peor salio: publicaba un techo por minuto para cuatro modelos y no acertaba ninguno. **Esta todo cableado menos la URL.** `LIMITS_POLICY_URL` no esta puesta, asi que `resolveRateLimitsConfig()` devuelve la tabla interna y no hace ninguna peticion: la pagina renderiza hoy exactamente igual que antes de que este fichero existiera, verificado sirviendola. Apuntando esa variable a un endpoint que sirva el documento, la web pasa a seguir a la plataforma. El endpoint que leeria, `/api/internal/limits/policy`, va detras del `CreditsHookSecret`, compartido con el hook. Dar eso a un worker publico es decision de quien lleva la plataforma, y la respuesta estrecha es una ruta publica de solo lectura: nada de ese documento es secreto, ya esta publicado literalmente en /docs/models. `LIMITS_POLICY_TOKEN` esta ahi para cualquiera de las dos. Cuatro reglas, cada una con su motivo: 1. **Un fallo nunca se ve.** Cualquier error, timeout o forma que no reconozca, y se usa la tabla interna. Una card de limites que no renderiza es peor que una que va un deploy por detras. 2. **Rechaza un documento mas nuevo del que entiende**, igual que el hook: malinterpretar un documento reformado pone numeros MAL, declinarlo pone numeros un poco viejos. 3. **Todo o nada.** Un documento al que le falta un campo se rechaza entero, no se parchea desde la tabla interna. Media card mezclando dos fuentes en silencio es justo el fallo que esto viene a cerrar. 4. **Solo los modelos del catalogo.** La politica gobierna todo lo que sirve la plataforma, que no es el mismo conjunto: `glm5.2` esta ahi y esta web no lo documenta a proposito. La politica pone los numeros; modelos.json sigue decidiendo de que modelos habla la pagina. Y no toca ni los limites por key (viven en la key de LiteLLM) ni la generacion de imagenes (no pasa por LiteLLM), que el mapeo conserva explicitamente. Los tests cubren sobre todo las formas de fallar, que es lo que importa en una pagina de documentacion. Comprobados en rojo tres: aceptar una version desconocida, no filtrar por el catalogo -se cuela glm5.2- y parchear un hueco en vez de rechazar. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Empezo como triaje de una revision externa antes de lanzar y acabo siendo cuatro cosas, tres de ellas cifras publicadas que eran falsas.
1. Los techos por minuto estaban mal los cuatro
cloud-api no guarda un TPM:
modelRateLimitsguarda ventana de contexto yFillsPerMin, y el techo es el producto.deepseek-v4-flashmimo-v2.5qwen3.6gemma4glm5.3-flashqwen3.8-flashgemma4prometia de mas: su techo real esta por debajo del anunciado, asi que quien planificara contra la pagina se comia 429 que la pagina decia imposibles. Yrerankno tiene 1000 rpm: esta enrateLimitExemptModels, sin limite por minuto propio.Ahora se guardan los dos factores y el producto se deriva. El extractor de
/api/docstenia el mismo supuesto dentro: metia los cuatro en una fila y cogiatpm[0].2. La busqueda web y el servidor MCP, deprecados
Sin fecha todavia. Y como la busqueda web es la unica herramienta que el MCP tiene, el servidor se va con ella: no tiene sentido avisar de media.
Seis superficies: las dos paginas
/docs/mcp, la seccion de Ejemplos,/searchy/mcpen la spec condeprecated: true(lo que Scalar pinta como badge y lo que recoge un cliente generado), los dos tags, y los enlaces cruzados desde intro, agent-setup, claude-code, gentle-ai y agents.3. La politica de flux, que estaba publicada y era falsa
Dos sorpresas. Flux no pasa por LiteLLM: lo sirve cloud-api contra Workers AI, asi que los 60/min y las 5 a la vez de la key no le aplican. Y ya estaba publicada, mal: Ejemplos decia "20 peticiones por minuto", cifra que no aparece en ninguna parte de la plataforma. Lo real, de
imagegen.NewRateLimiter(1, 3)yMonthlyQuota: 1 peticion por segundo con rafaga de 3, 100 al mes natural, hasta 4 imagenes por peticion y una peticion que pida varias sigue costando una.No hizo falta tocar cloud-api: flux ya tenia politica propia y deliberada, solo que en otro subsistema.
4. La web, preparada para leer la politica
Cableado todo menos la URL.
LIMITS_POLICY_URLsin poner, asi que se usa la tabla interna y no se hace ninguna peticion - verificado sirviendo la pagina. Cuatro reglas: un fallo nunca se ve, rechaza un documento mas nuevo del que entiende, todo o nada (nunca parchea un hueco desde la tabla interna), y publica solo los modelos del catalogo, porque la politica incluyeglm5.2y esta web no lo documenta.Falta decidir de donde lee:
/api/internal/limits/policyva detras delCreditsHookSecret, y la respuesta estrecha es una ruta publica de solo lectura, porque nada de ese documento es secreto.Y los cuatro restos de la migracion con los que empezo esto
Las dos rutas de Windows del CLI con el
\ny el\tya gastados; la pagina diciendo dos veces que en Windows hay que compilar cuando v0.1.19 publica binarios;/rerankvendido como compatible con OpenAI en el cuerpo; y el chrome de/es/docsen ingles.Los tests
Los 1150 pasaban con todo esto puesto. Ahora 1294. Cada regla vista en rojo antes, y las que mas importan comprobadas rompiendolas a proposito de varias formas:
Lo que la revision externa decia y no era
La ES de Primeros pasos esta bien. Lo que leyeron -LiteLLM,
qwen3.6,@ai-sdk/openai- existio, perogit log -S'LiteLLM'lo situa antes del #56: leyeron cache. Igual los "60 rpm" de la portada, que salen de un bloquei18nmuerto. La navegacion ES no esta stale (misma fuente, paridad perfecta, ya habia test). Lo de MiMo y Pi ya estaba escrito.🤖 Generated with Claude Code