Skip to content

refactor(core)!: provider results carry request and response information as upstream does - #219

Draft
cunninghamcard-bit wants to merge 14 commits into
rfc-0036/provider-prompt-typesfrom
rfc-0036/provider-result-types
Draft

cunninghamcard-bit wants to merge 14 commits into
rfc-0036/provider-prompt-typesfrom
rfc-0036/provider-result-types

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What

Move language-model request and response information into the provider result objects and carry it into the call-layer results, following LanguageModelV4GenerateResult, LanguageModelV4StreamResult, generateText, and streamText. This pull request is based on rfc-0036/provider-prompt-types and precedes rfc-0036/factory-test-removal. It removes the old flat JSON adapter, separates public text-stream chunks from provider chunks, and absorbs provider response metadata into the completed step. Provider implementations and language bindings are updated for these changes.

Before and after

Type or behavior Before After and upstream reference
Provider generation result GenerateResultWire flattened request body and response headers during serialization GenerateResult serializes directly with optional request: RequestInfo and response: ResponseInfo, corresponding to LanguageModelV4GenerateResult
Provider response types Separate GenerateResponse and StreamResponse types Shared ResponseInfo contains optional id, timestamp, model id, headers, and body; StreamResponseInfo contains optional headers, as in LanguageModelV4StreamResult
Non-streaming call result No top-level request; response held metadata alone GenerateTextResult.request carries the provider request and response carries metadata, headers, and body, following generate-text.ts; missing metadata gets call-layer defaults
GenerateTextResult.raw A second serialization path preserved the earlier flat JSON The field uses GenerateResult directly. The call layer moves request and response into its top-level fields, leaving those fields empty in raw
Public stream chunks TextStreamPart was an alias of the provider union and exposed response metadata events A distinct TextStreamPart enum receives content chunks; provider metadata is absorbed and exposed through FinishStep.response, following stream-text.ts
Incomplete stream without output Consumption used a generic invalid-response error; the direct stream could synthesize successful metadata The direct stream emits NoOutputGenerated, and consumption returns that error, following the upstream NoOutputGeneratedError path
Stream aggregation Request and response headers were lost StreamTextResultAggregated carries request information and the last step's response metadata and headers
OpenAI Responses and Azure streaming request information Returned request body included stream: true Only the HTTP body gets stream: true; the returned request body excludes it, following openai-responses-language-model.ts

Upstream's Date and number types are compile-time constraints, not runtime validation in these result types. Rust retains String timestamps and u32 token counts. Request and response bodies remain Value, which cannot represent every value allowed by upstream's unknown.

Tests

Earlier deletions of self-generated contract tests and hand-written tests remain in the stack. This branch retains upstream-ported tests and cassette replays; cassette_full_test.rs now matches TextStreamPart. Existing end-to-end stream assertions are migrated to FinishStep followed by Finish, and output round-trip callers use generate_text_result_to_chat_completion to read call-layer response metadata. Those hand-written end-to-end and round-trip test files still exist in this branch; this pull request does not claim they were deleted or ported from upstream. Cassette payloads and fixtures/ are unchanged.

Not in this pull request

  • Serde naming remains pending the maintainer's decision: snake_case fields, PascalCase external tags, Option values serialized as null where not omitted, and generated Node types with required-nullable fields. These representations are not claimed to match upstream's optional properties.
  • The call-layer ModelMessage tool-result shape remains pending the maintainer's decision. The new public stream enum still carries the provider ToolResult payload; it does not complete the call-layer tool-result and error separation. The stream also retains its Rust Result error channel.
  • Bedrock generation still exposes the full response body here, although upstream's Converse result omits it. This is fixed on rfc-0036/vendor-alignment.
  • Other model families' result contracts are outside this language-model result change; they need separate upstream comparison.
  • Removal of the remaining hand-written provider end-to-end and output round-trip tests is outside this result migration; their continued presence is not evidence of upstream behavior.

Not verified

No cargo command or runtime test was run for this documentation update. Go, Java, Kotlin, Swift, and Flutter files edited on this branch were not compiled locally. Node npm test and Python pytest were not run. CI YAML was only syntax-checked; CI execution is not verified. Providers without corresponding source in the local upstream packages were not independently checked for behavioral parity.

Verification

Gate on the final commit 6a1da337: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo doc, tests of core, provider-utils, providers, ffi, web, replay and cli: 2,316 passed, 0 failed; Node and Python bindings cargo check; provider boundary script; gen_ts_types.py --check, gen_providers_doc.py --check. Cassettes and fixtures/aisdk unchanged relative to the base. 61 files, +986 / −627.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS

chenhaonan and others added 14 commits October 5, 2026 11:57
… upstream does

What: `GenerateResult` has `request: Option<RequestInfo>` and
`response: Option<ResponseInfo>`; `ResponseInfo` holds the response id,
timestamp and model id next to headers and body. `StreamResult` has
`request` and `response: Option<StreamResponseInfo>` (headers only). The
flat `request_body` and `response_headers` fields are removed, and
`response` is no longer required. Vendors, tools, FFI and tests build and
read the new fields. The call layer fills a missing response id, timestamp
and model id where upstream `generateText` and `streamText` do, so a
provider no longer has to invent them.

Why: this is the shape of upstream's `LanguageModelV4GenerateResult` and
`LanguageModelV4StreamResult`. Before, a provider had to return response
metadata even when the API gave none, and the response body had no place.

Kept on purpose: the user-facing results and their JSON do not change. The
`raw` field of `GenerateTextResult` still serializes the flat keys through a
small adapter, because six language bindings parse that shape; it goes away
with `raw` in the user-result pull request. No cassette or fixture changes.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…ds as breaking

The provider trait output changes shape, so implementers of a custom
provider need the old and new field names side by side.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the media-type, text-data and tool-result alias fixes up the
stack so every branch above the prompt types has them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the tool, content, provider-option and prompt type changes up
the stack. This branch's request and response information is kept and the
newly arrived code is converted to it. Hand-written tests that no longer
compile are deleted.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: removes 86 test functions and in-file test modules that the branch
below had deleted and the last merge resolution restored.

Why: the project keeps cassette replays, upstream samples and contract
fixtures as its tests. The difference from the branch below is now only
this branch's own work on request and response information.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the tag spelling fix up the stack.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: removes the adapter that re-serialized GenerateTextResult.raw into
an earlier flat JSON. raw now serializes the provider result directly,
with its request and response information. The language bindings' result
types follow.

Why: the adapter was a second JSON path for one value and dropped the
response body. The Go, Java, Kotlin, Swift and Flutter changes are not
compiled here.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…ontract fixtures

Why: carry up the stack the source union, the removal of the separate
thought-signature field, the deletion of the self-generated contract
fixtures and the changes below this branch. This branch's code is
converted to them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…st and response on results

What: the call-layer stream no longer emits a separate metadata event;
response information arrives on the finish step, and an empty provider
stream is NoOutputGeneratedError. GenerateTextResult and the stream
aggregate carry request body and response id, timestamp, model id,
headers and body from the provider result. OpenAI Responses and Azure
expose the request body without the stream flag added for sending.

Why: upstream surfaces request and response this way and has no public
metadata event; the review found the extra event and the missing fields.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry up the stack the fixes made after the third independent
review on the branches below; this branch's code is converted to them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry up the stack the fixes made after the third independent
review on the branches below; this branch's code is converted to them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry up the stack the fixes made after the third independent
review on the branches below; this branch's code is converted to them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the formatting commit up the stack; no code change.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS

This branch has not been deployed

No deployments
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