cryogenic22 commited on
Commit
2139ab0
·
verified ·
1 Parent(s): 13a153e

Create utils.py

Browse files
Files changed (1) hide show
  1. utils.py +109 -126
utils.py CHANGED
@@ -4,20 +4,7 @@ from io import BytesIO
4
  import json
5
  import anthropic
6
  import os
7
-
8
- # Initialize the Claude client with API key from environment (Huggingface secrets)
9
- api_key = os.getenv("ANTHROPIC_API_KEY")
10
- if not api_key:
11
- st.sidebar.error("ANTHROPIC_API_KEY not found in environment variables")
12
- client = None
13
- else:
14
- # Create the client
15
- try:
16
- client = anthropic.Anthropic(api_key=api_key)
17
- st.sidebar.success("Anthropic API client initialized successfully")
18
- except Exception as e:
19
- st.sidebar.error(f"Error initializing Anthropic API: {str(e)}")
20
- client = None
21
 
22
  # Define available templates
23
  TEMPLATES = {
@@ -79,12 +66,32 @@ TEMPLATES = {
79
  }
80
  }
81
 
82
- def generate_storyboard(title, purpose, audience):
83
- """Generate a presentation storyboard using Claude"""
84
- if not client:
85
- st.error("Anthropic API client not available")
86
- return default_storyboard(title)
 
 
 
 
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  prompt = f"""
89
  You are an expert presentation designer with deep expertise in creating compelling PowerPoint presentations.
90
 
@@ -93,6 +100,8 @@ def generate_storyboard(title, purpose, audience):
93
  - Purpose: {purpose}
94
  - Target Audience: {audience}
95
 
 
 
96
  For each slide, include:
97
  1. Slide title
98
  2. Slide purpose
@@ -116,21 +125,21 @@ def generate_storyboard(title, purpose, audience):
116
  ]
117
  """
118
 
 
 
119
  with st.sidebar:
120
  progress_text = st.empty()
121
- progress_text.write("Generating storyboard...")
122
 
123
  try:
124
- response = client.messages.create(
125
- model="claude-3-7-sonnet-20250219",
126
- max_tokens=4000,
127
- temperature=0.7,
128
- system="You are an expert presentation designer specializing in creating logical, compelling PowerPoint storyboards. Always output valid JSON without explanations or extra text.",
129
- messages=[
130
- {"role": "user", "content": prompt}
131
- ]
132
  )
133
- response_text = response.content[0].text
134
 
135
  # For debugging, show raw response
136
  st.sidebar.write("Raw response: " + response_text[:200] + "...")
@@ -143,6 +152,13 @@ def generate_storyboard(title, purpose, audience):
143
  json_str = response_text[json_start:json_end]
144
  parsed_json = json.loads(json_str)
145
  progress_text.write(f"Generated storyboard with {len(parsed_json)} slides")
 
 
 
 
 
 
 
146
  return parsed_json
147
  else:
148
  progress_text.error("JSON not found in response")
@@ -185,11 +201,15 @@ def default_storyboard(title):
185
  }
186
  ]
187
 
188
- def generate_slide_content(slide_info, template_name):
189
- """Generate detailed content for a slide using Claude"""
190
- if not client:
191
- st.error("Anthropic API client not available")
192
- return default_slide_content(slide_info)
 
 
 
 
193
 
194
  prompt = f"""
195
  Create detailed content for a PowerPoint slide with the following information:
@@ -218,17 +238,17 @@ def generate_slide_content(slide_info, template_name):
218
  Focus on clarity, impact, and alignment with the slide's purpose.
219
  """
220
 
 
 
221
  try:
222
- response = client.messages.create(
223
- model="claude-3-7-sonnet-20250219",
224
- max_tokens=2000,
225
- temperature=0.5,
226
- system="You are an expert presentation content creator. Always output valid JSON without explanations or extra text.",
227
- messages=[
228
- {"role": "user", "content": prompt}
229
- ]
230
  )
231
- response_text = response.content[0].text
232
 
