barunsaha commited on
Commit
e55d16a
1 Parent(s): aa4f694

Use a form, a button, and a single LLM call to generate contents in JSON format. Disable image generation.

Browse files
app.py CHANGED
@@ -1,4 +1,3 @@
1
- import base64
2
  import os
3
  import json5
4
  import logging
@@ -7,6 +6,7 @@ import time
7
  import streamlit as st
8
  import streamlit.runtime.scriptrunner as st_sr
9
  from typing import List, Tuple
 
10
 
11
  import llm_helper
12
  import pptx_helper
@@ -29,22 +29,22 @@ def get_contents_wrapper(text: str) -> str:
29
  Fetch and cache the slide deck contents on a topic by calling an external API.
30
 
31
  :param text: The presentation topic
32
- :return: The slide deck contents or outline
33
  """
34
 
 
35
  return llm_helper.generate_slides_content(text).strip()
36
 
37
 
38
- @st.cache_data
39
- def get_json_wrapper(text: str) -> str:
40
  """
41
- Fetch and cache the JSON-formatted slide deck contents by calling an external API.
42
 
43
- :param text: The slide deck contents or outline
44
- :return: The JSON-formatted contents
45
  """
46
 
47
- return llm_helper.text_to_json(text)
48
 
49
 
50
  @st.cache_data
@@ -57,7 +57,11 @@ def get_web_search_results_wrapper(text: str) -> List[Tuple[str, str]]:
57
  """
58
 
59
  results = []
60
- search_results = llm_helper.get_related_websites(text)
 
 
 
 
61
 
62
  for a_result in search_results.results:
63
  results.append((a_result.title, a_result.url))
@@ -104,52 +108,60 @@ def build_ui():
104
  Display the input elements for content generation. Only covers the first step.
105
  """
106
 
107
- get_disk_used_percentage()
108
 
109
  st.title(APP_TEXT['app_name'])
110
  st.subheader(APP_TEXT['caption'])
111
- # st.markdown(
112
- # '![Visitors](https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fbarunsaha%2Fslide-deck-ai&countColor=%23263759)'
113
- # )
114
- st.divider()
115
 
116
- st.header(APP_TEXT['section_headers'][0])
117
- st.caption(APP_TEXT['section_captions'][0])
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- try:
120
- with open(GlobalConfig.PRELOAD_DATA_FILE, 'r') as in_file:
121
- preload_data = json5.loads(in_file.read())
122
- except (FileExistsError, FileNotFoundError):
123
- preload_data = {'topic': '', 'audience': ''}
124
-
125
- # with st.form('describe-topic-form'):
126
- topic = st.text_area(
127
- APP_TEXT['input_labels'][0],
128
- value=preload_data['topic']
129
- )
130
 
131
- # Button with callback function
132
- st.button(APP_TEXT['button_labels'][0], on_click=button_clicked, args=[0])
133
- # desc_topic_btn_submitted = st.form_submit_button(
134
- # APP_TEXT['button_labels'][0],
135
- # on_click=button_clicked,
136
- # args=[0]
137
- # )
138
-
139
- if st.session_state.clicked[0]:
140
- # if desc_topic_btn_submitted:
141
- progress_text = 'Generating contents for your slides...give it a moment'
142
  progress_bar = st.progress(0, text=progress_text)
143
 
144
  topic_txt = topic.strip()
145
- process_topic_inputs(topic_txt, progress_bar)
 
 
 
 
 
 
 
 
146
 
147
 
148
- def process_topic_inputs(topic: str, progress_bar):
149
  """
150
- Process the inputs to generate contents for the slides.
151
 
152
- :param topic: The presentation topic
 
153
  :param progress_bar: Progress bar from the page
154
  :return:
