Repository navigation
[4/8] refactor!: results and streams follow the AI SDK - #252
Merged
Merged
Conversation
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0eb91-5e79-775e-975e-b85e2ac49ab6 (cherry picked from commit 7f68ac3)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 758d0bc)
The returned impl Iterator is already must_use, so clippy's double_must_use rejected it. (provider.rs part of commit 8397424) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…e removal - scripts/gen_providers_doc.py: the `provider_name` module no longer exists; the docstrings and the section-header guard now name only `provider`. `--check` output is unchanged. - docs/internal/phase4-audit.md: add a follow-up note (same style as RFC-0020) so the historical audit no longer reads as if the generated ProviderName types and their drift tests still exist. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS (cherry picked from commit dfcd54f)
The A2 line said Node would keep a generated string-literal union for completion. It does not: overlay-registered provider names can never be part of a compile-time union, so keeping one would keep the second name path this PR removes. Record the actual A2 scope. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS (cherry picked from commit 3e2d9fc)
Pull requests into a branch other than master got no CI, so a stacked PR could not show it was green against the PR below it. The pull_request trigger now has no branch filter. A new push to a PR cancels that PR's run still in progress; pushes to master are never cancelled. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…that pin Rust-only behaviour The V4 migration that follows replaces these tests with tests ported from the AI SDK. They are deleted first, on their own, so the PRs after this one show only code changes. This commit removes only test files (Rust, the seven bindings, the shared contract fixtures and their Node runner) and the CI steps that ran the contract fixtures. No library code changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Amp-Thread-ID: https://ampcode.com/threads/T-01a0eb91-5e79-775e-975e-b85e2ac49ab6 Co-authored-by: Amp <amp@ampcode.com> (cherry picked from commit dc1cd14)
…ia alias - Wire*.ts were hand-maintained mirrors of wire.rs and had already drifted (WireContentPart lacked provider_executed). Derive TS on the wire structs and export them to web/src/types/generated/ through gen_ts_types.py, which now handles two targets and checks both in CI. - Core types resolve through the @aimux/types tsconfig alias instead of five-level relative paths into bindings/node. - gen_ts_types.py sync no longer rewrites files that differ only by ts-rs end-of-line spaces. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 69c1351)
No crate in the workspace referenced the tracker; providers accumulate streamed tool-call deltas themselves. Drops the module, its 787-line test file and the re-exported companion types. Breaking for aimux-stream's public Rust API; noted in CHANGELOG under Unreleased. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EYDYDWYcPsDmjBjFeuDVe (cherry picked from commit fe8758f)
Re-adds the tracker as a port of the current @ai-sdk/provider-utils streaming-tool-call-tracker.ts instead of the older index-only version removed in the previous commit. - Correlate deltas by wire id, index and function name; drop ambiguous ones - Unique tool-call ids with bounded suffixes; blank generator output falls back to "tool-call" - Ignore unmatched blank function names; retain blank-name continuations - Flush in index order only when every call has an index - New StreamingToolCallArgumentState / starts_with_structured_value port - process_delta and flush return the emitted parts instead of buffering Tests are ported case for case from the upstream suites (45 tracker cases, 9 argument-state cases). No caller in the workspace uses the tracker yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015EYDYDWYcPsDmjBjFeuDVe (cherry picked from commit 6035201)
…nd wire the OpenAI stream to it The AI SDK keeps StreamingToolCallTracker in @ai-sdk/provider-utils, where every provider that speaks the OpenAI chat-completions wire format imports it. aimux had the tracker in aimux-stream (the eventsource-parser role), with its own ToolCallStreamPart event type and no callers, while openai/model.rs kept an index-only accumulator. This commit puts the pieces where the AI SDK has them. aimux-stream - StreamingToolCallTracker, StreamingToolCallArgumentState, starts_with_structured_value and ToolCallStreamPart move out; the crate is SSE decoding only. serde_json becomes a dev-dependency (test fixture); the `tool-calls` keyword is dropped. aimux-provider-utils - streaming_tool_call_tracker.rs / streaming_tool_call_argument_state.rs land here, logic unchanged. The tracker emits aimux_core::StreamPart ToolInputStart / ToolInputDelta / ToolInputEnd / ToolCall directly, so there is no second event type (RFC-0036: ToolCallStreamPart must not be a second public protocol). The generic metadata parameter becomes serde_json::Value / ProviderMetadata; TrackerError converts into AiMuxError::InvalidResponseData. The ported upstream tests (45 + 9) move with it, projecting StreamPart back to the upstream event shape. aimux-providers - openai/model.rs replaces its HashMap<usize, ToolCallAccumulator> with the tracker: tool_calls deltas are correlated by wire id, index and function name instead of index alone; id-less continuations follow their call; indices reused across parallel calls stay distinct; ambiguous deltas are dropped; a call without a wire id gets a generated `tool-call` / `tool-call-N` id instead of an empty string; a new call without a function name ends the stream with InvalidResponseData (AI SDK behaviour) instead of starting a call with an empty name. - DeltaToolCall.index is Option<usize> (the AI SDK schema is `index: z.number().nullish()`). Every registry-backed provider goes through openai/model.rs, so this is the one plug point until S4-2 folds the mistral and xai chat copies into the same implementation. Verification: cargo test -p aimux-stream -p aimux-provider-utils; cargo test -p aimux-providers (129 suites, 3041 passed, 0 failed, including the cassette replays); cargo clippy -p aimux-stream -p aimux-provider-utils -p aimux-providers --all-targets -- -D warnings; cargo fmt --all -- --check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS (cherry picked from commit df4d09f)
…-state port Carried over from the SSE test port commit so the two split branches leave the file identical. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS (cherry picked from commit 7bc200e)
Describes only the final API: the tracker lives in aimux-provider-utils and emits StreamPart directly; the intermediate ToolCallStreamPart- returning API from earlier commits is not mentioned. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS (cherry picked from commit 264ade3)
… Rust Same behaviour as the upstream port (checked differentially against the previous implementation on ~1.1M random deltas), with Rust structure: - Correlate by linear scan over per-call wire ids and indices instead of id/index hash maps re-sorted on every delta; no per-delta allocations besides the emitted parts. - `finish(self)` consumes the tracker, so the never-true-before-flush `has_finished` state is gone and post-flush use cannot compile. - `StreamingToolCallDelta<'a>` borrows from the provider wire type and carries `provider_metadata`, replacing `StreamingToolCallFunction`, the untyped `extra` bag, the test-only builders and the two boxed metadata hooks. - `process` / `finish` append to a caller-owned buffer. - Drop the unreachable `TrackerError::IdExhausted`. - The argument state scans bytes and models completion as its own state. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee (cherry picked from commit b0fd923)
…es and docs With the tracker in aimux-provider-utils, aimux-stream only decodes SSE: serde_json is needed by its tests alone, and the tool-calls keyword no longer applies. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
- Drop the redundant #[must_use] on provider_names(); the returned impl Iterator is already must_use, so clippy::double_must_use failed CI. - Gate the wire types' ts-rs export tests behind a ts-export feature that only gen_ts_types.py enables. A plain cargo test previously wrote Wire*.ts into bindings/node/src/types via the workspace-wide TS_RS_EXPORT_DIR, which made gen_ts_types.py --check fail on stale files. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 8397424)
…ow the AI SDK V4 types - Content: one source, file and reasoning type per layer. A source is a url source or a document source; file citations and grounding documents map to document sources. The missing upstream content variants and usage fields are added. - Provider options and provider metadata have one typed shape. Vendor packages build and read them; a provider option that is not an object is an error, unknown keys no longer leak, and option parse errors surface for Cohere, Google and Mistral. Bedrock reads amazonBedrock before bedrock. - The provider prompt is modelled by role with one file part. Part tags use upstream's hyphenated names; references, files and tool output convert as upstream does, and file parts keep upstream's media-type and text-data rules. - The #226 decision adapters and Jev follow the typed options, metadata and split usage types. - Hand-written tests that built provider prompts by hand or pinned Rust-only behaviour are deleted; ported upstream tests are restored. FFI, bindings and their generated types follow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…ion as upstream does - Provider generate and stream results carry request and response information, as the upstream V4 result types do; raw is the provider result itself. - The response metadata stream part is absorbed into the finish step, and request and response are carried onto the call-layer results. - A tool-call repair applied to a result also updates the result's content. CHANGELOG records the result fields as breaking; the migration notes describe tool-call metadata on StreamingToolCallDelta. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
One tool struct per layer, field for field with the pinned upstream types:
- CallOptions.tool_choice is optional. Vendors omit an unset choice, as
their upstream packages do; generate_text and stream_text default it to
auto when tools are given, as upstream prepare-tool-choice does.
- FunctionTool.input_examples is a list of { input: object } and
ProviderTool.args is a JSON object.
- Tool calls carry no separate thought signature. The value travels in
provider metadata on output and provider options on input, under the
vendor namespace, where each upstream vendor package reads and writes it.
- Call sites pass the tool structs instead of their field lists.
The fifteen tool-choice tests ported from upstream (OpenAI, Groq, Mistral,
Bedrock) are kept and adapted; hand-written tests that pinned the old
shapes are deleted. CHANGELOG and docs/api/gaps.md record the wire-format
changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Since messages follow the AI SDK V4 types, a reasoning part keeps its signature in provider_options.anthropic.signature, as the Node test already does. The Go round-trip test still sent the removed top-level `signature`, so no thinking block reached the wire. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…uctor The content field was added to StreamTextResultAggregated and to its generated fromJson, but not to the constructor, so lib/types.g.dart did not compile and every test file importing the types failed to load. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
6 of 12 tasks
…more Deleting the hand-written typed GenerateTextResult test left _TypedArgs.maxOutputTokens and .temperature without a caller, which flutter analyze reports as unused_element_parameter warnings and fails the job. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…results-and-streams-follow-the-ai-sdk
…ned code The results layer adds AIMUX_E_NO_OUTPUT_GENERATED = 18 (the AI SDK's NoOutputGeneratedError) and every binding maps it, but the Go, Java, Kotlin and Flutter tests still used 18 as the first value outside the enum. They now check that 18 resolves to NoOutputGenerated and that 19 is rejected. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
stream_text now emits a FinishStep part (finish reason, usage, provider metadata and the step's response metadata) before Finish, as the AI SDK's finish-step part. The Swift StreamPart decoder threw "unknown stream-part tag FinishStep", which failed every typed stream. It is a first-class case now, as in the Python wrapper. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The file is a working note for the provider options / metadata namespace migration and says on line 3 that it must not be committed. Nothing in the tree links to it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The InputRecord.options comment no longer names a recording_context field (docs(core) commit before); the generated Node type carries the same text. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in the tool call tracker's default generate_id. No conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in the tool call tracker's default generate_id. No conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Stacks the series on #247 (which carries #246): aimux-core's ts-rs exports run only under the ts-export feature that scripts/gen_ts_types.py enables, and the generated ProviderName types are gone. Each later layer gates the exports it adds in its own merge, so no layer leaves an ungated export. Resolution: scripts/local-ci.sh keeps this layer's version without the retired contract section; CHANGELOG keeps both sides, the ts-rs entry as a Fixed section before Removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #247 (with #246) through the layer below: ts-rs exports run only under the ts-export feature, and the generated ProviderName types are gone. Resolution: the aimux-core files keep this layer's code, and every export in them, including the ones this layer adds (ToolChoice, the tool and stream-part types), is gated as #[cfg_attr(feature = "ts-export", ts(export))]; CHANGELOG keeps both sides. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #247 (with #246) through the layer below: ts-rs exports run only under the ts-export feature, and the generated ProviderName types are gone. Resolution: the aimux-core files keep this layer's code, and every export in them, including the ones this layer adds to the message, content, result and stream types, is gated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #247 (with #246) through the layer below: ts-rs exports run only under the ts-export feature, and the generated ProviderName types are gone. Resolution: the aimux-core files keep this layer's code with every export gated, including the result, step and output types this layer adds. The web console's copies of the core types, which this layer had updated, stay deleted as #247 deletes them; engine.ts and client.ts import TextStreamPart and the other core types from @aimux/types and the wire types from @/types/generated, and the web typecheck passes. docs/api/reference.md keeps this layer's rows without the ProviderName row; CHANGELOG keeps both sides. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The entry for supportsDeferredResults read as if every tool result without its call were checked. Only generate_text checks it, as upstream's generateText does (ai@7.0.127 convert-language-model-content.ts:96-103); stream_text, like streamText, passes the result on without its input (stream-language-model-call.ts:760-798), which stream_tool_results_and_errors_follow_the_ai_sdk already pins. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in #266 (a blank key on a custom base URL is sent as an empty key, as the AI SDK does for apiKey: ''). Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Contributor
Author
Generated by Claude Code |
The fetch test pointed npm's user and global config at the same empty
file. npm refuses that ("double-loading config ... as global, previously
loaded as user") and exits before any request, so on CI every test that
downloads failed with "npm pack failed". It passed in the cloud only
because the environment's NPM_CONFIG_USERCONFIG overrode the test's own
setting.
The test now writes two files, and drops inherited npm_config_* variables
in any case before setting its own, so it runs the same everywhere.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in 6ca248e: the fetch test gives npm separate user and global config files, so the AI SDK pin scripts step in the generator drift job passes on CI. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in 6ca248e: the fetch test gives npm separate user and global config files, so the AI SDK pin scripts step in the generator drift job passes on CI. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in 6ca248e: the fetch test gives npm separate user and global config files, so the AI SDK pin scripts step in the generator drift job passes on CI. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…ings #252 took max_retries, timeout and session_id off CallOptions and made them operation settings, but RFC-0015, RFC-0016, RFC-0017, RFC-0023, RFC-0024, RFC-0029 and RFC-0041 still named them as CallOptions fields. Each of those RFCs gets a later-change line in its header: the three are set on GenerateTextOptions, generate_text and stream_text put them in OperationSettings, and code below the model interface reads them with OperationSettings::current(), as the AI SDK keeps maxRetries and timeout on generateText and not in LanguageModelV4CallOptions; recordings still write them under the old keys. The passages that describe the current shape are rewritten (RFC-0015 §5 and §8, RFC-0017 §2.4, the RFC-0023 §1 table, RFC-0024 §1.2 and §3.1, RFC-0029 §5.4, the RFC-0041 §10.2 row); dated implementation records keep the field where it was then. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in master at ec338c9 (#262, #246 and #247, each squashed into one commit) through [1/8], whose master merge eric made (2a0b02b). The tree is unchanged: this branch already had all three. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in master at ec338c9 (#262, #246 and #247, each squashed into one commit) through [2/8]. The tree is unchanged: this branch already had all three. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings in master at ec338c9 (#262, #246 and #247, each squashed into one commit) through [3/8]. The tree is unchanged: this branch already had all three. Resolution: no conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
7 of 12 tasks
eric8810
changed the base branch from
v4/3-messages-and-content-follow-the-ai-sdk
to
master
October 17, 2026 13:27
…ster merge The master merge (afd378f) left a conflict block in StepModel.ts and master's trailing spaces in OutcomeRecord.ts, so the Generator drift check failed. Both files are back to what scripts/gen_ts_types.py generates on this layer; the next commit removes GenerateResponse.ts, which this layer no longer generates. The tree then equals the layer before the merge. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…ger generates The master merge (afd378f) brought it back from master; on this layer the type is gone, and the Generator drift check reports it as stale. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by li
Summary
Before: provider results do not carry request and response information, response metadata arrives as its own stream part, and applying a tool-call repair to a result leaves the result's content unchanged. Text results use aimux's own shapes: a nested usage, a finish-reason struct,
raw, a separateStreamTextResultAggregatedandgenerate_object, and no steps. The stream has aimux's own part set, withdeltaon text deltas. Response messages come from a builder that leaves out upstream rules, for exampleinputSchemaInputon approval requests. The retry count, timeout and session id ride onCallOptions, so every model sees them. Each video model sets its own polling, and a hanging status check is not bounded by the poll timeout. A non-finiteRetry-Aftergives a hint ofi64::MAXms, and the RFC-0031 stream timeout tests are gone.After: provider generate and stream results carry request and response as the upstream V4 result types do, and
rawis the provider result itself. Response metadata is absorbed into the finish step. Text results follow ai 7.0.127:steps,finalStep,totalUsageandoutput, with a flatLanguageModelUsageand a string finish reason; text, tool calls, usage and the rest are derived from the steps. Object generation goes throughoutput, andgenerate_objectis a deprecated wrapper over it. The stream has upstream's 26 part types (framing, typed tool results and tool errors,tool-output-denied,tool-approval-response, ...), text and reasoning deltas carrytext, and an invalid tool call is followed by a tool error. Response messages come from an accumulator plusto_response_messageswith upstream's rules, and a tool-call repair also updates the result's content. A provider tool result without its call is an error. The retry count, timeout and session id are operation settings, scoped around the model call. Video polling followsgenerateVideo'spolloption. Retry errors read as upstream's, a non-finiteRetry-Aftergives no hint, and the stream timeout and pipeline tests are back.This PR takes the largest block of new work in the series.
Type of change
Checklist
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets -- -D warningspassescargo test --workspacepasses (cassette playback — no network/keys)README.md,docs/,rfc/,bindings/README.md)Notes for reviewers
ai7.0.127generate-text/(step-result, stream-text-result, stream-language-model-call, to-response-messages, output),types/usage.ts,generate-video/generate-video.ts,prompt/request-options.ts;@ai-sdk/provider-utilsretry-with-exponential-backoff.texthere; the JSON field follows in PR 8.titleandtoolMetadataare not (see below).generate_text, a provider tool result with no matching call is now an error unless a provider tool of that name setssupportsDeferredResults(see the review follow-up below).inputSchemaInputonly when it differs from the call's input, compared as upstreamisDeepEqualDatacompares (JavaScript number semantics).providerExecutedon typed tool results.stream_textdrops empty text deltas that carry no provider metadata.streamRetries: aimux has noonErrorcallback in the operation options, so the setting's shape needs a decision. With it unset, which is the upstream default, behaviour equals upstream. RFC-0031 and the alignment doc §5 say so.do_stream, so starting the timer later would leave a 200-then-silence stream unbounded. Following upstream needs every provider to return on response headers. Documented the same way; the restored pipeline tests pin the current behaviour.typetag on standalone tool calls and sources: it exists only in the JSON, so it comes in PR 8.tool-output-deniedis decoded everywhere but never emitted. Upstream emits it when it executes tools or collects approvals from the prompt, which aimux does not do.GenerateTextResultstores steps, total usage and output;text(),tool_calls(),usage()and the rest are accessors. Its JSON still writes the derived fields, so bindings keep readingtext,tool_callsandusageuntil PR 8.usagehas noraw, as upstream'saddLanguageModelUsage; vendor fields are onsteps[i].usage.raw.StreamTextResultAggregatedis gone, andconsume()returnsGenerateTextResult. There is nostream_object.typetags.CallOptionshas nomax_retries,timeoutorsession_id. They areOperationSettings, current insidegenerate_text,stream_textand replay. Recordings still write them under the old keys, so the recording format is unchanged.RetryErrorreadsFailed after N attempts. Last error: <message>.VideoModel::poll_config()and RunwayML's own poll settings are removed. Polling usesVideoCallOptions::pollwith the AI SDK defaults (5000 / 600000 ms), and a status check with its retries races the remaining poll time. Video batches keep their:batch:iidempotency keys, registered as a product difference.cargo test --workspacepass;scripts/gen_ts_types.py --checkis clean. The Go, Java, Kotlin, Node (ava) and Python (pytest) suites pass locally; Swift and Flutter run in CI.generate_object, video batches, C45, C47); README,API.mdand the Node, Python and Rust guides describe step-based results and the full stream; RFC-0015, RFC-0016, RFC-0017, RFC-0023, RFC-0024, RFC-0029 and RFC-0041 say the retry count, timeout and session id are operation settings (a later-change line in each header, and the passages that describe the current shape); CHANGELOG.stream_textchunk loop no longer depends on its arm order.StreamPart::split_payloadsplits a part into the variants that carry a payload (tool call, approval request, reasoning file) and the rest, whose payload types areInfallible; the loop has nounreachable!arm, and a new payload-carrying variant fails to compile. Behaviour is unchanged.recording_contextis gone fromCallOptionsandDecisionCallOptions(breaking). The recording context is a task-local operation scope:RecordingContext::scope(ctx, future)andRecordingContext::scope_stream(ctx, stream), read withRecordingContext::current()by the HTTP exchange, the trace layer and the Anthropic request builder.generate_text, a provider tool result without its call passes only when a provider tool of that name setssupportsDeferredResults: true, as upstreamgenerateTextdoes (ai@7.0.127generate-text/convert-language-model-content.ts:96-103);stream_textpasses such a result on without its input, asstreamTextdoes (stream-language-model-call.ts:760-798).ProviderTool(and so the generated Node type) and the typed provider tools of the Java, Kotlin, Swift, Python and Flutter bindings carry the optional flag; it is never sent to a vendor.ffi-x86_64-unknown-linux-gnuffi-aarch64-apple-darwinffi-x86_64-pc-windows-msvcnode-x86_64-unknown-linux-gnunode-aarch64-unknown-linux-gnunode-aarch64-apple-darwinnode-x86_64-apple-darwinnode-x86_64-pc-windows-msvcnode-aarch64-pc-windows-msvc