|
1 | 1 | """Progress interactions against the low-level Server, driven through the public Client API. |
2 | 2 |
|
3 | 3 | Server-to-client progress emitted during a request follows the same ordering guarantee as |
4 | | -logging notifications (see test_logging.py): everything the server sends before its response is |
5 | | -dispatched to the progress callback before the request returns, so no synchronisation is needed. |
6 | | -The client-to-server direction is a standalone notification with no response to await, so that |
7 | | -test waits on an event set by the server's handler. |
| 4 | +logging notifications (see test_logging.py) -- on the in-memory transport unconditionally, and |
| 5 | +over streamable HTTP only when sent with ``related_request_id`` so the notification rides the |
| 6 | +originating request's POST stream rather than the standalone GET stream. These tests pass |
| 7 | +``related_request_id`` so no synchronisation is needed. The client-to-server direction is a |
| 8 | +standalone notification with no response to await, so that test waits on an event set by the |
| 9 | +server's handler. |
8 | 10 | """ |
9 | 11 |
|
10 | 12 | import anyio |
@@ -42,9 +44,15 @@ async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestPara |
42 | 44 | assert ctx.meta is not None |
43 | 45 | token = ctx.meta.get("progress_token") |
44 | 46 | assert token is not None |
45 | | - await ctx.session.send_progress_notification(token, 1.0, total=3.0, message="first chunk") |
46 | | - await ctx.session.send_progress_notification(token, 2.0, total=3.0, message="second chunk") |
47 | | - await ctx.session.send_progress_notification(token, 3.0, total=3.0, message="done") |
| 47 | + await ctx.session.send_progress_notification( |
| 48 | + token, 1.0, total=3.0, message="first chunk", related_request_id=str(ctx.request_id) |
| 49 | + ) |
| 50 | + await ctx.session.send_progress_notification( |
| 51 | + token, 2.0, total=3.0, message="second chunk", related_request_id=str(ctx.request_id) |
| 52 | + ) |
| 53 | + await ctx.session.send_progress_notification( |
| 54 | + token, 3.0, total=3.0, message="done", related_request_id=str(ctx.request_id) |
| 55 | + ) |
48 | 56 | return CallToolResult(content=[TextContent(text="downloaded")]) |
49 | 57 |
|
50 | 58 | server = Server("downloader", on_list_tools=list_tools, on_call_tool=call_tool) |
@@ -166,10 +174,14 @@ async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestPara |
166 | 174 | # The two handlers interleave by waiting on alternating turns: a takes 0 and 2, b takes 1 and 3. |
167 | 175 | first, second = (0, 2) if label == "a" else (1, 3) |
168 | 176 | await turns[first].wait() |
169 | | - await ctx.session.send_progress_notification(token, progress_values[label][0]) |
| 177 | + await ctx.session.send_progress_notification( |
| 178 | + token, progress_values[label][0], related_request_id=str(ctx.request_id) |
| 179 | + ) |
170 | 180 | turns[first + 1].set() |
171 | 181 | await turns[second].wait() |
172 | | - await ctx.session.send_progress_notification(token, progress_values[label][1]) |
| 182 | + await ctx.session.send_progress_notification( |
| 183 | + token, progress_values[label][1], related_request_id=str(ctx.request_id) |
| 184 | + ) |
173 | 185 | if second + 1 < len(turns): |
174 | 186 | turns[second + 1].set() |
175 | 187 | return CallToolResult(content=[TextContent(text="done")]) |
@@ -227,7 +239,7 @@ async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestPara |
227 | 239 | token = ctx.meta.get("progress_token") |
228 | 240 | assert token is not None |
229 | 241 | captured.append((ctx.session, token)) |
230 | | - await ctx.session.send_progress_notification(token, 0.5) |
| 242 | + await ctx.session.send_progress_notification(token, 0.5, related_request_id=str(ctx.request_id)) |
231 | 243 | return CallToolResult(content=[TextContent(text="done")]) |
232 | 244 |
|
233 | 245 | server = Server("reporter", on_list_tools=list_tools, on_call_tool=call_tool) |
@@ -276,9 +288,9 @@ async def call_tool(ctx: ServerRequestContext, params: types.CallToolRequestPara |
276 | 288 | assert ctx.meta is not None |
277 | 289 | token = ctx.meta.get("progress_token") |
278 | 290 | assert token is not None |
279 | | - await ctx.session.send_progress_notification(token, 0.5) |
280 | | - await ctx.session.send_progress_notification(token, 0.3) |
281 | | - await ctx.session.send_progress_notification(token, 0.9) |
| 291 | + await ctx.session.send_progress_notification(token, 0.5, related_request_id=str(ctx.request_id)) |
| 292 | + await ctx.session.send_progress_notification(token, 0.3, related_request_id=str(ctx.request_id)) |
| 293 | + await ctx.session.send_progress_notification(token, 0.9, related_request_id=str(ctx.request_id)) |
282 | 294 | return CallToolResult(content=[TextContent(text="done")]) |
283 | 295 |
|
284 | 296 | server = Server("zigzagger", on_list_tools=list_tools, on_call_tool=call_tool) |
|
0 commit comments