File size: 7,313 Bytes
0e9775d
 
 
 
 
8de2b58
 
 
 
0e9775d
 
 
 
 
 
8de2b58
0e9775d
 
 
 
 
8de2b58
0e9775d
 
 
8de2b58
 
0e9775d
 
 
 
8de2b58
 
 
0e9775d
8de2b58
84c338e
0e9775d
 
 
 
 
 
8de2b58
 
 
 
 
 
 
 
0e9775d
8de2b58
 
0e9775d
 
 
 
 
8de2b58
0e9775d
 
 
 
 
 
8de2b58
0e9775d
8de2b58
 
 
0e9775d
 
 
 
 
 
 
 
 
8de2b58
d7cbd17
0e9775d
d7cbd17
0e9775d
8de2b58
 
0e9775d
 
 
8de2b58
 
 
 
 
 
 
0e9775d
8de2b58
 
 
 
 
 
78f1a57
0e9775d
8de2b58
0e9775d
8de2b58
 
0e9775d
 
 
 
 
 
 
 
 
 
 
 
 
8de2b58
 
0e9775d
8de2b58
 
 
 
 
 
 
 
78f1a57
 
0e9775d
 
78f1a57
8de2b58
0e9775d
8de2b58
 
 
0e9775d
 
8de2b58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78f1a57
8de2b58
 
0e9775d
 
 
 
 
 
 
 
8de2b58
0e9775d
 
 
8de2b58
 
 
55beb81
8de2b58
0e9775d
 
8de2b58
 
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
178
179
180
181
182
183
184
185
186
187
import gradio as gr
import os
import openai
import subprocess
import base64
import shutil
import glob
import uuid
import time

def encode_image(image_path):
    with open(image_path, 'rb') as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')

def extract_code_pieces(text: str, concat: bool = True) -> list[str]:
    """Extract code pieces from a text string."""
    code_pieces = []
    while "```python" in text:
        st_idx = text.index("```python") + 10
        if "```" in text[st_idx:]:
            end_idx = text.index("```", st_idx)
        else:
            end_idx = len(text)
        code_pieces.append(text[st_idx:end_idx].strip())
        text = text[end_idx+3:].strip()
    if concat:
        return "\n\n".join(code_pieces)
    return code_pieces

SYSTEM_MESSAGE = """* You are an expert presentation slides designer who creates modern, fashionable, and stylish slides using Python code. Your job is to generate the required PPTX slide by writing and executing a Python script. Make sure to follow the guidelines below and do not skip any of them:
1.  Ensure your code can successfully execute. If needed, you can also write tests to verify your code.
2.  Maintain proper spacing and arrangements of elements in the slide: make sure to keep sufficient spacing between different elements; do not make elements overlap or overflow to the slide page.
3.  Carefully select the colors of text, shapes, and backgrounds, to ensure all contents are readable.
4.  The slides should not look empty or incomplete. When filling the content in the slides, maintain good design and layout."""

# Load the user instruction template
with open('agent_no_image_new_lib.prompt', 'r') as f:
    INSTRUCTION = f.read()

def list_files_in_directory(directory):
    """Lists all files in the given directory."""
    return set(glob.glob(os.path.join(directory, "*")))

