| import json |
| import logging |
| import os |
| from typing import List, Optional, Tuple |
|
|
| from openai import OpenAI |
|
|
| from backend import db |
|
|
| LOGGER = logging.getLogger(__name__) |
| PERSONALITY_MODEL = "gpt-4-turbo" |
| _CLIENT: Optional[OpenAI] = None |
|
|
|
|
| def _get_client() -> OpenAI: |
| global _CLIENT |
| if _CLIENT is None: |
| api_key = os.getenv("OPENAI_API_KEY") |
| if not api_key: |
| raise RuntimeError("OPENAI_API_KEY is not set. Please configure the environment.") |
| _CLIENT = OpenAI(api_key=api_key) |
| return _CLIENT |
|
|
|
|
| def _format_conversation_log(conversation_log: List[str]) -> str: |
| lines = [] |
| for idx, entry in enumerate(conversation_log, start=1): |
| lines.append(f"{idx}. {entry.strip()}") |
| return "\n".join(lines) |
|
|
|
|
| def _build_personality_prompt(conversation_log: List[str]) -> str: |
| formatted_log = _format_conversation_log(conversation_log) |
| return ( |
| "Analyze the following conversation snippets to characterize the user. " |
| "Focus on their tone, conversational behavior, interests, and emotional patterns. " |
| "Respond with compact JSON containing the keys 'personality_summary' and 'preferences'.\n\n" |
| f"Conversation snippets:\n{formatted_log}" |
| ) |
|
|
|
|
| def _parse_personality_response(content: str) -> Tuple[str, Optional[str]]: |
| try: |
| payload = json.loads(content) |
| summary = payload.get("personality_summary", "").strip() |
| preferences = payload.get("preferences") |
| if isinstance(preferences, str): |
| preferences = preferences.strip() |
| elif preferences is not None: |
| preferences = json.dumps(preferences, ensure_ascii=False) |
| return summary, preferences |
| except json.JSONDecodeError: |
| return content.strip(), None |
|
|
|
|
| def update_user_personality(user_id: str, conversation_log: List[str]) -> Optional[str]: |
| """Analyze recent conversations and persist an updated personality profile.""" |
| if not conversation_log: |
| LOGGER.debug("No conversation log provided for user %s; skipping personality update.", user_id) |
| return None |
|
|
| prompt = _build_personality_prompt(conversation_log) |
|
|
| try: |
| client = _get_client() |
| except RuntimeError: |
| LOGGER.exception("Cannot update personality without OPENAI_API_KEY") |
| return None |
|
|
| try: |
| response = client.chat.completions.create( |
| model=PERSONALITY_MODEL, |
| messages=[ |
| { |
| "role": "system", |
| "content": ( |
| "You are a mirror of the user that distills enduring personality insights from user conversations. " |
| "Provide grounded, respectful observations without speculation. Act as a behavioral buffer: when you " |
| "sense unproductive or negative patterns, offer gentle reframes, actionable nudges, and reflective " |
| "questions that support autonomy. Reinforce any progress—no matter how small—while staying humble and " |
| "non-judgmental. Monitor the user's emotional tone and engagement, tailoring the intensity of nudges " |
| "to their receptivity so they never feel overwhelmed. Prioritize encouragement and autonomy, avoid " |
| "manipulation, and regularly recalibrate guidance to prevent reinforcing negative patterns or " |
| "overstepping boundaries." |
| ), |
| }, |
| {"role": "user", "content": prompt}, |
| ], |
| temperature=0.6, |
| ) |
| except Exception as exc: |
| LOGGER.exception("OpenAI personality update failed for user %s: %s", user_id, exc) |
| return None |
|
|
| try: |
| content = response.choices[0].message.content.strip() |
| except (AttributeError, IndexError): |
| LOGGER.error("Malformed OpenAI response when updating personality for user %s", user_id) |
| return None |
|
|
| summary, preferences = _parse_personality_response(content) |
| if not summary: |
| LOGGER.warning("Received empty personality summary for user %s", user_id) |
| return None |
|
|
| try: |
| db.update_user_personality(user_id, summary, preferences=preferences) |
| except Exception as exc: |
| LOGGER.exception("Failed to persist personality summary for user %s: %s", user_id, exc) |
| return None |
|
|
| LOGGER.info("Updated personality profile for user %s", user_id) |
| return summary |
|
|