Update agent.py
Browse files
agent.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
agent.py β Claude
|
| 3 |
-----------------------------------------------------------
|
| 4 |
Environment
|
| 5 |
-----------
|
|
@@ -7,8 +7,6 @@ ANTHROPIC_API_KEY β API key from Anthropic (set in Hugging Face space secret
|
|
| 7 |
GAIA_API_URL β (optional) override for the GAIA scoring endpoint
|
| 8 |
"""
|
| 9 |
|
| 10 |
-
from __future__ import annotations
|
| 11 |
-
|
| 12 |
import base64
|
| 13 |
import mimetypes
|
| 14 |
import os
|
|
@@ -16,20 +14,13 @@ import re
|
|
| 16 |
import tempfile
|
| 17 |
import time
|
| 18 |
from typing import List, Dict, Any, Optional
|
| 19 |
-
import
|
| 20 |
import requests
|
| 21 |
from urllib.parse import urlparse
|
| 22 |
-
import
|
| 23 |
-
|
| 24 |
-
from smolagents import (
|
| 25 |
-
CodeAgent,
|
| 26 |
-
DuckDuckGoSearchTool,
|
| 27 |
-
PythonInterpreterTool,
|
| 28 |
-
tool,
|
| 29 |
-
)
|
| 30 |
|
| 31 |
# --------------------------------------------------------------------------- #
|
| 32 |
-
#
|
| 33 |
# --------------------------------------------------------------------------- #
|
| 34 |
DEFAULT_API_URL = os.getenv(
|
| 35 |
"GAIA_API_URL", "https://agents-course-unit4-scoring.hf.space"
|
|
@@ -74,15 +65,74 @@ class RateLimiter:
|
|
| 74 |
time.sleep(random.uniform(0.2, 1.0))
|
| 75 |
|
| 76 |
# Global rate limiter instance
|
| 77 |
-
RATE_LIMITER = RateLimiter(requests_per_minute=
|
| 78 |
|
| 79 |
# --------------------------------------------------------------------------- #
|
| 80 |
-
#
|
| 81 |
# --------------------------------------------------------------------------- #
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
"""
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
"""
|
| 87 |
|
| 88 |
def __init__(
|
|
@@ -93,16 +143,7 @@ class FixedAnthropicModel:
|
|
| 93 |
max_tokens: int = 1024,
|
| 94 |
system_prompt: Optional[str] = None,
|
| 95 |
):
|
| 96 |
-
"""
|
| 97 |
-
Initialize a model that properly handles system prompts for Anthropic via LiteLLM
|
| 98 |
-
|
| 99 |
-
Args:
|
| 100 |
-
model_id: Claude model ID to use
|
| 101 |
-
api_key: API key (will use ANTHROPIC_API_KEY env var if not provided)
|
| 102 |
-
temperature: Temperature for text generation
|
| 103 |
-
max_tokens: Maximum tokens to generate
|
| 104 |
-
system_prompt: System prompt to use
|
| 105 |
-
"""
|
| 106 |
# Get API key from env if not provided
|
| 107 |
if api_key is None:
|
| 108 |
api_key = os.getenv("ANTHROPIC_API_KEY")
|
|
@@ -119,49 +160,18 @@ class FixedAnthropicModel:
|
|
| 119 |
Your answers should be precise, direct, and exactly match the expected format.
|
| 120 |
All answers are graded by exact string match, so format carefully!"""
|
| 121 |
|
| 122 |
-
print(f"Initialized
|
| 123 |
|
| 124 |
def __call__(self, prompt: str, **kwargs) -> str:
|
| 125 |
-
"""
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
del kwargs['system_instruction']
|
| 135 |
-
|
| 136 |
-
# For Anthropic via LiteLLM, use the direct completion method
|
| 137 |
-
from litellm import completion
|
| 138 |
-
|
| 139 |
-
# Create a simple prompt with system instructions at the beginning
|
| 140 |
-
# This avoids the nested message structure issue
|
| 141 |
-
complete_prompt = f"{self.system_prompt}\n\n{prompt}"
|
| 142 |
-
|
| 143 |
-
response = completion(
|
| 144 |
-
model=self.model_id,
|
| 145 |
-
messages=[{"role": "user", "content": complete_prompt}],
|
| 146 |
-
api_key=self.api_key,
|
| 147 |
-
temperature=self.temperature,
|
| 148 |
-
max_tokens=self.max_tokens,
|
| 149 |
-
**kwargs
|
| 150 |
-
)
|
| 151 |
-
|
| 152 |
-
# Extract the content from the response
|
| 153 |
-
return response.choices[0].message.content
|
| 154 |
-
|
| 155 |
-
except Exception as e:
|
| 156 |
-
# Handle rate limit errors
|
| 157 |
-
if "rate_limit" in str(e).lower():
|
| 158 |
-
print(f"Rate limit error: {e}")
|
| 159 |
-
print("Waiting 60 seconds before retrying...")
|
| 160 |
-
time.sleep(60)
|
| 161 |
-
return self.__call__(prompt, **kwargs)
|
| 162 |
-
else:
|
| 163 |
-
print(f"Error calling Anthropic API: {e}")
|
| 164 |
-
raise
|
| 165 |
|
| 166 |
# --------------------------------------------------------------------------- #
|
| 167 |
# custom tool: fetch GAIA attachments
|
|
@@ -383,8 +393,8 @@ All answers are graded by exact string match, so format carefully!"""
|
|
| 383 |
if self.verbose:
|
| 384 |
print(f"Using Anthropic token: {api_key[:5]}...")
|
| 385 |
|
| 386 |
-
# Initialize Claude model with our
|
| 387 |
-
self.model =
|
| 388 |
model_id="anthropic/claude-3-5-sonnet-20240620", # Use Claude 3.5 Sonnet
|
| 389 |
api_key=api_key,
|
| 390 |
temperature=temperature,
|
|
@@ -393,7 +403,7 @@ All answers are graded by exact string match, so format carefully!"""
|
|
| 393 |
)
|
| 394 |
|
| 395 |
if self.verbose:
|
| 396 |
-
print(f"Initialized model:
|
| 397 |
|
| 398 |
# Initialize default tools
|
| 399 |
self.tools = [
|
|
|
|
| 1 |
"""
|
| 2 |
+
agent.py β Simplified Claude implementation for GAIA challenge
|
| 3 |
-----------------------------------------------------------
|
| 4 |
Environment
|
| 5 |
-----------
|
|
|
|
| 7 |
GAIA_API_URL β (optional) override for the GAIA scoring endpoint
|
| 8 |
"""
|
| 9 |
|
|
|
|
|
|
|
| 10 |
import base64
|
| 11 |
import mimetypes
|
| 12 |
import os
|
|
|
|
| 14 |
import tempfile
|
| 15 |
import time
|
| 16 |
from typing import List, Dict, Any, Optional
|
| 17 |
+
import random
|
| 18 |
import requests
|
| 19 |
from urllib.parse import urlparse
|
| 20 |
+
from smolagents import CodeAgent, DuckDuckGoSearchTool, PythonInterpreterTool, tool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# --------------------------------------------------------------------------- #
|
| 23 |
+
# Constants & helpers
|
| 24 |
# --------------------------------------------------------------------------- #
|
| 25 |
DEFAULT_API_URL = os.getenv(
|
| 26 |
"GAIA_API_URL", "https://agents-course-unit4-scoring.hf.space"
|
|
|
|
| 65 |
time.sleep(random.uniform(0.2, 1.0))
|
| 66 |
|
| 67 |
# Global rate limiter instance
|
| 68 |
+
RATE_LIMITER = RateLimiter(requests_per_minute=15) # Reduced to be extra cautious
|
| 69 |
|
| 70 |
# --------------------------------------------------------------------------- #
|
| 71 |
+
# Direct function to call Claude via LiteLLM
|
| 72 |
# --------------------------------------------------------------------------- #
|
| 73 |
+
def call_claude(
|
| 74 |
+
prompt: str,
|
| 75 |
+
system_prompt: Optional[str] = None,
|
| 76 |
+
temperature: float = 0.1,
|
| 77 |
+
max_tokens: int = 1024,
|
| 78 |
+
model_name: str = "anthropic/claude-3-5-sonnet-20240620"
|
| 79 |
+
) -> str:
|
| 80 |
"""
|
| 81 |
+
Call Claude through LiteLLM directly, following official LiteLLM documentation
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
prompt: The user's question
|
| 85 |
+
system_prompt: Optional system prompt
|
| 86 |
+
temperature: Temperature for generation
|
| 87 |
+
max_tokens: Max tokens to generate
|
| 88 |
+
model_name: Claude model to use
|
| 89 |
+
|
| 90 |
+
Returns:
|
| 91 |
+
The response text from Claude
|
| 92 |
+
"""
|
| 93 |
+
from litellm import completion
|
| 94 |
+
|
| 95 |
+
# Respect rate limits
|
| 96 |
+
RATE_LIMITER.wait()
|
| 97 |
+
|
| 98 |
+
try:
|
| 99 |
+
# Build messages following exactly LiteLLM's documented format
|
| 100 |
+
messages = []
|
| 101 |
+
|
| 102 |
+
# Add system message if provided
|
| 103 |
+
if system_prompt:
|
| 104 |
+
messages.append({"role": "system", "content": system_prompt})
|
| 105 |
+
|
| 106 |
+
# Add user message - this is simple text only format
|
| 107 |
+
messages.append({"role": "user", "content": prompt})
|
| 108 |
+
|
| 109 |
+
# Make the API call exactly as documented
|
| 110 |
+
response = completion(
|
| 111 |
+
model=model_name,
|
| 112 |
+
messages=messages,
|
| 113 |
+
temperature=temperature,
|
| 114 |
+
max_tokens=max_tokens
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
# Extract just the text content from the response
|
| 118 |
+
return response.choices[0].message.content
|
| 119 |
+
|
| 120 |
+
except Exception as e:
|
| 121 |
+
if "rate_limit" in str(e).lower():
|
| 122 |
+
print(f"Rate limit hit: {e}")
|
| 123 |
+
# Wait 60 seconds and try again
|
| 124 |
+
time.sleep(60)
|
| 125 |
+
return call_claude(prompt, system_prompt, temperature, max_tokens, model_name)
|
| 126 |
+
else:
|
| 127 |
+
print(f"Error calling Claude API: {e}")
|
| 128 |
+
raise
|
| 129 |
+
|
| 130 |
+
# --------------------------------------------------------------------------- #
|
| 131 |
+
# Simple Claude Model wrapper for smolagents
|
| 132 |
+
# --------------------------------------------------------------------------- #
|
| 133 |
+
class SimpleClaudeModel:
|
| 134 |
+
"""
|
| 135 |
+
A minimal wrapper around LiteLLM's direct call to Anthropic that works with smolagents
|
| 136 |
"""
|
| 137 |
|
| 138 |
def __init__(
|
|
|
|
| 143 |
max_tokens: int = 1024,
|
| 144 |
system_prompt: Optional[str] = None,
|
| 145 |
):
|
| 146 |
+
"""Initialize a minimal Claude model wrapper"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
# Get API key from env if not provided
|
| 148 |
if api_key is None:
|
| 149 |
api_key = os.getenv("ANTHROPIC_API_KEY")
|
|
|
|
| 160 |
Your answers should be precise, direct, and exactly match the expected format.
|
| 161 |
All answers are graded by exact string match, so format carefully!"""
|
| 162 |
|
| 163 |
+
print(f"Initialized SimpleClaudeModel with {model_id}")
|
| 164 |
|
| 165 |
def __call__(self, prompt: str, **kwargs) -> str:
|
| 166 |
+
"""Call method to make this class callable by smolagents CodeAgent"""
|
| 167 |
+
# Directly use the call_claude function
|
| 168 |
+
return call_claude(
|
| 169 |
+
prompt=prompt,
|
| 170 |
+
system_prompt=self.system_prompt,
|
| 171 |
+
temperature=self.temperature,
|
| 172 |
+
max_tokens=self.max_tokens,
|
| 173 |
+
model_name=self.model_id
|
| 174 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
# --------------------------------------------------------------------------- #
|
| 177 |
# custom tool: fetch GAIA attachments
|
|
|
|
| 393 |
if self.verbose:
|
| 394 |
print(f"Using Anthropic token: {api_key[:5]}...")
|
| 395 |
|
| 396 |
+
# Initialize Claude model with our simplified wrapper
|
| 397 |
+
self.model = SimpleClaudeModel(
|
| 398 |
model_id="anthropic/claude-3-5-sonnet-20240620", # Use Claude 3.5 Sonnet
|
| 399 |
api_key=api_key,
|
| 400 |
temperature=temperature,
|
|
|
|
| 403 |
)
|
| 404 |
|
| 405 |
if self.verbose:
|
| 406 |
+
print(f"Initialized model: SimpleClaudeModel - claude-3-5-sonnet-20240620")
|
| 407 |
|
| 408 |
# Initialize default tools
|
| 409 |
self.tools = [
|