def create_pptx(api_key, instruction, model_name="gpt-4o", max_tokens=4096):
    """
    Generates a PPTX from instructions using GPT, isolating all new files 
    in a unique directory so multiple requests don't conflict.
    """
    unique_id = str(uuid.uuid4())[:8]        # Short unique directory name
    working_dir = f"temp_work_{unique_id}"   # e.g. temp_work_3f2b1d4c
    os.makedirs(working_dir, exist_ok=True)

    # The name of the PPTX we intend to produce
    pptx_path = os.path.join(working_dir, "output.pptx")

    try:
        # Set OpenAI API key
        openai.api_key = api_key

        # Create an OpenAI client
        client = openai.OpenAI(api_key=api_key)

        # Prepare messages for the chat completion
        messages = [{"role": "system", "content": SYSTEM_MESSAGE}]
        instruction_message = INSTRUCTION.replace("INSERT_INSTRUCTION_HERE", instruction)
        messages.append({"role": "user", "content": instruction_message})

        # Capture the directory state before execution
        files_before = list_files_in_directory(working_dir)

        # Try up to 3 times to generate code and run it
        for attempt in range(3):
            try:
                response = client.chat.completions.create(
                    model=model_name,
                    messages=messages,
                    max_tokens=max_tokens,
                    n=1,
                )
                generated_code = extract_code_pieces(response.choices[0].message.content, concat=True)
                # Replace references to other library with local SlidesLib
                generated_code = generated_code.replace("from library import", "from SlidesLib import")
                generated_code = generated_code.replace("generate_image(", f"generate_image({repr(api_key)},")
                generated_code = "from SlidesLib import *\n\n" + generated_code

                code_file = os.path.join(working_dir, "generated_slide_code.py")
                with open(code_file, "w", encoding="utf-8") as f:
                    f.write(generated_code)

                # Execute the generated code
                result = subprocess.run(
                    ["python", code_file],
                    capture_output=True, 
                    text=True, 
                    check=True, 
                    cwd=working_dir
                )
                print(result.stdout)

                # Check for newly created files
                files_after = list_files_in_directory(working_dir)
                temp_files = files_after - files_before

                # If successful, return
                return "Slide generated successfully! Download your slide below.", pptx_path
            except subprocess.CalledProcessError as e:
                print(f"Attempt {attempt + 1} failed:\n{e.stderr}\n")

        # If all attempts fail, run default code
        print("All attempts failed. Running default code.")
        default_code = f"""
from pptx import Presentation

ppt = Presentation()
slide = ppt.slides.add_slide(ppt.slide_layouts[5])
title = slide.shapes.title
title.text = "Insert Title Here"

content = slide.placeholders[0]
content.text = "{instruction}"

ppt.save("output.pptx")
"""
        code_file = os.path.join(working_dir, "default_slide_code.py")
        with open(code_file, "w", encoding="utf-8") as f:
            f.write(default_code)

        subprocess.run(
            ["python", code_file],
            capture_output=True, 
            text=True, 
            check=True,
            cwd=working_dir
        )

        return "Default slide generated after 3 attempts failed.", pptx_path

    except Exception as e:
        return f"An error occurred: {str(e)}", None

    finally:
        # We won't automatically delete the working_dir here if we want the user 
        # to be able to download the pptx. Otherwise, we could do cleanup.
        pass

def gradio_demo(api_key, instruction):
    """
    The Gradio demo function. It calls create_pptx, returns
    the status string, and the path to the PPTX file.
    """
    # 1) Important: Warn the user about key usage.
    if not api_key:
        return ("Warning: No OpenAI API key provided. Please provide a valid key "
                "to generate slides."), None
    else:
        # Additional caution about potential key leaks
        warning_message = (
            "Caution: Your OpenAI API key is used to generate the slide. "
            "Make sure you trust this environment, as keys can appear in logs. "
            "We recommend using a disposable or restricted-scope key if possible.\n"
        )

    status, pptx_path = create_pptx(api_key, instruction)
    # Prepend the warning to the final status
    return f"{warning_message}\n{status}", pptx_path

iface = gr.Interface(
    fn=gradio_demo,
    inputs=[
        gr.Textbox(label="OpenAI API Key", type="password"),
        gr.Textbox(label="Instruction", placeholder="Enter your slide instruction here...")
    ],
    outputs=[
        gr.Textbox(label="Status", lines=5),
        gr.File(label="Download Slide"),
    ],
    title="AutoPresent",
    description=(
        "Automatically Generate a presentation slide.\n\n"
        "**WARNING**: Please be cautious with your OpenAI API key. "
        "Logs or server code might store it temporarily. **We suggest one-time use.**"
    )
)

if __name__ == "__main__":
    iface.launch()