Spaces:
Sleeping
Sleeping
Alon Albalak
major update: data saved to hf, user sessions maintain separation, fixed scoring bug
36d5e94
| """Session management and progress tracking functionality""" | |
| import uuid | |
| class SessionManager: | |
| """Manages user session state and progress tracking""" | |
| def __init__(self, session_id=None, data_manager=None, achievement_system=None): | |
| self.session_id = session_id or str(uuid.uuid4()) | |
| self.data_manager = data_manager | |
| self.achievement_system = achievement_system | |
| def get_session_statistics(self): | |
| """Get statistics for the current session.""" | |
| if not self.data_manager: | |
| return self._get_empty_session_stats() | |
| all_results = self.data_manager.get_results() | |
| session_results = self.data_manager.filter_results_by_session(all_results, self.session_id) | |
| if not session_results: | |
| return self._get_empty_session_stats() | |
| scores = [r["cosine_distance"] for r in session_results] | |
| unique_prompts = set((r["prompt"], r["llm_partial_response"]) for r in session_results) | |
| return { | |
| "total_attempts": len(session_results), | |
| "best_score": max(scores), | |
| "average_score": sum(scores) / len(scores), | |
| "total_tokens_used": sum(r["num_user_tokens"] for r in session_results), | |
| "prompts_tried": len(unique_prompts), | |
| "session_results": session_results | |
| } | |
| def _get_empty_session_stats(self): | |
| """Return empty session statistics structure""" | |
| return { | |
| "total_attempts": 0, | |
| "best_score": 0.0, | |
| "average_score": 0.0, | |
| "total_tokens_used": 0, | |
| "prompts_tried": 0 | |
| } | |
| def generate_session_progress_html(self, template_renderer): | |
| """Generate HTML for session progress display.""" | |
| stats = self.get_session_statistics() | |
| if stats["total_attempts"] == 0: | |
| return template_renderer.load_template("session-progress-empty.html") | |
| # Create achievement summary for session | |
| session_achievements = set() | |
| if self.achievement_system and "session_results" in stats: | |
| for result in stats["session_results"]: | |
| achievements = self.achievement_system.determine_achievement_titles( | |
| result["cosine_distance"], | |
| result["num_user_tokens"] | |
| ) | |
| session_achievements.update(achievements) | |
| achievement_badges = "" | |
| if session_achievements: | |
| badges_html = "" | |
| for achievement in list(session_achievements)[:5]: # Show top 5 | |
| badges_html += template_renderer.load_template("achievement-badge.html", achievement=achievement) | |
| achievement_badges = template_renderer.load_template("session-achievements.html", | |
| achievement_badges=badges_html) | |
| # Recent attempts preview | |
| recent_attempts = sorted(stats["session_results"], | |
| key=lambda x: x["timestamp"], | |
| reverse=True)[:3] | |
| attempts_html = "" | |
| for i, attempt in enumerate(recent_attempts, 1): | |
| score_color = "#4CAF50" if attempt["cosine_distance"] >= 0.3 else "#FF9800" if attempt["cosine_distance"] >= 0.15 else "#9E9E9E" | |
| user_input = attempt['user_continuation'][:30] + ('...' if len(attempt['user_continuation']) > 30 else '') | |
| plural = "s" if attempt["num_user_tokens"] != 1 else "" | |
| attempt_number = len(stats["session_results"]) - i + 1 | |
| attempts_html += template_renderer.load_template("attempt-item.html", | |
| attempt_number=attempt_number, | |
| user_input=user_input, | |
| score_color=score_color, | |
| cosine_distance=attempt["cosine_distance"], | |
| num_tokens=attempt["num_user_tokens"], | |
| plural=plural | |
| ) | |
| recent_html = template_renderer.load_template("recent-attempts.html", attempts=attempts_html) | |
| return template_renderer.load_template("session-progress-main.html", | |
| total_attempts=stats['total_attempts'], | |
| best_score=stats['best_score'], | |
| average_score=stats['average_score'], | |
| prompts_tried=stats['prompts_tried'], | |
| achievement_badges=achievement_badges, | |
| recent_html=recent_html | |
| ) | |
| def generate_session_page_html(self, template_renderer, statistics_calculator=None, scorer=None): | |
| """Generate comprehensive session page HTML with ranking information.""" | |
| stats = self.get_session_statistics() | |
| if stats["total_attempts"] == 0: | |
| empty_html = template_renderer.load_template("session-page-empty.html") | |
| return empty_html, "", "" | |
| # Calculate ranking statistics | |
| ranking_stats = {} | |
| if statistics_calculator and scorer and "session_results" in stats: | |
| ranking_stats = statistics_calculator.calculate_session_ranking_stats( | |
| stats["session_results"], self.data_manager, scorer | |
| ) | |
| # Main stats overview | |
| best_attempt = max(stats["session_results"], key=lambda x: x["cosine_distance"]) | |
| recent_attempts = sorted(stats["session_results"], key=lambda x: x["timestamp"], reverse=True)[:5] | |
| # Determine trend icon and message | |
| trend_icon = {"up": "π", "down": "π", "stable": "π"}.get(ranking_stats.get("ranking_trend", "stable"), "π") | |
| trend_message = { | |
| "up": "Improving!", | |
| "down": "Room to grow", | |
| "stable": "Consistent" | |
| }.get(ranking_stats.get("ranking_trend", "stable"), "Consistent") | |
| main_stats_html = f""" | |
| <div class="session-progress"> | |
| <div class="session-stats"> | |
| <div class="stat-item"> | |
| <div class="stat-value">{stats['total_attempts']}</div> | |
| <div class="stat-label">Creative Attempts</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-value">{stats['best_score']:.3f}</div> | |
| <div class="stat-label">Personal Best</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-value">{stats['average_score']:.3f}</div> | |
| <div class="stat-label">Average Score</div> | |
| </div>""" | |
| # Add ranking stats if available | |
| if ranking_stats.get("total_ranked_attempts", 0) > 0: | |
| main_stats_html += f""" | |
| <div class="stat-item ranking-highlight"> | |
| <div class="stat-value">#{ranking_stats['best_rank']}</div> | |
| <div class="stat-label">Best Rank</div> | |
| </div> | |
| <div class="stat-item ranking-highlight"> | |
| <div class="stat-value">{ranking_stats['average_percentile']:.0f}%</div> | |
| <div class="stat-label">Avg vs Others</div> | |
| </div>""" | |
| else: | |
| main_stats_html += f""" | |
| <div class="stat-item"> | |
| <div class="stat-value">{stats['prompts_tried']}</div> | |
| <div class="stat-label">Prompts Explored</div> | |
| </div>""" | |
| main_stats_html += f""" | |
| </div> | |
| <div class="session-overview"> | |
| <div class="best-response-showcase"> | |
| <h4>π― Your Best Response</h4> | |
| <div class="best-response-content"> | |
| <span class="best-input">"{best_attempt['user_continuation']}"</span> | |
| <div class="best-metrics"> | |
| <span class="score-badge">Score: {best_attempt['cosine_distance']:.3f}</span>""" | |
| # Add ranking info for best attempt if available | |
| if ranking_stats.get("total_ranked_attempts", 0) > 0 and "recent_percentiles" in ranking_stats: | |
| best_ranking = next((r for r in ranking_stats["recent_percentiles"] | |
| if abs(float(r["timestamp"].replace('-', '').replace(':', '').replace('T', '').replace('.', '')[:14]) - | |
| float(best_attempt["timestamp"].replace('-', '').replace(':', '').replace('T', '').replace('.', '')[:14])) < 1000), None) | |
| if best_ranking: | |
| main_stats_html += f""" | |
| <span class="rank-badge">#{best_ranking['rank']} of {best_ranking['total']}</span>""" | |
| main_stats_html += f""" | |
| </div> | |
| </div> | |
| </div>""" | |
| # Add trend information if sufficient data | |
| if ranking_stats.get("total_ranked_attempts", 0) >= 2: | |
| main_stats_html += f""" | |
| <div class="performance-trend"> | |
| {trend_icon} <strong>Ranking Trend:</strong> {trend_message} | |
| <span class="trend-detail">({ranking_stats['total_ranked_attempts']} ranked attempts)</span> | |
| </div>""" | |
| main_stats_html += f""" | |
| </div> | |
| </div> | |
| """ | |
| # Enhanced detailed history with ranking | |
| history_html = """ | |
| <div class="session-progress"> | |
| <div class="recent-attempts"> | |
| """ | |
| for i, attempt in enumerate(recent_attempts, 1): | |
| score_color = "#4CAF50" if attempt["cosine_distance"] >= 0.3 else "#FF9800" if attempt["cosine_distance"] >= 0.15 else "#9E9E9E" | |
| # Truncate long inputs for display | |
| display_input = attempt['user_continuation'][:50] + ('...' if len(attempt['user_continuation']) > 50 else '') | |
| # Find ranking info for this attempt | |
| ranking_info = None | |
| if "recent_percentiles" in ranking_stats: | |
| ranking_info = next((r for r in ranking_stats["recent_percentiles"] | |
| if r["timestamp"] == attempt["timestamp"]), None) | |
| history_html += f""" | |
| <div class="attempt-item enhanced"> | |
| <span class="attempt-number">#{len(stats["session_results"]) - i + 1}</span> | |
| <div class="attempt-content"> | |
| <div class="attempt-input">"{display_input}"</div> | |
| <div class="attempt-details"> | |
| <span class="timestamp">{attempt['timestamp'][:10]} {attempt['timestamp'][11:16]}</span>""" | |
| if ranking_info: | |
| # Add percentile indicator | |
| percentile = ranking_info["percentile"] | |
| percentile_color = "#4CAF50" if percentile >= 75 else "#FF9800" if percentile >= 50 else "#9E9E9E" | |
| percentile_icon = "π" if percentile >= 90 else "β" if percentile >= 75 else "π" if percentile >= 50 else "π―" | |
| history_html += f""" | |
| <span class="ranking-info" style="color: {percentile_color};"> | |
| {percentile_icon} #{ranking_info['rank']} of {ranking_info['total']} ({percentile:.0f}%) | |
| </span>""" | |
| else: | |
| history_html += f""" | |
| <span class="ranking-info" style="opacity: 0.6;"> | |
| π± First attempt at this prompt+token combo | |
| </span>""" | |
| history_html += f""" | |
| </div> | |
| </div> | |
| <div class="attempt-metrics"> | |
| <span class="attempt-score" style="color: {score_color};"> | |
| {attempt["cosine_distance"]:.3f} | |
| </span> | |
| <span class="token-count">{attempt["num_user_tokens"]} tokens</span> | |
| </div> | |
| </div> | |
| """ | |
| if len(recent_attempts) < len(stats["session_results"]): | |
| remaining = len(stats["session_results"]) - len(recent_attempts) | |
| history_html += f""" | |
| <div class="attempt-item" style="opacity: 0.6;"> | |
| <span class="attempt-number">...</span> | |
| <div class="attempt-content"> | |
| <div class="attempt-input">And {remaining} more creative attempts!</div> | |
| <div class="attempt-details"> | |
| <span class="ranking-info">π View all attempts for complete ranking history</span> | |
| </div> | |
| </div> | |
| <div class="attempt-metrics"> | |
| <span class="attempt-score">π</span> | |
| </div> | |
| </div> | |
| """ | |
| history_html += """ | |
| </div> | |
| </div> | |
| """ | |
| # Achievements | |
| all_achievements = set() | |
| if self.achievement_system and "session_results" in stats: | |
| for result in stats["session_results"]: | |
| achievements = self.achievement_system.determine_achievement_titles( | |
| result["cosine_distance"], | |
| result["num_user_tokens"] | |
| ) | |
| all_achievements.update(achievements) | |
| achievements_html = """ | |
| <div class="session-progress"> | |
| """ | |
| if all_achievements: | |
| achievements_html += """ | |
| <div class="session-achievements"> | |
| <div class="achievement-badges"> | |
| """ | |
| for achievement in sorted(all_achievements): | |
| achievements_html += f'<div class="achievement-badge">{achievement}</div>' | |
| achievements_html += """ | |
| </div> | |
| </div> | |
| """ | |
| else: | |
| achievements_html += """ | |
| <div class="session-tip"> | |
| π Keep being creative to unlock achievements! Try different token counts and creative approaches. | |
| </div> | |
| """ | |
| achievements_html += """ | |
| </div> | |
| """ | |
| return main_stats_html, history_html, achievements_html |