import json from flask import Blueprint, render_template, request, redirect, url_for, flash from flask_login import login_required from core import utils from ai import models as ai_models from ai import setup as ai_setup from web.db import db, Persona persona_bp = Blueprint('persona', __name__) def _all_personas_dict(): """Return all personas as a dict keyed by name, matching the old personas.json structure.""" records = Persona.query.all() result = {} for rec in records: try: details = json.loads(rec.details_json) if rec.details_json else {} except Exception: details = {} result[rec.name] = details return result @persona_bp.route('/personas') @login_required def list_personas(): personas = _all_personas_dict() return render_template('personas.html', personas=personas) @persona_bp.route('/persona/new') @login_required def new_persona(): return render_template('persona_edit.html', persona={}, name="") @persona_bp.route('/persona/') @login_required def edit_persona(name): record = Persona.query.filter_by(name=name).first() if not record: flash(f"Persona '{name}' not found.") return redirect(url_for('persona.list_personas')) try: persona = json.loads(record.details_json) if record.details_json else {} except Exception: persona = {} return render_template('persona_edit.html', persona=persona, name=name) @persona_bp.route('/persona/save', methods=['POST']) @login_required def save_persona(): old_name = request.form.get('old_name') name = request.form.get('name') if not name: flash("Persona name is required.") return redirect(url_for('persona.list_personas')) persona_data = { "name": name, "bio": request.form.get('bio'), "age": request.form.get('age'), "gender": request.form.get('gender'), "race": request.form.get('race'), "nationality": request.form.get('nationality'), "language": request.form.get('language'), "sample_text": request.form.get('sample_text'), "voice_keywords": request.form.get('voice_keywords'), "style_inspirations": request.form.get('style_inspirations') } # If name changed, remove old record if old_name and old_name != name: old_record = Persona.query.filter_by(name=old_name).first() if old_record: db.session.delete(old_record) db.session.flush() record = Persona.query.filter_by(name=name).first() if record: record.details_json = json.dumps(persona_data) else: record = Persona(name=name, details_json=json.dumps(persona_data)) db.session.add(record) db.session.commit() flash(f"Persona '{name}' saved.") return redirect(url_for('persona.list_personas')) @persona_bp.route('/persona/delete/', methods=['POST']) @login_required def delete_persona(name): record = Persona.query.filter_by(name=name).first() if record: db.session.delete(record) db.session.commit() flash(f"Persona '{name}' deleted.") return redirect(url_for('persona.list_personas')) @persona_bp.route('/persona/analyze', methods=['POST']) @login_required def analyze_persona(): try: ai_setup.init_models() except: pass if not ai_models.model_logic: return {"error": "AI models not initialized."}, 500 data = request.json sample = data.get('sample_text', '') # Cache by a hash of the inputs to avoid redundant API calls for unchanged data cache_key = utils.make_cache_key( "persona_analyze", data.get('name', ''), data.get('age', ''), data.get('gender', ''), data.get('nationality', ''), sample[:500] ) cached = utils.get_ai_cache(cache_key) if cached: return cached prompt = f""" ROLE: Literary Analyst TASK: Create or analyze an Author Persona profile. INPUT_DATA: - NAME: {data.get('name')} - DEMOGRAPHICS: Age: {data.get('age')} | Gender: {data.get('gender')} | Nationality: {data.get('nationality')} - SAMPLE_TEXT: {utils.truncate_to_tokens(sample, 750)} INSTRUCTIONS: 1. BIO: Write a 2-3 sentence description of the writing style. If sample is provided, analyze it. If not, invent a style that fits the demographics/name. 2. KEYWORDS: Comma-separated list of 3-5 adjectives describing the voice (e.g. Gritty, Whimsical, Sarcastic). 3. INSPIRATIONS: Comma-separated list of 1-3 famous authors or genres that this style resembles. OUTPUT_FORMAT (JSON): {{ "bio": "String", "voice_keywords": "String", "style_inspirations": "String" }} """ try: response = ai_models.model_logic.generate_content(prompt) result = json.loads(utils.clean_json(response.text)) utils.set_ai_cache(cache_key, result) return result except Exception as e: return {"error": str(e)}, 500