juniorjukeko commited on
Commit
a6c3afc
·
verified ·
1 Parent(s): a3f9851

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -134
app.py CHANGED
@@ -1,11 +1,9 @@
1
  import os
 
2
  from pypdf import PdfReader
3
  from langchain_community.document_loaders import PyPDFLoader
4
  from langchain_core.documents import Document
5
  from langchain_text_splitters import RecursiveCharacterTextSplitter
6
- from langchain_openai import ChatOpenAI, OpenAI
7
- from langchain_core.prompts import PromptTemplate
8
- from langchain.chains.summarize import load_summarize_chain
9
  import gradio as gr
10
 
11
  title = '''
@@ -15,7 +13,7 @@ title = '''
15
  How to Use:<br/>
16
  1. Upload a .PDF from your computer and fill OpenAI API key.<br/>
17
  2. Click the "Upload PDF" button, if successful a preview of your PDF text will be shown.<br/>
18
- 3. Click "Summarize!" and the output will be shown on the textbox bellow.<br/>
19
  You can also change some LLM configurations from the 'config' tab.<br/>
20
  </div>
21
  '''
@@ -23,31 +21,31 @@ title = '''
23
  desc_1 = '''
24
  <div style="text-align: left; font-family:Arial; color:Black; font-size: 14px;">
25
  <h3>Custom Prompt Template</h3>
26
- <p style="text-align: left;">You can customize input prompt for the map and combine prompt of langchain's Map-Reduce Summarization pipeline
27
- using the texboxt bellow.<br/>
28
- Prompt which will be fed into LLM use the format of : <b>{textbox input} + {pdf_text} + "SUMMARY:"</b> <br/>
29
- In essence each page of PDF will be summarized using map prompt, and each summary then be combined for final output using combine prompt.<br/>
30
  <a href="https://python.langchain.com/docs/use_cases/summarization">More Info on Map-Reduce for Summarization</a>
31
  </div>
32
  '''
33
 
34
  MAP_PROMPT = """
35
- You will be given a page of text which section is enclosed in triple backticks (```).
36
- Your goal is to give a summary of this section, ignoring references and footnote if present.
37
- Your response should be at least 200 words only if input classified as academic text.
38
- Your response must fully encompass what was said in the page.
39
-
40
- ```{text}```
41
- SUMMARY:
42
- """
43
  COMBINE_PROMPT = """
44
- Write a full summary of the following text enclosed in triple backticks (```).
45
- Full summary consists of a descriptive summary of at least 100 words (if possible),
46
- followed by numbered list which covers key points of the text.
 
 
 
47
 
48
- ```{text}```
49
- SUMMARY:
50
- """
51
  config_info = {'temperature': 'Higher means more randomness to the output.',
52
  'max_tokens' : 'The maximum number of tokens to generate in the output.',
53
  'llm_list' : ''}
