Skip to content

fix(providers)!: align vendor packages with upstream and add missing model families - #220

Closed
cunninghamcard-bit wants to merge 55 commits into
rfc-0036/provider-factoryfrom
rfc-0036/vendor-alignment
Closed

cunninghamcard-bit wants to merge 55 commits into
rfc-0036/provider-factoryfrom
rfc-0036/vendor-alignment

Conversation

@cunninghamcard-bit

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

Copy link
Copy Markdown
Contributor

What

Aligns vendor request and response conversion, authentication and streaming with the pinned AI SDK sources, and ports the model families that the pinned packages have and aimux lacked, plus the evaluation model and customProvider. Each new family translates the upstream model and options files; the upstream test file is ported case by case (/// TS:). Base rfc-0036/provider-factory (B); follows B, precedes D. The head rfc-0036/vendor-alignment is fast-forwarded to the model-families commit. Replaces #220.

Layers

Branch PR Content Diff size
rfc-0036/vendor-alignment #220 Ten vendor packages aligned with upstream Relative to the updated immediate base: 159 files, 21212 insertions(+), 6466 deletions(-)
rfc-0036/model-families none (unpushed) Missing model families, evaluation model, customProvider 52 files, 10649 insertions(+), 235 deletions(-)

Before and after

Area Before After Upstream reference
OpenAI embeddings Accepted base64 vector strings Requests encoding_format: "float", reads numeric vectors OpenAIEmbeddingModel
OpenAI Responses Provider tool requests, events, history incomplete Typed provider-tool factories, request and event conversion, approval requests, compaction custom content; approval IDs map back to tool-call IDs in generate and stream; custom-tool history keeps async; local shell environment accepts an omitted type OpenAIResponsesLanguageModel, convertToOpenAIResponsesInput, shell.ts
Azure transcription OpenAI route only Selects OpenAI or Azure Speech (endpoint, auth headers, base-URL override) Azure provider and transcription modules
Anthropic Document citations and container uploads dropped; structured-output fallback and computer toolset mapping incomplete Document sources, container-upload custom content; JSON-tool fallback; computer toolset members map to the caller's tool name with action, toolsetName (kept in history); mid-stream errors forwarded without a synthetic finish AnthropicLanguageModel
Bedrock chat Structured JSON, provider-defined Anthropic tools missing; inline media rejected before detection Native response format, JSON-tool and prompt-instruction paths for generate and stream, with finish-reason and metadata handling; provider-defined tools, model-family selection; inline media resolved before format validation; percent-encoded model IDs; metadata under amazonBedrock and bedrock; no response body; S3 supported-url entry; empty tool-use input sent as {} AmazonBedrockChatLanguageModel
Google Reasoning-file replay rejected; null args could become "null"; null partialArgs could discard a call Keeps generated files, reasoning files, replay metadata; null args becomes {} with no delta; null partialArgs permits the complete-call path; response model IDs unset GoogleLanguageModel
Vertex providers Auth needed an explicit or env token; Anthropic models also on the main provider Application default credentials, googleAuthOptions; separate Vertex Anthropic factory (googleVertex metadata, no Express mode); main provider's anthropic_model removed GoogleVertexProvider, GoogleVertexAnthropicProvider
Vertex language, image, transcription Partial args and thought files incomplete; Speech-to-Text lost auth headers Accumulates partial args, keeps thought files, rejects provider references in user files, carries credentials to Speech-to-Text, adds Gemini transcription; image follows the Gemini path; URL image input stays rejected, as upstream Vertex modules
Mistral Separate conversion helpers Consolidated; keeps tool result name, shared output conversion MistralChatLanguageModel
Cohere, xAI Responses Stream without usage could panic; image-generation tool calls and results dropped Parse error for missing message-end usage; image-generation calls and results returned CohereChatLanguageModel, XaiResponsesLanguageModel
Groq, OpenAI-compatible stream Read the first SSE event before returning Error event flows as stream-start, error, finish, as upstream n/a
Azure Foundry Responses no Foundry-host detection explicit_message_item_type set for Foundry project hosts (*.services.ai.azure.com) azure/src/azure-openai-provider.ts (explicitMessageItemType: isFoundryProject)
Groq transcription missing groq/transcription.rs, GroqProvider::transcription groq-transcription-model.ts
Mistral speech, transcription missing mistral/speech.rs, mistral/transcription.rs mistral-speech-model.ts, mistral-transcription-model.ts
Google speech, transcription missing (Vertex had its own Gemini transcription) google/speech.rs, google/transcription.rs (including Live); Vertex reuses them with its own options namespace google-speech-model.ts, google/src/transcription/*
xAI speech, transcription, video missing xai/speech.rs, xai/transcription.rs (HTTP and realtime WebSocket), xai/video.rs xai-speech-model.ts, xai-transcription-model.ts, xai-video-model.ts
Evaluation model missing aimux-core/src/evaluation_model.rs, Provider::evaluation_model, EvaluationLanguageModel adapter in provider-utils; OpenAI and Google expose it evaluation-model/v4/*, evaluation-language-model.ts
Registry, customProvider No evaluation lookup; no customProvider evaluationModel lookup with upstream's NoSuchModel context; one Provider impl with per-modality alias tables and a fallback provider provider-registry.ts, custom-provider.ts

Tests

  • Restored: the Groq first-error SSE sequence and the Bedrock toolChoice: "auto" end-to-end case; existing ports follow upstream behavior. Terminal-SSE regressions assert one non-recoverable StreamPart::Error, no successful finish, no finalized incomplete tool call.
  • bedrock_remaining_test.rs (38 cases) and anthropic_remaining_test.rs (21 cases), deleted in A, return adapted and not ignored. Anthropic gains the tool change handling, argument validation, leading system message handling, beta headers and empty base-url error text they cover. Six Bedrock cases are left out: three with a changed upstream expectation, two env cases without upstream counterpart, one duplicated by e2e.
  • Adds selected upstream cases to the vendor upstream_e2e_test.rs files through public factories with an injected transport; not complete suites. Bedrock image and embedding tests use the percent-encoded model path.
  • Ported files (cases): groq_transcription_test.rs (8), mistral_speech_test.rs (16), mistral_transcription_test.rs (12), google_speech_test.rs (39), google_transcription_test.rs (15), google_evaluation_test.rs (7), xai_speech_test.rs (17), xai_transcription_test.rs (17), xai_video_test.rs (76), evaluation_language_model_test.rs (16), custom_provider_test.rs (18): 241 cases. No hand-written tests added.
  • Deleted: hand-written tests preserving removed Rust behavior (including Anthropic assertions of a synthetic finish after a mid-stream error); OpenAI embedding replays with base64 vectors; the Bedrock conformance streaming replay (text-encoded binary data cannot exercise the upstream decoder). Kept: binary Bedrock body_base64 stream replay; Google generated-file coverage is restored from recordings. Cassettes and fixtures/ are unchanged.

Not in this pull request

  • xAI image and files, and OpenAI skills, are not ported. xAI Responses image-generation tools give no standalone image-model family.
  • Google speech translation, Vertex Chirp text-to-speech, customProvider string aliases, evaluate() and openai-evaluation.test.ts are not ported. Bindings expose no constructors for the new families; only generated TypeScript types change.
  • OpenAI chat and Responses still read the first SSE event before returning (aimux-providers/src/openai/model.rs, openai/responses/mod.rs); no ported test covers it.
  • Serde naming, the by-name entries in aimux_providers::provider and the ModelMessage tool-result shape: covered by D. D does not mention remaining tool-error behavior.
  • The Google auth port covers credential resolution and token exchange for the Vertex factories, not every transporter option, retry policy, environment probe or project-discovery path.
  • Selected vendor tests are not complete upstream suites. Groq options, usage, message conversion, tool preparation and generate/stream cases remain unported, as do runtime option-schema cases in other vendors.

Relation to the maintainer's design

Reference order: the pinned AI SDK source first; where it has no counterpart (C ABI, bindings, recording, code generation), the maintainer's RFCs 0039 (ops and bindings, #209), 0040 (provider construction and auth, #210), 0041 (replay governance, #211) and 0042 (V4 type boundaries, #212), which are Drafts that refine the #200 design doc docs/aisdk-architecture-alignment.md; that design doc only where those RFCs defer to it. Where an RFC restates upstream behaviour differently from the pinned source, upstream wins and the RFC gets an amendment request. The stack targets rfc-0036/integration; RFC-0039 §11 and RFC-0040 §7.2 require the whole closure to switch to master at once, so items marked "before master" must land on integration first. "Branch E" is the follow-up branch stacked on D, in progress.

  • RFC-0040 §6 rows B4–B7 (Draft) list vendor behaviour that must not be lost (Mistral, xAI, the Responses family, Vertex). This PR's evidence is the ported upstream tests listed above; RFC-0040 §8's per-package, per-modality acceptance record (B01, with baseline and target SHA) is not produced separately.
  • Package descriptors (see B, implemented in branch E) and the crate split (see A, before master) apply to this PR's code too.

Verification

Gate on the head f7bcb79d: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo doc with -D warnings, tests of core, provider-utils, providers, ffi, web, replay and cli: 2,794 passed, 0 failed, 6 ignored; Node and Python bindings cargo check; provider boundary script; gen_ts_types.py --check; gen_providers_doc.py --check. Cassettes and fixtures/aisdk unchanged. Diff against the base: 199 files changed, 31643 insertions(+), 6483 deletions(-). After the restack onto the current master, only this head was tested; the intermediate layers were checked with fmt and clippy only.

Restacked head ff339b50. Restack (2026-10-09): merged master (#213, SSE) and the rewritten #204 tracker down the stack by merge, no history rewrite. This layer ports openai_compatible's end-of-stream flush of still-unnamed pending calls to the new API. Checked: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, tests of provider-utils and providers (129 test binaries, 0 failed). cargo doc, binding checks and the generator --check scripts were not rerun.

Not verified

No GitHub check runs were attached at the last refresh. Go, Java, Kotlin, Swift and Flutter were not compiled locally in that refresh; Node npm test and Python pytest were not run on model-families. Live vendor calls and unported upstream cases are outside validation.

🤖 Generated with Claude Code

chenhaonan and others added 30 commits October 5, 2026 19:09
…parts

What: fixes the differences that a package-by-package comparison against
the pinned upstream sources found in OpenAI, Azure, Anthropic, Amazon
Bedrock, Google, Google Vertex, Mistral, Cohere, xAI and the
OpenAI-compatible package. The changes are in request building (option
names and validation, schema handling, cache and beta merging, tool
conversion), response mapping (usage, provider metadata keys, citations and
sources, reasoning) and stream handling (error parts, raw chunks, finish
data). Examples:

- Bedrock percent-encodes the model id in the URL, writes metadata under
  both `amazonBedrock` and the legacy `bedrock` key, and applies upstream's
  capability rules to sampling settings and reasoning.
- OpenAI embeddings request `encoding_format: "float"` and no longer decode
  base64 vectors; the Responses log-probability include follows upstream's
  condition.
- Google no longer invents `response.model_id`; the call layer supplies the
  default, as upstream does.
- Anthropic emits citation sources, forwards a mid-stream `error` event as
  an error part without a synthetic finish, and falls back to a JSON tool
  for structured output on models without native support.
- Vertex accumulates partial function-call arguments; Vertex Anthropic uses
  the `googleVertex` namespace and never Express mode; Vertex transcription
  sends its credentials to the Speech-to-Text host (they were dropped
  before, so every such request went out unauthenticated).
- xAI Responses returns image generation calls and results.
- Each package sends its own `user-agent` suffix. Mistral has no
  `transform_request_body` setting. The deprecated `text_embedding` and
  `text_embedding_model` aliases are not carried over.

Why: the direction is that Rust behaves as the AI SDK does, with upstream
as the reference. Of 106 severe findings, 94 are fixed here; the 12 left
need new model kinds, shared type changes or Google default credentials,
and are listed with reasons in the pull request description.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What:
- The cassette replay harness and the Bedrock regression mocks compare
  request paths after percent-decoding, so an encoded model id matches a
  recording made either way.
- The upstream-sample tests ignore `user-agent` on both sides: the recorded
  value carries the upstream package version, ours carries this crate's.
- The Google upstream-sample test no longer expects the provider result to
  carry a model id.
- Bedrock regression tests accept the legacy `bedrock` metadata key next to
  `amazonBedrock` and read the reasoning signature from the delta that
  carries it. These tests replay values taken from recordings, so their
  assertions are adjusted rather than the tests removed.
- Removed tests that pinned behaviour Rust no longer has: top-level
  `cached_tokens` usage for two preset rows, a synthetic error finish after
  a mid-stream Anthropic error, a Responses stream that ends itself after
  an error event, two OpenAI embedding replays whose recordings hold base64
  vectors, and a Bedrock stream replay over recordings that stored the
  binary event stream as text.

Why: these tests asserted the old behaviour. No cassette or fixture is
changed; the recordings that can no longer be replayed are listed in the
pull request description.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…am tests

What: ten files, `<vendor>_upstream_e2e_test.rs`, for OpenAI, Azure,
Anthropic, Bedrock, Google, Vertex, Mistral, Cohere, xAI and the
OpenAI-compatible package: 67 tests. Each builds the provider through its
public factory with an in-process transport, calls the model, and asserts
the request that was sent and the parts that came back. Per model kind
there is at most one test each for generate, stream, tool call and error.
Every test names the upstream test it comes from.

Why: Azure and Vertex had no replay test at all, and the alignment needed
something that exercises it. This is a selection, not a case-by-case port.
The Vertex transcription test is the one that showed requests went out
without credentials. One test is ignored with its reason:
`CallOptions::tool_choice` is not optional, so `tool_choice: "auto"` is
always sent where upstream omits it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Request URLs, metadata keys, embedding encoding and removed provider
methods change for callers, so they are listed under Breaking.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: a Cohere message-end event without a usage object is a parse error
instead of a panic. Groq and DeepSeek return the raw response body again.
Mistral takes the tool message name from the tool result only. Bedrock has
one request builder. OpenAI and Azure lose the unused request-body
transform slot. Two end-to-end tests assert what the upstream tests
assert: the full OpenAI stream sequence, and native structured output
selected by the model id the upstream test uses.

Why: an independent review found a crash, two dropped fields, one finding
marked fixed that was not, a second code path, and two weakened tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: bring the tool-result alias removal up the stack. The provider
sources keep this branch's versions; they already read the base URL
environment variables, skip empty keys and report upstream's provider
names, as the vendor alignment did those first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: Bedrock structured JSON output for generate and stream, Bedrock
provider-defined Anthropic tools, a closed set of Bedrock model families.
Azure speech transcription with its own route and endpoint. Vertex and
Vertex Anthropic application default credentials with the upstream auth
options. A Gemini transcription model on Vertex. OpenAI Responses provider
tools with their typed factories, request mapping and response and stream
events. The Responses conversion returns an error where it could panic.
One test per new feature is selected from the upstream test files.

Why: these were listed as open differences from upstream; the owner's
decision is to close them in this pull request rather than carry them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: brings up the tool, content and provider-option type changes and
converts this branch's code to them. OpenAI Responses emits tool approval
requests and compaction as custom content. Anthropic emits custom content
where upstream does. Google and Vertex return thought images and files as
reasoning files. A vendor omits tool_choice when the caller set none, and
the test that was ignored for this runs again.

Why: these open differences were waiting for the content variants that
the lower branches now provide.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the provider prompt changes up the stack: structured tool
result output with a required tool name, signatures in provider options,
and the reasoning-file, custom and tool-approval-response parts. Each
vendor converter consumes them as its upstream package does. Hand-written
tests that no longer compile are deleted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the factory branch's additions up the stack: completion(id) for
OpenAI and the OpenAI-compatible package, Anthropic skills, registry
middleware, and the private request-body builder. This branch's
upstream-aligned vendor behaviour is kept. Vendor namespaces in the newly
ported code go through the package constants so the boundary check passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…back

What: the Bedrock stream test and the OpenAI factory test no longer expect
a default tool choice, which upstream does not send; a Google request-body
difference is fixed. In-file test modules and test functions that earlier
merge resolutions had restored are removed. Test files ported from
upstream test suites are kept.

Why: tool choice is optional now and the vendors omit it when unset, as
upstream does. Hand-written tests that pin Rust-only behaviour are
deleted in this project, and the branches below had already deleted these.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: keep the stack linear after the factory branch dropped the
hand-written tests that earlier merges had restored. This branch's
upstream-aligned vendor code is kept in the conflicts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…der docs

What: the data-loss regression test keeps this branch's assertions, which
follow the upstream-aligned Bedrock and Anthropic behaviour, instead of
the factory branch's. The Google end-to-end test no longer expects a
default tool config. docs/api/providers.md is regenerated.

Why: the last merge took the lower branch's copy of the regression test,
which asserts the pre-alignment behaviour, and the generated provider
list was stale after the new models were added.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…ours

What: brings back 30 test functions ported from upstream test suites
(DeepSeek, Groq, Open Responses). Anthropic document citations are
emitted as sources as far as the source type can carry them. Bedrock no
longer returns a response body upstream does not return. A DeepSeek
stream error event is reported as upstream reports it. The hand-written
provider trait test is deleted.

Why: an independent audit found that upstream-ported coverage had been
removed together with hand-written tests, and listed these differences.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…ntract fixtures

Why: carry up the stack the source union, the removal of the separate
thought-signature field and the deletion of the self-generated contract
fixtures. This branch's vendor code builds url and document sources and
carries signatures in provider metadata and options as upstream does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…nges from below

Why: carry up the factory branch's settings alignment, image-model
middleware and restored upstream-ported tests, together with the raw
adapter removal from below. The settings take the factory branch's shape;
this branch's upstream-aligned vendor behaviour is kept; the test files
keep the union of upstream-ported tests from both sides.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: drops one assertion from the Bedrock generate test.

Why: upstream's Bedrock model returns id, timestamp, model id and headers
as response information and no body; the source already follows that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: the factory branch merged its split-out parts without changing any
file; this keeps the stack linear.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: Google and Vertex treat `partialArgs: null` and `args: null` as
upstream does, Vertex rejects user file references and loses its old
anthropic_model entry in favour of the Vertex Anthropic factory;
Anthropic maps computer toolset calls back to the caller tool with
action; OpenAI Responses maps approval ids to the original call id, keeps
the async option on custom tool history and accepts a shell environment
without a type tag; Bedrock detects inline media before validating it;
Groq turns a first error event into stream-start, error, finish. Removes
the GenerateResponse and StreamResponse aliases. The Bedrock e2e test
regains the upstream toolChoice input and assertion.

Why: review of this branch against the pinned vendor packages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
… the encoded model id

What: Groq and the openai-compatible chat model no longer pre-read the
first SSE event; an error event flows as stream-start, error, finish as
upstream does. Bedrock gains the S3 supported-url entry and sends an
empty tool-use input as {}; its temperature warning matches upstream.
The migrated Bedrock image and embedding tests mount the percent-encoded
model path the model now requests.

Why: the gate showed sixteen failures after the merges; in each case
the pinned upstream behaviour decides.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…d remaining cases

What: brings back bedrock_remaining_test.rs (38 cases) and
anthropic_remaining_test.rs (21 cases), translated from the upstream
packages, adapted to the current API and no longer ignored; Anthropic
gains the mid-conversation tool change handling, argument validation,
leading system message handling and beta headers those cases cover, and
the empty base-url error text. Six Bedrock cases are left out: three
whose upstream expectation changed (merged into the hash case), two
environment cases with no upstream counterpart, one duplicated by the
e2e selection.

Why: the files were deleted lower in the stack as hand-written; they are
upstream translations and the project keeps those.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: the Azure speech path spells the package name through
options::NAMESPACE instead of a literal.

Why: the provider boundary check forbids the literal outside options.rs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Why: carry the ancestry up the stack; no code change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: GroqTranscriptionModel (multipart POST to /audio/transcriptions,
options parsed as the upstream schema does, text and strict JSON
responses, segments from segments or words), exposed as
provider.transcription() and through the Provider lookup; eight
upstream test cases ported.

Why: the upstream groq package has a transcription model and Rust did
not; the user ruled that the missing model families are ported now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: MistralSpeechModel (/audio/speech, refAudio, upstream warnings and
request-body redaction) and MistralTranscriptionModel (multipart
/audio/transcriptions, upstream option rules, segments and usage
metadata), exposed as provider.speech() and provider.transcription() and
through the Provider lookups; 28 upstream test cases ported. The shared
media_type_to_extension now maps mpeg, mp4, x-wav and opus as upstream
does.

Why: the upstream mistral package has both model families and Rust did
not; the user ruled that the missing model families are ported now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: GoogleSpeechModel (WAV framing, option validation) and
GoogleTranscriptionModel (unary /interactions call and the Live
websocket loop, api key in the query, /v1beta stripped), exposed as
provider.speech() and provider.transcription() and through the Provider
lookups; Vertex gains speech() over the same implementation and its
Gemini transcription reuses the shared options, offset parser and live
loop (about 200 lines fewer). A peer close after the audio ended
finishes the stream with the accumulated text as upstream does. 54
upstream test cases ported.

Why: the upstream google package has both model families and Rust did
not; the user ruled that the missing model families are ported now.
Not ported: speech translation (no core type) and Vertex chirp
text-to-speech.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
@cunninghamcard-bit

cunninghamcard-bit commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

OpenAI MCP 审批的标准化转换需要随公共类型范围调整(核对 head f7bcb79d)。

本轮确认的范围调整:aimux 不承担审批运行时,也不定义统一的审批公共类型/语义;审批相关的定义、解释和处理由上层负责。这是对 #200 §0.4 原先“保留审批 variant”范围的收窄,不是否认 AI SDK 中存在这些类型。

本 PR 的 provider_events.rs 将 mcp_approval_request 构造成统一的 ToolCall 和 RawToolApprovalRequest;prompt_approval_tool_call_ids 及 responses_convert.rs 又维护 approval id 与 tool-call id 的对应关系,用于非流式和流式转换。审批回复转换为 mcp_approval_response 的路径则继承自 #219。

请配合 #219 撤去 aimux 统一审批输出和审批专用关联状态。范围是移除 aimux 的统一审批抽象;provider 原生参数的去留按其既有接口契约判断,不按 approval 关键词批量删除。保留并遵守现有原始响应/事件的返回契约。是否补充通用数据出入口另行评估,不把新增透传通道、完整审批往返或审批专用无损机制作为移除统一审批抽象的前置条件。

SDK 调研后的边界说明

这里要解决的是:厂商返回 aimux 不负责解释的内容时,上层如何接管,而不是要求 aimux 实现审批。

  • 不需要逐种业务定义公共类型。 通用 JSON/raw 接口可以承载厂商数据,厂商原生 mcp_approval_request/response 的解释、决策和回复构造由上层负责。定义通用承载契约与定义统一审批协议是两回事。
  • 输出可读,但普通输入接口不能代替审批专用类型。 当前审阅栈的 OpenAI Responses 实现已有 response.body 和可选 raw chunks,承载解析后的 JSON。下面的请求捕获验证已确认:普通 providerOptions 和自定义内容均不能直接发送原生审批回复;Rust 自定义 fetch 可改写请求,但当前 Node/Python/C 绑定未开放该回调。
  • 新增能力尚未决定。 是否引入通用 opaque 内容、显式原生请求扩展及其合并规则,应作为通用能力单独讨论;本评论不要求新增这些接口,也不宣称现有接口已支持完整审批往返。移除统一审批转换时,不应把审批请求改成可执行的普通工具调用,或自动产生批准/拒绝结果。

调研依据与适用范围:

  1. Rig 0.44.0 的 Responses decoder 将此类审批请求保存在通用 Opaque.item,并设置 replay=false;通用请求合并另行处理 additional_params。这是“保留厂商内容而不定义统一审批 DTO”的参考。Rig 整体也做 agent loop 和工具执行,因此这里只参考其底层承载机制,不照搬其运行时边界,也不据此要求 aimux 新增 Opaque。
  2. Pydantic AI 的固定版本源码 对非流 McpApprovalRequest 直接跳过,MCP 工具配置采用 require_approval='never'。它有 metadata/扩展参数,并不能证明这条审批流程可以由上层完整接管。
  3. OpenAI 官方流程由调用者读取原生审批请求,再在下一次 Responses 的 input 中提交原生回复。厂商协议本身已有定义,无须额外经过 aimux 的跨厂商审批类型。

本轮结论:移除 aimux 统一审批语义;保留既有通用数据契约;通用回传能力是否扩展另行决定。以上是源码与文档路径核查,未进行真实服务的审批往返验证。

已核实:删掉审批类型后,上层还能不能发回结果?

通过普通调用接口不能;Rust 调用者可以自己写 fetch 改写请求。

在审阅栈 415e37e1 上运行了请求捕获测试;涉及的 OpenAI provider 与 Fetch 源码同 #220 head f7bcb79d,这些文件在两者之间无差异。结果:

输入方式 捕获到的请求
现有 ToolApprovalResponsePart 正确生成 mcp_approval_response;这是准备移除的专用转换
providerOptions.openai 中放 input、extraBody、additionalParams 审批 JSON 均未进入请求;已支持的 previousResponseId 能进入
CustomPart(kind="openai.mcp_approval_response") 被忽略,没有生成原生审批输入
Rust OpenAIProviderSettings.fetch 自定义回调改写请求体 能将上层构造的原生审批 JSON 放入请求

源码依据:专用审批转换及 Custom 分支、Rust fetch 设置。绑定侧也已核查:Node/Python/C 没有公开该 fetch 回调;Node 的 bodyOverrides 明确报错,不能作为替代入口。内部虽有 transform_request_body,原生 OpenAI 的公开 settings 不提供它。

因此,移除审批专用类型后,不能宣称普通调用者已经可以仅靠现有接口完成审批。现状是能读取厂商数据,但 Node/Python/C 调用者若要回复,必须自己调用厂商 API;Rust 还可以利用现有 fetch 扩展。这是已经确认的能力缺口,不再是“输入路径待核实”。若要求这些绑定也能通过 aimux 完成上层自定义流程,所缺的是通用原生请求入口,不是统一审批协议。

验证:1 个请求捕获测试、4 条路径全部通过;无真实 OpenAI 请求,无生产代码修改,临时测试已移出仓库。

@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

补充澄清当前实现与 Rig 的比较(核对 #220 head f7bcb79d):

当前 aimux 已经能完成审批往返。 OpenAI 返回 mcp_approval_request 后,本 PR 将其转换成 ToolCall + ToolApprovalRequest;上层决定同意或拒绝,再传入已有的 ToolApprovalResponsePart;OpenAI provider 将它转换成下一次请求中的 mcp_approval_response。aimux 不负责作决定,也不需要运行审批 loop。输出转换 · 输入转换

与 Rig 的差别是承载方式,不是“能否让上层作决定”。 Rig 把厂商审批请求作为通用 Opaque.item 交给上层,并允许上层通过 additional_params 自行构造原生回复;当前 aimux 使用审批专用公共类型,由 provider 负责原生协议转换。现有 response.body / 可选 raw chunks 也能提供原始输出,但它们不是 Rig 式 Opaque 内容项;普通请求参数目前没有相应的原生 input 覆盖入口。

因此,前面“普通调用接口不能回传”的结论仅针对移除 ToolApprovalResponsePart 等专用类型之后,并不是说当前实现无法完成审批。若决定保留现有 provider 层转换,往返路径已经存在;若按本轮范围决定移除 aimux 的统一审批公共类型,则 Node/Python/C 的普通调用接口将失去这条回传路径,除非另行提供通用原生请求入口。是否增加该入口应单独决定,不能把现有能力和删除后的能力混为一谈。

@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

关于是否删除 ToolApprovalResponsePart(以及配套的 RawToolApprovalRequest / ToolApprovalRequest),建议重新评估。我前面评论中的“请撤去统一审批输出”是基于“不做审批运行时”得出的过强结论;这两个问题需要分开。

不做审批 loop,并不要求删除 provider 层的审批传输类型。 当前实现只负责把 OpenAI 的 mcp_approval_request 交给上层,再把上层给出的 ToolApprovalResponsePart 转成 mcp_approval_response;批准或拒绝仍由上层决定。#220 输出映射 · 现有输入映射。AI SDK 也在 provider 层保留相应传输类型,直接调用 provider 即可完成转换,不依赖上层 agent runtime。

删除是范围选择,会改变现有能力。 ToolApprovalResponsePart 是当前普通调用接口中唯一已核实能发出原生审批回复的入口。删除后,虽然非流式 response.body 和可选 raw chunks 还能让上层读取厂商输出,Node/Python/C 的普通调用接口却不能把上层构造的原生 mcp_approval_response 送回去;Rust 仍可用自定义 fetch 改写请求。Rig 采用的 Opaque.item + additional_params 是另一套完整接口,aimux 目前没有对应的常规原生 input 入口。

我的建议:如果目标只是“不在 aimux 运行审批、决定权归上层”,保留这些传输类型即可,不必删除。 如果产品边界进一步要求“aimux 连审批传输协议也不提供”,则可以删除,但应明确这是撤销现有往返能力;不能同时声称上层仍可通过当前普通接口自行完成同一流程。请按这个能力取舍决定 #219 的公共类型和 #220 的转换是否保留,而不要仅凭“没有审批 runtime”决定删除。

chenhaonan and others added 4 commits October 10, 2026 17:25
…pstream

What: the OpenAI Chat encoder replays a non-object tool-call input as
`{}`. That is upstream's `serializeToolCallArguments` (@ai-sdk/openai
4.0.49 and later), not a loss of data: OpenAI function arguments are
always objects. A comment and a test now say so.

Why: the behaviour looked like a fidelity bug; it was kept on purpose to
stay with upstream.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QeURmKQzedTSiu2vL3yVes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R6wpZPfWrKN9XKB7WXXK2o
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.

2 participants