MMSP-RFC-0008 Request Interruption
- Feature Name:
request_interruption - Start Date: 2026-10-24
- RFC PR: #301
- Kind: spec
Abstract
A caller can interrupt a request at any point by aborting its signal or closing its stream, and the provider's request then closes at once, in every client of both packages, with the one exception §3 names. This document specifies what the caller does and receives, how each language closes the provider's request, what an interrupted stateful call records, and how the server, the playground and the mmsp client propagate an interruption.
1. Introduction
The owner's decision was 允许立即中断: a caller can stop a stream at any point, and the provider's request ends with it. Before #292:
- closing or leaving a Python stream on the openai or google-genai SDK left the connection open until the garbage collector ran;
- an abort that came while the caller was busy, or while another task was reading, waited for the next read or the next event;
- an aborted TypeScript stream on the openai or anthropic SDK ended like a cut-off one:
.doneitems andIncompleteStreamError, or, once the usage had arrived, astopand a recorded turn.
So in Python the playground's Stop, and a server whose caller had gone, could keep the upstream request open for seconds, or for as long as the provider stayed silent. Stated in #298 as MMSP-RFC-0012.
2. Conventions and Terminology
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.
- Signal: the
AbortSignala call is given, frommmsp.abort_signalin Python and the platform's own in TypeScript. - Abort:
signal.abort(reason)in Python;abort(reason)on theAbortControllerthat owns the signal in TypeScript. - Close:
aclose()/return()on the stream's generator, or leaving the loop that reads it (in Python, once nothing else holds the generator). - Abort error:
asyncio.CancelledErrorcarrying the signal's reason in Python; the signal'sreasonin TypeScript. - Open item: the item whose deltas are streaming and which has no
.doneyet.
3. Interrupting a Stream
- A caller MAY interrupt a stream at any point by aborting its signal or closing it. The provider's request MUST close with it at once, except that the Python
mmspclient, aborted while the caller is busy between reads, closes its request at the caller's next read (§4). - After an abort the caller MUST get the deltas that arrived and then the abort error; a closed generator yields nothing more. The caller MUST NOT get a
.donefor the open item or astop, nor a partial.donefollowed byIncompleteStreamError. - A stream that ends without its
stopMUST record nothing and save no trace (MMSP-RFC-0004 §6.1, MMSP-RFC-0010 §3).
4. Python
Every Python client MUST read the provider's stream, where it has one, inside a context that closes it (async with stream:; aclosing on google_genai, which also closes the HTTP responses its request opened, because its SDK leaves them to the garbage collector), so closing the client's generator closes the connection. The base class does that as soon as the signal is aborted: it cancels a pending read in whichever task makes it, and with no read pending it closes the client's generator where it rests. The mmsp client forwards its server's stream in a streaming_response of its own, which cancels a pending read too but watches the signal only while a read is pending, so an abort with no read pending takes effect at the caller's next read, which raises the abort error and closes the request. AutoLLMClient and streaming_response_stateful close the generator they read with their own.
5. TypeScript
Every TypeScript client MUST pass the signal to its SDK, or to fetch in the mmsp client. google_genai passes it through an AbortController of its own, which its generator aborts on the way out too, because closing that SDK's stream leaves the connection open. An aborted stream MUST throw the caller's reason, not the SDK's: the base class throws it when the SDK ends its stream quietly, and google_genai rethrows it in place of the SDK's own AbortError.
6. Stateful Calls
An abort MUST release a stateful call's claim on the client at once, and the aborted call MUST NOT record its turn, even when the abort lands as the stop arrives (MMSP-RFC-0004 §6.2). An abort that lands as the provider's stream ends MUST still end the stream as an abort.
7. Server, Playground and mmsp Client
- The MMSP server and the playground MUST abort their upstream when they find their caller gone, and the playground's Stop MUST abort it too.
- The Python server finds a disconnect when a write fails, at its next event or keep-alive, and from then on closes the upstream at once.
- The
mmspclient's signal ends its stream mid-read, and between events at once in TypeScript and at the caller's next read in Python (§4).
8. Leaving Without Aborting
A caller that leaves a Python stream before its stop, without aborting or closing it, holds the provider's request, and on a stateful call the client, until the generator is closed: by await stream.aclose(), or by the event loop once nothing holds the generator. In TypeScript, leaving for await closes it. A caller that keeps a stream it leaves SHOULD abort or close it.
9. Conformance
src_py/mmsp/base_client.py/src_ts/src/baseClient.ts;src_py/mmsp/abort_signal.py;src_py/mmsp/auto_client.py; the stream loop of every client undersrc_py/mmsp/<client>/client.pyandsrc_ts/src/<client>/client.ts.src_py/mmsp/integration/server.pyandplayground.py, mirrored insrc_ts/src/integration/server.tsandplayground.ts.src_py/tests/test_interruption.py/src_ts/tests/interruption.test.ts, which stream every streaming client type, stateless and stateful, from a local upstream that goes silent, and check that:- the connection closes within 200 ms of an abort while the caller waits, of the caller's next read after an abort while it was busy, of
aclose()/return(), and of leaving the loop; - the caller gets the abort error and no
.doneorstop; - nothing is recorded or traced, also when the abort lands after the usage and the finish reason arrived.
- the connection closes within 200 ms of an abort while the caller waits, of the caller's next read after an abort while it was busy, of
test_abort_signal.py;test_mmsp_server.py/mmsp-server.test.ts.