1919    ConversationResponse ,
2020    ConversationsListResponse ,
2121    UnauthorizedResponse ,
22+     NotFoundResponse ,
23+     AccessDeniedResponse ,
24+     BadRequestResponse ,
25+     ServiceUnavailableResponse ,
2226)
2327from  utils .endpoints  import  (
2428    check_configuration_loaded ,
2529    delete_conversation ,
26-     validate_conversation_ownership ,
30+     can_access_conversation ,
31+     retrieve_conversation ,
2732)
2833from  utils .suid  import  check_suid 
2934
3237
3338conversation_responses : dict [int  |  str , dict [str , Any ]] =  {
3439    200 : {
35-         "conversation_id" : "123e4567-e89b-12d3-a456-426614174000" ,
36-         "chat_history" : [
37-             {
38-                 "messages" : [
39-                     {"content" : "Hi" , "type" : "user" },
40-                     {"content" : "Hello!" , "type" : "assistant" },
41-                 ],
42-                 "started_at" : "2024-01-01T00:00:00Z" ,
43-                 "completed_at" : "2024-01-01T00:00:05Z" ,
44-             }
45-         ],
40+         "model" : ConversationResponse ,
41+         "description" : "Conversation retrieved successfully" ,
4642    },
4743    400 : {
48-         "description " : "Missing or invalid credentials provided by client" ,
49-         "model " : UnauthorizedResponse ,
44+         "model " : BadRequestResponse ,
45+         "description " : "Invalid request" ,
5046    },
5147    401 : {
52-         "description" : "Unauthorized: Invalid or missing Bearer token" ,
5348        "model" : UnauthorizedResponse ,
49+         "description" : "Unauthorized: Invalid or missing Bearer token" ,
50+     },
51+     403 : {
52+         "model" : AccessDeniedResponse ,
53+         "description" : "Client does not have permission to access conversation" ,
5454    },
5555    404 : {
56-         "detail" : {
57-             "response" : "Conversation not found" ,
58-             "cause" : "The specified conversation ID does not exist." ,
59-         }
56+         "model" : NotFoundResponse ,
57+         "description" : "Conversation not found" ,
6058    },
6159    503 : {
62-         "detail" : {
63-             "response" : "Unable to connect to Llama Stack" ,
64-             "cause" : "Connection error." ,
65-         }
60+         "model" : ServiceUnavailableResponse ,
61+         "description" : "Service unavailable" ,
6662    },
6763}
6864
6965conversation_delete_responses : dict [int  |  str , dict [str , Any ]] =  {
7066    200 : {
71-         "conversation_id" : "123e4567-e89b-12d3-a456-426614174000" ,
72-         "success" : True ,
73-         "message" : "Conversation deleted successfully" ,
67+         "model" : ConversationDeleteResponse ,
68+         "description" : "Conversation deleted successfully" ,
7469    },
7570    400 : {
76-         "description " : "Missing or invalid credentials provided by client" ,
77-         "model " : UnauthorizedResponse ,
71+         "model " : BadRequestResponse ,
72+         "description " : "Invalid request" ,
7873    },
7974    401 : {
80-         "description" : "Unauthorized: Invalid or missing Bearer token" ,
8175        "model" : UnauthorizedResponse ,
76+         "description" : "Unauthorized: Invalid or missing Bearer token" ,
77+     },
78+     403 : {
79+         "model" : AccessDeniedResponse ,
80+         "description" : "Client does not have permission to access conversation" ,
8281    },
8382    404 : {
84-         "detail" : {
85-             "response" : "Conversation not found" ,
86-             "cause" : "The specified conversation ID does not exist." ,
87-         }
83+         "model" : NotFoundResponse ,
84+         "description" : "Conversation not found" ,
8885    },
8986    503 : {
90-         "detail" : {
91-             "response" : "Unable to connect to Llama Stack" ,
92-             "cause" : "Connection error." ,
93-         }
87+         "model" : ServiceUnavailableResponse ,
88+         "description" : "Service unavailable" ,
9489    },
9590}
9691
9792conversations_list_responses : dict [int  |  str , dict [str , Any ]] =  {
9893    200 : {
99-         "conversations" : [
100-             {
101-                 "conversation_id" : "123e4567-e89b-12d3-a456-426614174000" ,
102-                 "created_at" : "2024-01-01T00:00:00Z" ,
103-                 "last_message_at" : "2024-01-01T00:05:00Z" ,
104-                 "last_used_model" : "gemini/gemini-1.5-flash" ,
105-                 "last_used_provider" : "gemini" ,
106-                 "message_count" : 5 ,
107-             },
108-             {
109-                 "conversation_id" : "456e7890-e12b-34d5-a678-901234567890" ,
110-                 "created_at" : "2024-01-01T01:00:00Z" ,
111-                 "last_message_at" : "2024-01-01T01:02:00Z" ,
112-                 "last_used_model" : "gemini/gemini-2.0-flash" ,
113-                 "last_used_provider" : "gemini" ,
114-                 "message_count" : 2 ,
115-             },
116-         ]
117-     },
118-     400 : {
119-         "description" : "Missing or invalid credentials provided by client" ,
120-         "model" : UnauthorizedResponse ,
94+         "model" : ConversationsListResponse ,
95+         "description" : "List of conversations retrieved successfully" ,
12196    },
12297    401 : {
123-         "description" : "Unauthorized: Invalid or missing Bearer token" ,
12498        "model" : UnauthorizedResponse ,
99+         "description" : "Unauthorized: Invalid or missing Bearer token" ,
125100    },
126101    503 : {
127-         "detail" : {
128-             "response" : "Unable to connect to Llama Stack" ,
129-             "cause" : "Connection error." ,
130-         }
102+         "model" : ServiceUnavailableResponse ,
103+         "description" : "Service unavailable" ,
131104    },
132105}
133106
@@ -267,34 +240,42 @@ async def get_conversation_endpoint_handler(
267240        logger .error ("Invalid conversation ID format: %s" , conversation_id )
268241        raise  HTTPException (
269242            status_code = status .HTTP_400_BAD_REQUEST ,
270-             detail = {
271-                 "response" : "Invalid conversation ID format" ,
272-                 "cause" : f"Conversation ID { conversation_id }  ,
273-             },
243+             detail = BadRequestResponse (
244+                 resource = "conversation" , resource_id = conversation_id 
245+             ).dump_detail (),
274246        )
275247
276248    user_id  =  auth [0 ]
277- 
278-     user_conversation  =  validate_conversation_ownership (
279-         user_id = user_id ,
280-         conversation_id = conversation_id ,
249+     if  not  can_access_conversation (
250+         conversation_id ,
251+         user_id ,
281252        others_allowed = (
282253            Action .READ_OTHERS_CONVERSATIONS  in  request .state .authorized_actions 
283254        ),
284-     )
285- 
286-     if  user_conversation  is  None :
255+     ):
287256        logger .warning (
288-             "User %s attempted to read conversation %s they don't own " ,
257+             "User %s attempted to read conversation %s they don't have access to " ,
289258            user_id ,
290259            conversation_id ,
291260        )
292261        raise  HTTPException (
293262            status_code = status .HTTP_403_FORBIDDEN ,
294-             detail = {
295-                 "response" : "Access denied" ,
296-                 "cause" : "You do not have permission to read this conversation" ,
297-             },
263+             detail = AccessDeniedResponse (
264+                 user_id = user_id ,
265+                 resource = "conversation" ,
266+                 resource_id = conversation_id ,
267+                 action = "read" ,
268+             ).dump_detail (),
269+         )
270+ 
271+     # If reached this, user is authorized to retreive this conversation 
272+     conversation  =  retrieve_conversation (conversation_id )
273+     if  conversation  is  None :
274+         raise  HTTPException (
275+             status_code = status .HTTP_404_NOT_FOUND ,
276+             detail = NotFoundResponse (
277+                 resource = "conversation" , resource_id = conversation_id 
278+             ).dump_detail (),
298279        )
299280
300281    agent_id  =  conversation_id 
@@ -308,10 +289,9 @@ async def get_conversation_endpoint_handler(
308289            logger .error ("No sessions found for conversation %s" , conversation_id )
309290            raise  HTTPException (
310291                status_code = status .HTTP_404_NOT_FOUND ,
311-                 detail = {
312-                     "response" : "Conversation not found" ,
313-                     "cause" : f"Conversation { conversation_id }  ,
314-                 },
292+                 detail = NotFoundResponse (
293+                     resource = "conversation" , resource_id = conversation_id 
294+                 ).dump_detail (),
315295            )
316296        session_id  =  str (agent_sessions [0 ].get ("session_id" ))
317297
@@ -334,22 +314,23 @@ async def get_conversation_endpoint_handler(
334314        logger .error ("Unable to connect to Llama Stack: %s" , e )
335315        raise  HTTPException (
336316            status_code = status .HTTP_503_SERVICE_UNAVAILABLE ,
337-             detail = {
338-                 "response" : "Unable to connect to Llama Stack" ,
339-                 "cause" : str (e ),
340-             },
317+             detail = ServiceUnavailableResponse (
318+                 backend_name = "Llama Stack" , cause = str (e )
319+             ).dump_detail (),
341320        ) from  e 
321+ 
342322    except  NotFoundError  as  e :
343323        logger .error ("Conversation not found: %s" , e )
344324        raise  HTTPException (
345325            status_code = status .HTTP_404_NOT_FOUND ,
346-             detail = {
347-                 "response" : "Conversation not found" ,
348-                 "cause" : f"Conversation { conversation_id } { str (e )}  ,
349-             },
326+             detail = NotFoundResponse (
327+                 resource = "conversation" , resource_id = conversation_id 
328+             ).dump_detail (),
350329        ) from  e 
330+ 
351331    except  HTTPException :
352332        raise 
333+ 
353334    except  Exception  as  e :
354335        # Handle case where session doesn't exist or other errors 
355336        logger .exception ("Error retrieving conversation %s: %s" , conversation_id , e )
@@ -389,34 +370,42 @@ async def delete_conversation_endpoint_handler(
389370        logger .error ("Invalid conversation ID format: %s" , conversation_id )
390371        raise  HTTPException (
391372            status_code = status .HTTP_400_BAD_REQUEST ,
392-             detail = {
393-                 "response" : "Invalid conversation ID format" ,
394-                 "cause" : f"Conversation ID { conversation_id }  ,
395-             },
373+             detail = BadRequestResponse (
374+                 resource = "conversation" , resource_id = conversation_id 
375+             ).dump_detail (),
396376        )
397377
398378    user_id  =  auth [0 ]
399- 
400-     user_conversation  =  validate_conversation_ownership (
401-         user_id = user_id ,
402-         conversation_id = conversation_id ,
379+     if  not  can_access_conversation (
380+         conversation_id ,
381+         user_id ,
403382        others_allowed = (
404383            Action .DELETE_OTHERS_CONVERSATIONS  in  request .state .authorized_actions 
405384        ),
406-     )
407- 
408-     if  user_conversation  is  None :
385+     ):
409386        logger .warning (
410-             "User %s attempted to delete conversation %s they don't own " ,
387+             "User %s attempted to delete conversation %s they don't have access to " ,
411388            user_id ,
412389            conversation_id ,
413390        )
414391        raise  HTTPException (
415392            status_code = status .HTTP_403_FORBIDDEN ,
416-             detail = {
417-                 "response" : "Access denied" ,
418-                 "cause" : "You do not have permission to delete this conversation" ,
419-             },
393+             detail = AccessDeniedResponse (
394+                 user_id = user_id ,
395+                 resource = "conversation" ,
396+                 resource_id = conversation_id ,
397+                 action = "delete" ,
398+             ).dump_detail (),
399+         )
400+ 
401+     # If reached this, user is authorized to retreive this conversation 
402+     conversation  =  retrieve_conversation (conversation_id )
403+     if  conversation  is  None :
404+         raise  HTTPException (
405+             status_code = status .HTTP_404_NOT_FOUND ,
406+             detail = NotFoundResponse (
407+                 resource = "conversation" , resource_id = conversation_id 
408+             ).dump_detail (),
420409        )
421410
422411    agent_id  =  conversation_id 
@@ -452,25 +441,24 @@ async def delete_conversation_endpoint_handler(
452441        )
453442
454443    except  APIConnectionError  as  e :
455-         logger .error ("Unable to connect to Llama Stack: %s" , e )
456444        raise  HTTPException (
457445            status_code = status .HTTP_503_SERVICE_UNAVAILABLE ,
458-             detail = {
459-                 "response" : "Unable to connect to Llama Stack" ,
460-                 "cause" : str (e ),
461-             },
446+             detail = ServiceUnavailableResponse (
447+                 backend_name = "Llama Stack" , cause = str (e )
448+             ).dump_detail (),
462449        ) from  e 
450+ 
463451    except  NotFoundError  as  e :
464-         logger .error ("Conversation not found: %s" , e )
465452        raise  HTTPException (
466453            status_code = status .HTTP_404_NOT_FOUND ,
467-             detail = {
468-                 "response" : "Conversation not found" ,
469-                 "cause" : f"Conversation { conversation_id } { str (e )}  ,
470-             },
454+             detail = NotFoundResponse (
455+                 resource = "conversation" , resource_id = conversation_id 
456+             ).dump_detail (),
471457        ) from  e 
458+ 
472459    except  HTTPException :
473460        raise 
461+ 
474462    except  Exception  as  e :
475463        # Handle case where session doesn't exist or other errors 
476464        logger .exception ("Error deleting conversation %s: %s" , conversation_id , e )
0 commit comments