Repository navigation
refactor(core)!: public JSON equals the AI SDK JSON, one provider construction path - #225
Closed
cunninghamcard-bit wants to merge 25 commits into
Closed
cunninghamcard-bit wants to merge 25 commits into
cunninghamcard-bit wants to merge 25 commits into
Conversation
…o through create_provider What: aimux_providers::provider (provider, provider_handle, register_provider, is_external_provider, load_providers_from_json, ExternalProviderEntry, ProviderProfile, ProviderOptions and the JSON overlay registration) is deleted. The ffi exports aimux_provider_new, aimux_provider_from_env and aimux_provider_handle_new build their provider through create_provider with PresetSettings; aimux_register_providers is removed from the C ABI and header. The Node and Python by-name functions keep their names and parameters and use the same path; their registerProviders / register_providers entries are removed. Unknown keys in the by-name config are now rejected. Deletes presets_test.rs and the Node provider_config test, which pinned the old registry. Why: one path for provider construction, the per-package factories plus create_provider and the registry; upstream has no by-name overlay. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
… output What: ContentPart::ToolResult has tool_call_id, tool_name, output (ToolResultOutput: text, json, execution-denied, error-text, error-json, content) and provider_options; the free JSON result and the is_error, preliminary and dynamic flags are gone, and the conversion to the provider prompt copies the output unchanged. Response messages build the output from the generate-level tool result. The web wire type, its TypeScript consumer, the Node and Python test suites and the Python wrapper follow. A repaired tool call that still fails parsing reports that parse error directly, as upstream does; only a failing repair function is wrapped. tool_input_test and tool_choice_test are now ports of parse-tool-call.test.ts and prepare-tool-choice.test.ts; their cases without an upstream counterpart are deleted. Why: upstream's ModelMessage tool result uses the same output union as the provider layer; one shape for one thing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
Every public type in aimux-core serializes camelCase fields, unions carry upstream's type tags, and optional fields are omitted instead of null. The generated TypeScript types, the web wire mirror and the FFI, CLI and replay readers follow. Vendor request/response types are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # bindings/node/src/types/ContentPart.ts # bindings/node/src/types/EvaluationCallOptions.ts # bindings/node/src/types/EvaluationResult.ts # tools/aimux-web/web/src/types/generated/WireContentPart.ts
Image size and video resolution are "WxH", aspect ratios "W:H", as the
upstream call options type them; they were {width, height} objects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…C ABI Node, Python, Go, Java, Kotlin, Swift and Flutter decode and encode the new public JSON (camelCase fields, upstream type tags, absent optionals, structured tool-result output). aimux_register_providers and every register-providers wrapper are removed. aimux-ffi.h documents the camelCase config keys and that stream callbacks receive TextStreamPart. Tests whose mock servers speak chat completions use a chat preset, since the OpenAI constructor now returns the Responses model as upstream's openai(id) does. Missing API keys surface on the first call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
公共 JSON 与绑定不应继续固化统一审批契约(核对 head 本轮确认的范围调整:aimux 不承担审批运行时,也不定义统一的审批公共类型/语义;审批相关的定义、解释和处理由上层负责。这是对 #200 §0.4 原先“保留审批 variant”范围的收窄,不是否认 AI SDK 中存在这些类型。 本 PR 将继承的审批类型发布为 tool-approval-request / tool-approval-response 的 wire variant,并更新 Node、Python、Java、Kotlin、Swift、Flutter 等绑定及文档。例如 ToolApprovalResponsePart.ts;错误序列化还新增了 AI_ToolCallNotFoundForApprovalError 的映射。 请在 #219/#220 调整完成后,同步移除专用审批 DTO、联合分支、公开导出、专用错误映射,以及声明这些类型的文档和仅固定该审批契约的测试;由原始类型源修改后再生成/同步绑定,不只手删 TypeScript 文件。保留已有通用 JSON/原始数据接口的合同测试即可;本次不要求新增审批专用数据承载、无损往返能力或对应验收门槛。 |
A misspelled provider name now reads `No such provider: opeanai. Did you mean 'openai'? (available providers: …)`. The suggestion comes from the error's own `available_providers`: names compare case-insensitively with `-` read as `_`, a name is close at an optimal-string-alignment distance of at most 2 and at most a third of the longer name, and at most three are listed, nearest first. This extends the upstream message: the pinned `ai/src/registry/no-such-provider-error.ts` text has no suggestion. The fields, the error JSON and the ABI are unchanged, and callers do not parse the message (docs/error-model.md, rule 5). No ported upstream test asserts the full message text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…options Type validation 'if-present', the shared generate_id, and a required tool-call index, as in openai-chat-language-model.ts and openai-chat-api.ts. Servers that omit the index use openai_compatible. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
This was referenced Oct 9, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R6wpZPfWrKN9XKB7WXXK2o
This was referenced Oct 12, 2026
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.
What
Makes aimux's public JSON equal the AI SDK's JSON, leaves one construction path for providers, and gives the call-layer tool result upstream's structured
output. All seven language bindings follow. Baserfc-0036/vendor-alignment(C); follows C and is the last of four (A to D). It is a breaking change for every binding. New PR, with no earlier PR to replace.Layers
rfc-0036/wire-formattype-tagged public JSON, omitted optionals, structured tool resultoutput, by-name provider entries deleted, bindings migratedBefore and after
tool_call_id,finish_reason,provider_metadata, ...)provider/src/**,ai/src/**type definitions{"TextDelta":{...}}) or snake_case tags (tool_call)type(or upstream's key:role,sourceType) with upstream's strings (text-delta,tool-call,tool-result,file-base64, ...); aimux-only unions use kebab-caselanguage-model-v4-stream-part.ts,language-model-v4-content.ts,ai/src/prompt/message.ts, ...nullfield?: TT | undefinedfields{width, height}objects"1024x1024","16:9"stringsimage-model-v4-call-options.ts,video-model-v4-call-options.tsresultplusisError,preliminary,dynamictoolCallId,toolName,output(text,json,execution-denied,error-text,error-json,content),providerOptions; conversion to the provider prompt is an identity mapping; a repair function's parse failure is rethrown as isai/src/prompt/message.ts(ToolResultPart),convert-to-language-model-prompt.ts,parse-tool-call.tsaimux_providers::provider::{provider, provider_handle, register_provider, load_providers_from_json, ...}and Caimux_register_providersbeside the factoriescreate_provider(name, PresetSettings)and keep their public function names;config_jsonrejects unknown keyscreate<Vendor>factories,ai/src/registry/provider-registry.tsNoSuchProvidermessageNo such provider: x (available providers: …), upstream's textNo such provider: x. Did you mean 'y'? (available providers: …): up to three close names fromavailable_providers(case-insensitive,-read as_, edit distance ≤ 2 and ≤ a third of the name). An aimux extension of the upstream message; fields, error JSON and ABI unchanged, and callers do not parse messages (docs/error-model.mdrule 5). No ported test asserts the full textai/src/registry/no-such-provider-error.ts(no suggestion)Vendor request and response types under
aimux-providers/src/*/are unchanged: they keep the vendor's wire format.ChatCompletion*inopenai_output.rskeeps OpenAI's snake_case because it is OpenAI's response shape.Bindings
Node, Python, Go, Java, Kotlin, Swift and Flutter decode and encode the new JSON, drop
registerProvidersand document the camelCaseconfig_jsonkeys.aimux-ffi.hdocuments the new keys and that the stream callbacks receiveTextStreamPart. The OpenAI constructors return the Responses model, as upstream'sopenai(id)does, so binding tests whose mock servers speak chat completions now build a chat preset at the mock URL; tests that expected a missing API key at construction now expect it on the first call.maturin developgo test ./...against the debug static librarygradle clean test, Java 17gradle test, Java 17dart test(Flutter 3.47 SDK) with the debug dylibswiftcand run through a local XCTest stand-in that is not in the repositoryTests
aimux-providers/tests/presets_test.rsandbindings/node/__test__/provider_config.test.ts(hand-written tests of the removed by-name registry); hand-written cases intool_choice_test.rsandtool_input_test.rswithout an upstream counterpart; binding tests that only pinnedregisterProviders.*_upstream_e2e_test.rsexpectations to the new JSON; the inline error-JSON golden strings inerror_value_golden_test.rsnow hold thename-tagged shape (AI SDK error names such asAI_APICallError), and the test checks them against the serializer; binding tests to the new names.fixtures/aisdkand the fourreplay_fixtures(vendor wire format) are unchanged.Not in this pull request
ProxyConfig(aimux-provider-utils) and the provider token JSON (aimux-providers) keep snake_case keys; they are outside aimux-core, which this rule covers.AiMuxErrorstays an untyped JSON value in the Swift, Kotlin and Flutter bindings, and transcription stream parts stay JSON strings there.aspectRatiodoes not accept upstream's'adaptive'value.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 targetsrfc-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.Upstream over RFC text
ToolCallRepair { original_error, cause }and states this matches AI SDKparseToolCallstep by step; RFC-0042 §5 (item 3) repeats it. The pinnedai/src/generate-text/parse-tool-call.ts(ai 7.0.122) wraps only an exception thrown by the repair function: the re-parse of the repaired call sits outside that try, so its error reaches the outer catch, which returns an invalid tool call carrying that parse error unwrapped, with the original call'sinputandtoolName. This PR follows upstream (aimux-core/src/parse_tool_call.rs,apply_repair_outcome). Upstream'sparse-tool-call.test.tshas no case for it; the evidence is the source. Request: amend RFC-0035 §3.2.Implemented in branch E, where the AI SDK has no counterpart
namewith the class properties (e.g.{"name":"AI_APICallError","url":…,"requestBodyValues":{},"statusCode":500,"isRetryable":true}, seeaimux-core/tests/error_value_golden_test.rs). E adds RFC-0039 §6.1's envelope around it,{ kind, code, message, error, retryable, retryAfterMs }, and reduces the 23aimux_error_*getters to code, message and json.aimux_call/aimux_streamprimitives with ABI version and manifest query (§4.1); named convenience functions such as the docs: full-chain AI SDK alignment design and impact map #200 design doc §6'saimux_provider_model(provider, method, model_id)generated from the same op table (§0.2, §4.1). This PR still has 127 exports including the per-vendor constructors; it removes onlyaimux_register_providers. The old exports are deleted in one commit at the end of E.ts-rs/scripts/gen_*retire in the same switch. This PR keepsts-rs(189#[ts(export)]in aimux-core) and hand-updates the Go, Java, Kotlin, Swift and Flutter types (71 files, +4,973 / −6,816). E builds the descriptors, op table,manifest.jsonwith--check, and the generated C header; the per-language DTO generators that replace the hand-written types, and the retirement ofts-rs, are needed before master.Types.ktalongsideTypes.java. E moves Kotlin onto Java after Java uses the new ABI.Partial or not checked
FileByteshere accepts a base64 string or an integer array.InvalidResponseData, unlike feat(provider-utils)!: align StreamingToolCallTracker with the AI SDK and move it next to its users #204's drop. This stack inherits feat(provider-utils)!: align StreamingToolCallTracker with the AI SDK and move it next to its users #204's tracker, which drops ambiguous deltas as upstream does.Agrees
typetags, absent distinct from null (RFC-0042 §1; RFC-0041 §4.1 for the recording JSON).Verification
Gate on the head
415e37e1:cargo fmt --check,cargo clippy --workspace --all-targets -D warnings,cargo docwith-D warnings, tests of core, provider-utils, providers, ffi, web, replay and cli: 2,780 passed, 0 failed, 6 ignored (14 fewer than C; this PR deletes hand-written tests); Node and Python bindingscargo check; provider boundary script;gen_ts_types.py --check;gen_providers_doc.py --check. Cassettes andfixtures/aisdkunchanged. Diff against the base: 324 files changed, 8223 insertions(+), 10330 deletions(-). After the restack onto the currentmaster, only this head was tested; the intermediate layers were checked with fmt and clippy only. Binding suites: see the table above.Restacked head
117df5ce. Restack (2026-10-09): mergedmaster(#213, SSE) and the rewritten #204 tracker down the stack by merge, no history rewrite. The merge into this layer was conflict-free.117df5cethen drives the OpenAI/Azure chat tracker with@ai-sdk/openai's options:typevalidated if present, ids fromgenerate_id,DeltaToolCall.indexrequired. Checked on that head:cargo fmt --check,cargo clippy --workspace --all-targets -D warnings,cargo test --workspace(153 test binaries, 0 failed).cargo doc, binding suites and the generator--checkscripts were not rerun.Commit
2a5f7d68(theNoSuchProvidersuggestion, on top of8f5265c6):aimux-coreerror::tests (13), theaimux-providersdefault_providerstests (2, one checks real misspellings such asopeanai,gorq,google-vertexagainst the 326 built-in names), andcargo clippy -D warningsfor both crates. The full gate above was not rerun.Not verified
None of the binding suites run in CI for stacked branches. Swift ran through the stand-in above because SwiftPM crashes at startup on the test machine's Command Line Tools and XCTest is absent there, so
swift testwas not run.🤖 Generated with Claude Code