Skip to main content
Glama
budget_manager.py4.57 kB
from dataclasses import dataclass import logging logger = logging.getLogger(__name__) class BudgetExceededError(Exception): """Raised when a budget limit is exceeded.""" pass @dataclass class BudgetConfig: max_queries: int = 5 max_credits: float = 100.0 max_schema_calls: int = 3 class BudgetManager: def __init__(self, config: BudgetConfig): self.config = config self.query_count = 0 self.schema_calls = 0 self.used_credits = 0.0 def check_can_execute_query(self, estimated_cost: float = 0.0) -> None: """ Check if a new query can be executed within the session budget. Raises BudgetExceededError if limits would be breached. """ # Check query count limit if self.query_count >= self.config.max_queries: raise BudgetExceededError( f"Session limit reached: Maximum {self.config.max_queries} queries allowed. " "Please search for existing queries or optimize your workflow." ) # Check credit limit if self.used_credits + estimated_cost > self.config.max_credits: remaining = self.config.max_credits - self.used_credits raise BudgetExceededError( f"Credit limit exceeded: Query costs ~{estimated_cost} credits, " f"but only {remaining:.2f} remaining in session budget." ) def check_can_access_schema(self) -> None: """ Check if schema access is allowed. """ if self.schema_calls >= self.config.max_schema_calls: raise BudgetExceededError( f"Schema access limit reached: Max {self.config.max_schema_calls} calls allowed. " "Schema lookups are expensive. Please use 'search_public_queries' instead." ) def track_execution(self, cost: float) -> None: """Record a successful execution.""" self.query_count += 1 self.used_credits += cost logger.info(f"Budget update: {self.query_count}/{self.config.max_queries} queries, " f"{self.used_credits:.2f}/{self.config.max_credits} credits used.") def track_schema_access(self) -> None: """Record a schema access.""" self.schema_calls += 1 logger.info(f"Budget update: {self.schema_calls}/{self.config.max_schema_calls} schema calls.") def sync_usage(self, actual_used: float, actual_limit: float) -> None: """ Sync local budget tracking with the actual usage reported by the Dune API. This corrects any drift from our estimations. """ # We only update 'used_credits' based on the API's report for the current period. # Note: API usage is global (monthly), while our session budget is local. # A simple strategy: If actual usage jumped significantly, we deduct that diff from our session. # But for simplicity and safety, let's just log it and perhaps warn if we are near the global limit. # Actually, for a session manager, we care about "Credits consumed THIS SESSION". # Syncing with global usage is hard unless we stored the "start_usage". # Let's just update the limit if the API says we have less remaining than we thought. remaining_global = actual_limit - actual_used remaining_session = self.config.max_credits - self.used_credits if remaining_global < remaining_session: # If global remaining is LESS than what we think we have for the session, # we must cap our session budget to reality. self.config.max_credits = self.used_credits + remaining_global logger.info(f"Budget synced: Session limit lowered to match global remaining ({remaining_global:.2f}).") def get_status(self) -> dict: return { "queries": { "used": self.query_count, "limit": self.config.max_queries, "remaining": self.config.max_queries - self.query_count }, "credits": { "used": self.used_credits, "limit": self.config.max_credits, "remaining": self.config.max_credits - self.used_credits }, "schema_calls": { "used": self.schema_calls, "limit": self.config.max_schema_calls, "remaining": self.config.max_schema_calls - self.schema_calls } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nice-bills/dune-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server