feat(ui): interaktywny wybor modelu u dostawcy
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 11m14s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 10m19s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 46s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 27s
build / build (push) Successful in 1m18s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m2s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 36s
Testy / Kontrola składni wszystkich warstw (push) Successful in 23s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 11m14s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 10m19s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 46s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 27s
build / build (push) Successful in 1m18s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m2s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 36s
Testy / Kontrola składni wszystkich warstw (push) Successful in 23s
Uzytkownik wybiera nie tylko dostawce, ale konkretny model — Fable czy Opus u Anthropica, gpt-4o-mini czy gpt-5 u OpenAI, cokolwiek ma pobrane lokalnie. - app/llm/catalog.py: podpowiedzi modeli per dostawca wraz z oknem kontekstu, nadpisywalne przez <DOSTAWCA>_MODELS; GET /llm/models wystawia je dla UI. - Pole modelu w UI jest TEKSTOWE z datalista, nie zamknietym <select> — konto moze miec dostep do modeli, o ktorych kod nie wie, a nowe wychodza szybciej, niz aktualizuje sie katalog. Puste pole = model domyslny dostawcy. - static/models.js: zmiana dostawcy przelacza podpowiedzi, podmienia placeholder na model domyslny i pokazuje okno kontekstu wybranego modelu. - build_provider(name, model) — model z zadania wygrywa nad konfiguracja. WAZNE (znalezione przy tescie e2e): liczenie budzetu „maksymalny kontekst modelu" szlo przez build_provider(), ktory WYMAGA klucza API — bez klucza budzet cicho spadal do wartosci zapasowej i byl identyczny dla wszystkich modeli Anthropic. Budzet zalezy wylacznie od okna kontekstu, wiec doszlo resolve_model(), ktore rozwiazuje nazwe modelu bez budowania dostawcy. Teraz budzet realnie sie rozni: Opus/Fable 3,48 mln znakow, Haiku 536 tys., gpt-4o-mini 438 tys., llama3.1 8 tys. Pewnosc danych w katalogu: modele Anthropic pochodza z oficjalnej dokumentacji API (okna i limity zgodne z limits.py); modele OpenAI to podpowiedzi, ktorych nie weryfikowalem; lokalne zaleza od tego, co masz pobrane. Testy: 174 passed / 1 skipped (logika) + 17 (prezentacja). Nowy test strukturalny pilnuje, ze KAZDE wywolanie w dol niesie wybrany model i dostawce — dokladnie ta klasa bledu zlapala brakujacy parametr przy horoskopie okresowym. Zweryfikowane w przegladarce: przelaczanie dostawcy podmienia liste modeli. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #18.
This commit is contained in:
@@ -60,3 +60,52 @@ def test_auth_headers_helper_is_lazy():
|
||||
os.environ["INTERNAL_TOKEN"] = old
|
||||
else:
|
||||
os.environ.pop("INTERNAL_TOKEN", None)
|
||||
|
||||
|
||||
# --------------------------------------------- wybor modelu musi dojsc do logiki
|
||||
# Ta sama klasa bledu co przy tokenie: dokladajac nowa sciezke latwo zapomniec
|
||||
# przekazac parametr, a objaw (cichy powrot do modelu domyslnego) jest niewidoczny.
|
||||
|
||||
def _calls_to(path_fragment: str) -> list[int]:
|
||||
"""Linie wywolan logic.<metoda>(...) w handlerach prezentacji."""
|
||||
main = CLIENT.parent.parent / "main.py"
|
||||
tree = ast.parse(main.read_text(encoding="utf-8"))
|
||||
out = []
|
||||
for node in ast.walk(tree):
|
||||
if (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)
|
||||
and node.func.attr == path_fragment
|
||||
and isinstance(node.func.value, ast.Name) and node.func.value.id == "logic"):
|
||||
out.append(node.lineno)
|
||||
return out
|
||||
|
||||
|
||||
def _has_kwarg(main_src: str, lineno: int, name: str) -> bool:
|
||||
tree = ast.parse(main_src)
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Call) and node.lineno == lineno:
|
||||
return any(kw.arg == name for kw in node.keywords)
|
||||
return False
|
||||
|
||||
|
||||
def test_every_llm_call_passes_selected_model():
|
||||
main = CLIENT.parent.parent / "main.py"
|
||||
src = main.read_text(encoding="utf-8")
|
||||
missing = []
|
||||
for method in ("prompt", "horoscope"):
|
||||
for line in _calls_to(method):
|
||||
if not _has_kwarg(src, line, "model"):
|
||||
missing.append(f"main.py:{line} logic.{method}()")
|
||||
assert not missing, (
|
||||
"Wywołania bez wybranego modelu — po cichu użyją domyślnego: " + ", ".join(missing)
|
||||
)
|
||||
|
||||
|
||||
def test_every_llm_call_passes_provider():
|
||||
main = CLIENT.parent.parent / "main.py"
|
||||
src = main.read_text(encoding="utf-8")
|
||||
missing = []
|
||||
for method in ("prompt", "horoscope"):
|
||||
for line in _calls_to(method):
|
||||
if not _has_kwarg(src, line, "provider"):
|
||||
missing.append(f"main.py:{line} logic.{method}()")
|
||||
assert not missing, "Wywołania bez dostawcy: " + ", ".join(missing)
|
||||
|
||||
Reference in New Issue
Block a user