File size: 5,395 Bytes
a18ee47
 
 
3778f70
 
b38d998
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3778f70
b38d998
 
 
 
 
a18ee47
b38d998
 
 
3778f70
b38d998
 
 
3778f70
b38d998
 
 
 
3778f70
 
b38d998
3778f70
b38d998
 
 
 
 
 
 
 
3778f70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b38d998
3778f70
b38d998
3778f70
a18ee47
3778f70
 
 
a18ee47
 
 
b38d998
a18ee47
 
b38d998
a18ee47
3778f70
 
b38d998
3778f70
 
 
 
 
 
 
b38d998
3778f70
b38d998
3778f70
b38d998
3778f70
b38d998
3778f70
a18ee47
3778f70
b38d998
 
 
 
 
 
 
3778f70
 
 
 
a18ee47
 
3778f70
 
 
a18ee47
3778f70
 
 
 
a18ee47
3778f70
 
b38d998
3778f70
 
 
 
 
 
 
 
 
a18ee47
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import os
from pathlib import Path

import gradio as gr
from ctransformers import AutoModelForCausalLM
from huggingface_hub import hf_hub_download

# -----------------------------
# Configuration
# -----------------------------
# Option A: local file path inside your Space repo (recommended folder: "models/")
LOCAL_MODEL_FILE = os.environ.get("LOCAL_MODEL_FILE", "models/falcon3-1b-instruct-q4_k_m.gguf")

# Option B: download from Hugging Face Hub (set these in Space Variables)
MODEL_REPO_ID = os.environ.get("MODEL_REPO_ID", "")          # e.g. "TheBloke/Falcon3-1B-Instruct-GGUF"
MODEL_FILENAME = os.environ.get("MODEL_FILENAME", "")        # e.g. "falcon3-1b-instruct-q4_k_m.gguf"

# Inference knobs
MODEL_TYPE = os.environ.get("MODEL_TYPE", "falcon")
GPU_LAYERS = int(os.environ.get("GPU_LAYERS", "0"))
CONTEXT_LENGTH = int(os.environ.get("CONTEXT_LENGTH", "4096"))

# -----------------------------
# Resolve model path
# -----------------------------
APP_DIR = Path(__file__).resolve().parent
local_path = (APP_DIR / LOCAL_MODEL_FILE).resolve()

def resolve_model_path() -> str:
    # 1) Prefer local file (if you committed it to the Space repo)
    if local_path.exists():
        return str(local_path)

    # 2) Otherwise download from Hub (requires MODEL_REPO_ID + MODEL_FILENAME)
    if not MODEL_REPO_ID or not MODEL_FILENAME:
        raise FileNotFoundError(
            f"Model not found locally at: {local_path}\n\n"
            "AND Hub download is not configured.\n"
            "Fix ONE of these:\n"
            "A) Commit the GGUF into your Space repo (recommended: put it in /models).\n"
            "   - Ensure it exists at the path in LOCAL_MODEL_FILE.\n"
            "B) Configure Hub download:\n"
            "   - In Space Settings > Variables:\n"
            "       MODEL_REPO_ID = <your repo_id>\n"
            "       MODEL_FILENAME = <your .gguf filename>\n"
            "   - If repo is gated/private, add Space Secret:\n"
            "       HF_TOKEN = <your HF access token>\n"
        )

    token = os.environ.get("HF_TOKEN")  # optional; required for private/gated repos
    downloaded = hf_hub_download(
        repo_id=MODEL_REPO_ID,
        filename=MODEL_FILENAME,
        token=token,
    )
    return downloaded

MODEL_PATH = resolve_model_path()

# -----------------------------
# Load model
# -----------------------------
llm = AutoModelForCausalLM.from_pretrained(
    MODEL_PATH,
    model_type=MODEL_TYPE,
    gpu_layers=GPU_LAYERS,
    context_length=CONTEXT_LENGTH,
)

# -----------------------------
# Prompt template
# -----------------------------
SYSTEM_PROMPT = """
You are an expert instructional designer who writes clear,
engaging PowerPoint scripts with slide titles, bullet points,
and speaker notes.
""".strip()

def build_prompt(topic, slide_count):
    return f"""{SYSTEM_PROMPT}

Write a complete PowerPoint script with EXACTLY {slide_count} slides.

Topic: {topic}

Return the output in this structure:

Title Slide:
- Title:
- Subtitle:
- Speaker Notes:

Slide 1:
- Slide Title:
- Bullet Points:
- Speaker Notes:

Slide 2:
- Slide Title:
- Bullet Points:
- Speaker Notes:

Continue until you reach Slide {slide_count}.
"""

# -----------------------------
# Generation function
# -----------------------------
def generate_script(topic, slide_count, max_tokens, temperature, top_p):
    topic = (topic or "").strip()
    if not topic:
        return "Please enter a topic."

    try:
        slide_count = int(slide_count)
    except Exception:
        return "Number of slides must be a whole number."

    if slide_count < 1 or slide_count > 50:
        return "Number of slides must be between 1 and 50."

    prompt = build_prompt(topic, slide_count)

    output = ""
    for token in llm(
        prompt,
        max_new_tokens=int(max_tokens),
        temperature=float(temperature),
        top_p=float(top_p),
        stream=True,
    ):
        output += token

    return output.strip()

# -----------------------------
# Gradio UI
# -----------------------------
def build_ui():
    with gr.Blocks(title="Falcon3-1B PPT Script Generator") as demo:
        gr.Markdown(
            """
            # Falcon3-1B-Instruct (GGUF)
            ### PowerPoint Script Generator (Topic + Slide Count)

            Provide a topic and the number of slides you want.
            The model will generate a structured slide-by-slide script.
            """
        )

        with gr.Row():
            with gr.Column():
                topic = gr.Textbox(label="Topic", placeholder="e.g., AI Maturity Model")
                slide_count = gr.Number(label="Number of Slides", value=8, precision=0)

                max_tokens = gr.Slider(256, 4096, value=1500, label="Max Tokens")
                temperature = gr.Slider(0.1, 1.5, value=0.7, label="Temperature")
                top_p = gr.Slider(0.1, 1.0, value=0.9, label="Top-p")

                generate_btn = gr.Button("Generate Script")

            with gr.Column():
                output = gr.Textbox(label="Generated PowerPoint Script", lines=25)

        generate_btn.click(
            generate_script,
            inputs=[topic, slide_count, max_tokens, temperature, top_p],
            outputs=output,
        )

    return demo

demo = build_ui()

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))