FIT_PLAN_2 / model_api.py
Karthik71212's picture
Update model_api.py
d061432 verified
import os, time
def query_model(prompt, max_tokens=1500):
"""Single Groq API call."""
GROQ_API_KEY = os.getenv("GROQ_API_KEY", "").strip()
if not GROQ_API_KEY:
raise ValueError(
"GROQ_API_KEY is not set.\n\n"
"Steps to fix:\n"
"1. Go to https://console.groq.com β†’ sign up free\n"
"2. Click 'API Keys' β†’ 'Create API Key'\n"
"3. Copy the key (starts with gsk_...)\n"
"4. In your HuggingFace Space β†’ Settings β†’ Variables and Secrets\n"
" β†’ Add secret: Name = GROQ_API_KEY, Value = gsk_xxxxxxxxxxxx\n"
"5. Restart the Space"
)
try:
from groq import Groq
client = Groq(api_key=GROQ_API_KEY)
response = client.chat.completions.create(
model="llama-3.3-70b-versatile", # fastest + best quality on Groq
messages=[
{
"role": "system",
"content": (
"You are a certified professional fitness trainer. "
"Provide detailed, structured, day-by-day workout plans exactly as instructed."
)
},
{"role": "user", "content": prompt}
],
max_tokens=max_tokens,
temperature=0.7,
)
return response.choices[0].message.content
except Exception as e:
err = str(e)
if "401" in err or "invalid_api_key" in err.lower() or "authentication" in err.lower():
raise ValueError(
"❌ Groq API key is invalid or expired.\n\n"
"Fix: Go to https://console.groq.com/keys β†’ create a new key β†’ "
"update GROQ_API_KEY in your HuggingFace Space Secrets."
) from None
if "429" in err or "rate_limit" in err.lower() or "rate limit" in err.lower():
raise ValueError(
"⏳ Groq rate limit hit. Wait 60 seconds and try again.\n\n"
"Free tier: 6,000 tokens/min. Upgrade at console.groq.com for higher limits."
) from None
if "503" in err or "504" in err or "timeout" in err.lower():
raise ValueError(
"⚑ Groq server busy. Please try again in a few seconds."
) from None
raise
def query_model_chunked(name, gender, height, weight, goal, fitness_level,
equipment, days_per_week=5, months=1,
progress_callback=None):
"""
Generate a long plan by splitting into chunks of 3 days each.
Groq is fast enough (~2-3s per chunk) so even 30-day plans finish in ~30s.
progress_callback(chunk_num, total_chunks, days_done, total_days) β€” optional.
Returns: (full_plan_text, bmi, bmi_cat)
"""
from prompt_builder import calculate_bmi, bmi_category, bmi_advice
bmi = calculate_bmi(weight, height)
bmi_cat = bmi_category(bmi)
advice = bmi_advice(bmi_cat)
total_days = min(days_per_week * 4 * months, 30)
eq_list = ", ".join(equipment) if equipment else "Bodyweight only"
intensity_map = {
"Beginner": "2–3 sets, moderate weight, 90s rest. Prioritise form.",
"Intermediate": "3–4 sets, progressive overload, 60–75s rest.",
"Advanced": "4–5 sets, heavy compounds, 45–60s rest, supersets.",
}
intensity = intensity_map.get(fitness_level, "3 sets, 60s rest.")
CHUNK_SIZE = 3 # 3 days per call β€” well within Groq token limits
chunks = []
start_day = 1
total_chunks = max(1, (total_days + CHUNK_SIZE - 1) // CHUNK_SIZE)
while start_day <= total_days:
end_day = min(start_day + CHUNK_SIZE - 1, total_days)
chunk_num = len(chunks) + 1
if progress_callback:
progress_callback(chunk_num, total_chunks, start_day - 1, total_days)
context_note = ""
if chunks:
context_note = (
f"(Continue from Day {start_day}. Do NOT repeat Days 1–{start_day-1}. "
f"Vary muscle groups from previous days.)"
)
prompt = f"""Fitness trainer. Generate ONLY Days {start_day}-{end_day} of a {total_days}-day plan.
Client: {name}, {gender}, {height}cm/{weight}kg, BMI {bmi:.1f} ({bmi_cat}), Goal: {goal}, Level: {fitness_level}, Equipment: {eq_list}.
{context_note}
For EACH day use EXACTLY this format:
## Day N - [Muscle Group]
**Warm-Up** - Exercise: 2x10, Exercise: 2x10
**Main Workout**
- ExerciseName β€” 3x12 reps (rest 60s)
- ExerciseName β€” 3x12 reps (rest 60s)
- ExerciseName β€” 3x12 reps (rest 60s)
- ExerciseName β€” 3x12 reps (rest 60s)
- ExerciseName β€” 3x12 reps (rest 60s)
**Cool-Down** - Stretch1, Stretch2
Rules: vary muscle groups each day, {fitness_level} appropriate, use {eq_list} only, intensity: {intensity}.
{"End with 1 motivational sentence for "+name+"." if end_day == total_days else "No motivational text β€” more days follow."}
Output ONLY Days {start_day}-{end_day}. No preamble. No extra text.
"""
# Retry up to 3 times on transient errors
for attempt in range(3):
try:
chunk_text = query_model(prompt, max_tokens=1500)
break
except ValueError as e:
# Rate limit β€” wait and retry
err = str(e)
if "rate limit" in err.lower() and attempt < 2:
wait = 65 # wait just over 1 minute for rate limit window to reset
if progress_callback:
progress_callback(chunk_num, total_chunks, start_day - 1, total_days,
status=f"Rate limit β€” waiting {wait}s…")
time.sleep(wait)
continue
raise
except Exception as e:
err = str(e)
if attempt < 2:
time.sleep(5 * (attempt + 1))
continue
raise
chunks.append(chunk_text.strip())
start_day = end_day + 1
# Small pause between chunks to respect rate limits on free tier
if start_day <= total_days:
time.sleep(1)
if progress_callback:
progress_callback(total_chunks, total_chunks, total_days, total_days)
full_plan = "\n\n".join(chunks)
return full_plan, bmi, bmi_cat