Workflow Analysen V1.1 #73

Merged
Lars merged 5 commits from develop into main 2026-04-11 12:38:09 +02:00
3 changed files with 76 additions and 20 deletions
Showing only changes of commit d803f39de3 - Show all commits

View File

@ -31,6 +31,42 @@ OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "anthropic/claude-sonnet-4")
router = APIRouter(prefix="/api/prompts", tags=["prompts"])
# Metadaten-Schlüssel in workflow aggregate_results (nicht als „einziger“ Nutzer-Output)
_WORKFLOW_AGG_META_KEYS = frozenset({
"combined_analysis",
"all_signals",
"total_nodes",
"executed_nodes",
"failed_nodes",
"skipped_nodes",
})
def _workflow_user_facing_content(agg: object) -> str:
"""
Nutzer-sichtbarer Text wie im Admin WorkflowResultViewer (Final Output):
primär aggregated_result['analysis_core'], nicht das gesamte JSON.
"""
if agg is None:
return ""
if isinstance(agg, str):
return agg
if not isinstance(agg, dict):
return json.dumps(agg, ensure_ascii=False)
core = agg.get("analysis_core")
if isinstance(core, str) and core.strip():
return core
combined = agg.get("combined_analysis")
if isinstance(combined, str) and combined.strip():
return combined
non_meta = [k for k in agg.keys() if k not in _WORKFLOW_AGG_META_KEYS]
if len(non_meta) == 1:
v = agg[non_meta[0]]
if isinstance(v, str):
return v
return json.dumps(v, ensure_ascii=False)
return json.dumps(agg, ensure_ascii=False)
@router.get("")
def list_prompts(session: dict=Depends(require_auth)):
@ -1254,17 +1290,7 @@ async def execute_unified_prompt(
else:
content = json.dumps(final_output, ensure_ascii=False)
elif result['type'] == 'workflow':
# Graph-Workflows: kein "output", sondern aggregated_result
agg = result.get('aggregated_result')
if isinstance(agg, dict) and len(agg) == 1:
v = list(agg.values())[0]
content = v if isinstance(v, str) else json.dumps(v, ensure_ascii=False)
elif agg is None:
content = ''
elif isinstance(agg, str):
content = agg
else:
content = json.dumps(agg, ensure_ascii=False)
content = _workflow_user_facing_content(result.get('aggregated_result'))
else:
# For base prompts, use output directly
content = result.get('output', '')

View File

@ -2,6 +2,7 @@ import React, { useState, useEffect, useMemo } from 'react'
import { Brain, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
import { Link } from 'react-router-dom'
import { api } from '../utils/api'
import { getWorkflowDisplayContent } from '../utils/workflowDisplay'
import { useAuth } from '../context/AuthContext'
import Markdown from '../utils/Markdown'
import UsageBadge from '../components/UsageBadge'
@ -400,15 +401,7 @@ export default function Analysis() {
content = JSON.stringify(finalOutput, null, 2)
}
} else if (result.type === 'workflow') {
const agg = result.aggregated_result
if (agg != null && typeof agg === 'object' && !Array.isArray(agg) && Object.keys(agg).length === 1) {
const v = Object.values(agg)[0]
content = typeof v === 'string' ? v : JSON.stringify(v, null, 2)
} else if (typeof agg === 'string') {
content = agg
} else {
content = JSON.stringify(agg ?? {}, null, 2)
}
content = getWorkflowDisplayContent(result.aggregated_result)
} else {
// For base prompts, use output directly
content = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2)

View File

@ -0,0 +1,37 @@
/**
* Nutzer-sichtbare Textausgabe eines Workflow-Laufs gleiche Logik wie
* WorkflowResultViewer (Final Output): primär aggregated_result.analysis_core.
*/
const AGG_META_KEYS = new Set([
'combined_analysis',
'all_signals',
'total_nodes',
'executed_nodes',
'failed_nodes',
'skipped_nodes',
])
export function getWorkflowDisplayContent(aggregatedResult) {
if (aggregatedResult == null) return ''
if (typeof aggregatedResult !== 'object' || Array.isArray(aggregatedResult)) {
return typeof aggregatedResult === 'string'
? aggregatedResult
: JSON.stringify(aggregatedResult, null, 2)
}
const core = aggregatedResult.analysis_core
if (typeof core === 'string' && core.trim() !== '') return core
const combined = aggregatedResult.combined_analysis
if (typeof combined === 'string' && combined.trim() !== '') return combined
const keys = Object.keys(aggregatedResult).filter((k) => !AGG_META_KEYS.has(k))
if (keys.length === 1) {
const v = aggregatedResult[keys[0]]
if (typeof v === 'string') return v
if (v != null && typeof v === 'object') return JSON.stringify(v, null, 2)
return String(v)
}
return JSON.stringify(aggregatedResult, null, 2)
}