155
  """
@@ -161,51 +173,30 @@ def process_topic_inputs(topic: str, progress_bar):
161
  logging.debug(
162
  f'Topic: {topic}\n'
163
  )
164
- logging.debug('=' * 20)
165
 
166
  target_length = min(topic_length, GlobalConfig.LLM_MODEL_MAX_INPUT_LENGTH)
167
 
168
  try:
169
- logging.info('Calling LLM for content generation...')
170
- slides_content = get_contents_wrapper(topic[:target_length])
171
- content_length = len(slides_content)
172
-
173
- logging.debug('=' * 20)
174
- logging.debug(f'Slides content:\n{slides_content}')
175
- logging.debug(f'Content length: {content_length}')
176
- logging.debug('=' * 20)
177
- st.write(f'''Slides content:\n{slides_content}''')
178
- progress_bar.progress(100, text='Done!')
179
-
180
- if content_length == 0:
181
- st.error(APP_TEXT['content_generation_failure_error'])
182
  return
183
 
184
- st.info(
185
- 'The generated content doesn\'t look so great?'
186
- ' Need alternatives? Just change your description text and try again.',
187
- icon="💡️"
188
- )
189
 
190
- # Move on to step 2
191
- st.divider()
192
- st.header(APP_TEXT['section_headers'][1])
193
- st.caption(APP_TEXT['section_captions'][1])
194
 
195
- # Streamlit multiple buttons work in a weird way!
196
- # Click on any button, the page just reloads!
197
- # Buttons are not "stateful"
198
- # https://blog.streamlit.io/10-most-common-explanations-on-the-streamlit-forum/#1-buttons-aren%E2%80%99t-stateful
199
- # Apparently, "nested button click" needs to be handled differently
200
- # https://playground.streamlit.app/?q=triple-button
201
-
202
- st.button(APP_TEXT['button_labels'][1], on_click=button_clicked, args=[1])
203
-
204
- if st.session_state.clicked[1]:
205
- progress_text = 'Converting...give it a moment'
206
- progress_bar = st.progress(0, text=progress_text)
207
-
208
- process_slides_contents(slides_content, progress_bar)
209
  except ValueError as ve:
210
  st.error(f'Unfortunately, an error occurred: {ve}! '
211
  f'Please change the text, try again later, or report it, sharing your inputs.')
@@ -214,151 +205,79 @@ def process_topic_inputs(topic: str, progress_bar):
214
  st.error('Not enough information provided! Please be little more descriptive :)')
215
 
216
 
217
- def process_slides_contents(text: str, progress_bar: st.progress):
218
  """
219
  Convert given text into structured data and display. Update the UI.
220
 
221
- :param text: The contents generated for the slides
222
  :param progress_bar: Progress bar for this step
 
