feat(llm): horoskop powstaje zawsze — kontynuacja, okna kontekstu, budzet max #18

Merged
gitea merged 2 commits from feat/llm-context-budget into master 2026-07-22 19:40:57 +00:00
Owner

Zawiera i zastepuje PR #17 (ten sam obszar — pusta odpowiedz modelu).

Dlaczego Anthropic zwracal pustke — przyczyna potwierdzona w dokumentacji API

Domyslnym modelem byl claude-sonnet-5, ktory przy pominietym parametrze thinking
wlacza myslenie adaptacyjne, a thinking.display domyslnie jest "omitted". Tokeny
myslenia licza sie do max_tokens — wiec przy LLM_MAX_TOKENS=2000 cala tura wychodzila
jako bloki thinking z pustym tekstem
. Parser filtrowal type == "text" i oddawal pusty string.

Opus 4.8 bez thinking nie mysli, wiec tam objaw by nie wystapil — stad „pustke dostaje
na anthropicu", a nie wszedzie.

Gwarancja: horoskop powstaje zawsze (wszyscy trzej dostawcy)

generate() to teraz petla, nie pojedynczy strzal:

  1. tura z policzonym limitem wyjscia,
  2. urwana na limicie → dopisz ture „kontynuuj" w tej samej rozmowie i sklej tekst,
  3. tura z samego myslenia → traktowana jak urwana, nie jak pustka,
  4. pusta i nie urwana → jedna proba z podpowiedzia, dopiero potem blad z diagnostyka.

Realizuje to wprost Twoja sugestie „podzielic prompt na serie pytan i odpowiedzi w ramach
jednej rozmowy" — i jest odporniejsze niz jedno wielkie zadanie, bo kazda tura miesci sie
w timeoucie HTTP, a dlugosc odpowiedzi przestaje byc ograniczona jedna tura.

Detal, ktory latwo przeoczyc: kontynuacja konczy sie tura uzytkownika. Claude
odrzuca prefill w turze asystenta bledem 400, wiec naiwne „dopisz asystenta i wyslij"
by nie zadzialalo. Jest na to osobny test.

thinking jest teraz konfigurowany jawnie: adaptive + effort: high (jakosc tekstu),
z wylacznikiem ANTHROPIC_THINKING=off.

Okna kontekstu i rezerwa na odpowiedz (app/llm/limits.py)

  • tabela okien i limitow wyjscia per model + nadpisanie z ENV (<DOSTAWCA>_CONTEXT_WINDOW,
    <DOSTAWCA>_MAX_OUTPUT) — modele wychodza szybciej, niz aktualizuje sie kod;
  • zawsze rezerwujemy miejsce na odpowiedz: budzet promptu = okno − wyjscie − margines,
    nigdy odwrotnie;
  • Anthropic liczy tokeny dokladnie (/v1/messages/count_tokens), reszta szacuje —
    od tego zalezy, czy w oknie w ogole zostanie miejsce na horoskop.

Suwak i ostrzezenie

Doszly poziomy „bardzo obszerny (~120 tys.)" i „maksymalny kontekst modelu" — ten
drugi liczy sie z okna wybranego modelu po odjeciu rezerwy (Opus 4.8: 870 000 tokenow
promptu; model lokalny: 2 096 — bo ma male okno).

Powyzej 90 tys. tokenow pojawia sie ostrzezenie, ale wyslanie jest nadal mozliwe
i okno odpowiedzi zostaje pelne — dokladnie jak prosiles. Przy wyniku widac plan tokenow,
liczbe tur i ostrzezenia.

Domyslny model Anthropic zmieniony na claude-opus-4-8.

Weryfikacja

  • 170 passed / 1 skipped (logika) + 15 (prezentacja). Nowe testy: sklejanie
    kontynuacji, brak prefillu asystenta, tura z samego myslenia, rezerwa na odpowiedz,
    prog ostrzezenia, nadpisania z ENV.
  • E2E na atrapie Anthropica odtwarzajacej zgloszony objaw (tura 1 = samo myslenie):
    3 tury, obie czesci tekstu obecne, znacznik konca odciety.

