feat: DeepSeek V4 Flash primario, Big Pickle fallback
test / test (push) Has been cancelled

- deepseek-v4-flash su opencode/go come modello primario
- big-pickle su opencode/zen come fallback (gratuito)
- Fallback automatico su censura "User Safety" o errore HTTP
- Aggiornati .env, config.py, start.sh, docker-compose.yml
This commit is contained in:
truetype committed 2026-07-01 11:13:29 +02:00
1 parent ffeb199ef5
commit 90ba835868
4 files changed
+38 -10

No files matched your search

+25 -5
View File
@@ -84,13 +84,15 @@ class OpenRouterProvider(LLMProvider):
class LocalProvider(LLMProvider):
def __init__(self, base_url: str, model: str, api_key: str = "", timeout: int = 60):
def __init__(self, base_url: str, model: str, api_key: str = "",
timeout: int = 60, fallbacks: list[tuple[str, str]] | None = None):
self.base_url = base_url.rstrip("/")
self.model = model
self.api_key = api_key
self.timeout = timeout
self.fallbacks = fallbacks or []
def ask(self, prompt: str, system: str = "") -> str:
def _call(self, base_url: str, model: str, prompt: str, system: str) -> str:
messages = []
if system:
messages.append({"role": "system", "content": system})
@@ -100,10 +102,10 @@ class LocalProvider(LLMProvider):
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
body = {"model": self.model, "messages": messages, "stream": False}
body = {"model": model, "messages": messages, "stream": False}
resp = httpx.post(
f"{self.base_url}/chat/completions",
f"{base_url}/chat/completions",
json=body,
headers=headers,
timeout=self.timeout,
@@ -112,6 +114,21 @@ class LocalProvider(LLMProvider):
data = resp.json()
return data["choices"][0]["message"]["content"]
def ask(self, prompt: str, system: str = "") -> str:
candidates = [(self.base_url, self.model)] + self.fallbacks
last_err = None
for base_url, model in candidates:
try:
text = self._call(base_url, model, prompt, system)
# Rileva risposte di censura del modello gratuito
if "User Safety" in text and "safe" in text.lower():
raise ValueError(f"Modello '{model}' ha censurato la risposta: {text[:60]}")
return text
except Exception as e:
last_err = e
continue
raise last_err # type: ignore
_registry: dict[str, Any] = {
"ollama": OllamaProvider,
@@ -136,6 +153,9 @@ def create_provider(cfg: ProviderConfig | None = None) -> LLMProvider:
raise ValueError("DCC_OPENROUTER_KEY non configurata")
return cls(key, cfg.openrouter_model, cfg.timeout, cfg.openrouter_free_fallback)
elif cfg.kind == "local":
return cls(cfg.local_base_url, cfg.local_model, cfg.local_api_key, cfg.timeout)
fallbacks = []
if cfg.local_fallback_url and cfg.local_fallback_model:
fallbacks.append((cfg.local_fallback_url, cfg.local_fallback_model))
return cls(cfg.local_base_url, cfg.local_model, cfg.local_api_key, cfg.timeout, fallbacks=fallbacks)
else:
raise ValueError(f"Provider non implementato: {cfg.kind}")