Crackershoot commited on
Commit
d2ae2c3
Β·
verified Β·
1 Parent(s): 9b5db97

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -270
app.py CHANGED
@@ -15,36 +15,37 @@ from agno.vectordb.lancedb import LanceDb, SearchType
15
  import gradio as gr
16
 
17
  # Import libraries for handling PDFs and images.
18
- import fitz # PyMuPDF, used for PDF processing.
19
- from PIL import Image # Pillow library for image manipulation.
20
- import io # Used to handle in-memory binary streams.
21
- import requests # For making HTTP requests to download files.
22
- import re # Regular expressions for searching text patterns.
23
-
24
- # Configure basic logging to output messages to the console.
 
 
25
  logging.basicConfig(stream=sys.stdout, level=logging.INFO)
26
- # Get a logger instance for this script.
27
  logger = logging.getLogger(__name__)
28
 
29
- # Retrieve the OpenAI API key from environment variables.
 
 
30
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
31
- # If the API key is not found, raise an error.
32
  if not OPENAI_API_KEY:
33
  raise ValueError("Missing OPENAI_API_KEY")
34
 
35
- # Initialize the Knowledge Base for the agent.
 
 
36
  knowledge = Knowledge(
37
- # Use LanceDB as the vector database to store and search document embeddings.
38
  vector_db=LanceDb(
39
- uri="tmp/lancedb", # Directory to store the database.
40
- table_name="pdf_documents", # Name of the table within the database.
41
- search_type=SearchType.vector, # Use vector search for finding relevant documents.
42
- # Use OpenAI's embedding model to convert text into numerical vectors.
43
  embedder=OpenAIEmbedder(id="text-embedding-3-small"),
44
  )
45
  )
46
 
