Commit ·
1b10369
1
Parent(s): 795b7e9
Fix: Groq API key check both GROQ_API and GROQ_API_KEY, improve fallback with varied poses
Browse files
modules/art_reels/services/professional_stick_figure.py
CHANGED
|
@@ -130,12 +130,14 @@ class ProfessionalStickFigure:
|
|
| 130 |
CHARACTER_Y = 1100
|
| 131 |
|
| 132 |
def __init__(self, groq_api_key: str = None):
|
| 133 |
-
|
|
|
|
| 134 |
if self.groq_api_key:
|
| 135 |
self.groq = Groq(api_key=self.groq_api_key)
|
|
|
|
| 136 |
else:
|
| 137 |
self.groq = None
|
| 138 |
-
logger.warning("Groq API key not found - AI scene generation disabled")
|
| 139 |
|
| 140 |
# Load fonts
|
| 141 |
self._load_fonts()
|
|
@@ -203,11 +205,62 @@ Generate exactly {len(chunks)} scenes. Return ONLY valid JSON array."""
|
|
| 203 |
return self._generate_fallback_scenes(chunks)
|
| 204 |
|
| 205 |
def _generate_fallback_scenes(self, chunks: List[Dict]) -> List[Dict]:
|
| 206 |
-
"""Fallback scene generation"""
|
| 207 |
scenes = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
for i, chunk in enumerate(chunks):
|
| 209 |
text = chunk.get("text", "").lower()
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
# Detect scene type
|
| 212 |
scene_type = "single"
|
| 213 |
if any(word in text for word in ["vs", "versus", "compare", "between", "both", "two"]):
|
|
@@ -215,16 +268,25 @@ Generate exactly {len(chunks)} scenes. Return ONLY valid JSON array."""
|
|
| 215 |
elif any(word in text for word in ["introduce", "what is", "explained", "definition"]):
|
| 216 |
scene_type = "title"
|
| 217 |
|
| 218 |
-
|
|
|
|
| 219 |
"chunk_id": i,
|
| 220 |
"scene_type": scene_type,
|
| 221 |
"background": "white",
|
| 222 |
"title_text": text[:30].upper() if scene_type == "title" else None,
|
| 223 |
"characters": [
|
| 224 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
] if scene_type != "title" else [],
|
| 226 |
"caption": text[:50] + "..." if len(text) > 50 else text
|
| 227 |
-
}
|
|
|
|
|
|
|
|
|
|
| 228 |
|
| 229 |
return scenes
|
| 230 |
|
|
|
|
| 130 |
CHARACTER_Y = 1100
|
| 131 |
|
| 132 |
def __init__(self, groq_api_key: str = None):
|
| 133 |
+
# Try both GROQ_API (config) and GROQ_API_KEY env vars
|
| 134 |
+
self.groq_api_key = groq_api_key or os.environ.get("GROQ_API") or os.environ.get("GROQ_API_KEY")
|
| 135 |
if self.groq_api_key:
|
| 136 |
self.groq = Groq(api_key=self.groq_api_key)
|
| 137 |
+
logger.info("Groq API initialized for AI scene generation")
|
| 138 |
else:
|
| 139 |
self.groq = None
|
| 140 |
+
logger.warning("Groq API key not found (GROQ_API or GROQ_API_KEY) - AI scene generation disabled")
|
| 141 |
|
| 142 |
# Load fonts
|
| 143 |
self._load_fonts()
|
|
|
|
| 205 |
return self._generate_fallback_scenes(chunks)
|
| 206 |
|
| 207 |
def _generate_fallback_scenes(self, chunks: List[Dict]) -> List[Dict]:
|
| 208 |
+
"""Fallback scene generation with varied poses and emotions"""
|
| 209 |
scenes = []
|
| 210 |
+
|
| 211 |
+
# Pose keywords for text detection
|
| 212 |
+
pose_keywords = {
|
| 213 |
+
"walking": ["walk", "walked", "forward", "step", "move", "journey"],
|
| 214 |
+
"running": ["run", "ran", "fast", "quick", "rush", "hurry"],
|
| 215 |
+
"sitting": ["sit", "sat", "rest", "relax", "seat", "chair"],
|
| 216 |
+
"sleeping": ["sleep", "slept", "tired", "exhausted", "rest", "night"],
|
| 217 |
+
"thinking": ["think", "thought", "wonder", "consider", "decide", "choice"],
|
| 218 |
+
"jumping": ["jump", "leap", "excited", "joy", "success", "win"],
|
| 219 |
+
"celebrating": ["celebrate", "success", "victory", "won", "achieve", "happy"],
|
| 220 |
+
"pointing": ["point", "show", "look", "direction", "this", "that"],
|
| 221 |
+
"talking": ["said", "told", "speak", "talk", "explain", "teach"],
|
| 222 |
+
"waving": ["hello", "hi", "bye", "goodbye", "wave", "greeting"]
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
# Emotion keywords
|
| 226 |
+
emotion_keywords = {
|
| 227 |
+
"happy": ["happy", "joy", "success", "win", "good", "great", "smile"],
|
| 228 |
+
"sad": ["sad", "fail", "failed", "lost", "wrong", "bad", "sorry"],
|
| 229 |
+
"thinking": ["think", "wonder", "decide", "choose", "question"],
|
| 230 |
+
"excited": ["excited", "wow", "amazing", "incredible", "jump", "celebrate"],
|
| 231 |
+
"surprised": ["surprise", "shock", "unexpected", "suddenly"],
|
| 232 |
+
"confused": ["confused", "wonder", "how", "why", "what"]
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
# Default poses for variety cycling
|
| 236 |
+
default_poses = ["standing", "walking", "pointing", "thinking", "talking", "waving"]
|
| 237 |
+
default_emotions = ["happy", "thinking", "excited", "happy"]
|
| 238 |
+
|
| 239 |
for i, chunk in enumerate(chunks):
|
| 240 |
text = chunk.get("text", "").lower()
|
| 241 |
|
| 242 |
+
# Detect pose from text
|
| 243 |
+
detected_pose = None
|
| 244 |
+
for pose, keywords in pose_keywords.items():
|
| 245 |
+
if any(kw in text for kw in keywords):
|
| 246 |
+
detected_pose = pose
|
| 247 |
+
break
|
| 248 |
+
|
| 249 |
+
# If no pose detected, cycle through defaults
|
| 250 |
+
if not detected_pose:
|
| 251 |
+
detected_pose = default_poses[i % len(default_poses)]
|
| 252 |
+
|
| 253 |
+
# Detect emotion from text
|
| 254 |
+
detected_emotion = None
|
| 255 |
+
for emotion, keywords in emotion_keywords.items():
|
| 256 |
+
if any(kw in text for kw in keywords):
|
| 257 |
+
detected_emotion = emotion
|
| 258 |
+
break
|
| 259 |
+
|
| 260 |
+
# If no emotion detected, cycle through defaults
|
| 261 |
+
if not detected_emotion:
|
| 262 |
+
detected_emotion = default_emotions[i % len(default_emotions)]
|
| 263 |
+
|
| 264 |
# Detect scene type
|
| 265 |
scene_type = "single"
|
| 266 |
if any(word in text for word in ["vs", "versus", "compare", "between", "both", "two"]):
|
|
|
|
| 268 |
elif any(word in text for word in ["introduce", "what is", "explained", "definition"]):
|
| 269 |
scene_type = "title"
|
| 270 |
|
| 271 |
+
# Build scene
|
| 272 |
+
scene = {
|
| 273 |
"chunk_id": i,
|
| 274 |
"scene_type": scene_type,
|
| 275 |
"background": "white",
|
| 276 |
"title_text": text[:30].upper() if scene_type == "title" else None,
|
| 277 |
"characters": [
|
| 278 |
+
{
|
| 279 |
+
"position": "center",
|
| 280 |
+
"pose": detected_pose,
|
| 281 |
+
"emotion": detected_emotion,
|
| 282 |
+
"props": []
|
| 283 |
+
}
|
| 284 |
] if scene_type != "title" else [],
|
| 285 |
"caption": text[:50] + "..." if len(text) > 50 else text
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
scenes.append(scene)
|
| 289 |
+
logger.info(f"Fallback scene {i}: pose={detected_pose}, emotion={detected_emotion}")
|
| 290 |
|
| 291 |
return scenes
|
| 292 |
|