import os import json import re from huggingface_hub import InferenceClient MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct" _api_key = os.environ.get("GROQ_API_KEY") or os.environ.get("HF_TOKEN") client = InferenceClient(api_key=_api_key) SYSTEM_PROMPT = ( "You are an expert presentation designer and subject-matter expert. " "Return only valid JSON — no markdown fences, no extra text." ) def _extract_json(text: str) -> dict: try: return json.loads(text.strip()) except json.JSONDecodeError: pass cleaned = re.sub(r"^```[a-z]*\n?", "", text.strip(), flags=re.MULTILINE) cleaned = re.sub(r"\n?```$", "", cleaned.strip(), flags=re.MULTILINE) try: return json.loads(cleaned.strip()) except json.JSONDecodeError: pass match = re.search(r"\{.*\}", text, re.DOTALL) if match: return json.loads(match.group()) raise ValueError("Could not extract JSON from model response.") def generate_presentation(topic: str, style: str, num_slides: int, audience: str, key_points: str) -> dict: key_points_section = ( f"\nKey points to include: {key_points}" if key_points.strip() else "" ) user_prompt = f"""Create a detailed, informative {style.lower()} presentation about: "{topic}" Target audience: {audience} Number of slides: {num_slides} (including title slide){key_points_section} Return a JSON object with this EXACT structure: {{ "title": "Main presentation title", "subtitle": "A compelling subtitle", "slides": [ {{ "slide_number": 1, "type": "title", "title": "Presentation Title", "subtitle": "Subtitle or tagline", "image_keyword": "{topic} overview", "speaker_notes": "Detailed opening notes for the presenter, 3-4 sentences." }}, {{ "slide_number": 2, "type": "content", "title": "Slide Title", "bullets": [ "First key point with sufficient detail and explanation to be meaningful", "Second point that elaborates on a core concept with specific data or insight", "Third point covering an important aspect with supporting context", "Fourth point with actionable information or a compelling statistic", "Fifth point summarizing implications or real-world applications" ], "image_keyword": "{topic} [specific aspect of this slide — 2-4 words total]", "speaker_notes": "Detailed speaker notes for this slide, 3-4 sentences explaining what to say and emphasize." }} ] }} Rules: - First slide must be type "title" with subtitle field - All other slides type "content" with bullets array - Each slide must have EXACTLY 5 bullet points - Each bullet point: 15-25 words, informative and specific — include facts, data, or clear explanations. No vague one-liners. - image_keyword: MUST include the topic "{topic}" plus 1-2 extra words describing the slide's specific angle. Examples for topic "Electric Vehicles": "electric vehicles charging station", "electric vehicles battery technology", "electric vehicles environmental impact". Always make keywords directly about the main topic. - speaker_notes: 3-4 detailed sentences per slide - Total slides: exactly {num_slides} - Return only the JSON object, nothing else """ response = client.chat.completions.create( model=MODEL_NAME, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_prompt}, ], temperature=0.7, max_tokens=6000, ) raw = response.choices[0].message.content return _extract_json(raw)