Repository navigation
refactor(providers)!: data-only provider configuration and stream structs - #227
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
…a, not closures
The compatible chat config holds data where the AI SDK passes functions:
- `ConvertUsage` is an enum (OpenAICompatible / Alibaba / MoonshotAI); the
Alibaba and Moonshot AI rows select it through `profile.convert_usage`
in the registry instead of a name match in preset.rs.
- `supported_urls` is a `SupportedUrls` value.
- `ChatDialect` is folded into `ChatSettings`; the error structure, which
only ever had the default value, is no longer a field.
- `MetadataExtractor` / `StreamMetadataExtractor` are removed: nothing
implemented them.
- Provider headers are a `ProviderHeaders` value resolved per request, and
a preset's lazy base URL is `BaseUrl::Preset { descriptor, params }`.
Behaviour is unchanged: provider-utils and providers tests pass as before.
Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee
Co-authored-by: Amp <amp@ampcode.com>
`generate_id` settings (anthropic, google, cohere, mistral) and the tool-call tracker take `IdGenerator = fn() -> String` (the AI SDK's `IdGenerator`) instead of boxed closures: no captured state, usable in const configs. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
Provider configuration is data; the request-body rewrite closure is gone from every provider's settings, PresetSettings and the internal configs. The OpenAI-compatible provider still passes unknown providerOptions fields of its namespace through to the body. Callers that did not set a transform see identical requests. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…ock are plain data
- anthropic config: drop UrlFn/RequestUrlFn/BodyFn/ResolveFn. url() is
base_url + path; MessagesUrl { Messages, RawPredict } selects the
messages URL; prepare_body is a fn pointer; AnthropicEndpoint
{ Fixed, PerRequest(Arc<dyn ResolveEndpoint>) } replaces the late-binding
closure (Vertex implements ResolveEndpoint over its Resolver).
- openai config: OpenAIUrl { Base, AzureDeployment, AzureSpeech } replaces
UrlFn; azure UrlRules is pub(crate) and gains speech_url().
- SigV4Fetch clock is fn() -> DateTime<Utc>.
Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee
Co-authored-by: Amp <amp@ampcode.com>
EndpointConfig no longer holds EndpointFn / SupportedUrlsFn closures.
Where a request's base URL and headers come from is now described by
EndpointSource (Http { BaseUrl, EndpointHeaders } | Vertex | Bedrock |
AwsPolly), resolved by EndpointConfig::endpoint() per request; the URL
patterns a model fetches itself are SupportedUrlsSource (None | Fixed |
Google { base_url }).
- BaseUrl: Fixed | Env { var, description } (SearXNG instance URL).
- EndpointHeaders: Provider(ProviderHeaders) | Basic { login, password,
user } (DataForSEO) | WithUserHeaders { provider, user } (Open
Responses' resolvable user headers).
- Providers store ProviderHeaders instead of a HeadersFn; user-agent
suffixes use ProviderHeaders::with_user_agent.
- vertex: GeminiEndpoint and ProjectLocationSource replace the endpoint
and ProjectLocationFn closures.
- bedrock: BedrockEndpoint replaces the header/endpoint closures; the
reranking model reads the region from the shared Auth (RegionFn gone).
- aws_polly: PollyEndpoint replaces signing_headers and the dynamic
endpoint closure.
URLs, headers, error types and when the environment is read are
unchanged.
…rs data OpenAIModelConfig and AnthropicModelConfig take the ProviderHeaders data struct instead of a HeadersFn closure; the credential_headers / provider_headers / into_headers_fn closure builders are gone. Vertex Anthropic resolves its per-request endpoint through AnthropicEndpoint::Vertex holding the Vertex resolver, so the ResolveEndpoint trait object is gone. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
The Rust form of the WHATWG TransformStream the AI SDK models build doStream with: a Transformer value holds the state the TypeScript closures capture, with start / transform / flush methods writing to a controller (enqueue, error, terminate), and pipe_through drives it over an input stream. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…ucts GroqChatStream and MistralChatStream hold the doStream state and are driven by pipe_through. DeepSeek keeps its generator: it yields a recoverable Err (then continues) that the controller cannot express yet. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…nsformer structs OpenAICompatibleChatStream and OpenAIChatStream hold the doStream state that lived in async_stream locals and are driven by pipe_through. The emitted part sequence is unchanged. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…ippable errors Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…streams as Transformer structs Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
…rmer structs Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
Convert the async_stream generators of the OpenAI, xAI, Hugging Face and Open Responses doStream paths into OpenAIResponsesStream, XaiResponsesStream, HuggingFaceResponsesStream and OpenResponsesStream driven by pipe_through. Emitted part sequences are unchanged. Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a11e83-bf7b-75bb-900b-34851e6f12ee Co-authored-by: Amp <amp@ampcode.com>
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 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.
Summary
Stacked on #225 (
rfc-0036/wire-format). Public behaviour and AI SDK naming are unchanged; the internals become idiomatic Rust, following the shaperiguses: provider configuration is plain data, and every language-modeldoStreamis a named stream struct.Configuration is data, not closures
transform_request_bodysetting of every provider and ofPresetSettings(vendor body fields go through the provider's providerOptions namespace), andOpenAICompatibleProviderSettings::metadata_extractorwith theMetadataExtractor/StreamMetadataExtractortraits (nothing implemented them).ConvertUsageis an enum (OpenAICompatible,Alibaba,MoonshotAI); registry rows select it withprofile.convert_usageinstead of a vendor-name match inpreset.rs.supported_urlsis aSupportedUrlsvalue.ChatDialectis folded intoChatSettings.EndpointSource,EndpointHeaders,SupportedUrlsSource,ProviderHeaders,OpenAIUrl,MessagesUrl,AnthropicEndpoint); Vertex Anthropic holds the Vertex resolver instead of a chain of boxed closures.generate_idsettings and the tool-call tracker takeIdGenerator = fn() -> String;SigV4Fetch::with_clocktakes afnpointer.Resolvable, e.g. rotating STS / Azure AD tokens) and thefetchtransport.Streams as structs
aimux-provider-utilsgainsTransformer/TransformStreamController/pipe_through, the Rust form of the WHATWGTransformStreamthe AI SDK models builddoStreamwith (start/transform/flush;enqueue,enqueue_error,error,terminate).message_stopor a Google / Vertex / xAI / Hugging Face error event the rest of the body is now read and ignored beforeFinish(as the AI SDK'sflushdoes) instead of dropping the connection early.Not done, deliberately
match.is_deepseek_v4_modeland the Groq browser-search model list stay as upstream's prefix / list rules rather than moving to the model catalogue, which would change results for future model ids.Checks
On head
a1e11487:cargo fmt --all --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace— 153 test binaries, 0 failed (same count as refactor(core)!: public JSON equals the AI SDK JSON, one provider construction path #225)scripts/gen_providers_doc.py --check,scripts/check_provider_boundaries.shCHANGELOG,
docs/api/rust.md,docs/provider-config-manual.mdanddocs/contributing/adding-a-provider.mdare updated.