Skip to content

Commit

Permalink
Improve agent loop tracking and make concurrent limit configurable
Browse files Browse the repository at this point in the history
- Add max_concurrent_conversations to AppConfig (default: 3)
- Add start_time tracking to Session class
- Make get_running_agent_loops() return sessions in chronological order
- Simplify oldest session selection by using chronological order
  • Loading branch information
openhands-agent committed Feb 25, 2025
1 parent 6ba79c4 commit 5980bc8
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
1 change: 1 addition & 0 deletions openhands/core/config/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class AppConfig(BaseModel):
daytona_target: str = Field(default='us')
cli_multiline_input: bool = Field(default=False)
conversation_max_age_seconds: int = Field(default=864000) # 10 days in seconds
max_concurrent_conversations: int = Field(default=3) # Maximum number of concurrent agent loops allowed per user

defaults_dict: ClassVar[dict] = {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from .conversation_manager import ConversationManager

_CLEANUP_INTERVAL = 15
MAX_RUNNING_CONVERSATIONS = 3


@dataclass
Expand Down Expand Up @@ -157,13 +156,20 @@ async def get_running_agent_loops(
self, user_id: str | None = None, filter_to_sids: set[str] | None = None
) -> set[str]:
"""Get the running session ids. If a user is supplied, then the results are limited to session ids for that user. If a set of filter_to_sids is supplied, then results are limited to these ids of interest."""
items: Iterable[tuple[str, Session]] = self._local_agent_loops_by_sid.items()
# Get all items and convert to list for sorting
items: list[tuple[str, Session]] = list(self._local_agent_loops_by_sid.items())

# Filter items if needed
if filter_to_sids is not None:
items = (item for item in items if item[0] in filter_to_sids)
items = [item for item in items if item[0] in filter_to_sids]
if user_id:
items = (item for item in items if item[1].user_id == user_id)
sids = {sid for sid, _ in items}
return sids
items = [item for item in items if item[1].user_id == user_id]

# Sort by start_time (oldest first)
items.sort(key=lambda x: x[1].start_time)

# Convert to set of sids
return {sid for sid, _ in items}

async def get_connections(
self, user_id: str | None = None, filter_to_sids: set[str] | None = None
Expand Down Expand Up @@ -195,12 +201,12 @@ async def maybe_start_agent_loop(
logger.info(f'start_agent_loop:{sid}')

response_ids = await self.get_running_agent_loops(user_id)
if len(response_ids) >= MAX_RUNNING_CONVERSATIONS:
if len(response_ids) >= self.config.max_concurrent_conversations:
logger.info('too_many_sessions_for:{user_id}')
# Order is not guaranteed, but response_ids tend to be in descending chronological order
# By reversing, we are likely to pick the oldest (or at least an older) conversation
session_id = next(iter(reversed(list(response_ids))))
await self.close_session(session_id)
# Since get_running_agent_loops returns sessions in chronological order,
# the first session is the oldest one
oldest_session_id = next(iter(response_ids))
await self.close_session(oldest_session_id)

session = Session(
sid=sid,
Expand Down
5 changes: 4 additions & 1 deletion openhands/server/session/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Session:
sid: str
sio: socketio.AsyncServer | None
last_active_ts: int = 0
start_time: float = 0 # Timestamp when the session started
is_alive: bool = True
agent_session: AgentSession
loop: asyncio.AbstractEventLoop
Expand All @@ -52,7 +53,9 @@ def __init__(
):
self.sid = sid
self.sio = sio
self.last_active_ts = int(time.time())
current_time = time.time()
self.last_active_ts = int(current_time)
self.start_time = current_time
self.file_store = file_store
self.agent_session = AgentSession(
sid,
Expand Down

0 comments on commit 5980bc8

Please sign in to comment.