phase: 178_split_thinking_levels
## Phase 178 — final verification pass: 100% green
**Verified (all 5 tasks complete in `complete/178_split_thinking_levels/`):**
- `config.py` rename (`llm_chat_thinking_level` default `low`) + new `llm_summary_thinking_level` (default `""`); two `CATALOG` rows; `.env.example` replaced; no stale old-key references (only intentional negative pins in tests)
- Wire split: `chat_stream()` → chat level, one-shot `chat()` → summary level (absent `extra_body` when empty); per-turn line reads chat level only
**Gates:**
- `uv run pytest -k "config and thinking" -v` → 7 passed (defaults + env overrides both knobs)
- `uv run pytest` (unit + integration) → 4170 tests, exit 0; `--cov=app` → **99%** (>90% ✓)
- `uv run ruff check .` clean; `uv run pyright` clean
- `uv run pytest tests/e2e/test_split_thinking_levels.py -v --no-cov` → 3 passed in isolation
- `test_admin_settings_panel.py` + `test_admin_settings_layout.py` → 17 passed in isolation
**Completion criteria:** all 6 test/doc criteria ✅ (admin GET lists both keys, old key gone — integration + E2E pins); commit + phase-dir move = harness's job.
**Documentation:** README.md — both new env vars correctly documented in the LLM (aipi) table with defaults/wire-mapping/off-rule, defaults match `config.py`, all `.env.example` vars covered: verified consistent, no changes needed. AGENTS.md — all referenced files/commands verified to exist, no phase-178 impact (no UI code change, A4): verified consistent, no changes needed.
**Decisions:** none required — no live changes made (E2E booted its own app subprocess against the existing DB).
**Next pending phase:** none — 178 is the last phase in `todo/`.
This commit is contained in:
+56
-23
@@ -105,32 +105,65 @@ def test_llm_summary_model_env_override(monkeypatch) -> None:
|
||||
assert s.llm_summary_model == "mini"
|
||||
|
||||
|
||||
def test_llm_thinking_level_default_is_low(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Phase 172: the model's thinking level defaults to ``low``
|
||||
(``BOR_LLM_THINKING_LEVEL`` unset)."""
|
||||
monkeypatch.delenv("BOR_LLM_THINKING_LEVEL", raising=False)
|
||||
s = _settings()
|
||||
assert s.llm_thinking_level == "low"
|
||||
|
||||
|
||||
def test_llm_thinking_level_env_override(monkeypatch) -> None:
|
||||
"""Phase 172 (A4): free-form like ``llm_chat_model`` —
|
||||
``BOR_LLM_THINKING_LEVEL=high`` resolves to ``"high"`` (no
|
||||
allowlist, no validator)."""
|
||||
monkeypatch.setenv("BOR_LLM_THINKING_LEVEL", "high")
|
||||
s = _settings()
|
||||
assert s.llm_thinking_level == "high"
|
||||
|
||||
|
||||
def test_llm_thinking_level_empty_env_value_stays_empty(
|
||||
def test_llm_chat_thinking_level_default_is_low(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Phase 172 (A2): the EMPTY value is the off switch — it must not
|
||||
be coerced back to the default (trimming happens only at the wire,
|
||||
task 03; no normalization at load)."""
|
||||
monkeypatch.setenv("BOR_LLM_THINKING_LEVEL", "")
|
||||
"""Phase 178 (the phase-172 knob, renamed): the chat turn's
|
||||
thinking level defaults to ``low`` (``BOR_LLM_CHAT_THINKING_LEVEL``
|
||||
unset)."""
|
||||
monkeypatch.delenv("BOR_LLM_CHAT_THINKING_LEVEL", raising=False)
|
||||
s = _settings()
|
||||
assert s.llm_thinking_level == ""
|
||||
assert s.llm_chat_thinking_level == "low"
|
||||
|
||||
|
||||
def test_llm_chat_thinking_level_env_override(monkeypatch) -> None:
|
||||
"""Phase 178 (A4, kept): free-form like ``llm_chat_model`` —
|
||||
``BOR_LLM_CHAT_THINKING_LEVEL=high`` resolves to ``"high"`` (no
|
||||
allowlist, no validator)."""
|
||||
monkeypatch.setenv("BOR_LLM_CHAT_THINKING_LEVEL", "high")
|
||||
s = _settings()
|
||||
assert s.llm_chat_thinking_level == "high"
|
||||
|
||||
|
||||
def test_llm_chat_thinking_level_empty_env_value_stays_empty(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Phase 178 (A2, kept): the EMPTY value is the off switch — it
|
||||
must not be coerced back to the default (trimming happens only at
|
||||
the wire; no normalization at load)."""
|
||||
monkeypatch.setenv("BOR_LLM_CHAT_THINKING_LEVEL", "")
|
||||
s = _settings()
|
||||
assert s.llm_chat_thinking_level == ""
|
||||
|
||||
|
||||
def test_llm_summary_thinking_level_default_is_empty(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Phase 178 (A2): the one-shot summary thinking level defaults to
|
||||
EMPTY (``BOR_LLM_SUMMARY_THINKING_LEVEL`` unset) — the TODO's
|
||||
"none" means absence, never a literal "none" string."""
|
||||
monkeypatch.delenv("BOR_LLM_SUMMARY_THINKING_LEVEL", raising=False)
|
||||
s = _settings()
|
||||
assert s.llm_summary_thinking_level == ""
|
||||
|
||||
|
||||
def test_llm_summary_thinking_level_env_override(monkeypatch) -> None:
|
||||
"""Phase 178: free-form like the chat level —
|
||||
``BOR_LLM_SUMMARY_THINKING_LEVEL=high`` resolves to ``"high"``
|
||||
(no allowlist, no validator)."""
|
||||
monkeypatch.setenv("BOR_LLM_SUMMARY_THINKING_LEVEL", "high")
|
||||
s = _settings()
|
||||
assert s.llm_summary_thinking_level == "high"
|
||||
|
||||
|
||||
def test_llm_summary_thinking_level_empty_env_value_stays_empty(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""Phase 178 (A2, kept): the EMPTY value is the off switch — it
|
||||
must not be coerced to anything at load (no normalization)."""
|
||||
monkeypatch.setenv("BOR_LLM_SUMMARY_THINKING_LEVEL", "")
|
||||
s = _settings()
|
||||
assert s.llm_summary_thinking_level == ""
|
||||
|
||||
|
||||
def test_summary_max_chars_default_and_env_override(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
||||
Reference in New Issue
Block a user