yoga_pose / app /services /correction.py
github-actions[bot]
Sync backend from GitHub commit 38216922784881d46b39e5fbe1705cc6bdc66f07
28e3ed6
Raw
History Blame Contribute Delete
11.2 kB
import os
import logging
import requests
from typing import Dict, Optional
logger = logging.getLogger(__name__)
# Biomechanical knowledge graph templates
BIOMECHANICAL_TEMPLATES = {
"warrior_2": {
"knee_l": {
"issue_low": {
"en": "Bend your left knee more to bring it directly over your ankle.",
"hi": "अपने बाएं घुटने को थोड़ा और मोड़ें ताकि वह टखने के ठीक ऊपर रहे।",
"bn": "আপনার বাম হাঁটু আরও বাঁকুন যাতে এটি গোড়ালির ঠিক উপরে থাকে।"
}
},
"knee_r": {
"issue_low": {
"en": "Bend your right knee more to align it directly over your ankle.",
"hi": "अपने दाएं घुटने को थोड़ा और मोड़ें ताकि वह टखने के ठीक ऊपर रहे।",
"bn": "আপনার ডান হাঁটু আরও বাঁকুন যাতে এটি গোড়ালির ঠিক উপরে থাকে।"
}
}
},
"chair_pose": {
"knee_l": {
"issue_low": {
"en": "Sit deeper to bring your thighs closer to parallel to the floor.",
"hi": "जांघों को फर्श के समानांतर लाने के लिए थोड़ा नीचे बैठें।",
"bn": "উরু মেঝের সমান্তরাল করার জন্য আর একটু নিচে বসুন।"
}
},
"knee_r": {
"issue_low": {
"en": "Sit deeper to align both thighs towards parallel to the floor.",
"hi": "दोनों जांघों को फर्श के समानांतर करने के लिए थोड़ा नीचे बैठें।",
"bn": "দুই উরু মেঝের সমান্তরাল করার জন্য আর একটু নিচে বসুন।"
}
}
},
"cobra_pose": {
"neck": {
"issue_low": {
"en": "Elongate your neck and gaze forward, relaxing your shoulders.",
"hi": "अपनी गर्दन को लंबी करें और सामने की ओर देखें, कंधों को ढीला छोड़ें।",
"bn": "আপনার ঘাড় সোজা করুন এবং সামনের দিকে তাকান, কাঁধ শিথিল রাখুন।"
}
}
},
"plank": {
"elbow_l": {
"issue_low": {
"en": "Extend your left elbow to keep your arm straight.",
"hi": "अपने बाएं हाथ को सीधा रखने के लिए अपनी बाईं कोहनी को फैलाएं।",
"bn": "আপনার বাম হাত সোজা রাখতে বাম কনুই প্রসারিত করুন।"
}
},
"elbow_r": {
"issue_low": {
"en": "Extend your right elbow to keep your arm straight.",
"hi": "अपने दाएं हाथ को सीधा रखने के लिए अपनी दाईं कोहनी को फैलाएं।",
"bn": "আপনার ডান হাত সোজা রাখতে ডান কনুই প্রসারিত করুন।"
}
},
"trunk_l": {
"issue_low": {
"en": "Keep your spine and body in a straight line, do not let your hips sag.",
"hi": "अपनी रीढ़ और शरीर को एक सीधी रेखा में रखें, अपने नितंबों को नीचे न झुकने दें।",
"bn": "আপনার মেরুদণ্ড এবং শরীর সোজা রাখুন, নিতম্ব ঝুলে যেতে দেবেন না।"
}
}
},
"tree_pose": {
"knee_l": {
"issue_low": {
"en": "Bend your left knee more and place your foot on your inner thigh.",
"hi": "अपने बाएं घुटने को अधिक मोड़ें और अपने पैर को अपनी आंतरिक जांघ पर रखें।",
"bn": "আপনার বাম হাঁটু আরও বাঁকুন এবং পায়ের পাতাটি উরুর ভেতরের দিকে রাখুন।"
}
},
"hip_abduct_l": {
"issue_low": {
"en": "Open your left hip outward to bring your knee to the side.",
"hi": "अपने बाएं कूल्हे को बाहर की ओर खोलें ताकि आपका घुटना बगल की ओर हो सके।",
"bn": "আপনার বাম হাঁটু পাশের দিকে আনতে বাম হিপ বাইরের দিকে প্রসারিত করুন।"
}
},
"knee_r": {
"issue_low": {
"en": "Keep your standing right leg fully straight and strong.",
"hi": "अपने खड़े हुए दाएं पैर को पूरी तरह से सीधा और मजबूत रखें।",
"bn": "দাঁড়িয়ে থাকা ডান পা সম্পূর্ণ সোজা এবং শক্ত রাখুন।"
}
}
},
"mountain_pose": {
"knee_l": {
"issue_low": {
"en": "Keep your left leg straight and engage your thigh muscles.",
"hi": "अपने बाएं पैर को सीधा रखें और अपनी जांघ की मांसपेशियों को सक्रिय करें।",
"bn": "আপনার বাম পা সোজা রাখুন এবং উরুর পেশী সক্রিয় করুন।"
}
},
"knee_r": {
"issue_low": {
"en": "Keep your right leg straight and engage your thigh muscles.",
"hi": "अपने दाएं पैर को सीधा रखें और अपनी जांघ की मांसपेशियों को सक्रिय करें।",
"bn": "আপনার ডান পা সোজা রাখুন এবং উরুর পেশী সক্রিয় করুন।"
}
},
"trunk_l": {
"issue_low": {
"en": "Lengthen your spine and stand tall with shoulders relaxed.",
"hi": "अपनी रीढ़ को लंबा करें और कंधों को आराम देते हुए सीधे खड़े हों।",
"bn": "মেরুদণ্ড সোজা করে বুক টানটান করে দাঁড়ান এবং কাঁধ শিথিল রাখুন।"
}
}
}
}
DEFAULT_CORRECTIONS = {
"en": "Maintain your posture, breathing steadily.",
"hi": "अपनी मुद्रा बनाए रखें, लगातार सांस लेते रहें।",
"bn": "আপনার অঙ্গভঙ্গি বজায় রাখুন, নিয়মিত শ্বাস নিন।"
}
def generate_safe_correction(
pose_id: str,
deviations: Dict[str, float],
language: str = "en",
groq_api_key: Optional[str] = None
) -> tuple:
"""
Enforces a 3-stage validation pipeline:
Stage 1: Symbolic Rules Engine + Local template matching.
Stage 2: Optional Groq API translation/paraphrase inside safe boundaries.
Stage 3: Post-generation safety validation screening.
"""
pose_templates = BIOMECHANICAL_TEMPLATES.get(pose_id, {})
# Stage 1: Find highest deviation joint exceeding safe threshold (10 degrees)
target_joint = None
max_dev = 0.0
for joint, dev_val in deviations.items():
if joint in pose_templates and dev_val > max_dev:
if dev_val > 10.0:
max_dev = dev_val
target_joint = joint
# Retrieve pre-approved template
if target_joint:
correction_text = pose_templates[target_joint]["issue_low"].get(language, "Adjust your alignment.")
else:
correction_text = DEFAULT_CORRECTIONS.get(language, "Adjust your alignment.")
is_safe = True
# Stage 2: Paraphrase using LLM if Groq API key is available
api_key = groq_api_key or os.environ.get("GROQ_API_KEY")
if api_key:
try:
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
# Map language code to full language name to instruct the LLM
lang_names = {"en": "English", "hi": "Hindi", "bn": "Bengali"}
target_lang_name = lang_names.get(language, "English")
payload = {
"model": "qwen/qwen3.6-27b",
"messages": [
{
"role": "system",
"content": f"You are a professional yoga instructor. Translate or paraphrase the following instruction: '{correction_text}' into {target_lang_name}. Do NOT suggest stretching further or pushing deeper. Keep it safe, concise (under 15 words), and respond ONLY in the {target_lang_name} language."
}
],
"temperature": 0.2,
"reasoning_format": "hidden"
}
response = requests.post("https://api.groq.com/openai/v1/chat/completions", json=payload, headers=headers, timeout=5)
if response.status_code == 200:
candidate_text = response.json()["choices"][0]["message"]["content"].strip()
# Stage 3: Post-Generation Safety Validation
forbidden_words = ["push", "force", "hurt", "pain", "stretch more", "further"]
if not any(word in candidate_text.lower() for word in forbidden_words):
correction_text = candidate_text
else:
is_safe = False # Flag candidate as unsafe; fallback to rule-based template
except Exception as e:
logger.warning(f"Groq correction call failed, falling back to template: {e}")
return correction_text, is_safe