223
  """
224
 
225
- logging.debug('JSON button clicked')
226
  json_str = ''
227
 
228
  try:
229
- logging.info('Calling LLM for conversion...')
230
- json_str = get_json_wrapper(text)
231
  except Exception as ex:
232
  st.error(f'An exception occurred while trying to convert to JSON.'
233
  f' It could be because of heavy traffic or something else.'
234
  f' Try doing it again or try again later.\n'
235
  f' Error message: {ex}')
236
- # st.stop()
237
-
238
- # yaml_str = llm_helper.text_to_yaml(text)
239
- logging.debug('=' * 20)
240
- logging.debug(f'JSON:\n{json_str}')
241
- logging.debug('=' * 20)
242
- st.code(json_str, language='json')
243
-
244
- if len(json_str) > 0:
245
- progress_bar.progress(100, text='Done!')
246
- st.info(
247
- 'Tip: In some scenarios, the JSON creates a deeper nesting of the bullet points'
248
- ' than what is expected. You can try to regenerate the JSON'
249
- ' by making a minor change in the topic description in the previous step, e.g.,'
250
- ' by adding or removing a character. Alternatively, you can edit this in the slide'
251
- ' deck that would be generated in the next step.',
252
- icon="💡️"
253
- )
254
- else:
255
- st.error('Unfortunately, JSON generation failed, so the next steps would lead to nowhere.'
256
- ' Try again or come back later.')
257
 
258
- # Now, step 3
259
- st.divider()
260
- st.header(APP_TEXT['section_headers'][2])
261
- st.caption(APP_TEXT['section_captions'][2])
262
-
263
- texts = list(GlobalConfig.PPTX_TEMPLATE_FILES.keys())
264
- captions = [GlobalConfig.PPTX_TEMPLATE_FILES[x]['caption'] for x in texts]
265
-
266
- # with st.form('create-slides-form'):
267
- pptx_template = st.radio(
268
- 'Select a presentation template:',
269
- texts,
270
- captions=captions,
271
- horizontal=True
272
- )
273
 
274
- st.button(APP_TEXT['button_labels'][2], on_click=button_clicked, args=[2])
275
- # create_slides_btn_submitted = st.form_submit_button(APP_TEXT['button_labels'][2])
276
 
277
- if st.session_state.clicked[2]:
278
- # if create_slides_btn_submitted:
279
- progress_text = 'Creating the slide deck...give it a moment'
280
- progress_bar = st.progress(0, text=progress_text)
281
 
282
- # Get a unique name for the file to save -- use the session ID
283
- ctx = st_sr.get_script_run_ctx()
284
- session_id = ctx.session_id
285
- timestamp = time.time()
286
- output_file_name = f'{session_id}_{timestamp}.pptx'
287
-
288
- logging.info('Creating PPTX file...')
289
- all_headers = pptx_helper.generate_powerpoint_presentation(
290
- json_str,
291
- as_yaml=False,
292
- slides_template=pptx_template,
293
- output_file_name=output_file_name
294
- )
295
- progress_bar.progress(100, text='Done!')
296
 
297
- # st.download_button('Download file', binary_contents) # Defaults to 'application/octet-stream'
298
 
299
- with open(output_file_name, 'rb') as f:
300
- st.download_button('Download PPTX file', f, file_name=output_file_name)
 
301
 
302
- bonus_divider = st.empty()
303
- bonus_header = st.empty()
304
- bonus_caption = st.empty()
 
 
305
 
306
- urls_text = st.empty()
307
- urls_list = st.empty()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
- img_empty = st.empty()
310
- img_text = st.empty()
311
- img_contents = st.empty()
312
- img_tip = st.empty()
313
 
314
- st.divider()
315
- st.text(APP_TEXT['tos'])
316
- st.text(APP_TEXT['tos2'])
317
-
318
- show_bonus_stuff(
319
- all_headers,
320
- bonus_divider,
321
- bonus_header,
322
- bonus_caption,
323
- urls_text,
324
- urls_list,
325
- img_empty,
326
- img_text,
327
- img_contents,
328
- img_tip
329
- )
330
 
331
 
332
- def show_bonus_stuff(
333
- ppt_headers: List,
334
- *st_placeholders
335
- ):
336
  """
337
- Show relevant links and images for the presentation topic.
338
 
339
- :param ppt_headers: A list of all slide headers
340
  """
341
 
342
- (
343
- bonus_divider,
344
- bonus_header,
345
- bonus_caption,
346
- urls_text,
347
- urls_list,
348
- img_empty,
349
- img_text,
350
- img_contents,
351
- img_tip
352
- ) = st_placeholders
353
-
354
- bonus_divider.divider()
355
- bonus_header.header(APP_TEXT['section_headers'][3])
356
- bonus_caption.caption(APP_TEXT['section_captions'][3])
357
-
358
- urls_text.write(APP_TEXT['urls_info'])
359
-
360
  # Use the presentation title and the slides headers to find relevant info online
361
- logging.info('Using Metaphor search...')
362
  ppt_text = ' '.join(ppt_headers)
363
  search_results = get_web_search_results_wrapper(ppt_text)
364
  md_text_items = []
@@ -366,32 +285,13 @@ def show_bonus_stuff(
366
  for (title, link) in search_results:
367
  md_text_items.append(f'[{title}]({link})')
368
 
369
- urls_list.markdown('\n\n'.join(md_text_items))
370
-
371
- logging.info('Calling SDXL for image generation...')
372
- img_empty.write('')
373
- img_text.write(APP_TEXT['image_info'])
374
- image = get_ai_image_wrapper(ppt_text)
375
 
376
- if len(image) > 0:
377
- image = base64.b64decode(image)
378
- img_contents.image(image, caption=ppt_text)
379
- img_tip.info('Tip: Right-click on the image to save it.', icon="💡️")
380
-
381
-
382
- def button_clicked(button):
383
- """
384
- Update the button clicked value in session state.
385
- """
386
-
387
- st.session_state.clicked[button] = True
388
 
389
 
390
  def main():
391
- # Initialize the key in session state to manage the nested buttons states
392
- if 'clicked' not in st.session_state:
393
- st.session_state.clicked = {0: False, 1: False, 2: False}
394
-
395
  build_ui()
396
 
397
 
 
 
1
  import os
2
  import json5
3
  import logging
 
6
  import streamlit as st
7
  import streamlit.runtime.scriptrunner as st_sr
8
  from typing import List, Tuple
9
+ import metaphor_python as metaphor
10
 
11
  import llm_helper
12
  import pptx_helper
 
29
  Fetch and cache the slide deck contents on a topic by calling an external API.
30
 
31
  :param text: The presentation topic
32
+ :return: The slide deck contents or outline in JSON format
33
  """
34
 
35
+ logging.info('LLM call because of cache miss...')
36
  return llm_helper.generate_slides_content(text).strip()
37
 
38
 
39
+ @st.cache_resource
40
+ def get_metaphor_client_wrapper() -> metaphor.Metaphor:
41
  """
42
+ Create a Metaphor client for semantic Web search.
43
 
44
+ :return: Metaphor instance
 
45
  """
46
 
47
+ return metaphor.Metaphor(api_key=GlobalConfig.METAPHOR_API_KEY)
48
 
49
 
50
  @st.cache_data
 
57
  """
58
 
59
  results = []
60
+ search_results = get_metaphor_client_wrapper().search(
61
+ text,
62
+ use_autoprompt=True,
63
+ num_results=5
64
+ )
65
 
66
  for a_result in search_results.results:
67
  results.append((a_result.title, a_result.url))
 
108
  Display the input elements for content generation. Only covers the first step.
109
  """
110
 
111
+ # get_disk_used_percentage()
112
 
113
  st.title(APP_TEXT['app_name'])
114
  st.subheader(APP_TEXT['caption'])
 
 
 
 
115
 
116
+ with st.form('my_form'):
117
+ # Topic input
118
+ try:
119
+ with open(GlobalConfig.PRELOAD_DATA_FILE, 'r') as in_file:
120
+ preload_data = json5.loads(in_file.read())
121
+ except (FileExistsError, FileNotFoundError):
122
+ preload_data = {'topic': '', 'audience': ''}
123
+
124
+ topic = st.text_area(
125
+ APP_TEXT['input_labels'][0],
126
+ value=preload_data['topic']
127
+ )
128
+
129
+ texts = list(GlobalConfig.PPTX_TEMPLATE_FILES.keys())
130
+ captions = [GlobalConfig.PPTX_TEMPLATE_FILES[x]['caption'] for x in texts]
131
 
132
+ pptx_template = st.radio(
133
+ 'Select a presentation template:',
134
+ texts,
135
+ captions=captions,
136
+ horizontal=True
137
+ )
138
+
139
+ st.divider()
140
+ submit = st.form_submit_button('Generate slide deck')
 
 
141
 
142
+ if submit:
143
+ # st.write(f'Clicked {time.time()}')
144
+ progress_text = 'Generating the slides...give it a moment'
 
 
 
 
 
 
 
 
145
  progress_bar = st.progress(0, text=progress_text)
146
 
147
  topic_txt = topic.strip()
148
+ generate_presentation(topic_txt, pptx_template, progress_bar)
149
+
150
+ st.divider()
151
+ st.text(APP_TEXT['tos'])
152
+ st.text(APP_TEXT['tos2'])
153
+
154
+ st.markdown(
155
+ '![Visitors](https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fbarunsaha%2Fslide-deck-ai&countColor=%23263759)'
156
+ )
157
 
158
 
159
+ def generate_presentation(topic: str, pptx_template: str, progress_bar):
160
  """
161
+ Process the inputs to generate the slides.
162
 
163
+ :param topic: The presentation topic based on which contents are to be generated
164
+ :param pptx_template: The PowerPoint template name to be used
165
  :param progress_bar: Progress bar from the page
166
  :return:
167
  """
 
173
  logging.debug(
174
  f'Topic: {topic}\n'
175
  )
 
176
 
177
  target_length = min(topic_length, GlobalConfig.LLM_MODEL_MAX_INPUT_LENGTH)
178
 
179
  try:
