Macbook
Add FastAPI application
cc4ea58
raw
history blame
8.09 kB
"""
AI Feedback Module - GPT-4o powered speech therapy feedback.
Uses GitHub Models API for GPT-4o access.
"""
import os
import json
import logging
from typing import Optional, List
from dataclasses import dataclass
from openai import OpenAI
logger = logging.getLogger(__name__)
@dataclass
class AIFeedbackResult:
"""AI-generated feedback for speech therapy."""
feedback: str
encouragement: str
specific_tips: List[str]
recommended_exercises: List[str]
difficulty_adjustment: Optional[str] # "easier", "same", "harder"
class AIFeedbackGenerator:
"""
Generate personalized speech therapy feedback using GPT-4o.
Uses GitHub Models API (free for GitHub users).
"""
def __init__(self):
self.client: Optional[OpenAI] = None
self.model = "gpt-4o"
self._initialize_client()
def _initialize_client(self):
"""Initialize the OpenAI client with GitHub Models."""
github_token = os.getenv("GITHUB_TOKEN")
if not github_token:
raise ValueError(
"GITHUB_TOKEN not found. Please set it in your .env file. "
"Get your token at: https://github.com/settings/tokens"
)
# Use GitHub Models (free GPT-4o access)
self.client = OpenAI(
base_url="https://models.inference.ai.azure.com",
api_key=github_token,
)
self.model = "gpt-4o"
logger.info("AI Feedback: Using GitHub Models (GPT-4o)")
async def generate_feedback(
self,
target_text: str,
transcription: str,
overall_score: float,
clarity_score: float,
pace_score: float,
fluency_score: float,
errors: List[dict],
user_context: Optional[dict] = None
) -> AIFeedbackResult:
"""
Generate personalized feedback for a speech exercise attempt.
Args:
target_text: The text the user was supposed to say
transcription: What the ASR heard
overall_score: 0-100 overall score
clarity_score: 0-100 clarity score
pace_score: 0-100 pace score
fluency_score: 0-100 fluency score
errors: List of pronunciation errors detected
user_context: Optional user profile info (speech condition, etc.)
Returns:
AIFeedbackResult with personalized feedback
"""
# Build context about user if available
user_info = ""
if user_context:
condition = user_context.get("speech_condition", "")
severity = user_context.get("severity_level", "")
if condition:
user_info = f"\nUser has {condition}"
if severity:
user_info += f" (severity: {severity}/5)"
user_info += ". Adjust feedback accordingly."
# Format errors for the prompt
error_summary = ""
if errors:
error_items = []
for e in errors[:5]: # Limit to 5 errors
error_items.append(
f"- '{e.get('expected', '')}' → '{e.get('actual', '')}' ({e.get('error_type', '')})"
)
error_summary = "\n".join(error_items)
system_prompt = """You are a supportive, encouraging speech therapist helping users improve their speech clarity.
Your feedback should be:
- Warm and encouraging, never discouraging
- Specific and actionable
- Age-appropriate and easy to understand
- Focused on progress, not perfection
Always acknowledge effort and provide constructive guidance."""
user_prompt = f"""Please provide feedback for this speech exercise attempt:
**Target phrase:** "{target_text}"
**User said:** "{transcription}"
**Scores:**
- Overall: {overall_score:.0f}/100
- Clarity: {clarity_score:.0f}/100
- Pace: {pace_score:.0f}/100
- Fluency: {fluency_score:.0f}/100
**Pronunciation differences:**
{error_summary if error_summary else "No major differences detected"}
{user_info}
Please respond in this JSON format:
{{
"feedback": "2-3 sentences of overall feedback",
"encouragement": "A short encouraging message",
"specific_tips": ["tip 1", "tip 2", "tip 3"],
"recommended_exercises": ["exercise 1", "exercise 2"],
"difficulty_adjustment": "easier" or "same" or "harder"
}}"""
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.7,
max_tokens=500,
response_format={"type": "json_object"}
)
# Parse the response
result = json.loads(response.choices[0].message.content)
return AIFeedbackResult(
feedback=result.get("feedback", "Good effort! Keep practicing."),
encouragement=result.get("encouragement", "You're making progress!"),
specific_tips=result.get("specific_tips", []),
recommended_exercises=result.get("recommended_exercises", []),
difficulty_adjustment=result.get("difficulty_adjustment", "same")
)
async def generate_session_summary(
self,
session_stats: dict,
attempts: List[dict]
) -> str:
"""Generate an AI summary of a therapy session."""
prompt = f"""Summarize this speech therapy session for the user:
**Session Stats:**
- Duration: {session_stats.get('duration_minutes', 0)} minutes
- Exercises completed: {session_stats.get('exercise_count', 0)}
- Average score: {session_stats.get('average_score', 0):.0f}/100
- Best score: {session_stats.get('best_score', 0):.0f}/100
**Exercise Types Practiced:** {', '.join(session_stats.get('exercise_types', []))}
Please provide a brief, encouraging 2-3 sentence summary of their session."""
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "You are a supportive speech therapist providing session summaries."},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=150
)
return response.choices[0].message.content
async def generate_weekly_insights(
self,
weekly_data: dict
) -> dict:
"""Generate AI-powered weekly progress insights."""
prompt = f"""Analyze this user's weekly speech therapy progress:
**This Week:**
- Sessions: {weekly_data.get('sessions_this_week', 0)}
- Total practice time: {weekly_data.get('practice_minutes', 0)} minutes
- Average score: {weekly_data.get('avg_score', 0):.0f}/100
- Score change from last week: {weekly_data.get('score_change', 0):+.1f}%
**Strengths:** {', '.join(weekly_data.get('strengths', ['Consistent practice']))}
**Areas to improve:** {', '.join(weekly_data.get('weaknesses', ['Continue practicing']))}
Provide a JSON response with:
{{
"summary": "2-3 sentence progress summary",
"celebration": "Something specific to celebrate",
"focus_area": "One specific thing to focus on next week",
"goal": "A realistic goal for next week"
}}"""
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "You are an encouraging speech therapist analyzing weekly progress."},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=300,
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
# Singleton instance
_feedback_generator: Optional[AIFeedbackGenerator] = None
def get_ai_feedback_generator() -> AIFeedbackGenerator:
"""Get or create AIFeedbackGenerator singleton."""
global _feedback_generator
if _feedback_generator is None:
_feedback_generator = AIFeedbackGenerator()
return _feedback_generator