fix: Analysis page now uses unified prompt executor (Issue #28)
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s

BREAKING: Analysis page switched from old /insights/run to new /prompts/execute

Changes:
- Backend: Added save=true parameter to /prompts/execute
  - When enabled, saves final output to ai_insights table
  - Extracts content from pipeline output (last stage)
- Frontend api.js: Added save parameter to executeUnifiedPrompt()
- Frontend Analysis.jsx: Switched from api.runInsight() to api.executeUnifiedPrompt()
  - Transforms new result format to match InsightCard expectations
  - Pipeline outputs properly extracted and displayed

Fixes: PIPELINE_MASTER responses (old template being sent to AI)
The old /insights/run endpoint used raw template field, which for the
legacy "pipeline" prompt was literally "PIPELINE_MASTER". The new
executor properly handles stages and data processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-26 09:38:58 +01:00
parent 811ba8b3dc
commit 97e57481f9
3 changed files with 50 additions and 3 deletions

View File

@ -700,6 +700,7 @@ async def execute_unified_prompt(
modules: Optional[dict] = None, modules: Optional[dict] = None,
timeframes: Optional[dict] = None, timeframes: Optional[dict] = None,
debug: bool = False, debug: bool = False,
save: bool = False,
session: dict = Depends(require_auth) session: dict = Depends(require_auth)
): ):
""" """
@ -710,6 +711,7 @@ async def execute_unified_prompt(
modules: Dict of enabled modules (e.g., {"körper": true}) modules: Dict of enabled modules (e.g., {"körper": true})
timeframes: Dict of timeframes per module (e.g., {"körper": 30}) timeframes: Dict of timeframes per module (e.g., {"körper": 30})
debug: If true, include debug information (placeholders, final prompts, etc.) debug: If true, include debug information (placeholders, final prompts, etc.)
save: If true, save result to ai_insights table
Returns: Returns:
Execution result with outputs (and debug info if debug=true) Execution result with outputs (and debug info if debug=true)
@ -745,6 +747,33 @@ async def execute_unified_prompt(
enable_debug=debug enable_debug=debug
) )
# Save to ai_insights if requested
if save:
# Extract final output text/markdown
if result['type'] == 'pipeline':
# For pipeline, get the last stage's output
final_output = result.get('output', {})
# If output is dict with single key, use that value
if isinstance(final_output, dict) and len(final_output) == 1:
content = list(final_output.values())[0]
else:
content = json.dumps(final_output, ensure_ascii=False)
else:
# For base prompts, use output directly
content = result.get('output', '')
if isinstance(content, dict):
content = json.dumps(content, ensure_ascii=False)
# Save to database
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"""INSERT INTO ai_insights (id, profile_id, scope, content, created)
VALUES (%s, %s, %s, %s, CURRENT_TIMESTAMP)""",
(str(uuid.uuid4()), profile_id, prompt_slug, content)
)
conn.commit()
return result return result

View File

@ -75,8 +75,25 @@ export default function Analysis() {
const runPrompt = async (slug) => { const runPrompt = async (slug) => {
setLoading(slug); setError(null); setNewResult(null) setLoading(slug); setError(null); setNewResult(null)
try { try {
const result = await api.runInsight(slug) // Use new unified executor with save=true
setNewResult(result) const result = await api.executeUnifiedPrompt(slug, null, null, false, true)
// Transform result to match old format for InsightCard
let content = ''
if (result.type === 'pipeline') {
// For pipeline, extract final output
const finalOutput = result.output || {}
if (typeof finalOutput === 'object' && Object.keys(finalOutput).length === 1) {
content = Object.values(finalOutput)[0]
} else {
content = JSON.stringify(finalOutput, null, 2)
}
} else {
// For base prompts, use output directly
content = typeof result.output === 'string' ? result.output : JSON.stringify(result.output, null, 2)
}
setNewResult({ scope: slug, content })
await loadAll() await loadAll()
setTab('run') setTab('run')
} catch(e) { } catch(e) {

View File

@ -307,9 +307,10 @@ export const api = {
executePipeline: (configId=null) => req('/insights/pipeline' + (configId ? `?config_id=${configId}` : ''), json({})), executePipeline: (configId=null) => req('/insights/pipeline' + (configId ? `?config_id=${configId}` : ''), json({})),
// Unified Prompt System (Issue #28 Phase 2) // Unified Prompt System (Issue #28 Phase 2)
executeUnifiedPrompt: (slug, modules=null, timeframes=null, debug=false) => { executeUnifiedPrompt: (slug, modules=null, timeframes=null, debug=false, save=false) => {
const params = new URLSearchParams({ prompt_slug: slug }) const params = new URLSearchParams({ prompt_slug: slug })
if (debug) params.append('debug', 'true') if (debug) params.append('debug', 'true')
if (save) params.append('save', 'true')
const body = {} const body = {}
if (modules) body.modules = modules if (modules) body.modules = modules
if (timeframes) body.timeframes = timeframes if (timeframes) body.timeframes = timeframes