Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
425 commits
Select commit Hold shift + click to select a range
92e86a9
fix(core): tests and comments follow the merged retry and error sets
claude Oct 13, 2026
4b662a5
fix(providers): layer 5 provider code takes layers 2 to 4 types
claude Oct 13, 2026
9bfa1e9
chore(fixtures): the recorder pins provider-utils 5.0.55 as VERSIONS.…
claude Oct 13, 2026
bbbc72a
refactor(core)!: decisions follow DecisionModelV4
claude Oct 13, 2026
ed192ea
docs: video polling, retry comments and generated types after the merge
claude Oct 13, 2026
b647fb8
fix(core)!: bytes are base64 strings in the public JSON (#232 C33)
claude Oct 13, 2026
8f9bcca
fix(decision): decision image bytes are a base64 string (#232 C33a)
claude Oct 13, 2026
06bb478
feat(core)!: the operations take an injectable download, as the AI SD…
claude Oct 13, 2026
52bd723
fix(core)!: JSON null is kept apart from a missing value (#232 C34)
claude Oct 13, 2026
0b1908d
docs(changelog): note that JSON null is kept apart from missing (#232…
claude Oct 13, 2026
d2c82df
Merge [5/8] into [6/8]: providers are AI SDK factories
claude Oct 13, 2026
8b23a16
fix(providers): layer 6 provider code takes layer 5's types
claude Oct 13, 2026
f4c5fe0
fix(providers): the layer 6 transcription models refuse URL audio
claude Oct 13, 2026
87544a5
fix(core)!: file content parts are the AI SDK FilePart (#232 C35)
claude Oct 13, 2026
c3846ba
test(anthropic): an unparsable stream chunk is an error part
claude Oct 13, 2026
ac0d061
fix(providers): Cohere, Open Responses and xAI Responses declare thei…
claude Oct 13, 2026
19a9a3c
fix(core)!: generated files are {base64, mediaType} (#232 C46)
claude Oct 13, 2026
317ebcd
test: restore the hand-written unit tests 48a92aaf deleted that still…
claude Oct 13, 2026
d062be3
fix(codex): Codex models declare the OpenAI Responses supported URLs
claude Oct 13, 2026
3f389ba
fix(huggingface): pin @ai-sdk/huggingface 2.0.62 and declare its supp…
claude Oct 13, 2026
97c6fbc
fix(core)!: timestamps are toISOString() strings (#232 C48)
claude Oct 13, 2026
bc013ad
chore(node): export the generated Timestamp type (#232 C48)
claude Oct 13, 2026
c71fae0
Merge [6/8] into [7/8]: layer 6 with layers 2-5 (V4 types, operation …
claude Oct 13, 2026
21719b5
fix(core)!: text and reasoning stream deltas carry text (#229 B9)
claude Oct 13, 2026
e8279c4
fix: layer 7 code takes layers 2-6's types
claude Oct 13, 2026
635a3f4
test(providers): the layer 7 transcription models refuse URL audio
claude Oct 13, 2026
f94eadb
fix(core): decide runs its model call in the operation settings
claude Oct 13, 2026
3d78123
refactor(core)!: provider namespace maps keep insertion order (#229 N…
claude Oct 13, 2026
efd10d6
test(kotlin): the OpenAI decision test sends the fixture's options in…
claude Oct 13, 2026
6aa327d
docs(node): regenerate index.d.ts for applyToolCallRepair's doc
claude Oct 13, 2026
35711cb
test(node): the reasoning replay test gives its tool call a result
claude Oct 13, 2026
dcc11f8
Merge [3/8] into [4/8]: the reasoning replay test gives its tool call…
claude Oct 13, 2026
18e3e0e
test(bindings): the DeepSeek usage.raw tests read the step usage
claude Oct 13, 2026
4ce9aa0
docs(node): regenerate index.d.ts for applyToolCallRepair's doc
claude Oct 13, 2026
cfe290a
Merge [4/8] into [5/8]: binding tests follow layers 3 and 4
claude Oct 13, 2026
8bef2c9
Merge [5/8] into [6/8]: binding tests follow layers 3 and 4
claude Oct 13, 2026
1602ed8
Merge [6/8] into [7/8]: binding tests follow layers 3 and 4
claude Oct 13, 2026
5415fea
test(swift): the OpenAI decision test sends the fixture's options in …
claude Oct 13, 2026
f69a33f
fix(core)!: standalone tool calls and sources carry their type tag (#…
claude Oct 13, 2026
af252bb
fix(core)!: integral tool input numbers are written as JavaScript wri…
claude Oct 13, 2026
f42526b
docs(rfc): RFC-0035 shows the final repair wire and upstream's contro…
claude Oct 13, 2026
4b200a5
test(node,python): decision usage omits unknown totals, never null
claude Oct 13, 2026
38770b5
fix(core): keep recording schema 2 and read the recordings master writes
claude Oct 13, 2026
9d01eb5
chore(core): keep the removed key-source field name out of code
claude Oct 13, 2026
a382242
Merge [5/8] into [6/8]: recording schema 2, master recordings load
claude Oct 13, 2026
7294e13
Merge [6/8] into [7/8]: recording schema 2, master recordings load
claude Oct 13, 2026
370f077
docs(changelog): legacy vertex and bedrock keys are still read and wr…
claude Oct 13, 2026
4cb1e53
Merge [5/8] into [6/8]: CHANGELOG wording for legacy namespace keys
claude Oct 13, 2026
9fcc401
Merge [6/8] into [7/8]: CHANGELOG wording for legacy namespace keys
claude Oct 13, 2026
3bae669
docs(changelog): decision recordings carry no capability snapshot
claude Oct 13, 2026
3949042
Merge [7/8] into [8/8]: layer 7 (decisions, speech usage, layers 2-6)…
claude Oct 13, 2026
2da6fa9
fix(go)!: Error.Data keeps a null API call error data apart from none
claude Oct 13, 2026
7a9a4e7
docs: RFC-0035 names the tests that pin repair; RFC-0026 deltas carry…
claude Oct 13, 2026
c9d99c9
refactor!: text results' usage, steps, tool outputs and Output follow…
claude Oct 13, 2026
1f6d61e
docs(rfc): RFC-0036's stream example writes the public JSON
claude Oct 13, 2026
a8fb12f
Merge [7/8] into [8/8]: recording schema 2, master recordings load
claude Oct 13, 2026
6b214ac
refactor!: decisions follow the public JSON
claude Oct 13, 2026
87e652c
docs: fix two rustdoc links that fail cargo doc -D warnings
claude Oct 13, 2026
ee60e4e
Merge [6/8] into [7/8]: rustdoc link fixes
claude Oct 13, 2026
58572b3
Merge [5/8] into [6/8]: rustdoc link fixes
claude Oct 13, 2026
48083ff
docs: plain code spans for the private stream_transcription methods
claude Oct 13, 2026
6d51fe7
Merge [7/8] into [8/8]: rustdoc link fixes
claude Oct 13, 2026
e2e9ca4
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 14, 2026
75cfef6
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 14, 2026
2b42899
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 14, 2026
c94e8d7
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 14, 2026
03b43b7
ci: speed up test builds, deduplicate caches, and use prebuilt cargo-ndk
cunninghamcard-bit Oct 14, 2026
f755c28
test(flutter): typed stream fixtures are JSON maps; the chat mock is …
claude Oct 14, 2026
9ab9dff
ci: keep uploaded artifacts unchanged and stop PR runs from evicting …
claude Oct 14, 2026
c75e37f
test(swift): the plain-text result's content part is type-tagged
claude Oct 14, 2026
c9ade5f
ci: fewer jobs per run so pull requests stop queueing for runners
claude Oct 14, 2026
e27c13c
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 14, 2026
ba3f923
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 14, 2026
fc67f42
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 14, 2026
8656cd0
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 14, 2026
2e746cd
docs(alignment): the first-chunk timer start is a registered difference
claude Oct 14, 2026
df7a67c
Merge the CI speed-up (#262) into [1/8]
claude Oct 14, 2026
4e0bfe2
Merge [1/8] into [2/8]: the CI speed-up (#262)
claude Oct 14, 2026
0fe1a89
Merge [2/8] into [3/8]: the CI speed-up (#262)
claude Oct 14, 2026
2af8255
Merge [3/8] into [4/8]: the CI speed-up (#262)
claude Oct 14, 2026
8ecea4e
Merge [4/8] into [5/8]: the CI speed-up (#262)
claude Oct 14, 2026
777e358
Merge [5/8] into [6/8]: the CI speed-up (#262)
claude Oct 14, 2026
008859c
Merge [7/8] into [8/8]: the CI speed-up (#262)
claude Oct 14, 2026
f80fff2
Merge [6/8] into [7/8]: the CI speed-up (#262)
claude Oct 14, 2026
ee0b00e
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 14, 2026
ecbfc3e
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 14, 2026
c5c84f2
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 14, 2026
f371112
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 14, 2026
8ab9abb
ci: restore the Flutter SDK cache everywhere, save it from master only
claude Oct 14, 2026
753b61b
ci: build test-only FFI libraries at opt-level 1, not 0
claude Oct 14, 2026
19b9e9f
Merge the CI fix (#262) into [1/8]: test libraries at opt-level 1
claude Oct 14, 2026
824f948
Merge [1/8] into [2/8]: the CI fix (#262)
claude Oct 14, 2026
515fb3d
Merge [2/8] into [3/8]: the CI fix (#262)
claude Oct 14, 2026
75e96c2
Merge [3/8] into [4/8]: the CI fix (#262)
claude Oct 14, 2026
5aadb1c
Merge [4/8] into [5/8]: the CI fix (#262)
claude Oct 14, 2026
dd13b04
Merge [5/8] into [6/8]: the CI fix (#262)
claude Oct 14, 2026
ca181dd
Merge [7/8] into [8/8]: the CI fix (#262)
claude Oct 14, 2026
22db086
Merge [6/8] into [7/8]: the CI fix (#262)
claude Oct 14, 2026
540fef5
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 14, 2026
4166bf0
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 14, 2026
cdf678a
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 14, 2026
430eb5d
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 14, 2026
8ccd560
Merge branch 'master' into ci/optimize-ci-speed-and-cache
claude Oct 15, 2026
ca886c1
ci: pin cargo-ndk and keep pull requests from saving two more caches
claude Oct 15, 2026
7c5ded7
Merge branch 'master' into ci/optimize-ci-speed-and-cache
claude Oct 15, 2026
3fcf1da
Merge master into the ProviderName removal: Bedrock SigV4 and secret …
claude Oct 15, 2026
ec7ab7c
chore: delete the web console's generated ProviderName copy
claude Oct 15, 2026
ea0384a
Merge master into the web wire types: tiered CI (#263), Bedrock SigV4…
claude Oct 15, 2026
1bd6a55
ci: type-check the web console against the generated types
claude Oct 15, 2026
d1005cf
Merge the CI speed-up branch (with master) into [1/8]: tiered CI (#26…
claude Oct 15, 2026
d482975
docs: size and performance gates follow RFC-0043
claude Oct 15, 2026
c1d10b5
docs: the alignment doc links back to RFC-0036 §12.2 and names the me…
claude Oct 15, 2026
b32dc0a
ci: run the AI SDK pin script tests
claude Oct 15, 2026
710fcd7
docs: note the retired contract fixtures in the RFCs and the changelog
claude Oct 15, 2026
be87f81
docs(web): note that cargo test rewrites the tracked node types
claude Oct 15, 2026
d30833d
Merge [1/8] into [2/8]: master (#205-#212, #248, #263), the RFC-0043 …
claude Oct 15, 2026
d005871
test(openai): tool calls are flushed after an error frame
claude Oct 15, 2026
f0ee302
refactor(provider-utils): bound the tracker's id suffix search
claude Oct 15, 2026
fb2f23f
Merge [2/8] into [3/8]: master (#205-#212, #248, #263), the RFC-0043 …
claude Oct 15, 2026
7a726e3
test(core): restore the OpenAI output tool-argument tests
claude Oct 15, 2026
88ce267
test(core): part metadata replays whole into providerOptions
claude Oct 15, 2026
750f9a0
test(ffi): restore the composite, frame-error and session-input tests
claude Oct 15, 2026
4b21206
fix(core): a stream that ends with Finish is traced as completed
claude Oct 15, 2026
bfd9fdd
test(core): restore the TraceLayer suite
claude Oct 15, 2026
6faea33
test(core): a traced call keeps the id of its recording
claude Oct 15, 2026
62e2967
test(google): restore the inlineData-to-file tests
claude Oct 15, 2026
9fd8905
test(core): restore the replay_with_model tests
claude Oct 15, 2026
95d9a3a
Merge [3/8] into [4/8]: master (#205-#212, #248, #263), the RFC-0043 …
claude Oct 15, 2026
a7efac0
test(core): pin an abort after a finished step and repeated tool-call…
claude Oct 15, 2026
d989330
docs(alignment): register recording_context and deferred provider res…
claude Oct 15, 2026
1aead53
refactor(stream_text): the chunk loop has no arm that cannot run
claude Oct 16, 2026
73626fe
Merge [4/8] into [5/8]: master (#205-#212, #248, #263), #262's CI fol…
claude Oct 16, 2026
5fa4045
test(provider-utils): the default-transport test keeps 127.0.0.1 off …
claude Oct 15, 2026
e03b5f1
docs(web): a blank key at a custom base_url goes out as an empty key
claude Oct 15, 2026
20a0ee5
test(provider-utils): pin the redirect cap and re-signing on a same-o…
claude Oct 15, 2026
b7f1b7f
docs(rfc): RFC-0040 §3.2 follows the implemented redirect rule
claude Oct 15, 2026
a2ba419
test(providers): a preset's base-URL and optional-key variables
claude Oct 15, 2026
636b4ca
Merge [5/8] into [6/8]: master (#205-#212, #248, #263), #262's CI fol…
claude Oct 16, 2026
a693548
test(mistral): model_length is a length finish
claude Oct 15, 2026
bd2d041
test(providers): non-object input of a past tool call
claude Oct 15, 2026
007ad30
fix(anthropic): toolset member calls go back as upstream sends them
claude Oct 16, 2026
ed69b7e
test(preset): the registry checks run on any input
claude Oct 16, 2026
a9a97fd
fix(anthropic): provider tool args are checked as upstream checks them
claude Oct 16, 2026
78d7b90
Merge [7/8] into [8/8]: master (#205-#212, #248, #263), #262's CI fol…
claude Oct 16, 2026
13845e1
test(provider-utils): say how the botocore SigV4 pins were made
claude Oct 15, 2026
9db6427
Merge [6/8] into [7/8]: master (#205-#212, #248, #263), #262's CI fol…
claude Oct 16, 2026
272a70b
docs(core): a tool result carries only output
claude Oct 15, 2026
e899de9
feat(anthropic): decision_model is upstream's evaluationModel
claude Oct 15, 2026
8f1eaf4
test(google): restore the thought-signature replay tests
claude Oct 15, 2026
7511ff4
test(providers): a failed decision is recorded and replays as the fai…
claude Oct 15, 2026
f275cdf
fix(core): request options reject unknown keys
claude Oct 15, 2026
7c3bb6a
docs(core): replay's question types and the video status key
claude Oct 15, 2026
d365388
fix(core): a prompt part with an unknown type names it
claude Oct 15, 2026
72e4fb9
docs(core): cite upstream for the snake_case video frame type
claude Oct 15, 2026
586235f
test(anthropic): compare usage JSON with what the AI SDK wrote
claude Oct 15, 2026
0e2ffc3
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 16, 2026
1e60857
test(aimux-core): replace the in-test legacy json_repair port with re…
claude Oct 14, 2026
f0b17f6
docs(core): say why json_repair stays public
claude Oct 15, 2026
b1fd1b3
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 16, 2026
0eeb671
refactor(providers): keep vendor endpoints out of shared and openai_c…
claude Oct 14, 2026
bd6c67e
test(searxng): the instance URL comes from SEARXNG_URL per request
claude Oct 15, 2026
8676f29
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 16, 2026
b21d9ed
refactor(provider-utils): PipeThrough is a Stream over one reused queue
claude Oct 14, 2026
f04c217
refactor(providers): chat streams share ChatToolCalls, no tracker type
claude Oct 14, 2026
faf5c0f
refactor(providers): vendor streams share StreamContext and Emit
claude Oct 14, 2026
ae1437d
test(providers): drive chat and Anthropic stream transformers synchro…
claude Oct 14, 2026
a6d890c
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 16, 2026
70abded
chore(providers): let shared layers use the stream module in the boun…
claude Oct 14, 2026
da607da
docs: say where the chat streams keep their tool-call state
claude Oct 15, 2026
8e6a597
test(provider-utils): nothing is emitted after a transform stream ended
claude Oct 15, 2026
2574772
fix(core): fix_json steps through UTF-16 code units like upstream
claude Oct 16, 2026
563d65c
test(dataforseo): the login comes from DATAFORSEO_LOGIN per request
cunninghamcard-bit Oct 16, 2026
8f90ad6
feat(core): the recording context has an operation scope
claude Oct 16, 2026
13131b8
refactor(core)!: the recording context is an operation value, not a c…
claude Oct 16, 2026
2c86792
Merge the ProviderName removal (#246) into the web wire types
claude Oct 16, 2026
c749bf5
fix(core)!: a deferred tool result needs a provider tool that support…
claude Oct 16, 2026
9432c71
docs(core): the replay key and input record no longer name a recordin…
claude Oct 16, 2026
36b91bc
Merge [4/8] into [5/8]: deferred tool results and the recording scope
claude Oct 16, 2026
46e974a
Merge [5/8] into [6/8]: deferred tool results and the recording scope
claude Oct 16, 2026
aad72e8
Merge [6/8] into [7/8]: deferred tool results and the recording scope
claude Oct 16, 2026
67c7f1d
Merge [7/8] into [8/8]: deferred tool results and the recording scope
claude Oct 16, 2026
0b78344
build(core): ts-rs exports run only from the generator
claude Oct 16, 2026
e4104aa
fix(provider-utils): the tool call tracker generates ids as upstream
claude Oct 16, 2026
2f51e47
feat(bindings): typed provider tools carry supportsDeferredResults
claude Oct 16, 2026
79ea814
chore(node): regenerate InputRecord.ts for the corrected options comment
claude Oct 16, 2026
af67cb8
Merge [2/8] into [3/8]: the tracker generates ids as upstream
claude Oct 16, 2026
1e0a04d
Merge [3/8] into [4/8]: the tracker generates ids as upstream
claude Oct 16, 2026
85e65d8
Merge [4/8] into [5/8]: tracker ids, binding provider tools, InputRec…
claude Oct 16, 2026
2eef03b
Merge [5/8] into [6/8]: tracker ids, binding provider tools, InputRec…
claude Oct 16, 2026
31fb8fe
Merge [6/8] into [7/8]: tracker ids, binding provider tools, InputRec…
claude Oct 16, 2026
eb24c62
Merge [7/8] into [8/8]: tracker ids, binding provider tools, InputRec…
claude Oct 16, 2026
c6382f1
fix(core)!: MoA failMode and the recording operation are kebab-case
claude Oct 16, 2026
c525525
Merge #247 into [1/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
020688d
Merge [1/8] into [2/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
21a4b2f
Merge [2/8] into [3/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
36a9e5c
Merge [3/8] into [4/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
2c477ce
Merge [4/8] into [5/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
4ba56b0
Merge [5/8] into [6/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
e661f11
Merge [6/8] into [7/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
564cfff
Merge [7/8] into [8/8]: gated ts-rs exports and the ProviderName removal
claude Oct 16, 2026
b0dc009
docs(changelog): the deferred tool result rule is generate_text's
claude Oct 16, 2026
e347e8c
feat(go): provider tools carry supportsDeferredResults
claude Oct 16, 2026
6422a37
Merge [4/8] into [5/8]: the deferred tool result rule is generate_text's
claude Oct 16, 2026
7da96b4
Merge [5/8] into [6/8]: the deferred tool result rule is generate_text's
claude Oct 16, 2026
de9e9a0
Merge [6/8] into [7/8]: the deferred tool result rule is generate_text's
claude Oct 16, 2026
095ee27
Merge [7/8] into [8/8]: the deferred tool result rule is generate_text's
claude Oct 16, 2026
7a4b023
Merge master into #246: the #266 docs wording
claude Oct 16, 2026
453f611
Merge #246 into #247: master's #266 docs wording
claude Oct 16, 2026
796bd99
Merge #247 into [1/8]: master's #266 docs wording
claude Oct 16, 2026
36c8823
Merge [1/8] into [2/8]: master's #266 docs wording
claude Oct 16, 2026
59d9005
Merge [2/8] into [3/8]: master's #266 docs wording
claude Oct 16, 2026
2997346
Merge [3/8] into [4/8]: master's #266 docs wording
claude Oct 16, 2026
857b29d
Merge [4/8] into [5/8]: master's #266 docs wording
claude Oct 16, 2026
bed5ca7
Merge [5/8] into [6/8]: master's #266 docs wording
claude Oct 16, 2026
cd55182
Merge [6/8] into [7/8]: master's #266 docs wording
claude Oct 16, 2026
8eaa63d
Merge [7/8] into [8/8]: master's #266 docs wording
claude Oct 16, 2026
f390ff4
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 16, 2026
adec25f
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 16, 2026
5330a48
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 16, 2026
e2f2e46
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 16, 2026
6ca248e
test(scripts): give the pin fetch test its own user and global npmrc
claude Oct 16, 2026
e572504
Merge [3/8] into [4/8]: the pin fetch test fix
claude Oct 16, 2026
7790a2b
Merge [1/8] into [2/8]: the pin fetch test fix
claude Oct 16, 2026
be39004
Merge [2/8] into [3/8]: the pin fetch test fix
claude Oct 16, 2026
9a9056c
Merge [4/8] into [5/8]: the pin fetch test fix
claude Oct 16, 2026
f88bd61
Merge [5/8] into [6/8]: the pin fetch test fix
claude Oct 16, 2026
dc656f8
Merge [6/8] into [7/8]: the pin fetch test fix
claude Oct 16, 2026
5ee53e2
Merge [7/8] into [8/8]: the pin fetch test fix
claude Oct 16, 2026
f89c989
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 16, 2026
27901f1
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 16, 2026
6e2b132
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 16, 2026
a4e5267
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 16, 2026
38b6cde
docs(rfc): the retry count, timeout and session id are operation sett…
claude Oct 16, 2026
f039e50
Merge [4/8] into [5/8]: RFC text for the operation settings
claude Oct 16, 2026
7be67e6
Merge [5/8] into [6/8]: RFC text for the operation settings
claude Oct 16, 2026
593b5e5
Merge [6/8] into [7/8]: RFC text for the operation settings
claude Oct 16, 2026
9cef9bf
Merge [7/8] into [8/8]: RFC text for the operation settings
claude Oct 16, 2026
2a0b02b
Merge master into [1/8]: keep this layer ci.yml/CHANGELOG/local-ci
eric8810 Oct 17, 2026
057453e
Merge [1/8] into [2/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
bdeebad
Merge [2/8] into [3/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
6fec783
Merge [3/8] into [4/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
79c2efc
Merge [4/8] into [5/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
dbf852f
Merge [5/8] into [6/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
fa3d877
Merge [6/8] into [7/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
f8a4287
Merge [7/8] into [8/8]: #262, #246 and #247 as eric merged them
claude Oct 17, 2026
8c8eb61
Merge branch 'v4/8-public-json-equals-the-ai-sdk-json' into v4/9-rewr…
claude Oct 17, 2026
50a6fb7
Merge branch 'v4/9-rewrite-json-repair-in-idiomatic-rust' into v4/10-…
claude Oct 17, 2026
4e7afd8
Merge branch 'v4/10-tool-call-repair-is-a-trait' into v4/11-provider-…
claude Oct 17, 2026
a510dda
Merge branch 'v4/11-provider-config-is-plain-data' into v4/12-vendor-…
claude Oct 17, 2026
f4f4746
Merge master into [12/13]: converge to the layer tree
eric8810 Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
longer merged into the first call.
- `SigV4Fetch::with_clock` takes a plain function (`fn() -> DateTime<Utc>`)
instead of a closure.
- Gains `Transformer`, `TransformStreamController` and `pipe_through`, the
Rust form of the WHATWG `TransformStream` the AI SDK models build `doStream`
with (`start` / `transform` / `flush`; `enqueue`, `enqueue_error`, `error`,
`terminate`). `pipe_through` returns `PipeThrough`, a `Stream` that calls
`start` immediately and the transformer's other methods as it is polled;
`TransformStreamController::new`, `drain` and `is_closed` let a test drive
a transformer without a stream.
- `StreamingToolCallTracker`, `StreamingToolCallDelta`, `TypeValidation` and
`TrackerError` are removed. The tool-call matching they ported from
`@ai-sdk/provider-utils` 5.0.55 is now part of the OpenAI-style chat
streams in `aimux-providers` (OpenAI, OpenAI-compatible, DeepSeek, Groq,
Mistral), which emit the same parts as before.

**Rust (aimux-providers)**

Expand Down Expand Up @@ -741,6 +753,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `PresetDescriptor` gains a crate-private field (the row's usage converter),
so code outside the crate can no longer build one with a struct literal;
descriptors come from the registry JSON.
- Every language-model `doStream` (OpenAI chat and Responses, OpenAI-compatible
chat and completion, Groq, DeepSeek, Mistral, Anthropic, Bedrock, Cohere,
Google, Vertex, xAI, Hugging Face, Open Responses) is a named stream struct
driven by `pipe_through`; emitted parts are unchanged, and a stream still
stops reading the body at an Anthropic `message_stop` or a
Google/Vertex/xAI/Hugging Face error event.

**Rust (provider factory, RFC-0036)**

Expand Down
9 changes: 2 additions & 7 deletions aimux-provider-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ pub mod read_response_with_size_limit;
pub mod resolvable;
pub mod response_handler;
pub mod sigv4_fetch;
mod streaming_tool_call_argument_state;
mod streaming_tool_call_tracker;
pub mod transform_stream;
pub mod url;
/// WebSocket client for realtime provider APIs (RFC-0028). Empty unless the
/// `ws` feature is enabled.
Expand Down Expand Up @@ -65,9 +64,5 @@ pub use response_handler::{
create_status_code_error_response_handler, invalid_response_api_call, stream_error_api_call,
};
pub use sigv4_fetch::{AwsCredentials, SigV4Fetch, SigV4Signing};
// As `@ai-sdk/provider-utils` (`index.ts:115-119`): the tracker is exported
// from the crate root, its argument state stays internal.
pub use streaming_tool_call_tracker::{
StreamingToolCallDelta, StreamingToolCallTracker, TrackerError, TypeValidation,
};
pub use transform_stream::{PipeThrough, TransformStreamController, Transformer, pipe_through};
pub use url::{validate_base_url, without_trailing_slash, without_trailing_slash_opt};
279 changes: 279 additions & 0 deletions aimux-provider-utils/src/transform_stream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
//! The Rust form of the WHATWG `TransformStream` that the AI SDK's models
//! build their `doStream` output with (`response.pipeThrough(new
//! TransformStream({ start, transform, flush }))`).
//!
//! The state the TypeScript closures capture lives in a [`Transformer`]
//! value instead, so a model's stream logic is a plain struct with methods
//! that can be driven and tested without any I/O.

use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll, ready};

use futures::Stream;

use aimux_core::error::AiMuxError;

/// What a transformer writes to: the `TransformStreamDefaultController`.
///
/// [`pipe_through`] owns one and drains it after every call into the
/// transformer; tests can own one directly, call the transformer's methods
/// with it and [`drain`](Self::drain) what was emitted.
pub struct TransformStreamController<O> {
queue: VecDeque<Result<O, AiMuxError>>,
closed: bool,
}

impl<O> Default for TransformStreamController<O> {
fn default() -> Self {
Self {
queue: VecDeque::new(),
closed: false,
}
}
}

impl<O> TransformStreamController<O> {
/// An open controller with nothing queued.
#[must_use]
pub fn new() -> Self {
Self::default()
}

/// Take everything emitted so far, in order. The buffer is kept for reuse.
pub fn drain(&mut self) -> impl Iterator<Item = Result<O, AiMuxError>> + '_ {
self.queue.drain(..)
}

/// Whether [`error`](Self::error) or [`terminate`](Self::terminate) ended
/// the stream.
#[must_use]
pub fn is_closed(&self) -> bool {
self.closed
}

/// Emit one value downstream.
pub fn enqueue(&mut self, value: O) {
if !self.closed {
self.queue.push_back(Ok(value));
}
}

/// Emit `error` and keep the stream open: the next chunk is transformed
/// as usual. For errors a consumer may skip (a malformed event inside an
/// otherwise valid stream). The AI SDK's models do this by enqueueing an
/// `{ type: 'error', error }` part and carrying on.
pub fn enqueue_error(&mut self, error: AiMuxError) {
if !self.closed {
self.queue.push_back(Err(error));
}
}

/// Emit `error` and end the stream: no further chunk is transformed and
/// `flush` is not called. WHATWG `controller.error(e)`.
pub fn error(&mut self, error: AiMuxError) {
if !self.closed {
self.queue.push_back(Err(error));
self.closed = true;
}
}

/// End the stream without an error: no further chunk is transformed and
/// `flush` is not called. WHATWG `controller.terminate()`. Anything
/// emitted after the stream ended is dropped.
pub fn terminate(&mut self) {
self.closed = true;
}
}

/// The `{ start, transform, flush }` object of a `TransformStream`.
pub trait Transformer: Send + 'static {
type Input: Send + 'static;
type Output: Send + 'static;

/// Called once, before the first chunk.
fn start(&mut self, controller: &mut TransformStreamController<Self::Output>) {
let _ = controller;
}

/// Called for every input chunk.
fn transform(
&mut self,
chunk: Self::Input,
controller: &mut TransformStreamController<Self::Output>,
);

/// Called once when the input ends, unless the stream was ended earlier
/// with [`TransformStreamController::error`] or
/// [`TransformStreamController::terminate`].
fn flush(self, controller: &mut TransformStreamController<Self::Output>);
}

/// `input.pipeThrough(new TransformStream(transformer))`.
///
/// Like the WHATWG constructor, this calls [`Transformer::start`] right away;
/// every later call happens while the returned stream is polled.
pub fn pipe_through<S, T>(input: S, mut transformer: T) -> PipeThrough<S, T>
where
S: Stream<Item = T::Input> + Send + 'static,
T: Transformer,
{
let mut controller = TransformStreamController::new();
transformer.start(&mut controller);
PipeThrough {
input: Box::pin(input),
transformer: Some(transformer),
controller,
}
}

/// The stream [`pipe_through`] returns: it pulls a chunk from `input` only
/// when everything the transformer emitted so far has been handed out.
pub struct PipeThrough<S, T: Transformer> {
input: Pin<Box<S>>,
/// `None` once `flush` has run.
transformer: Option<T>,
controller: TransformStreamController<T::Output>,
}

// The transformer is never pinned: it is only reached through `&mut`.
impl<S, T: Transformer> Unpin for PipeThrough<S, T> {}

impl<S, T> Stream for PipeThrough<S, T>
where
S: Stream<Item = T::Input>,
T: Transformer,
{
type Item = Result<T::Output, AiMuxError>;

fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(item) = this.controller.queue.pop_front() {
return Poll::Ready(Some(item));
}
if this.controller.closed {
return Poll::Ready(None);
}
let Some(transformer) = this.transformer.as_mut() else {
return Poll::Ready(None);
};
match ready!(this.input.as_mut().poll_next(cx)) {
Some(chunk) => transformer.transform(chunk, &mut this.controller),
None => {
if let Some(transformer) = this.transformer.take() {
transformer.flush(&mut this.controller);
}
}
}
}
}
}

#[cfg(test)]
mod tests {
use futures::StreamExt;

use super::*;

/// Doubles each number, reports a skippable error for zero, stops with an
/// error on a negative one, and emits the running total on flush.
#[derive(Default)]
struct Doubler {
total: i32,
}

impl Transformer for Doubler {
type Input = i32;
type Output = i32;

fn start(&mut self, controller: &mut TransformStreamController<i32>) {
controller.enqueue(0);
}

fn transform(&mut self, chunk: i32, controller: &mut TransformStreamController<i32>) {
if chunk == 0 {
controller.enqueue_error(AiMuxError::InvalidResponseData("zero".into()));
return;
}
if chunk < 0 {
controller.error(AiMuxError::InvalidResponseData("negative".into()));
return;
}
self.total += chunk;
controller.enqueue(chunk * 2);
}

fn flush(self, controller: &mut TransformStreamController<i32>) {
controller.enqueue(self.total);
}
}

async fn run(input: Vec<i32>) -> Vec<Result<i32, String>> {
pipe_through(futures::stream::iter(input), Doubler::default())
.map(|item| item.map_err(|error| error.to_string()))
.collect()
.await
}

#[tokio::test]
async fn start_transform_then_flush_in_order() {
assert_eq!(run(vec![1, 2]).await, vec![Ok(0), Ok(2), Ok(4), Ok(3)]);
}

#[tokio::test]
async fn enqueue_error_keeps_transforming_and_flushes() {
let out = run(vec![1, 0, 2]).await;
assert_eq!(out.len(), 5);
assert_eq!(out[..2], [Ok(0), Ok(2)]);
assert!(out[2].is_err());
assert_eq!(out[3..], [Ok(4), Ok(3)]);
}

#[tokio::test]
async fn error_ends_the_stream_without_flush() {
let out = run(vec![1, -1, 5]).await;
assert_eq!(out.len(), 3);
assert_eq!(out[..2], [Ok(0), Ok(2)]);
assert!(out[2].is_err());
}

#[test]
fn a_transformer_can_be_driven_without_a_stream() {
let mut controller = TransformStreamController::new();
let mut doubler = Doubler::default();
doubler.start(&mut controller);
doubler.transform(3, &mut controller);
let emitted: Vec<i32> = controller.drain().map(Result::unwrap).collect();
assert_eq!(emitted, [0, 6]);
doubler.transform(-1, &mut controller);
assert!(controller.is_closed());
assert!(controller.drain().next().unwrap().is_err());
}

#[test]
fn nothing_is_emitted_after_the_stream_ended() {
let mut controller = TransformStreamController::new();
controller.enqueue(1);
controller.terminate();
controller.enqueue(2);
controller.enqueue_error(AiMuxError::InvalidResponseData("late".into()));
controller.error(AiMuxError::InvalidResponseData("late".into()));
let emitted: Vec<i32> = controller.drain().map(Result::unwrap).collect();
assert_eq!(emitted, [1]);
assert!(controller.is_closed());
}

#[tokio::test]
async fn waits_for_input_that_is_not_ready_yet() {
let (tx, rx) = futures::channel::mpsc::unbounded();
let mut out = pipe_through(rx, Doubler::default());
assert_eq!(out.next().await.unwrap().unwrap(), 0);
tx.unbounded_send(4).unwrap();
assert_eq!(out.next().await.unwrap().unwrap(), 8);
drop(tx);
assert_eq!(out.next().await.unwrap().unwrap(), 4);
assert!(out.next().await.is_none());
assert!(out.next().await.is_none());
}
}
Loading
Loading