ragunath-ravi commited on
Commit
2da7fa0
·
verified ·
1 Parent(s): c9e1414

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +126 -232
app.py CHANGED
@@ -13,6 +13,10 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
13
  from langchain_community.embeddings import HuggingFaceEmbeddings
14
  from langchain_community.vectorstores import FAISS
15
  from langchain.docstore.document import Document
 
 
 
 
16
 
17
  # Import document parsers
18
  import PyPDF2
@@ -30,8 +34,12 @@ HF_TOKEN = os.getenv("hf_token")
30
  if not HF_TOKEN:
31
  raise ValueError("HuggingFace token not found in environment variables")
32
 
33
- # Initialize HuggingFace Inference Client
34
- client = InferenceClient(model="meta-llama/Llama-3.1-8B-Instruct", token=HF_TOKEN)
 
 
 
 
35
 
36
  # Initialize embeddings
37
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
@@ -197,29 +205,60 @@ class IngestionAgent:
197
  self.message_bus.publish(response)
198
 
199
  class RetrievalAgent:
200
- """Agent responsible for embedding and semantic retrieval"""
201
 
202
  def __init__(self, message_bus: MessageBus):
203
  self.name = "RetrievalAgent"
204
  self.message_bus = message_bus
205
  self.message_bus.subscribe(self.name, self.handle_message)
206
  self.vector_store = None
 
 
 
 
 
 
 
 
207
 
208
  def handle_message(self, message: MCPMessage):
209
  """Handle incoming MCP messages"""
210
  if message.type == "INGESTION_COMPLETE":
211
  self.create_vector_store(message)
212
  elif message.type == "RETRIEVAL_REQUEST":
213
- self.retrieve_context(message)
214
 
215
  def create_vector_store(self, message: MCPMessage):
216
- """Create vector store from processed documents"""
217
  documents = message.payload.get("documents", [])
218
 
219
  if documents:
220
  try:
221
  self.vector_store = FAISS.from_documents(documents, embeddings)
