Repository navigation
[8/8] refactor!: public JSON equals the AI SDK JSON - #256
Merged
Merged
Conversation
This was referenced Oct 12, 2026
This was referenced Oct 13, 2026
Merged
The AI SDK puts its provider tools on the provider: openai.tools is openaiTools (openai-provider.ts:387) and azure.tools is azureOpenaiTools, the code interpreter, file search, image generation and web search subset (azure-openai-provider.ts:434, azure-openai-tools.ts). Give OpenAIProvider and AzureOpenAIProvider a public tools member over the existing openai::tools factories. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
openai-responses-language-model.ts:1660-1663 enqueues a raw part for every chunk, before the parts derived from it, when includeRawChunks is set. The Responses stream already does; nothing pinned it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Regenerated with scripts/gen_providers_doc.py after the Vertex MaaS and xAI sub-providers were added. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
is_sensitive_key matched only cookie, key, authorization, api-key and *token names, so X-Prodia-Key (sent by the Prodia provider) and Azure's Ocp-Apim-Subscription-Key would be written in clear text into recordings (and logs, which share the policy) whenever recording was on. Both names are now sensitive, the second as any name ending in subscription-key, separator-insensitive. A recording test sends them through the HTTP layer and checks that the stored exchange holds [REDACTED] and that neither secret appears in the JSONL. Carries master's d38a9d3 (#228 S2-1) to the V4 code. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The status_code field doc changed with the stream-error status fix (97e131d); this is scripts/gen_ts_types.py's output for it. The script's normalization also drops the file's old trailing spaces. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
Brings [6/8]'s vendor alignment (user-agents from the pinned versions, SigV4 per upstream, Vertex MaaS and xAI, Azure token honours omitted credentials, the restored registry base URL test) together with what [6/8] merged from [5/8] and [1/8]. No conflicts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The Dart NativeFinalizer called aimux_drop_handle(uint64_t) through a void (*)(void *) pointer. On 32-bit ARM (armeabi-v7a, which the release ships) a uint64_t argument does not travel like a pointer, so the drop received a garbage high word and a GC'd Model or ProviderHandle leaked its handle. aimux-ffi now exports aimux_drop_handle_ptr(void *), which takes the handle as a pointer-sized value and drops it (NULL is a no-op), and the Flutter finalizer uses it. Every existing export is unchanged. The C header and docs/api/c.md list it; the export count is 130. Tests: the Rust unit test and the export smoke test release handles through it, and a Flutter test checks it releases the handle its address names. Carries master's 3d57895 (#228 S2-3) to the V4 code. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
The binding's block_on calls for generate_text, generate_object, consume_stream_text, generate_text_as_openai, ProviderHandle.list_models, get_model_specs and the eight multimodal calls held the GIL while they waited on the network, so every other Python thread stopped for the whole request. They now run inside allow_threads, as decide() and the stream iterator already did, and so do the recording flushes, which wait on the writer thread. Results and errors are unchanged. test_gil.py serves the mock API from a thread of the test process: it can only answer while the call releases the GIL. Without the fix both calls fail with their 10 s total timeout. The chat case uses deepseek() (chat completions), since openai() builds a Responses model here. Carries master's 478522d (#228 C30) to the V4 code. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…t differs (fork #18, #12) The response messages' tool approval request part carries `input_schema_input` only when it is not deep-equal to the call's input under the AI SDK's `isDeepEqualData` (ai to-response-messages.ts:133-146, util/is-deep-equal-data.ts:8-48): numbers compare as JavaScript doubles, objects by key set in any order, arrays element by element, and `[]` is not `{}`. The builder wrote `None` unconditionally. `ToolApprovalRequestOutput` holds the pre-validation input Rust-side only (upstream keeps it on the tool call, tool-call.ts:64-86); it is not part of the type's JSON. aimux's parse validates without transforming the input, so text generation leaves it unset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…s deferred (#229 NP2-3) As convertLanguageModelContent does (ai convert-language-model-content.ts: 87-104), `generate_text` fails with `Tool call {id} not found.` when the model returns a provider tool result whose call is not in the response, unless the result is a deferred one. Before, such a result passed through silently. Upstream accepts a deferred result only for a provider tool whose definition sets `supportsDeferredResults`. aimux's tool definitions are the V4 tool set and carry no such flag, so a result is accepted as deferred when a provider tool of that name was passed; whether the tool definition should carry the flag is left open. The stream passes such results through, as upstream's stream does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
A request's `api_key: "env:VAR"` read any environment variable of the console process. Together with a caller-supplied base_url, a client of a non-loopback console could send any server variable to a host of its choice. resolve_api_key now accepts only the variable the provider's key is registered under and refuses every other name before reading it. This covers the calls, cache-probe and replay paths, which all resolve keys through it. aimux-providers gains provider_api_key_env(), the variable create_provider()'s factory reads a key from when none is passed: the vendor package's own constant (now crate-visible, so the table cannot drift from what the factory reads) or the registry row's env_var. The console does not keep its own copy of the provider table. Carries master's d5a10bd (#228 B13) to the V4 code. The tests use registry rows (xpersona, the_grid_ai, siliconflow) whose variables they set and restore, since this layer's console builds providers through create_provider and has no overlay rows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…V4 release @ai-sdk/provider 4.0.22 renamed the experimental EvaluationModelV4 to DecisionModelV4 (src/decision-model/v4/decision-model-v4.ts:6-17) and EvaluationUnsupportedQuestionTypeError to DecisionUnsupportedQuestionTypeError. The decision model follows that release, so the baseline moves to it. - VERSIONS.json pins 4.0.22 with its integrity. - The recorder's package.json and package-lock.json name the same version (regenerated with npm install --package-lock-only --ignore-scripts; the pinned vendor packages keep their own 4.0.21 copies). - aisdk_provider_pin_test fails when the pin drifts from 4.0.22. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…lts (#229 C41 stream and results, fork #27 part) As streamText's model call does (ai stream-language-model-call.ts:760-792), a provider's tool result in the stream becomes a `ToolResult` part holding a `TypedToolResult` (`tool_call_id`, `tool_name`, `input` of the call it answers, `output`, `provider_executed: true`, the chunk's `dynamic`, `provider_metadata`), or a `ToolError` part holding a `TypedToolError` when the provider marks it as an error. A result whose call is not in the stream (a deferred result) has no `input`. Before, the stream forwarded the provider's `ToolResult` with `result` and `is_error`. `TypedToolResult` and `TypedToolError` follow ai generate-text/ tool-result.ts:10-42 and tool-error.ts:6-36 (the user-level tool result's `providerExecuted` of fork #27); `toolMetadata` and `title` are absent, as aimux has no user-level tool definitions. Upstream drops the provider's `preliminary` flag at this point; aimux keeps it, so a preliminary result stays out of the step content as on the generated path and as the alignment doc's rule 6 asks. A consumed stream keeps the results and the provider's error results in the step content. The OpenAI chat output renders a provider error result as `[tool error: name]` text, as the generated path does. The bindings decode the new `ToolResult` shape and `ToolError` (Go keeps raw payloads and needs no change). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…error (#229 C41) As streamText's model call does (ai stream-language-model-call.ts:709-725), a client tool call that could not be parsed is followed in the stream by a `ToolError` part with the call's input, its error message (`getErrorMessage`) and `dynamic: true`. An invalid provider-executed call gets none. The error message is the error's display text, as layer 3 uses for the generated path's tool error. This branch's step content has no core tool error part, so a consumed stream does not keep it; the core content parts of layer 3 do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…-agent Upstream the `ai-sdk-provider-utils/<v>` part comes from the provider-utils the calling package imports (provider-utils post-to-api.ts:100-104, get-from-api.ts:77-81). npm installs each package's exact dependency, so the version follows the package's package.json: 5.0.51 for azure, amazon-bedrock, cohere, deepseek, groq, mistral, xai and google-vertex, and 5.0.53 for openai, openai-compatible, anthropic, anthropic-aws and google. google-vertex runs MaaS and xAI on its own openai-compatible 3.0.59. shared::aisdk_pins is now a per-package table (name, version, provider_utils), and each entry cites its package.json. Every pinned package's transport goes through shared::package_fetch, which rewrites the provider-utils part the helper layer adds. A per-call user-agent therefore keeps the package's part, as upstream does. The provider-utils crate's default part is no longer tied to the VERSIONS.json provider-utils pin, which follows the port. The drift tests check each package's version against VERSIONS.json and its provider-utils against the dependency in the recorder lock, including google-vertex's nested packages. package_user_agent_test pins the full user-agent of every package as literals. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…l-approval-response (#229 C41) `TextStreamPart` gains `ToolOutputDenied` (ai generate-text/ tool-output-denied.ts:7-15) and `ToolApprovalResponse` (the `ToolApprovalResponseOutput` of tool-approval-response-output.ts:7-35), in upstream's order (stream-text-result.ts:581-607), which completes the AI SDK's 26 part types. `tool-output-denied` is a stream part only, not a core content part (content-part.ts:12-33). aimux's stream does not produce them yet. Upstream answers approvals and reports denied tools when it executes tools (execute-tools-from-stream.ts: 170-210), which aimux does not, and reports the approvals denied in the prompt's last tool message, provider-executed ones included, before the first model call (stream-text.ts:2030-2090); that needs the prompt's approval collection (collect-tool-approvals.ts), which is not ported. The bindings decode both parts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
/api/calls and /api/cache-probe resolved the key from the request spec, then the Settings key store, then the provider's env var, and passed the request's base_url to the provider unchanged. A client could point base_url at its own host and receive the console's key. When the base URL differs from the provider's own default, only a key carried in the request (plaintext, loopback binds only) is used: the Settings key and the env var are not read, env: references are refused, and a call without a key goes out with an empty key instead of the provider's env fallback. Every built-in provider is reachable from the console on this layer, and an empty key does not stop the ones that read other credentials from the environment (AWS SigV4 credentials, the Vertex access token, the Codex sign-in, the DataForSEO login), so those refuse a custom base_url. A base_url equal to the provider's default keeps the normal key order. aimux-providers gains provider_default_base_url(), the endpoint create_provider()'s factory calls when given no base URL (the package's DEFAULT_BASE_URL, now crate-visible, or the registry row's URL; None when the URL is built from settings). Replay rebuilds the provider from the recording's identity with its default base URL (recordings store no URL on this layer), so it never calls a custom base URL. The test sends calls with a custom base_url to a local server and checks that neither the stored keys nor the env keys arrive, that env: and a Bedrock call are refused before reaching it, and that a key carried in the request does arrive. README, RFC-0029 §5.5 and the alignment doc's §0.6 note say the same. Carries master's bf0d19e (#228 B12) to the V4 code. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
An `{"error": ...}` chunk on the chat stream is passed on as it is
upstream (openai-compatible-chat-language-model.ts:588-595). aimux
took a numeric `code` in 400..=599 as the status, which made a code
like 429 retryable. The error now keeps the code as provider_code with
no status, as the completion path already did since N3. Presets and
the Vertex MaaS and xAI sub-providers share this chat model.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A3WxDBWBA4ituJjXsHaEAa
…N31-13-1) The master branch's chunk-timer matrix (tests/stream_timeout_matrix_test.rs) and the stream pipeline tests in generate.rs were deleted on the way to V4 without replacement. They come back as `generate::pipeline_tests`, adjusted to the AI SDK's full stream: - the chunk timer is armed and reset only by output chunks (stream-text.ts:182-225, :2842-2857), and a consumer's delay does not count against it (§13-17, -18); - dropping the stream leaves no armed timer behind (§13-21b); - the first event a provider peeks inside do_stream is kept, the first-chunk budget measures its arrival rather than the first poll, and it bounds a do_stream that never returns (§8.3); - the model's finish ends the stream while the connection stays open, a provider error part does not hide the finish, a malformed frame is reported and the stream goes on, a transport error ends it; - a retryable first error (429) is retried before the stream is returned, a non-retryable one is returned once (stream-text.ts:2409-2420). The pinned AI SDK package ships no stream-text tests, so these keep aimux's own cases with the upstream source cited. The first-chunk tests follow the current design (timer armed at operation start, U3) and change if that does. 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: rfc/0029-web-console.md and tools/aimux-web/README.md keep this layer's text, which already says what #266 says and adds which providers cannot take a custom base_url and where Replay sends. 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
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
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 38b6cde: the RFCs that named max_retries, timeout and session_id as CallOptions fields now say they are operation settings. 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 38b6cde: the RFCs that named max_retries, timeout and session_id as CallOptions fields now say they are operation settings. 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 38b6cde: the RFCs that named max_retries, timeout and session_id as CallOptions fields now say they are operation settings. 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 38b6cde: the RFCs that named max_retries, timeout and session_id as CallOptions fields now say they are operation settings. 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 [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
Brings in master at ec338c9 (#262, #246 and #247, each squashed into one commit) through [4/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 [5/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 [6/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 [7/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
eric8810
changed the base branch from
v4/7-remaining-upstream-model-kinds
to
master
October 17, 2026 14:28
master 带入下方各层的 squash;冲突与自动合并漂移全部收敛到本层树: ModelTest.kt 取层版本;OpenAiResponsesFixtures.java/.kt 与 ReasoningFileOutput.ts 为本层已删文件(fixtures 已内联、类型已并), 按层意图 git rm。零差异校验随下。
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: the public JSON of results, stream parts, errors, recordings and options uses aimux's own snake_case shapes, and errors are tagged aimux's way. Bytes are arrays of numbers, an explicit
nulland a missing value are the same, file parts use aimux'sfile-base64/file-url/file-referencetags, generated files are a tagged data-or-URL union, and timestamps are any string. Text and reasoning deltas carrydeltain the JSON, and standalone tool calls and sources carry notype. A tool input of1e2comes back as100.0. Go and Java reorder provider namespace keys.After: it is the AI SDK's JSON: camelCase fields, type-tagged unions, and errors tagged by the AI SDK error name (
AI_APICallError, ...). Bytes are base64 strings, decision images included, read withatob's forgiving decode. Where upstream's field isunknown(response and request bodies,APICallError.data,inputSchemaInput),nullstays apart from a missing value. A file part is upstream'sFilePartwith the one tagfile, generated files are{base64, mediaType}, and timestamps aretoISOString()strings. Text and reasoning deltas carrytext, and standalone tool calls and sources carry theirtype. Integral tool-input numbers are written as JavaScript writes them. The call-layer tool result carries upstream's structured output, andSizeandAspectRatioserialize as upstream's template strings. The seven bindings follow: they keep their byte-valued APIs and encode at the JSON boundary, and they keep provider namespace order.The by-name provider entries (
aimux_register_providersand the binding calls to it) stay until the bindings migrate to the new C ABI.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
LanguageModelUsageis camelCase and omits an unknown count instead of writingnull;StepResultand its response are camelCase; typed tool results,tool-output-deniedandtool-approval-responseare type-tagged;Outputis tagged with the AI SDK output mode names. The decision types (DecisionCallOptions,DecisionResult,DecideOptions,DecideResult, the model description) followdecision-model-v4-*.ts,decide.tsanddecision-result.ts. Keys aimux parses inside a provider namespace are camelCase; options a runtime receives as given (Ollamakeep_alive, vLLMchat_template_kwargs) keep their names.master_recording_testpasses unchanged. Replaying a master recording whose call options are snake_case fails withJsonParsenaming the keys, instead of silently dropping them.Error.Dataisjson.RawMessage, so no data,nulland a value stay apart, as upstream'sAPICallError.data(unknown).baseUrl, andbase_urlis still accepted. The removed retry and body settings are rejected in either spelling. Node retry tests passmaxRetriesas a call option, so zero disables retries.@ai-sdk/provider-utils5.0.53uint8-utils,types/content-part;ai7.0.127generate-text/(generated-file, tool-call, stream-text-result, parse-tool-call),prompt/file-part-data;@ai-sdk/provider4.0.21 / 4.0.22 shared V4 types, results and errors.total, nevernull. Its recording and Flutter commits were already on this branch.type: "file"field, which upstream's file object adds.GenerateVideoResult(video/videos,responses[]) differs from aimux'sVideoResult(videos, oneresponse). The JSON rules hold; the result shape stays aimux's own.file-base64,file-urlandfile-referenceparts are removed. Adata:URL inside{type: "data"}is an invalid prompt.GeneratedFile(base64(),bytes()),LanguageModelFileandTimestampare new;ReasoningFileOutputbecomesGeneratedFileOutput.Tool.ProviderOptionsandTranscriptionSessionOpts.ProviderOptionsarejson.RawMessage, andUploadFileResult.ProviderReferenceis aProviderReference.cargo docwith-D warningsandcargo test --workspace(3170 passed, 6 ignored; the ts-rs export tests now run underscripts/gen_ts_types.py --checkinstead) pass, as doscripts/check_provider_boundaries.sh,scripts/gen_ts_types.py --check,scripts/gen_providers_doc.py --checkand the pin script tests. Node (ava, 129), Python (pytest, 111), Go, Java (108), Kotlin (76) and Flutter (132,flutter analyzeclean) pass locally.docs/api/decision.mdin camelCase; RFC-0035 §6 names the tests that pin each repair branch; RFC-0026's converter table and RFC-0036's stream example carrytext; revision lines in RFC-0023, RFC-0037 and RFC-0038; the web console and README readpart.text; CHANGELOG.name).StreamPartPayloadgains aFilevariant here, sinceFilecarries the same generic file payload asReasoningFilein this layer.failModeand the recording operation use kebab-case values, as the AI SDK spells multi-word literals:"best-effort"/"fail-fast"and"language-model"/"decision"(breaking forfailMode; recordings do not change, sincelanguage-modelis never written).ToolcarriesSupportsDeferredResults(supportsDeferredResults, omitted when unset), since Go gains provider tools in this layer; the other bindings got the flag in [4/8].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