mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(api): add pagination to GET /api/sessions/{id}/messages
The session messages endpoint returned ALL messages in a single response with no limit/offset. Sessions with 500+ messages produced 1.2-1.6 MB JSON payloads, causing GIL starvation and WebSocket timeouts on the Desktop client (#60155). Add optional limit/offset query params to both the API endpoint and SessionDB.get_messages(). Limit clamped to 500 max per page. Response now includes a pagination object with limit/offset/returned count. Backward compatible: callers that omit limit get the old behavior (all messages). Closes #60155
This commit is contained in:
parent
009b42d008
commit
d58396b154
2 changed files with 36 additions and 9 deletions
|
|
@ -8121,15 +8121,30 @@ async def get_session_latest_descendant(session_id: str):
|
|||
}
|
||||
|
||||
@app.get("/api/sessions/{session_id}/messages")
|
||||
async def get_session_messages(session_id: str, profile: Optional[str] = None):
|
||||
async def get_session_messages(
|
||||
session_id: str,
|
||||
profile: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: int = 0,
|
||||
):
|
||||
db = _open_session_db_for_profile(profile)
|
||||
try:
|
||||
sid = db.resolve_session_id(session_id)
|
||||
if not sid:
|
||||
raise HTTPException(status_code=404, detail="Session not found")
|
||||
sid = db.resolve_resume_session_id(sid)
|
||||
messages = db.get_messages(sid)
|
||||
return {"session_id": sid, "messages": messages}
|
||||
# Clamp limit to prevent abuse (max 500 per page)
|
||||
_limit = min(limit, 500) if limit is not None else None
|
||||
messages = db.get_messages(sid, limit=_limit, offset=offset)
|
||||
return {
|
||||
"session_id": sid,
|
||||
"messages": messages,
|
||||
"pagination": {
|
||||
"limit": _limit,
|
||||
"offset": offset,
|
||||
"returned": len(messages),
|
||||
},
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -3710,7 +3710,11 @@ class SessionDB:
|
|||
|
||||
|
||||
def get_messages(
|
||||
self, session_id: str, include_inactive: bool = False
|
||||
self,
|
||||
session_id: str,
|
||||
include_inactive: bool = False,
|
||||
limit: Optional[int] = None,
|
||||
offset: int = 0,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Load messages for a session in insertion order.
|
||||
|
||||
|
|
@ -3721,14 +3725,22 @@ class SessionDB:
|
|||
|
||||
Ordered by AUTOINCREMENT id (true insertion order) rather than
|
||||
timestamp — see c03acca50 for the WSL2 clock-regression rationale.
|
||||
|
||||
When ``limit`` is provided, returns at most ``limit`` messages
|
||||
starting from ``offset`` (0-based, in insertion order). Enables
|
||||
pagination for the API endpoint to avoid loading entire transcripts.
|
||||
"""
|
||||
active_clause = "" if include_inactive else " AND active = 1"
|
||||
sql = (
|
||||
"SELECT * FROM messages WHERE session_id = ?"
|
||||
f"{active_clause} ORDER BY id"
|
||||
)
|
||||
params: list = [session_id]
|
||||
if limit is not None:
|
||||
sql += " LIMIT ? OFFSET ?"
|
||||
params.extend([limit, offset])
|
||||
with self._lock:
|
||||
cursor = self._conn.execute(
|
||||
"SELECT * FROM messages WHERE session_id = ?"
|
||||
f"{active_clause} ORDER BY id",
|
||||
(session_id,),
|
||||
)
|
||||
cursor = self._conn.execute(sql, params)
|
||||
rows = cursor.fetchall()
|
||||
result = []
|
||||
for row in rows:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue