@@ -545,66 +545,73 @@ async def _handle_reconnection(
545545 assert isinstance (ctx .session_message .message , JSONRPCRequest )
546546 original_request_id = ctx .session_message .message .id
547547
548- if attempt >= MAX_RECONNECTION_ATTEMPTS :
549- # Resolve on give-up: a request with no read timeout (a listen
550- # stream) would otherwise hang its caller forever.
551- logger .debug (f"Max reconnection attempts ({ MAX_RECONNECTION_ATTEMPTS } ) exceeded" )
552- await self ._resolve_abandoned_request (
553- ctx .read_stream_writer , original_request_id , "SSE stream ended and reconnection attempts were exhausted"
554- )
555- return
548+ while True :
549+ if attempt >= MAX_RECONNECTION_ATTEMPTS :
550+ # Resolve on give-up: a request with no read timeout (a listen
551+ # stream) would otherwise hang its caller forever.
552+ logger .debug (f"Max reconnection attempts ({ MAX_RECONNECTION_ATTEMPTS } ) exceeded" )
553+ await self ._resolve_abandoned_request (
554+ ctx .read_stream_writer ,
555+ original_request_id ,
556+ "SSE stream ended and reconnection attempts were exhausted" ,
557+ )
558+ return
556559
557- # Always wait - use server value or default
558- delay_ms = retry_interval_ms if retry_interval_ms is not None else DEFAULT_RECONNECTION_DELAY_MS
559- await anyio .sleep (delay_ms / 1000.0 )
560+ # Always wait - use server value or default
561+ delay_ms = retry_interval_ms if retry_interval_ms is not None else DEFAULT_RECONNECTION_DELAY_MS
562+ await anyio .sleep (delay_ms / 1000.0 )
560563
561- headers = self ._prepare_headers ()
562- headers [LAST_EVENT_ID ] = last_event_id
564+ headers = self ._prepare_headers ()
565+ headers [LAST_EVENT_ID ] = last_event_id
563566
564- is_sse_response = False
565- try :
566- async with sse_within_origin (
567- ctx .client , self .url , headers = headers , max_event_size = self .max_sse_event_size
568- ) as event_source :
569- event_source .response .raise_for_status ()
570- content_type = event_source .response .headers .get ("content-type" , "" ).partition (";" )[0 ]
571- is_sse_response = content_type .strip ().lower () == "text/event-stream"
572- logger .info ("Reconnected to SSE stream" )
573-
574- # Track for potential further reconnection
575- reconnect_last_event_id : str = last_event_id
576- reconnect_retry_ms = retry_interval_ms
577-
578- async for sse in event_source :
579- if sse .id : # pragma: no branch
580- reconnect_last_event_id = sse .id
581- if sse .retry is not None :
582- reconnect_retry_ms = sse .retry
567+ is_sse_response = False
568+ try :
569+ async with sse_within_origin (
570+ ctx .client , self .url , headers = headers , max_event_size = self .max_sse_event_size
571+ ) as event_source :
572+ event_source .response .raise_for_status ()
573+ content_type = event_source .response .headers .get ("content-type" , "" ).partition (";" )[0 ]
574+ is_sse_response = content_type .strip ().lower () == "text/event-stream"
575+ logger .info ("Reconnected to SSE stream" )
583576
584- is_complete = await self ._handle_sse_event (
585- sse ,
586- ctx .read_stream_writer ,
587- original_request_id ,
588- ctx .metadata .on_resumption_token_update if ctx .metadata else None ,
577+ # Track for potential further reconnection
578+ reconnect_last_event_id : str = last_event_id
579+ reconnect_retry_ms = retry_interval_ms
580+
581+ async for sse in event_source :
582+ if sse .id : # pragma: no branch
583+ reconnect_last_event_id = sse .id
584+ if sse .retry is not None :
585+ reconnect_retry_ms = sse .retry
586+
587+ is_complete = await self ._handle_sse_event (
588+ sse ,
589+ ctx .read_stream_writer ,
590+ original_request_id ,
591+ ctx .metadata .on_resumption_token_update if ctx .metadata else None ,
592+ )
593+ if is_complete :
594+ await event_source .response .aclose ()
595+ return
596+
597+ # Stream ended again without response - reconnect again (reset attempt counter)
598+ logger .info ("SSE stream disconnected, reconnecting..." )
599+ except httpx2 .SSEError as exc :
600+ if is_sse_response :
601+ await self ._resolve_abandoned_request (
602+ ctx .read_stream_writer , original_request_id , f"SSE stream failed: { exc } "
589603 )
590- if is_complete :
591- await event_source .response .aclose ()
592- return
604+ return
605+ attempt += 1
606+ continue
607+ except Exception as e : # pragma: no cover
608+ logger .debug (f"Reconnection failed: { e } " )
609+ attempt += 1
610+ continue
593611
594- # Stream ended again without response - reconnect again (reset attempt counter)
595- logger .info ("SSE stream disconnected, reconnecting..." )
596- await self ._handle_reconnection (ctx , reconnect_last_event_id , reconnect_retry_ms , 0 )
597- except httpx2 .SSEError as exc :
598- if is_sse_response :
599- await self ._resolve_abandoned_request (
600- ctx .read_stream_writer , original_request_id , f"SSE stream failed: { exc } "
601- )
602- else :
603- await self ._handle_reconnection (ctx , last_event_id , retry_interval_ms , attempt + 1 )
604- except Exception as e : # pragma: no cover
605- logger .debug (f"Reconnection failed: { e } " )
606- # Try to reconnect again if we still have an event ID
607- await self ._handle_reconnection (ctx , last_event_id , retry_interval_ms , attempt + 1 )
612+ last_event_id = reconnect_last_event_id
613+ retry_interval_ms = reconnect_retry_ms
614+ attempt = 0
608615
609616 async def post_writer (
610617 self ,
0 commit comments