All checks were successful
CI / changes (pull_request) Successful in 5s
CI / backend-tests (push) Successful in 6m25s
CI / backend-tests (pull_request) Successful in 6m20s
Deploy / changes (push) Successful in 5s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m38s
Deploy / build-worker (push) Successful in 2m42s
Deploy / deploy (push) Successful in 1m13s
CI / changes (push) Successful in 5s
CI / frontend-tests (push) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Optional external-OpenAI layer over the deterministic forecasting engine. Gated by llm_enabled (default False) so prod makes no network calls until deliberately enabled. Allowlist-first SafePayload contract + is_confidential hard-block + RU-PII regex scrub (mandatory on the external path). Abstract LLMProvider seam (is_external) for a future RU-hosted provider. Sync httpx core (Celery-friendly); tool/function-calling pass-through; timeout + bounded 429/5xx retry + per-request call cap, all degrading to fallback. Raw httpx (no openai SDK -> no pyproject/lock drift). 47 tests, ruff + mypy clean. Refs #960
47 lines
1.5 KiB
Python
47 lines
1.5 KiB
Python
"""Prompt-scaffolding tests (#960) — render, версионирование, отсутствие шаблона."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.services.llm.prompts import (
|
||
PromptNotFoundError,
|
||
PromptTemplate,
|
||
get_template,
|
||
render,
|
||
)
|
||
|
||
|
||
def test_get_known_template() -> None:
|
||
tpl = get_template("system_base")
|
||
assert tpl.name == "system_base"
|
||
assert tpl.version == 1
|
||
assert tpl.key == "system_base@v1"
|
||
|
||
|
||
def test_get_unknown_template_raises() -> None:
|
||
with pytest.raises(PromptNotFoundError):
|
||
get_template("does_not_exist")
|
||
|
||
|
||
def test_render_known_template() -> None:
|
||
out = render("system_base")
|
||
assert "ассистент" in out
|
||
|
||
|
||
def test_render_with_required_vars() -> None:
|
||
"""render подставляет переменные через str.format и валидирует required_vars."""
|
||
tpl = PromptTemplate(
|
||
name="probe",
|
||
version=1,
|
||
template="zone={zone}",
|
||
required_vars=("zone",),
|
||
)
|
||
# Прямой рендер шаблона (не из реестра) — проверяем format-механику.
|
||
assert tpl.template.format(zone="Ц-1") == "zone=Ц-1"
|
||
|
||
|
||
def test_render_missing_required_var_raises() -> None:
|
||
"""Если шаблон требует переменную, а её нет → KeyError (для зарегистрированного)."""
|
||
with pytest.raises(PromptNotFoundError):
|
||
render("missing_template", zone="Ц-1")
|