Exodus28 commited on
Commit
7bbdea5
·
verified ·
1 Parent(s): d4e9c60

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +36 -43
agent.py CHANGED
@@ -1,14 +1,17 @@
1
  """
2
- agent.py: free agent for the Hugging Face Agents Course, Unit 4 (GAIA level 1).
3
-
4
- Uses smolagents + a free Gemini API key (Google AI Studio) through LiteLLM.
5
- Put GEMINI_API_KEY in your Space: Settings -> Variables and secrets -> New secret.
6
-
7
- Faster version:
8
- - no fixed pause after every question: a shared rate limiter spaces out model calls
9
- only as much as the free tier needs, so several questions can run in parallel
10
- - a fresh agent per question, so parallel runs don't share memory
11
- - fewer steps per question and short retries
 
 
 
12
  """
13
 
14
  import os
@@ -18,12 +21,11 @@ import tempfile
18
  import threading
19
 
20
  import requests
21
- from PIL import Image
22
  from smolagents import CodeAgent, LiteLLMModel, DuckDuckGoSearchTool, VisitWebpageTool
23
 
24
  API_URL = "https://agents-course-unit4-scoring.hf.space" # same as DEFAULT_API_URL in app.py
25
- MODEL_ID = "groq/llama-3.3-70b-versatile" # any current free Gemini Flash model
26
- REQUESTS_PER_MINUTE = 9 # keep just under your free-tier limit (check it in Google AI Studio)
27
  MAX_STEPS = 6 # most level-1 questions need 2-5 steps
28
 
29
  INSTRUCTIONS = """You are answering a question from the GAIA benchmark. Your answer is checked by EXACT MATCH.
@@ -59,8 +61,8 @@ class RateLimiter:
59
  LIMITER = RateLimiter(REQUESTS_PER_MINUTE)
60
 
61
 
62
- class RateLimitedModel(LiteLLMModel):
63
- """Gemini model that waits for the shared rate limiter and retries briefly on 429 errors."""
64
  def generate(self, *args, **kwargs):
65
  for attempt in range(4):
66
  LIMITER.wait()
@@ -68,21 +70,20 @@ class RateLimitedModel(LiteLLMModel):
68
  return super().generate(*args, **kwargs)
69
  except Exception as e:
70
  msg = str(e).lower()
71
- if attempt < 3 and ("429" in msg or "rate" in msg or "quota" in msg or "overloaded" in msg):
72
- time.sleep(8 * (attempt + 1))
73
  continue
74
  raise
75
 
76
 
 
 
 
 
77
  def _make_agent():
78
- model = RateLimitedModel(
79
- model_id=MODEL_ID,
80
- api_key=os.environ["GEMINI_API_KEY"],
81
- temperature=0.0,
82
- )
83
  return CodeAgent(
84
- tools=[DuckDuckGoSearchTool(max_results=5), VisitWebpageTool(max_output_length=12000)],
85
- model=model,
86
  add_base_tools=False,
87
  additional_authorized_imports=[
88
  "pandas", "numpy", "openpyxl", "json", "re", "math",
@@ -94,10 +95,11 @@ def _make_agent():
94
 
95
 
96
  def check_model():
97
- """One tiny call to Gemini before the run. Returns None if it works, or the error text."""
 
 
98
  try:
99
- model = LiteLLMModel(model_id=MODEL_ID, api_key=os.environ["GEMINI_API_KEY"], temperature=0.0)
100
- model.generate([{"role": "user", "content": [{"type": "text", "text": "Reply with the word OK."}]}])
101
  return None
102
  except Exception as e:
103
  return f"{type(e).__name__}: {e}"
@@ -105,9 +107,6 @@ def check_model():
105
 
106
  class BasicAgent:
107
  def __init__(self):
108
- if not os.environ.get("GEMINI_API_KEY"):
109
- raise RuntimeError("GEMINI_API_KEY secret is missing (Space Settings -> Variables and secrets), "
110
- "or the Space wasn't restarted after adding it.")
111
  self.last_error = {} # task_id -> error text, shown in the results table
112
 
113
  @staticmethod
@@ -137,24 +136,18 @@ class BasicAgent:
137
  return text.strip().rstrip(".").strip()
138
 
139
  def __call__(self, question, task_id=None):
140
- prompt = INSTRUCTIONS + question
141
- images = None
142
 
 
143
  path = self._download_file(task_id)
144
  if path:
145
- if path.lower().endswith((".mp3", ".wav", ".m4a")):
146
- return "unknown" # audio can't be handled for free: skip fast
147
- if path.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
148
- images = [Image.open(path)] # Gemini can look at images directly
149
- prompt += "\n\nThe image attached to this question is provided to you."
150
- else:
151
- prompt += f"\n\nThe file for this question is saved at: {path}"
152
-
153
- if "youtube.com" in question or "youtu.be" in question:
154
- return "unknown" # video questions: skip instead of burning steps
155
 
156
  try:
157
- answer = _make_agent().run(prompt, images=images)
158
  except Exception as e:
159
  err = f"{type(e).__name__}: {e}"
160
  print(f"[{task_id}] FAILED: {err}")
 
1
  """
2
+ agent.py: free agent for the Hugging Face Agents Course, Unit 4 (GAIA level 1), using Groq.
3
+
4
+ Setup:
5
+ 1. Create a free key at console.groq.com -> API Keys (no credit card).
6
+ 2. In your Space: Settings -> Variables and secrets -> New secret
7
+ name: GROQ_API_KEY value: your key
8
+ 3. Restart the Space.
9
+
10
+ How it stays fast and inside Groq's free limits:
11
+ - a shared rate limiter spaces out model calls across the parallel questions
12
+ - short retries when Groq answers "rate limit" (429)
13
+ - a fresh agent per question, at most MAX_STEPS steps each
14
+ - questions that need audio, video or images are skipped (Llama 3.3 can't use them)
15
  """
16
 
17
  import os
 
21
  import threading
22
 
23
  import requests
 
24
  from smolagents import CodeAgent, LiteLLMModel, DuckDuckGoSearchTool, VisitWebpageTool
25
 
26
  API_URL = "https://agents-course-unit4-scoring.hf.space" # same as DEFAULT_API_URL in app.py
27
+ MODEL_ID = "groq/llama-3.3-70b-versatile" # if Groq renames it, pick a current model at console.groq.com
28
+ REQUESTS_PER_MINUTE = 10 # Groq's free plan also limits tokens per minute, so stay cautious
29
  MAX_STEPS = 6 # most level-1 questions need 2-5 steps
30
 
31
  INSTRUCTIONS = """You are answering a question from the GAIA benchmark. Your answer is checked by EXACT MATCH.
 
61
  LIMITER = RateLimiter(REQUESTS_PER_MINUTE)
62
 
63
 
64
+ class GroqModel(LiteLLMModel):
65
+ """Groq model that waits for the shared rate limiter and retries briefly on rate-limit errors."""
66
  def generate(self, *args, **kwargs):
67
  for attempt in range(4):
68
  LIMITER.wait()
 
70
  return super().generate(*args, **kwargs)
71
  except Exception as e:
72
  msg = str(e).lower()
73
+ if attempt < 3 and ("429" in msg or "rate" in msg or "overloaded" in msg):
74
+ time.sleep(10 * (attempt + 1))
75
  continue
76
  raise
77
 
78
 
79
+ def _new_model():
80
+ return GroqModel(model_id=MODEL_ID, api_key=os.environ["GROQ_API_KEY"], temperature=0.0)
81
+
82
+
83
  def _make_agent():
 
 
 
 
 
84
  return CodeAgent(
85
+ tools=[DuckDuckGoSearchTool(max_results=5), VisitWebpageTool(max_output_length=6000)],
86
+ model=_new_model(),
87
  add_base_tools=False,
88
  additional_authorized_imports=[
89
  "pandas", "numpy", "openpyxl", "json", "re", "math",
 
95
 
96
 
97
  def check_model():
98
+ """One tiny call to Groq before the run. Returns None if it works, or the error text."""
99
+ if not os.environ.get("GROQ_API_KEY"):
100
+ return "GROQ_API_KEY secret is missing. Add it in Settings -> Variables and secrets, then restart the Space."
101
  try:
102
+ _new_model().generate([{"role": "user", "content": [{"type": "text", "text": "Reply with the word OK."}]}])
 
103
  return None
104
  except Exception as e:
105
  return f"{type(e).__name__}: {e}"
 
107
 
108
  class BasicAgent:
109
  def __init__(self):
 
 
 
110
  self.last_error = {} # task_id -> error text, shown in the results table
111
 
112
  @staticmethod
 
136
  return text.strip().rstrip(".").strip()
137
 
138
  def __call__(self, question, task_id=None):
139
+ if "youtube.com" in question or "youtu.be" in question:
140
+ return "unknown" # video: can't be watched, skip fast
141
 
142
+ prompt = INSTRUCTIONS + question
143
  path = self._download_file(task_id)
144
  if path:
145
+ if path.lower().endswith((".mp3", ".wav", ".m4a", ".png", ".jpg", ".jpeg", ".webp")):
146
+ return "unknown" # audio or image: Llama 3.3 can't use it, skip fast
147
+ prompt += f"\n\nThe file for this question is saved at: {path}"
 
 
 
 
 
 
 
148
 
149
  try:
150
+ answer = _make_agent().run(prompt)
151
  except Exception as e:
152
  err = f"{type(e).__name__}: {e}"
153
  print(f"[{task_id}] FAILED: {err}")