SIMPLE_AI / personality.py
tiahchia's picture
Update personality.py
e65594d verified
import json
import logging
import os
from typing import List, Optional, Tuple
from openai import OpenAI
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: # pragma: no cover
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): # pragma: no cover
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: # pragma: no cover
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