Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import markdown | |
| from digester.gradio_method_service import GradioMethodService | |
| title_html = """ | |
| <h1 align=\"center\">DigestEverythingGPT</h1> | |
| <p align=\"center\"> | |
| DigestEverythingGPT leverages ChatGPT/LLMs to help users quickly understand essential information from various forms of content, such as podcasts, YouTube videos, and PDF documents.<br> | |
| The prompt engineering is chained and tuned so that is result is of high quality and fast. It is not a simple single query and response tool.<br> | |
| Version 20230627 ( | |
| <a href="https://github.com/michaelthwan/digest-everything-gpt"><i class="fa fa-github"></i> Github</a> | |
| ) ( | |
| <a href="https://huggingface.co/spaces/michaelthwan/digest-everything-gpt"> HFSpace</a> | |
| )<br> | |
| Please give me a star in github if you like it 🌟! Your feedback will help us to improve</p> | |
| """ | |
| cancel_handles = [] | |
| class GradioUIService: | |
| def get_functions(): | |
| functions = { | |
| "Fetch and summarize!": { | |
| "function": GradioMethodService.fetch_and_summarize, | |
| }, | |
| "Ask": { | |
| "function": GradioMethodService.ask_question | |
| }, | |
| "Test formatting": { | |
| "function": GradioMethodService.test_formatting | |
| }, | |
| "Test asking": { | |
| "function": GradioMethodService.test_asking | |
| }, | |
| } | |
| return functions | |
| def post_define_functions(functions, folder_md): | |
| """Append extra gradio objects to functions after creating gradio objects""" | |
| functions["Fetch and summarize!"]["extra_outputs"] = [folder_md] | |
| return functions | |
| def get_gradio_ui(): | |
| def get_extra_outputs(functions, fn_key): | |
| if functions[fn_key].get('extra_outputs'): | |
| return functions[fn_key]['extra_outputs'] | |
| return [] | |
| # gr.Chatbot.postprocess = GradioUIService.format_io | |
| functions = GradioUIService.get_functions() | |
| with gr.Blocks(theme=GradioUIService.get_theme(), css=GradioUIService.get_css()) as demo: | |
| gr.HTML(title_html) | |
| with gr.Row().style(equal_height=True): | |
| with gr.Column(scale=1): | |
| with gr.Row(): | |
| apikey_textbox = gr.Textbox(label="OpenAI API key", placeholder="e.g. sk-xxxxx", css_class="api-key") | |
| with gr.Row(): | |
| source_textbox = gr.Dropdown( | |
| ["youtube", "podcast (not support now)", "pdf (not support now)"], | |
| value="youtube", label="Source", info="Choose your content provider" | |
| # TODO: dynamic list from everything2text4prompt | |
| ) | |
| with gr.Row(): | |
| source_target_textbox = gr.Textbox(show_label=True, label="URL / source target", | |
| placeholder="For youtube video, give video id\nFor podcast, give podcast URL") | |
| with gr.Accordion("Options", open=True): | |
| with gr.Row(): | |
| gpt_model_textbox = gr.Dropdown( | |
| ["gpt-3.5-turbo-16k", "gpt-4"], | |
| value="gpt-3.5-turbo-16k", label="GPT model", info="gpt-3.5 is cheaper.\nBut if you found that the result is not good, try gpt-4 \nYour API key must support gpt-4" | |
| ) | |
| with gr.Row(): | |
| language_textbox = gr.Dropdown( | |
| ["en-US", "zh-CN", "zh-TW", "it-IT", "fr-FR", "de-DE", "es-ES", "ja-JP", "ko-KR", "ru-RU", ], | |
| value="en-US", label="Language", info="Choose your language, regardless of video language" | |
| ) | |
| with gr.Row(): | |
| functions["Fetch and summarize!"]["btn"] = gr.Button("Fetch and summarize!", variant="primary") | |
| with gr.Row().style(equal_height=True): | |
| gr.Markdown(f"Status: ") | |
| status_md = gr.Markdown(f"Normal") | |
| with gr.Row(): | |
| folder_md = gr.Markdown(f"Waiting for source target input") | |
| with gr.Row(): | |
| qa_textbox = gr.Textbox(show_label=False, placeholder="Ask questions").style(container=False) | |
| with gr.Row(): | |
| functions["Ask"]["btn"] = gr.Button("Ask", variant="primary") | |
| with gr.Row(): | |
| reset_btn = gr.Button("Reset", variant="secondary") | |
| reset_btn.style(size="sm") | |
| stop_btn = gr.Button("Stop", variant="secondary") | |
| stop_btn.style(size="sm") | |
| with gr.Accordion("debug", open=True): | |
| with gr.Row(): | |
| functions["Test formatting"]["btn"] = gr.Button("Test formatting") | |
| functions["Test asking"]["btn"] = gr.Button("Test asking") | |
| with gr.Column(scale=3): | |
| chatbot = gr.Chatbot() | |
| chatbot.style(height=1100) | |
| history = gr.State([]) | |
| # after creating gradio objects, append to functions to centralize things. | |
| functions = GradioUIService.post_define_functions(functions, folder_md) | |
| #### handle click(=submit) and cancel behaviour | |
| # Standard inputs/outputs (global for all actions) | |
| inputs = [apikey_textbox, source_textbox, source_target_textbox, qa_textbox, gpt_model_textbox, language_textbox, chatbot, history] | |
| outputs = [chatbot, history, status_md] | |
| # fetch_and_summarize_textbox | |
| fn_key = "Fetch and summarize!" | |
| analyze_code_base_args = dict(fn=functions[fn_key]["function"], inputs=inputs, outputs=[*outputs, folder_md]) | |
| cancel_handles.append(source_target_textbox.submit(**analyze_code_base_args)) | |
| # qa_textbox | |
| fn_key = "Ask" | |
| ask_args = dict(fn=functions[fn_key]["function"], inputs=inputs, outputs=outputs) | |
| cancel_handles.append(qa_textbox.submit(**ask_args)) | |
| # all buttons | |
| for fn_key in functions: | |
| click_handle = functions[fn_key]["btn"].click(fn=functions[fn_key]["function"], | |
| inputs=inputs, outputs=[*outputs, *get_extra_outputs(functions, fn_key)]) | |
| cancel_handles.append(click_handle) | |
| stop_btn.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) | |
| reset_btn.click(fn=lambda: ([], [], "Already reset"), inputs=None, outputs=outputs) | |
| demo.title = "DigestEverythingGPT" | |
| return demo | |
| def format_io(self, y): | |
| """ | |
| Convert the input and output to HTML format. | |
| Paragraphize the input part of the last item in y, | |
| and convert the Markdown and mathematical formula in the output part to HTML format. | |
| """ | |
| def text_divide_paragraph(text): | |
| """ | |
| Separate the text according to the paragraph separator and generate HTML code with paragraph tags. | |
| """ | |
| if '```' in text: | |
| return text | |
| else: | |
| lines = text.split("\n") | |
| for i, line in enumerate(lines): | |
| lines[i] = lines[i].replace(" ", " ") | |
| text = "</br>".join(lines) | |
| return text | |
| def close_up_code_segment_during_stream(gpt_reply): | |
| """ | |
| Handling when the GPT output is cut in half | |
| Add '```' at the end of the output if the output is not complete. | |
| """ | |
| # guard pattern for normal cases | |
| if '```' not in gpt_reply: | |
| return gpt_reply | |
| if gpt_reply.endswith('```'): | |
| return gpt_reply | |
| # otherwise | |
| segments = gpt_reply.split('```') | |
| n_mark = len(segments) - 1 | |
| if n_mark % 2 == 1: | |
| return gpt_reply + '\n```' | |
| else: | |
| return gpt_reply | |
| def markdown_convertion(txt): | |
| """ | |
| Convert markdown text to HTML format | |
| """ | |
| pre = '<div class="markdown-body">' | |
| suf = '</div>' | |
| # if ('$' in txt) and ('```' not in txt): | |
| # return pre + markdown.markdown(txt, extensions=['fenced_code', 'tables']) + '<br><br>' + markdown.markdown(convert_math(txt, splitParagraphs=False), | |
| # extensions=['fenced_code', 'tables']) + suf | |
| # else: | |
| # return pre + markdown.markdown(txt, extensions=['fenced_code', 'tables']) + suf | |
| return pre + markdown.markdown(txt, extensions=['fenced_code', 'tables']) + suf | |
| if y is None or y == []: return [] | |
| i_ask, gpt_reply = y[-1] | |
| i_ask = text_divide_paragraph(i_ask) | |
| gpt_reply = close_up_code_segment_during_stream(gpt_reply) | |
| # y[-1] = ( | |
| # None if i_ask is None else markdown.markdown(i_ask, extensions=['fenced_code', 'tables']), | |
| # None if gpt_reply is None else markdown_convertion(gpt_reply) | |
| # ) | |
| return y | |
| def get_theme(): | |
| try: | |
| set_theme = gr.themes.Default( | |
| primary_hue=gr.themes.utils.colors.cyan, | |
| neutral_hue=gr.themes.utils.colors.gray, | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif", ], | |
| font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "Consolas", "ui-monospace", "monospace"] | |
| ) | |
| except Exception as e: | |
| set_theme = None | |
| print(f'please upgrade to newer version of gradio {e}') | |
| return set_theme | |
| def get_css(): | |
| css = """ | |
| /* Set the outer margins of the table to 1em, merge the borders between internal cells, and display empty cells. */ | |
| .markdown-body table { | |
| margin: 1em 0; | |
| border-collapse: collapse; | |
| empty-cells: show; | |
| } | |
| /* Set the inner margin of the table cell to 5px, the border thickness to 1.2px, and the color to --border-color-primary. */ | |
| .markdown-body th, .markdown-body td { | |
| border: 1.2px solid var(--border-color-primary); | |
| padding: 5px; | |
| } | |
| /* Set the table header background color to rgba(175,184,193,0.2) and transparency to 0.2. */ | |
| .markdown-body thead { | |
| background-color: rgba(175,184,193,0.2); | |
| } | |
| /* Set the padding of the table header cell to 0.5em and 0.2em. */ | |
| .markdown-body thead th { | |
| padding: .5em .2em; | |
| } | |
| /* Remove the default padding of the list prefix to align it with the text line. */ | |
| .markdown-body ol, .markdown-body ul { | |
| padding-inline-start: 2em !important; | |
| } | |
| /* Set the style of the chat bubble, including the radius, the maximum width, and the shadow. */ | |
| [class *= "message"] { | |
| border-radius: var(--radius-xl) !important; | |
| /* padding: var(--spacing-xl) !important; */ | |
| /* font-size: var(--text-md) !important; */ | |
| /* line-height: var(--line-md) !important; */ | |
| /* min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl)); */ | |
| /* min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl)); */ | |
| } | |
| [data-testid = "bot"] { | |
| max-width: 95%; | |
| /* width: auto !important; */ | |
| border-bottom-left-radius: 0 !important; | |
| } | |
| [data-testid = "user"] { | |
| max-width: 100%; | |
| /* width: auto !important; */ | |
| border-bottom-right-radius: 0 !important; | |
| } | |
| /* Set the background of the inline code to light gray, set the radius and spacing. */ | |
| .markdown-body code { | |
| font-family: 'JetBrains Mono', monospace; | |
| display: inline; | |
| white-space: break-spaces; | |
| border-radius: 6px; | |
| margin: 0 2px 0 2px; | |
| padding: .2em .4em .1em .4em; | |
| background-color: rgba(175,184,193,0.2); | |
| } | |
| /* Set the style of the code block, including the background color, the inner and outer margins, and the radius. */ | |
| .markdown-body pre code { | |
| font-family: 'JetBrains Mono', monospace; | |
| display: block; | |
| overflow: auto; | |
| white-space: pre; | |
| background-color: rgba(175,184,193,0.2); | |
| border-radius: 10px; | |
| padding: 1em; | |
| margin: 1em 2em 1em 0.5em; | |
| } | |
| """ | |
| return css | |