233
  # Extract the JSON from the response
234
  json_start = response_text.find("{")
@@ -236,7 +256,14 @@ def generate_slide_content(slide_info, template_name):
236
 
237
  if json_start >= 0 and json_end > 0:
238
  json_str = response_text[json_start:json_end]
239
- return json.loads(json_str)
 
 
 
 
 
 
 
240
  else:
241
  # Fallback: create a basic slide
242
  st.sidebar.error(f"JSON not found in response: {response_text}")
@@ -264,6 +291,12 @@ def create_slide_preview(slide, template_name):
264
  colors = template["colors"]
265
  fonts = template["fonts"]
266
 
 
 
 
 
 
 
267
  # Prepare content
268
  title = slide.get('title', 'Untitled Slide')
269
 
@@ -337,87 +370,6 @@ def create_slide_preview(slide, template_name):
337
 
338
  return html
339
 
340
- def create_ppt(slides_content, template_name):
341
- """Create a PowerPoint presentation from the slides content"""
342
- # Create a basic presentation
343
- prs = Presentation()
344
-
345
- # Get template info
346
- template = TEMPLATES.get(template_name, TEMPLATES["professional"])
347
-
348
- # Title slide
349
- title_slide_layout = prs.slide_layouts[0]
350
- slide = prs.slides.add_slide(title_slide_layout)
351
- title = slide.shapes.title
352
- subtitle = slide.placeholders[1]
353
-
354
- title.text = slides_content[0]["title"]
355
- subtitle.text = "Created with AI PowerPoint Creator"
356
-
357
- # Content slides
358
- for slide_content in slides_content[1:]:
359
- # Determine best layout based on content or explicit design
360
- layout_type = "standard"
361
- if "design" in slide_content and "layout" in slide_content["design"]:
362
- layout_type = slide_content["design"]["layout"].lower()
363
-
364
- # Select appropriate slide layout
365
- if "two column" in layout_type or "comparison" in layout_type:
366
- content_slide_layout = prs.slide_layouts[3] if len(prs.slide_layouts) > 3 else prs.slide_layouts[1]
367
- elif "title only" in layout_type or "quote" in layout_type:
368
- content_slide_layout = prs.slide_layouts[5] if len(prs.slide_layouts) > 5 else prs.slide_layouts[0]
369
- elif "picture" in layout_type:
370
- content_slide_layout = prs.slide_layouts[6] if len(prs.slide_layouts) > 6 else prs.slide_layouts[1]
371
- else:
372
- content_slide_layout = prs.slide_layouts[1] # Default: Title and Content
373
-
374
- slide = prs.slides.add_slide(content_slide_layout)
375
-
376
- # Add title
377
- if slide.shapes.title:
378
- slide.shapes.title.text = slide_content["title"]
379
-
380
- # Add content based on layout and available placeholders
381
- content_placeholders = [shape for shape in slide.placeholders
382
- if shape.placeholder_format.type != 1] # 1 is title
383
-
384
- if len(content_placeholders) > 0:
385
- content_placeholder = content_placeholders[0]
386
-
387
- # Format content based on what's available and layout type
388
- if "content" in slide_content:
389
- if isinstance(slide_content["content"], list):
390
- if "two column" in layout_type and len(content_placeholders) > 1:
391
- # Split content for two columns
392
- left_placeholder = content_placeholders[0]
393
- right_placeholder = content_placeholders[1]
394
-
395
- mid_point = len(slide_content["content"]) // 2
396
- left_content = slide_content["content"][:mid_point]
397
- right_content = slide_content["content"][mid_point:]
398
-
399
- left_placeholder.text = "\n".join(f"• {point}" for point in left_content)
400
- right_placeholder.text = "\n".join(f"• {point}" for point in right_content)
401
- else:
402
- # Standard bullet points
403
- content_placeholder.text = "\n".join(f"• {point}" for point in slide_content["content"])
404
- else:
405
- # Plain text
406
- content_placeholder.text = slide_content["content"]
407
- else:
408
- content_placeholder.text = "Content to be added"
409
-
410
- # Add notes if available
411
- if "notes" in slide_content and slide_content["notes"]:
412
- notes_slide = slide.notes_slide
413
- notes_slide.notes_text_frame.text = slide_content["notes"]
414
-
415
- # Save to BytesIO
416
- output = BytesIO()
417
- prs.save(output)
418
- output.seek(0)
419
- return output
420
-
421
  def add_custom_template(template_file):
