feat: Refactor workflow result handling in prompts and analysis components
- Introduced a new utility function to streamline the extraction of user-facing content from aggregated workflow results. - Updated backend prompt handling to utilize the new function for improved clarity and maintainability. - Adjusted frontend analysis component to leverage the utility for consistent content display across different workflow result formats. These changes enhance the overall user experience by ensuring more reliable and readable output from workflow executions.
This commit is contained in:
parent
300d96a9d8
commit
d803f39de3
|
|
@ -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', '')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
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