22
33from __future__ import annotations
44
5- from typing import Optional
5+ from typing import Optional , cast
66from typing_extensions import Literal
77
88import httpx
1616 AsyncSqlResourceWithStreamingResponse ,
1717)
1818from ...types import axon_list_params , axon_create_params , axon_publish_params
19+ from ...types .axons import axon_subscribe_sse_params
1920from ..._types import Body , Omit , Query , Headers , NotGiven , omit , not_given
2021from ..._utils import path_template , maybe_transform , async_maybe_transform
2122from ..._compat import cached_property
2627 async_to_raw_response_wrapper ,
2728 async_to_streamed_response_wrapper ,
2829)
29- from ..._streaming import Stream , AsyncStream
30+ from ..._constants import RAW_RESPONSE_HEADER
31+ from ..._streaming import Stream , AsyncStream , ReconnectingStream , AsyncReconnectingStream
3032from ...pagination import SyncAxonsCursorIDPage , AsyncAxonsCursorIDPage
3133from ..._base_client import AsyncPaginator , make_request_options
3234from ...types .axon_view import AxonView
@@ -269,6 +271,8 @@ def subscribe_sse(
269271 """
270272 [Beta] Subscribe to an axon event stream via server-sent events.
271273
274+ Automatically reconnects on timeout, resuming from last received event.
275+
272276 Args:
273277 extra_headers: Send extra headers
274278
@@ -282,14 +286,54 @@ def subscribe_sse(
282286 raise ValueError (f"Expected a non-empty value for `id` but received { id !r} " )
283287 default_headers : Headers = {"Accept" : "text/event-stream" }
284288 merged_headers = default_headers if extra_headers is None else {** default_headers , ** extra_headers }
285- return self ._get (
286- path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
287- options = make_request_options (
288- extra_headers = merged_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
289+
290+ # Check if user wants raw response (opt-out of reconnection)
291+ if extra_headers is not None and RAW_RESPONSE_HEADER in extra_headers :
292+ return self ._get (
293+ path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
294+ options = make_request_options (
295+ extra_headers = merged_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
296+ ),
297+ cast_to = AxonEventView ,
298+ stream = True ,
299+ stream_cls = Stream [AxonEventView ],
300+ )
301+
302+ def create_stream (last_sequence : str | None ) -> Stream [AxonEventView ]:
303+ # after_sequence is used internally for reconnection only
304+ sequence_int = int (last_sequence ) if last_sequence is not None else None
305+ return self ._get (
306+ path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
307+ options = make_request_options (
308+ extra_headers = merged_headers ,
309+ extra_query = extra_query ,
310+ extra_body = extra_body ,
311+ timeout = timeout ,
312+ query = maybe_transform (
313+ {"after_sequence" : sequence_int },
314+ axon_subscribe_sse_params .AxonSubscribeSseParams ,
315+ ),
316+ ),
317+ cast_to = AxonEventView ,
318+ stream = True ,
319+ stream_cls = Stream [AxonEventView ],
320+ )
321+
322+ initial_stream = create_stream (None )
323+
324+ def get_sequence (item : AxonEventView ) -> str | None :
325+ value = getattr (item , "sequence" , None )
326+ if value is None :
327+ return None
328+ return str (value )
329+
330+ return cast (
331+ Stream [AxonEventView ],
332+ ReconnectingStream (
333+ current_stream = initial_stream ,
334+ stream_creator = create_stream ,
335+ get_offset = get_sequence ,
289336 ),
290- cast_to = AxonEventView ,
291- stream = True ,
292- stream_cls = Stream [AxonEventView ],
293337 )
294338
295339
@@ -526,6 +570,8 @@ async def subscribe_sse(
526570 """
527571 [Beta] Subscribe to an axon event stream via server-sent events.
528572
573+ Automatically reconnects on timeout, resuming from last received event.
574+
529575 Args:
530576 extra_headers: Send extra headers
531577
@@ -539,14 +585,54 @@ async def subscribe_sse(
539585 raise ValueError (f"Expected a non-empty value for `id` but received { id !r} " )
540586 default_headers : Headers = {"Accept" : "text/event-stream" }
541587 merged_headers = default_headers if extra_headers is None else {** default_headers , ** extra_headers }
542- return await self ._get (
543- path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
544- options = make_request_options (
545- extra_headers = merged_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
588+
589+ # Check if user wants raw response (opt-out of reconnection)
590+ if extra_headers is not None and RAW_RESPONSE_HEADER in extra_headers :
591+ return await self ._get (
592+ path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
593+ options = make_request_options (
594+ extra_headers = merged_headers , extra_query = extra_query , extra_body = extra_body , timeout = timeout
595+ ),
596+ cast_to = AxonEventView ,
597+ stream = True ,
598+ stream_cls = AsyncStream [AxonEventView ],
599+ )
600+
601+ async def create_stream (last_sequence : str | None ) -> AsyncStream [AxonEventView ]:
602+ # after_sequence is used internally for reconnection only
603+ sequence_int = int (last_sequence ) if last_sequence is not None else None
604+ return await self ._get (
605+ path_template ("/v1/axons/{id}/subscribe/sse" , id = id ),
606+ options = make_request_options (
607+ extra_headers = merged_headers ,
608+ extra_query = extra_query ,
609+ extra_body = extra_body ,
610+ timeout = timeout ,
611+ query = maybe_transform (
612+ {"after_sequence" : sequence_int },
613+ axon_subscribe_sse_params .AxonSubscribeSseParams ,
614+ ),
615+ ),
616+ cast_to = AxonEventView ,
617+ stream = True ,
618+ stream_cls = AsyncStream [AxonEventView ],
619+ )
620+
621+ initial_stream = await create_stream (None )
622+
623+ def get_sequence (item : AxonEventView ) -> str | None :
624+ value = getattr (item , "sequence" , None )
625+ if value is None :
626+ return None
627+ return str (value )
628+
629+ return cast (
630+ AsyncStream [AxonEventView ],
631+ AsyncReconnectingStream (
632+ current_stream = initial_stream ,
633+ stream_creator = create_stream ,
634+ get_offset = get_sequence ,
546635 ),
547- cast_to = AxonEventView ,
548- stream = True ,
549- stream_cls = AsyncStream [AxonEventView ],
550636 )
551637
552638
0 commit comments