47
- # A list of URLs pointing to PDF documents that will be added to the knowledge base.
48
  pdf_urls = [
49
  "https://media.datacamp.com/cms/working-with-hugging-face.pdf",
50
  "https://media.datacamp.com/cms/ai-agents-cheat-sheet.pdf",
@@ -52,89 +53,72 @@ pdf_urls = [
52
  "https://media.datacamp.com/legacy/image/upload/v1719844709/Marketing/Blog/Azure_CLI_Cheat_Sheet.pdf"
53
  ]
54
 
55
- # Defines a function to download a file from a URL if it doesn't already exist locally.
56
- def download_if_needed(url, filename):
57
- # Check if the file path does not exist.
58
- if not os.path.exists(filename):
59
- logger.info(f"Downloading {url}...")
60
- # Send an HTTP GET request to the URL.
61
- response = requests.get(url)
62
- # Open the local file in write-binary mode.
63
- with open(filename, "wb") as f:
64
- # Write the content of the response to the file.
65
- f.write(response.content)
66
- logger.info(f"Downloaded {filename} ({len(response.content)} bytes)")
67
-
68
- # Create a directory named 'pdf_cache' to store downloaded PDF files.
69
- # 'exist_ok=True' prevents an error if the directory already exists.
70
  os.makedirs("pdf_cache", exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- # Defines a function to add the specified PDFs to the agent's knowledge base.
73
  def add_pdfs_to_knowledge():
74
- """Add PDFs to knowledge base using the correct method for the installed agno version"""
75
- # Create an empty list to hold information about the content to be added.
76
  contents_to_add = []
77
-
78
- # Loop through the list of PDF URLs with their index.
79
  for i, url in enumerate(pdf_urls):
80
- # Define a local filename for the cached PDF.
81
  filename = f"pdf_cache/file_{i}.pdf"
82
  try:
83
- # Download the PDF if it's not already in the cache.
84
  download_if_needed(url, filename)
85
- # Prepare a dictionary with the file path and metadata (source URL).
86
  contents_to_add.append({
87
  "path": filename,
88
  "metadata": {"source": url}
89
  })
90
- logger.info(f"Prepared PDF {i+1}: {url}")
91
  except Exception as e:
92
- # Log an error if the PDF preparation fails.
93
- logger.error(f"Failed to prepare PDF {i+1}: {str(e)}")
94
-
95
- # Proceed only if there are PDFs to add.
96
- if contents_to_add:
97
- try:
98
- # This block checks for the correct method to add documents based on the 'agno' library version.
99
- # Check if the 'add_contents' method (for batch processing) exists.
100
- if hasattr(knowledge, 'add_contents'):
101
- knowledge.add_contents(contents_to_add)
102
- logger.info(f"βœ… Successfully added {len(contents_to_add)} PDFs using add_contents")
103
- # Else, check if the 'add_content' method (for single item processing) exists.
104
- elif hasattr(knowledge, 'add_content'):
105
- for item in contents_to_add:
106
- knowledge.add_content(**item)
107
- logger.info(f"βœ… Successfully added {len(contents_to_add)} PDFs using add_content")
108
- # As a fallback for older versions, manually read and insert the documents.
109
- else:
110
- from agno.document.reader.pdf_reader import PDFReader
111
- reader = PDFReader()
112
- all_docs = []
113
- for item in contents_to_add:
114
- docs = reader.read(item["path"])
115
- for doc in docs:
116
- doc.metadata = item["metadata"]
117
- all_docs.append(doc)
118
- knowledge.vector_db.insert(documents=all_docs)
119
- logger.info(f"βœ… Successfully added {len(all_docs)} document chunks from {len(contents_to_add)} PDFs")
120
- except Exception as e:
121
- # Log and re-raise any exception that occurs during the addition process.
122
- logger.error(f"Failed to add PDFs: {str(e)}")
123
- raise
124
- else:
125
- # Warn if no PDFs were prepared.
126
- logger.warning("No PDFs were prepared to add")
127
 
128
- # Call the function to load the PDFs into the knowledge base.
129
- add_pdfs_to_knowledge()
130
 
131
- # Initialize the AI agent with its configuration.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  agent = Agent(
133
- # Set the underlying language model to OpenAI's GPT-4.1-mini with low temperature for more predictable responses.
134
  model=OpenAIChat(id="gpt-4.1-mini", temperature=0.2),
135
- # Give the agent a name/description.
136
  description="You are Dox a data expert!",
137
- # Provide detailed instructions (the "system prompt") that govern the agent's behavior.
138
  instructions="""
139
  You are a data professional's assistant named Dox.
140
  Your primary goal is to answer questions about data, programming, cloud computing, AI/ML, and technology topics.
@@ -158,237 +142,103 @@ agent = Agent(
158
  * NEVER invent or hallucinate information. If an answer cannot be found, state that directly.
159
  Make sure to follow these instructions precisely.
160
  """,
161
- # Link the agent to the knowledge base created earlier.
162
  knowledge=knowledge,
163
- # Automatically add the current date and time to the agent's context.
164
  add_datetime_to_context=True,
165
- # Automatically add the user's location to the context (if available).
166
  add_location_to_context=True,
167
- # Enable the agent to search its knowledge base by default.
168
  search_knowledge=True,
169
- # Equip the agent with tools, in this case, the ability to search the web using DuckDuckGo.
170
  tools=[DuckDuckGoTools()],
171
- # Enable markdown formatting in the agent's output.
172
  markdown=True
173
  )
174
 
175
- # Log a success message indicating the agent is ready.
176
  logger.info("🟒 Agent initialized successfully!")
177
 
178
- # Defines a function to process a user's question.
 
 
179
  def ask_agent(question):
180
- logger.info(f"Question asked: {question[:100]}...")
181
- # Run the agent with the user's question, ensuring it uses its knowledge base.
182
  response = agent.run(question, use_knowledge=True)
183
- # Get the agent's response as a single string.
184
- full_content = response.get_content_as_string()
185
- # Use a regular expression to find the first URL ending in '.pdf' in the response.
186
- match = re.search(r'https?://[^\s]+\.pdf', full_content, re.IGNORECASE)
187
- # Extract the link if a match is found, otherwise set it to None.
188
- link = match.group(0) if match else None
189
-
190
- if link:
191
- logger.info(f"PDF link found: {link}")
192
- else:
193
- logger.info("πŸ”΄ No PDF link found in response")
194
- # Return the full text response and the extracted PDF link.
195
- return full_content, link
196
 
197
- # Defines a function to download the raw content of a PDF from a URL.
198
  def download_pdf_from_url(url):
199
- # Make an HTTP GET request with a timeout.
200
- response = requests.get(url, timeout=30)
201
- # Raise an exception if the request was not successful (e.g., 404 error).
202
- response.raise_for_status()
203
- # Return the binary content of the PDF.
204
- return response.content
205
-
206
- # A Gradio helper function to update the UI while a PDF is being prepared for display.
207
  def prepare_pdf_loading(link):
208
- # If a link exists, show a "Loading..." message.
209
- if link:
210
- return gr.update(value="πŸ“„ Loading PDF preview...", visible=True)
211
- # Otherwise, hide the message.
212
- return gr.update(value="", visible=False)
213
 
214
- # Defines a function to display the first page of a PDF as an image.
215
  def display_pdf(pdf_url):
216
- # If no URL is provided, hide the image and status components in the UI.
217
  if not pdf_url:
218
- return (
219
- gr.update(value=None, visible=False),
220
- gr.update(value="", visible=False)
221
- )
 
222
  try:
223
- # Download the PDF content from the URL.
224
  pdf_bytes = download_pdf_from_url(pdf_url)
225
- # Open the PDF from the in-memory bytes.
226
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
227
- # Get the first page of the document.
228
  page = doc[0]
229
- # Create a transformation matrix to render the page at a higher resolution (5x zoom).
230
- mat = fitz.Matrix(5, 5)
231
- # Get a pixmap (a raster image) of the page.
232
- pix = page.get_pixmap(matrix=mat)
233
- # Convert the pixmap to a PNG image using PIL.
234
  img = Image.open(io.BytesIO(pix.tobytes("png")))
235
- # Close the PDF document to free up resources.
236
  doc.close()
237
- # Return the image to be displayed in the UI and hide any status messages.
238
- return (
239
- gr.update(value=img, visible=True),
240
- gr.update(value="", visible=False)
241
- )
242
  except Exception as e:
243
- # If an error occurs, log it and display a failure message in the UI.
244
- logger.error(f"PDF error: {e}")
245
- return (
246
- gr.update(value=None, visible=False),
247
- gr.update(value="❌ Failed to load PDF", visible=True)
248
- )
249
-
250
- # Define a custom theme for the Gradio interface.
251
  theme = gr.themes.Ocean(
252
  font=[gr.themes.GoogleFont("Quicksand"), "sans-serif"],
253
  font_mono=[gr.themes.GoogleFont("Fira Code"), "monospace"],
254
  )
255
 
256
- # Create the Gradio interface using `gr.Blocks` for a custom layout.
257
- with gr.Blocks(
258
- title="# πŸ€– Dox the Data Professional's Advisor πŸ€–",
259
- theme=theme
260
- ) as demo:
261
- # Add titles and descriptions using Markdown.
262
  gr.Markdown("# πŸ€– Dox the Data Professional's Advisor πŸ€–")
263
  gr.Markdown("### 🧠 Dox knows about 4 DataCamp cheat sheets: (1️⃣ Hugging Face | 2️⃣ AI Agents | 3️⃣ SQL with AI | 4️⃣ Azure CLI):")
264
-
265
- # Create a main row for the layout.
266
  with gr.Row():
267
- # LEFT-SIDE COLUMN: for the chat interface.
268
  with gr.Column(scale=3):
269
- # The chatbot display window.
270
- chatbot = gr.Chatbot(label="πŸ’¬ Conversation", elem_classes="chatbot", height=450)
271
- # A text area for status messages (used for PDF loading status).
272
  status_text = gr.Markdown("")
273
- # The textbox where the user types their question.
274
- question = gr.Textbox(
275
- label="πŸ™‹ Ask Dox a question:",
276
- placeholder="πŸ€” Type your question here...",
277
- lines=1
278
- )
279
- # The submit button.
280
  ask_btn = gr.Button("Submit πŸ“€", variant="primary")
281
- # A section for example questions.
282
- gr.Markdown("### πŸ’‘ Example Questions")
283
- gr.Examples(
284
- examples=[
285
- "How do you log into Azure using device code authentication?",
286
- "What are the three main components of an AI agent?",
287
- "What are the \"core four\" Hugging Face libraries?",
288
- "What SQL clause is used to filter data after grouping?",
289
- "How to use \"HAVING\" clause in SQL?",
290
- "What is the latest GPT model?"
291
- ],
292
- inputs=question,
293
- )
294
- # RIGHT-SIDE COLUMN: for the PDF preview.
295
  with gr.Column(scale=2):
296
  gr.Markdown("### πŸ“„ Referenced PDF Document")
297
- # A hidden state to store the PDF link found in the agent's response.
298
  link_state = gr.State()
299
- # A markdown component to show PDF loading status.
300
  pdf_status = gr.Markdown(visible=False)
301
- # An image component to display the PDF preview.
302
- output_image = gr.Image(
303
- label="⬇️ Cheat Sheet Preview",
304
- visible=False
305
- )
306
-
307
- # Defines the main chat logic as a generator function for streaming output.
308
- def chat_ui(user_message, chat_history):
309
- # Initialize chat history if it's the first turn.
310
- if chat_history is None:
311
- chat_history = []
312
-
313
- # Append the user's message to the chat history.
314
- chat_history.append({
315
- "role": "user",
316
- "content": user_message
317
- })
318
-
319
- # Append a temporary "Thinking..." message from the assistant.
320
- chat_history.append({
321
- "role": "assistant",
322
- "content": "πŸ€” Thinking..."
323
- })
324
-
325
- # `yield` immediately updates the UI with the user's message and "Thinking...".
326
- # It also clears the user's input textbox.
327
- yield (
328
- chat_history,
329
- None, # No link yet.
330
- gr.update(value=None, visible=False), # Hide image preview.
331
- "" # Clear textbox.
332
- )
333
-
334
- # Call the agent to get the actual response and PDF link.
335
- response_text, link = ask_agent(user_message)
336
-
337
- # Replace the "Thinking..." message with the final response from the agent.
338
- chat_history[-1] = {
339
- "role": "assistant",
340
- "content": response_text
341
- }
342
-
343
- # `yield` again to update the UI with the final response.
344
- yield (
345
- chat_history,
346
- link, # Pass the extracted link to the link_state.
347
- gr.update(value=None, visible=False), # Keep image preview hidden for now.
348
- "" # Keep textbox clear.
349
- )
350
-
351
- # This is a helper function to avoid repeating the event handler chain.
352
- def submit_chain():
353
- # It specifies that `chat_ui` is the function to run.
354
- # It maps the `question` textbox and `chatbot` history as inputs.
355
- # It maps the outputs to `chatbot` history, `link_state`, `output_image`, and clears the `question` textbox.
356
- return (
357
- chat_ui,
358
- [question, chatbot],
359
- [chatbot, link_state, output_image, question]
360
- )
361
-
362
- # Set up the event handler for the "Submit" button click.
363
  ask_btn.click(
364
- *submit_chain()
365
- # `.then()` chains subsequent actions after the first one completes.
366
- ).then(
367
- # After chat_ui, call `prepare_pdf_loading` to show the "loading" message.
368
- prepare_pdf_loading,
369
- inputs=link_state, # Use the link from chat_ui's output.
370
- outputs=pdf_status # Update the pdf_status text.
371
- ).then(
372
- # Finally, call `display_pdf` to render the PDF page.
373
- display_pdf,
374
- inputs=link_state, # Use the same link.
375
- outputs=[output_image, pdf_status] # Update the image and hide the status text.
376
- )
377
-
378
- # Set up the same event handler for when the user presses Enter in the textbox.
379
- question.submit(
380
- *submit_chain()
381
  ).then(
382
- prepare_pdf_loading,
383
- inputs=link_state,
384
- outputs=pdf_status
385
  ).then(
386
- display_pdf,
387
- inputs=link_state,
388
- outputs=[output_image, pdf_status]
389
  )
390
 
391
- # This block ensures the code inside only runs when the script is executed directly.
 
 
392
  if __name__ == "__main__":
393
- # Launch the Gradio web server.
394
- demo.launch()
 
15
  import gradio as gr
16
 
17
  # Import libraries for handling PDFs and images.
18
+ import fitz # PyMuPDF
19
+ from PIL import Image
20
+ import io
21
+ import requests
22
+ import re
23
+
24
+ # -------------------------------------------------------------------
25
+ # Logging
26
+ # -------------------------------------------------------------------
27
  logging.basicConfig(stream=sys.stdout, level=logging.INFO)
 
28
  logger = logging.getLogger(__name__)
29
 
30
+ # -------------------------------------------------------------------
31
+ # Environment
32
+ # -------------------------------------------------------------------
33
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
 
34
  if not OPENAI_API_KEY:
35
  raise ValueError("Missing OPENAI_API_KEY")
36
 
37
+ # -------------------------------------------------------------------
38
+ # Knowledge Base
39
+ # -------------------------------------------------------------------
40
  knowledge = Knowledge(
 
41
  vector_db=LanceDb(
42
+ uri="tmp/lancedb",
43
+ table_name="pdf_documents",
44
+ search_type=SearchType.vector,
 
45
  embedder=OpenAIEmbedder(id="text-embedding-3-small"),
46
  )
47
  )
48
 
 
49
  pdf_urls = [
50
  "https://media.datacamp.com/cms/working-with-hugging-face.pdf",
51
  "https://media.datacamp.com/cms/ai-agents-cheat-sheet.pdf",
 
53
  "https://media.datacamp.com/legacy/image/upload/v1719844709/Marketing/Blog/Azure_CLI_Cheat_Sheet.pdf"
54
  ]
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  os.makedirs("pdf_cache", exist_ok=True)
57
+ INDEX_MARKER = "tmp/lancedb/.indexed"
58
+
59
+ # Shared HTTP session (HF Spaces safe)
60
+ SESSION = requests.Session()
61
+
62
+ def download_if_needed(url, filename):
63
+ if os.path.exists(filename) and os.path.getsize(filename) > 0:
64
+ return
65
+ logger.info(f"Downloading {url}...")
66
+ r = SESSION.get(url, timeout=30)
67
+ r.raise_for_status()
68
+ with open(filename, "wb") as f:
69
+ f.write(r.content)
70
+ logger.info(f"Downloaded {filename}")
71
 
 
72
  def add_pdfs_to_knowledge():
 
 
73
  contents_to_add = []
 
 
74
  for i, url in enumerate(pdf_urls):
 
75
  filename = f"pdf_cache/file_{i}.pdf"
76
  try:
 
77
  download_if_needed(url, filename)
 
78
  contents_to_add.append({
79
  "path": filename,
80
  "metadata": {"source": url}
81
  })
 
82
  except Exception as e:
83
+ logger.error(f"PDF {i+1} failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ if not contents_to_add:
86
+ return
87
 
88
+ if hasattr(knowledge, "add_contents"):
89
+ knowledge.add_contents(contents_to_add)
90
+ elif hasattr(knowledge, "add_content"):
91
+ for item in contents_to_add:
92
+ knowledge.add_content(**item)
93
+ else:
94
+ from agno.document.reader.pdf_reader import PDFReader
95
+ reader = PDFReader()
96
+ docs = []
97
+ for item in contents_to_add:
98
+ for d in reader.read(item["path"]):
99
+ d.metadata = item["metadata"]
100
+ docs.append(d)
101
+ knowledge.vector_db.insert(documents=docs)
102
+
103
+ logger.info("βœ… Knowledge base indexed")
104
+
105
+ def ensure_indexed():
106
+ if os.path.exists(INDEX_MARKER):
107
+ logger.info("Knowledge already indexed β€” skipping.")
108
+ return
109
+ add_pdfs_to_knowledge()
110
+ os.makedirs(os.path.dirname(INDEX_MARKER), exist_ok=True)
111
+ with open(INDEX_MARKER, "w") as f:
112
+ f.write("ok")
113
+
114
+ ensure_indexed()
115
+
116
+ # -------------------------------------------------------------------
117
+ # Agent (UNCHANGED PROMPT)
118
+ # -------------------------------------------------------------------
119
  agent = Agent(
 
120
  model=OpenAIChat(id="gpt-4.1-mini", temperature=0.2),
 
121
  description="You are Dox a data expert!",
 
122
  instructions="""
123
  You are a data professional's assistant named Dox.
124
  Your primary goal is to answer questions about data, programming, cloud computing, AI/ML, and technology topics.
 
142
  * NEVER invent or hallucinate information. If an answer cannot be found, state that directly.
143
  Make sure to follow these instructions precisely.
144
  """,
 
145
  knowledge=knowledge,
 
146
  add_datetime_to_context=True,
 
147
  add_location_to_context=True,
 
148
  search_knowledge=True,
 
149
  tools=[DuckDuckGoTools()],
 
150
  markdown=True
151
  )
152
 
 
153
  logger.info("🟒 Agent initialized successfully!")
154
 
155
+ # -------------------------------------------------------------------
156
+ # Agent call
157
+ # -------------------------------------------------------------------
158
  def ask_agent(question):
 
 
159
  response = agent.run(question, use_knowledge=True)
160
+ text = response.get_content_as_string()
161
+ match = re.search(r'https?://[^\s\)]+\.pdf', text, re.IGNORECASE)
162
+ return text, match.group(0) if match else None
163
+
164
+ # -------------------------------------------------------------------
165
+ # PDF preview (cached, no new imports)
166
+ # -------------------------------------------------------------------
167
+ PDF_IMAGE_CACHE = {}
 
 
 
 
 
168
 
 
169
  def download_pdf_from_url(url):
170
+ r = SESSION.get(url, timeout=30)
171
+ r.raise_for_status()
172
+ return r.content
173
+
 
 
 
 
174
  def prepare_pdf_loading(link):
175
+ return gr.update(value="πŸ“„ Loading PDF preview...", visible=bool(link))
 
 
 
 
176
 
 
177
  def display_pdf(pdf_url):
 
178
  if not pdf_url:
179
+ return gr.update(value=None, visible=False), gr.update(value="", visible=False)
180
+
181
+ if pdf_url in PDF_IMAGE_CACHE:
182
+ return gr.update(value=PDF_IMAGE_CACHE[pdf_url], visible=True), gr.update(value="", visible=False)
183
+
184
  try:
 
185
  pdf_bytes = download_pdf_from_url(pdf_url)
 
186
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
 
187
  page = doc[0]
188
+ pix = page.get_pixmap(matrix=fitz.Matrix(3, 3))
 
 
 
 
189
  img = Image.open(io.BytesIO(pix.tobytes("png")))
 
190
  doc.close()
191
+ PDF_IMAGE_CACHE[pdf_url] = img
192
+ return gr.update(value=img, visible=True), gr.update(value="", visible=False)
 
 
 
193
  except Exception as e:
194
+ logger.error(e)
195
+ return gr.update(value=None, visible=False), gr.update(value="❌ Failed to load PDF", visible=True)
196
+
197
+ # -------------------------------------------------------------------
198
+ # UI (UNCHANGED TITLES / SETTINGS)
199
+ # -------------------------------------------------------------------
 
 
200
  theme = gr.themes.Ocean(
201
  font=[gr.themes.GoogleFont("Quicksand"), "sans-serif"],
202
  font_mono=[gr.themes.GoogleFont("Fira Code"), "monospace"],
203
  )
204
 
205
+ with gr.Blocks(title="# πŸ€– Dox the Data Professional's Advisor πŸ€–", theme=theme) as demo:
 
 
 
 
 
206
  gr.Markdown("# πŸ€– Dox the Data Professional's Advisor πŸ€–")
207
  gr.Markdown("### 🧠 Dox knows about 4 DataCamp cheat sheets: (1️⃣ Hugging Face | 2️⃣ AI Agents | 3️⃣ SQL with AI | 4️⃣ Azure CLI):")
208
+
 
209
  with gr.Row():
 
210
  with gr.Column(scale=3):
211
+ chatbot = gr.Chatbot(label="πŸ’¬ Conversation", height=450)
 
 
212
  status_text = gr.Markdown("")
213
+ question = gr.Textbox(label="πŸ™‹ Ask Dox a question:", placeholder="πŸ€” Type your question here...")
 
 
 
 
 
 
214
  ask_btn = gr.Button("Submit πŸ“€", variant="primary")
215
+
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  with gr.Column(scale=2):
217
  gr.Markdown("### πŸ“„ Referenced PDF Document")
 
218
  link_state = gr.State()
 
219
  pdf_status = gr.Markdown(visible=False)
220
+ output_image = gr.Image(visible=False)
221
+
222
+ def chat_ui(user_message, history):
223
+ history = history or []
224
+ history.append((user_message, "πŸ€” Thinking..."))
225
+ yield history, None, None, ""
226
+ answer, link = ask_agent(user_message)
227
+ history[-1] = (user_message, answer)
228
+ yield history, link, None, ""
229
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  ask_btn.click(
231
+ chat_ui,
232
+ [question, chatbot],
233
+ [chatbot, link_state, output_image, question]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  ).then(
235
+ prepare_pdf_loading, link_state, pdf_status
 
 
236
  ).then(
237
+ display_pdf, link_state, [output_image, pdf_status]
 
 
238
  )
239
 
240
+ # -------------------------------------------------------------------
241
+ # Hugging Face Spaces launch
242
+ # -------------------------------------------------------------------
243
  if __name__ == "__main__":
244
+ demo.launch(server_name="0.0.0.0", server_port=7860)