feat(llm): провайдер за протоколом — OpenAI-совместимый API и mock, фабрика по конфигу

- LLMProvider: complete(messages, schema) -> LLMResult (текст, разобранный JSON, токены)
- openai_compat: /chat/completions, structured output через response_format json_schema,
  JSON из ```-обёртки тоже разбирается; ретраи на 429/5xx и сетевые ошибки, 4xx — сразу ошибка
- mock: детерминированные правила для тестов и демо без сети
- LLMSettings (LLM_PROVIDER/BASE_URL/MODEL/API_KEY…), CLI python -m app.llm.cli

Refs #17

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 18:06:30 +03:00
co-authored by Claude Opus 5.5
parent 15f354d06b
commit ab988e6ed0
11 changed files with 396 additions and 1 deletions
+21
View File
@@ -0,0 +1,21 @@
import sys
import pytest
from app.llm import cli
def test_cli_asks_configured_provider(monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None:
monkeypatch.setenv("LLM_PROVIDER", "mock")
monkeypatch.setattr(sys, "argv", ["cli", "привет", "мир"])
cli.main()
out = capsys.readouterr().out
assert "[mock -]" in out and "mock: привет мир" in out
def test_cli_usage(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(sys, "argv", ["cli"])
with pytest.raises(SystemExit):
cli.main()