fix: integrate placeholder resolver + JSON unwrapping (Issue #28)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

- Backend: integrate get_placeholder_example_values in execute_prompt_with_data
- Backend: now provides BOTH raw data AND processed placeholders
- Backend: unwrap Markdown-wrapped JSON (```json ... ```)
- Fixes old-style prompts that expect name, weight_trend, caliper_summary

Resolves unresolved placeholders issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-26 08:14:41 +01:00
parent 84dad07e15
commit afc70b5a95

View File

@ -64,6 +64,8 @@ def validate_json_output(output: str, schema: Optional[Dict] = None, debug_info:
"""
Validate that output is valid JSON.
Unwraps Markdown-wrapped JSON (```json ... ```) if present.
Args:
output: String to validate
schema: Optional JSON schema to validate against (TODO: jsonschema library)
@ -75,14 +77,28 @@ def validate_json_output(output: str, schema: Optional[Dict] = None, debug_info:
Raises:
HTTPException: If output is not valid JSON (with debug info attached)
"""
# Try to unwrap Markdown code blocks (common AI pattern)
unwrapped = output.strip()
if unwrapped.startswith('```json'):
# Extract content between ```json and ```
lines = unwrapped.split('\n')
if len(lines) > 2 and lines[-1].strip() == '```':
unwrapped = '\n'.join(lines[1:-1])
elif unwrapped.startswith('```'):
# Generic code block
lines = unwrapped.split('\n')
if len(lines) > 2 and lines[-1].strip() == '```':
unwrapped = '\n'.join(lines[1:-1])
try:
parsed = json.loads(output)
parsed = json.loads(unwrapped)
# TODO: Add jsonschema validation if schema provided
return parsed
except json.JSONDecodeError as e:
error_detail = {
"error": f"AI returned invalid JSON: {str(e)}",
"raw_output": output[:500] + ('...' if len(output) > 500 else ''),
"unwrapped": unwrapped[:500] if unwrapped != output else None,
"output_length": len(output)
}
if debug_info:
@ -352,6 +368,7 @@ async def execute_prompt_with_data(
Execution result dict
"""
from datetime import datetime, timedelta
from placeholder_resolver import get_placeholder_example_values
# Build variables from data modules
variables = {
@ -359,6 +376,16 @@ async def execute_prompt_with_data(
'today': datetime.now().strftime('%Y-%m-%d')
}
# Add PROCESSED placeholders (name, weight_trend, caliper_summary, etc.)
# This makes old-style prompts work with the new executor
try:
processed_placeholders = get_placeholder_example_values(profile_id)
variables.update(processed_placeholders)
except Exception as e:
# Continue even if placeholder resolution fails
if enable_debug:
variables['_placeholder_error'] = str(e)
# Load data for enabled modules
if modules:
with get_db() as conn: