Shivangsinha commited on
Commit
37790b8
·
verified ·
1 Parent(s): 2db6e24

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -49
app.py CHANGED
@@ -5,72 +5,130 @@ import requests
5
  import pandas as pd
6
  from smolagents import (
7
  CodeAgent,
 
8
  InferenceClientModel,
9
  DuckDuckGoSearchTool,
10
  WikipediaSearchTool,
11
  PythonInterpreterTool,
 
12
  tool,
13
  )
14
 
15
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
16
 
 
 
 
 
 
 
17
  @tool
18
  def get_current_date_time() -> str:
19
  """Returns the current date and time in ISO format."""
20
  from datetime import datetime
21
  return datetime.now().isoformat()
22
 
23
- class BasicAgent:
24
  def __init__(self):
25
- print("BasicAgent initialized.")
 
26
 
27
- new_hf_token = os.getenv("HF_TOKEN")
28
- if not new_hf_token:
29
- raise ValueError("HF_TOKEN environment variable not set in Space Secrets.")
 
 
 
 
30
 
31
- self.model = InferenceClientModel(
32
- model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
33
- token=new_hf_token,
34
- )
35
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  self.tools = [
37
  DuckDuckGoSearchTool(),
38
  WikipediaSearchTool(),
39
  PythonInterpreterTool(),
 
40
  get_current_date_time,
41
  ]
42
-
43
- self.agent = CodeAgent(
44
- tools=self.tools,
45
- model=self.model,
46
- max_steps=10,
47
- # CRITICAL FIX: Added pandas and requests so the agent can download and read Excel/CSV files!
48
- additional_authorized_imports=["datetime", "re", "json", "math", "collections", "pandas", "requests"],
49
- )
50
- print("BasicAgent ready with Hugging Face (Qwen2.5-Coder-32B).")
51
 
52
  def __call__(self, question: str) -> str:
53
  print(f"\nAgent received question: {question[:80]}...")
54
- max_retries = 3
55
 
56
- for attempt in range(max_retries):
57
- try:
58
- time.sleep(2)
59
- answer = self.agent.run(question)
60
- print(f"Agent answer: {str(answer)[:200]}")
61
- return str(answer)
62
- except Exception as e:
63
- err_msg = str(e).lower()
64
- if "429" in err_msg or "rate limit" in err_msg or "too many requests" in err_msg:
65
- wait_time = 20 * (attempt + 1)
66
- print(f"Rate limit hit! Pausing for {wait_time} seconds before retrying...")
67
- time.sleep(wait_time)
68
- else:
69
- print(f"Agent error processing question: {e}")
70
- return f"Error: {str(e)}"
 
 
 
 
 
 
 
71
 
72
- return "Error: Rate limit exceeded after maximum retries."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
 
74
  def run_and_submit_all(profile: gr.OAuthProfile | None):
75
  space_id = os.getenv("SPACE_ID")
76
  if profile:
@@ -85,13 +143,12 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
85
  submit_url = f"{api_url}/submit"
86
 
87
  try:
88
- agent = BasicAgent()
89
  except Exception as e:
90
  print(f"Error instantiating agent: {e}")
91
  return f"Error initializing agent: {e}", None
92
 
93
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
94
- print(f"Fetching questions from: {questions_url}")
95
 
96
  try:
97
  response = requests.get(questions_url, timeout=15)
@@ -109,18 +166,14 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
109
  for i, item in enumerate(questions_data):
110
  task_id = item.get("task_id")
111
  question_text = item.get("question")
112
-
113
- # CRITICAL FIX 1: Grab the hidden file URL if the server provides one
114
  file_url = item.get("file_url")
115
 
116
  if not task_id or not question_text:
117
  continue
118
 
119
- # CRITICAL FIX 2: Inject the file URL into the agent's prompt so it can download it
120
  if file_url:
121
  question_text += f"\n\n[IMPORTANT: This task requires analyzing an attached file. You MUST download or read it directly from this URL: {file_url} using your Python tool.]"
122
 
123
- # CRITICAL FIX 3: Threaten the agent to act like a strict robot to pass the automated grader
124
  strict_prompt = (
125
  f"{question_text}\n\n"
126
  "CRITICAL SUBMISSION INSTRUCTIONS:\n"
@@ -129,19 +182,18 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
129
  "2. DO NOT include any conversational text, explanations, or reasoning in your final output.\n"
130
  "3. If the answer is a name, number, or short string, output ONLY that exact string.\n"
131
  "4. For numbers, do not include symbols unless explicitly requested."
 
132
  )
133
 
134
  try:
135
- # We pass the strict prompt instead of the raw question
136
  submitted_answer = agent(strict_prompt)
137
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
138
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
139
  except Exception as e:
140
- print(f"Error on task {task_id}: {e}")
141
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"ERROR: {e}"})
142
 
143
- print("Cooling down for 15 seconds to prevent token exhaustion...")
144
- time.sleep(15)
145
 
146
  if not answers_payload:
147
  return "No answers.", pd.DataFrame(results_log)
@@ -163,18 +215,18 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
163
  print(final_status)
164
  return final_status, pd.DataFrame(results_log)
165
  except Exception as e:
166
- print(f"Submission error: {e}")
167
  return f"Submission failed: {e}", pd.DataFrame(results_log)
168
 
169
  # --- Build Gradio UI ---
170
  with gr.Blocks() as demo:
171
- gr.Markdown("# Basic Agent Evaluation Runner")
172
  gr.Markdown(
173
  """
174
  **Instructions:**
175
- 1. Ensure your `HF_TOKEN` is set.
176
  2. Log in below.
177
  3. Click 'Run Evaluation & Submit' to start.
 
178
  """
179
  )
180
  gr.LoginButton()
 
5
  import pandas as pd
6
  from smolagents import (
7
  CodeAgent,
8
+ LiteLLMModel,
9
  InferenceClientModel,
10
  DuckDuckGoSearchTool,
11
  WikipediaSearchTool,
12
  PythonInterpreterTool,
13
+ VisitWebpageTool,
14
  tool,
15
  )
16
 
17
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
18
 
19
+ # --- Custom Throttled Model to protect Gemini ---
20
+ class ThrottledGeminiModel(LiteLLMModel):
21
+ def __call__(self, *args, **kwargs):
22
+ time.sleep(5) # Base 5-second delay to stay under 15 RPM
23
+ return super().__call__(*args, **kwargs)
24
+
25
  @tool
26
  def get_current_date_time() -> str:
27
  """Returns the current date and time in ISO format."""
28
  from datetime import datetime
29
  return datetime.now().isoformat()
30
 
31
+ class FailproofAgent:
32
  def __init__(self):
33
+ print("Initializing Failproof Cascade Agent...")
34
+ self.models = []
35
 
36
+ # 1. Primary: Gemini 2.0 Flash (1500 daily requests, huge context)
37
+ gemini_key = os.getenv("GEMINI_API_KEY")
38
+ if gemini_key:
39
+ self.models.append({
40
+ "name": "Gemini 2.0 Flash",
41
+ "model": ThrottledGeminiModel(model_id="gemini/gemini-2.0-flash", api_key=gemini_key)
42
+ })
43
 
44
+ # 2. Secondary: HF Qwen2.5-Coder (Great for code, serverless)
45
+ hf_token = os.getenv("HF_TOKEN") or os.getenv("HF_TOKEN")
46
+ if hf_token:
47
+ self.models.append({
48
+ "name": "Hugging Face Qwen2.5 Coder",
49
+ "model": InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", token=hf_token)
50
+ })
51
+
52
+ # 3. Tertiary: Groq Llama 3.3 (Fast, smart fallback)
53
+ groq_key = os.getenv("GROQ_API_KEY")
54
+ if groq_key:
55
+ self.models.append({
56
+ "name": "Groq Llama 3.3 70B",
57
+ "model": LiteLLMModel(model_id="groq/llama-3.3-70b-versatile", api_key=groq_key)
58
+ })
59
+
60
+ # 4. Emergency: OpenRouter Free Pool (Decentralized backup)
61
+ or_key = os.getenv("OPENROUTER_API_KEY")
62
+ if or_key:
63
+ self.models.append({
64
+ "name": "OpenRouter Auto-Free",
65
+ "model": LiteLLMModel(model_id="openrouter/openrouter/free", api_key=or_key)
66
+ })
67
+
68
+ if not self.models:
69
+ raise ValueError("No API keys found! Please set at least one in Space Secrets.")
70
+
71
+ self.current_model_idx = 0
72
  self.tools = [
73
  DuckDuckGoSearchTool(),
74
  WikipediaSearchTool(),
75
  PythonInterpreterTool(),
76
+ VisitWebpageTool(), # Allows the agent to read inside websites
77
  get_current_date_time,
78
  ]
79
+ print(f"Agent armed with {len(self.models)} fallback brains. Ready to go.")
 
 
 
 
 
 
 
 
80
 
81
  def __call__(self, question: str) -> str:
82
  print(f"\nAgent received question: {question[:80]}...")
83
+ max_retries_per_model = 3
84
 
