From b327b6c208849dea09927f6dc3c2099b20b0472f Mon Sep 17 00:00:00 2001 From: Samuel Lawrentz Date: Tue, 17 Mar 2026 15:47:29 +0530 Subject: [PATCH] Fix SessionResource to use flat /AgentSession endpoints matching backend API The backend uses account-scoped /v1/AgentSession routes, not nested /Agent/{uuid}/Session. Align the SDK: list() no longer requires agent_uuid (use agent_id filter param instead), get() takes only session_id, and all backend filters (phone_number, call_uuid, date/duration ranges) are documented. Fixes SER-4434 --- src/plivo_agentstack/agent/client.py | 28 +++++++++++++------ tests/test_agent/test_client.py | 40 +++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/plivo_agentstack/agent/client.py b/src/plivo_agentstack/agent/client.py index 0df47ab..0180bfc 100644 --- a/src/plivo_agentstack/agent/client.py +++ b/src/plivo_agentstack/agent/client.py @@ -220,25 +220,37 @@ async def unassign(self, agent_uuid: str, number: str) -> None: class SessionResource: - """Session history -- list and get agent sessions.""" + """Session history -- list and get agent sessions. + + Sessions are account-scoped (not nested under an agent). + Use ``agent_id`` query param to filter by agent. + + Session IDs accept both raw UUIDs and ``as_`` prefix format. + """ def __init__(self, http: HttpTransport, prefix: str) -> None: self._http = http self._prefix = prefix - async def list(self, agent_uuid: str, **params: Any) -> dict: - """GET /Agent/{agent_uuid}/Session -- list sessions. + async def list(self, **params: Any) -> dict: + """GET /AgentSession -- paginated list. + + Optional query params: limit, offset, sort_by, sort_order, + agent_id, agent_mode, call_uuid, phone_number, + ended_at__gte, ended_at__lte, created_at__gte, created_at__lte, + duration__gte, duration__lte. - Optional query params: limit, offset, sort_by, sort_order, agent_mode. + Returns ``{"api_id": "...", "objects": [...], + "meta": {"limit", "offset", "total_count", "previous", "next"}}``. """ return await self._http.request( - "GET", f"{self._prefix}/Agent/{agent_uuid}/Session", params=params + "GET", f"{self._prefix}/AgentSession", params=params ) - async def get(self, agent_uuid: str, session_id: str) -> dict: - """GET /Agent/{agent_uuid}/Session/{session_id} -- get session details.""" + async def get(self, session_id: str) -> dict: + """GET /AgentSession/{session_id} -- get session details.""" return await self._http.request( - "GET", f"{self._prefix}/Agent/{agent_uuid}/Session/{session_id}" + "GET", f"{self._prefix}/AgentSession/{session_id}" ) diff --git a/tests/test_agent/test_client.py b/tests/test_agent/test_client.py index a8e718d..90be0dd 100644 --- a/tests/test_agent/test_client.py +++ b/tests/test_agent/test_client.py @@ -172,8 +172,8 @@ async def test_number_unassign(mock_api, http_transport): async def test_session_list(mock_api, http_transport): - """GET /v1/Account/TESTAUTH123/Agent/{uuid}/Session lists sessions.""" - mock_api.get(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Session").mock( + """GET /v1/Account/TESTAUTH123/AgentSession lists sessions.""" + mock_api.get("/v1/Account/TESTAUTH123/AgentSession").mock( return_value=httpx.Response( 200, json={ @@ -192,14 +192,42 @@ async def test_session_list(mock_api, http_transport): ) ) client = AgentClient(http_transport) - result = await client.sessions.list(AGENT_UUID, limit=10, offset=0) + result = await client.sessions.list(limit=10, offset=0) assert result["meta"]["total_count"] == 1 assert result["objects"][0]["agent_session_id"] == SESSION_ID +async def test_session_list_with_filters(mock_api, http_transport): + """GET /v1/Account/TESTAUTH123/AgentSession passes filter params.""" + mock_api.get("/v1/Account/TESTAUTH123/AgentSession").mock( + return_value=httpx.Response( + 200, + json={ + "api_id": "abc-123", + "objects": [], + "meta": { + "limit": 20, + "offset": 0, + "total_count": 0, + "previous": None, + "next": None, + }, + }, + ) + ) + client = AgentClient(http_transport) + result = await client.sessions.list( + agent_id=AGENT_UUID, phone_number="+14155551234" + ) + assert result["meta"]["total_count"] == 0 + request = mock_api.calls[0].request + assert request.url.params["agent_id"] == AGENT_UUID + assert request.url.params["phone_number"] == "+14155551234" + + async def test_session_get(mock_api, http_transport): - """GET /v1/Account/TESTAUTH123/Agent/{uuid}/Session/{session_id} gets session details.""" - mock_api.get(f"/v1/Account/TESTAUTH123/Agent/{AGENT_UUID}/Session/{SESSION_ID}").mock( + """GET /v1/Account/TESTAUTH123/AgentSession/{session_id} gets session details.""" + mock_api.get(f"/v1/Account/TESTAUTH123/AgentSession/{SESSION_ID}").mock( return_value=httpx.Response( 200, json={ @@ -212,7 +240,7 @@ async def test_session_get(mock_api, http_transport): ) ) client = AgentClient(http_transport) - result = await client.sessions.get(AGENT_UUID, SESSION_ID) + result = await client.sessions.get(SESSION_ID) assert result["agent_session_id"] == SESSION_ID assert result["duration_seconds"] == 120 assert result["turn_count"] == 5