Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -8,7 +8,7 @@ import json
|
|
8 |
import asyncio
|
9 |
from dataclasses import dataclass, asdict
|
10 |
import logging
|
11 |
-
import sys
|
12 |
|
13 |
# Document processing imports
|
14 |
import PyPDF2
|
@@ -29,7 +29,7 @@ logging.basicConfig(level=logging.INFO)
|
|
29 |
logger = logging.getLogger(__name__)
|
30 |
|
31 |
# --- Get HF token from environment and perform a crucial check ---
|
32 |
-
HF_TOKEN = os.getenv('
|
33 |
|
34 |
if HF_TOKEN is None:
|
35 |
logger.error("FATAL ERROR: HuggingFace token (HF_TOKEN) environment variable is not set.")
|
@@ -70,7 +70,7 @@ class MCPCommunicator:
|
|
70 |
if message.receiver == agent_name:
|
71 |
return message
|
72 |
# Re-queue if not for this agent
|
73 |
-
await self.
|
74 |
|
75 |
# Global MCP instance
|
76 |
mcp = MCPCommunicator()
|
@@ -171,7 +171,7 @@ class IngestionAgent(BaseAgent):
|
|
171 |
"""Process uploaded documents and return chunked documents"""
|
172 |
all_documents = []
|
173 |
|
174 |
-
for file_path in files:
|
175 |
file_ext = os.path.splitext(file_path)[1].lower()
|
176 |
filename = os.path.basename(file_path)
|
177 |
|
@@ -256,10 +256,9 @@ class RetrievalAgent(BaseAgent):
|
|
256 |
class LLMResponseAgent(BaseAgent):
|
257 |
def __init__(self):
|
258 |
super().__init__("LLMResponseAgent")
|
259 |
-
# Use the global HF_TOKEN which is validated at script start
|
260 |
self.client = InferenceClient(
|
261 |
model="meta-llama/Llama-3.1-8B-Instruct",
|
262 |
-
token=HF_TOKEN
|
263 |
)
|
264 |
|
265 |
def format_prompt_for_conversational(self, query: str, context_chunks: List[Dict]) -> str:
|
@@ -272,8 +271,6 @@ class LLMResponseAgent(BaseAgent):
|
|
272 |
for chunk in context_chunks
|
273 |
])
|
274 |
|
275 |
-
# We are putting the RAG prompt into the 'user' input for the conversational model.
|
276 |
-
# This is a common way to use a conversational model for RAG if text_generation isn't available.
|
277 |
prompt_as_user_input = f"""Based on the following context from uploaded documents, please answer the user's question.
|
278 |
|
279 |
Context:
|
@@ -283,32 +280,22 @@ Question: {query}
|
|
283 |
|
284 |
Please provide a comprehensive answer based on the context above. If the context doesn't contain enough information to fully answer the question, please mention what information is available and what might be missing.
|
285 |
|
286 |
-
Answer:"""
|
287 |
return prompt_as_user_input
|
288 |
|
289 |
async def generate_response(self, query: str, context_chunks: List[Dict], trace_id: str) -> str:
|
290 |
"""Generate response using LLM via the conversational task."""
|
291 |
try:
|
292 |
-
# Format the RAG prompt as the user's input for the conversational model
|
293 |
formatted_input = self.format_prompt_for_conversational(query, context_chunks)
|
294 |
|
295 |
-
# Use the conversational task
|
296 |
response = self.client.conversational(
|
297 |
-
inputs=formatted_input,
|
298 |
-
# No past_user_inputs or generated_responses are provided initially
|
299 |
-
# to keep it stateless per query, akin to text_generation.
|
300 |
parameters={
|
301 |
"temperature": 0.7,
|
302 |
"max_new_tokens": 512,
|
303 |
-
# Add other parameters if needed, e.g., do_sample, top_p, top_k
|
304 |
-
# "do_sample": True,
|
305 |
-
# "top_p": 0.95,
|
306 |
-
# "top_k": 50,
|
307 |
}
|
308 |
)
|
309 |
|
310 |
-
# The conversational response has a list of generated responses.
|
311 |
-
# We assume the first one is the primary answer.
|
312 |
if response.generated_responses:
|
313 |
return response.generated_responses[0]
|
314 |
else:
|
@@ -325,7 +312,7 @@ class CoordinatorAgent(BaseAgent):
|
|
325 |
super().__init__("CoordinatorAgent")
|
326 |
self.ingestion_agent = IngestionAgent()
|
327 |
self.retrieval_agent = RetrievalAgent()
|
328 |
-
self.llm_agent = LLMResponseAgent()
|
329 |
self.documents_processed = False
|
330 |
|
331 |
async def process_documents(self, files: List[str]) -> str:
|
@@ -333,7 +320,6 @@ class CoordinatorAgent(BaseAgent):
|
|
333 |
trace_id = str(uuid.uuid4())
|
334 |
|
335 |
try:
|
336 |
-
# Step 1: Ingestion
|
337 |
await self.send_mcp_message(
|
338 |
"IngestionAgent",
|
339 |
"DOCUMENT_INGESTION_REQUEST",
|
@@ -350,7 +336,6 @@ class CoordinatorAgent(BaseAgent):
|
|
350 |
trace_id
|
351 |
)
|
352 |
|
353 |
-
# Step 2: Create vector store
|
354 |
await self.retrieval_agent.create_vector_store(documents, trace_id)
|
355 |
|
356 |
self.documents_processed = True
|
@@ -369,7 +354,6 @@ class CoordinatorAgent(BaseAgent):
|
|
369 |
trace_id = str(uuid.uuid4())
|
370 |
|
371 |
try:
|
372 |
-
# Step 1: Retrieval
|
373 |
await self.send_mcp_message(
|
374 |
"RetrievalAgent",
|
375 |
"RETRIEVAL_REQUEST",
|
@@ -377,9 +361,8 @@ class CoordinatorAgent(BaseAgent):
|
|
377 |
trace_id
|
378 |
)
|
379 |
|
380 |
-
context_chunks = await self.
|
381 |
|
382 |
-
# Step 2: LLM Response
|
383 |
await self.send_mcp_message(
|
384 |
"LLMResponseAgent",
|
385 |
"LLM_GENERATION_REQUEST",
|
@@ -399,31 +382,19 @@ class CoordinatorAgent(BaseAgent):
|
|
399 |
coordinator = CoordinatorAgent()
|
400 |
|
401 |
async def process_files(files):
|
402 |
-
"""Process uploaded files"""
|
403 |
if not files:
|
404 |
return "❌ Please upload at least one file."
|
405 |
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
temp_path = os.path.join(temp_dir, unique_filename)
|
411 |
-
try:
|
412 |
-
file_content = file.read()
|
413 |
-
with open(temp_path, 'wb') as f:
|
414 |
-
f.write(file_content)
|
415 |
-
file_paths.append(temp_path)
|
416 |
-
except Exception as e:
|
417 |
-
logger.error(f"Error saving uploaded file {file.name}: {e}")
|
418 |
-
return f"❌ Error saving uploaded file {file.name}: {e}"
|
419 |
|
420 |
-
result = await coordinator.process_documents(
|
421 |
|
422 |
-
|
423 |
-
|
424 |
-
os.remove(path)
|
425 |
-
except Exception as e:
|
426 |
-
logger.warning(f"Could not remove temporary file {path}: {e}")
|
427 |
|
428 |
return result
|
429 |
|
@@ -629,6 +600,8 @@ def create_interface():
|
|
629 |
label="Choose Files",
|
630 |
file_count="multiple",
|
631 |
file_types=[".pdf", ".docx", ".pptx", ".csv", ".txt", ".md"],
|
|
|
|
|
632 |
elem_classes=["file-upload"]
|
633 |
)
|
634 |
|
|
|
8 |
import asyncio
|
9 |
from dataclasses import dataclass, asdict
|
10 |
import logging
|
11 |
+
import sys
|
12 |
|
13 |
# Document processing imports
|
14 |
import PyPDF2
|
|
|
29 |
logger = logging.getLogger(__name__)
|
30 |
|
31 |
# --- Get HF token from environment and perform a crucial check ---
|
32 |
+
HF_TOKEN = os.getenv('HF_TOKEN')
|
33 |
|
34 |
if HF_TOKEN is None:
|
35 |
logger.error("FATAL ERROR: HuggingFace token (HF_TOKEN) environment variable is not set.")
|
|
|
70 |
if message.receiver == agent_name:
|
71 |
return message
|
72 |
# Re-queue if not for this agent
|
73 |
+
await self.mcp.put(message) # Corrected: Use mcp.put instead of message_queue.put directly
|
74 |
|
75 |
# Global MCP instance
|
76 |
mcp = MCPCommunicator()
|
|
|
171 |
"""Process uploaded documents and return chunked documents"""
|
172 |
all_documents = []
|
173 |
|
174 |
+
for file_path in files: # file_path is already the path to the temporary file
|
175 |
file_ext = os.path.splitext(file_path)[1].lower()
|
176 |
filename = os.path.basename(file_path)
|
177 |
|
|
|
256 |
class LLMResponseAgent(BaseAgent):
|
257 |
def __init__(self):
|
258 |
super().__init__("LLMResponseAgent")
|
|
|
259 |
self.client = InferenceClient(
|
260 |
model="meta-llama/Llama-3.1-8B-Instruct",
|
261 |
+
token=HF_TOKEN
|
262 |
)
|
263 |
|
264 |
def format_prompt_for_conversational(self, query: str, context_chunks: List[Dict]) -> str:
|
|
|
271 |
for chunk in context_chunks
|
272 |
])
|
273 |
|
|
|
|
|
274 |
prompt_as_user_input = f"""Based on the following context from uploaded documents, please answer the user's question.
|
275 |
|
276 |
Context:
|
|
|
280 |
|
281 |
Please provide a comprehensive answer based on the context above. If the context doesn't contain enough information to fully answer the question, please mention what information is available and what might be missing.
|
282 |
|
283 |
+
Answer:"""
|
284 |
return prompt_as_user_input
|
285 |
|
286 |
async def generate_response(self, query: str, context_chunks: List[Dict], trace_id: str) -> str:
|
287 |
"""Generate response using LLM via the conversational task."""
|
288 |
try:
|
|
|
289 |
formatted_input = self.format_prompt_for_conversational(query, context_chunks)
|
290 |
|
|
|
291 |
response = self.client.conversational(
|
292 |
+
inputs=formatted_input,
|
|
|
|
|
293 |
parameters={
|
294 |
"temperature": 0.7,
|
295 |
"max_new_tokens": 512,
|
|
|
|
|
|
|
|
|
296 |
}
|
297 |
)
|
298 |
|
|
|
|
|
299 |
if response.generated_responses:
|
300 |
return response.generated_responses[0]
|
301 |
else:
|
|
|
312 |
super().__init__("CoordinatorAgent")
|
313 |
self.ingestion_agent = IngestionAgent()
|
314 |
self.retrieval_agent = RetrievalAgent()
|
315 |
+
self.llm_agent = LLMResponseAgent()
|
316 |
self.documents_processed = False
|
317 |
|
318 |
async def process_documents(self, files: List[str]) -> str:
|
|
|
320 |
trace_id = str(uuid.uuid4())
|
321 |
|
322 |
try:
|
|
|
323 |
await self.send_mcp_message(
|
324 |
"IngestionAgent",
|
325 |
"DOCUMENT_INGESTION_REQUEST",
|
|
|
336 |
trace_id
|
337 |
)
|
338 |
|
|
|
339 |
await self.retrieval_agent.create_vector_store(documents, trace_id)
|
340 |
|
341 |
self.documents_processed = True
|
|
|
354 |
trace_id = str(uuid.uuid4())
|
355 |
|
356 |
try:
|
|
|
357 |
await self.send_mcp_message(
|
358 |
"RetrievalAgent",
|
359 |
"RETRIEVAL_REQUEST",
|
|
|
361 |
trace_id
|
362 |
)
|
363 |
|
364 |
+
context_chunks = await self.retrierieval_agent.retrieve_relevant_chunks(query, k=5, trace_id=trace_id)
|
365 |
|
|
|
366 |
await self.send_mcp_message(
|
367 |
"LLMResponseAgent",
|
368 |
"LLM_GENERATION_REQUEST",
|
|
|
382 |
coordinator = CoordinatorAgent()
|
383 |
|
384 |
async def process_files(files):
|
385 |
+
"""Process uploaded files (already temporary file paths from Gradio)"""
|
386 |
if not files:
|
387 |
return "❌ Please upload at least one file."
|
388 |
|
389 |
+
# Gradio's gr.File component with default type="filepath" already provides
|
390 |
+
# the temporary file paths. We just need to pass them to the ingestion agent.
|
391 |
+
# The 'files' variable here is already a list of strings (filepaths).
|
392 |
+
file_paths_for_ingestion = [file.name for file in files] # Extract the actual path string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
|
394 |
+
result = await coordinator.process_documents(file_paths_for_ingestion)
|
395 |
|
396 |
+
# Cleanup is handled by Gradio's tmp directory cleanup, but explicit removal is also fine.
|
397 |
+
# No need to manually create temp files or read content.
|
|
|
|
|
|
|
398 |
|
399 |
return result
|
400 |
|
|
|
600 |
label="Choose Files",
|
601 |
file_count="multiple",
|
602 |
file_types=[".pdf", ".docx", ".pptx", ".csv", ".txt", ".md"],
|
603 |
+
# type="filepath" is the default, but explicitly setting it helps clarify
|
604 |
+
type="filepath",
|
605 |
elem_classes=["file-upload"]
|
606 |
)
|
607 |
|