85
+ # Keep trying models until we run out of backups
86
+ while self.current_model_idx < len(self.models):
87
+ current_brain = self.models[self.current_model_idx]
88
+ print(f"🧠 USING BRAIN: {current_brain['name']}")
89
+
90
+ # Re-instantiate the agent cleanly for this attempt
91
+ agent = CodeAgent(
92
+ tools=self.tools,
93
+ model=current_brain["model"],
94
+ max_steps=7,
95
+ additional_authorized_imports=["datetime", "re", "json", "math", "collections", "pandas", "requests"],
96
+ )
97
+
98
+ for attempt in range(max_retries_per_model):
99
+ try:
100
+ time.sleep(2)
101
+ answer = agent.run(question)
102
+ print(f"Agent answer: {str(answer)[:200]}")
103
+ return str(answer)
104
+ except Exception as e:
105
+ err_msg = str(e).lower()
106
+ print(f"⚠️ Agent Error: {err_msg}")
107
 
108
+ # FATAL QUOTA ERROR: Break the retry loop and switch brains
109
+ if "402" in err_msg or "payment required" in err_msg or "quota" in err_msg or "limit 0" in err_msg or "spend limit" in err_msg:
110
+ print(f"🚨 FATAL QUOTA HIT on {current_brain['name']}. Swapping to backup brain...")
111
+ break # This exits the attempt loop and moves to the next model
112
+
113
+ # TEMPORARY RATE LIMIT: Pause and retry the same brain
114
+ elif "429" in err_msg or "rate limit" in err_msg or "too many requests" in err_msg:
115
+ wait_time = 20 * (attempt + 1)
116
+ print(f"⏳ Temporary rate limit. Pausing for {wait_time}s...")
117
+ time.sleep(wait_time)
118
+ continue
119
+
120
+ # OTHER ERRORS (Code failures, etc): Retry
121
+ else:
122
+ print("Retrying due to generic error...")
123
+ continue
124
+
125
+ # If we exit the loop, this brain has failed completely. Move to the next one.
126
+ print(f"⏭️ Exhausted retries or hit hard limit on {current_brain['name']}. Escalating...")
127
+ self.current_model_idx += 1
128
+
129
+ return "Error: All available models exhausted their quotas or failed."
130
 
131
+ # --- App Runner ---
132
  def run_and_submit_all(profile: gr.OAuthProfile | None):
133
  space_id = os.getenv("SPACE_ID")
134
  if profile:
 
143
  submit_url = f"{api_url}/submit"
144
 
145
  try:
146
+ agent = FailproofAgent()
147
  except Exception as e:
148
  print(f"Error instantiating agent: {e}")
149
  return f"Error initializing agent: {e}", None
150
 
151
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
 
152
 
153
  try:
154
  response = requests.get(questions_url, timeout=15)
 
166
  for i, item in enumerate(questions_data):
167
  task_id = item.get("task_id")
168
  question_text = item.get("question")
 
 
169
  file_url = item.get("file_url")
170
 
171
  if not task_id or not question_text:
172
  continue
173
 
 
174
  if file_url:
175
  question_text += f"\n\n[IMPORTANT: This task requires analyzing an attached file. You MUST download or read it directly from this URL: {file_url} using your Python tool.]"
176
 
 
177
  strict_prompt = (
178
  f"{question_text}\n\n"
179
  "CRITICAL SUBMISSION INSTRUCTIONS:\n"
 
182
  "2. DO NOT include any conversational text, explanations, or reasoning in your final output.\n"
183
  "3. If the answer is a name, number, or short string, output ONLY that exact string.\n"
184
  "4. For numbers, do not include symbols unless explicitly requested."
185
+ "5. **ULTRATHINK** and double check the response making sure the return answer."
186
  )
187
 
188
  try:
 
189
  submitted_answer = agent(strict_prompt)
190
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
191
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
192
  except Exception as e:
 
193
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"ERROR: {e}"})
194
 
195
+ print("Cooling down for 10 seconds to protect quotas...")
196
+ time.sleep(10)
197
 
198
  if not answers_payload:
199
  return "No answers.", pd.DataFrame(results_log)
 
215
  print(final_status)
216
  return final_status, pd.DataFrame(results_log)
217
  except Exception as e:
 
218
  return f"Submission failed: {e}", pd.DataFrame(results_log)
219
 
220
  # --- Build Gradio UI ---
221
  with gr.Blocks() as demo:
222
+ gr.Markdown("# The Failproof Multi-Model Agent Runner")
223
  gr.Markdown(
224
  """
225
  **Instructions:**
226
+ 1. Ensure your API keys (`GEMINI_API_KEY`, `NEW_HF_TOKEN`, `GROQ_API_KEY`, etc.) are set in Space Secrets.
227
  2. Log in below.
228
  3. Click 'Run Evaluation & Submit' to start.
229
+ *(Watch the logs! If a model dies, it will automatically hot-swap to the next one).*
230
  """
231
  )
232
  gr.LoginButton()