Backend: - question_augmenter.py (290 Zeilen): Hybrid-Modell für Fragenergänzungen * merge_question_augmentations(): Knotengebundene Fragen überschreiben Prompt-Defaults * augment_prompt_with_questions(): Markdown-formatierte Fragenergänzung * parse_question_augmentations_from_jsonb(): JSONB → QuestionAugmentation[] - result_container_parser.py (250 Zeilen): Markdown-Sektionen-Parsing * parse_result_container(): Extrahiert Analysekern, Entscheidungsanteil, Begründungsanker * validate_decision_signal(): Normalisierung gegen answer_spectrum * Fallback-Parsing bei unstrukturierten Antworten - routers/workflow_questions.py (236 Zeilen): CRUD für workflow_question_catalog * GET /api/workflow/questions (mit active_only Filter) * POST/PUT/DELETE (Admin only, Soft Delete) - prompt_executor.py: Integration in execute_base_prompt() * Fragenergänzung vor LLM-Call (wenn node_questions oder catalog vorhanden) * Result-Container-Parsing nach LLM-Response - main.py: Router-Registrierung (workflow_questions) Tests: - test_phase1_question_augmenter.py (8 Tests): Hybrid-Modell, Formatierung, JSONB-Parsing - test_phase1_result_container_parser.py (17 Tests): Sektion-Extraktion, Decision-Parsing, Validierung Alle 25 Unit-Tests bestanden. version: 0.9j (backend) module: workflow 0.2.0 Konzept: .claude/task/Workflow_engine_prompting_engine/konzept_workflow_engine_konsolidated.md (Phase 1)
136 lines
4.0 KiB
Python
136 lines
4.0 KiB
Python
"""
|
|
Unit Tests für question_augmenter.py (Phase 1)
|
|
|
|
Run with: PYTHONPATH=./backend pytest tests/backend/test_phase1_question_augmenter.py -v
|
|
"""
|
|
import pytest
|
|
from workflow_models import QuestionAugmentation
|
|
from question_augmenter import (
|
|
augment_prompt_with_questions,
|
|
merge_question_augmentations,
|
|
format_question_list,
|
|
parse_question_augmentations_from_jsonb
|
|
)
|
|
|
|
|
|
def test_format_question_list():
|
|
"""Test: Formatierung der Fragenliste"""
|
|
questions = [
|
|
QuestionAugmentation(
|
|
id="q1",
|
|
type="relevanz",
|
|
question="Ist relevant?",
|
|
answer_spectrum=["ja", "nein", "unklar"]
|
|
),
|
|
QuestionAugmentation(
|
|
id="q2",
|
|
type="prioritaet",
|
|
question="Wie hoch?",
|
|
answer_spectrum=["hoch", "mittel", "niedrig", "unklar"]
|
|
)
|
|
]
|
|
|
|
result = format_question_list(questions)
|
|
|
|
assert "Relevanz" in result
|
|
assert "[ja/nein/unklar]" in result
|
|
assert "Prioritaet" in result # Lowercase wird capitalized
|
|
assert "[hoch/mittel/niedrig/unklar]" in result
|
|
|
|
|
|
def test_augment_prompt_with_questions():
|
|
"""Test: Prompt-Erweiterung mit Fragenergänzungen"""
|
|
base_prompt = "Analysiere die Körperdaten."
|
|
questions = [
|
|
QuestionAugmentation(
|
|
id="q1",
|
|
type="relevanz",
|
|
question="Ist relevant?",
|
|
answer_spectrum=["ja", "nein", "unklar"]
|
|
)
|
|
]
|
|
|
|
augmented = augment_prompt_with_questions(base_prompt, questions)
|
|
|
|
assert "Analysiere die Körperdaten." in augmented
|
|
assert "## Analyse" in augmented
|
|
assert "## Entscheidungsfragen" in augmented
|
|
assert "Relevanz" in augmented
|
|
assert "[ja/nein/unklar]" in augmented
|
|
|
|
|
|
def test_merge_question_augmentations_node_priority():
|
|
"""Test: Knotengebundene Fragen haben Vorrang (Hybridmodell)"""
|
|
node_questions = [
|
|
QuestionAugmentation(id="q1", type="relevanz", question="Q1", answer_spectrum=["ja", "nein"])
|
|
]
|
|
prompt_questions = [
|
|
QuestionAugmentation(id="q2", type="prioritaet", question="Q2", answer_spectrum=["hoch", "niedrig"])
|
|
]
|
|
|
|
result = merge_question_augmentations(node_questions, prompt_questions)
|
|
|
|
# Knotengebundene haben Vorrang
|
|
assert len(result) == 1
|
|
assert result[0].type == "relevanz"
|
|
|
|
|
|
def test_merge_question_augmentations_prompt_fallback():
|
|
"""Test: Prompt-Defaults werden verwendet wenn Knoten leer"""
|
|
node_questions = None
|
|
prompt_questions = [
|
|
QuestionAugmentation(id="q2", type="prioritaet", question="Q2", answer_spectrum=["hoch", "niedrig"])
|
|
]
|
|
|
|
result = merge_question_augmentations(node_questions, prompt_questions)
|
|
|
|
# Prompt-Defaults werden verwendet
|
|
assert len(result) == 1
|
|
assert result[0].type == "prioritaet"
|
|
|
|
|
|
def test_merge_question_augmentations_empty():
|
|
"""Test: Leere Liste wenn weder Knoten noch Prompt Fragen haben"""
|
|
result = merge_question_augmentations(None, None)
|
|
assert result == []
|
|
|
|
|
|
def test_parse_question_augmentations_from_jsonb():
|
|
"""Test: Parsing aus JSONB-Format"""
|
|
jsonb_data = [
|
|
{
|
|
"id": "q1",
|
|
"type": "relevanz",
|
|
"question": "Ist relevant?",
|
|
"answer_spectrum": ["ja", "nein", "unklar"]
|
|
},
|
|
{
|
|
"id": "q2",
|
|
"type": "prioritaet",
|
|
"question": "Wie hoch?",
|
|
"answer_spectrum": ["hoch", "mittel", "niedrig"]
|
|
}
|
|
]
|
|
|
|
result = parse_question_augmentations_from_jsonb(jsonb_data)
|
|
|
|
assert len(result) == 2
|
|
assert result[0].type == "relevanz"
|
|
assert result[1].type == "prioritaet"
|
|
|
|
|
|
def test_parse_question_augmentations_empty_jsonb():
|
|
"""Test: Leere Liste bei None JSONB"""
|
|
result = parse_question_augmentations_from_jsonb(None)
|
|
assert result == []
|
|
|
|
|
|
def test_parse_question_augmentations_invalid_jsonb():
|
|
"""Test: ValueError bei ungültigem JSONB"""
|
|
with pytest.raises(ValueError, match="muss ein Array sein"):
|
|
parse_question_augmentations_from_jsonb({"invalid": "format"})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|