Skip to content

fix(ml,back): enforce the output token cap (#106) - #123

Merged
sergi-torres merged 3 commits into
mainfrom
fix/106-max-tokens
Jul 28, 2026
Merged

fix(ml,back): enforce the output token cap (#106)#123
sergi-torres merged 3 commits into
mainfrom
fix/106-max-tokens

Conversation

@sergi-torres

Copy link
Copy Markdown
Owner

Closes #106.

El defecto

_GENERATION_PARAMS declaraba max_new_tokens: 512, pero el camino de llamada es ModelInference.chat(), cuyo esquema (TextChatParameters) no tiene ese campo. Las claves desconocidas se descartan en silencio, así que el tope nunca estuvo en vigor y se aplicaba el default del servicio: 1024.

Medición contra Watsonx real (eu-de, llama-3-3-70b)

Prompt: "Write a detailed 800-word essay about the history of the printing press."

Parámetro Resultado
max_new_tokens=16 (estado anterior) TimeoutError en los 4 intentos — revienta el timeout duro de 8 s
max_tokens=16 (corregido) 15 palabras en 1,2 s

La consecuencia no era solo coste y latencia: cualquier prompt que invitara a una respuesta larga agotaba los reintentos y salía como 503 desde /api/generate. Es más grave de lo que la issue estimaba.

Prueba

backend/tests/test_generation_params.py:

  • Control positivo estático: cada clave que enviamos se comprueba contra los campos del dataclass TextChatParameters. Es la comprobación que habría cazado esto el día que se introdujo, y caza cualquier futura clave mal escrita.
  • Test en vivo (vallado por credenciales): pide mucho más de lo que el tope permite y exige que la respuesta vuelva cortada. Sin tope en vigor, falla.

Verificación

pytest backend/tests/test_generation_params.py -q   -> 3 passed
pytest backend/tests -q                             -> 1 failed, 100 passed
pytest ai_pipeline/tests -q                         -> 222 passed, 3 skipped
ruff check .                                        -> All checks passed!

El único fallo del backend es test_generate_live_watsonx, que apunta al ibm/granite-3-8b-instruct retirado upstream — ajeno a este cambio y en curso en el PR #120.

🤖 Generated with Claude Code

…chat param (#106)

`_GENERATION_PARAMS` declared `max_new_tokens: 512`, but the call path is
`ModelInference.chat()`, whose schema (`TextChatParameters`) has no such
field. Unknown keys are dropped in silence, so the cap was never in force
and the service default of 1024 applied to both columns of the A/B.

Measured against real Watsonx (eu-de, llama-3-3-70b) on the prompt
"Write a detailed 800-word essay about the history of the printing press":

    max_new_tokens=16  -> TimeoutError on all 4 attempts (8s hard timeout)
    max_tokens=16      -> 15 words in 1.2s

So the consequence was not only cost and latency: any prompt inviting a
long answer exhausted the retries and surfaced as a 503 from
`/api/generate`.

Adds `backend/tests/test_generation_params.py` with a positive control —
every key we send is checked against the dataclass fields of
`TextChatParameters`, which is the check that would have caught this when
it was introduced — plus a credential-gated live test that asks for far
more than the cap allows and asserts the answer comes back cut short.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
author-ai Ready Ready Preview, Comment Jul 28, 2026 9:45pm

…udget

Measured against real Watsonx (eu-de, llama-3-3-70b, n=5 per cell, hard
timeout lifted so the true duration shows):

    cap=512  vanilla  median 4.5s  worst 5.9s (~196 words)
             autoria  median 7.2s  worst 8.4s (~313 words)   <- over the cap
    cap=320  vanilla  median 5.0s  worst 5.3s (~242 words)
             autoria  median 5.4s  worst 5.6s (~223 words)

At 512 the conditioned branch straddles HARD_TIMEOUT_SECONDS: it sometimes
returns and sometimes exhausts all four attempts. A failed AutorIA branch
cannot be degraded away because the passport needs it, so that surfaces as
an intermittent 503 from POST /api/generate — reproduced twice while
verifying the RAG wiring end to end, once passing and once failing on the
same prompt.

320 keeps a margin under both the 8s hard timeout and the 10s client-side
abort in frontend/src/lib/api.ts, and still yields ~220 words per column.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`test_generation_params.py` imports `autoria_ai.generator` to check our
parameters against the SDK schema. That passes locally, where an editable
install of `autoria_ai` sits in the venv, and fails in CI, which installs
only `backend/`:

    ModuleNotFoundError: No module named 'autoria_ai'

Same resolution the production code already uses in
`app.routes.generate._ensure_ai_pipeline_on_path` — the repo-root
`ai_pipeline` directory goes on `sys.path`. This is exactly the local/CI
divergence the completeness audit warned about: a green local run proving
nothing about the deployed shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sergi-torres
sergi-torres merged commit f19d4a8 into main Jul 28, 2026
8 checks passed
@sergi-torres
sergi-torres deleted the fix/106-max-tokens branch July 28, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[backend] El tope de tokens de salida se ignora en silencio: max_new_tokens vs max_tokens en ModelInference.chat()

1 participant