180
+ # Step 1: Generate the contents in JSON format using an LLM
181
+ json_str = process_slides_contents(topic[:target_length], progress_bar)
182
+
183
+ # Step 2: Generate the slide deck based on the template specified
184
+ if len(json_str) > 0:
185
+ st.info(
186
+ 'Tip: The generated content doesn\'t look so great?'
187
+ ' Need alternatives? Just change your description text and try again.',
188
+ icon="💡️"
189
+ )
190
+ else:
191
+ st.error('Unfortunately, JSON generation failed, so the next steps would lead to nowhere.'
192
+ ' Try again or come back later.')
193
  return
194
 
195
+ all_headers = generate_slide_deck(json_str, pptx_template, progress_bar)
 
 
 
 
196
 
197
+ # Step 3: Bonus stuff: Web references and AI art
198
+ show_bonus_stuff(all_headers)
 
 
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  except ValueError as ve:
201
  st.error(f'Unfortunately, an error occurred: {ve}! '
202
  f'Please change the text, try again later, or report it, sharing your inputs.')
 
205
  st.error('Not enough information provided! Please be little more descriptive :)')
206
 
207
 
208
+ def process_slides_contents(text: str, progress_bar: st.progress) -> str:
209
  """
210
  Convert given text into structured data and display. Update the UI.
211
 
212
+ :param text: The topic description for the presentation
213
  :param progress_bar: Progress bar for this step
214
+ :return: The contents as a JSON-formatted string
215
  """
216
 
 
217
  json_str = ''
218
 
219
  try:
220
+ logging.info(f'Calling LLM for content generation on the topic: {text}')
221
+ json_str = get_contents_wrapper(text)
222
  except Exception as ex:
