File size: 3,640 Bytes
4c152d1
 
 
 
 
 
 
 
 
 
 
1a1d9ee
4c152d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91af44e
 
 
 
 
4c152d1
1a1d9ee
4c152d1
 
 
91af44e
4c152d1
 
 
 
 
 
 
 
 
5de2d90
1a1d9ee
4c152d1
 
 
 
 
1a1d9ee
 
 
 
 
 
 
5de2d90
1a1d9ee
4c152d1
 
 
 
 
 
1a1d9ee
 
 
5de2d90
1a1d9ee
4c152d1
 
 
 
 
 
 
 
 
 
 
1a1d9ee
4c152d1
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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)