From 542ff1289015067c695305459ee8f8883e349d88 Mon Sep 17 00:00:00 2001 From: Manuel Escalante Date: Mon, 26 Oct 2026 18:20:05 +0100 Subject: [PATCH 1/4] fix: send supported reasoning parameters to gpt-5.1 and gpt-6 models --- .../scripts/ai_providers/openai_provider.py | 41 ++++++++++++------- .../tests/test_openai_compatible_ai_models.py | 13 ++++++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/AppImage/scripts/ai_providers/openai_provider.py b/AppImage/scripts/ai_providers/openai_provider.py index 9e012dd7..01a33e8a 100644 --- a/AppImage/scripts/ai_providers/openai_provider.py +++ b/AppImage/scripts/ai_providers/openai_provider.py @@ -5,6 +5,7 @@ Models are loaded dynamically from the API. """ from typing import Optional, List import json +import re import urllib.request import urllib.error from .base import AIProvider, AIProviderError @@ -36,11 +37,11 @@ class OpenAIProvider(AIProvider): ] # Recommended models for chat (in priority order) - RECOMMENDED_PREFIXES = ['gpt-4o-mini', 'gpt-4o', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'] + RECOMMENDED_PREFIXES = ['gpt-6-luna', 'gpt-5.6-luna', 'gpt-5.4-mini', 'gpt-6-sol'] @staticmethod def _is_reasoning_model(model: str) -> bool: - """True for OpenAI reasoning models (o-series + non-chat gpt-5+). + """True for OpenAI reasoning models (o-series + non-chat gpt-5 and later). These use a stricter API contract than chat models: - Must use ``max_completion_tokens`` instead of ``max_tokens`` @@ -54,11 +55,24 @@ class OpenAIProvider(AIProvider): # o1, o3, o4, o5 ... (o...) if len(m) >= 2 and m[0] == 'o' and m[1].isdigit(): return True - # gpt-5, gpt-5-mini, gpt-5.1, gpt-5.2-pro ... EXCEPT *-chat-latest - if m.startswith('gpt-5') and '-chat' not in m: - return True - return False - + # gpt-5, gpt-5-mini, gpt-5.1, gpt-6-luna, gpt-6.1-sol ... EXCEPT *-chat-latest + major = re.match(r'gpt-(\d+)', m) + return bool(major) and int(major.group(1)) >= 5 and '-chat' not in m + + @staticmethod + def _reasoning_effort(model: str) -> str: + """Lowest reasoning_effort the model accepts. + + gpt-5 / gpt-5-mini / gpt-5-nano (and the o-series) accept 'minimal'. + gpt-5.1 and later reject 'minimal' with HTTP 400, and gpt-6-astra / + gpt-6.1-sol also reject 'none', so 'low' is the value all of them + accept. In tests it spent 0-5 reasoning tokens on a notification. + """ + m = model.lower() + if re.match(r'gpt-5(?!\.)', m) or m.startswith('o'): + return 'minimal' + return 'low' + def list_models(self) -> List[str]: """List available models for chat completions. @@ -189,22 +203,19 @@ class OpenAIProvider(AIProvider): # apply model-specific defaults. if self.base_url: payload['max_tokens'] = max_tokens - # Reasoning models (o1/o3/o4/gpt-5*, excluding *-chat-latest) use a + # Reasoning models (o1/o3/o4/gpt-5+, excluding *-chat-latest) use a # different parameter contract: max_completion_tokens instead of # max_tokens, and no temperature field. Sending the classic chat # parameters to them produces HTTP 400 Bad Request. # # They also spend output budget on internal reasoning by default, # which empties the user-visible reply when max_tokens is small - # (like the ~200 we use for notifications). reasoning_effort - # 'minimal' keeps that internal reasoning to a minimum so the - # entire budget is available for the translation, which is - # exactly what this pipeline wants. OpenAI documents 'minimal', - # 'low', 'medium', 'high' — 'minimal' is the right setting for a - # straightforward translate+explain task. + # (like the ~200 we use for notifications). The lowest + # reasoning_effort the model accepts keeps that internal reasoning + # to a minimum so the budget goes to the translation. elif self._is_reasoning_model(self.model): payload['max_completion_tokens'] = max_tokens - payload['reasoning_effort'] = 'minimal' + payload['reasoning_effort'] = self._reasoning_effort(self.model) else: payload['max_tokens'] = max_tokens payload['temperature'] = 0.3 diff --git a/AppImage/scripts/tests/test_openai_compatible_ai_models.py b/AppImage/scripts/tests/test_openai_compatible_ai_models.py index 0b7febaf..55985bd2 100644 --- a/AppImage/scripts/tests/test_openai_compatible_ai_models.py +++ b/AppImage/scripts/tests/test_openai_compatible_ai_models.py @@ -98,6 +98,19 @@ class OpenAICompatibleModelTests(unittest.TestCase): self.assertNotIn("temperature", provider.captured_payload) self.assertNotIn("max_tokens", provider.captured_payload) + def test_newer_reasoning_models_use_low_effort(self): + # gpt-5.1+ reject 'minimal'; gpt-6* were sent chat parameters before. + for model in ("gpt-5.6-luna", "gpt-6-luna", "gpt-6.1-sol"): + with self.subTest(model=model): + provider = CapturingOpenAIProvider(api_key="token", model=model) + + self.assertEqual(provider.generate("system", "user", max_tokens=123), "ok") + + self.assertEqual(provider.captured_payload["max_completion_tokens"], 123) + self.assertEqual(provider.captured_payload["reasoning_effort"], "low") + self.assertNotIn("temperature", provider.captured_payload) + self.assertNotIn("max_tokens", provider.captured_payload) + def test_runtime_ai_config_prefers_provider_specific_model(self): manager = self._manager({ "ai_enabled": "true", From acbe53ad4a6c949d38f298a15886c158b54fa2ec Mon Sep 17 00:00:00 2001 From: Manuel Escalante Date: Mon, 26 Oct 2026 18:20:05 +0100 Subject: [PATCH 2/4] chore: update verified OpenAI models to current lineup --- AppImage/config/verified_ai_models.json | 41 ++++++++++--------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/AppImage/config/verified_ai_models.json b/AppImage/config/verified_ai_models.json index 0b387613..87dc7564 100644 --- a/AppImage/config/verified_ai_models.json +++ b/AppImage/config/verified_ai_models.json @@ -35,31 +35,24 @@ }, "openai": { "models": [ - "gpt-4.1-nano-2025-04-14", - "gpt-4.1-nano", - "gpt-4.1-2025-04-14", - "gpt-4.1", - "gpt-4o-2024-11-20", - "gpt-4o-2024-05-13", - "gpt-4o-mini", - "gpt-3.5-turbo-16k", - "gpt-4o-mini-2024-07-18", - "gpt-5-nano", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-5-nano-2025-08-07", - "gpt-4.1-mini", - "gpt-4.1-mini-2025-04-14", - "gpt-3.5-turbo", - "gpt-4o", - "gpt-4o-2024-08-06", - "gpt-4", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0613" + "gpt-6-luna", + "gpt-5.6-luna", + "gpt-5.4-mini", + "gpt-5.4-mini-2026-03-17", + "gpt-6-sol", + "gpt-6.1-sol", + "gpt-5.6-terra", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.4", + "gpt-5.4-2026-03-05", + "gpt-5.6-sol", + "gpt-5.5", + "gpt-5.5-2026-04-23", + "gpt-6-astra" ], - "recommended": "gpt-4.1-nano", - "_note": "Verified 2026-09-02 with the OpenAI API — 22 of 74 tested pass. Includes stable aliases (gpt-4.1-nano, gpt-4o-mini, ...), dated snapshots for pinning (gpt-4o-2024-11-20, gpt-4.1-nano-2025-04-14) and legacy families (gpt-3.5-turbo, gpt-4) for cost-optimised use cases. Recommended kept as `gpt-4.1-nano` (stable alias, 1.39s)." + "recommended": "gpt-6-luna", + "_note": "Verified 2026-10-26 against the live API. Recommended `gpt-6-luna` (cheapest current model). Dropped every model that is retired (gpt-4.1-nano, gpt-3.5-turbo, gpt-4, gpt-4-turbo, ...) or has a shutdown date (gpt-4.1, gpt-4o, gpt-4o-mini, gpt-5-nano, gpt-5.1, gpt-5.4-nano)." }, "anthropic": { "models": [ From a4148c66168bd7455bfa3997e4de8a49f4323454 Mon Sep 17 00:00:00 2001 From: Manuel Escalante Date: Mon, 26 Oct 2026 18:20:05 +0100 Subject: [PATCH 3/4] docs: update OpenAI model examples --- web/app/[locale]/docs/monitor/notifications/page.tsx | 2 +- web/messages/de/docs/monitor/ai-assistant.json | 4 ++-- web/messages/en/docs/monitor/ai-assistant.json | 4 ++-- web/messages/es/docs/monitor/ai-assistant.json | 4 ++-- web/messages/fr/docs/monitor/ai-assistant.json | 4 ++-- web/messages/it/docs/monitor/ai-assistant.json | 4 ++-- web/messages/pt/docs/monitor/ai-assistant.json | 4 ++-- web/messages/sk/docs/monitor/ai-assistant.json | 4 ++-- web/messages/sv/docs/monitor/ai-assistant.json | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/web/app/[locale]/docs/monitor/notifications/page.tsx b/web/app/[locale]/docs/monitor/notifications/page.tsx index c9f092ee..293ceb86 100644 --- a/web/app/[locale]/docs/monitor/notifications/page.tsx +++ b/web/app/[locale]/docs/monitor/notifications/page.tsx @@ -814,7 +814,7 @@ curl -H "Authorization: Bearer " \\ curl -X POST http://:8008/api/notifications/test-ai \\ -H "Authorization: Bearer " \\ -H "Content-Type: application/json" \\ - -d '{"provider":"openai","api_key":"sk-...","model":"gpt-4o-mini"}'`} + -d '{"provider":"openai","api_key":"sk-...","model":"gpt-6-luna"}'`} className="my-4" /> diff --git a/web/messages/de/docs/monitor/ai-assistant.json b/web/messages/de/docs/monitor/ai-assistant.json index 5892455f..56439afe 100644 --- a/web/messages/de/docs/monitor/ai-assistant.json +++ b/web/messages/de/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Industriestandard. Sehr genau und weit verbreitet.", "items": [ "API-Schlüssel: platform.openai.com/api-keys", - "Beispiele aus der mitgelieferten Modellliste: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Mitgelieferte Empfehlung: gpt-4.1-nano. Der Adapter unterstützt auch besondere Anforderungen von Reasoning-Modellen; die Liste enthält gpt-5-nano. Diese Modelle sind daher nicht pauschal ausgeschlossen." + "Beispiele aus der mitgelieferten Modellliste: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Mitgelieferte Empfehlung: gpt-6-luna, das günstigste aktuelle Modell. Alle Modelle der Liste sind Reasoning-Modelle; der Adapter sendet jedem die Parameter, die es akzeptiert." ], "baseUrlTitle": "OpenAI-kompatible Basis URL", "baseUrlBody": "Der OpenAI-Anbieter akzeptiert eine eigene Basis-URL für OpenAI-kompatible Endpunkte, etwa BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI und Fireworks AI, die das Format /v1/chat/completions unterstützen. Tragen Sie die URL im OpenAI-Bereich neben dem API-Schlüssel ein." diff --git a/web/messages/en/docs/monitor/ai-assistant.json b/web/messages/en/docs/monitor/ai-assistant.json index 39031290..12af6eaa 100644 --- a/web/messages/en/docs/monitor/ai-assistant.json +++ b/web/messages/en/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Industry standard. Very accurate and widely used.", "items": [ "API key: platform.openai.com/api-keys", - "Examples from the bundled model list: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Bundled recommendation: gpt-4.1-nano. The adapter also handles model-specific requirements for reasoning models; the bundled list includes gpt-5-nano, so reasoning models are not universally excluded." + "Examples from the bundled model list: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Bundled recommendation: gpt-6-luna, the most economical current model. Every model in the bundled list is a reasoning model; the adapter sends each one the parameters it accepts." ], "baseUrlTitle": "OpenAI-compatible base URL", "baseUrlBody": "The OpenAI provider also accepts a custom Base URL, which lets you point the Monitor at any OpenAI-compatible endpoint. Confirmed to work with BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI, Fireworks AI, and any other service that speaks the OpenAI /v1/chat/completions dialect. Set the URL in the OpenAI tab next to the API key field." diff --git a/web/messages/es/docs/monitor/ai-assistant.json b/web/messages/es/docs/monitor/ai-assistant.json index 802c3aa5..f3eaf24b 100644 --- a/web/messages/es/docs/monitor/ai-assistant.json +++ b/web/messages/es/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Estándar de la industria. Muy preciso y ampliamente usado.", "items": [ "Clave API: platform.openai.com/api-keys", - "Ejemplos de la lista de modelos incluida: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Recomendación incluida: gpt-4.1-nano. El adaptador también gestiona requisitos específicos de los modelos de razonamiento; la lista incluye gpt-5-nano, por lo que no se excluyen todos esos modelos." + "Ejemplos de la lista de modelos incluida: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Recomendación incluida: gpt-6-luna, el modelo actual más económico. Todos los modelos de la lista son de razonamiento; el adaptador envía a cada uno los parámetros que acepta." ], "baseUrlTitle": "Base URL compatible con OpenAI", "baseUrlBody": "El proveedor OpenAI también acepta una Base URL personalizada, que te permite apuntar el Monitor a cualquier endpoint compatible con OpenAI. Confirmado que funciona con BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI, Fireworks AI y cualquier otro servicio que hable el dialecto OpenAI /v1/chat/completions. Configura la URL en la pestaña OpenAI junto al campo de API key." diff --git a/web/messages/fr/docs/monitor/ai-assistant.json b/web/messages/fr/docs/monitor/ai-assistant.json index b7a334e2..bff43958 100644 --- a/web/messages/fr/docs/monitor/ai-assistant.json +++ b/web/messages/fr/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Norme de l'industrie. Très précis et largement utilisé.", "items": [ "Clé API : platform.openai.com/api-keys", - "Exemples de la liste de modèles fournie : gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Recommandation fournie : gpt-4.1-nano. L’adaptateur gère aussi les exigences des modèles de raisonnement ; la liste inclut gpt-5-nano, ils ne sont donc pas tous exclus." + "Exemples de la liste de modèles fournie : gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Recommandation fournie : gpt-6-luna, le modèle actuel le plus économique. Tous les modèles de la liste sont des modèles de raisonnement ; l’adaptateur envoie à chacun les paramètres qu’il accepte." ], "baseUrlTitle": "URL de base compatible OpenAI", "baseUrlBody": "Le fournisseur OpenAI accepte une URL de base personnalisée pour les points d’accès compatibles : BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI, Fireworks AI et les services utilisant le format /v1/chat/completions. Saisissez l’URL dans l’onglet OpenAI près de la clé API." diff --git a/web/messages/it/docs/monitor/ai-assistant.json b/web/messages/it/docs/monitor/ai-assistant.json index 32db8cc5..d547224f 100644 --- a/web/messages/it/docs/monitor/ai-assistant.json +++ b/web/messages/it/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Standard di settore. Molto accurato e ampiamente usato.", "items": [ "Chiave API: platform.openai.com/api-keys", - "Esempi dall’elenco di modelli incluso: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Raccomandazione inclusa: gpt-4.1-nano. L’adattatore gestisce anche i requisiti dei modelli di ragionamento; la lista include gpt-5-nano, quindi questi modelli non sono esclusi in generale." + "Esempi dall’elenco di modelli incluso: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Raccomandazione inclusa: gpt-6-luna, il modello attuale più economico. Tutti i modelli dell’elenco sono modelli di ragionamento; l’adattatore invia a ciascuno i parametri che accetta." ], "baseUrlTitle": "URL di base compatibile con OpenAI", "baseUrlBody": "Il provider OpenAI accetta un URL di base personalizzato per endpoint compatibili, come BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI e Fireworks AI, che usano il formato /v1/chat/completions. Imposta l’URL nella scheda OpenAI accanto alla chiave API." diff --git a/web/messages/pt/docs/monitor/ai-assistant.json b/web/messages/pt/docs/monitor/ai-assistant.json index 2c9de7df..ef12750c 100644 --- a/web/messages/pt/docs/monitor/ai-assistant.json +++ b/web/messages/pt/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Padrão industrial. Muito preciso e amplamente utilizado.", "items": [ "Chave API: platform.openai.com/api-keys", - "Exemplos da lista de modelos incluída: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Recomendação incluída: gpt-4.1-nano. O adaptador também trata requisitos dos modelos de raciocínio; a lista inclui gpt-5-nano, pelo que esses modelos não são excluídos em geral." + "Exemplos da lista de modelos incluída: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Recomendação incluída: gpt-6-luna, o modelo atual mais económico. Todos os modelos da lista são de raciocínio; o adaptador envia a cada um os parâmetros que aceita." ], "baseUrlTitle": "URL base compatível com OpenAI", "baseUrlBody": "O fornecedor OpenAI aceita um URL base personalizado para serviços compatíveis, como BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI e Fireworks AI, que utilizam o formato /v1/chat/completions. Defina o URL no separador OpenAI junto à chave API." diff --git a/web/messages/sk/docs/monitor/ai-assistant.json b/web/messages/sk/docs/monitor/ai-assistant.json index b76a7f5d..b0774ecd 100644 --- a/web/messages/sk/docs/monitor/ai-assistant.json +++ b/web/messages/sk/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Priemyselná norma. Veľmi presné a široko používané.", "items": [ "Kľúč API: platform.openai.com/api-keys", - "Príklady z dodaného zoznamu modelov: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Dodané odporúčanie: gpt-4.1-nano. Adaptér spracúva aj osobitné požiadavky modelov uvažovania; zoznam obsahuje gpt-5-nano, takže tieto modely nie sú všeobecne vylúčené." + "Príklady z dodaného zoznamu modelov: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Dodané odporúčanie: gpt-6-luna, najúspornejší aktuálny model. Všetky modely v zozname sú modely uvažovania; adaptér každému posiela parametre, ktoré prijíma." ], "baseUrlTitle": "Základné URL kompatibilné s OpenAI", "baseUrlBody": "Poskytovateľ OpenAI prijíma vlastnú Základnú URL pre kompatibilné služby, napríklad BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI a Fireworks AI, ktoré používajú formát /v1/chat/completions. URL nastavte na karte OpenAI vedľa kľúča API." diff --git a/web/messages/sv/docs/monitor/ai-assistant.json b/web/messages/sv/docs/monitor/ai-assistant.json index 3eb83ece..c967b843 100644 --- a/web/messages/sv/docs/monitor/ai-assistant.json +++ b/web/messages/sv/docs/monitor/ai-assistant.json @@ -130,8 +130,8 @@ "tagline": "Industristandard. Mycket exakt och allmänt används.", "items": [ "API-nyckel: platform.openai.com/api-keys", - "Exempel från den medföljande modellistan: gpt-4.1-nano, gpt-4.1-mini, gpt-4o-mini, gpt-4.1, gpt-4o, gpt-5-nano.", - "Medföljande rekommendation: gpt-4.1-nano. Adaptern hanterar även särskilda krav för resonemangsmodeller; listan innehåller gpt-5-nano, så sådana modeller är inte generellt uteslutna." + "Exempel från den medföljande modellistan: gpt-6-luna, gpt-5.6-luna, gpt-5.4-mini, gpt-6-sol, gpt-5.5.", + "Medföljande rekommendation: gpt-6-luna, den mest ekonomiska aktuella modellen. Alla modeller i listan är resonemangsmodeller; adaptern skickar varje modell de parametrar den accepterar." ], "baseUrlTitle": "OpenAI-kompatibel bas URL", "baseUrlBody": "OpenAI-leverantören accepterar en egen Bas-URL för kompatibla tjänster som BytePlus / ByteDance (Kimi K2.5), LocalAI, LM Studio, vLLM, Together AI och Fireworks AI, som stöder formatet /v1/chat/completions. Ange URL:en på OpenAI-fliken bredvid API-nyckeln." From e9b8fd28db36c23ddba020e3ef4b2af4b3563acc Mon Sep 17 00:00:00 2001 From: Manuel Escalante Date: Mon, 26 Oct 2026 19:02:32 +0100 Subject: [PATCH 4/4] test: cover chat contract for classic OpenAI models --- .../tests/test_openai_compatible_ai_models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/AppImage/scripts/tests/test_openai_compatible_ai_models.py b/AppImage/scripts/tests/test_openai_compatible_ai_models.py index 55985bd2..36d63258 100644 --- a/AppImage/scripts/tests/test_openai_compatible_ai_models.py +++ b/AppImage/scripts/tests/test_openai_compatible_ai_models.py @@ -111,6 +111,18 @@ class OpenAICompatibleModelTests(unittest.TestCase): self.assertNotIn("temperature", provider.captured_payload) self.assertNotIn("max_tokens", provider.captured_payload) + def test_classic_chat_models_keep_chat_contract(self): + for model in ("gpt-4.1", "gpt-4o-mini"): + with self.subTest(model=model): + provider = CapturingOpenAIProvider(api_key="token", model=model) + + self.assertEqual(provider.generate("system", "user", max_tokens=123), "ok") + + self.assertEqual(provider.captured_payload["max_tokens"], 123) + self.assertEqual(provider.captured_payload["temperature"], 0.3) + self.assertNotIn("reasoning_effort", provider.captured_payload) + self.assertNotIn("max_completion_tokens", provider.captured_payload) + def test_runtime_ai_config_prefers_provider_specific_model(self): manager = self._manager({ "ai_enabled": "true",