Skip to content

feat(openai)!: expose typed Responses retry observability - #21

Merged
danielkov merged 2 commits into
mainfrom
feat/responses-retry-observability
Sep 5, 2026
Merged

feat(openai)!: expose typed Responses retry observability#21
danielkov merged 2 commits into
mainfrom
feat/responses-retry-observability

Conversation

@danielkov

Copy link
Copy Markdown
Owner

Summary

Expose typed, sanitized Responses retry progress through loop observers and retain provider classification and retry accounting in terminal model failures. This supplies the upstream contract needed by Kit #45, #46, and #47.

Motivation

Initial retries occur inside begin_turn, before a model stream is available. Hosts need live progress during those waits and structured terminal metadata without parsing provider error strings.

Impact

Responses model-session failures now use LoopError::ProviderFailure, with an accessor for typed metadata. This is a breaking change for exhaustive LoopError matches. Existing serialized AgentEvent variants remain unchanged, but readers must understand the new retry variant before it crosses a versioned boundary.

The host still owns ACP delivery, fatal-record persistence, stable fatal correlation, and effects provenance. Downstream integration requires coordinated reader updates and separately published compatible crates.

Technical details

Bounded observations across request setup and streaming

A default-compatible ModelSession::set_retry_observer hook forwards Scheduled, Stopped, and Succeeded observations through AgentEvent::ProviderRetry. Scheduled snapshots are limited to one per 250 ms per model request, with no queue and unsuppressed terminal observations. These are not additional model results or effects records.

Sanitized classification and exact accounting

Canonical allowlists retain upstream type/code separately from local terminal and last-attempt reasons; unknown values become Unknown. No provider messages, response bodies, credentials, prompts, or endpoint URLs enter the payloads. Attempts count polled HTTP client executions, including reactive-authentication resends. Completed backoff sums only fully completed requested waits; elapsed time begins before authentication.

Explicit cancellation and deadline precedence

LoopError::Cancelled remains unchanged, with accounting available through the terminal observation. A default-compatible ModelTurn::on_cancelled hook finalizes library-owned post-event cancellation before the driver drops a turn. Cancellation and logical deadlines win ready-completion races, including buffered stream completion; arbitrary dropped futures do not promise terminal delivery.

Forward bounded per-request retry observations through loop observers and retain sanitized provider classification with terminal accounting. Count actual sends and completed waits, preserve cancellation semantics, and finalize library-owned cancellation and deadline races.

BREAKING CHANGE: Responses model-session failures now use LoopError::ProviderFailure. Downstream exhaustive LoopError matches and versioned AgentEvent readers must be updated before consuming the new contract.
Reject malformed and non-HTTP(S) endpoints locally instead of counting and retrying reqwest builder failures. Align invalid local attribution headers with InvalidRequest and verify zero executions with a real reqwest-backed transport.
@danielkov
danielkov merged commit 629e9eb into main Sep 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant