feat(logic): dostawcy LLM i pisanie horoskopu (LOG-31)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m38s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m43s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 24s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 24s
build / build (push) Successful in 59s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m47s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 29s
Testy / Kontrola składni wszystkich warstw (push) Successful in 18s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m38s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m43s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 24s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 24s
build / build (push) Successful in 59s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m47s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 29s
Testy / Kontrola składni wszystkich warstw (push) Successful in 18s
Domyslnie model LOKALNY — prompt niesie oryginalne opisy z baz, wiec domyslnie NIC nie opuszcza sieci. Chmura wlaczana swiadomie (LOG-32). - app/llm/: LLMProvider (jak EphemerisEngine z LOG-24) + dwie implementacje. Lokalny serwer modelu (Ollama/vLLM/llama.cpp) i OpenAI mowia TYM SAMYM protokolem /chat/completions, wiec obsluguje je jedna klasa; Anthropic ma wlasny /v1/messages. Napisane na samym httpx — bez SDK openai/anthropic: mniej zaleznosci i pelna kontrola nad tym, co wychodzi z sieci. - Kazdy dostawca deklaruje `leaves_lan` — interfejs MUSI jawnie mowic, czy tresc baz opuszcza siec; UI na tej podstawie ostrzega. - Ponawianie z backoffem (429/5xx), timeouty, czytelne bledy zamiast stacktrace. - Klucz wylacznie z LLM_API_KEY (sekret), nigdy w repo ani w UI. - POST /chart/horoscope + GET /llm/health. WAZNE: prompt jest zwracany ZAWSZE — takze gdy model padnie lub brakuje klucza. Dzieki temu awaria dostawcy nie blokuje pracy: prompt mozna skopiowac i uzyc recznie. Prezentacja: wybor modelu (lokalny/Anthropic/OpenAI), przycisk „Napisz horoskop (AI)", wynik z informacja kto go napisal, czy dane opuscily siec, ile wskazan weszlo, oraz zastrzezenie ze to nie porada medyczna (PRE-15). Testy: 13 nowych (transport podstawiony — zaden prawdziwy model nie wolany), calosc 131 passed / 1 skipped. Zweryfikowane e2e na atrapie serwera modelu: horoskop napisany, leaves_lan=false, tokeny zliczone; a przy padnietym modelu / braku klucza / zlym dostawcy — czytelny blad i zachowany prompt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #11.
This commit is contained in:
@@ -203,6 +203,54 @@ def chart_prompt(req: PromptRequest) -> dict:
|
||||
return out
|
||||
|
||||
|
||||
class HoroscopeRequest(PromptRequest):
|
||||
"""Jak PromptRequest + wybór dostawcy modelu (LOG-31)."""
|
||||
provider: str | None = None # local (dom.) | openai | anthropic
|
||||
max_tokens: int | None = None
|
||||
|
||||
|
||||
@app.post("/chart/horoscope")
|
||||
def chart_horoscope(req: HoroscopeRequest) -> dict:
|
||||
"""Napisany horoskop (LOG-31): prompt z LOG-29/30 → model → gotowy tekst.
|
||||
|
||||
Zwraca TAKŻE użyty prompt — również gdy wywołanie modelu padnie — żeby dało się
|
||||
go obejrzeć i użyć ręcznie. `leaves_lan` mówi, czy treść baz opuściła naszą sieć
|
||||
(LOG-32); prezentacja ma na tej podstawie ostrzegać.
|
||||
"""
|
||||
from app.llm.base import LLMError
|
||||
from app.llm.factory import build_provider, max_tokens
|
||||
|
||||
out = chart_prompt(req) # ten sam prompt co w podglądzie
|
||||
try:
|
||||
provider = build_provider(req.provider)
|
||||
result = provider.generate(out["prompt"], req.max_tokens or max_tokens())
|
||||
except LLMError as e:
|
||||
# prompt zostaje — użytkownik moze go skopiowac i uzyc recznie
|
||||
out["llm_error"] = str(e)
|
||||
return out
|
||||
|
||||
out.update(
|
||||
horoscope=result.text,
|
||||
provider=result.provider,
|
||||
model=result.model,
|
||||
leaves_lan=result.leaves_lan,
|
||||
usage=result.usage,
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
@app.get("/llm/health")
|
||||
def llm_health(provider: str | None = None) -> dict:
|
||||
"""Czy model jest osiągalny i skonfigurowany (bez generowania czegokolwiek)."""
|
||||
from app.llm.base import LLMError
|
||||
from app.llm.factory import build_provider
|
||||
|
||||
try:
|
||||
return build_provider(provider).health()
|
||||
except LLMError as e:
|
||||
return {"status": f"blad konfiguracji: {e}"}
|
||||
|
||||
|
||||
class ProfectionsRequest(BaseModel):
|
||||
when_utc: datetime # moment urodzenia (UTC)
|
||||
lat: float = 0.0
|
||||
|
||||
Reference in New Issue
Block a user