Workflow Analysen V1.1 #73
|
|
@ -31,6 +31,42 @@ OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "anthropic/claude-sonnet-4")
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/prompts", tags=["prompts"])
|
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("")
|
@router.get("")
|
||||||
def list_prompts(session: dict=Depends(require_auth)):
|
def list_prompts(session: dict=Depends(require_auth)):
|
||||||
|
|
@ -1254,17 +1290,7 @@ async def execute_unified_prompt(
|
||||||
else:
|
else:
|
||||||
content = json.dumps(final_output, ensure_ascii=False)
|
content = json.dumps(final_output, ensure_ascii=False)
|
||||||
elif result['type'] == 'workflow':
|
elif result['type'] == 'workflow':
|
||||||
# Graph-Workflows: kein "output", sondern aggregated_result
|
content = _workflow_user_facing_content(result.get('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)
|
|
||||||
else:
|
else:
|
||||||
# For base prompts, use output directly
|
# For base prompts, use output directly
|
||||||
content = result.get('output', '')
|
content = result.get('output', '')
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useState, useEffect, useMemo } from 'react'
|
||||||
import { Brain, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
|
import { Brain, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { api } from '../utils/api'
|
import { api } from '../utils/api'
|
||||||
|
import { getWorkflowDisplayContent } from '../utils/workflowDisplay'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import Markdown from '../utils/Markdown'
|
import Markdown from '../utils/Markdown'
|
||||||
import UsageBadge from '../components/UsageBadge'
|
import UsageBadge from '../components/UsageBadge'
|
||||||
|
|
@ -400,15 +401,7 @@ export default function Analysis() {
|
||||||
content = JSON.stringify(finalOutput, null, 2)
|
content = JSON.stringify(finalOutput, null, 2)
|
||||||
}
|
}
|
||||||
} else if (result.type === 'workflow') {
|
} else if (result.type === 'workflow') {
|
||||||
const agg = result.aggregated_result
|
content = getWorkflowDisplayContent(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)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// For base prompts, use output directly
|
// For base prompts, use output directly
|
||||||
content = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2)
|
content = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2)
|
||||||
|
|
|
||||||
37
frontend/src/utils/workflowDisplay.js
Normal file
37
frontend/src/utils/workflowDisplay.js
Normal 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)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user