223
  st.error(f'An exception occurred while trying to convert to JSON.'
224
  f' It could be because of heavy traffic or something else.'
225
  f' Try doing it again or try again later.\n'
226
  f' Error message: {ex}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
+ logging.debug(f'JSON: {json_str}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ progress_bar.progress(50, text='Contents generated')
 
231
 
232
+ with st.expander('The generated contents (in JSON format)'):
233
+ st.code(json_str, language='json')
 
 
234
 
235
+ return json_str
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
 
237
 
238
+ def generate_slide_deck(json_str: str, pptx_template: str, progress_bar) -> List:
239
+ """
240
+ Create a slide deck.
241
 
242
+ :param json_str: The contents in JSON format
243
+ :param pptx_template: The PPTX template name
244
+ :param progress_bar: Progress bar
245
+ :return: A list of all slide headers and the title
246
+ """
247
 
248
+ progress_text = 'Creating the slide deck...give it a moment'
249
+ progress_bar.progress(75, text=progress_text)
250
+
251
+ # Get a unique name for the file to save -- use the session ID
252
+ ctx = st_sr.get_script_run_ctx()
253
+ session_id = ctx.session_id
254
+ timestamp = time.time()
255
+ output_file_name = f'{session_id}_{timestamp}.pptx'
256
+
257
+ logging.info('Creating PPTX file...')
258
+ all_headers = pptx_helper.generate_powerpoint_presentation(
259
+ json_str,
260
+ as_yaml=False,
261
+ slides_template=pptx_template,
262
+ output_file_name=output_file_name
263
+ )
264
+ progress_bar.progress(100, text='Done!')
265
 
266
+ with open(output_file_name, 'rb') as f:
267
+ st.download_button('Download PPTX file', f, file_name=output_file_name)
 
 
268
 
269
+ return all_headers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
 
271
 
272
+ def show_bonus_stuff(ppt_headers: List[str]):
 
 
 
273
  """
274
+ Show bonus stuff for the presentation.
275
 
276
+ :param ppt_headers: A list of the slide headings.
277
  """
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  # Use the presentation title and the slides headers to find relevant info online
280
+ logging.info('Calling Metaphor search...')
281
  ppt_text = ' '.join(ppt_headers)
282
  search_results = get_web_search_results_wrapper(ppt_text)
283
  md_text_items = []
 
285
  for (title, link) in search_results:
286
  md_text_items.append(f'[{title}]({link})')
287
 
288
+ with st.expander('Related Web references'):
289
+ st.markdown('\n\n'.join(md_text_items))
 
 
 
 
290
 
291
+ # Avoid image generation. It costs time and an API call, so just limit to the text generation.
 
 
 
 
 
 
 
 
 
 
 
292
 
293
 
294
  def main():
 
 
 
 
295
  build_ui()
296
 
297
 
global_config.py CHANGED
@@ -25,14 +25,14 @@ class GlobalConfig:
25
  # LLM_MODEL_TEMPERATURE: float = 0.5
26
  LLM_MODEL_MIN_OUTPUT_LENGTH: int = 50
27
  LLM_MODEL_MAX_OUTPUT_LENGTH: int = 2000
28
- LLM_MODEL_MAX_INPUT_LENGTH: int = 1000
29
 
30
  METAPHOR_API_KEY = os.environ.get('METAPHOR_API_KEY', '')
31
 
32
  LOG_LEVEL = 'INFO'
33
  APP_STRINGS_FILE = 'strings.json'
34
  PRELOAD_DATA_FILE = 'examples/example_02.json'
35
- SLIDES_TEMPLATE_FILE = 'langchain_templates/template_07.txt'
36
  JSON_TEMPLATE_FILE = 'langchain_templates/text_to_json_template_02.txt'
37
 
38
  PPTX_TEMPLATE_FILES = {
 
25
  # LLM_MODEL_TEMPERATURE: float = 0.5
26
  LLM_MODEL_MIN_OUTPUT_LENGTH: int = 50
27
  LLM_MODEL_MAX_OUTPUT_LENGTH: int = 2000
28
+ LLM_MODEL_MAX_INPUT_LENGTH: int = 250
29
 
30
  METAPHOR_API_KEY = os.environ.get('METAPHOR_API_KEY', '')
31
 
32
  LOG_LEVEL = 'INFO'
33
  APP_STRINGS_FILE = 'strings.json'
34
  PRELOAD_DATA_FILE = 'examples/example_02.json'
35
+ SLIDES_TEMPLATE_FILE = 'langchain_templates/template_combined.txt'
36
  JSON_TEMPLATE_FILE = 'langchain_templates/text_to_json_template_02.txt'
37
 
38
  PPTX_TEMPLATE_FILES = {
langchain_templates/template_combined.txt ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are a helpful, intelligent chatbot. Create the slides for a presentation on the given topic. Include main headings for each slide, detailed bullet points for each slide. Add relevant content to each slide.
2
+
3
+
4
+ Topic:
5
+ <REPLACE_PLACEHOLDER>
6
+
7
+
8
+ Desired JSON output format:
9
+
10
+ {
11
+ "title": "Presentation Title",
12
+ "slides": [
13
+ {
14
+ "heading": "Heading for the First Slide",
15
+ "bullet_points": [
16
+ "First bullet point",
17
+ [
18
+ "Sub-bullet point 1",
19
+ "Sub-bullet point 1"
20
+ ],
21
+ "Second bullet point"
22
+ ]
23
+ },
24
+ {
25
+ "heading": "Heading for the Second Slide",
26
+ "bullet_points": [
27
+ "First bullet point",
28
+ "Second bullet item",
29
+ "Third bullet point"
30
+ ]
31
+ }
32
+ ]
33
+ }
34
+
35
+
36
+ Output:
37
+ ```json
llm_helper.py CHANGED
@@ -1,9 +1,7 @@
1
  import json
2
  import logging
3
  import time
4
- import metaphor_python as metaphor
5
  import requests
6
- from langchain import PromptTemplate
7
  from langchain.llms import Clarifai
8
 
9
  from global_config import GlobalConfig
@@ -14,10 +12,7 @@ logging.basicConfig(
14
  format='%(asctime)s - %(message)s',
15
  )
16
 
17
- prompt = None
18
- llm_contents = None
19
- llm_json = None
20
- metaphor_client = None
21
 
22
 
23
  def get_llm(use_gpt: bool) -> Clarifai:
@@ -28,7 +23,7 @@ def get_llm(use_gpt: bool) -> Clarifai:
28
  """
29
 
30
  if use_gpt:
31
- llm = Clarifai(
32
  pat=GlobalConfig.CLARIFAI_PAT,
33
  user_id=GlobalConfig.CLARIFAI_USER_ID_GPT,
34
  app_id=GlobalConfig.CLARIFAI_APP_ID_GPT,
@@ -37,7 +32,7 @@ def get_llm(use_gpt: bool) -> Clarifai:
37
  # temperature=0.1,
38
  )
39
  else:
40
- llm = Clarifai(
41
  pat=GlobalConfig.CLARIFAI_PAT,
42
  user_id=GlobalConfig.CLARIFAI_USER_ID,
43
  app_id=GlobalConfig.CLARIFAI_APP_ID,
@@ -47,7 +42,7 @@ def get_llm(use_gpt: bool) -> Clarifai:
47
  )
48
  # print(llm)
49
 
50
- return llm
51
 
52
 
53
  def generate_slides_content(topic: str) -> str:
@@ -55,140 +50,24 @@ def generate_slides_content(topic: str) -> str:
55
  Generate the outline/contents of slides for a presentation on a given topic.
56
 
57
  :param topic: Topic/subject matter/idea on which slides are to be generated
58
- :return: The content
59
  """
60
 
61
- global prompt
62
- global llm_contents
63
 
64
- if prompt is None:
65
- with open(GlobalConfig.SLIDES_TEMPLATE_FILE, 'r') as in_file:
66
- template_txt = in_file.read().strip()
67
 
68
- prompt = PromptTemplate.from_template(template_txt)
 
69
 
70
- formatted_prompt = prompt.format(topic=topic)
71
- # print(f'formatted_prompt:\n{formatted_prompt}')
72
-
73
- if llm_contents is None:
74
- llm_contents = get_llm(use_gpt=False)
75
-
76
- slides_content = llm_contents(formatted_prompt, verbose=True)
77
 
78
  return slides_content
79
 
80
 
81
- def text_to_json(content: str) -> str:
82
- """
83
- Convert input text into structured JSON representation.
84
-
85
- :param content: Input text
86
- :return: JSON string
87
- """
88
-
89
- global llm_json
90
-
91
- content = content.replace('```', '')
92
-
93
- # f-string is not used in order to prevent interpreting the brackets
94
- with open(GlobalConfig.JSON_TEMPLATE_FILE, 'r') as in_file:
95
- text = in_file.read()
96
- # Insert the actual text contents
97
- text = text.replace('<REPLACE_PLACEHOLDER>', content)
98
-
99
- text = text.strip()
100
- # print(text)
101
-
102
- if llm_json is None:
103
- llm_json = get_llm(use_gpt=True)
104
-
105
- output = llm_json(text, verbose=True)
106
- output = output.strip()
107
-
108
- first_index = max(0, output.find('{'))
109
- last_index = min(output.rfind('}'), len(output))
110
- output = output[first_index: last_index + 1]
111
-
112
- return output
113
-
114
-
115
- def text_to_yaml(content: str) -> str:
116
- """
117
- Convert input text into structured YAML representation.
118
-
119
- :param content: Input text
120
- :return: JSON string
121
- """
122
-
123
- global llm_json
124
-
125
- content = content.replace('```', '')
126
-
127
- # f-string is not used in order to prevent interpreting the brackets
128
- text = '''
129
- You are a helpful AI assistant.
130
- Convert the given slide deck text into structured YAML output.
131
- Also, generate and add an engaging presentation title.
132
- The output should be only correct and valid YAML having the following structure:
133
-
134
- title: "..."
135
- slides:
136
- - heading: "..."
137
- bullet_points:
138
- - "..."
139
- - "..."
140
- - heading: "..."
141
- bullet_points:
142
- - "..."
143
- - "...": # This line ends with a colon because it has a sub-block
144
- - "..."
145
- - "..."
146
-
147
-
148
- Text:
149
- '''
150
- text += content
151
- text += '''
152
-
153
-
154
-
155
-
156
- Output:
157
- ```yaml
158
- '''
159
-
160
- text = text.strip()
161
- # print(text)
162
-
163
- if llm_json is None:
164
- llm_json = get_llm(use_gpt=True)
165
-
166
- output = llm_json(text, verbose=True)
167
- output = output.strip()
168
-
169
- # first_index = max(0, output.find('{'))
170
- # last_index = min(output.rfind('}'), len(output))
171
- # output = output[first_index: last_index + 1]
172
-
173
- return output
174
-
175
-
176
- def get_related_websites(query: str) -> metaphor.api.SearchResponse:
177
- """
178
- Fetch Web search results for a given query.
179
-
180
- :param query: The query text
181
- :return: The search results object
182
- """
183
-
184
- global metaphor_client
185
-
186
- if not metaphor_client:
187
- metaphor_client = metaphor.Metaphor(api_key=GlobalConfig.METAPHOR_API_KEY)
188
-
189
- return metaphor_client.search(query, use_autoprompt=True, num_results=5)
190
-
191
-
192
  def get_ai_image(text: str) -> str:
193
  """
194
  Get a Stable Diffusion-generated image based on a given text.
 
1
  import json
2
  import logging
3
  import time
 
4
  import requests
 
5
  from langchain.llms import Clarifai
6
 
7
  from global_config import GlobalConfig
 
12
  format='%(asctime)s - %(message)s',
13
  )
14
 
15
+ llm = None
 
 
 
16
 
17
 
18
  def get_llm(use_gpt: bool) -> Clarifai:
 
23
  """
24
 
25
  if use_gpt:
26
+ _ = Clarifai(
27
  pat=GlobalConfig.CLARIFAI_PAT,
28
  user_id=GlobalConfig.CLARIFAI_USER_ID_GPT,
29
  app_id=GlobalConfig.CLARIFAI_APP_ID_GPT,
 
32
  # temperature=0.1,
33
  )
34
  else:
35
+ _ = Clarifai(
36
  pat=GlobalConfig.CLARIFAI_PAT,
37
  user_id=GlobalConfig.CLARIFAI_USER_ID,
38
  app_id=GlobalConfig.CLARIFAI_APP_ID,
 
42
  )
43
  # print(llm)
44
 
45
+ return _
46
 
47
 
48
  def generate_slides_content(topic: str) -> str:
 
50
  Generate the outline/contents of slides for a presentation on a given topic.
51
 
52
  :param topic: Topic/subject matter/idea on which slides are to be generated
53
+ :return: The content in JSON format
54
  """
55
 
56
+ # global prompt
57
+ global llm
58
 
59
+ with open(GlobalConfig.SLIDES_TEMPLATE_FILE, 'r') as in_file:
60
+ template_txt = in_file.read().strip()
61
+ template_txt = template_txt.replace('<REPLACE_PLACEHOLDER>', topic)
62
 
63
+ if llm is None:
64
+ llm = get_llm(use_gpt=True)
65
 
66
+ slides_content = llm(template_txt, verbose=True)
 
 
 
 
 
 
67
 
68
  return slides_content
69
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  def get_ai_image(text: str) -> str:
72
  """
73
  Get a Stable Diffusion-generated image based on a given text.
pptx_helper.py CHANGED
@@ -9,6 +9,22 @@ from global_config import GlobalConfig
9
 
10
 
11
  PATTERN = re.compile(r"^slide[ ]+\d+:", re.IGNORECASE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  logging.basicConfig(
14
  level=GlobalConfig.LOG_LEVEL,
 
9
 
10
 
11
  PATTERN = re.compile(r"^slide[ ]+\d+:", re.IGNORECASE)
12
+ SAMPLE_JSON_FOR_PPTX = '''
13
+ {
14
+ "title": "Understanding AI",
15
+ "slides": [
16
+ {
17
+ "heading": "Introduction",
18
+ "bullet_points": [
19
+ "Brief overview of AI",
20
+ [
21
+ "Importance of understanding AI"
22
+ ]
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ '''
28
 
29
  logging.basicConfig(
30
  level=GlobalConfig.LOG_LEVEL,
strings.json CHANGED
@@ -14,7 +14,7 @@
14
  "Since you have come this far, we have unlocked some more good stuff for you!"
15
  ],
16
  "input_labels": [
17
- "**Describe the topic of the presentation. Avoid mentioning the count of slides.**\n*Note: the output may be truncated due to API limitations.*"
18
  ],
19
  "button_labels": [
20
  "Generate contents",
 
14
  "Since you have come this far, we have unlocked some more good stuff for you!"
15
  ],
16
  "input_labels": [
17
+ "**Describe the topic of the presentation using 10 to 250 characters. Avoid mentioning the count of slides.**"
18
  ],
19
  "button_labels": [
20
  "Generate contents",