"""Helpers to load and persist the UserProfile from/to SQLite.""" from .db import get_profile, upsert_profile from .models import UserProfile def load_profile(session_id: str) -> UserProfile: """Fetch profile from DB, or create a fresh one if it doesn't exist.""" row = get_profile(session_id) if row: return UserProfile(**row) return UserProfile(session_id=session_id) def update_profile(profile: UserProfile, topic: str, gap_magnitude: int, solved: bool) -> UserProfile: """ Update the profile in-memory after a reasoning evaluation: - Increment weak_topics score for the identified topic - Recalculate avg_gap with exponential moving average (alpha=0.3) - Increment turn count and solved count """ topic_key = topic.strip().lower() profile.weak_topics[topic_key] = profile.weak_topics.get(topic_key, 0) + gap_magnitude # EMA for avg_gap alpha = 0.3 profile.avg_gap = (alpha * gap_magnitude) + ((1 - alpha) * profile.avg_gap) profile.total_turns += 1 if solved: profile.solved_problems += 1 return profile def persist_profile(profile: UserProfile) -> None: """Save the updated profile back to SQLite.""" upsert_profile(profile.model_dump()) def top_weak_topics(profile: UserProfile, n: int = 3) -> list[str]: """Return the top-N weakest topics by cumulative score.""" sorted_topics = sorted(profile.weak_topics.items(), key=lambda x: x[1], reverse=True) return [t[0] for t in sorted_topics[:n]]