422
  """Add a custom template from an uploaded file"""
423
  try:
@@ -427,7 +379,38 @@ def add_custom_template(template_file):
427
 
428
  # Store in session state
429
  st.session_state.custom_template = file_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
- return True, f"Custom template '{template_file.name}' uploaded successfully!"
432
  except Exception as e:
433
  return False, f"Error with template file: {str(e)}"
 
4
  import json
5
  import anthropic
6
  import os
7
+ from multi_llm_provider import get_ai_manager
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  # Define available templates
10
  TEMPLATES = {
 
66
  }
67
  }
68
 
69
+ def generate_storyboard(title, purpose, audience, model=None):
70
+ """Generate a presentation storyboard using the specified AI model"""
71
+
72
+ # Get AI manager
73
+ ai_manager = get_ai_manager()
74
+
75
+ # Use default model if none specified
76
+ if not model:
77
+ model = st.session_state.get("default_model", "claude-3-sonnet-20250219")
78
 
79
+ # Check for web research data
80
+ web_research = ""
81
+ if hasattr(st.session_state, 'web_research') and st.session_state.web_research:
82
+ research_data = st.session_state.web_research
83
+ web_research = "Recent information from web research:\n\n"
84
+
85
+ for i, item in enumerate(research_data[:3]): # Limit to 3 results
86
+ title = item.get("title", "No title")
87
+ snippet = item.get("snippet", "No snippet")
88
+ url = item.get("url", "No URL")
89
+
90
+ web_research += f"{i+1}. {title}\n"
91
+ web_research += f" {snippet}\n"
92
+ web_research += f" Source: {url}\n\n"
93
+
94
+ # Create the prompt
95
  prompt = f"""
96
  You are an expert presentation designer with deep expertise in creating compelling PowerPoint presentations.
97
 
 
100
  - Purpose: {purpose}
101
  - Target Audience: {audience}
102
 
103
+ {web_research if web_research else ""}
104
+
105
  For each slide, include:
106
  1. Slide title
107
  2. Slide purpose
 
125
  ]
126
  """
127
 
128
+ system_prompt = "You are an expert presentation designer specializing in creating logical, compelling PowerPoint storyboards. Always output valid JSON without explanations or extra text."
129
+
130
  with st.sidebar:
131
  progress_text = st.empty()
132
+ progress_text.write(f"Generating storyboard using {model}...")
133
 
134
  try:
135
+ # Generate storyboard using the selected model
136
+ response_text = ai_manager.generate_text(
137
+ prompt=prompt,
138
+ model=model,
139
+ system_prompt=system_prompt,
140
+ temperature=st.session_state.get("ai_temperature", 0.7),
141
+ max_tokens=4000
 
142
  )
 
143
 
144
  # For debugging, show raw response
145
  st.sidebar.write("Raw response: " + response_text[:200] + "...")
 
152
  json_str = response_text[json_start:json_end]
153
  parsed_json = json.loads(json_str)
154
  progress_text.write(f"Generated storyboard with {len(parsed_json)} slides")
155
+
156
+ # Add AI model used to each slide for future reference
157
+ for slide in parsed_json:
158
+ if "ai_settings" not in slide:
159
+ slide["ai_settings"] = {}
160
+ slide["ai_settings"]["model"] = model
161
+
162
  return parsed_json
163
  else:
164
  progress_text.error("JSON not found in response")
 
201
  }
202
  ]
203
 