Czego swiadomie NIE zrobilem

Trybu agenta — dla zadania „napisz tekst z dostarczonych danych" nie ma czego
narzedziowo wywolywac; petla kontynuacji rozwiazuje problem dlugosci prosciej i taniej.
Jesli kiedys horoskop mialby sam siegac do bazy w trakcie pisania, wtedy tryb agenta
zacznie miec sens — powiedz, to wroce do tematu.

Zawiera i **zastepuje** PR #17 (ten sam obszar — pusta odpowiedz modelu). ## Dlaczego Anthropic zwracal pustke — przyczyna potwierdzona w dokumentacji API Domyslnym modelem byl **`claude-sonnet-5`**, ktory przy **pominietym** parametrze `thinking` wlacza **myslenie adaptacyjne**, a `thinking.display` domyslnie jest `"omitted"`. Tokeny myslenia licza sie do `max_tokens` — wiec przy `LLM_MAX_TOKENS=2000` **cala tura wychodzila jako bloki `thinking` z pustym tekstem**. Parser filtrowal `type == "text"` i oddawal pusty string. Opus 4.8 bez `thinking` nie mysli, wiec tam objaw by nie wystapil — stad „pustke dostaje na anthropicu", a nie wszedzie. ## Gwarancja: horoskop powstaje zawsze (wszyscy trzej dostawcy) `generate()` to teraz **petla**, nie pojedynczy strzal: 1. tura z policzonym limitem wyjscia, 2. urwana na limicie → dopisz ture **„kontynuuj"** w tej samej rozmowie i sklej tekst, 3. tura z **samego myslenia** → traktowana jak urwana, nie jak pustka, 4. pusta i nie urwana → jedna proba z podpowiedzia, dopiero potem blad z diagnostyka. Realizuje to wprost Twoja sugestie „podzielic prompt na serie pytan i odpowiedzi w ramach jednej rozmowy" — i jest odporniejsze niz jedno wielkie zadanie, bo kazda tura miesci sie w timeoucie HTTP, a dlugosc odpowiedzi przestaje byc ograniczona jedna tura. **Detal, ktory latwo przeoczyc:** kontynuacja konczy sie tura **uzytkownika**. Claude **odrzuca prefill w turze asystenta bledem 400**, wiec naiwne „dopisz asystenta i wyslij" by nie zadzialalo. Jest na to osobny test. `thinking` jest teraz konfigurowany **jawnie**: `adaptive` + `effort: high` (jakosc tekstu), z wylacznikiem `ANTHROPIC_THINKING=off`. ## Okna kontekstu i rezerwa na odpowiedz (`app/llm/limits.py`) - tabela okien i limitow wyjscia per model + nadpisanie z ENV (`<DOSTAWCA>_CONTEXT_WINDOW`, `<DOSTAWCA>_MAX_OUTPUT`) — modele wychodza szybciej, niz aktualizuje sie kod; - **zawsze rezerwujemy miejsce na odpowiedz**: budzet promptu = okno − wyjscie − margines, nigdy odwrotnie; - **Anthropic liczy tokeny dokladnie** (`/v1/messages/count_tokens`), reszta szacuje — od tego zalezy, czy w oknie w ogole zostanie miejsce na horoskop. ## Suwak i ostrzezenie Doszly poziomy **„bardzo obszerny (~120 tys.)"** i **„maksymalny kontekst modelu"** — ten drugi liczy sie z okna wybranego modelu po odjeciu rezerwy (Opus 4.8: **870 000 tokenow** promptu; model lokalny: 2 096 — bo ma male okno). **Powyzej 90 tys. tokenow** pojawia sie ostrzezenie, ale **wyslanie jest nadal mozliwe** i okno odpowiedzi zostaje pelne — dokladnie jak prosiles. Przy wyniku widac plan tokenow, liczbe tur i ostrzezenia. Domyslny model Anthropic zmieniony na **`claude-opus-4-8`**. ## Weryfikacja - **170 passed / 1 skipped** (logika) + **15** (prezentacja). Nowe testy: sklejanie kontynuacji, brak prefillu asystenta, tura z samego myslenia, rezerwa na odpowiedz, prog ostrzezenia, nadpisania z ENV. - **E2E na atrapie Anthropica** odtwarzajacej zgloszony objaw (tura 1 = samo myslenie): 3 tury, obie czesci tekstu obecne, znacznik konca odciety. ## Czego swiadomie NIE zrobilem **Trybu agenta** — dla zadania „napisz tekst z dostarczonych danych" nie ma czego narzedziowo wywolywac; petla kontynuacji rozwiazuje problem dlugosci prosciej i taniej. Jesli kiedys horoskop mialby sam siegac do bazy w trakcie pisania, wtedy tryb agenta zacznie miec sens — powiedz, to wroce do tematu.
gitea added 2 commits 2026-07-22 19:40:32 +00:00
PRZYCZYNA PUSTYCH ODPOWIEDZI NA ANTHROPICU (potwierdzona w dokumentacji API):
domyslnym modelem byl `claude-sonnet-5`, ktory przy POMINIETYM parametrze
`thinking` wlacza myslenie adaptacyjne, a `thinking.display` domyslnie jest
"omitted". Tokeny myslenia licza sie do max_tokens, wiec przy LLM_MAX_TOKENS=2000
cala tura wychodzila jako bloki `thinking` z pustym tekstem — parser filtrowal
type=="text" i zwracal pusty string. Opus 4.8 bez `thinking` nie mysli, wiec tam
objaw by nie wystapil.

