mirror of https://github.com/kortix-ai/suna.git
refactor(agent-run-limits): centralize agent run limit configuration
- Removed hardcoded maximum parallel agent runs and replaced with a configurable property in the Configuration class. - Updated API and agent limit checks to utilize the new configuration property. - Simplified error handling by removing limit from error details in the frontend components. - Adjusted UI components to reflect changes in agent limit data structure.
This commit is contained in:
parent
43bc71779e
commit
ddef5fab19
|
@ -25,7 +25,7 @@ from utils.constants import MODEL_NAME_ALIASES
|
|||
from flags.flags import is_enabled
|
||||
|
||||
from .config_helper import extract_agent_config, build_unified_config, extract_tools_for_agent_run, get_mcp_configs
|
||||
from .utils import check_agent_run_limit, MAX_PARALLEL_AGENT_RUNS
|
||||
from .utils import check_agent_run_limit
|
||||
from .versioning.version_service import get_version_service
|
||||
from .versioning.api import router as version_router, initialize as initialize_versioning
|
||||
|
||||
|
@ -442,10 +442,10 @@ async def start_agent(
|
|||
limit_check = await check_agent_run_limit(client, account_id)
|
||||
if not limit_check['can_start']:
|
||||
error_detail = {
|
||||
"message": f"Maximum of {MAX_PARALLEL_AGENT_RUNS} parallel agent runs allowed within 24 hours. You currently have {limit_check['running_count']} running.",
|
||||
"message": f"Maximum of {config.MAX_PARALLEL_AGENT_RUNS} parallel agent runs allowed within 24 hours. You currently have {limit_check['running_count']} running.",
|
||||
"running_thread_ids": limit_check['running_thread_ids'],
|
||||
"running_count": limit_check['running_count'],
|
||||
"limit": MAX_PARALLEL_AGENT_RUNS
|
||||
"limit": config.MAX_PARALLEL_AGENT_RUNS
|
||||
}
|
||||
logger.warning(f"Agent run limit exceeded for account {account_id}: {limit_check['running_count']} running agents")
|
||||
raise HTTPException(status_code=429, detail=error_detail)
|
||||
|
@ -1066,10 +1066,10 @@ async def initiate_agent_with_files(
|
|||
limit_check = await check_agent_run_limit(client, account_id)
|
||||
if not limit_check['can_start']:
|
||||
error_detail = {
|
||||
"message": f"Maximum of {MAX_PARALLEL_AGENT_RUNS} parallel agent runs allowed within 24 hours. You currently have {limit_check['running_count']} running.",
|
||||
"message": f"Maximum of {config.MAX_PARALLEL_AGENT_RUNS} parallel agent runs allowed within 24 hours. You currently have {limit_check['running_count']} running.",
|
||||
"running_thread_ids": limit_check['running_thread_ids'],
|
||||
"running_count": limit_check['running_count'],
|
||||
"limit": MAX_PARALLEL_AGENT_RUNS
|
||||
"limit": config.MAX_PARALLEL_AGENT_RUNS
|
||||
}
|
||||
logger.warning(f"Agent run limit exceeded for account {account_id}: {limit_check['running_count']} running agents")
|
||||
raise HTTPException(status_code=429, detail=error_detail)
|
||||
|
|
|
@ -2,12 +2,10 @@ import json
|
|||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from utils.logger import logger
|
||||
from utils.config import config
|
||||
from services import redis
|
||||
from run_agent_background import update_agent_run_status
|
||||
|
||||
# Agent run limits
|
||||
MAX_PARALLEL_AGENT_RUNS = 3
|
||||
|
||||
|
||||
async def _cleanup_redis_response_list(agent_run_id: str):
|
||||
try:
|
||||
|
@ -120,7 +118,7 @@ async def check_agent_run_limit(client, account_id: str) -> Dict[str, Any]:
|
|||
logger.info(f"Account {account_id} has {running_count} running agent runs in the past 24 hours")
|
||||
|
||||
return {
|
||||
'can_start': running_count < MAX_PARALLEL_AGENT_RUNS,
|
||||
'can_start': running_count < config.MAX_PARALLEL_AGENT_RUNS,
|
||||
'running_count': running_count,
|
||||
'running_thread_ids': running_thread_ids
|
||||
}
|
||||
|
|
|
@ -267,7 +267,34 @@ class Configuration:
|
|||
# API Keys system configuration
|
||||
API_KEY_SECRET: str = "default-secret-key-change-in-production"
|
||||
API_KEY_LAST_USED_THROTTLE_SECONDS: int = 900
|
||||
|
||||
# Agent execution limits (can be overridden via environment variable)
|
||||
_MAX_PARALLEL_AGENT_RUNS_ENV: Optional[str] = None
|
||||
|
||||
@property
|
||||
def MAX_PARALLEL_AGENT_RUNS(self) -> int:
|
||||
"""
|
||||
Get the maximum parallel agent runs limit.
|
||||
|
||||
Can be overridden via MAX_PARALLEL_AGENT_RUNS environment variable.
|
||||
Defaults:
|
||||
- Production: 3
|
||||
- Local/Staging: 999999 (effectively infinite)
|
||||
"""
|
||||
# Check for environment variable override first
|
||||
if self._MAX_PARALLEL_AGENT_RUNS_ENV is not None:
|
||||
try:
|
||||
return int(self._MAX_PARALLEL_AGENT_RUNS_ENV)
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid MAX_PARALLEL_AGENT_RUNS value: {self._MAX_PARALLEL_AGENT_RUNS_ENV}, using default")
|
||||
|
||||
# Environment-based defaults
|
||||
if self.ENV_MODE == EnvMode.PRODUCTION:
|
||||
return 3
|
||||
else:
|
||||
# Local and staging: effectively infinite
|
||||
return 999999
|
||||
|
||||
@property
|
||||
def STRIPE_PRODUCT_ID(self) -> str:
|
||||
if self.ENV_MODE == EnvMode.STAGING:
|
||||
|
@ -317,6 +344,11 @@ class Configuration:
|
|||
else:
|
||||
# String or other type
|
||||
setattr(self, key, env_val)
|
||||
|
||||
# Custom handling for environment-dependent properties
|
||||
max_parallel_runs_env = os.getenv("MAX_PARALLEL_AGENT_RUNS")
|
||||
if max_parallel_runs_env is not None:
|
||||
self._MAX_PARALLEL_AGENT_RUNS_ENV = max_parallel_runs_env
|
||||
|
||||
def _validate(self):
|
||||
"""Validate configuration based on type hints."""
|
||||
|
|
|
@ -59,7 +59,6 @@ export default function ThreadPage({
|
|||
const [showAgentLimitDialog, setShowAgentLimitDialog] = useState(false);
|
||||
const [agentLimitData, setAgentLimitData] = useState<{
|
||||
runningCount: number;
|
||||
limit: number;
|
||||
runningThreadIds: string[];
|
||||
} | null>(null);
|
||||
|
||||
|
@ -329,12 +328,11 @@ export default function ThreadPage({
|
|||
|
||||
if (error instanceof AgentRunLimitError) {
|
||||
console.log("Caught AgentRunLimitError:", error.detail);
|
||||
const { running_thread_ids, running_count, limit } = error.detail;
|
||||
const { running_thread_ids, running_count } = error.detail;
|
||||
|
||||
// Show the dialog with limit information
|
||||
setAgentLimitData({
|
||||
runningCount: running_count,
|
||||
limit: limit,
|
||||
runningThreadIds: running_thread_ids,
|
||||
});
|
||||
setShowAgentLimitDialog(true);
|
||||
|
@ -764,7 +762,6 @@ export default function ThreadPage({
|
|||
open={showAgentLimitDialog}
|
||||
onOpenChange={setShowAgentLimitDialog}
|
||||
runningCount={agentLimitData.runningCount}
|
||||
limit={agentLimitData.limit}
|
||||
runningThreadIds={agentLimitData.runningThreadIds}
|
||||
projectId={projectId}
|
||||
/>
|
||||
|
|
|
@ -48,7 +48,6 @@ export function DashboardContent() {
|
|||
const [showAgentLimitDialog, setShowAgentLimitDialog] = useState(false);
|
||||
const [agentLimitData, setAgentLimitData] = useState<{
|
||||
runningCount: number;
|
||||
limit: number;
|
||||
runningThreadIds: string[];
|
||||
} | null>(null);
|
||||
const router = useRouter();
|
||||
|
@ -163,12 +162,11 @@ export function DashboardContent() {
|
|||
onOpen("paymentRequiredDialog");
|
||||
} else if (error instanceof AgentRunLimitError) {
|
||||
console.log('Handling AgentRunLimitError:', error.detail);
|
||||
const { running_thread_ids, running_count, limit } = error.detail;
|
||||
const { running_thread_ids, running_count } = error.detail;
|
||||
|
||||
// Show the dialog with limit information
|
||||
setAgentLimitData({
|
||||
runningCount: running_count,
|
||||
limit: limit,
|
||||
runningThreadIds: running_thread_ids,
|
||||
});
|
||||
setShowAgentLimitDialog(true);
|
||||
|
@ -282,7 +280,6 @@ export function DashboardContent() {
|
|||
open={showAgentLimitDialog}
|
||||
onOpenChange={setShowAgentLimitDialog}
|
||||
runningCount={agentLimitData.runningCount}
|
||||
limit={agentLimitData.limit}
|
||||
runningThreadIds={agentLimitData.runningThreadIds}
|
||||
projectId={undefined} // Dashboard doesn't have a specific project context
|
||||
/>
|
||||
|
|
|
@ -72,7 +72,6 @@ export function HeroSection() {
|
|||
const [showAgentLimitDialog, setShowAgentLimitDialog] = useState(false);
|
||||
const [agentLimitData, setAgentLimitData] = useState<{
|
||||
runningCount: number;
|
||||
limit: number;
|
||||
runningThreadIds: string[];
|
||||
} | null>(null);
|
||||
|
||||
|
@ -209,12 +208,11 @@ export function HeroSection() {
|
|||
onOpen("paymentRequiredDialog");
|
||||
} else if (error instanceof AgentRunLimitError) {
|
||||
console.log('Handling AgentRunLimitError:', error.detail);
|
||||
const { running_thread_ids, running_count, limit } = error.detail;
|
||||
const { running_thread_ids, running_count } = error.detail;
|
||||
|
||||
// Show the dialog with limit information
|
||||
setAgentLimitData({
|
||||
runningCount: running_count,
|
||||
limit: limit,
|
||||
runningThreadIds: running_thread_ids,
|
||||
});
|
||||
setShowAgentLimitDialog(true);
|
||||
|
@ -439,7 +437,6 @@ export function HeroSection() {
|
|||
open={showAgentLimitDialog}
|
||||
onOpenChange={setShowAgentLimitDialog}
|
||||
runningCount={agentLimitData.runningCount}
|
||||
limit={agentLimitData.limit}
|
||||
runningThreadIds={agentLimitData.runningThreadIds}
|
||||
projectId={undefined} // Hero section doesn't have a specific project context
|
||||
/>
|
||||
|
|
|
@ -148,7 +148,6 @@ interface AgentRunLimitDialogProps {
|
|||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
runningCount: number;
|
||||
limit: number;
|
||||
runningThreadIds: string[];
|
||||
projectId?: string;
|
||||
}
|
||||
|
@ -157,7 +156,6 @@ export const AgentRunLimitDialog: React.FC<AgentRunLimitDialogProps> = ({
|
|||
open,
|
||||
onOpenChange,
|
||||
runningCount,
|
||||
limit,
|
||||
runningThreadIds,
|
||||
projectId,
|
||||
}) => {
|
||||
|
|
|
@ -4,9 +4,6 @@ import { handleApiError } from './error-handler';
|
|||
// Get backend URL from environment variables
|
||||
const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || '';
|
||||
|
||||
// Agent run limits
|
||||
const MAX_PARALLEL_AGENT_RUNS = 3;
|
||||
|
||||
// Set to keep track of agent runs that are known to be non-running
|
||||
const nonRunningAgentRuns = new Set<string>();
|
||||
// Map to keep track of active EventSource streams
|
||||
|
@ -39,8 +36,6 @@ export class AgentRunLimitError extends Error {
|
|||
message: string;
|
||||
running_thread_ids: string[];
|
||||
running_count: number;
|
||||
limit: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
constructor(
|
||||
|
@ -49,7 +44,6 @@ export class AgentRunLimitError extends Error {
|
|||
message: string;
|
||||
running_thread_ids: string[];
|
||||
running_count: number;
|
||||
limit: number;
|
||||
[key: string]: any;
|
||||
},
|
||||
message?: string,
|
||||
|
@ -751,7 +745,6 @@ export const startAgent = async (
|
|||
message: 'Too many agent runs running',
|
||||
running_thread_ids: [],
|
||||
running_count: 0,
|
||||
limit: MAX_PARALLEL_AGENT_RUNS
|
||||
};
|
||||
if (typeof detail.message !== 'string') {
|
||||
detail.message = 'Too many agent runs running';
|
||||
|
@ -762,9 +755,6 @@ export const startAgent = async (
|
|||
if (typeof detail.running_count !== 'number') {
|
||||
detail.running_count = 0;
|
||||
}
|
||||
if (typeof detail.limit !== 'number') {
|
||||
detail.limit = MAX_PARALLEL_AGENT_RUNS;
|
||||
}
|
||||
throw new AgentRunLimitError(response.status, detail);
|
||||
}
|
||||
|
||||
|
@ -1608,7 +1598,6 @@ export const initiateAgent = async (
|
|||
message: 'Too many agent runs running',
|
||||
running_thread_ids: [],
|
||||
running_count: 0,
|
||||
limit: MAX_PARALLEL_AGENT_RUNS
|
||||
};
|
||||
if (typeof detail.message !== 'string') {
|
||||
detail.message = 'Too many agent runs running';
|
||||
|
@ -1619,9 +1608,6 @@ export const initiateAgent = async (
|
|||
if (typeof detail.running_count !== 'number') {
|
||||
detail.running_count = 0;
|
||||
}
|
||||
if (typeof detail.limit !== 'number') {
|
||||
detail.limit = MAX_PARALLEL_AGENT_RUNS;
|
||||
}
|
||||
throw new AgentRunLimitError(response.status, detail);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue