AgileAndy commited on
Commit
33bdd46
·
1 Parent(s): 81917a3

Enhanced agent with web search, Wikipedia, and multi-step reasoning capabilities

Browse files
Files changed (7) hide show
  1. .python-version +1 -0
  2. app.py +142 -7
  3. main.py +6 -0
  4. pyproject.toml +16 -0
  5. requirements.txt +8 -1
  6. test_agent.py +138 -0
  7. uv.lock +0 -0
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12.4
app.py CHANGED
@@ -3,21 +3,156 @@ import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
 
 
 
 
 
 
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
11
- # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
  class BasicAgent:
14
  def __init__(self):
15
- print("BasicAgent initialized.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
 
 
 
 
 
21
 
22
  def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ import re
7
+ import wikipedia
8
+ from duckduckgo_search import DDGS
9
+ from urllib.parse import urlparse
10
+ import json
11
+ from datetime import datetime
12
+ from bs4 import BeautifulSoup
13
 
14
  # (Keep Constants as is)
15
  # --- Constants ---
16
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
17
 
18
+ # --- Enhanced Agent Definition ---
 
19
  class BasicAgent:
20
  def __init__(self):
21
+ print("Enhanced BasicAgent initialized with tools.")
22
+ self.ddgs = DDGS()
23
+
24
+ def search_web(self, query, max_results=3):
25
+ """Search the web using DuckDuckGo"""
26
+ try:
27
+ results = list(self.ddgs.text(query, max_results=max_results))
28
+ return [{"title": r["title"], "body": r["body"], "href": r["href"]} for r in results]
29
+ except Exception as e:
30
+ print(f"Web search error: {e}")
31
+ return []
32
+
33
+ def search_wikipedia(self, query):
34
+ """Search Wikipedia for information"""
35
+ try:
36
+ # Search for pages
37
+ search_results = wikipedia.search(query, results=3)
38
+ if not search_results:
39
+ return None
40
+
41
+ # Get the first result
42
+ page = wikipedia.page(search_results[0])
43
+ return {
44
+ "title": page.title,
45
+ "summary": wikipedia.summary(search_results[0], sentences=3),
46
+ "content": page.content[:2000], # First 2000 chars
47
+ "url": page.url
48
+ }
49
+ except Exception as e:
50
+ print(f"Wikipedia search error: {e}")
51
+ return None
52
+
53
+ def extract_numbers(self, text):
54
+ """Extract numbers from text"""
55
+ numbers = re.findall(r'\d+\.?\d*', text)
56
+ return [float(n) if '.' in n else int(n) for n in numbers]
57
+
58
+ def extract_years(self, text):
59
+ """Extract years from text (4-digit numbers between 1000-2100)"""
60
+ years = re.findall(r'\b(1[0-9]{3}|20[0-9]{2}|2100)\b', text)
61
+ return [int(year) for year in years]
62
+
63
+ def calculate_basic_math(self, expression):
64
+ """Safely evaluate basic math expressions"""
65
+ try:
66
+ # Only allow basic math operations for safety
67
+ allowed_chars = set('0123456789+-*/.() ')
68
+ if all(c in allowed_chars for c in expression):
69
+ return eval(expression)
70
+ except:
71
+ pass
72
+ return None
73
+
74
+ def process_question(self, question):
75
+ """Main reasoning engine"""
76
+ question_lower = question.lower()
77
+
78
+ # Check if it's a math question
79
+ if any(word in question_lower for word in ['calculate', 'add', 'subtract', 'multiply', 'divide', '+', '-', '*', '/']):
80
+ numbers = self.extract_numbers(question)
81
+ if len(numbers) >= 2:
82
+ if '+' in question or 'add' in question_lower:
83
+ return str(sum(numbers))
84
+ elif '-' in question or 'subtract' in question_lower:
85
+ return str(numbers[0] - numbers[1])
86
+ elif '*' in question or 'multiply' in question_lower:
87
+ return str(numbers[0] * numbers[1])
88
+ elif '/' in question or 'divide' in question_lower:
89
+ if numbers[1] != 0:
90
+ return str(numbers[0] / numbers[1])
91
+
92
+ # Check if it's asking for a year or date
93
+ if any(word in question_lower for word in ['year', 'when', 'date']):
94
+ # Search for the answer
95
+ search_results = self.search_web(question, max_results=2)
96
+ wiki_result = self.search_wikipedia(question)
97
+
98
+ # Extract years from results
99
+ all_text = question
100
+ if search_results:
101
+ all_text += " " + " ".join([r["body"] for r in search_results])
102
+ if wiki_result:
103
+ all_text += " " + wiki_result["summary"]
104
+
105
+ years = self.extract_years(all_text)
106
+ if years:
107
+ # Return the most frequently mentioned year, or the first one
108
+ return str(max(set(years), key=years.count))
109
+
110
+ # Check if it's asking for a specific fact or entity
111
+ if any(word in question_lower for word in ['what', 'who', 'where', 'which', 'how many']):
112
+ # Try Wikipedia first for factual questions
113
+ wiki_result = self.search_wikipedia(question)
114
+ if wiki_result:
115
+ # Look for numbers in the summary that might be the answer
116
+ numbers = self.extract_numbers(wiki_result["summary"])
117
+ if numbers and 'how many' in question_lower:
118
+ return str(numbers[0])
119
+
120
+ # For other questions, try to extract a short answer
121
+ summary = wiki_result["summary"]
122
+ sentences = summary.split('.')
123
+ if sentences:
124
+ return sentences[0].strip()
125
+
126
+ # Fall back to web search
127
+ search_results = self.search_web(question, max_results=3)
128
+ if search_results:
129
+ # Try to find a concise answer in the search results
130
+ for result in search_results:
131
+ body = result["body"]
132
+ if len(body) < 200: # Prefer shorter, more direct answers
133
+ return body.strip()
134
+
135
+ # Return first result if no short answer found
136
+ return search_results[0]["body"][:200].strip()
137
+
138
+ # If no specific pattern matches, try a general web search
139
+ search_results = self.search_web(question, max_results=2)
140
+ if search_results:
141
+ return search_results[0]["body"][:100].strip()
142
+
143
+ # Fallback
144
+ return "Unable to determine answer"
145
+
146
  def __call__(self, question: str) -> str:
147
+ print(f"Agent processing question: {question[:100]}...")
148
+
149
+ try:
150
+ answer = self.process_question(question)
151
+ print(f"Agent answer: {answer}")
152
+ return answer
153
+ except Exception as e:
154
+ print(f"Error processing question: {e}")
155
+ return "Error processing question"
156
 
157
  def run_and_submit_all( profile: gr.OAuthProfile | None):
158
  """
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def main():
2
+ print("Hello from final-assignment-template!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
pyproject.toml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "final-assignment-template"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ requires-python = ">=3.12.4"
6
+ dependencies = [
7
+ "beautifulsoup4>=4.13.4",
8
+ "duckduckgo-search>=8.1.1",
9
+ "gradio[oauth]>=5.36.2",
10
+ "pillow>=11.3.0",
11
+ "python-dateutil>=2.9.0.post0",
12
+ "requests>=2.32.4",
13
+ "torch>=2.7.1",
14
+ "transformers>=4.53.2",
15
+ "wikipedia>=1.4.0",
16
+ ]
requirements.txt CHANGED
@@ -1,2 +1,9 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
1
  gradio
2
+ requests
3
+ transformers
4
+ torch
5
+ pillow
6
+ wikipedia
7
+ duckduckgo-search
8
+ beautifulsoup4
9
+ python-dateutil
test_agent.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import wikipedia
3
+ from duckduckgo_search import DDGS
4
+
5
+ class BasicAgent:
6
+ def __init__(self):
7
+ print("Enhanced BasicAgent initialized with tools.")
8
+ self.ddgs = DDGS()
9
+
10
+ def search_web(self, query, max_results=3):
11
+ """Search the web using DuckDuckGo"""
12
+ try:
13
+ results = list(self.ddgs.text(query, max_results=max_results))
14
+ return [{"title": r["title"], "body": r["body"], "href": r["href"]} for r in results]
15
+ except Exception as e:
16
+ print(f"Web search error: {e}")
17
+ return []
18
+
19
+ def search_wikipedia(self, query):
20
+ """Search Wikipedia for information"""
21
+ try:
22
+ search_results = wikipedia.search(query, results=3)
23
+ if not search_results:
24
+ return None
25
+
26
+ page = wikipedia.page(search_results[0])
27
+ return {
28
+ "title": page.title,
29
+ "summary": wikipedia.summary(search_results[0], sentences=3),
30
+ "content": page.content[:2000],
31
+ "url": page.url
32
+ }
33
+ except Exception as e:
34
+ print(f"Wikipedia search error: {e}")
35
+ return None
36
+
37
+ def extract_numbers(self, text):
38
+ """Extract numbers from text"""
39
+ numbers = re.findall(r'\d+\.?\d*', text)
40
+ return [float(n) if '.' in n else int(n) for n in numbers]
41
+
42
+ def extract_years(self, text):
43
+ """Extract years from text (4-digit numbers between 1000-2100)"""
44
+ years = re.findall(r'\b(1[0-9]{3}|20[0-9]{2}|2100)\b', text)
45
+ return [int(year) for year in years]
46
+
47
+ def process_question(self, question):
48
+ """Main reasoning engine"""
49
+ question_lower = question.lower()
50
+
51
+ # Check if it's a math question
52
+ if any(word in question_lower for word in ['calculate', 'add', 'subtract', 'multiply', 'divide', '+', '-', '*', '/']):
53
+ numbers = self.extract_numbers(question)
54
+ if len(numbers) >= 2:
55
+ if '+' in question or 'add' in question_lower:
56
+ return str(sum(numbers))
57
+ elif '-' in question or 'subtract' in question_lower:
58
+ return str(numbers[0] - numbers[1])
59
+ elif '*' in question or 'multiply' in question_lower:
60
+ return str(numbers[0] * numbers[1])
61
+ elif '/' in question or 'divide' in question_lower:
62
+ if numbers[1] != 0:
63
+ return str(numbers[0] / numbers[1])
64
+
65
+ # Check if it's asking for a year or date
66
+ if any(word in question_lower for word in ['year', 'when', 'date']):
67
+ search_results = self.search_web(question, max_results=2)
68
+ wiki_result = self.search_wikipedia(question)
69
+
70
+ all_text = question
71
+ if search_results:
72
+ all_text += " " + " ".join([r["body"] for r in search_results])
73
+ if wiki_result:
74
+ all_text += " " + wiki_result["summary"]
75
+
76
+ years = self.extract_years(all_text)
77
+ if years:
78
+ return str(max(set(years), key=years.count))
79
+
80
+ # Check if it's asking for a specific fact or entity
81
+ if any(word in question_lower for word in ['what', 'who', 'where', 'which', 'how many']):
82
+ wiki_result = self.search_wikipedia(question)
83
+ if wiki_result:
84
+ numbers = self.extract_numbers(wiki_result["summary"])
85
+ if numbers and 'how many' in question_lower:
86
+ return str(numbers[0])
87
+
88
+ summary = wiki_result["summary"]
89
+ sentences = summary.split('.')
90
+ if sentences:
91
+ return sentences[0].strip()
92
+
93
+ search_results = self.search_web(question, max_results=3)
94
+ if search_results:
95
+ for result in search_results:
96
+ body = result["body"]
97
+ if len(body) < 200:
98
+ return body.strip()
99
+
100
+ return search_results[0]["body"][:200].strip()
101
+
102
+ search_results = self.search_web(question, max_results=2)
103
+ if search_results:
104
+ return search_results[0]["body"][:100].strip()
105
+
106
+ return "Unable to determine answer"
107
+
108
+ def __call__(self, question: str) -> str:
109
+ print(f"Agent processing question: {question[:100]}...")
110
+
111
+ try:
112
+ answer = self.process_question(question)
113
+ print(f"Agent answer: {answer}")
114
+ return answer
115
+ except Exception as e:
116
+ print(f"Error processing question: {e}")
117
+ return "Error processing question"
118
+
119
+ # Test the agent
120
+ if __name__ == "__main__":
121
+ agent = BasicAgent()
122
+
123
+ # Test questions
124
+ test_questions = [
125
+ "What is 15 + 27?",
126
+ "When was the Eiffel Tower built?",
127
+ "Who was the first person to walk on the moon?",
128
+ "What is the capital of France?"
129
+ ]
130
+
131
+ print("Testing Enhanced Agent:")
132
+ print("=" * 50)
133
+
134
+ for i, question in enumerate(test_questions, 1):
135
+ print(f"\n{i}. Question: {question}")
136
+ answer = agent(question)
137
+ print(f" Answer: {answer}")
138
+ print("-" * 30)
uv.lock ADDED
The diff for this file is too large to render. See raw diff