Compare commits

..

5 Commits

Author SHA1 Message Date
46b703e5a2 Merge pull request 'Bug Fixes. Workflow Engine' (#81) from develop into main
All checks were successful
Deploy Production / deploy (push) Successful in 1m0s
Build Test / pytest-backend (push) Successful in 8s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Reviewed-on: #81
2026-04-12 14:08:43 +02:00
e09cbc112e fix: Preserve case in question IDs during parsing
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Problem: Parser converted question IDs to lowercase ('qAnalyst' → 'qanalyst'),
causing normalization to fail because id_catalog lookup is case-sensitive.

Impact: All workflow question signals were lost - normalized_signals stayed empty,
so template placeholders like {{node_2.signal_qAnalyst}} remained unresolved.

Solution: Removed .lower() call in parse_decision_questions() to preserve
original case from AI response.

Root cause: Line 162 in result_container_parser.py
Fixes: Question augmentation signals not appearing in workflow end nodes
2026-04-12 14:04:14 +02:00
f6b3182a80 fix: Add wrapper in prompts.py execute endpoint for workflow signature mismatch
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Problem: Workflows executed via /api/prompts/execute (not /api/workflows/execute)
were passing call_openrouter directly to execute_prompt_with_data, which then
passes it to workflow_executor. workflow_executor expects (prompt, model) signature
but call_openrouter has (prompt, max_tokens=4096) signature.

Previous fix in workflows.py was correct but unused - workflows use prompts.py endpoint.

Solution: Added workflow_llm_call() wrapper in execute_unified_prompt() endpoint
that matches expected (prompt, model) -> str signature.

Related: cb3aa48 (workflows.py fix for different endpoint)
2026-04-12 13:44:08 +02:00
cb3aa48999 fix: Add wrapper function for workflow LLM calls to prevent max_tokens signature mismatch
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Problem: workflow_executor calls openrouter_call_func(prompt, model) but
call_openrouter expects (prompt, max_tokens=4096). This caused the model string
'anthropic/claude-sonnet-4' to be passed as max_tokens, resulting in OpenRouter
requesting 64000 tokens and failing with 402 credit errors.

Solution: Added workflow_llm_call() wrapper in workflows.py that matches the
expected (prompt, model) -> str signature and calls call_openrouter correctly.

Fixes: All workflows failing with 402 'insufficient credits' errors
2026-04-12 13:37:31 +02:00
77f1ed14c5 fix: Cursor-Problem beim Frage-ID Editieren
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
Problem: Cursor springt nach jedem Tastendruck aus dem ID-Feld

Ursache: key={q.id} in QuestionEditor map
- Wenn ID geändert wird, ändert sich der React Key
- React unmountet alte Component und mountet neue
- Focus geht verloren

Lösung: key={idx} verwenden
- Stabiler Key während Editing
- Komponente bleibt gemountet
- Cursor bleibt im Feld

UX: Jetzt kann man IDs flüssig editieren ohne Unterbrechung

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 12:29:52 +02:00
4 changed files with 21 additions and 4 deletions

View File

@ -158,7 +158,8 @@ def parse_decision_questions(section_text: str) -> Dict[str, str]:
for pattern in patterns: for pattern in patterns:
matches = re.finditer(pattern, section_text, re.MULTILINE | re.IGNORECASE) matches = re.finditer(pattern, section_text, re.MULTILINE | re.IGNORECASE)
for match in matches: for match in matches:
question_type = match.group(1).strip().lower() # Preserve original case for question IDs (e.g., "qAnalyst" not "qanalyst")
question_type = match.group(1).strip()
answer = match.group(2).strip() answer = match.group(2).strip()
# Entferne Klammern und Whitespace # Entferne Klammern und Whitespace

View File

@ -1488,6 +1488,14 @@ async def execute_unified_prompt(
'vitalwerte': 7 'vitalwerte': 7
} }
# Wrapper function to match workflow_executor's expected signature: (prompt, model) -> str
# workflow_executor calls: openrouter_call_func(prompt, "anthropic/claude-sonnet-4")
# but call_openrouter expects: call_openrouter(prompt, max_tokens=4096)
async def workflow_llm_call(prompt: str, model: str = None) -> str:
# Ignore model parameter (already set in OPENROUTER_MODEL env var)
# Use default max_tokens=4096 from call_openrouter
return await call_openrouter(prompt)
# Execute with prompt_executor # Execute with prompt_executor
# Always enable debug when saving to collect metadata for value table # Always enable debug when saving to collect metadata for value table
result = await execute_prompt_with_data( result = await execute_prompt_with_data(
@ -1495,7 +1503,7 @@ async def execute_unified_prompt(
profile_id=profile_id, profile_id=profile_id,
modules=modules, modules=modules,
timeframes=timeframes, timeframes=timeframes,
openrouter_call_func=call_openrouter, openrouter_call_func=workflow_llm_call, # Use wrapper with correct signature
enable_debug=debug or save # Enable debug if saving for metadata collection enable_debug=debug or save # Enable debug if saving for metadata collection
) )

View File

@ -86,11 +86,19 @@ async def execute_workflow_endpoint(
"type": "workflow" "type": "workflow"
} }
# Wrapper function to match workflow_executor's expected signature: (prompt, model) -> str
# workflow_executor calls: openrouter_call_func(prompt, "anthropic/claude-sonnet-4")
# but call_openrouter expects: call_openrouter(prompt, max_tokens=4096)
async def workflow_llm_call(prompt: str, model: str) -> str:
# Ignore model parameter (already set in OPENROUTER_MODEL env var)
# Use default max_tokens=4096 from call_openrouter
return await call_openrouter(prompt)
try: try:
result = await execute_workflow_prompt( result = await execute_workflow_prompt(
prompt=workflow_prompt, prompt=workflow_prompt,
variables=variables, variables=variables,
openrouter_call_func=call_openrouter, openrouter_call_func=workflow_llm_call, # Use wrapper with correct signature
enable_debug=request.enable_debug enable_debug=request.enable_debug
) )
return result return result

View File

@ -47,7 +47,7 @@ export function QuestionAugmentationPanel({ node, onChange }) {
{questions.map((q, idx) => ( {questions.map((q, idx) => (
<QuestionEditor <QuestionEditor
key={q.id} key={idx}
question={q} question={q}
index={idx} index={idx}
onChange={(field, value) => handleQuestionChange(idx, field, value)} onChange={(field, value) => handleQuestionChange(idx, field, value)}