4343 DAuthorizedResourceIds ,
4444)
4545from src .utils .logging import make_logger
46+ from src .utils .rpc_metrics import (
47+ rpc_request_timing ,
48+ )
4649from src .utils .task_authorization import check_task_or_collapse_to_404
4750
4851logger = make_logger (__name__ )
@@ -549,53 +552,62 @@ async def _handle_sync_rpc(
549552 request_headers : dict [str , str ] | None = None ,
550553) -> AgentRPCResponse :
551554 """Handle synchronous JSON-RPC requests."""
552- try :
553- result_entity = await agents_acp_use_case .handle_rpc_request (
554- agent_id = agent_id ,
555- agent_name = agent_name ,
556- method = request .method ,
557- params = request .params ,
558- request_headers = request_headers ,
559- )
555+ with rpc_request_timing (request .method .value , streaming = False ) as rpc_call :
556+ try :
557+ result_entity = await agents_acp_use_case .handle_rpc_request (
558+ agent_id = agent_id ,
559+ agent_name = agent_name ,
560+ method = request .method ,
561+ params = request .params ,
562+ request_headers = request_headers ,
563+ )
560564
561- if isinstance (result_entity , AsyncIterator ):
562- raise ValueError (f"Expected non-async iterator, got { type (result_entity )} " )
565+ if isinstance (result_entity , AsyncIterator ):
566+ raise ValueError (
567+ f"Expected non-async iterator, got { type (result_entity )} "
568+ )
563569
564- if isinstance (result_entity , list ):
565- serialized_result = [item .model_dump () for item in result_entity ]
566- else :
567- serialized_result = result_entity .model_dump ()
568-
569- # if request.method == AgentRPCMethod.MESSAGE_SEND:
570- # if isinstance(result_entity, list):
571- # result = [TaskMessage.model_validate(task_message_entity) for task_message_entity in result_entity]
572- # else:
573- # raise ValueError(f"Expected list of TaskMessage entities, got {type(result_entity)}")
574- # elif request.method == AgentRPCMethod.TASK_CREATE:
575- # result = Task.model_validate(result_entity)
576- # elif request.method == AgentRPCMethod.TASK_CANCEL:
577- # result = Task.model_validate(result_entity)
578- # elif request.method == AgentRPCMethod.EVENT_SEND:
579- # result = Event.model_validate(result_entity)
580- # else:
581- # raise ValueError(f"Unsupported method: {request.method}")
582- # logger.info(f"AgentRPCResponse Result: {result}")
583- return AgentRPCResponse .model_validate (
584- {
585- "id" : request .id ,
586- "result" : serialized_result ,
587- "error" : None ,
588- }
589- )
570+ if isinstance (result_entity , list ):
571+ serialized_result = [item .model_dump () for item in result_entity ]
572+ else :
573+ serialized_result = result_entity .model_dump ()
574+
575+ # if request.method == AgentRPCMethod.MESSAGE_SEND:
576+ # if isinstance(result_entity, list):
577+ # result = [TaskMessage.model_validate(task_message_entity) for task_message_entity in result_entity]
578+ # else:
579+ # raise ValueError(f"Expected list of TaskMessage entities, got {type(result_entity)}")
580+ # elif request.method == AgentRPCMethod.TASK_CREATE:
581+ # result = Task.model_validate(result_entity)
582+ # elif request.method == AgentRPCMethod.TASK_CANCEL:
583+ # result = Task.model_validate(result_entity)
584+ # elif request.method == AgentRPCMethod.EVENT_SEND:
585+ # result = Event.model_validate(result_entity)
586+ # else:
587+ # raise ValueError(f"Unsupported method: {request.method}")
588+ # logger.info(f"AgentRPCResponse Result: {result}")
589+ return AgentRPCResponse .model_validate (
590+ {
591+ "id" : request .id ,
592+ "result" : serialized_result ,
593+ "error" : None ,
594+ }
595+ )
590596
591- except ValidationError as e :
592- logger .error (f"Validation error in RPC request: { e } " , exc_info = True )
593- error = JSONRPCError (code = - 32602 , message = f"Invalid parameters: { e } " )
594- return AgentRPCResponse (id = request .id , error = error .model_dump (), result = None )
595- except Exception as e :
596- logger .error (f"Error handling JSON-RPC request: { e } " , exc_info = True )
597- error = JSONRPCError (code = - 32603 , message = str (e ))
598- return AgentRPCResponse (id = request .id , error = error .model_dump (), result = None )
597+ except ValidationError as e :
598+ logger .error (f"Validation error in RPC request: { e } " , exc_info = True )
599+ error = JSONRPCError (code = - 32602 , message = f"Invalid parameters: { e } " )
600+ rpc_call .fail (error .code , e )
601+ return AgentRPCResponse (
602+ id = request .id , error = error .model_dump (), result = None
603+ )
604+ except Exception as e :
605+ logger .error (f"Error handling JSON-RPC request: { e } " , exc_info = True )
606+ error = JSONRPCError (code = - 32603 , message = str (e ))
607+ rpc_call .fail (error .code , e )
608+ return AgentRPCResponse (
609+ id = request .id , error = error .model_dump (), result = None
610+ )
599611
600612
601613async def _handle_streaming_rpc (
@@ -608,56 +620,60 @@ async def _handle_streaming_rpc(
608620 """Handle streaming JSON-RPC requests."""
609621
610622 async def rpc_response_generator ():
611- result_entity_async_iterator = None
612- try :
613- result_entity_async_iterator = await agents_acp_use_case .handle_rpc_request (
614- agent_id = agent_id ,
615- agent_name = agent_name ,
616- method = request .method ,
617- params = request .params ,
618- request_headers = request_headers ,
619- )
620-
621- if not isinstance (result_entity_async_iterator , AsyncIterator ):
622- raise ValueError (
623- f"Expected AsyncIterator, got { type (result_entity_async_iterator )} "
623+ with rpc_request_timing (request .method .value , streaming = True ) as rpc_call :
624+ result_entity_async_iterator = None
625+ try :
626+ result_entity_async_iterator = (
627+ await agents_acp_use_case .handle_rpc_request (
628+ agent_id = agent_id ,
629+ agent_name = agent_name ,
630+ method = request .method ,
631+ params = request .params ,
632+ request_headers = request_headers ,
633+ )
624634 )
625635
626- # At this point we know it's an AsyncIterator[TaskMessage]
627- async for task_message_update_entity in result_entity_async_iterator :
628- logger .debug (
629- f"Streaming message chunk type: { type (task_message_update_entity ).__name__ } "
630- )
631- rpc_response = AgentRPCResponse .model_validate (
632- {
633- "id" : request .id ,
634- "result" : task_message_update_entity .model_dump (),
635- "error" : None ,
636- }
637- )
638- # Yield JSON bytes with newline for NDJSON format
639- yield rpc_response .model_dump_json ().encode () + b"\n "
636+ if not isinstance (result_entity_async_iterator , AsyncIterator ):
637+ raise ValueError (
638+ f"Expected AsyncIterator, got { type (result_entity_async_iterator )} "
639+ )
640640
641- except Exception as e :
642- logger .error (f"Error in streaming RPC response: { e } " , exc_info = True )
643- # Yield error response
644- error_response = AgentRPCResponse (
645- id = request .id ,
646- result = None ,
647- error = JSONRPCError (code = - 32603 , message = str (e )).model_dump (),
648- )
649- yield error_response .model_dump_json ().encode () + b"\n "
650- finally :
651- # CRITICAL: Ensure the async iterator is properly closed
652- # This ensures HTTP connections are released back to the pool
653- if result_entity_async_iterator is not None and hasattr (
654- result_entity_async_iterator , "aclose"
655- ):
656- try :
657- await result_entity_async_iterator .aclose ()
658- logger .debug ("Closed streaming iterator properly" )
659- except Exception as e :
660- logger .warning (f"Error closing streaming iterator: { e } " )
641+ # At this point we know it's an AsyncIterator[TaskMessage]
642+ async for task_message_update_entity in result_entity_async_iterator :
643+ logger .debug (
644+ f"Streaming message chunk type: { type (task_message_update_entity ).__name__ } "
645+ )
646+ rpc_response = AgentRPCResponse .model_validate (
647+ {
648+ "id" : request .id ,
649+ "result" : task_message_update_entity .model_dump (),
650+ "error" : None ,
651+ }
652+ )
653+ # Yield JSON bytes with newline for NDJSON format
654+ yield rpc_response .model_dump_json ().encode () + b"\n "
655+
656+ except Exception as e :
657+ logger .error (f"Error in streaming RPC response: { e } " , exc_info = True )
658+ rpc_call .fail (- 32603 , e )
659+ # Yield error response
660+ error_response = AgentRPCResponse (
661+ id = request .id ,
662+ result = None ,
663+ error = JSONRPCError (code = - 32603 , message = str (e )).model_dump (),
664+ )
665+ yield error_response .model_dump_json ().encode () + b"\n "
666+ finally :
667+ # CRITICAL: Ensure the async iterator is properly closed
668+ # This ensures HTTP connections are released back to the pool
669+ if result_entity_async_iterator is not None and hasattr (
670+ result_entity_async_iterator , "aclose"
671+ ):
672+ try :
673+ await result_entity_async_iterator .aclose ()
674+ logger .debug ("Closed streaming iterator properly" )
675+ except Exception as e :
676+ logger .warning (f"Error closing streaming iterator: { e } " )
661677
662678 return StreamingResponse (
663679 rpc_response_generator (),
0 commit comments