"""Expectation quality loop pure control plane (no LLM).""" from __future__ import annotations from agents.world_class_advisor import evaluate_application from core.generation.expectation_loop import ( is_regulation_ready, run_advisor_expectation_loop, run_expectation_loop, ) BRIEF = { "key_rules": ["Wnioskodawca musi posiadać status MŚP", "Projekt musi spełniać zasadę DNSH"], "required_sections": ["Opis projektu", "Budżet"], "required_attachments": [], "attention_points": ["Sprawdź DNSH / wpływ środowiskowy w opisie projektu."], "usable": True, } def test_is_regulation_ready_false_for_structure_only(): assert ( is_regulation_ready( { "passed": True, "score": 95, "regulation_grounded_pass": False, "blockers": [], "grounding_mode": "structure_only", } ) is False ) def test_loop_stops_when_ready(): sections = { "Opis projektu": ( "Projekt MŚP z DNSH i pełnym opisem innowacji. " * 10 ), "Budżet": ( "Budżet z wkładem własnym i kosztami kwalifikowalnymi. " * 10 ), } def evaluate(state): return evaluate_application( sections=state.get("generated_sections") or {}, brief=BRIEF, grounding_mode="regulation", ) result = run_expectation_loop( evaluate=evaluate, apply_fixes=None, initial_state={"generated_sections": sections}, max_iterations=3, min_score=70, ) assert result.ready is True assert result.stop_reason == "ready" assert result.regulation_grounded_pass is True assert result.iterations >= 1 assert not result.remaining_blockers def test_loop_max_iter_with_blockers(): weak = {"Opis projektu": "za mało"} def evaluate(state): return evaluate_application( sections=state.get("generated_sections") or {}, brief=BRIEF, grounding_mode="regulation", ) def fix(state, report): # Improves slightly but not enough to cover Budżet gen = dict(state.get("generated_sections") or {}) gen["Opis projektu"] = (gen.get("Opis projektu") or "") + " dodatek MŚP DNSH " * 5 state = dict(state) state["generated_sections"] = gen state["fixed_sections"] = ["Opis projektu"] return state result = run_expectation_loop( evaluate=evaluate, apply_fixes=fix, initial_state={"generated_sections": weak}, max_iterations=2, min_score=70, ) assert result.ready is False assert result.stop_reason == "max_iter" assert result.iterations == 2 assert result.remaining_blockers assert result.regulation_grounded_pass is False def test_loop_structure_only_never_ready(): sections = { "Opis projektu": "x" * 250, "Budżet": "y" * 250, } def evaluate(state): return evaluate_application( sections=state.get("generated_sections") or {}, brief=BRIEF, grounding_mode="structure_only", ) result = run_expectation_loop( evaluate=evaluate, apply_fixes=lambda s, r: s, initial_state={ "generated_sections": sections, "external_context": {"grounding_mode": "structure_only"}, "grounding_mode": "structure_only", }, max_iterations=2, ) assert result.ready is False assert result.regulation_grounded_pass is False assert result.stop_reason in ("max_iter", "blocked_grounding") def test_quality_expectation_step_wired_for_production(): """Production helper run_quality_expectation_step drives real advisor + fix path.""" from core.generation.quality_loop import run_quality_expectation_step state = { "sections_plan": [ {"title": "Opis projektu", "type": "desc"}, {"title": "Budżet", "type": "budget"}, ], "generated_sections": { "Opis projektu": "Pełny opis projektu MŚP z DNSH i celami. " * 8, "Budżet": "Budżet z wkładem własnym 30% i kosztami kwalifikowalnymi. " * 8, }, "external_context": { "grounding_mode": "regulation", "advisor_brief": BRIEF, "required_sections": BRIEF["required_sections"], "key_rules": BRIEF["key_rules"], }, } out = run_quality_expectation_step(state, source="holistic", min_score=70) assert "advisor_before" in out assert "regulation_ready" in out assert out["stop_reason"] in ("ready", "needs_retry") # Strong content with brief signals should be ready without needing rewrite assert out["regulation_ready"] is True assert out["stop_reason"] == "ready" def test_soft_pass_gate_requires_regulation_grounded_pass_semantics(): """Mirror generator gate: usable brief → only regulation_grounded_pass is ready.""" from core.generation.expectation_loop import is_regulation_ready fluff_rep = evaluate_application( sections={ "Opis projektu": "Projekt innowacyjny z bogatym doświadczeniem zespołu. " * 20, "Budżet": "Budżet obejmuje koszty osobowe i sprzęt w pełnym zakresie. " * 20, }, brief=BRIEF, grounding_mode="regulation", ) assert fluff_rep.regulation_grounded_pass is False assert is_regulation_ready(fluff_rep) is False def test_run_advisor_expectation_loop_improves_to_ready(): state = { "generated_sections": { "Opis projektu": "krótki start", }, "external_context": { "grounding_mode": "regulation", "advisor_brief": BRIEF, "required_sections": BRIEF["required_sections"], }, } def pure_fix(st, report): # Simulate targeted rewrite filling required sections st = dict(st) st["generated_sections"] = { "Opis projektu": ( "Pełny opis projektu MŚP z DNSH, innowacją i celami programu. " * 8 ), "Budżet": ( "Szczegółowy budżet, wkład własny 30%, koszty kwalifikowalne. " * 8 ), } st["fixed_sections"] = ["Opis projektu", "Budżet"] return st result = run_advisor_expectation_loop( state, max_iterations=3, min_score=70, apply_fixes=pure_fix, ) assert result.ready is True assert result.stop_reason == "ready" assert result.regulation_grounded_pass is True