File size: 7,894 Bytes
c45cbe3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | """
Fine-tuned persona model for generating coaching feedback
This module provides a local alternative to the Gemini API by using
fine-tuned transformer models for each persona.
"""
import os
import torch
from pathlib import Path
from transformers import AutoTokenizer, AutoModelForCausalLM
class PersonaModel:
"""Fine-tuned model for generating persona-specific coaching feedback"""
def __init__(self, persona_name, model_dir='./models'):
"""
Initialize persona model
Args:
persona_name: Name of persona (e.g., "Hype Beast")
model_dir: Directory containing fine-tuned models
"""
self.persona_name = persona_name
persona_safe = persona_name.lower().replace(' ', '_')
model_path = Path(model_dir) / f'persona_{persona_safe}'
if not model_path.exists():
raise FileNotFoundError(
f"Model not found at {model_path}. "
f"Please fine-tune the model first using scripts/fine_tune_persona.py"
)
print(f"Loading persona model: {persona_name} from {model_path}")
self.tokenizer = AutoTokenizer.from_pretrained(model_path)
self.model = AutoModelForCausalLM.from_pretrained(model_path)
self.model.eval() # Set to evaluation mode
def generate_feedback(self, objective_report, max_length=512, temperature=0.7):
"""
Generate coaching feedback for given objective report
Args:
objective_report: Objective performance report string
max_length: Maximum length of generated text
temperature: Sampling temperature (higher = more creative)
Returns:
Generated feedback string
"""
prompt = f"<|persona|>{self.persona_name}<|input|>{objective_report}<|output|>"
inputs = self.tokenizer(
prompt,
return_tensors="pt",
truncation=True,
max_length=512
)
with torch.no_grad():
outputs = self.model.generate(
**inputs,
max_length=max_length,
temperature=temperature,
do_sample=True,
pad_token_id=self.tokenizer.eos_token_id,
eos_token_id=self.tokenizer.convert_tokens_to_ids('<|endoftext|>') if '<|endoftext|>' in self.tokenizer.get_vocab() else self.tokenizer.eos_token_id,
)
generated_text = self.tokenizer.decode(outputs[0], skip_special_tokens=False)
# Extract output part
if '<|output|>' in generated_text:
output = generated_text.split('<|output|>')[-1]
if '<|endoftext|>' in output:
output = output.split('<|endoftext|>')[0]
return output.strip()
return generated_text
def get_persona_feedback(objective_report, persona_name, use_fine_tuned=True, fallback_to_gemini=True):
"""
Get persona feedback, using fine-tuned model if available, otherwise Gemini API
Args:
objective_report: Objective performance report
persona_name: Name of persona
use_fine_tuned: Try to use fine-tuned model first
fallback_to_gemini: Fall back to Gemini API if fine-tuned model fails
Returns:
Feedback string
"""
# Try fine-tuned model first
if use_fine_tuned:
try:
model = PersonaModel(persona_name)
feedback = model.generate_feedback(objective_report)
print(f"✅ Generated feedback using fine-tuned model for {persona_name}")
return feedback
except FileNotFoundError:
print(f"⚠️ Fine-tuned model not found for {persona_name}, falling back to Gemini")
except Exception as e:
print(f"⚠️ Error using fine-tuned model: {e}, falling back to Gemini")
# Fall back to Gemini API
if fallback_to_gemini:
try:
import google.generativeai as genai
api_key = os.environ.get('GEMINI_API_KEY')
if not api_key:
config_path = Path(__file__).parent.parent / 'config.txt'
if config_path.exists():
api_key = config_path.read_text().strip()
if not api_key:
raise ValueError("GEMINI_API_KEY not found")
genai.configure(api_key=api_key)
# Persona system instructions
PERSONAS = {
"Hype Beast": "You are The Hype Beast. Your tone is ultra-motivational, energetic, and uses enthusiastic modern slang. You focus on confidence, confidence, confidence, bringing the energy, and framing corrections as leveling up. Use many emojis and exclamation points. Make the user feel like a star. DO NOT use LaTeX formatting like $...$ for scores or percentages.",
"Data Scientist": "You are The Data Scientist. You speak in precise, objective terms. Your feedback uses specific metrics and quantified improvement. Translate complex biomechanical terms into clear, actionable, technical advice that a novice can understand. Use a formal, structured report format. DO NOT use LaTeX formatting like $...$ for scores or percentages.",
"No-Nonsense Pro": "You are The No-Nonsense Pro. You are direct, challenging, and slightly impatient with wasted effort. Your language is concise and demanding. Emphasize immediate correction and demand a higher standard of execution. Focus on 'why' the bad form wastes energy and must be fixed NOW. DO NOT use LaTeX formatting like $...$ for scores or percentages.",
"Mindful Aligner": "You are The Mindful Aligner. Your tone is calm, centered, and encouraging. You focus on connecting movement to breath, finding internal stability, and making gentle, internal adjustments to achieve proper alignment. Use soft, encouraging language. DO NOT use LaTeX formatting like $...$ for scores or percentages.",
}
system_instruction = PERSONAS.get(persona_name, PERSONAS["Hype Beast"])
prompt = f"""Based on the following objective performance report for a workout, you must adopt the selected persona and provide detailed, actionable coaching feedback. Start your response with a clear, concise title using a markdown H1 heading (# TITLE). DO NOT use LaTeX commands like $...$ or \\frac{{}}{{}} for scores, percentages, or numbers. Use plain text and standard Unicode symbols (like the percent sign %).
**OBJECTIVE PERFORMANCE DATA:**
---
{objective_report}
---"""
# Try available models
model_names = [
"gemini-1.5-flash-latest",
"gemini-1.5-flash-002",
"gemini-1.5-pro-latest",
"gemini-pro",
]
for model_name in model_names:
try:
model = genai.GenerativeModel(
model_name=model_name,
system_instruction=system_instruction
)
response = model.generate_content(prompt)
print(f"✅ Generated feedback using Gemini API ({model_name})")
return response.text
except Exception as e:
continue
raise Exception("All Gemini models failed")
except Exception as e:
raise Exception(f"Failed to generate feedback: {e}")
raise Exception("No feedback generation method available")
|