Skip to content

Commit c629c5a

Browse files
authored
fix(api): improve error handling and response format of the Conversation Observation Status API (#94)
* fix(api): improve error handling and response format of the Conversation Observation Status API * feat(docs): add endpoint for retrieving message observing status by session ID
1 parent 2e88977 commit c629c5a

File tree

6 files changed

+171
-18
lines changed

6 files changed

+171
-18
lines changed

docs/api-reference/openapi.json

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,47 @@
913913
"x-codegen-request-body-name" : "payload"
914914
}
915915
},
916+
"/session/{session_id}/observing_status" : {
917+
"get" : {
918+
"description" : "Returns the count of observed, in_process, and pending messages",
919+
"parameters" : [ {
920+
"description" : "Session ID",
921+
"in" : "path",
922+
"name" : "session_id",
923+
"required" : true,
924+
"schema" : {
925+
"format" : "uuid",
926+
"type" : "string"
927+
}
928+
} ],
929+
"responses" : {
930+
"200" : {
931+
"content" : {
932+
"application/json" : {
933+
"schema" : {
934+
"$ref" : "#/components/schemas/_session__session_id__observing_status_get_200_response"
935+
}
936+
}
937+
},
938+
"description" : "OK"
939+
}
940+
},
941+
"security" : [ {
942+
"BearerAuth" : [ ]
943+
} ],
944+
"summary" : "Get message observing status for a session",
945+
"tags" : [ "session" ],
946+
"x-code-samples" : [ {
947+
"label" : "Python",
948+
"lang" : "python",
949+
"source" : "from acontext import AcontextClient\n\nclient = AcontextClient(api_key='sk_project_token')\n\n# Get message observing status\nresult = client.sessions.messages_observing_status(session_id='session-uuid')\nprint(f\"Observed: {result.observed}, In Process: {result.in_process}, Pending: {result.pending}\")\n"
950+
}, {
951+
"label" : "JavaScript",
952+
"lang" : "javascript",
953+
"source" : "import { AcontextClient } from '@acontext/acontext';\n\nconst client = new AcontextClient({ apiKey: 'sk_project_token' });\n\n// Get message observing status\nconst result = await client.sessions.messagesObservingStatus('session-uuid');\nconsole.log(`Observed: ${result.observed}, In Process: ${result.in_process}, Pending: ${result.pending}`);\n"
954+
} ]
955+
}
956+
},
916957
"/session/{session_id}/task" : {
917958
"get" : {
918959
"description" : "Get tasks from session with cursor-based pagination",
@@ -2360,6 +2401,23 @@
23602401
},
23612402
"type" : "object"
23622403
},
2404+
"model.MessageObservingStatus" : {
2405+
"properties" : {
2406+
"in_process" : {
2407+
"type" : "integer"
2408+
},
2409+
"observed" : {
2410+
"type" : "integer"
2411+
},
2412+
"pending" : {
2413+
"type" : "integer"
2414+
},
2415+
"updated_at" : {
2416+
"type" : "string"
2417+
}
2418+
},
2419+
"type" : "object"
2420+
},
23632421
"model.Session" : {
23642422
"properties" : {
23652423
"configs" : {
@@ -2777,6 +2835,18 @@
27772835
"type" : "object"
27782836
} ]
27792837
},
2838+
"_session__session_id__observing_status_get_200_response" : {
2839+
"allOf" : [ {
2840+
"$ref" : "#/components/schemas/serializer.Response"
2841+
}, {
2842+
"properties" : {
2843+
"data" : {
2844+
"$ref" : "#/components/schemas/model.MessageObservingStatus"
2845+
}
2846+
},
2847+
"type" : "object"
2848+
} ]
2849+
},
27802850
"_session__session_id__task_get_200_response" : {
27812851
"allOf" : [ {
27822852
"$ref" : "#/components/schemas/serializer.Response"

src/server/api/go/docs/docs.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1292,7 +1292,19 @@ const docTemplate = `{
12921292
]
12931293
}
12941294
}
1295-
}
1295+
},
1296+
"x-code-samples": [
1297+
{
1298+
"label": "Python",
1299+
"lang": "python",
1300+
"source": "from acontext import AcontextClient\n\nclient = AcontextClient(api_key='sk_project_token')\n\n# Get message observing status\nresult = client.sessions.messages_observing_status(session_id='session-uuid')\nprint(f\"Observed: {result.observed}, In Process: {result.in_process}, Pending: {result.pending}\")\n"
1301+
},
1302+
{
1303+
"label": "JavaScript",
1304+
"lang": "javascript",
1305+
"source": "import { AcontextClient } from '@acontext/acontext';\n\nconst client = new AcontextClient({ apiKey: 'sk_project_token' });\n\n// Get message observing status\nconst result = await client.sessions.messagesObservingStatus('session-uuid');\nconsole.log(` + "`" + `Observed: ${result.observed}, In Process: ${result.in_process}, Pending: ${result.pending}` + "`" + `);\n"
1306+
}
1307+
]
12961308
}
12971309
},
12981310
"/session/{session_id}/task": {

src/server/api/go/docs/swagger.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,19 @@
12891289
]
12901290
}
12911291
}
1292-
}
1292+
},
1293+
"x-code-samples": [
1294+
{
1295+
"label": "Python",
1296+
"lang": "python",
1297+
"source": "from acontext import AcontextClient\n\nclient = AcontextClient(api_key='sk_project_token')\n\n# Get message observing status\nresult = client.sessions.messages_observing_status(session_id='session-uuid')\nprint(f\"Observed: {result.observed}, In Process: {result.in_process}, Pending: {result.pending}\")\n"
1298+
},
1299+
{
1300+
"label": "JavaScript",
1301+
"lang": "javascript",
1302+
"source": "import { AcontextClient } from '@acontext/acontext';\n\nconst client = new AcontextClient({ apiKey: 'sk_project_token' });\n\n// Get message observing status\nconst result = await client.sessions.messagesObservingStatus('session-uuid');\nconsole.log(`Observed: ${result.observed}, In Process: ${result.in_process}, Pending: ${result.pending}`);\n"
1303+
}
1304+
]
12931305
}
12941306
},
12951307
"/session/{session_id}/task": {

src/server/api/go/docs/swagger.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,27 @@ paths:
16471647
summary: Get message observing status for a session
16481648
tags:
16491649
- session
1650+
x-code-samples:
1651+
- label: Python
1652+
lang: python
1653+
source: |
1654+
from acontext import AcontextClient
1655+
1656+
client = AcontextClient(api_key='sk_project_token')
1657+
1658+
# Get message observing status
1659+
result = client.sessions.messages_observing_status(session_id='session-uuid')
1660+
print(f"Observed: {result.observed}, In Process: {result.in_process}, Pending: {result.pending}")
1661+
- label: JavaScript
1662+
lang: javascript
1663+
source: |
1664+
import { AcontextClient } from '@acontext/acontext';
1665+
1666+
const client = new AcontextClient({ apiKey: 'sk_project_token' });
1667+
1668+
// Get message observing status
1669+
const result = await client.sessions.messagesObservingStatus('session-uuid');
1670+
console.log(`Observed: ${result.observed}, In Process: ${result.in_process}, Pending: ${result.pending}`);
16501671
/session/{session_id}/task:
16511672
get:
16521673
consumes:

src/server/api/go/internal/modules/handler/session.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -685,23 +685,19 @@ func (h *SessionHandler) GetTokenCounts(c *gin.Context) {
685685
// @Security BearerAuth
686686
// @Success 200 {object} serializer.Response{data=model.MessageObservingStatus}
687687
// @Router /session/{session_id}/observing_status [get]
688+
// @x-code-samples [{"lang":"python","source":"from acontext import AcontextClient\n\nclient = AcontextClient(api_key='sk_project_token')\n\n# Get message observing status\nresult = client.sessions.messages_observing_status(session_id='session-uuid')\nprint(f\"Observed: {result.observed}, In Process: {result.in_process}, Pending: {result.pending}\")\n","label":"Python"},{"lang":"javascript","source":"import { AcontextClient } from '@acontext/acontext';\n\nconst client = new AcontextClient({ apiKey: 'sk_project_token' });\n\n// Get message observing status\nconst result = await client.sessions.messagesObservingStatus('session-uuid');\nconsole.log(`Observed: ${result.observed}, In Process: ${result.in_process}, Pending: ${result.pending}`);\n","label":"JavaScript"}]
688689
func (h *SessionHandler) GetSessionObservingStatus(c *gin.Context) {
689-
sessionID := c.Param("session_id")
690-
691-
if sessionID == "" {
692-
c.JSON(http.StatusBadRequest, gin.H{
693-
"error": "session_id is required",
694-
})
690+
sessionID, err := uuid.Parse(c.Param("session_id"))
691+
if err != nil {
692+
c.JSON(http.StatusBadRequest, serializer.ParamErr("", err))
695693
return
696694
}
697695

698-
status, err := h.svc.GetSessionObservingStatus(c.Request.Context(), sessionID)
696+
status, err := h.svc.GetSessionObservingStatus(c.Request.Context(), sessionID.String())
699697
if err != nil {
700-
c.JSON(http.StatusInternalServerError, gin.H{
701-
"error": err.Error(),
702-
})
698+
c.JSON(http.StatusInternalServerError, serializer.DBErr("", err))
703699
return
704700
}
705701

706-
c.JSON(http.StatusOK, status)
702+
c.JSON(http.StatusOK, serializer.Response{Data: status})
707703
}

src/server/api/go/internal/modules/handler/session_test.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3584,9 +3584,17 @@ func TestSessionHandler_GetSessionObservingStatus_Success(t *testing.T) {
35843584

35853585
assert.Equal(t, http.StatusOK, w.Code)
35863586
mockService.AssertExpectations(t)
3587-
assert.Contains(t, w.Body.String(), `"observed":10`)
3588-
assert.Contains(t, w.Body.String(), `"in_process":5`)
3589-
assert.Contains(t, w.Body.String(), `"pending":3`)
3587+
3588+
// Verify response format: serializer.Response{Data: status}
3589+
var response map[string]interface{}
3590+
err := sonic.Unmarshal(w.Body.Bytes(), &response)
3591+
require.NoError(t, err)
3592+
3593+
data, ok := response["data"].(map[string]interface{})
3594+
require.True(t, ok, "Should have data field")
3595+
assert.Equal(t, float64(10), data["observed"])
3596+
assert.Equal(t, float64(5), data["in_process"])
3597+
assert.Equal(t, float64(3), data["pending"])
35903598
}
35913599

35923600
func TestSessionHandler_GetSessionObservingStatus_EmptySessionID(t *testing.T) {
@@ -3606,7 +3614,36 @@ func TestSessionHandler_GetSessionObservingStatus_EmptySessionID(t *testing.T) {
36063614
handler.GetSessionObservingStatus(c)
36073615

36083616
assert.Equal(t, http.StatusBadRequest, w.Code)
3609-
assert.Contains(t, w.Body.String(), "session_id is required")
3617+
// Now uses uuid.Parse which will return invalid UUID format error
3618+
var response map[string]interface{}
3619+
err := sonic.Unmarshal(w.Body.Bytes(), &response)
3620+
require.NoError(t, err)
3621+
assert.NotEmpty(t, response["error"])
3622+
mockService.AssertNotCalled(t, "GetSessionObservingStatus")
3623+
}
3624+
3625+
func TestSessionHandler_GetSessionObservingStatus_InvalidSessionID(t *testing.T) {
3626+
gin.SetMode(gin.TestMode)
3627+
3628+
mockService := new(MockSessionService)
3629+
handler := NewSessionHandler(mockService, getMockSessionCoreClient())
3630+
3631+
w := httptest.NewRecorder()
3632+
c, _ := gin.CreateTestContext(w)
3633+
c.Params = gin.Params{
3634+
{Key: "session_id", Value: "invalid-uuid"},
3635+
}
3636+
req, _ := http.NewRequest("GET", "/session/invalid-uuid/observing_status", nil)
3637+
c.Request = req
3638+
3639+
handler.GetSessionObservingStatus(c)
3640+
3641+
assert.Equal(t, http.StatusBadRequest, w.Code)
3642+
// uuid.Parse will return invalid UUID format error
3643+
var response map[string]interface{}
3644+
err := sonic.Unmarshal(w.Body.Bytes(), &response)
3645+
require.NoError(t, err)
3646+
assert.NotEmpty(t, response["error"])
36103647
mockService.AssertNotCalled(t, "GetSessionObservingStatus")
36113648
}
36123649

@@ -3633,6 +3670,11 @@ func TestSessionHandler_GetSessionObservingStatus_ServiceError(t *testing.T) {
36333670
handler.GetSessionObservingStatus(c)
36343671

36353672
assert.Equal(t, http.StatusInternalServerError, w.Code)
3636-
assert.Contains(t, w.Body.String(), "database connection failed")
3673+
// Verify response format: serializer.DBErr returns Response with error field
3674+
var response map[string]interface{}
3675+
err := sonic.Unmarshal(w.Body.Bytes(), &response)
3676+
require.NoError(t, err)
3677+
assert.NotEmpty(t, response["error"])
3678+
assert.Contains(t, response["error"].(string), "database connection failed")
36373679
mockService.AssertExpectations(t)
36383680
}

0 commit comments

Comments
 (0)