63
63
EventId = str
64
64
65
65
66
+ class EventMessage :
67
+ """
68
+ A JSONRPCMessage with an optional event ID for stream resumability.
69
+ """
70
+
71
+ message : JSONRPCMessage
72
+ event_id : str | None
73
+
74
+ def __init__ (self , message : JSONRPCMessage , event_id : str | None = None ):
75
+ self .message = message
76
+ self .event_id = event_id
77
+
78
+
79
+ EventCallback = Callable [[EventMessage ], Awaitable [None ]]
80
+
81
+
66
82
class EventStore (ABC ):
67
83
"""
68
84
Interface for resumability support via event storage.
@@ -88,8 +104,8 @@ async def store_event(
88
104
async def replay_events_after (
89
105
self ,
90
106
last_event_id : EventId ,
91
- send_callback : Callable [[ EventId , JSONRPCMessage ], Awaitable [ None ]] ,
92
- ) -> StreamId :
107
+ send_callback : EventCallback ,
108
+ ) -> StreamId | None :
93
109
"""
94
110
Replays events that occurred after the specified event ID.
95
111
@@ -149,7 +165,7 @@ def __init__(
149
165
self .is_json_response_enabled = is_json_response_enabled
150
166
self ._event_store = event_store
151
167
self ._request_streams : dict [
152
- RequestId , MemoryObjectSendStream [tuple [ JSONRPCMessage , str | None ] ]
168
+ RequestId , MemoryObjectSendStream [EventMessage ]
153
169
] = {}
154
170
self ._terminated = False
155
171
@@ -358,7 +374,7 @@ async def _handle_post_request(
358
374
request_id = str (message .root .id )
359
375
# Create promise stream for getting response
360
376
request_stream_writer , request_stream_reader = (
361
- anyio .create_memory_object_stream [tuple [ JSONRPCMessage , str | None ] ](0 )
377
+ anyio .create_memory_object_stream [EventMessage ](0 )
362
378
)
363
379
364
380
# Register this stream for the request ID
@@ -373,16 +389,18 @@ async def _handle_post_request(
373
389
response_message = None
374
390
375
391
# Use similar approach to SSE writer for consistency
376
- async for received_message , _ in request_stream_reader :
392
+ async for event_message in request_stream_reader :
377
393
# If it's a response, this is what we're waiting for
378
394
if isinstance (
379
- received_message .root , JSONRPCResponse | JSONRPCError
395
+ event_message . message .root , JSONRPCResponse | JSONRPCError
380
396
):
381
- response_message = received_message
397
+ response_message = event_message . message
382
398
break
383
399
# For notifications and request, keep waiting
384
400
else :
385
- logger .debug (f"received: { received_message .root .method } " )
401
+ logger .debug (
402
+ f"received: { event_message .message .root .method } "
403
+ )
386
404
387
405
# At this point we should have a response
388
406
if response_message :
@@ -424,27 +442,24 @@ async def sse_writer():
424
442
try :
425
443
async with sse_stream_writer , request_stream_reader :
426
444
# Process messages from the request-specific stream
427
- async for (
428
- received_message ,
429
- event_id ,
430
- ) in request_stream_reader :
445
+ async for event_message in request_stream_reader :
431
446
# Build the event data
432
447
event_data = {
433
448
"event" : "message" ,
434
- "data" : received_message .model_dump_json (
449
+ "data" : event_message . message .model_dump_json (
435
450
by_alias = True , exclude_none = True
436
451
),
437
452
}
438
453
439
454
# If an event ID was provided, include it
440
- if event_id :
441
- event_data ["id" ] = event_id
455
+ if event_message . event_id :
456
+ event_data ["id" ] = event_message . event_id
442
457
443
458
await sse_stream_writer .send (event_data )
444
459
445
460
# If response, remove from pending streams and close
446
461
if isinstance (
447
- received_message .root ,
462
+ event_message . message .root ,
448
463
JSONRPCResponse | JSONRPCError ,
449
464
):
450
465
if request_id :
@@ -563,20 +578,15 @@ async def standalone_sse_writer():
563
578
try :
564
579
# Create a standalone message stream for server-initiated messages
565
580
standalone_stream_writer , standalone_stream_reader = (
566
- anyio .create_memory_object_stream [
567
- tuple [JSONRPCMessage , str | None ]
568
- ](0 )
581
+ anyio .create_memory_object_stream [EventMessage ](0 )
569
582
)
570
583
571
584
# Register this stream using the special key
572
585
self ._request_streams [GET_STREAM_KEY ] = standalone_stream_writer
573
586
574
587
async with sse_stream_writer , standalone_stream_reader :
575
588
# Process messages from the standalone stream
576
- async for item in standalone_stream_reader :
577
- # The message router always sends a tuple of (message, event_id)
578
- received_message , event_id = item
579
-
589
+ async for event_message in standalone_stream_reader :
580
590
# For the standalone stream, we handle:
581
591
# - JSONRPCNotification (server sends notifications to client)
582
592
# - JSONRPCRequest (server sends requests to client)
@@ -585,14 +595,14 @@ async def standalone_sse_writer():
585
595
# Send the message via SSE
586
596
event_data = {
587
597
"event" : "message" ,
588
- "data" : received_message .model_dump_json (
598
+ "data" : event_message . message .model_dump_json (
589
599
by_alias = True , exclude_none = True
590
600
),
591
601
}
592
602
593
603
# If an event ID was provided, include it in the SSE stream
594
- if event_id :
595
- event_data ["id" ] = event_id
604
+ if event_message . event_id :
605
+ event_data ["id" ] = event_message . event_id
596
606
597
607
await sse_stream_writer .send (event_data )
598
608
except Exception as e :
@@ -741,14 +751,12 @@ async def replay_sender():
741
751
try :
742
752
async with sse_stream_writer :
743
753
# Define an async callback for sending events
744
- async def send_event (
745
- event_id : EventId , message : JSONRPCMessage
746
- ) -> None :
754
+ async def send_event (event_message : EventMessage ) -> None :
747
755
await sse_stream_writer .send (
748
756
{
749
757
"event" : "message" ,
750
- "id" : event_id ,
751
- "data" : message .model_dump_json (
758
+ "id" : event_message . event_id ,
759
+ "data" : event_message . message .model_dump_json (
752
760
by_alias = True , exclude_none = True
753
761
),
754
762
}
@@ -762,22 +770,21 @@ async def send_event(
762
770
# If stream ID not in mapping, create it
763
771
if stream_id and stream_id not in self ._request_streams :
764
772
msg_writer , msg_reader = anyio .create_memory_object_stream [
765
- tuple [ JSONRPCMessage , str | None ]
773
+ EventMessage
766
774
](0 )
767
775
self ._request_streams [stream_id ] = msg_writer
768
776
769
777
# Forward messages to SSE
770
778
async with msg_reader :
771
- async for item in msg_reader :
772
- message , event_id = item
773
-
779
+ async for event_message in msg_reader :
780
+ event_data = event_message .message .model_dump_json (
781
+ by_alias = True , exclude_none = True
782
+ )
774
783
await sse_stream_writer .send (
775
784
{
776
785
"event" : "message" ,
777
- "id" : event_id ,
778
- "data" : message .model_dump_json (
779
- by_alias = True , exclude_none = True
780
- ),
786
+ "id" : event_message .event_id ,
787
+ "data" : event_data ,
781
788
}
782
789
)
783
790
except Exception as e :
@@ -871,7 +878,7 @@ async def message_router():
871
878
try :
872
879
# Send both the message and the event ID
873
880
await self ._request_streams [request_stream_id ].send (
874
- (message , event_id )
881
+ EventMessage (message , event_id )
875
882
)
876
883
except (
877
884
anyio .BrokenResourceError ,
0 commit comments