From fb3bb521fe01f1d162377be70da4cb925c4aa7a9 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Sun, 16 Aug 2026 15:15:02 -0700 Subject: [PATCH] fix(gateway-chat): guard max_tokens against NaN from an empty field An empty or non-numeric Max tokens field makes parseInt return NaN, and JSON.stringify serialises NaN as null. The server reads null as 'no max_tokens supplied' and substitutes its own default -- which is indistinguishable from the UI ignoring the field, and is the most likely explanation for a typed value appearing to have no effect. Falls back to the same 4096 the input defaults to. Ruled out on the way to this, all measured rather than assumed: - LiteLLM caps nothing: max_tokens=None on both aliases, no max-token keys in litellm_settings or general_settings. - The gateway honours large values end-to-end: 5,346 completion tokens returned at max_tokens=8192, finish=stop. - The UI has ONE chat send path, no duplicate element ids, a standard getElementById helper, and the request body is never mutated after construction -- so the field is read live at send time. Remaining client-side cause if it recurs is a stale cached page: nginx serves this file with only Last-Modified/ETag and no Cache-Control, so an already-open tab will not re-fetch. ETag changes on each deploy, so a reload picks it up. --- stacks/gateway-chat/conf/index.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stacks/gateway-chat/conf/index.html b/stacks/gateway-chat/conf/index.html index df7ae53..75c386f 100644 --- a/stacks/gateway-chat/conf/index.html +++ b/stacks/gateway-chat/conf/index.html @@ -249,7 +249,12 @@ async function send(){ model: $('model').value.trim() || 'gen', messages, temperature: parseFloat($('temp').value), - max_tokens: parseInt($('max').value, 10), + // Guarded: an empty or non-numeric field makes parseInt return NaN, and + // JSON.stringify serialises NaN as `null` -- which the server reads as + // "no max_tokens supplied" and silently substitutes its own default. + // That is indistinguishable from the field being ignored. Fall back to the + // same 4096 the input defaults to. + max_tokens: parseInt($('max').value, 10) || 4096, stream: true // intentionally NO `tools` — vLLM 400s on an empty array. };