Universal CSV Importer #70

Merged
Lars merged 54 commits from develop into main 2026-04-11 07:06:47 +02:00
2 changed files with 8 additions and 63 deletions
Showing only changes of commit 856a82ec1d - Show all commits

View File

@ -281,21 +281,13 @@ async def execute_node(
prompt_template = await load_prompt_template(node.prompt_slug, context)
logger.debug(f"Node {node.id}: Loaded prompt '{node.prompt_slug}'")
# 2. Parse question_augmentations (Hybrid Model)
# 2. Parse question_augmentations
questions = []
if node.question_augmentations:
# Node-specific questions (override base prompt questions)
# Convert list of dicts to JSONB-like format for parser
questions_jsonb = [q.model_dump() if hasattr(q, 'model_dump') else q for q in node.question_augmentations]
questions = parse_question_augmentations_from_jsonb(questions_jsonb)
logger.debug(f"Node {node.id}: {len(questions)} node-specific questions")
else:
# Fallback: Load questions from base prompt (Hybrid Model)
base_questions = await load_prompt_questions(node.prompt_slug)
if base_questions:
questions = parse_question_augmentations_from_jsonb(base_questions)
logger.debug(f"Node {node.id}: {len(questions)} questions from base prompt '{node.prompt_slug}'")
else:
logger.debug(f"Node {node.id}: No questions (neither node-specific nor base prompt)")
logger.debug(f"Node {node.id}: {len(questions)} question augmentations")
# 3. Augment Prompt
if questions:
@ -303,10 +295,8 @@ async def execute_node(
base_prompt=prompt_template,
questions=questions
)
logger.debug(f"Node {node.id}: Augmented prompt with {len(questions)} questions")
else:
augmented_prompt = prompt_template
logger.debug(f"Node {node.id}: No augmentation (no questions)")
# 4. LLM Call
logger.debug(f"Node {node.id}: Calling LLM")
@ -322,17 +312,16 @@ async def execute_node(
# 6. Normalize Signals
normalized_signals = []
if parsed["decision_signals"]:
# Hybrid Model: Questions (node-specific or base prompt) override Catalog
# Hybrid Model: Node-spezifische Questions überschreiben Catalog
node_catalog = catalog.copy()
if questions:
for q in questions:
q_dict = q.model_dump() if hasattr(q, 'model_dump') else q
node_catalog[q_dict['type']] = {
"answer_spectrum": q_dict['answer_spectrum'],
"normalization_rules": None # Questions haben keine Synonyme
"normalization_rules": None # Node-Questions haben keine Synonyme
}
source = "node-specific" if node.question_augmentations else "base prompt"
logger.debug(f"Node {node.id}: Override catalog for '{q_dict['type']}' with {source} spectrum")
logger.debug(f"Node {node.id}: Override catalog for '{q_dict['type']}' with node-specific spectrum")
normalized_signals = normalize_all_signals(
decision_signals=parsed["decision_signals"],
@ -827,50 +816,6 @@ async def load_prompt_template(prompt_slug: str, context: Dict[str, Any]) -> str
return resolved
async def load_prompt_questions(prompt_slug: str) -> List[Dict]:
"""
Lädt Fragen aus einem Basis-Prompt (Hybrid Model - Fallback).
Wenn ein Analysis Node KEINE node-spezifischen Fragen hat,
werden die Fragen aus dem referenzierten Basis-Prompt geladen.
Args:
prompt_slug: Slug des Prompts (z.B. "pipeline_body")
Returns:
Liste von Question-Dicts im format:
[
{
"id": "q1",
"type": "relevanz",
"question": "Ist eine vertiefte Analyse relevant?",
"answer_spectrum": ["ja", "nein", "unklar"]
},
...
]
Beispiel:
>>> questions = await load_prompt_questions("pipeline_body")
>>> len(questions) > 0
True
"""
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"SELECT questions FROM ai_prompts WHERE slug = %s AND active = true",
(prompt_slug,)
)
row = cur.fetchone()
if not row or not row.get('questions'):
return []
questions = row['questions']
# PostgreSQL JSONB wird automatisch zu Python list/dict konvertiert
if isinstance(questions, list):
return questions
else:
logger.warning(f"Unexpected questions format for {prompt_slug}: {type(questions)}")
return []
def aggregate_results(node_states: List[NodeExecutionState]) -> Dict[str, Any]:

View File

@ -23,7 +23,7 @@ export function serializeToWorkflowGraph(nodes, edges, metadata = {}) {
...(node.type === 'analysis' && {
prompt_slug: node.data.prompt_slug || null,
prompt_name: node.data.prompt_name || null,
questions: node.data.questions || [],
question_augmentations: node.data.questions || [], // Backend erwartet question_augmentations
fallback_strategy: node.data.fallback_strategy || 'conservative_skip'
}),
@ -85,7 +85,7 @@ export function deserializeFromWorkflowGraph(jsonbData) {
...(node.type === 'analysis' && {
prompt_slug: node.prompt_slug || node.prompt_id || null, // Fallback für alte Workflows mit prompt_id
prompt_name: node.prompt_name || null, // Falls vom Backend mitgeliefert
questions: node.questions || [],
questions: node.question_augmentations || node.questions || [], // Backend sendet question_augmentations
fallback_strategy: node.fallback_strategy || 'conservative_skip'
}),