From 4c119a109eca64d2a43338472ffa18e7468a904d Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Tue, 12 May 2026 00:10:27 +0200 Subject: [PATCH] fix(llm): exclude OpenRouter Novita provider via extra_body (Qwen 2.x rotto) OpenRouter ha cominciato a routare qwen/qwen-2.5-72b-instruct attraverso Novita, che rifiuta /chat/completions con HTTP 400: "model: qwen/qwen-2.5-72b-instruct does not support endpoint: completions". Aggiunto LLMClient.DEFAULT_PROVIDER_IGNORE = ("Novita",) e flag opzionale provider_ignore al constructor. Le request includono ora extra_body={"provider": {"ignore": ["Novita"]}} per default. Verificato manualmente: con ignore Novita, qwen-2.5-72b risponde normalmente via altri provider (DeepInfra, Together, ecc). Mantenuta backward compat: passando provider_ignore=() si disabilita l'esclusione (utile per test o se in futuro Novita si sistema). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/multi_swarm/llm/client.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/multi_swarm/llm/client.py b/src/multi_swarm/llm/client.py index 0af6913..1ea0183 100644 --- a/src/multi_swarm/llm/client.py +++ b/src/multi_swarm/llm/client.py @@ -1,6 +1,7 @@ from __future__ import annotations from dataclasses import dataclass +from typing import Any import openai from openai import OpenAI @@ -44,6 +45,10 @@ class CompletionResult: class LLMClient: + # Provider OpenRouter da escludere di default. Novita rifiuta /completions + # endpoint per modelli Qwen 2.x — vedi bug 2026-05-12. + DEFAULT_PROVIDER_IGNORE: tuple[str, ...] = ("Novita",) + def __init__( self, openrouter_api_key: str, @@ -53,6 +58,7 @@ class LLMClient: model_tier_c: str = MODEL_TIER_C, model_tier_d: str = MODEL_TIER_D, openrouter_base_url: str = OPENROUTER_BASE_URL, + provider_ignore: tuple[str, ...] | None = None, ) -> None: self.model_tier_s = model_tier_s self.model_tier_a = model_tier_a @@ -60,6 +66,9 @@ class LLMClient: self.model_tier_c = model_tier_c self.model_tier_d = model_tier_d self.openrouter_base_url = openrouter_base_url + self._provider_ignore = ( + provider_ignore if provider_ignore is not None else self.DEFAULT_PROVIDER_IGNORE + ) self._tier_models: dict[ModelTier, str] = { ModelTier.S: model_tier_s, ModelTier.A: model_tier_a, @@ -83,6 +92,9 @@ class LLMClient: max_tokens: int = 2000, ) -> CompletionResult: model = self._tier_models[genome.model_tier] + extra_body: dict[str, Any] = {} + if self._provider_ignore: + extra_body["provider"] = {"ignore": list(self._provider_ignore)} resp = self._client.chat.completions.create( model=model, messages=[ @@ -92,6 +104,7 @@ class LLMClient: temperature=genome.temperature, top_p=genome.top_p, max_tokens=max_tokens, + extra_body=extra_body or None, ) if not resp.choices or resp.choices[0].message.content is None: raise EmptyCompletionError(f"empty response from {model}")