222
- logger.info(f"Vector store created with {len(documents)} documents")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  # Notify completion
225
  response = MCPMessage(
@@ -233,102 +272,60 @@ class RetrievalAgent:
233
  except Exception as e:
234
  logger.error(f"Error creating vector store: {e}")
235
 
236
- def retrieve_context(self, message: MCPMessage):
237
- """Retrieve relevant context for a query"""
238
  query = message.payload.get("query", "")
239
- k = message.payload.get("k", 3)
240
 
241
- if self.vector_store and query:
242
- try:
243
- docs = self.vector_store.similarity_search(query, k=k)
244
- context = [{"content": doc.page_content, "source": doc.metadata.get("source", "")}
245
- for doc in docs]
246
-
247
- response = MCPMessage(
248
- sender=self.name,
249
- receiver="LLMResponseAgent",
250
- msg_type="CONTEXT_RESPONSE",
251
- trace_id=message.trace_id,
252
- payload={
253
- "query": query,
254
- "retrieved_context": context,
255
- "top_chunks": [doc.page_content for doc in docs]
256
- }
257
- )
258
- self.message_bus.publish(response)
259
- except Exception as e:
260
- logger.error(f"Error retrieving context: {e}")
261
-
262
- class LLMResponseAgent:
263
- """Agent responsible for generating LLM responses"""
264
-
265
- def __init__(self, message_bus: MessageBus):
266
- self.name = "LLMResponseAgent"
267
- self.message_bus = message_bus
268
- self.message_bus.subscribe(self.name, self.handle_message)
269
-
270
- def handle_message(self, message: MCPMessage):
271
- """Handle incoming MCP messages"""
272
- if message.type == "CONTEXT_RESPONSE":
273
- self.generate_response(message)
274
-
275
- def generate_response(self, message: MCPMessage):
276
- """Generate response using retrieved context"""
277
- query = message.payload.get("query", "")
278
- context = message.payload.get("retrieved_context", [])
279
-
280
- # Build context string
281
- context_text = "\n\n".join([f"Source: {ctx['source']}\nContent: {ctx['content']}"
282
- for ctx in context])
283
 
284
- # Create messages for conversational format
285
- messages = [
286
- {
287
- "role": "system",
288
- "content": "You are a helpful assistant. Based on the provided context below, answer the user's question accurately and comprehensively. Cite the sources if possible.",
289
- },
290
- {
291
- "role": "user",
292
- "content": f"Context:\n\n{context_text}\n\nQuestion: {query}"
293
- }
294
- ]
295
-
296
  try:
297
- # Use client.chat_completion for conversational models
298
- response_stream = client.chat_completion(
299
- messages=messages,
300
- max_tokens=512,
301
- temperature=0.7,
302
- stream=True
303
- )
 
 
 
 
 
 
 
 
 
 
 
304
 
305
- # Send streaming response
306
  response = MCPMessage(
307
  sender=self.name,
308
  receiver="CoordinatorAgent",
309
- msg_type="LLM_RESPONSE_STREAM",
310
  trace_id=message.trace_id,
311
  payload={
312
  "query": query,
313
- "response_stream": response_stream,
314
- "context": context
315
  }
316
  )
317
  self.message_bus.publish(response)
318
 
319
  except Exception as e:
320
- logger.error(f"Error generating response: {e}")
321
- # Send an error stream back
322
- error_msg = f"Error from LLM: {e}"
323
- def error_generator():
324
- yield error_msg
325
-
326
  response = MCPMessage(
327
  sender=self.name,
328
  receiver="CoordinatorAgent",
329
- msg_type="LLM_RESPONSE_STREAM",
330
  trace_id=message.trace_id,
331
- payload={"response_stream": error_generator()}
 
 
 
 
332
  )
333
  self.message_bus.publish(response)
334
 
@@ -339,15 +336,15 @@ class CoordinatorAgent:
339
  self.name = "CoordinatorAgent"
340
  self.message_bus = message_bus
341
  self.message_bus.subscribe(self.name, self.handle_message)
342
- self.current_response_stream = None
343
  self.vector_store_ready = False
 
344
 
345
  def handle_message(self, message: MCPMessage):
346
  """Handle incoming MCP messages"""
347
  if message.type == "VECTORSTORE_READY":
348
  self.vector_store_ready = True
349
- elif message.type == "LLM_RESPONSE_STREAM":
350
- self.current_response_stream = message.payload.get("response_stream")
351
 
352
  def process_files(self, files):
353
  """Process uploaded files"""
@@ -367,59 +364,56 @@ class CoordinatorAgent:
367
 
368
  return f"Processing {len(files)} files: {', '.join([os.path.basename(fp) for fp in file_paths])}"
369
 
370
- def handle_query(self, query: str, history: List) -> Generator[str, None, None]:
371
- """Handle user query and return streaming response"""
372
  if not self.vector_store_ready:
373
- yield "Please upload and process documents first."
374
- return
375
 
376
  # Send retrieval request
377
  message = MCPMessage(
378
  sender=self.name,
379
  receiver="RetrievalAgent",
380
  msg_type="RETRIEVAL_REQUEST",
381
- payload={"query": query}
382
  )
383
  self.message_bus.publish(message)
384
 
385
- # Wait for response and stream
386
  import time
387
- timeout = 20 # seconds
388
  start_time = time.time()
389
 
390
- while not self.current_response_stream and (time.time() - start_time) < timeout:
391
  time.sleep(0.1)
392
 
393
- if self.current_response_stream:
394
- try:
395
- # Stream tokens directly
396
- for chunk in self.current_response_stream:
397
- # The token is in chunk.choices[0].delta.content for chat_completion
398
- token = chunk.choices[0].delta.content
399
- if token:
400
- yield token
401
- except Exception as e:
402
- yield f"Error streaming response: {e}"
403
- finally:
404
- self.current_response_stream = None # Reset for next query
 
 
 
405
  else:
406
- yield "Timeout: No response received from LLM agent."
407
 
408
  # Initialize agents
409
  ingestion_agent = IngestionAgent(message_bus)
410
  retrieval_agent = RetrievalAgent(message_bus)
411
- llm_response_agent = LLMResponseAgent(message_bus)
412
  coordinator_agent = CoordinatorAgent(message_bus)
413
 
414
  def create_interface():
415
  """Create ChatGPT-style Gradio interface"""
416
 
417
  with gr.Blocks(
418
- theme=gr.themes.Base(
419
- primary_hue="yellow",
420
- secondary_hue="gray",
421
- neutral_hue="slate"
422
- ),
423
  css="""
424
  /* Dark theme styling */
425
  .gradio-container {
@@ -478,24 +472,6 @@ def create_interface():
478
  margin-bottom: 1rem;
479
  }
480
 
481
- .gradio-chatbot .message {
482
- background: rgba(45, 45, 45, 0.8) !important;
483
- border: 1px solid rgba(255, 193, 7, 0.1) !important;
484
- border-radius: 12px !important;
485
- margin: 0.5rem 0 !important;
486
- }
487
-
488
- .gradio-chatbot .message.user {
489
- background: rgba(255, 193, 7, 0.1) !important;
490
- border-color: rgba(255, 193, 7, 0.3) !important;
491
- margin-left: 20% !important;
492
- }
493
-
494
- .gradio-chatbot .message.bot {
495
- background: rgba(45, 45, 45, 0.9) !important;
496
- margin-right: 20% !important;
497
- }
498
-
499
  /* Input area */
500
  .input-area {
501
  background: rgba(45, 45, 45, 0.6);
@@ -515,11 +491,6 @@ def create_interface():
515
  transition: all 0.3s ease;
516
  }
517
 
518
- .upload-area:hover {
519
- background: rgba(255, 193, 7, 0.1);
520
- border-color: rgba(255, 193, 7, 0.5);
521
- }
522
-
523
  /* Buttons */
524
  .primary-btn {
525
  background: linear-gradient(135deg, #ffc107 0%, #ff8f00 100%) !important;
@@ -527,13 +498,6 @@ def create_interface():
527
  border: none !important;
528
  border-radius: 8px !important;
529
  font-weight: 600 !important;
530
- transition: all 0.3s ease !important;
531
- }
532
-
533
- .primary-btn:hover {
534
- background: linear-gradient(135deg, #ffcd38 0%, #ffa726 100%) !important;
535
- transform: translateY(-1px);
536
- box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3);
537
  }
538
 
539
  /* Text inputs */
@@ -544,17 +508,6 @@ def create_interface():
544
  border-radius: 8px !important;
545
  }
546
 
547
- .gradio-textbox input:focus, .gradio-textbox textarea:focus {
548
- border-color: #ffc107 !important;
549
- box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2) !important;
550
- }
551
-
552
- /* File component */
553
- .gradio-file {
554
- background: transparent !important;
555
- border: none !important;
556
- }
557
-
558
  /* Processing indicator */
559
  .processing-indicator {
560
  background: rgba(255, 193, 7, 0.1);
@@ -564,76 +517,40 @@ def create_interface():
564
  margin: 0.5rem 0;
565
  color: #ffc107;
566
  text-align: center;
567
- animation: pulse 2s infinite;
568
- }
569
-
570
- @keyframes pulse {
571
- 0%, 100% { opacity: 1; }
572
- 50% { opacity: 0.7; }
573
- }
574
-
575
- /* Hide labels for cleaner look */
576
- .gradio-textbox label,
577
- .gradio-file label {
578
- display: none !important;
579
- }
580
-
581
- /* Scrollbar styling */
582
- ::-webkit-scrollbar {
583
- width: 8px;
584
- }
585
-
586
- ::-webkit-scrollbar-track {
587
- background: rgba(45, 45, 45, 0.3);
588
- border-radius: 4px;
589
- }
590
-
591
- ::-webkit-scrollbar-thumb {
592
- background: rgba(255, 193, 7, 0.5);
593
- border-radius: 4px;
594
- }
595
-
596
- ::-webkit-scrollbar-thumb:hover {
597
- background: rgba(255, 193, 7, 0.7);
598
  }
599
  """,
600
  title="Agentic RAG Assistant"
601
  ) as iface:
602
 
603
  # Header
604
- with gr.Row(elem_classes=["header"]):
605
  with gr.Column():
606
  gr.HTML("""
607
- <div style="display: flex; align-items: center; gap: 1rem;">
608
- <div>
609
- <h1>🤖 Agentic RAG Assistant</h1>
610
- <p>Upload documents and ask questions - powered by Multi-Agent Architecture</p>
611
- </div>
612
  </div>
613
  """)
614
 
615
  # Main chat container
616
- with gr.Row(elem_classes=["chat-container"]):
617
  with gr.Column():
618
 
619
  # Chatbot
620
  chatbot = gr.Chatbot(
621
  value=[],
622
  height=500,
623
- show_copy_button=True,
624
- bubble_full_width=False,
625
- elem_classes=["gradio-chatbot"]
626
  )
627
 
628
  # Input area
629
- with gr.Column(elem_classes=["input-area"]):
630
 
631
- # File upload (initially hidden, shows when needed)
632
  file_upload = gr.File(
633
  file_count="multiple",
634
  file_types=[".pdf", ".pptx", ".csv", ".docx", ".txt", ".md"],
635
- elem_classes=["upload-area"],
636
- visible=True
637
  )
638
 
639
  # Processing status
@@ -643,27 +560,10 @@ def create_interface():
643
  with gr.Row():
644
  msg_input = gr.Textbox(
645
  placeholder="Upload documents above, then ask your questions here...",
646
- scale=5,
647
- max_lines=3,
648
- autofocus=True
649
- )
650
- send_btn = gr.Button(
651
- "Send",
652
- scale=1,
653
- elem_classes=["primary-btn"]
654
  )
655
-
656
- # Quick examples
657
- gr.Examples(
658
- examples=[
659
- "What are the main topics in the documents?",
660
- "Summarize the key findings",
661
- "What metrics are mentioned?",
662
- "What are the recommendations?"
663
- ],
664
- inputs=msg_input,
665
- elem_classes=["examples"]
666
- )
667
 
668
  # State to track document processing
669
  doc_processed = gr.State(False)
@@ -686,9 +586,9 @@ def create_interface():
686
 
687
  # Wait a moment for processing to complete
688
  import time
689
- time.sleep(2)
690
 
691
- success_html = f"""
692
  <div style="background: rgba(76, 175, 80, 0.1); border: 1px solid rgba(76, 175, 80, 0.3);
693
  border-radius: 8px; padding: 0.75rem; color: #4caf50; text-align: center;">
694
  ✅ Documents processed successfully! You can now ask questions.
@@ -712,15 +612,11 @@ def create_interface():
712
  if not message.strip():
713
  return history, message
714
 
715
- # Add user message
716
- history.append([message, None])
717
 
718
- # Generate response
719
- response = ""
720
- for token in coordinator_agent.handle_query(message, history):
721
- response += token
722
- history[-1][1] = response
723
- yield history, ""
724
 
725
  return history, ""
726
 
@@ -735,15 +631,13 @@ def create_interface():
735
  send_btn.click(
736
  respond,
737
  inputs=[msg_input, chatbot, doc_processed],
738
- outputs=[chatbot, msg_input],
739
- show_progress=True
740
  )
741
 
742
  msg_input.submit(
743
  respond,
744
  inputs=[msg_input, chatbot, doc_processed],
745
- outputs=[chatbot, msg_input],
746
- show_progress=True
747
  )
748
 
749
  return iface
 
13
  from langchain_community.embeddings import HuggingFaceEmbeddings
14
  from langchain_community.vectorstores import FAISS
15
  from langchain.docstore.document import Document
16
+ from langchain.chains import LLMChain, RetrievalQA, ConversationalRetrievalChain
17
+ from langchain.memory import ConversationBufferMemory
18
+ from langchain.prompts import PromptTemplate
19
+ from langchain_community.llms import HuggingFaceHub
20
 
21
  # Import document parsers
22
  import PyPDF2
 
34
  if not HF_TOKEN:
35
  raise ValueError("HuggingFace token not found in environment variables")
36
 
37
+ # Initialize HuggingFace LLM
38
+ llm = HuggingFaceHub(
39
+ repo_id="meta-llama/Llama-3.1-8B-Instruct",
40
+ huggingfacehub_api_token=HF_TOKEN,
41
+ model_kwargs={"temperature": 0.7, "max_length": 512}
42
+ )
43
 
44
  # Initialize embeddings
45
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
 
205
  self.message_bus.publish(response)
206
 
207
  class RetrievalAgent:
208
+ """Agent responsible for embedding and semantic retrieval using LangChain"""
209
 
210
  def __init__(self, message_bus: MessageBus):
211
  self.name = "RetrievalAgent"
212
  self.message_bus = message_bus
213
  self.message_bus.subscribe(self.name, self.handle_message)
214
  self.vector_store = None
215
+ self.retriever = None
216
+ self.qa_chain = None
217
+ self.conversation_chain = None
218
+ self.memory = ConversationBufferMemory(
219
+ memory_key="chat_history",
220
+ return_messages=True,
221
+ output_key="answer"
222
+ )
223
 
224
  def handle_message(self, message: MCPMessage):
225
  """Handle incoming MCP messages"""
226
  if message.type == "INGESTION_COMPLETE":
227
  self.create_vector_store(message)
228
  elif message.type == "RETRIEVAL_REQUEST":
229
+ self.process_query(message)
230
 
231
  def create_vector_store(self, message: MCPMessage):
232
+ """Create vector store and chains from processed documents"""
233
  documents = message.payload.get("documents", [])
234
 
235
  if documents:
236
  try:
237
  self.vector_store = FAISS.from_documents(documents, embeddings)
238
+ self.retriever = self.vector_store.as_retriever(
239
+ search_type="similarity",
240
+ search_kwargs={"k": 3}
241
+ )
242
+
243
+ # Create QA chain
244
+ self.qa_chain = RetrievalQA.from_chain_type(
245
+ llm=llm,
246
+ chain_type="stuff",
247
+ retriever=self.retriever,
248
+ return_source_documents=True,
249
+ verbose=True
250
+ )
251
+
252
+ # Create conversational chain
253
+ self.conversation_chain = ConversationalRetrievalChain.from_llm(
254
+ llm=llm,
255
+ retriever=self.retriever,
256
+ memory=self.memory,
257
+ return_source_documents=True,
258
+ verbose=True
259
+ )
260
+
261
+ logger.info(f"Vector store and chains created with {len(documents)} documents")
262
 
263
  # Notify completion
264
  response = MCPMessage(
 
272
  except Exception as e:
273
  logger.error(f"Error creating vector store: {e}")
274
 
275
+ def process_query(self, message: MCPMessage):
276
+ """Process query using conversational retrieval chain"""
277
  query = message.payload.get("query", "")
278
+ use_conversation = message.payload.get("use_conversation", True)
279
 
280
+ if not self.qa_chain or not query:
281
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  try:
284
+ if use_conversation and self.conversation_chain:
285
+ # Use conversational chain for context-aware responses
286
+ result = self.conversation_chain({"question": query})
287
+ answer = result["answer"]
288
+ source_docs = result.get("source_documents", [])
289
+ else:
290
+ # Use simple QA chain
291
+ result = self.qa_chain({"query": query})
292
+ answer = result["result"]
293
+ source_docs = result.get("source_documents", [])
294
+
295
+ # Format sources
296
+ sources = []
297
+ for doc in source_docs:
298
+ sources.append({
299
+ "content": doc.page_content[:200] + "...",
300
+ "source": doc.metadata.get("source", "Unknown")
301
+ })
302
 
 
303
  response = MCPMessage(
304
  sender=self.name,
305
  receiver="CoordinatorAgent",
306
+ msg_type="CHAIN_RESPONSE",
307
  trace_id=message.trace_id,
308
  payload={
309
  "query": query,
310
+ "answer": answer,
311
+ "sources": sources
312
  }
313
  )
314
  self.message_bus.publish(response)
315
 
316
  except Exception as e:
317
+ logger.error(f"Error processing query: {e}")
318
+ # Send error response
 
 
 
 
319
  response = MCPMessage(
320
  sender=self.name,
321
  receiver="CoordinatorAgent",
322
+ msg_type="CHAIN_RESPONSE",
323
  trace_id=message.trace_id,
324
+ payload={
325
+ "query": query,
326
+ "answer": f"Error processing query: {str(e)}",
327
+ "sources": []
328
+ }
329
  )
330
  self.message_bus.publish(response)
331
 
 
336
  self.name = "CoordinatorAgent"
337
  self.message_bus = message_bus
338
  self.message_bus.subscribe(self.name, self.handle_message)
 
339
  self.vector_store_ready = False
340
+ self.current_response = None
341
 
342
  def handle_message(self, message: MCPMessage):
343
  """Handle incoming MCP messages"""
344
  if message.type == "VECTORSTORE_READY":
345
  self.vector_store_ready = True
346
+ elif message.type == "CHAIN_RESPONSE":
347
+ self.current_response = message.payload
348
 
349
  def process_files(self, files):
350
  """Process uploaded files"""
 
364
 
365
  return f"Processing {len(files)} files: {', '.join([os.path.basename(fp) for fp in file_paths])}"
366
 
367
+ def handle_query(self, query: str):
368
+ """Handle user query using LangChain chains"""
369
  if not self.vector_store_ready:
370
+ return "Please upload and process documents first."
 
371
 
372
  # Send retrieval request
373
  message = MCPMessage(
374
  sender=self.name,
375
  receiver="RetrievalAgent",
376
  msg_type="RETRIEVAL_REQUEST",
377
+ payload={"query": query, "use_conversation": True}
378
  )
379
  self.message_bus.publish(message)
380
 
381
+ # Wait for response
382
  import time
383
+ timeout = 30 # seconds
384
  start_time = time.time()
385
 
386
+ while not self.current_response and (time.time() - start_time) < timeout:
387
  time.sleep(0.1)
388
 
389
+ if self.current_response:
390
+ response = self.current_response
391
+ self.current_response = None # Reset for next query
392
+
393
+ # Format response with sources
394
+ answer = response.get("answer", "No answer generated.")
395
+ sources = response.get("sources", [])
396
+
397
+ if sources:
398
+ source_text = "\n\n**Sources:**\n"
399
+ for i, source in enumerate(sources, 1):
400
+ source_text += f"{i}. {source['source']}: {source['content']}\n"
401
+ answer += source_text
402
+
403
+ return answer
404
  else:
405
+ return "Timeout: No response received from the system."
406
 
407
  # Initialize agents
408
  ingestion_agent = IngestionAgent(message_bus)
409
  retrieval_agent = RetrievalAgent(message_bus)
 
410
  coordinator_agent = CoordinatorAgent(message_bus)
411
 
412
  def create_interface():
413
  """Create ChatGPT-style Gradio interface"""
414
 
415
  with gr.Blocks(
416
+ theme=gr.themes.Base(),
 
 
 
 
417
  css="""
418
  /* Dark theme styling */
419
  .gradio-container {
 
472
  margin-bottom: 1rem;
473
  }
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  /* Input area */
476
  .input-area {
477
  background: rgba(45, 45, 45, 0.6);
 
491
  transition: all 0.3s ease;
492
  }
493
 
 
 
 
 
 
494
  /* Buttons */
495
  .primary-btn {
496
  background: linear-gradient(135deg, #ffc107 0%, #ff8f00 100%) !important;
 
498
  border: none !important;
499
  border-radius: 8px !important;
500
  font-weight: 600 !important;
 
 
 
 
 
 
 
501
  }
502
 
503
  /* Text inputs */
 
508
  border-radius: 8px !important;
509
  }
510
 
 
 
 
 
 
 
 
 
 
 
 
511
  /* Processing indicator */
512
  .processing-indicator {
513
  background: rgba(255, 193, 7, 0.1);
 
517
  margin: 0.5rem 0;
518
  color: #ffc107;
519
  text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  }
521
  """,
522
  title="Agentic RAG Assistant"
523
  ) as iface:
524
 
525
  # Header
526
+ with gr.Row():
527
  with gr.Column():
528
  gr.HTML("""
529
+ <div class="header">
530
+ <h1>🤖 Agentic RAG Assistant</h1>
531
+ <p>Upload documents and ask questions - powered by LangChain Multi-Agent Architecture</p>
 
 
532
  </div>
533
  """)
534
 
535
  # Main chat container
536
+ with gr.Row():
537
  with gr.Column():
538
 
539
  # Chatbot
540
  chatbot = gr.Chatbot(
541
  value=[],
542
  height=500,
543
+ show_copy_button=True
 
 
544
  )
545
 
546
  # Input area
547
+ with gr.Column():
548
 
549
+ # File upload
550
  file_upload = gr.File(
551
  file_count="multiple",
552
  file_types=[".pdf", ".pptx", ".csv", ".docx", ".txt", ".md"],
553
+ label="Upload Documents"
 
554
  )
555
 
556
  # Processing status
 
560
  with gr.Row():
561
  msg_input = gr.Textbox(
562
  placeholder="Upload documents above, then ask your questions here...",
563
+ label="Message",
564
+ scale=4
 
 
 
 
 
 
565
  )
566
+ send_btn = gr.Button("Send", scale=1, variant="primary")
 
 
 
 
 
 
 
 
 
 
 
567
 
568
  # State to track document processing
569
  doc_processed = gr.State(False)
 
586
 
587
  # Wait a moment for processing to complete
588
  import time
589
+ time.sleep(3)
590
 
591
+ success_html = """
592
  <div style="background: rgba(76, 175, 80, 0.1); border: 1px solid rgba(76, 175, 80, 0.3);
593
  border-radius: 8px; padding: 0.75rem; color: #4caf50; text-align: center;">
594
  ✅ Documents processed successfully! You can now ask questions.
 
612
  if not message.strip():
613
  return history, message
614
 
615
+ # Get response from coordinator
616
+ response = coordinator_agent.handle_query(message)
617
 
618
+ # Add to chat history
619
+ history.append([message, response])
 
 
 
 
620
 
621
  return history, ""
622
 
 
631
  send_btn.click(
632
  respond,
633
  inputs=[msg_input, chatbot, doc_processed],
634
+ outputs=[chatbot, msg_input]
 
635
  )
636
 
637
  msg_input.submit(
638
  respond,
639
  inputs=[msg_input, chatbot, doc_processed],
640
+ outputs=[chatbot, msg_input]
 
641
  )
642
 
643
  return iface