All checks were successful
Deploy to Production / deploy (push) Successful in 2m23s
Includes frontend redesign (Navigation, billingApi), backend updates (auth routes, admin routes, LLM service refactor), MSAL removal, and dependency updates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
171 lines
No EOL
6.4 KiB
Python
Executable file
171 lines
No EOL
6.4 KiB
Python
Executable file
"""
|
|
Persona Profile Export Service
|
|
|
|
Generates beautifully formatted markdown profiles for individual personas using LLM processing.
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import logging
|
|
from typing import Dict, Any, Optional
|
|
from app.services.llm_service import LLMService
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class PersonaExportService:
|
|
"""Service for exporting individual persona profiles as formatted markdown."""
|
|
|
|
def __init__(self):
|
|
"""Initialize the persona export service."""
|
|
self.llm_service = LLMService()
|
|
self.prompt_template = self._load_prompt_template()
|
|
|
|
def _load_prompt_template(self) -> str:
|
|
"""Load the persona profile export prompt template."""
|
|
try:
|
|
prompt_path = os.path.join(
|
|
os.path.dirname(__file__),
|
|
"..",
|
|
"..",
|
|
"prompts",
|
|
"persona-profile-export.md"
|
|
)
|
|
with open(prompt_path, 'r', encoding='utf-8') as f:
|
|
return f.read()
|
|
except Exception as e:
|
|
logger.error(f"Failed to load persona export prompt template: {e}")
|
|
# Fallback prompt if file loading fails
|
|
return """
|
|
You are a professional documentation specialist. Transform the provided persona JSON data
|
|
into a well-structured, professional markdown document with proper headers, tables, lists,
|
|
and formatting. Include all available information organized logically with sections for
|
|
demographics, goals, personality traits, scenarios, and additional data.
|
|
"""
|
|
|
|
async def generate_profile_markdown(
|
|
self,
|
|
persona_data: Dict[str, Any],
|
|
llm_model: str = "gpt-5.4",
|
|
temperature: float = 0.3
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Generate a formatted markdown profile for a persona using LLM processing.
|
|
|
|
Args:
|
|
persona_data: Complete persona data as dictionary
|
|
llm_model: LLM model to use (default: gpt-5.4 for speed)
|
|
temperature: Temperature for LLM generation (lower for consistency)
|
|
|
|
Returns:
|
|
Dictionary containing:
|
|
- success: Boolean indicating success
|
|
- markdown_content: Generated markdown string
|
|
- error: Error message if failed
|
|
- persona_name: Name of the persona
|
|
"""
|
|
try:
|
|
# Validate input data
|
|
if not persona_data:
|
|
return {
|
|
"success": False,
|
|
"error": "No persona data provided",
|
|
"markdown_content": None,
|
|
"persona_name": None
|
|
}
|
|
|
|
persona_name = persona_data.get('name', 'Unknown Persona')
|
|
logger.info(f"🤖 Backend: Generating profile markdown for persona: {persona_name} using {llm_model}")
|
|
|
|
# Prepare the full prompt
|
|
persona_json = json.dumps(persona_data, indent=2, ensure_ascii=False)
|
|
full_prompt = f"{self.prompt_template}\n\n## Persona Data\n```json\n{persona_json}\n```"
|
|
|
|
# Generate markdown using LLM
|
|
markdown_content = await LLMService.generate_content(
|
|
prompt=full_prompt,
|
|
model_name=llm_model,
|
|
temperature=temperature,
|
|
max_tokens=4000 # Allow for comprehensive profiles
|
|
)
|
|
|
|
if not markdown_content:
|
|
return {
|
|
"success": False,
|
|
"error": "LLM failed to generate markdown content",
|
|
"markdown_content": None,
|
|
"persona_name": persona_name
|
|
}
|
|
|
|
markdown_content = markdown_content.strip()
|
|
|
|
# Basic validation of generated content
|
|
if len(markdown_content) < 100: # Too short, likely an error
|
|
logger.warning(f"Generated markdown seems too short for {persona_name}")
|
|
return {
|
|
"success": False,
|
|
"error": "Generated markdown content appears incomplete",
|
|
"markdown_content": None,
|
|
"persona_name": persona_name
|
|
}
|
|
|
|
logger.info(f"✅ Successfully generated profile markdown for {persona_name} ({len(markdown_content)} characters)")
|
|
|
|
return {
|
|
"success": True,
|
|
"markdown_content": markdown_content,
|
|
"persona_name": persona_name,
|
|
"model_used": llm_model,
|
|
"content_length": len(markdown_content)
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error generating persona profile markdown: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"Failed to generate markdown profile: {str(e)}",
|
|
"markdown_content": None,
|
|
"persona_name": persona_data.get('name', 'Unknown') if persona_data else None
|
|
}
|
|
|
|
def generate_fallback_markdown(self, persona_data: Dict[str, Any]) -> str:
|
|
"""
|
|
Generate a basic fallback markdown if LLM processing fails.
|
|
|
|
Args:
|
|
persona_data: Complete persona data as dictionary
|
|
|
|
Returns:
|
|
Basic markdown string
|
|
"""
|
|
try:
|
|
name = persona_data.get('name', 'Unknown Persona')
|
|
occupation = persona_data.get('occupation', 'Unknown')
|
|
age = persona_data.get('age', 'Unknown')
|
|
location = persona_data.get('location', 'Unknown')
|
|
|
|
# Create basic markdown structure
|
|
markdown = f"""# {name} - Complete Profile
|
|
|
|
## Overview
|
|
{name} is a {age} year old {occupation} based in {location}.
|
|
|
|
## Basic Information
|
|
- **Name:** {name}
|
|
- **Age:** {age}
|
|
- **Occupation:** {occupation}
|
|
- **Location:** {location}
|
|
|
|
## Raw Data
|
|
```json
|
|
{json.dumps(persona_data, indent=2, ensure_ascii=False)}
|
|
```
|
|
|
|
*This is a basic export. For enhanced formatting, please try again later.*
|
|
"""
|
|
return markdown
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to generate fallback markdown: {e}")
|
|
return f"# Persona Profile Export\n\nError generating profile. Raw data:\n\n```json\n{json.dumps(persona_data, indent=2)}\n```" |