EditsPaarth/PowerPoint-AI
0
1import pathlib2import logging3import tempfile4from typing import List, Tuple5 6import json57import metaphor_python as metaphor8import streamlit as st9 10from helpers import llm_helper, pptx_helper11from global_config import GlobalConfig12 13 14APP_TEXT = json5.loads(open(GlobalConfig.APP_STRINGS_FILE, 'r', encoding='utf-8').read())15GB_CONVERTER = 2 ** 3016 17 18logger = logging.getLogger(__name__)19 20 21@st.cache_data22def get_contents_wrapper(text: str) -> str:23 """24 Fetch and cache the slide deck contents on a topic by calling an external API.25 26 :param text: The presentation topic.27 :return: The slide deck contents or outline in JSON format.28 """29 30 logger.info('LLM call because of cache miss...')31 return llm_helper.generate_slides_content(text).strip()32 33 34@st.cache_resource35def get_metaphor_client_wrapper() -> metaphor.Metaphor:36 """37 Create a Metaphor client for semantic Web search.38 39 :return: Metaphor instance.40 """41 42 return metaphor.Metaphor(api_key=GlobalConfig.METAPHOR_API_KEY)43 44 45@st.cache_data46def get_web_search_results_wrapper(text: str) -> List[Tuple[str, str]]:47 """48 Fetch and cache the Web search results on a given topic.49 50 :param text: The topic.51 :return: A list of (title, link) tuples.52 """53 54 results = []55 search_results = get_metaphor_client_wrapper().search(56 text,57 use_autoprompt=True,58 num_results=559 )60 61 for a_result in search_results.results:62 results.append((a_result.title, a_result.url))63 64 return results65 66 67def build_ui():68 """69 Display the input elements for content generation. Only covers the first step.70 """71 72 # get_disk_used_percentage()73 74 st.title(APP_TEXT['app_name'])75 st.subheader(APP_TEXT['caption'])76 st.markdown(77 'Powered by'78 ' [Mistral-7B-Instruct-v0.2](https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2).'79 )80 st.markdown(81 '*If the JSON is generated or parsed incorrectly, try again later by making minor changes'82 ' to the input text.*'83 )84 85 with st.form('my_form'):86 # Topic input87 try:88 with open(GlobalConfig.PRELOAD_DATA_FILE, 'r', encoding='utf-8') as in_file:89 preload_data = json5.loads(in_file.read())90 except (FileExistsError, FileNotFoundError):91 preload_data = {'topic': '', 'audience': ''}92 93 topic = st.text_area(94 APP_TEXT['input_labels'][0],95 value=preload_data['topic']96 )97 98 texts = list(GlobalConfig.PPTX_TEMPLATE_FILES.keys())99 captions = [GlobalConfig.PPTX_TEMPLATE_FILES[x]['caption'] for x in texts]100 101 pptx_template = st.radio(102 'Select a presentation template:',103 texts,104 captions=captions,105 horizontal=True106 )107 108 st.divider()109 submit = st.form_submit_button('Generate slide deck')110 111 if submit:112 # st.write(f'Clicked {time.time()}')113 st.session_state.submitted = True114 115 # https://github.com/streamlit/streamlit/issues/3832#issuecomment-1138994421116 if 'submitted' in st.session_state:117 progress_text = 'Generating the slides...give it a moment'118 progress_bar = st.progress(0, text=progress_text)119 120 topic_txt = topic.strip()121 generate_presentation(topic_txt, pptx_template, progress_bar)122 123 st.divider()124 st.text(APP_TEXT['tos'])125 st.text(APP_TEXT['tos2'])126 127 st.markdown(128 '![Visitors]'129 '(https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fbarunsaha%2Fslide-deck-ai&countColor=%23263759)'130 )131 132 133def generate_presentation(topic: str, pptx_template: str, progress_bar):134 """135 Process the inputs to generate the slides.136 137 :param topic: The presentation topic based on which contents are to be generated.138 :param pptx_template: The PowerPoint template name to be used.139 :param progress_bar: Progress bar from the page.140 """141 142 topic_length = len(topic)143 logger.debug('Input length:: topic: %s', topic_length)144 145 if topic_length >= 10:146 logger.debug('Topic: %s', topic)147 target_length = min(topic_length, GlobalConfig.LLM_MODEL_MAX_INPUT_LENGTH)148 149 try:150 # Step 1: Generate the contents in JSON format using an LLM151 json_str = process_slides_contents(topic[:target_length], progress_bar)152 logger.debug('Truncated topic: %s', topic[:target_length])153 logger.debug('Length of JSON: %d', len(json_str))154 155 # Step 2: Generate the slide deck based on the template specified156 if len(json_str) > 0:157 st.info(158 'Tip: The generated content doesn\'t look so great?'159 ' Need alternatives? Just change your description text and try again.',160 icon="💡️"161 )162 else:163 st.error(164 'Unfortunately, JSON generation failed, so the next steps would lead'165 ' to nowhere. Try again or come back later.'166 )167 return168 169 all_headers = generate_slide_deck(json_str, pptx_template, progress_bar)170 171 # Step 3: Bonus stuff: Web references and AI art172 show_bonus_stuff(all_headers)173 174 except ValueError as ve:175 st.error(f'Unfortunately, an error occurred: {ve}! '176 f'Please change the text, try again later, or report it, sharing your inputs.')177 178 else:179 st.error('Not enough information provided! Please be little more descriptive :)')180 181 182def process_slides_contents(text: str, progress_bar: st.progress) -> str:183 """184 Convert given text into structured data and display. Update the UI.185 186 :param text: The topic description for the presentation.187 :param progress_bar: Progress bar for this step.188 :return: The contents as a JSON-formatted string.189 """190 191 json_str = ''192 193 try:194 logger.info('Calling LLM for content generation on the topic: %s', text)195 json_str = get_contents_wrapper(text)196 except Exception as ex:197 st.error(198 f'An exception occurred while trying to convert to JSON. It could be because of heavy'199 f' traffic or something else. Try doing it again or try again later.'200 f'\nError message: {ex}'201 )202 203 progress_bar.progress(50, text='Contents generated')204 205 with st.expander('The generated contents (in JSON format)'):206 st.code(json_str, language='json')207 208 return json_str209 210 211def generate_slide_deck(json_str: str, pptx_template: str, progress_bar) -> List:212 """213 Create a slide deck.214 215 :param json_str: The contents in JSON format.216 :param pptx_template: The PPTX template name.217 :param progress_bar: Progress bar.218 :return: A list of all slide headers and the title.219 """220 221 progress_text = 'Creating the slide deck...give it a moment'222 progress_bar.progress(75, text=progress_text)223 224 # # Get a unique name for the file to save -- use the session ID225 # ctx = st_sr.get_script_run_ctx()226 # session_id = ctx.session_id227 # timestamp = time.time()228 # output_file_name = f'{session_id}_{timestamp}.pptx'229 230 temp = tempfile.NamedTemporaryFile(delete=False, suffix='.pptx')231 path = pathlib.Path(temp.name)232 233 logger.info('Creating PPTX file...')234 all_headers = pptx_helper.generate_powerpoint_presentation(235 json_str,236 slides_template=pptx_template,237 output_file_path=path238 )239 progress_bar.progress(100, text='Done!')240 241 with open(path, 'rb') as f:242 st.download_button('Download PPTX file', f, file_name='Presentation.pptx')243 244 if temp:245 temp.close()246 247 return all_headers248 249 250def show_bonus_stuff(ppt_headers: List[str]):251 """252 Show bonus stuff for the presentation.253 254 :param ppt_headers: A list of the slide headings.255 """256 257 # Use the presentation title and the slide headers to find relevant info online258 logger.info('Calling Metaphor search...')259 ppt_text = ' '.join(ppt_headers)260 search_results = get_web_search_results_wrapper(ppt_text)261 md_text_items = []262 263 for (title, link) in search_results:264 md_text_items.append(f'[{title}]({link})')265 266 with st.expander('Related Web references'):267 st.markdown('\n\n'.join(md_text_items))268 269 logger.info('Done!')270 271 # # Avoid image generation. It costs time and an API call, so just limit to the text generation.272 # with st.expander('AI-generated image on the presentation topic'):273 # logger.info('Calling SDXL for image generation...')274 # # img_empty.write('')275 # # img_text.write(APP_TEXT['image_info'])276 # image = get_ai_image_wrapper(ppt_text)277 #278 # if len(image) > 0:279 # image = base64.b64decode(image)280 # st.image(image, caption=ppt_text)281 # st.info('Tip: Right-click on the image to save it.', icon="💡️")282 # logger.info('Image added')283 284 285def main():286 """287 Trigger application run.288 """289 290 build_ui()291 292 293if __name__ == '__main__':294 main()