You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add end-to-end response streaming for Praxis subrequests, including long-lived
SSE responses, without replacing or weakening the buffered SubResponse API.
StreamLimits should distinguish bounded memory from total response length:
Maximum buffered/in-flight bytes.
Optional maximum total bytes.
Idle timeout.
Optional overall timeout.
Whether a subrequest response is buffered or streamed is selected by filters,
not by Praxis configuration. The response mode defaults to buffered. A filter
that constructs the final outbound request may select streaming through a
typed HttpFilterContext API after deciding the effective request body and
whether the response can be exposed directly. Filters that may make this
selection should declare that capability so IRR can validate their step
pipelines conservatively.
For the OpenAI Responses API, the AI filters own this decision. They may use
the client request's stream value and orchestration state as inputs, but the
filter that constructs the effective outbound request must keep its serialized "stream": true value and the typed Praxis response-mode selection aligned.
Praxis core and IRR must not parse OpenAI JSON or depend on AI metadata keys.
Streaming must preserve the Praxis filter lifecycle. The iterative request
router must run response-header filters before deciding whether to transition
or expose the response. Once downstream headers are committed, failover is no
longer possible; later transport or filter failures terminate the stream.
Streaming should initially be supported only for a response that is terminal
once selected. IRR may still perform header-safe failover before exposing that
response. Intermediate rounds remain buffered because their bodies may be
needed by later transitions or steps.
Body-dependent transitions and filters requiring BodyMode::StreamBuffer must
be rejected during configuration validation when a step pipeline declares
that it may select streaming. Equivalent runtime guards must remain in place
for dynamically registered filters. Praxis must not silently turn a
filter-selected stream back into a buffered response.
Core owns transport mechanics, backpressure, resource cleanup, and typed
transport errors. The filter and protocol layers continue to own filter
execution, transition policy, downstream framing, and HTTP semantics.
Non-goals
Streaming request bodies.
Streaming intermediate IRR rounds whose response bodies affect control flow.
Retrying or failing over after downstream headers are committed.
A Praxis YAML option that decides whether a client requested streaming.
Parsing provider-specific request bodies or metadata in Praxis core or IRR.
SSE parsing or provider-specific transformation in praxis-core.
Routing, SSRF policy, retry policy, or HTTP-status interpretation in SubRequestClient.
WebSocket or HTTP upgrade tunnelling.
Replacing SubResponse with a buffered/streaming enum.
Silently buffering streams for incompatible filters.
Success Criteria
praxis-core exposes an opaque streaming response API that does not expose
Pingora session mechanics.
Streaming and buffered requests share the same hardened connection, framing,
sanitization, admission, deadline, and error-handling implementation.
Response chunks reach the downstream client incrementally and are subject to
real backpressure.
Long-lived SSE responses are not constrained by the buffered-response ceiling
unless an explicit total limit is configured.
Idle and overall deadlines have distinct, documented behavior.
Cancellation, timeout, downstream disconnect, early drop, and body errors
release the admission permit and safely close or reuse the upstream
connection.
HTTP/1.1 incomplete responses are never returned to the pool; HTTP/2
cancellation resets only the affected stream.
The filter API exposes a typed, per-subrequest buffered/streaming selection;
buffered remains the default.
IRR uses the filter-selected mode without interpreting OpenAI request bodies
or AI metadata keys.
Streaming IRR transitions can depend only on information available before the
body is exposed, such as status, origin, transport error, or default rules.
Step response-header filters run before transition evaluation.
Step and parent response-body filters run once per chunk with valid owned
state across the stream lifetime.
Response-body completion hooks run exactly once.
No transition or fallback occurs after downstream response headers are
committed.
Protocol handling correctly implements HEAD, 204, and 304 body suppression
and HTTP/1.1 versus HTTP/2 framing.
Incompatible body-buffering filters and body-dependent transitions fail
configuration validation for pipelines that declare streaming capability,
with runtime guards for dynamic cases.
Integration tests use a test-only mode-selecting filter and prove that SSE
data arrives before the upstream response completes.
The public filter contract is documented sufficiently for Praxis AI to
select streaming without a separate callout client.
Praxis AI provides the functional Responses SSE example when it adopts the
stable API; Praxis does not add a production streaming-mode configuration
filter solely for an example.
Summary
Add end-to-end response streaming for Praxis subrequests, including long-lived
SSE responses, without replacing or weakening the buffered
SubResponseAPI.This epic builds on:
SubRequestandSubResponseintopraxis-core.SubRequestClient.The streaming API should be additive:
SubResponseremains buffered, cloneable, and suitable for ordinarysubrequests.
permit, cancellation state, limits, and response body.
streaming response through the same hardened transport lifecycle.
A representative core interface is:
StreamLimitsshould distinguish bounded memory from total response length:Whether a subrequest response is buffered or streamed is selected by filters,
not by Praxis configuration. The response mode defaults to buffered. A filter
that constructs the final outbound request may select streaming through a
typed
HttpFilterContextAPI after deciding the effective request body andwhether the response can be exposed directly. Filters that may make this
selection should declare that capability so IRR can validate their step
pipelines conservatively.
For the OpenAI Responses API, the AI filters own this decision. They may use
the client request's
streamvalue and orchestration state as inputs, but thefilter that constructs the effective outbound request must keep its serialized
"stream": truevalue and the typed Praxis response-mode selection aligned.Praxis core and IRR must not parse OpenAI JSON or depend on AI metadata keys.
Streaming must preserve the Praxis filter lifecycle. The iterative request
router must run response-header filters before deciding whether to transition
or expose the response. Once downstream headers are committed, failover is no
longer possible; later transport or filter failures terminate the stream.
Streaming should initially be supported only for a response that is terminal
once selected. IRR may still perform header-safe failover before exposing that
response. Intermediate rounds remain buffered because their bodies may be
needed by later transitions or steps.
Body-dependent transitions and filters requiring
BodyMode::StreamBuffermustbe rejected during configuration validation when a step pipeline declares
that it may select streaming. Equivalent runtime guards must remain in place
for dynamically registered filters. Praxis must not silently turn a
filter-selected stream back into a buffered response.
Core owns transport mechanics, backpressure, resource cleanup, and typed
transport errors. The filter and protocol layers continue to own filter
execution, transition policy, downstream framing, and HTTP semantics.
Non-goals
praxis-core.SubRequestClient.SubResponsewith a buffered/streaming enum.Success Criteria
praxis-coreexposes an opaque streaming response API that does not exposePingora session mechanics.
sanitization, admission, deadline, and error-handling implementation.
real backpressure.
unless an explicit total limit is configured.
release the admission permit and safely close or reuse the upstream
connection.
cancellation resets only the affected stream.
buffered remains the default.
or AI metadata keys.
body is exposed, such as status, origin, transport error, or default rules.
state across the stream lifetime.
committed.
and HTTP/1.1 versus HTTP/2 framing.
configuration validation for pipelines that declare streaming capability,
with runtime guards for dynamic cases.
data arrives before the upstream response completes.
select streaming without a separate callout client.
stable API; Praxis does not add a production streaming-mode configuration
filter solely for an example.
Sub-Tasks
SubRequestClienttracked by core: add a hardened SubRequestClient over SubRequestConnector #826.ownership.
transport path.
declaration.
bodies.
on_request().chunks.
finish constructing the effective outbound request.
add static plus runtime compatibility validation.
timeouts, and termination causes.
cleanup, and HTTP/1.1 and HTTP/2 reuse.
hooks, and incompatible body modes.
latency, framing, header-based failover, downstream disconnects, and late
errors.
rounds buffered and adding the functional Responses SSE example there.
Area
Filter Pipeline