71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
"""Configuration and safety limits for the sentiment agent.
|
|
|
|
All guardrails are centralized here so they can be tuned from one place
|
|
or overridden via CLI flags / env vars.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RateLimitConfig:
|
|
"""Per-platform rate limiting."""
|
|
|
|
requests_per_minute: int = 10
|
|
burst_size: int = 3 # max concurrent requests
|
|
cooldown_after_429: float = 30.0 # seconds to wait after a 429
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SafetyConfig:
|
|
"""Top-level safety rails for the agent."""
|
|
|
|
# --- Agent-level limits ---
|
|
max_turns: int = 20
|
|
max_budget_usd: float = 0.50 # hard cap on Claude API spend per run
|
|
max_total_api_calls: int = 50 # across ALL platforms combined
|
|
max_results_per_call: int = 50 # cap the `limit` param sent to any API
|
|
|
|
# --- Per-platform rate limits ---
|
|
bluesky_rate: RateLimitConfig = field(default_factory=lambda: RateLimitConfig(
|
|
requests_per_minute=10, burst_size=2,
|
|
))
|
|
reddit_rate: RateLimitConfig = field(default_factory=lambda: RateLimitConfig(
|
|
requests_per_minute=10, burst_size=2,
|
|
))
|
|
hackernews_rate: RateLimitConfig = field(default_factory=lambda: RateLimitConfig(
|
|
requests_per_minute=15, burst_size=3, # HN Algolia is more generous
|
|
))
|
|
|
|
# --- Content size limits ---
|
|
max_post_text_chars: int = 2000 # truncate individual posts beyond this
|
|
max_total_content_bytes: int = 500_000 # ~500KB total data gathered before agent stops
|
|
|
|
# --- Timeout ---
|
|
api_timeout_seconds: float = 15.0
|
|
|
|
# --- Credibility thresholds ---
|
|
min_credibility_score: float = 0.3 # posts below this are flagged/excluded
|
|
flag_bot_threshold: float = 0.5 # posts between min and this are flagged but included
|
|
|
|
@classmethod
|
|
def from_env(cls) -> SafetyConfig:
|
|
"""Build config with env var overrides.
|
|
|
|
Env vars: SENTIMENT_MAX_TURNS, SENTIMENT_MAX_BUDGET_USD,
|
|
SENTIMENT_MAX_API_CALLS, SENTIMENT_MIN_CREDIBILITY.
|
|
"""
|
|
kwargs: dict = {}
|
|
if v := os.environ.get("SENTIMENT_MAX_TURNS"):
|
|
kwargs["max_turns"] = int(v)
|
|
if v := os.environ.get("SENTIMENT_MAX_BUDGET_USD"):
|
|
kwargs["max_budget_usd"] = float(v)
|
|
if v := os.environ.get("SENTIMENT_MAX_API_CALLS"):
|
|
kwargs["max_total_api_calls"] = int(v)
|
|
if v := os.environ.get("SENTIMENT_MIN_CREDIBILITY"):
|
|
kwargs["min_credibility_score"] = float(v)
|
|
return cls(**kwargs)
|