204
+ def generate_slide_content(slide_info, template_name, model=None):
205
+ """Generate detailed content for a slide using the specified AI model"""
206
+
207
+ # Get AI manager
208
+ ai_manager = get_ai_manager()
209
+
210
+ # Use default model if none specified
211
+ if not model:
212
+ model = slide_info.get("ai_settings", {}).get("model", st.session_state.get("default_model", "claude-3-sonnet-20250219"))
213
 
214
  prompt = f"""
215
  Create detailed content for a PowerPoint slide with the following information:
 
238
  Focus on clarity, impact, and alignment with the slide's purpose.
239
  """
240
 
241
+ system_prompt = "You are an expert presentation content creator. Always output valid JSON without explanations or extra text."
242
+
243
  try:
244
+ # Generate content using the selected model
245
+ response_text = ai_manager.generate_text(
246
+ prompt=prompt,
247
+ model=model,
248
+ system_prompt=system_prompt,
249
+ temperature=st.session_state.get("ai_temperature", 0.5),
250
+ max_tokens=2000
 
251
  )
 
252
 
253
  # Extract the JSON from the response
254
  json_start = response_text.find("{")
 
256
 
257
  if json_start >= 0 and json_end > 0:
258
  json_str = response_text[json_start:json_end]
259
+ content = json.loads(json_str)
260
+
261
+ # Store AI model used
262
+ if "ai_settings" not in content:
263
+ content["ai_settings"] = {}
264
+ content["ai_settings"]["model"] = model
265
+
266
+ return content
267
  else:
268
  # Fallback: create a basic slide
269
  st.sidebar.error(f"JSON not found in response: {response_text}")
 
291
  colors = template["colors"]
292
  fonts = template["fonts"]
293
 
294
+ # Apply custom colors if available
295
+ if "custom_colors" in st.session_state and template_name in st.session_state.custom_colors:
296
+ custom_colors = st.session_state.custom_colors[template_name]
297
+ colors["primary"] = custom_colors.get("primary", colors["primary"])
298
+ colors["accent"] = custom_colors.get("accent", colors["accent"])
299
+
300
  # Prepare content
301
  title = slide.get('title', 'Untitled Slide')
302
 
 
370
 
371
  return html
372
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  def add_custom_template(template_file):
374
  """Add a custom template from an uploaded file"""
375
  try:
 
379
 
380
  # Store in session state
381
  st.session_state.custom_template = file_content
382
+ st.session_state.custom_template_name = template_file.name
383
+
384
+ # Extract template information if possible
385
+ try:
386
+ # Try to extract color scheme from the template
387
+ background_color = "#FFFFFF" # Default
388
+ accent_color = "#0073E6" # Default
389
+
390
+ # Look for theme colors in the first slide if available
391
+ if len(prs.slides) > 0:
392
+ slide = prs.slides[0]
393
+
394
+ # Look for background color
395
+ for shape in slide.shapes:
396
+ if hasattr(shape, 'fill') and shape.fill.type != 0: # 0 is None
397
+ if hasattr(shape.fill, 'fore_color') and hasattr(shape.fill.fore_color, 'rgb'):
398
+ rgb = shape.fill.fore_color.rgb
399
+ if rgb:
400
+ background_color = f"#{rgb.r:02x}{rgb.g:02x}{rgb.b:02x}"
401
+ break
402
+
403
+ # Update custom template with extracted colors
404
+ TEMPLATES["custom"]["colors"]["primary"] = accent_color
405
+ TEMPLATES["custom"]["colors"]["secondary"] = background_color
406
+
407
+ # Get the number of slide layouts available
408
+ num_layouts = len(prs.slide_layouts)
409
+ st.session_state.custom_template_layouts = num_layouts
410
+
411
+ except Exception as extract_error:
412
+ st.warning(f"Could not extract all template details: {str(extract_error)}")
413
 
414
+ return True, f"Custom template '{template_file.name}' uploaded successfully with {len(prs.slides)} master layouts!"
415
  except Exception as e:
416
  return False, f"Error with template file: {str(e)}"