Spaces:
Running
Running
| 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) | |