yannis2025 commited on
Commit
6abc8f9
·
verified ·
1 Parent(s): 379ab8a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -28
app.py CHANGED
@@ -4,6 +4,9 @@ import requests
4
  import inspect
5
  import pandas as pd
6
  import re
 
 
 
7
 
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
@@ -12,62 +15,136 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
  class BasicAgent:
14
  def __init__(self):
15
- self.api_url = "https://api-inference.huggingface.co/models/mixtral-7b-instruct-v0.3"
16
  self.api_token = os.getenv("HF_TOKEN")
 
17
  if not self.api_token:
18
  raise ValueError("HF_TOKEN environment variable not set.")
19
  self.headers = {"Authorization": f"Bearer {self.api_token}"}
20
- print("BasicAgent initialized with Mistral-7B-Instruct-v0.3.")
21
 
22
  def __call__(self, question: str) -> tuple[str, str]:
23
  print(f"Agent received question (first 50 chars): {question[:50]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  try:
25
- # Prompt designed for concise answers and detailed reasoning
26
  prompt = (
27
  f"Question: {question}\n"
28
  "Please provide a concise answer (e.g., a number like '5' or a short phrase) "
29
  "followed by a detailed explanation of your reasoning. "
30
  "Format your response as: Answer: <concise answer>\nReasoning: <detailed reasoning>"
31
  )
32
- payload = {
33
- "inputs": prompt,
34
- "parameters": {"max_new_tokens": 300, "return_full_text": False}
35
- }
36
- response = requests.post(self.api_url, headers=self.headers, json=payload, timeout=10)
37
- response.raise_for_status()
38
- full_response = response.json()[0]["generated_text"].strip()
39
-
40
- # Extract concise answer and reasoning
41
  answer_match = re.search(r"Answer: (.*?)(?:\nReasoning: (.*))?$", full_response, re.DOTALL)
42
  if answer_match:
43
  concise_answer = answer_match.group(1).strip()
44
- reasoning = answer_match.group(2).strip() if answer_match.group(2) else "No reasoning provided by model."
45
  else:
46
- # Fallback: Try to extract a concise answer (e.g., a number or short phrase)
47
  concise_answer = self._extract_concise_answer(full_response)
48
- reasoning = full_response
49
-
50
- print(f"Agent returning concise answer: {concise_answer}")
51
- print(f"Reasoning (first 50 chars): {reasoning[:50]}...")
52
- return concise_answer, reasoning
53
- except requests.exceptions.RequestException as e:
54
  print(f"Error querying Inference API: {e}")
55
  return f"Error: {e}", f"Error: Failed to get answer from model - {e}"
56
- except Exception as e:
57
- print(f"Unexpected error in agent: {e}")
58
- return f"Error: {e}", f"Error: {e}"
 
 
 
 
 
 
 
 
 
59
 
60
  def _extract_concise_answer(self, response: str) -> str:
61
- """Extracts a concise answer (e.g., number or short phrase) from the model's response."""
62
- # Try to find a number (integer or float)
63
  number_match = re.search(r"\b\d+(\.\d+)?\b", response)
64
  if number_match:
65
  return number_match.group(0)
66
- # Try to find a short phrase (up to 3 words)
67
  words = response.split()[:3]
68
  if len(words) <= 3 and len(" ".join(words)) <= 20:
69
  return " ".join(words)
70
- # Fallback: Use first sentence or first 10 characters
71
  sentence_end = response.find(". ")
72
  if sentence_end != -1:
73
  return response[:sentence_end].strip()[:20]
@@ -214,7 +291,7 @@ with gr.Blocks() as demo:
214
  ---
215
  **Disclaimers:**
216
  Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
217
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
218
  """
219
  )
220
 
 
4
  import inspect
5
  import pandas as pd
6
  import re
7
+ import sympy as sp
8
+ import wikipedia
9
+ from bs4 import BeautifulSoup
10
 
11
  # --- Constants ---
12
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
15
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
16
  class BasicAgent:
17
  def __init__(self):
18
+ self.api_url = "https://api-inference.huggingface.co/models/distilbert-base-uncased"
19
  self.api_token = os.getenv("HF_TOKEN")
20
+ print(f"HF_TOKEN: {self.api_token}") # Display token for verification
21
  if not self.api_token:
22
  raise ValueError("HF_TOKEN environment variable not set.")
23
  self.headers = {"Authorization": f"Bearer {self.api_token}"}
24
+ print("BasicAgent initialized with DistilBERT, SymPy, Wikipedia, and DuckDuckGo search.")
25
 
26
  def __call__(self, question: str) -> tuple[str, str]:
27
  print(f"Agent received question (first 50 chars): {question[:50]}...")
28
+ reasoning = []
29
+
30
+ # Step 1: Try to identify if the question is mathematical
31
+ is_math = any(keyword in question.lower() for keyword in ["calculate", "solve", "what is", "+", "-", "*", "/", "=", "equation"])
32
+ if is_math:
33
+ try:
34
+ expr = question.lower().replace("what is", "").replace("calculate", "").replace("solve", "").strip()
35
+ if "=" in expr:
36
+ left, right = expr.split("=")
37
+ eq = sp.Eq(sp.sympify(left.strip()), sp.sympify(right.strip()))
38
+ solution = sp.solve(eq)
39
+ concise_answer = str(solution[0]) if solution else "No solution"
40
+ reasoning.append(f"Math Solver (SymPy): Parsed equation '{expr}'. Solution: {concise_answer}")
41
+ else:
42
+ result = sp.sympify(expr).evalf()
43
+ concise_answer = str(result)
44
+ reasoning.append(f"Math Solver (SymPy): Evaluated expression '{expr}'. Result: {concise_answer}")
45
+ if concise_answer != "No solution":
46
+ print(f"Agent returning concise answer (math): {concise_answer}")
47
+ return concise_answer, "\n".join(reasoning)
48
+ except Exception as e:
49
+ reasoning.append(f"Math Solver (SymPy) failed: {e}")
50
+
51
+ # Step 2: Try Wikipedia for factual questions
52
+ try:
53
+ wikipedia.set_lang("en")
54
+ key_terms = " ".join(question.split()[:3]).strip("?")
55
+ wiki_summary = wikipedia.summary(key_terms, sentences=2, auto_suggest=True)
56
+ prompt = (
57
+ f"Question: {question}\n"
58
+ f"Context: {wiki_summary}\n"
59
+ "Provide a concise answer (e.g., a number or short phrase) based on the context."
60
+ )
61
+ wiki_answer = self._query_llm(prompt)
62
+ answer_match = re.search(r"Answer: (.*?)(?:\nReasoning: (.*))?$", wiki_answer, re.DOTALL)
63
+ if answer_match:
64
+ concise_answer = answer_match.group(1).strip()
65
+ wiki_reasoning = answer_match.group(2).strip() if answer_match.group(2) else wiki_answer
66
+ else:
67
+ concise_answer = self._extract_concise_answer(wiki_answer)
68
+ wiki_reasoning = wiki_answer
69
+ reasoning.append(f"Wikipedia: Searched '{key_terms}'. Summary: {wiki_summary[:100]}... Answer: {concise_answer}")
70
+ print(f"Agent returning concise answer (Wikipedia): {concise_answer}")
71
+ return concise_answer, "\n".join(reasoning)
72
+ except wikipedia.exceptions.DisambiguationError:
73
+ reasoning.append("Wikipedia: Disambiguation error, multiple results found.")
74
+ except Exception as e:
75
+ reasoning.append(f"Wikipedia failed: {e}")
76
+
77
+ # Step 3: Try web search with DuckDuckGo
78
+ try:
79
+ search_url = f"https://duckduckgo.com/html/?q={question.replace(' ', '+')}"
80
+ response = requests.get(search_url, timeout=10)
81
+ response.raise_for_status()
82
+ soup = BeautifulSoup(response.text, "html.parser")
83
+ snippets = [s.text.strip() for s in soup.find_all("div", class_="result__snippet")[:2]]
84
+ if snippets:
85
+ prompt = (
86
+ f"Question: {question}\n"
87
+ f"Search Results: {' '.join(snippets)[:500]}\n"
88
+ "Provide a concise answer (e.g., a number or short phrase) based on the search results."
89
+ )
90
+ search_answer = self._query_llm(prompt)
91
+ answer_match = re.search(r"Answer: (.*?)(?:\nReasoning: (.*))?$", search_answer, re.DOTALL)
92
+ if answer_match:
93
+ concise_answer = answer_match.group(1).strip()
94
+ search_reasoning = answer_match.group(2).strip() if answer_match.group(2) else search_answer
95
+ else:
96
+ concise_answer = self._extract_concise_answer(search_answer)
97
+ search_reasoning = search_answer
98
+ reasoning.append(f"Web Search (DuckDuckGo): Searched '{question}'. Answer: {concise_answer}")
99
+ print(f"Agent returning concise answer (search): {concise_answer}")
100
+ return concise_answer, "\n".join(reasoning)
101
+ else:
102
+ reasoning.append("Web Search (DuckDuckGo): No relevant results found.")
103
+ except Exception as e:
104
+ reasoning.append(f"Web Search (DuckDuckGo) failed: {e}")
105
+
106
+ # Step 4: Fallback to LLM alone
107
  try:
 
108
  prompt = (
109
  f"Question: {question}\n"
110
  "Please provide a concise answer (e.g., a number like '5' or a short phrase) "
111
  "followed by a detailed explanation of your reasoning. "
112
  "Format your response as: Answer: <concise answer>\nReasoning: <detailed reasoning>"
113
  )
114
+ full_response = self._query_llm(prompt)
 
 
 
 
 
 
 
 
115
  answer_match = re.search(r"Answer: (.*?)(?:\nReasoning: (.*))?$", full_response, re.DOTALL)
116
  if answer_match:
117
  concise_answer = answer_match.group(1).strip()
118
+ llm_reasoning = answer_match.group(2).strip() if answer_match.group(2) else "No reasoning provided by model."
119
  else:
 
120
  concise_answer = self._extract_concise_answer(full_response)
121
+ llm_reasoning = full_response
122
+ reasoning.append(f"LLM (DistilBERT): {llm_reasoning}")
123
+ print(f"Agent returning concise answer (LLM): {concise_answer}")
124
+ return concise_answer, "\n".join(reasoning)
125
+ except Exception as e:
 
126
  print(f"Error querying Inference API: {e}")
127
  return f"Error: {e}", f"Error: Failed to get answer from model - {e}"
128
+
129
+ def _query_llm(self, prompt: str) -> str:
130
+ try:
131
+ payload = {
132
+ "inputs": prompt,
133
+ "parameters": {"max_length": 100, "return_full_text": False}
134
+ }
135
+ response = requests.post(self.api_url, headers=self.headers, json=payload, timeout=10)
136
+ response.raise_for_status()
137
+ return response.json()[0]["generated_text"].strip()
138
+ except requests.exceptions.RequestException as e:
139
+ return f"Error: Failed to get answer from model - {e}"
140
 
141
  def _extract_concise_answer(self, response: str) -> str:
 
 
142
  number_match = re.search(r"\b\d+(\.\d+)?\b", response)
143
  if number_match:
144
  return number_match.group(0)
 
145
  words = response.split()[:3]
146
  if len(words) <= 3 and len(" ".join(words)) <= 20:
147
  return " ".join(words)
 
148
  sentence_end = response.find(". ")
149
  if sentence_end != -1:
150
  return response[:sentence_end].strip()[:20]
 
291
  ---
292
  **Disclaimers:**
293
  Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
294
+ This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a separate action or even to answer the questions in async.
295
  """
296
  )
297