@@ -58,130 +56,150 @@ model_list = {'gpt-3.5-turbo':'chat',
58
 
59
  text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n", "\n"], chunk_size=10000, chunk_overlap=250)
60
 
 
 
 
 
61
  def parse_pdf(pdf_file):
62
  global pdf_docs, page_count
 
 
63
  loader = PyPDFLoader(pdf_file.name)
64
  pdf_docs = loader.load_and_split(text_splitter)
65
  page_count = len(pdf_docs)
66
- file_check(pdf_file)
67
- return pdf_docs[0].page_content[:100]
68
-
69
- def summarize_pdf(api_key, model_name, temperature, llm_max_tokens, custom_map_prompt, custom_combine_prompt):
70
- if not pdf_docs:
71
- raise gr.Error("No PDF File Detected!")
72
-
73
- os.environ["OPENAI_API_KEY"] = api_key
74
-
75
- # Updated LLM Initialization
76
- if model_list[model_name] == 'chat':
77
- gpt_llm = ChatOpenAI(temperature=temperature, model=model_name, max_tokens=int(llm_max_tokens))
78
- else:
79
- gpt_llm = OpenAI(temperature=temperature, model=model_name, max_tokens=int(llm_max_tokens))
80
-
81
- # Prompt Logic
82
- map_template = PromptTemplate(template=generate_template(custom_map_prompt) if custom_map_prompt else MAP_PROMPT, input_variables=["text"])
83
- combine_template = PromptTemplate(template=generate_template(custom_combine_prompt) if custom_combine_prompt else COMBINE_PROMPT, input_variables=["text"])
84
-
85
- map_reduce_chain = load_summarize_chain(
86
- gpt_llm,
87
- chain_type="map_reduce",
88
- map_prompt=map_template,
89
- combine_prompt=combine_template,
90
- token_max=3840
91
- )
92
-
93
- # Updated Invocation (invoke instead of __call__)
94
- map_reduce_outputs = map_reduce_chain.invoke({"input_documents": pdf_docs})
95
- return map_reduce_outputs['output_text']
96
 
97
  def file_check(pdf_file):
98
- if os.path.getsize(pdf_file.name)/1024 **2 > 1:
99
- raise gr.Error("Maximum File Size is 1MB!")
100
- elif page_count > 15:
101
- raise gr.Error("Maximum File Length is 15 Pages!")
102
- else:
103
- pass
104
-
105
- # Build LLM Model
106
- os.environ["OPENAI_API_KEY"] = api_key
107
- if model_list[model_name] == 'chat':
108
- gpt_llm = ChatOpenAI(temperature=temperature, model_name=model_name, max_tokens=int(llm_max_tokens))
109
- else:
110
- gpt_llm = OpenAI(temperature=temperature, model_name=model_name, max_tokens=int(llm_max_tokens))
111
-
112
- # Summarize PDF
113
- if custom_map_prompt !="":
114
- map_template = PromptTemplate(template=generate_template(custom_map_prompt), input_variables=["text"])
115
- else:
116
- map_template = PromptTemplate(template=MAP_PROMPT, input_variables=["text"])
117
-
118
- if custom_combine_prompt !="":
119
- combine_template = PromptTemplate(template=generate_template(custom_combine_prompt), input_variables=["text"])
120
- else:
121
- combine_template = PromptTemplate(template=COMBINE_PROMPT, input_variables=["text"])
122
-
123
- map_reduce_chain = load_summarize_chain(
124
- gpt_llm,
125
- chain_type="map_reduce",
126
- map_prompt=map_template,
127
- combine_prompt=combine_template,
128
- return_intermediate_steps=True,
129
- token_max=3840 # limit the maximum number of tokens in the combined document (combine prompt).
130
- )
131
- map_reduce_outputs = map_reduce_chain({"input_documents": pdf_docs})
132
- return map_reduce_outputs['output_text']
133
 
134
  def generate_template(custom_prompt):
135
- custom_template = custom_prompt + '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- ```{text}```
138
- SUMMARY:
139
- '''
140
- return custom_template
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  def main():
143
- with gr.Blocks() as demo:
144
- gr.HTML(title)
145
- with gr.Tab("Main"):
146
- with gr.Column():
147
- pdf_doc = gr.File(label="Uploaded PDF:", file_types=['.pdf'])
148
- with gr.Row():
149
- submit_button = gr.Button(value="Upload!")
150
- pdf_preview = gr.Textbox(label="PDF Preview:", lines=2, interactive=False)
151
-
152
- API_KEY = gr.Textbox(label="OpenAI API Key:", lines=1, type="password")
153
- summarize_button = gr.Button(value="Summarize!")
154
- summarized_text = gr.Textbox(label="Summary", lines=10, show_copy_button=True)
155
-
156
- with gr.Tab("Config"):
157
- llm_model = gr.Dropdown(choices=model_list.keys(), label="LLM model used", value='gpt-3.5-turbo', interactive=True)
158
- with gr.Row():
159
- temperature = gr.Slider(minimum=0, maximum=0.5, step=0.1, label="temperature", info=config_info['temperature'])
160
- llm_max_tokens = gr.Radio(choices=[128, 256, 512], value=256, interactive=True, label="LLM max tokens", info=config_info['max_tokens'])
161
- gr.HTML(desc_1)
162
- with gr.Row():
163
- user_map_prompt = gr.Textbox(label="Map PROMPT", lines=10, interactive=True)
164
- user_comb_prompt = gr.Textbox(label="Combine PROMPT", lines=10, interactive=True)
165
-
166
- with gr.Accordion("Default Template", open=False):
167
- with gr.Row():
168
- default_map_prompt = gr.Textbox(label="Map PROMPT", value=MAP_PROMPT, lines=10, interactive=False)
169
- default_comb_prompt = gr.Textbox(label="Combine PROMPT", value=COMBINE_PROMPT, lines=10, interactive=False)
170
- with gr.Accordion("User Custom Prompt Preview", open=False):
171
- prompt_preview_button = gr.Button(value="View Custom Prompt")
172
- with gr.Row():
173
- custom_map_view = gr.Textbox(label="Map PROMPT", lines=10, interactive=False)
174
- custom_comb_view = gr.Textbox(label="Combine PROMPT", lines=10, interactive=False)
175
-
176
- prompt_preview_button.click(generate_template, inputs=[user_map_prompt], outputs=[custom_map_view])
177
- prompt_preview_button.click(generate_template, inputs=[user_comb_prompt], outputs=[custom_comb_view])
178
-
179
- inputs_list = [API_KEY, llm_model, temperature, llm_max_tokens, user_map_prompt, user_comb_prompt]
180
-
181
- submit_button.click(parse_pdf, inputs=[pdf_doc], outputs=[pdf_preview])
182
- summarize_button.click(summarize_pdf, inputs=inputs_list, outputs=[summarized_text])
183
-
184
- demo.queue(concurrency_count=1).launch(share=True)
 
 
 
 
 
 
185
 
186
  if __name__ == "__main__":
187
  main()
 
1
  import os
2
+ import openai
3
  from pypdf import PdfReader
4
  from langchain_community.document_loaders import PyPDFLoader
5
  from langchain_core.documents import Document
6
  from langchain_text_splitters import RecursiveCharacterTextSplitter
 
 
 
7
  import gradio as gr
8
 
9
  title = '''
 
13
  How to Use:<br/>
14
  1. Upload a .PDF from your computer and fill OpenAI API key.<br/>
15
  2. Click the "Upload PDF" button, if successful a preview of your PDF text will be shown.<br/>
16
+ 3. Click "Summarize!" and the output will be shown on the textbox below.<br/>
17
  You can also change some LLM configurations from the 'config' tab.<br/>
18
  </div>
19
  '''
 
21
  desc_1 = '''
22
  <div style="text-align: left; font-family:Arial; color:Black; font-size: 14px;">
23
  <h3>Custom Prompt Template</h3>
24
+ <p style="text-align: left;">You can customize input prompt for the map and combine prompt of the map-reduce summarization pipeline
25
+ using the textbox below.<br/>
26
+ Prompt which will be fed into LLM uses the format: <b>{textbox input} + {pdf_text} + "SUMMARY:"</b> <br/>
27
+ In essence each page of PDF will be summarized using the map prompt, and each summary then be combined for final output using combine prompt.<br/>
28
  <a href="https://python.langchain.com/docs/use_cases/summarization">More Info on Map-Reduce for Summarization</a>
29
  </div>
30
  '''
31
 
32
  MAP_PROMPT = """
33
+ You will be given a page of text which section is enclosed in triple backticks (```).
34
+ Your goal is to give a summary of this section, ignoring references and footnote if present.
35
+ Your response should be at least 200 words only if input classified as academic text.
36
+ Your response must fully encompass what was said in the page.
37
+ ```{text}```
38
+ SUMMARY:
39
+ """
40
+
41
  COMBINE_PROMPT = """
42
+ Write a full summary of the following text enclosed in triple backticks (```).
43
+ Full summary consists of a descriptive summary of at least 100 words (if possible),
44
+ followed by numbered list which covers key points of the text.
45
+ ```{text}```
46
+ SUMMARY:
47
+ """
48
 
 
 
 
49
  config_info = {'temperature': 'Higher means more randomness to the output.',
50
  'max_tokens' : 'The maximum number of tokens to generate in the output.',
51
  'llm_list' : ''}
 
56
 
57
  text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n", "\n"], chunk_size=10000, chunk_overlap=250)
58
 
59
+ # globals to hold parsed PDF
60
+ pdf_docs = []
61
+ page_count = 0
62
+
63
  def parse_pdf(pdf_file):
64
  global pdf_docs, page_count
65
+ if pdf_file is None:
66
+ raise gr.Error("Please upload a PDF file.")
67
  loader = PyPDFLoader(pdf_file.name)
68
  pdf_docs = loader.load_and_split(text_splitter)
69
  page_count = len(pdf_docs)
70
+ file_check(pdf_file) # will raise gr.Error on invalid
71
+ # show first 200 chars preview
72
+ return pdf_docs[0].page_content[:200] if page_count > 0 else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  def file_check(pdf_file):
75
+ global page_count
76
+ size_mb = os.path.getsize(pdf_file.name) / (1024 ** 2)
77
+ if size_mb > 1:
78
+ raise gr.Error("Maximum File Size is 1MB!")
79
+ if page_count > 15:
80
+ raise gr.Error("Maximum File Length is 15 Pages!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  def generate_template(custom_prompt):
83
+ # ensure {text} placeholder remains for substitution
84
+ custom_template = custom_prompt + '''
85
+ ```{text}```
86
+ SUMMARY:
87
+ '''
88
+ return custom_template
89
+
90
+ def _call_openai_chat(model, prompt, temperature, max_tokens):
91
+ # Chat model path
92
+ resp = openai.ChatCompletion.create(
93
+ model=model,
94
+ messages=[{"role": "user", "content": prompt}],
95
+ temperature=float(temperature),
96
+ max_tokens=int(max_tokens)
97
+ )
98
+ return resp["choices"][0]["message"]["content"].strip()
99
+
100
+ def _call_openai_completion(model, prompt, temperature, max_tokens):
101
+ # Completion (instruct) model path
102
+ resp = openai.Completion.create(
103
+ model=model,
104
+ prompt=prompt,
105
+ temperature=float(temperature),
106
+ max_tokens=int(max_tokens),
107
+ n=1,
108
+ stop=None
109
+ )
110
+ return resp["choices"][0]["text"].strip()
111
 
112
+ def summarize_pdf(api_key, model_name, temperature, llm_max_tokens, custom_map_prompt, custom_combine_prompt):
113
+ global pdf_docs
114
+ if not pdf_docs:
115
+ raise gr.Error("No PDF File Detected! Please upload a PDF first and click Upload.")
116
+
117
+ # set API key
118
+ openai.api_key = api_key
119
+ if not api_key:
120
+ raise gr.Error("OpenAI API key is required.")
121
+
122
+ model_type = model_list.get(model_name, "chat")
123
+ map_template = generate_template(custom_map_prompt) if custom_map_prompt else MAP_PROMPT
124
+ combine_template = generate_template(custom_combine_prompt) if custom_combine_prompt else COMBINE_PROMPT
125
+
126
+ map_summaries = []
127
+ try:
128
+ # MAP step: summarize each page/chunk
129
+ for idx, doc in enumerate(pdf_docs, start=1):
130
+ text = doc.page_content
131
+ prompt = map_template.replace("{text}", text)
132
+ if model_type == "chat":
133
+ summary = _call_openai_chat(model_name, prompt, temperature, llm_max_tokens)
134
+ else:
135
+ summary = _call_openai_completion(model_name, prompt, temperature, llm_max_tokens)
136
+ map_summaries.append(f"--- Page {idx} Summary ---\n{summary}\n")
137
+
138
+ # COMBINE step: combine map summaries and produce final summary
139
+ combined_text = "\n\n".join(map_summaries)
140
+ combine_prompt = combine_template.replace("{text}", combined_text)
141
+
142
+ # For combine, you might allow more tokens (we reuse llm_max_tokens here)
143
+ if model_type == "chat":
144
+ final = _call_openai_chat(model_name, combine_prompt, temperature, llm_max_tokens)
145
+ else:
146
+ final = _call_openai_completion(model_name, combine_prompt, temperature, llm_max_tokens)
147
+
148
+ return final
149
+ except openai.error.OpenAIError as e:
150
+ raise gr.Error(f"OpenAI API error: {str(e)}")
151
+ except Exception as e:
152
+ raise gr.Error(f"Unexpected error: {str(e)}")
153
 
154
  def main():
155
+ with gr.Blocks() as demo:
156
+ gr.HTML(title)
157
+ with gr.Tab("Main"):
158
+ with gr.Column():
159
+ pdf_doc = gr.File(label="Uploaded PDF:", file_types=['.pdf'])
160
+ with gr.Row():
161
+ submit_button = gr.Button(value="Upload!")
162
+ pdf_preview = gr.Textbox(label="PDF Preview:", lines=4, interactive=False)
163
+
164
+ API_KEY = gr.Textbox(label="OpenAI API Key:", lines=1, type="password")
165
+ summarize_button = gr.Button(value="Summarize!")
166
+ summarized_text = gr.Textbox(label="Summary", lines=10, show_copy_button=True)
167
+
168
+ with gr.Tab("Config"):
169
+ llm_model = gr.Dropdown(choices=list(model_list.keys()), label="LLM model used", value='gpt-3.5-turbo', interactive=True)
170
+ with gr.Row():
171
+ temperature = gr.Slider(minimum=0, maximum=0.5, step=0.1, label="temperature", info=config_info['temperature'])
172
+ llm_max_tokens = gr.Radio(choices=[128, 256, 512], value=256, interactive=True, label="LLM max tokens", info=config_info['max_tokens'])
173
+ gr.HTML(desc_1)
174
+ with gr.Row():
175
+ user_map_prompt = gr.Textbox(label="Map PROMPT", lines=10, interactive=True)
176
+ user_comb_prompt = gr.Textbox(label="Combine PROMPT", lines=10, interactive=True)
177
+
178
+ with gr.Accordion("Default Template", open=False):
179
+ with gr.Row():
180
+ default_map_prompt = gr.Textbox(label="Map PROMPT", value=MAP_PROMPT, lines=10, interactive=False)
181
+ default_comb_prompt = gr.Textbox(label="Combine PROMPT", value=COMBINE_PROMPT, lines=10, interactive=False)
182
+
183
+ with gr.Accordion("User Custom Prompt Preview", open=False):
184
+ prompt_preview_button = gr.Button(value="View Custom Prompt")
185
+ with gr.Row():
186
+ custom_map_view = gr.Textbox(label="Map PROMPT", lines=10, interactive=False)
187
+ custom_comb_view = gr.Textbox(label="Combine PROMPT", lines=10, interactive=False)
188
+
189
+ # preview custom templates (wrapping the user's prompt)
190
+ def preview_custom(p):
191
+ if not p:
192
+ return ""
193
+ return generate_template(p)
194
+ prompt_preview_button.click(preview_custom, inputs=[user_map_prompt], outputs=[custom_map_view])
195
+ prompt_preview_button.click(preview_custom, inputs=[user_comb_prompt], outputs=[custom_comb_view])
196
+
197
+ inputs_list = [API_KEY, llm_model, temperature, llm_max_tokens, user_map_prompt, user_comb_prompt]
198
+
199
+ submit_button.click(parse_pdf, inputs=[pdf_doc], outputs=[pdf_preview])
200
+ summarize_button.click(summarize_pdf, inputs=inputs_list, outputs=[summarized_text])
201
+
202
+ demo.queue(concurrency_count=1).launch(share=True)
203
 
204
  if __name__ == "__main__":
205
  main()