pub struct StreamFinal {
pub kind: StreamFinalKind,
pub usage: Usage,
pub finish_reason: Option<FinishReason>,
pub message_id: Option<String>,
pub response_id: Option<String>,
pub provider_request_id: Option<String>,
pub provider: String,
pub model: Option<String>,
pub raw: Value,
}Expand description
The provider’s terminal stream record, normalized.
This replaces the provider-typed final payload that streams used to carry:
usage is a plain field rather than a trait method, and the finish reason is
normalized exactly as on the unary CompletionResponse.
Providers that want their own terminal type keep it behind
RawStreamingResult and map it once with normalize_stream.
§Emission contract
A terminal record is emitted only when the provider signaled genuine
completion — its own end-of-response event (an Anthropic message_delta
with a stop reason, an OpenAI [DONE] / response.completed, a Gemini
chunk carrying finishReason, and so on). Three failure shapes reach a
consumer, and they are distinct:
| Shape | Err item | Stream continues | Terminal record |
|---|---|---|---|
| Transport error (connection lost, HTTP failure) | yes | no | never |
| Malformed frame (recoverable parse error) | yes | yes | if a genuine terminal later arrives |
| Truncation (EOF without the provider’s end event) | no | — | never |
On a terminal error (a transport failure or the provider’s own failure
event), tool calls that were fully delivered before the failure are yielded
before the terminal Err; nothing follows the error — the stream then
ends without a terminal record.
Consequently an Err item is not by itself terminal: a malformed frame
is surfaced and the stream keeps consuming, so a later genuine terminal
still completes it. Consumers must drain the stream to None rather than
stop at the first Err, and must treat the absence of a terminal record as
truncation, never as a successful zero-usage completion.
Fields§
§kind: StreamFinalKindDiscriminating field; always StreamFinalKind::Final.
usage: UsageToken usage reported by the provider for this streamed completion. Zero-valued usage is the documented sentinel for missing metrics.
finish_reason: Option<FinishReason>Why the model stopped generating, when the provider reported it.
normalize_stream applies
FinishReason::reconcile_with_output
to this value using the tool calls actually seen on the stream, so a
provider mapper does not need to (and cannot — it has no view of the
preceding events).
message_id: Option<String>Provider-assigned assistant message ID, when available — only IDs the
provider would recognize on a replayed assistant message. Response-scoped
identifiers belong in StreamFinal::response_id.
response_id: Option<String>Provider-assigned response-scoped ID, when available — e.g. an OpenAI
chat chatcmpl- ID. Never replayed to a provider as a message ID.
provider_request_id: Option<String>The provider’s transport-level request identifier, taken from the SSE
connection’s HTTP response headers (Anthropic request-id, OpenAI/xAI
x-request-id). When the source reconnected, this is the connection
that delivered this terminal record. Never the body’s message/response
id. None means the provider did not report one — a documented
outcome, never an error.
provider: StringStable descriptor name of the provider that produced this stream.
model: Option<String>Provider-reported model identifier, when available.
raw: ValueThe provider’s own terminal record for this stream: the value the
model’s inherent raw_stream would have yielded as its FinalResponse,
serialized. It is the terminal record as rig’s wire type parsed it —
fields that type does not model are not here — and it is the terminal
record only, not the stream’s frames; see the module docs for why
frames are a separate mechanism. normalize_stream populates it
unconditionally — the same parity the pre-normalization Final(R) had.
An escape hatch for provider-specific data rig does not normalize — it
never replaces a normalized field, and every normalized field means the
same thing whatever this holds. Value::Null means the record was
built without a provider behind it — StreamFinal::new without
with_raw (a provider’s mapper before normalize_stream attaches
the terminal, test doubles, hand-built records), or a record persisted
before the field existed — never that the provider sent nothing: no
stream that reached its terminal yields Null here.
Typed access is recoverable: provider terminal types are
Deserialize, so provider::StreamingCompletionResponse::deserialize(&raw)
returns the provider’s own type.
Implementations§
Source§impl StreamFinal
impl StreamFinal
Sourcepub fn new(provider: impl Into<String>, usage: Usage) -> StreamFinal
pub fn new(provider: impl Into<String>, usage: Usage) -> StreamFinal
Create a terminal record for provider with usage; optional metadata
starts unset and is filled in with the with_* helpers.
Sourcepub fn with_finish_reason(self, finish_reason: FinishReason) -> StreamFinal
pub fn with_finish_reason(self, finish_reason: FinishReason) -> StreamFinal
Attach the normalized finish reason.
Sourcepub fn with_optional_finish_reason(
self,
finish_reason: Option<FinishReason>,
) -> StreamFinal
pub fn with_optional_finish_reason( self, finish_reason: Option<FinishReason>, ) -> StreamFinal
Attach the normalized finish reason when the provider reported one.
Sourcepub fn identity(&self) -> ResponseIdentity
pub fn identity(&self) -> ResponseIdentity
This terminal record’s identity metadata as one
crate::completion::ResponseIdentity carrier.
Source§impl StreamFinal
impl StreamFinal
Sourcepub fn with_message_id(self, message_id: impl Into<String>) -> StreamFinal
pub fn with_message_id(self, message_id: impl Into<String>) -> StreamFinal
Attach the provider-assigned message ID.
An empty string is treated as absent: gateways that echo ""
for fields they don’t populate must not produce a Some("")
that differs between the buffered and streaming paths. All
identifier and model setters share this rule so the invariant
lives here rather than at every provider call site.
Sourcepub fn with_optional_message_id(
self,
message_id: Option<impl Into<String>>,
) -> StreamFinal
pub fn with_optional_message_id( self, message_id: Option<impl Into<String>>, ) -> StreamFinal
Attach the provider-assigned message ID when the provider reported one.
Sourcepub fn with_response_id(self, response_id: impl Into<String>) -> StreamFinal
pub fn with_response_id(self, response_id: impl Into<String>) -> StreamFinal
Attach the provider-assigned response-scoped ID.
Sourcepub fn with_optional_response_id(
self,
response_id: Option<impl Into<String>>,
) -> StreamFinal
pub fn with_optional_response_id( self, response_id: Option<impl Into<String>>, ) -> StreamFinal
Attach the provider-assigned response-scoped ID when the provider reported one.
Sourcepub fn with_provider_request_id(
self,
request_id: impl Into<String>,
) -> StreamFinal
pub fn with_provider_request_id( self, request_id: impl Into<String>, ) -> StreamFinal
Attach the provider’s transport-level request identifier.
Sourcepub fn with_optional_provider_request_id(
self,
request_id: Option<impl Into<String>>,
) -> StreamFinal
pub fn with_optional_provider_request_id( self, request_id: Option<impl Into<String>>, ) -> StreamFinal
Attach the provider’s transport-level request identifier when the provider reported one.
Sourcepub fn with_model(self, model: impl Into<String>) -> StreamFinal
pub fn with_model(self, model: impl Into<String>) -> StreamFinal
Attach the provider-reported model identifier.
An empty string is treated as absent, matching the identifier setters.
Sourcepub fn with_optional_model(
self,
model: Option<impl Into<String>>,
) -> StreamFinal
pub fn with_optional_model( self, model: Option<impl Into<String>>, ) -> StreamFinal
Attach the provider-reported model identifier when the response carried one.
Sourcepub fn with_raw(self, raw: impl Into<Value>) -> StreamFinal
pub fn with_raw(self, raw: impl Into<Value>) -> StreamFinal
Attach the provider’s own response, serialized — the value the
model’s inherent raw method would have returned. Every provider
seam calls this; see the raw field for the exact meaning of
the payload (and of Value::Null).
Trait Implementations§
Source§impl Clone for StreamFinal
impl Clone for StreamFinal
Source§fn clone(&self) -> StreamFinal
fn clone(&self) -> StreamFinal
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for StreamFinal
impl Debug for StreamFinal
Source§impl<'de> Deserialize<'de> for StreamFinal
impl<'de> Deserialize<'de> for StreamFinal
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<StreamFinal, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<StreamFinal, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl From<(&str, CopilotStreamingResponse)> for StreamFinal
impl From<(&str, CopilotStreamingResponse)> for StreamFinal
Source§fn from(_: (&str, CopilotStreamingResponse)) -> StreamFinal
fn from(_: (&str, CopilotStreamingResponse)) -> StreamFinal
Source§impl From<(&str, StreamingCompletionResponse)> for StreamFinal
Normalize an Anthropic terminal stream record.
impl From<(&str, StreamingCompletionResponse)> for StreamFinal
Normalize an Anthropic terminal stream record.
The provider descriptor name is an input rather than a constant: the
Anthropic Messages stream format is shared by every Anthropic-compatible
provider, so baking in "anthropic" here would mislabel all of them.
Source§fn from(_: (&str, StreamingCompletionResponse)) -> StreamFinal
fn from(_: (&str, StreamingCompletionResponse)) -> StreamFinal
Source§impl From<(&str, StreamingCompletionResponse)> for StreamFinal
Normalize the Responses API’s terminal stream record.
impl From<(&str, StreamingCompletionResponse)> for StreamFinal
Normalize the Responses API’s terminal stream record.
The provider descriptor name is an input for the same reason it is on the
unary conversion: ChatGPT and Copilot stream this exact wire shape, so a
baked-in "openai" would mislabel them.
The finish reason is left exactly as the provider reported it;
crate::streaming::normalize_stream applies the tool-call reconciliation
afterwards, using the calls the stream actually emitted.
Source§fn from(_: (&str, StreamingCompletionResponse)) -> StreamFinal
fn from(_: (&str, StreamingCompletionResponse)) -> StreamFinal
Source§impl<U> From<(&str, StreamingCompletionResponse<U>)> for StreamFinal
Normalize an OpenAI-compatible streaming terminal record.
impl<U> From<(&str, StreamingCompletionResponse<U>)> for StreamFinal
Normalize an OpenAI-compatible streaming terminal record.
As on the unary path, the provider descriptor name is an input rather than
a constant: this terminal record is shared by every OpenAI-compatible
provider, so baking in "openai" here would mislabel Groq, Together,
DeepSeek and the rest.