LamiaYT's picture
Initial commit with LlamaIndex-based agent
a42d6f7
raw
history blame
13.3 kB
# app.py
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core.agent import ReActAgent
from llama_index.core.tools import FunctionTool
from transformers import AutoTokenizer
import os
import gradio as gr
import requests
import pandas as pd
import traceback
# Import real tool dependencies
try:
from duckduckgo_search import DDGS
except ImportError:
print("Warning: duckduckgo_search not installed. Web search will be limited.")
DDGS = None
try:
from sympy import sympify
from sympy.core.sympify import SympifyError
except ImportError:
print("Warning: sympy not installed. Math calculator will be limited.")
sympify = None
SympifyError = Exception
# --- Constants ---
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
# --- Advanced Agent Definition ---
class SmartAgent:
def __init__(self):
print("Initializing Local LLM Agent...")
# Initialize Zephyr-7B model
self.llm = HuggingFaceLLM(
model_name="HuggingFaceH4/zephyr-7b-beta",
tokenizer_name="HuggingFaceH4/zephyr-7b-beta",
context_window=2048,
max_new_tokens=256,
generate_kwargs={"temperature": 0.7, "do_sample": True},
device_map="auto"
)
# Define tools with real implementations
self.tools = [
FunctionTool.from_defaults(
fn=self.web_search,
name="web_search",
description="Searches the web for current information using DuckDuckGo when questions require up-to-date knowledge"
),
FunctionTool.from_defaults(
fn=self.math_calculator,
name="math_calculator",
description="Performs mathematical calculations and symbolic math using SymPy when questions involve numbers or equations"
)
]
# Create ReAct agent with tools
self.agent = ReActAgent.from_tools(
tools=self.tools,
llm=self.llm,
verbose=True
)
print("Local LLM Agent initialized successfully.")
def web_search(self, query: str) -> str:
"""Real web search using DuckDuckGo"""
print(f"Web search triggered for: {query[:50]}...")
if not DDGS:
return "Web search unavailable - duckduckgo_search not installed"
try:
with DDGS() as ddgs:
results = list(ddgs.text(query, max_results=3))
if results:
formatted_results = []
for i, r in enumerate(results, 1):
title = r.get('title', 'No title')
body = r.get('body', 'No description')[:200]
url = r.get('href', '')
formatted_results.append(f"{i}. {title}\n{body}...\nSource: {url}")
return "\n\n".join(formatted_results)
else:
return "No search results found for the query."
except Exception as e:
print(f"Web search error: {e}")
return f"Error during web search: {str(e)}"
def math_calculator(self, expression: str) -> str:
"""Safe math evaluation using SymPy"""
print(f"Math calculation triggered for: {expression}")
if not sympify:
# Fallback to basic eval with safety checks
try:
# Only allow basic math operations
allowed_chars = set('0123456789+-*/().^ ')
if not all(c in allowed_chars for c in expression.replace(' ', '')):
return "Error: Only basic math operations are allowed"
result = eval(expression.replace('^', '**'))
return str(result)
except Exception as e:
return f"Error: Could not evaluate the mathematical expression - {str(e)}"
try:
# Use SymPy for safe evaluation
result = sympify(expression).evalf()
return str(result)
except SympifyError as e:
return f"Error: Could not parse the mathematical expression - {str(e)}"
except Exception as e:
return f"Error: Calculation failed - {str(e)}"
def __call__(self, question: str) -> str:
print(f"Processing question (first 50 chars): {question[:50]}...")
try:
response = self.agent.query(question)
return str(response)
except Exception as e:
print(f"Agent error: {str(e)}")
print(f"Full traceback: {traceback.format_exc()}")
return f"Error processing question: {str(e)}"
# --- Submission Logic ---
def run_and_submit_all(profile: gr.OAuthProfile | None):
"""
Fetches all questions, runs the agent on them, submits all answers,
and displays the results.
"""
space_id = os.getenv("SPACE_ID")
if profile:
username = f"{profile.username}"
print(f"User logged in: {username}")
else:
print("User not logged in.")
return "Please Login to Hugging Face with the button.", None
api_url = DEFAULT_API_URL
questions_url = f"{api_url}/questions"
submit_url = f"{api_url}/submit"
# Instantiate Agent
try:
agent = SmartAgent()
except Exception as e:
print(f"Error instantiating agent: {e}")
print(f"Full traceback: {traceback.format_exc()}")
return f"Error initializing agent: {e}", None
agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
print(f"Agent code URL: {agent_code}")
# Fetch Questions
print(f"Fetching questions from: {questions_url}")
try:
response = requests.get(questions_url, timeout=15)
response.raise_for_status()
questions_data = response.json()
if not questions_data:
print("Fetched questions list is empty.")
return "Fetched questions list is empty or invalid format.", None
print(f"Fetched {len(questions_data)} questions.")
except requests.exceptions.RequestException as e:
print(f"Error fetching questions: {e}")
return f"Error fetching questions: {e}", None
except requests.exceptions.JSONDecodeError as e:
print(f"Error decoding JSON response from questions endpoint: {e}")
return f"Error decoding server response for questions: {e}", None
except Exception as e:
print(f"An unexpected error occurred fetching questions: {e}")
return f"An unexpected error occurred fetching questions: {e}", None
# Run Agent on all questions
results_log = []
answers_payload = []
print(f"Running agent on {len(questions_data)} questions...")
for i, item in enumerate(questions_data, 1):
task_id = item.get("task_id")
question_text = item.get("question")
if not task_id or question_text is None:
print(f"Skipping item with missing task_id or question: {item}")
continue
print(f"Processing question {i}/{len(questions_data)}: {task_id}")
try:
submitted_answer = agent(question_text)
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
results_log.append({
"Task ID": task_id,
"Question": question_text[:100] + "..." if len(question_text) > 100 else question_text,
"Submitted Answer": submitted_answer[:200] + "..." if len(submitted_answer) > 200 else submitted_answer
})
print(f"โœ… Completed question {i}: {task_id}")
except Exception as e:
print(f"โŒ Error running agent on task {task_id}: {e}")
error_answer = f"AGENT ERROR: {str(e)}"
answers_payload.append({"task_id": task_id, "submitted_answer": error_answer})
results_log.append({
"Task ID": task_id,
"Question": question_text[:100] + "..." if len(question_text) > 100 else question_text,
"Submitted Answer": error_answer
})
if not answers_payload:
print("Agent did not produce any answers to submit.")
return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
# Prepare submission
submission_data = {
"username": username.strip(),
"agent_code": agent_code,
"answers": answers_payload
}
status_update = f"Agent finished processing. Submitting {len(answers_payload)} answers for user '{username}'..."
print(status_update)
# Submit answers
print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
try:
response = requests.post(submit_url, json=submission_data, timeout=60)
response.raise_for_status()
result_data = response.json()
final_status = (
f"๐ŸŽ‰ Submission Successful!\n\n"
f"User: {result_data.get('username')}\n"
f"Overall Score: {result_data.get('score', 'N/A')}% "
f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
f"Message: {result_data.get('message', 'No message received.')}"
)
print("โœ… Submission successful!")
results_df = pd.DataFrame(results_log)
return final_status, results_df
except requests.exceptions.HTTPError as e:
error_detail = f"Server responded with status {e.response.status_code}."
try:
error_json = e.response.json()
error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
except requests.exceptions.JSONDecodeError:
error_detail += f" Response: {e.response.text[:500]}"
status_message = f"โŒ Submission Failed: {error_detail}"
print(status_message)
results_df = pd.DataFrame(results_log)
return status_message, results_df
except requests.exceptions.Timeout:
status_message = "โŒ Submission Failed: The request timed out."
print(status_message)
results_df = pd.DataFrame(results_log)
return status_message, results_df
except requests.exceptions.RequestException as e:
status_message = f"โŒ Submission Failed: Network error - {e}"
print(status_message)
results_df = pd.DataFrame(results_log)
return status_message, results_df
except Exception as e:
status_message = f"โŒ An unexpected error occurred during submission: {e}"
print(status_message)
results_df = pd.DataFrame(results_log)
return status_message, results_df
# --- Gradio UI ---
with gr.Blocks(title="Local LLM Agent Evaluation") as demo:
gr.Markdown("# ๐Ÿค– Local LLM Agent Evaluation Runner")
gr.Markdown(
"""
**Instructions:**
1. ๐Ÿ” Log in to your Hugging Face account using the button below
2. ๐Ÿš€ Click 'Run Evaluation & Submit All Answers'
3. โณ Wait for the local LLM (Zephyr-7B) to process all questions
4. ๐Ÿ“Š View your results and submission status
**Features:**
- ๐Ÿ” Real web search using DuckDuckGo
- ๐Ÿงฎ Advanced math calculations with SymPy
- ๐Ÿง  Powered by HuggingFace Zephyr-7B model
"""
)
with gr.Row():
gr.LoginButton()
with gr.Row():
run_button = gr.Button(
"๐Ÿš€ Run Evaluation & Submit All Answers",
variant="primary",
size="lg"
)
status_output = gr.Textbox(
label="๐Ÿ“‹ Run Status / Submission Result",
lines=8,
interactive=False,
placeholder="Click the button above to start the evaluation..."
)
results_table = gr.DataFrame(
label="๐Ÿ“Š Questions and Agent Answers",
wrap=True,
interactive=False
)
# Wire up the button
run_button.click(
fn=run_and_submit_all,
outputs=[status_output, results_table]
)
if __name__ == "__main__":
print("\n" + "="*60)
print("๐Ÿš€ Application Startup at", pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S"))
print("="*60)
space_host_startup = os.getenv("SPACE_HOST")
space_id_startup = os.getenv("SPACE_ID")
if space_host_startup:
print(f"โœ… SPACE_HOST found: {space_host_startup}")
print(f" Runtime URL should be: https://{space_host_startup}")
else:
print("โ„น๏ธ SPACE_HOST environment variable not found (running locally?).")
if space_id_startup:
print(f"โœ… SPACE_ID found: {space_id_startup}")
print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
else:
print("โ„น๏ธ SPACE_ID environment variable not found (running locally?).")
print("-" * 60)
print("๐ŸŽฏ Launching Gradio Interface for Local LLM Agent Evaluation...")
# Launch without share=True for Hugging Face Spaces
demo.launch(
server_name="0.0.0.0",
server_port=7860,
show_error=True
)