"""Katalog modeli do wyboru w UI (LOG-31). To są **podpowiedzi**, nie zamknięta lista. Pole modelu w UI jest tekstowe z datalistą, więc można wpisać dowolny identyfikator — konto może mieć dostęp do modeli, których tu nie ma, a nowe wychodzą szybciej, niż aktualizuje się kod. Puste pole = model domyślny dostawcy. Uwaga o pewności danych: * modele **Anthropic** pochodzą z oficjalnej dokumentacji API (okna kontekstu i limity wyjścia zgadzają się z `app/llm/limits.py`); * modele **OpenAI** to podpowiedzi — nie weryfikowałem ich katalogu, więc traktuj je jako wygodę, a nie źródło prawdy; * modele **lokalne** zależą wyłącznie od tego, co masz pobrane w Ollamie/vLLM. Katalog można nadpisać/rozszerzyć zmienną `_MODELS` (lista po przecinku), np. `OPENAI_MODELS="gpt-5,gpt-4o"`. """ from __future__ import annotations import os from app.llm.limits import limits_for # dostawca -> [(id modelu, krótki opis dla człowieka)] _CATALOG: dict[str, list[tuple[str, str]]] = { "anthropic": [ ("claude-opus-4-8", "Opus 4.8 — domyślny, bardzo zdolny, 1M kontekstu"), ("claude-fable-5", "Fable 5 — najbardziej zdolny, do najtrudniejszych zadań"), ("claude-sonnet-5", "Sonnet 5 — szybszy i tańszy, jakość blisko Opusa"), ("claude-opus-4-7", "Opus 4.7 — poprzednia generacja Opusa"), ("claude-haiku-4-5", "Haiku 4.5 — najszybszy i najtańszy, mniejsze okno"), ], "openai": [ ("gpt-4o-mini", "GPT-4o mini — tani i szybki"), ("gpt-4o", "GPT-4o"), ("gpt-5", "GPT-5 — jeśli Twoje konto ma dostęp"), ("gpt-4.1", "GPT-4.1"), ("gpt-4.1-mini", "GPT-4.1 mini"), ], "local": [ ("llama3.1:8b", "Llama 3.1 8B"), ("llama3.2", "Llama 3.2"), ("qwen2.5", "Qwen 2.5 — większe okno kontekstu"), ("mistral", "Mistral"), ], } def models_for(provider: str) -> list[dict]: """Podpowiedzi modeli dla dostawcy, wraz z oknem kontekstu. Okno kontekstu podajemy, bo wprost przekłada się na opcję „maksymalny kontekst modelu" — użytkownik widzi, na ile budżetu promptu może liczyć. """ override = os.getenv(f"{provider.upper()}_MODELS", "").strip() if override: entries = [(m.strip(), "") for m in override.split(",") if m.strip()] else: entries = _CATALOG.get(provider, []) out = [] for model_id, label in entries: context_window, max_output = limits_for(provider, model_id) out.append({ "id": model_id, "label": label or model_id, "context_window": context_window, "max_output": max_output, }) return out def catalog() -> dict[str, list[dict]]: """Pełny katalog dla UI — jedno żądanie zamiast trzech.""" return {provider: models_for(provider) for provider in ("local", "anthropic", "openai")}