Gwarancja niepustej odpowiedzi (wszyscy trzej dostawcy):
- generate() to teraz PETLA, nie pojedynczy strzal: tura -> jesli urwana na
  limicie, dopisz ture „kontynuuj" w tej samej rozmowie i sklej tekst,
- tura zlozona z samego myslenia traktowana jak urwana (nie jak pustka),
- pusta i NIE urwana -> jedna proba z podpowiedzia, dopiero potem blad,
- kontynuacja konczy sie tura UZYTKOWNIKA — Claude odrzuca prefill asystenta (400),
- `thinking` konfigurowany JAWNIE (adaptive + effort=high; ANTHROPIC_THINKING=off).

Okna kontekstu i rezerwa na odpowiedz (app/llm/limits.py):
- tabela okien/limitow wyjscia per model + nadpisanie z ENV,
- plan() liczy okno odpowiedzi jako okno - prompt - margines i NIGDY nie oddaje
  calego kontekstu promptowi,
- Anthropic liczy tokeny DOKLADNIE (/v1/messages/count_tokens), reszta szacuje,
- >90 tys. tokenow promptu -> ostrzezenie, ale wyslanie NADAL mozliwe i z pelnym
  oknem odpowiedzi.

UI: suwak budzetu rozszerzony o „bardzo obszerny" i „maksymalny kontekst modelu"
(liczony z okna wybranego modelu po odjeciu rezerwy); przy wyniku widac plan
tokenow, liczbe tur i ostrzezenia.

Domyslny model Anthropic: claude-opus-4-8.

Testy: 170 passed / 1 skipped (logika) + 15 (prezentacja). Nowe testy pokrywaja
sklejanie kontynuacji, brak prefillu asystenta, ture z samego myslenia, rezerwe
na odpowiedz i prog ostrzezenia. Zweryfikowane e2e na atrapie Anthropica
odtwarzajacej zgloszony objaw: 3 tury, obie czesci tekstu obecne.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
163ace4283
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>
gitea force-pushed feat/llm-context-budget from 46a21427db to 163ace4283 2026-07-22 19:40:32 +00:00 Compare
gitea merged commit 163ace4283 into master 2026-07-22 19:40:57 +00:00
gitea deleted branch feat/llm-context-budget 2026-07-22 19:40:58 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gitea/astrololo#18