All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 43s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m34s
- Introduced `_build_off_topic_slot_gap_spec` to generate specifications for off-topic slots, improving the handling of filled but thematically inappropriate slots. - Added `_build_unified_slot_review_entry` to streamline the review process for slots, incorporating various parameters for better evaluation and suggestions. - Enhanced existing logic in slot management to improve the robustness of path evaluations and user feedback. - Added tests for the new off-topic slot gap specification to ensure functionality and correctness.
131 lines
4.0 KiB
Python
131 lines
4.0 KiB
Python
"""Schachstellen-Erkennung für unified Slot-Review."""
|
|
from planning_exercise_path_builder import (
|
|
_parse_slot_refs_from_text,
|
|
_problematic_slots_from_path_qa,
|
|
_slot_auto_select_library,
|
|
_slot_suggestion_accepted,
|
|
)
|
|
from planning_progression_roadmap import StageSpecArtifact
|
|
|
|
|
|
def _spec(midx: int) -> StageSpecArtifact:
|
|
return StageSpecArtifact(
|
|
major_step_index=midx,
|
|
learning_goal=f"Lernziel Slot {midx + 1}",
|
|
load_profile=[],
|
|
exercise_type="",
|
|
success_criteria=[],
|
|
anti_patterns=[],
|
|
)
|
|
|
|
|
|
def test_problematic_slots_from_optimization_hints():
|
|
qa = {
|
|
"optimization_hints": [
|
|
{
|
|
"action": "rematch_slot",
|
|
"step_index": 1,
|
|
"issue": "stage_mismatch",
|
|
"reason": "Übung passt nicht zur Stufe",
|
|
}
|
|
],
|
|
"off_topic_steps": [],
|
|
}
|
|
steps = [
|
|
{"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"},
|
|
{"roadmap_major_step_index": 1, "exercise_id": 2, "title": "B"},
|
|
]
|
|
specs = [_spec(0), _spec(1)]
|
|
problems = _problematic_slots_from_path_qa(qa, steps, specs)
|
|
assert 1 in problems
|
|
assert any("Stufe" in r or "passt" in r for r in problems[1])
|
|
|
|
|
|
def test_slot_suggestion_accepted_for_problem_slot():
|
|
diff = {"baseline_exercise_id": 10, "proposed_exercise_id": 99}
|
|
assert _slot_suggestion_accepted(
|
|
baseline_qa={"optimization_hints": [{"action": "rematch_slot", "roadmap_major_step_index": 1}]},
|
|
projected_qa={"optimization_hints": []},
|
|
baseline_score=0.7,
|
|
projected_score=0.7,
|
|
diff=diff,
|
|
off_topic=False,
|
|
major_idx=1,
|
|
slot_problem=True,
|
|
)
|
|
|
|
|
|
def test_parse_slot_refs_schritt_is_one_based():
|
|
assert _parse_slot_refs_from_text("Schritt 8 (Ukemi Vorwärts) entfernen") == {7}
|
|
assert _parse_slot_refs_from_text("slot 3 und Stufe 5") == {2, 4}
|
|
|
|
|
|
def test_problematic_slots_from_refine_stage_spec_hint():
|
|
qa = {
|
|
"optimization_hints": [
|
|
{
|
|
"action": "refine_stage_spec",
|
|
"step_index": 7,
|
|
"issue": "stage_mismatch",
|
|
"reason": "Stufen-Fit zu schwach (0.00) für „Integration von Täuschung“",
|
|
}
|
|
],
|
|
"off_topic_steps": [],
|
|
}
|
|
steps = [
|
|
{"roadmap_major_step_index": i, "exercise_id": i + 1, "title": f"Übung {i + 1}"}
|
|
for i in range(8)
|
|
]
|
|
steps[7]["title"] = "Ukemi Vorwärts"
|
|
specs = [_spec(i) for i in range(8)]
|
|
problems = _problematic_slots_from_path_qa(qa, steps, specs)
|
|
assert 7 in problems
|
|
|
|
|
|
def test_problematic_slots_from_llm_schritt_text():
|
|
qa = {
|
|
"optimization_hints": [],
|
|
"off_topic_steps": [],
|
|
"issues": [
|
|
"Schritt 8 (Ukemi Vorwärts) hat keinen Bezug zur Kumite-Beinarbeit",
|
|
],
|
|
}
|
|
steps = [
|
|
{"roadmap_major_step_index": 7, "exercise_id": 99, "title": "Ukemi Vorwärts"},
|
|
]
|
|
specs = [_spec(7)]
|
|
problems = _problematic_slots_from_path_qa(qa, steps, specs)
|
|
assert 7 in problems
|
|
|
|
|
|
def test_slot_auto_select_requires_higher_score():
|
|
assert _slot_auto_select_library(
|
|
baseline_slot_score=0.5,
|
|
proposed_slot_score=0.51,
|
|
baseline_exercise_id=1,
|
|
proposed_exercise_id=2,
|
|
)
|
|
assert not _slot_auto_select_library(
|
|
baseline_slot_score=0.5,
|
|
proposed_slot_score=0.5,
|
|
baseline_exercise_id=1,
|
|
proposed_exercise_id=2,
|
|
)
|
|
|
|
|
|
def test_off_topic_slot_gap_spec_for_filled_slot():
|
|
from planning_exercise_path_builder import _build_off_topic_slot_gap_spec
|
|
|
|
spec = _build_off_topic_slot_gap_spec(
|
|
{
|
|
"roadmap_major_step_index": 7,
|
|
"exercise_id": 99,
|
|
"title": "Ukemi Vorwärts",
|
|
"roadmap_learning_goal": "Integration Täuschung",
|
|
}
|
|
)
|
|
assert spec is not None
|
|
assert spec["source"] == "off_topic"
|
|
assert spec["roadmap_major_step_index"] == 7
|
|
assert "Ukemi" in spec["rationale"]
|