Fix: UI wählt empfohlenes deepseek-Default vor statt gedrosseltem Free-Modell
Ursache des 429-'es hängt': Die UI wählte beim Laden das erste Top-Free-Modell vor; OpenRouter-Free-Modelle sind hart gedrosselt -> Lauf lief in 5x Retry + Abbruch. Jetzt: - /api/modelle stellt den Default (deepseek-v4-flash) als 'empfohlen' voran. - UI wählt das empfohlene Modell vor, markiert Free als 'oft gedrosselt' und stellt ein gemerktes Free-Modell NICHT automatisch wieder her. - Server-seitiges Fortschritts-Logging ([Stufe 2] Batch X/Y) fürs Live-Log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
62f5af533e
commit
aa60331f7f
2 changed files with 46 additions and 7 deletions
|
|
@ -48,16 +48,33 @@ def index() -> str:
|
|||
|
||||
@app.get("/api/modelle")
|
||||
def api_modelle(q: str = "") -> list[dict]:
|
||||
"""Modell-Liste für das Dropdown: Suche oder Top-Free. 'Aktualisieren' = neu rufen."""
|
||||
"""Modell-Liste fürs Dropdown. Ohne Suche: das EMPFOHLENE Default-Modell
|
||||
(günstig, nicht gedrosselt) zuerst, dann die Top-Free.
|
||||
|
||||
Hintergrund: Free-Modelle sind bei OpenRouter hart gedrosselt (429). Würde
|
||||
die UI eines davon vorwählen, läuft der erste Lauf ins Rate-Limit. Deshalb
|
||||
steht der Default (deepseek-v4-flash) vorne und wird vorgewählt.
|
||||
"""
|
||||
from .kategorisieren import _DEFAULT_MODELLE
|
||||
from .modelle import lade_modelle, suche, top_free
|
||||
|
||||
try:
|
||||
alle = lade_modelle()
|
||||
except Exception as e: # Netz/SSL -> ehrlich melden, nicht raten
|
||||
raise HTTPException(status_code=502, detail=f"Modelle nicht abrufbar: {e}")
|
||||
treffer = suche(alle, q) if q else top_free(alle, 8)
|
||||
|
||||
default_id = _DEFAULT_MODELLE.get("openrouter")
|
||||
if q:
|
||||
treffer = suche(alle, q)
|
||||
else:
|
||||
default_mi = next((m for m in alle if m.id == default_id), None)
|
||||
top = [m for m in top_free(alle, 8) if m.id != default_id]
|
||||
treffer = ([default_mi] if default_mi else []) + top
|
||||
return [
|
||||
{"id": m.id, "frei": m.frei, "tools": m.tools, "context": m.context}
|
||||
{
|
||||
"id": m.id, "frei": m.frei, "tools": m.tools, "context": m.context,
|
||||
"empfohlen": m.id == default_id,
|
||||
}
|
||||
for m in treffer[:25]
|
||||
]
|
||||
|
||||
|
|
@ -223,10 +240,17 @@ def _run_kategorisieren(job_id, plz, fetch, modell, anbieter, key) -> None:
|
|||
# Tatsächlich genutztes Modell (Default je Anbieter aufgelöst) -- für die
|
||||
# sichtbare Herkunft im Ergebnis.
|
||||
modell_genutzt = getattr(kt, "_modell", modell)
|
||||
print(
|
||||
f"[Stufe 2] start · PLZ {plz} · {anbieter}/{modell_genutzt} · "
|
||||
f"{len(fetch.angebote)} Angebote",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
def fort(done, total):
|
||||
job["done"] = done
|
||||
job["total"] = total
|
||||
if done == total or done % 5 == 0: # nicht jede Batch -> Log lesbar
|
||||
print(f"[Stufe 2] PLZ {plz} · Batch {done}/{total}", flush=True)
|
||||
|
||||
kat = kategorisiere(list(fetch.angebote), kt, fortschritt=fort)
|
||||
|
||||
|
|
@ -235,9 +259,14 @@ def _run_kategorisieren(job_id, plz, fetch, modell, anbieter, key) -> None:
|
|||
)
|
||||
job["status"] = "fertig"
|
||||
_ergebnis_cache[(plz, anbieter, modell)] = job["ergebnis"]
|
||||
print(
|
||||
f"[Stufe 2] fertig · PLZ {plz} · {job['ergebnis']['unsicher']} unsicher",
|
||||
flush=True,
|
||||
)
|
||||
except AbbruchFehler as e:
|
||||
job["status"] = "fehler"
|
||||
job["fehler"] = e.als_text()
|
||||
print(f"[Stufe 2] ABBRUCH · PLZ {plz} · {e.schwelle}: {e.ursache}", flush=True)
|
||||
except Exception as e: # nichts verstecken -- ehrliche Fehlermeldung
|
||||
job["status"] = "fehler"
|
||||
job["fehler"] = f"Unerwarteter Fehler: {e}"
|
||||
|
|
|
|||
|
|
@ -383,17 +383,27 @@ async function ladeModelle(q=""){
|
|||
for (const m of liste){
|
||||
const o = document.createElement("option");
|
||||
o.value = m.id;
|
||||
const tag = ab === "ollama" ? "lokal" : (m.frei ? "FREE" : "paid");
|
||||
o.dataset.frei = m.frei ? "1" : "0";
|
||||
o.dataset.empf = m.empfohlen ? "1" : "0";
|
||||
let tag;
|
||||
if (ab === "ollama") tag = "lokal";
|
||||
else if (m.empfohlen) tag = "★ empfohlen";
|
||||
else tag = m.frei ? "FREE · oft gedrosselt" : "paid";
|
||||
const tools = m.tools ? "" : " ⚠ kein tool-calling";
|
||||
o.textContent = `${m.id} [${tag}]${tools}`;
|
||||
if (!m.tools) o.disabled = true;
|
||||
sel.appendChild(o);
|
||||
}
|
||||
// gemerktes Modell für diesen Anbieter bevorzugen, sonst erstes brauchbares
|
||||
// Vorauswahl: gemerktes Modell -- aber bei OpenRouter NICHT automatisch ein
|
||||
// gedrosseltes Free-Modell (führt zu 429); dann lieber das empfohlene.
|
||||
const opts = [...sel.options];
|
||||
const gemerkt = gemerktesModell(ab);
|
||||
if (gemerkt && opts.some(o => o.value === gemerkt && !o.disabled)){
|
||||
sel.value = gemerkt;
|
||||
const gemerktOpt = gemerkt ? opts.find(o => o.value === gemerkt && !o.disabled) : null;
|
||||
const empfOpt = opts.find(o => o.dataset.empf === "1" && !o.disabled);
|
||||
if (gemerktOpt && !(ab !== "ollama" && gemerktOpt.dataset.frei === "1")){
|
||||
sel.value = gemerktOpt.value;
|
||||
} else if (empfOpt){
|
||||
sel.value = empfOpt.value;
|
||||
} else {
|
||||
const ok = opts.find(o => !o.disabled && o.value);
|
||||
if (ok) sel.value = ok.value;
|
||||
|
|
|
|||
Loading…
Reference in a new issue