Skip to content

test: port the upstream OpenAI tests - #369

Merged
eric8810 merged 58 commits into
masterfrom
test/port-the-upstream-openai-tests
Oct 27, 2026
Merged

eric8810 merged 58 commits into
masterfrom
test/port-the-upstream-openai-tests

Conversation

@cunninghamcard-bit

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

Copy link
Copy Markdown
Contributor

Summary

按 RFC-0045(#281)把 @ai-sdk/openai@4.0.83 的上游测试迁完,按厂商分批的最后一个(Mistral #295、Cohere #296、DeepSeek #299、Groq #301、Hugging Face #303、xAI #312、Anthropic AWS #319、Azure #324、Open Responses #347、Google Vertex #352、OpenAI-compatible #355、Amazon Bedrock #358、Anthropic #361、Google #363)。

上游 34 个测试文件,其中 25 个对应 aimux 已有的功能,共 780 个用例:

  • 704 个新写了测试并通过,放在 src/openai/tests/,一个上游测试文件对应一个 Rust 文件。
  • 28 个由原有的旧测试完整覆盖(embedding 2、image 12、speech 7、transcription 7),旧测试挪进按 describe 链命名的 mod,加上 /// upstream: 标记,函数名和函数体不变;另有 49 处旧测试只覆盖一部分,加了 /// partial: … 指向完整用例。
  • 48 个写进 layout.toml:
    • 34 个 sub-feature(Provider 构造与配置:迁移后遗留缺口 #275):Files 没有 get/download/delete 和流式上传(13 个),没有通用的 throwIfOpenAIStreamErrorBeforeOutput 帮助函数(7 个,同样的规则在 Responses 的 do_stream 里实现并由 doStream 用例覆盖),prompt 里的工具调用和结果 part 没有 provider_metadata(4 个),没有 parallel 包装调用的展开(5 个,另有 expand-parallel-tool-call.ts 整个文件 3 个),UnsupportedFunctionality 只带功能名、比不了上游的 message(2 个),嵌入模型没有单次输入字节上限、转写结果没有 usage、没有 supportsWebSearchSourcesInclude 设置、allowedTools 没有任意工具名映射(各 1 个)。
    • 11 个 ts-only:8 个 expectTypeOf 比较 zod 推出来的 TypeScript 类型,undefined 的工具输入和 undefined 的 header 值 Rust 表达不了,图片下载用的全局 fetch 在 Rust 里注入不了。
    • 另外 3 个属于 missing 的 responses/expand-parallel-tool-call.ts。

另外 9 个上游测试文件(live/ 3 个、realtime/ 3 个、openai-batch、skills/openai-skills、speech-translation/)测的是 aimux 没有的模块,源文件条目是 missing,挂 #275,用例不迁。

迁移中发现 36 处行为差异,修在 #368 里,一处一个提交,各带一个在 master 上失败的测试;其中 3 处是 #323 的同一改动,1 处是 #354 的同一改动,带上它们是为了这里的用例单独就能过。#368 已合入 master。四个用例按 aimux 已有的约定改了比法,见下面 harness 一节,也登记在 #276。

一、体系概览:OpenAI 的测试现在怎么跑(#368 分支 68ce701f)

cargo test -p aimux-providers
  ├─ unittests src/lib.rs                       openai 8 个(model.rs、convert.rs、responses/convert.rs 里的 #[cfg(test)])
  └─ tests/*.rs
       ├─ openai_upstream_e2e_test.rs           回归测试和上游部分用例
       ├─ openai_image_test.rs                  22 个   ├─ openai_decision_test.rs        13 个
       ├─ openai_output_round_trip_test.rs      16 个   ├─ openai_transcription_test.rs   10 个
       ├─ openai_factory_test.rs                14 个   ├─ openai_speech_test.rs           8 个
       ├─ openai_transcription_stream_test.rs   13 个   ├─ openai_embedding_test.rs        6 个
       └─ openai_realtime_ws_replay_test.rs      1 个
            create_openai(OpenAIProviderSettings { .. })                openai/mod.rs:253   impl Provider :429
            ├ chat → OpenAIModel                                       :340   model.rs do_generate :166、do_stream :184
            ├ completion → OpenAICompletionModel                       :346   completion.rs
            ├ responses → OpenAIResponsesModel                         :355   responses/mod.rs do_generate :260、do_stream :296
            │   ├ convert_to_responses_input                           responses/convert.rs:132
            │   └ read_until_output(流在出内容前报错)                  responses/responses_convert.rs:482
            ├ embedding / image / speech / transcription               :363 / :391 / :397 / :403
            ├ evaluation → DecisionLanguageModel                       :382
            └ files → OpenAIFiles                                      :415   files.rs impl Files :105
            错误:openai_failed_response_handler / openai_stream_error   mod.rs:64 / :91

上游 packages/openai/src/ 的 25 个已迁测试文件(用例数):

responses/openai-responses-language-model.test.ts   210    chat/openai-chat-language-model.test.ts          100
responses/convert-to-openai-responses-input.test.ts 135    responses/openai-responses-prepare-tools.test.ts  72
image/openai-image-model.test.ts                     34    chat/convert-to-openai-chat-messages.test.ts      34
transcription/openai-transcription-model.test.ts     31    responses/openai-responses-reasoning-effort-update 23
files/openai-files.test.ts                           22    responses/openai-responses-api.test.ts            19
completion/openai-completion-language-model.test.ts  18    chat/openai-chat-prepare-tools.test.ts            14
openai-stream-error.test.ts                          11    speech/openai-speech-model.test.ts                10
embedding/openai-embedding-model.test.ts              8    openai-language-model-capabilities.test.ts         8
openai-provider.test.ts                               7    normalize-openai-json-schema.test.ts               6
openai-evaluation.test.ts                             5    responses/openai-responses-computer.test.ts        4
responses/expand-parallel-tool-call.test.ts           3    openai-forward-compatible-defaults.test.ts         3
chat/convert-openai-chat-usage.test.ts                1    openai-error.test.ts                               1
responses/convert-to-openai-responses-input-tool-search.test.ts   1

二、本 PR 的改动

六个提交,按内容分组:

1 test(openai): port the provider, error, schema and capability tests
    src/lib.rs                       #[cfg(test)] mod test_support;
    src/test_support/mod.rs          #[path = "../../tests/common/mock_fetch.rs"] pub(crate) mod mock_fetch;
    src/openai/mod.rs                结尾 #[cfg(test)] mod tests;
    src/openai/tests/                provider.rs、error.rs、evaluation.rs、forward_compatible_defaults.rs、
                                     language_model_capabilities.rs、normalize_json_schema.rs、stream_error.rs、fixtures/
    tests/openai_decision_test.rs、openai_factory_test.rs     各 1 处 /// partial:
2 test(openai): port the chat and completion tests
    src/openai/tests/chat/           chat_language_model.rs、convert_to_chat_messages.rs、chat_prepare_tools.rs、
                                     convert_chat_usage.rs、fixtures/、snapshots/
    src/openai/tests/completion/     completion_language_model.rs、fixtures/
    src/openai/model.rs:95           convert_usage 改成 pub(super)(usage 用例直接调它)
    src/openai/convert.rs、model.rs、tests/openai_upstream_e2e_test.rs   /// partial:(e2e 里换掉原来的 /// TS: 行)
3 test(openai): port the Responses tests
    src/openai/tests/responses/      responses_language_model.rs、convert_to_responses_input.rs、
                                     convert_to_responses_input_tool_search.rs、responses_prepare_tools.rs、responses_api.rs、
                                     responses_computer.rs、responses_reasoning_effort_update.rs、expand_parallel_tool_call.rs、
                                     fixtures/、snapshots/
    src/openai/responses/convert.rs  prepare_responses_tools_for 改成 pub(crate)(allowedTools 用例直接调它);2 处 /// partial:
4 test(openai): port the embedding, files and image tests
    src/openai/tests/{embedding,files,image}/
    tests/openai_embedding_test.rs、openai_image_test.rs     旧测试挪进 describe mod 加 /// upstream:
5 test(openai): port the speech and transcription tests
    src/openai/tests/{speech,transcription}/          realtime 的 doStream 用例在 #[cfg(feature = "realtime")] 下,用内存里的 MockWebSocket
    tests/openai_speech_test.rs、openai_transcription_test.rs、openai_transcription_stream_test.rs、
    openai_realtime_ws_replay_test.rs                 旧测试挪进 describe mod 加 /// upstream: 或 /// partial:
6 test(openai): map the upstream OpenAI files in layout.toml
    aimux-providers/layout.toml      只加 [openai],77 个上游源文件

src/openai/tests/ 的用例跟着 cargo test -p aimux-providers --lib 跑,读调用:

openai::tests::<文件>::<describe mod>::<用例>
  └ create_openai(OpenAIProviderSettings { api_key, base_url, fetch: MockFetch, web_socket: MockWebSocket, .. })
      └ provider.responses(..) / chat(..) / … → do_generate / do_stream     与真实调用同一条路径
  直接调的内部函数:convert_to_responses_input、prepare_responses_tools_for、normalize_json_schema、convert_usage 等

共享进程状态: provider.rs 里 4 个用例改 OPENAI_BASE_URL,#[serial] 并在结束时恢复;lib 测试二进制里另有一个用例把它设成 "not a url",所以所有新用例都显式传 base_url。其余用例不读也不改环境变量,不换进程默认 transport,没有全局计数器,不写临时文件,每个用例用自己的 MockFetch / MockWebSocket。

layout.toml 的 [openai]

61 个 mapped 到 openai/*.rs
live/*(4)、realtime/*(5)、openai-batch.ts、skills/*(2)、speech-translation/*(2)、
responses/expand-parallel-tool-call.ts                                missing,issue 275
version.ts                                                            not-applicable
files/openai-files.ts                 sub-feature 13
openai-stream-error.ts                sub-feature 7
responses/convert-to-openai-responses-input.ts   sub-feature 6,ts-only 1
responses/openai-responses-language-model.ts     sub-feature 5
responses/openai-responses-api.ts                ts-only 8
transcription/openai-transcription-model.ts      sub-feature 1,ts-only 1
embedding、image、responses/openai-responses-prepare-tools.ts    各 1

openai-evaluation、openai-forward-compatible-defaults、responses/openai-responses-computer、responses/openai-responses-reasoning-effort-update、responses/convert-to-openai-responses-input-tool-search 没有同名源文件,它们的排除写在所测的源文件下面。

harness 上的差别(期望值都没改,除了下面写明的比法):

  • 上游直接 new OpenAIResponsesLanguageModel(...);这里用 create_openai 加 api_key、base_url、fetch 建,generateId 能注入的地方用 provider 设置注入。
  • 请求体按 JS 读 JSON 的方式比(10.0 等于 10);流里的错误 part 按上游形状比,上游 provider stream error 多出的 type 字段在 aimux 只留在 data.type 里,比的时候去掉。
  • 坏 JSON 的报错只比 \nError message: 之前的部分,后面是 JS 运行时 JSON.parse 自己的文字。
  • UnsupportedFunctionality、InvalidArgument 只带一个字符串,上游的 argument 等字段查不了,只比错误类型和消息。
  • 按 aimux 已有约定改了比法的四个用例(登记在 Core 运行时:与 AI SDK 的剩余差距(对齐文档 §9.1 已知差距) #276):
    • chat › doStream › should set .modelId for model-router request:aimux 的 Usage.raw 原样保留响应里的 usage(RFC-0016),上游快照是 zod 解析后的,少了 audio_tokens;改成快照里的字段是原始 usage 的子集,其余部分照旧全等。
    • doStream › late response.failed:错误 part 通过 ApiCallError 比,providerCode 对上游的 code;上游的 type 表达不了。
    • openai-provider › rejects an empty OPENAI_BASE_URL during provider creation:aimux 文档写明环境变量里的坏 base URL 在每次调用时报错而不是建 provider 时,改成建成功、第一次调用报 baseURL must be a non-empty string. 且不发请求。
    • transcription › doStream › should cancel the audio stream when the WebSocket constructor throws:aimux 的 WebSocket 转写在 do_stream 返回前就连上(RFC-0028),所以构造失败从 do_stream 本身返回;两处都接受,并照旧要求音频流被取消。

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • New provider
  • New / updated binding
  • Docs / RFC
  • Tests(上游测试迁移)

Checklist

  • cargo +1.99 fmt --all -- --check passes
  • cargo +1.99 clippy -p aimux-providers -p aimux-provider-utils -p aimux-ops --lib --tests --all-features -- -D warnings passes
  • cargo +1.99 test -p aimux-providers -p aimux-provider-utils -p aimux-ops --all-features:167 个测试二进制,3568 个测试通过(比 fix: OpenAI Responses, chat, transcription and image behaviour match the AI SDK #368 多 704 个)
  • scripts/check_provider_boundaries.sh passes
  • Commit messages follow Conventional Commits

Notes for reviewers


Generated by Claude Code

cunninghamcard-bit and others added 30 commits October 24, 2026 11:34
… never started

@ai-sdk/openai 4.0.83 starts the stream's `responseId` at `null`
(openai-responses-language-model.ts:1587) and only `response.created` sets
it, so a stream without that event finishes with `responseId: null`. aimux
wrote an empty string. The generate path already writes `null`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 puts the reasoning item's id on every
`reasoning-delta` as `providerMetadata.<key>.itemId`
(openai-responses-language-model.ts:2735-2749). aimux sent the deltas
without provider metadata, while its reasoning start and end parts
already carried it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 takes `maxNumResults` as a JS number and sends it as
is (openai-responses-prepare-tools.ts:219), so `5` reaches the API's integer
field as `5`. aimux typed the field `Option<f64>`, so the typed factory sent
`5.0`. It is an `Option<u32>` now; the other number fields of the tool
arguments that count things (`partial_images`, `output_compression`) are integers already.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 maps each computer action with mapComputerAction
(openai-responses-language-model.ts:128): it keeps the fields of the
action's type, renames the scroll offsets, and adds `keys` only when it
is not null. aimux copied the wire action and only renamed the scroll
offsets, so `"keys":null` (and any extra field) reached the tool input.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
… names

@ai-sdk/openai 4.0.83 builds its tool name mapping from a fixed
providerToolNames table (openai-responses-language-model.ts:378): code
interpreter, computer, file search, image generation, local shell, shell,
web search (and preview), MCP, apply patch, tool search and programmatic
tool calling. aimux mapped the name of any `openai.*` provider tool, so
`toCustomToolName('computer_use')` (line 1358) returned the caller's name
for an `openai.computer_use` tool instead of `computer_use`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 parses annotations with one zod object per type
(openai-responses-api.ts:1500-1535 for responses, 1366-1395 for the
annotation.added chunk), which drops unknown keys, and puts the parsed
annotations in the text part's provider metadata
(openai-responses-language-model.ts:1072). aimux copied the wire
annotation, so a container_file_citation kept an `index` field.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
… order

@ai-sdk/openai 4.0.83 parses an apply_patch_call operation as {type, path,
diff} (openai-responses-api.ts:1698) and JSON.stringifies the parsed
operation into the tool input (openai-responses-language-model.ts:1443,
and the delete_file delta in the stream at line 1857), so the keys follow
the schema order. aimux stringified the wire object, keeping the order the
API sent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 parses a local_shell_call action as {type, command,
timeout_ms, user, working_directory, env} (openai-responses-api.ts:147) and
stringifies the parsed action into the generated tool input
(openai-responses-language-model.ts:998); the stream writes {type, command,
timeoutMs, user, workingDirectory, env} (line 2435). aimux stringified the
wire action, and in the stream moved the renamed keys to the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 lets a message's content hold only output_text parts
(openai-responses-api.ts:1486), so a refusal part fails the response schema
and createJsonResponseHandler rejects the call with an APICallError
("Invalid JSON response"); openai-evaluation.test.ts expects doEvaluate to
reject with APICallError for it. aimux skipped non-output_text parts and
returned a result without them. The stream schema does not validate message
content (openai-responses-api.ts:1098), so streaming is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 parses Responses logprobs as {token, logprob,
top_logprobs: [{token, logprob}]} (openai-responses-api.ts:875 chunk,
:1490 response), dropping other keys, and pushes the parsed value into
providerMetadata.openai.logprobs (openai-responses-language-model.ts:1065
generate, :2685 stream). aimux pushed the wire value, `bytes` included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…ut-end

@ai-sdk/openai 4.0.83 enqueues the tool-input-end of a streamed
function_call with providerMetadata { namespace } when the item has one
(openai-responses-language-model.ts:1986). aimux sent the tool-input-end
without provider metadata and put the namespace only on the tool-call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…m id only

@ai-sdk/openai 4.0.83 ends a summary part concluded when the next part is
added (openai-responses-language-model.ts:2709) or on
reasoning_summary_part.done with store (:2762) with providerMetadata
{ itemId }; only the ends sent on output_item.done (:2523) carry
reasoningEncryptedContent. aimux added reasoningEncryptedContent to the
early ends as well.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
… index

@ai-sdk/openai 4.0.83 records the id each output item started with by
output_index (openai-responses-language-model.ts:1633-1644, set at :1905
for reasoning) and resolves reasoning_summary_part.added (:2688),
reasoning_summary_text.delta (:2736), reasoning_summary_part.done (:2751)
and the reasoning output_item.done (:2503) through it. aimux did this only
for messages, so rotated reasoning item ids split one reasoning part into
several.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 sends an error part and finishes with error for a
chunk that fails the Responses schema
(openai-responses-language-model.ts:1664-1680); when the chunk looks like a
Chat Completions chunk (a choices array and no string type, :2962) the
error is an APICallError explaining that the endpoint speaks Chat
Completions (:2972). aimux ignored the chunk as an unknown event.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…rrors

@ai-sdk/openai 4.0.83 parses each stream chunk with openaiResponsesChunkSchema
(openai-responses-api.ts:869-1443); a modeled event that fails its schema
is not matched by the unknown-chunk fallback (:1433-1441), so doStream sends
the TypeValidationError as an error part and finishes with error
(openai-responses-language-model.ts:1664-1680). aimux read events as loose
JSON: a missing output_index defaulted to 0, a missing item was skipped.

This checks the envelope fields of each modeled event (type, output_index,
item and its type, item_id, the delta/arguments/code/diff string,
summary_index, the response object); item internals stay unchecked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/openai 4.0.83 passes the Responses event stream through
throwIfOpenAIStreamErrorBeforeOutput (openai-stream-error.ts:39, called at
openai-responses-language-model.ts:1552): it reads events until the first
output event (isResponseOutputChunk, :3112), throws an APICallError for an
error frame or a response.failed with an error, stops at a chunk that fails
parsing, and after response.in_progress races each read against 50 ms.
aimux only checked the first event, so an error after response.created
became an error part of a resolved stream.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…name and the GPT-6 requirement

Upstream resolveAsyncToolOption (src/responses/openai-responses-prepare-tools.ts:684-688)
warns with feature `async tool calling for "<name>"` and details
"Async tool calling is only supported by GPT-6 and later models.". aimux
left the name unquoted and said "Async tool calling is not supported by
this model."

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…ream reasons

Upstream prepareResponsesTools (src/responses/openai-responses-prepare-tools.ts:183-202,
:454-504, :654-660) warns with feature `allowedTools entry "<name>"` and
separate reasons for a namespaced tool ("tools inside an OpenAI tool
namespace are not visible ..."), a deferred tool ("deferred tools are not
visible ..."), an unlistable provider tool ("OpenAI does not support
<type> tools in tool_choice.allowed_tools") and an ambiguous provider tool
name. aimux left the name unquoted, merged the namespace and deferred
reasons, and worded the tool_search and ambiguity details differently.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…be expressed

Upstream prepareResponsesTools (src/responses/openai-responses-prepare-tools.ts:509-515)
throws UnsupportedFunctionalityError "allowedTools with only tools that
cannot be allow-listed (<dropped names joined by ", ">)". aimux threw the
message without the list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…scriptions error

Upstream prepareResponsesTools (src/responses/openai-responses-prepare-tools.ts:174-178)
throws UnsupportedFunctionalityError `conflicting descriptions for OpenAI
tool namespace "<name>"`. aimux left the name unquoted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…ill reference has no openai entry

Upstream resolves file references (src/responses/convert-to-openai-responses-input.ts:142-145,
:548-551) and shell skill references (src/responses/openai-responses-prepare-tools.ts:780-783)
with resolveProviderReference, whose NoSuchProviderReferenceError says
"No provider reference found for provider '<provider>'. Available
providers: <keys>", as the chat conversion in aimux already does. The
Responses code said "No file reference for openai" and "No skill
reference for openai".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…is not supported by the model

Upstream (src/responses/openai-responses-language-model.ts:455-492) warns
about a request-level reasoningEffortUpdate outside the model's supported
efforts with "<model> only supports the following reasoning efforts:
<efforts joined by ", ">". aimux said "<model> does not support reasoning
effort <effort>".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…n full without response storage

Upstream convertToOpenAIResponsesInput (src/responses/convert-to-openai-responses-input.ts:780-842)
handles tool search and programmatic tool calls before the
providerExecuted check: with store false they are rebuilt as
tool_search_call / program items. aimux applied the provider-executed rule
first and dropped them (only shell calls were rebuilt).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…tput

Upstream convertToOpenAIResponsesInput (src/responses/convert-to-openai-responses-input.ts:1646-1683)
maps the caller provider option of a tool result with mapToolCaller and
adds it to the function_call_output item. aimux mapped the caller only on
function_call items.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
… error

A streamed SSE event whose data is not JSON yielded `JsonParse` with only
the parser's message. The AI SDK's `parseJsonEventStream` reads each event
with `safeParseJSON`, whose `JSONParseError` reads
`JSON parsing failed: Text: <text>.\nError message: <message>`
(`@ai-sdk/provider` src/errors/json-parse-error.ts:16-19). Found while
porting the `@ai-sdk/openai-compatible` 3.0.62 tests (`doStream › should
handle unparsable stream parts`, chat and completion); every stream read
through `create_event_source_response_handler` now reports it the same way.
Well-formed JSON of the wrong shape stays `InvalidResponseData`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Upstream chat/openai-chat-language-model.ts:235-245 drops
prompt_cache_retention for models with supportedReasoningEfforts and warns
"promptCacheRetention is not supported by GPT-6 and later models; use
promptCacheOptions instead". aimux dropped the field but said
"sixth-generation and later models".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Upstream embedding/openai-embedding-model.ts:94 passes
openaiOptions.dimensions through unchanged, so 64 is serialized as 64.
aimux parsed it into an f64 and sent 64.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Upstream src/openai-evaluation.test.ts:72-89 expects the Responses request
of provider.evaluationModel() to carry text.format.name "evaluation"
(EvaluationLanguageModel from provider-utils; its own test,
evaluation-language-model.test.ts:87, expects responseFormat.name
"evaluation"). aimux's shared DecisionLanguageModel sent "decision", for
OpenAI, Anthropic and Google alike.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Upstream image/openai-image-model.ts:61-92 returns supportsFileInputs true
for dall-e-2 and its listed gpt-image/chatgpt-image ids, false for
dall-e-3, undefined otherwise, and supportsMaskInputs follows it; Azure
passes imageInputCapabilities with undefined values
(azure-openai-provider.ts:372-376). aimux used the trait default (unknown)
for every model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Upstream normalize-openai-json-schema.ts:176-246 rewrites a schema whose
allOf is exactly [{ $ref }] to a direct $ref, and at the root expands a
local #/definitions or #/$defs reference into the root object (keeping the
root's other keys). aimux's normalize_json_schema left the allOf in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Adds the [openai] table: every source file of @ai-sdk/openai 4.0.83 with its Rust file, the modules aimux does not have (live, realtime, batch, skills, speech translation, parallel tool call expansion) as missing under #275, and each case that is not ported with its reason.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
cunninghamcard-bit and others added 2 commits October 24, 2026 12:10
…erver

OpenAI realtime transcription now offers the `realtime` subprotocol (with the key as `openai-insecure-api-key.<token>`), and a WebSocket client fails when its offer gets no answer, as the realtime API's own server answers it. The fixture server in ops_fixtures.py sent no Sec-WebSocket-Protocol, so test_gil_release's operation_next case failed with "Server sent no subprotocol".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…iour-match-the-ai-sdk' into test/port-the-upstream-openai-tests
cunninghamcard-bit and others added 4 commits October 25, 2026 08:08
The reasoning delta now resolves its item id through output_index (commit "correlate rotated Responses reasoning item ids by output index"), so its test sends rotated ids on the summary events and expects the id the item started with, as asked on #323.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…er Breaking

FileSearchArgs::max_num_results changed from Option<f64> to Option<u32>, a change to a public type, so its entry belongs under Breaking rather than Fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…s-chat-transcription-and-image-behaviour-match-the-ai-sdk

# Conflicts:
#	CHANGELOG.md
…iour-match-the-ai-sdk' of https://github.com/arcships/aimux into test/port-the-upstream-openai-tests

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenAI test port:704 tests/780 upstream cases accounted,无 skip、fixtures/snapshots 非空;在 #368 rebase 后 restack。Approve。

cunninghamcard-bit and others added 2 commits October 25, 2026 08:35
…as it

#354 merged the same entry that commit 25 carried. The master merge kept
this branch's copy at its old place, so the diff against master moved it;
it now sits where master has it and the diff no longer touches it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…ription-and-image-behaviour-match-the-ai-sdk' into test/port-the-upstream-openai-tests
@cunninghamcard-bit
cunninghamcard-bit changed the base branch from fix/openai-responses-chat-transcription-and-image-behaviour-match-the-ai-sdk to master October 25, 2026 11:15
cunninghamcard-bit and others added 6 commits October 25, 2026 11:19
…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml
#	aimux-providers/tests/openai_embedding_test.rs
#	aimux-providers/tests/openai_image_test.rs
#	aimux-providers/tests/openai_transcription_test.rs
#	aimux-providers/tests/openai_upstream_e2e_test.rs
…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml
…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml
…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml
…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase is content-preserving: only master/parent merges and CHANGELOG/layout conflict resolution; tests and CI unchanged. Re-approving current head.

…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase verified content-preserving: only master/parent merges and CHANGELOG/layout conflict resolution; CI 16/16. Re-approving current head.

…eam-openai-tests

# Conflicts:
#	aimux-providers/layout.toml

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase verified content-preserving: only master/parent merges and conflict resolution; CI 16/16. Re-approving current head.

@eric8810
eric8810 merged commit 7d86b1b into master Oct 27, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants