Spaces:
Sleeping
Sleeping
File size: 3,905 Bytes
68025ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | import time
import logging
import requests
from threading import Lock
from collections import deque
from config import OPENROUTER_API_KEY, OPENROUTER_BASE_URL, MAX_REQUESTS_PER_MINUTE, LLM_TIMEOUT, LLM_MAX_RETRIES
logger = logging.getLogger(__name__)
class RateLimiter:
"""Token-bucket style rate limiter: max N requests per 60s window."""
def __init__(self, max_per_minute: int = MAX_REQUESTS_PER_MINUTE):
self.max_per_minute = max_per_minute
self.timestamps: deque = deque()
self.lock = Lock()
def acquire(self):
with self.lock:
now = time.time()
# Remove timestamps older than 60s
while self.timestamps and now - self.timestamps[0] > 60:
self.timestamps.popleft()
if len(self.timestamps) >= self.max_per_minute:
sleep_for = 60 - (now - self.timestamps[0]) + 0.1
logger.info(f"Rate limit reached, sleeping {sleep_for:.1f}s")
time.sleep(sleep_for)
now = time.time()
while self.timestamps and now - self.timestamps[0] > 60:
self.timestamps.popleft()
self.timestamps.append(time.time())
_rate_limiter = RateLimiter()
class OpenRouterClient:
def __init__(self, model: str, api_key: str = OPENROUTER_API_KEY):
self.model = model
self.api_key = api_key
self.base_url = OPENROUTER_BASE_URL
def call(self, system_prompt: str, user_prompt: str) -> str:
"""Call OpenRouter with retry + exponential backoff. Returns raw text."""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
"HTTP-Referer": "https://cryptoagentbench.github.io",
"X-Title": "CryptoAgentBench",
}
payload = {
"model": self.model,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
"temperature": 0.1,
"max_tokens": 512,
}
for attempt in range(LLM_MAX_RETRIES):
_rate_limiter.acquire()
try:
resp = requests.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload,
timeout=LLM_TIMEOUT,
)
resp.raise_for_status()
data = resp.json()
content = data["choices"][0]["message"]["content"]
return content
except requests.exceptions.Timeout:
wait = 2 ** attempt
logger.warning(f"Timeout on attempt {attempt+1}, retrying in {wait}s")
time.sleep(wait)
except requests.exceptions.HTTPError as e:
status = e.response.status_code if e.response else None
if status in (429, 503, 502):
wait = 2 ** (attempt + 1)
logger.warning(f"HTTP {status} on attempt {attempt+1}, retrying in {wait}s")
time.sleep(wait)
else:
logger.error(f"HTTP error {status}: {e}")
break
except Exception as e:
logger.error(f"LLM call failed: {e}")
break
logger.error(f"All retries failed for model {self.model}, returning HOLD")
return '{"action": "HOLD", "size": 0.0, "confidence": 0.0, "reason": "LLM unavailable"}'
def ping_model(model: str, api_key: str = OPENROUTER_API_KEY) -> bool:
"""Quick check if a model is responding."""
client = OpenRouterClient(model=model, api_key=api_key)
try:
result = client.call("You are a test.", "Reply with OK")
return bool(result)
except Exception:
return False
|