Skip to content

refactor(core)!: model the provider prompt by role, with one file part - #218

Draft
cunninghamcard-bit wants to merge 4 commits into
rfc-0036/provider-options-typesfrom
rfc-0036/provider-prompt-types
Draft

cunninghamcard-bit wants to merge 4 commits into
rfc-0036/provider-options-typesfrom
rfc-0036/provider-prompt-types

Conversation

@cunninghamcard-bit

Copy link
Copy Markdown
Contributor

Reading the diff: 43,300 of the 45,865 deleted lines are one commit, 37523b89, which deletes 34 hand-written mock-server test files. The type change itself is the other commits: 61 files, +2544 / −2565. Review by commit.

What

The prompt a provider receives is modelled by role, with one file part, as upstream's LanguageModelV4Message. Fourth of the protocol-type pull requests; it sits on the provider-options-type one and comes before the provider-factory PR (#214), because every vendor converter reads this type.

Before and after

Concept Before After
Message LanguageModelPromptMessage { role, content: Vec<ContentPart>, provider_options } for every role LanguageModelMessage, an enum by role: System { content: String }, User { content: Vec<UserPart> }, Assistant { content: Vec<AssistantPart> }, Tool { content: Vec<ToolPart> }
Parts the user-facing ContentPart union, any part under any role TextPart, FilePart, ReasoningPart, ToolCallPart, ToolResultPart, each defined once; each role lists the parts it allows
File input five variants reach the provider: Image, File, FileBase64, FileUrl, FileReference one FilePart { data: FileData, media_type, filename }
Conversion strings wrapped, parts cloned convert_to_language_model_prompt folds the five file variants into FilePart and returns InvalidPrompt for a part its role does not allow or a system message that is not plain text

The user-facing ModelMessage, ContentPart, Role and their JSON are unchanged.

Vendor code

Each converter matches on the message variant and the per-role part. Its five file arms become one arm that matches FilePart.data. Arms for role and part combinations that the types rule out are deleted. The request JSON for a given prompt is unchanged.

Tests

34 hand-written mock-server test files are deleted in the first commit. They build provider prompts from the removed struct and assert Rust's own earlier behaviour; the same files are deleted on the provider-factory branch. Recorded-cassette tests, upstream-sample tests and the remaining suites are converted and pass.

Not in this pull request

  • ToolResultPart keeps result and is_error; upstream has a structured output union.
  • ReasoningPart.signature and ToolCallPart.thought_signature stay as fields; upstream carries them in provider options.
  • Upstream's reasoning-file, custom and tool-approval-response parts are not added. Nothing produces them in a single-step runtime.
  • The user-facing message types are a later pull request, after feat(providers)!: AI SDK-shaped provider factories (RFC-0036 Part I, Rust) #214.

Wire format

Recordings store the provider prompt. Its JSON changes in two places: a system message's content is a string, and a file part is {"type":"file","data":…} for all five former variants. Cassettes and contract fixtures are unchanged. TypeScript: LanguageModelPromptMessage is replaced by LanguageModelMessage and the part types.

Verification

cargo fmt, cargo clippy --workspace --all-targets -D warnings, cargo doc, gen_ts_types.py --check; tests of core, provider-utils, providers, ffi, web, replay, cli: 2612 passed, 0 failed; Node and Python bindings cargo check. Without the deleted test files: 29 files, +1681 / −1859. With them and the changelog entry: 95 files, +2544 / −45865.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS

chenhaonan and others added 4 commits October 5, 2026 11:51
…vider prompts by hand

What: deletes 34 test files under aimux-providers/tests. Each one builds
provider prompts from the old message struct and asserts request JSON or
parsed output that a hand-written mock server returns.

Why: the next commit replaces the provider prompt type. These files pin
Rust's own earlier behaviour rather than a recording or an upstream sample,
so they are removed instead of being rewritten. The same files are removed
on the provider factory branch. Recorded-cassette tests, upstream-sample
tests and the remaining suites stay and are converted.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
What: `LanguageModelPrompt` is now `Vec<LanguageModelMessage>`, an enum by
role: a system message holds a string; user, assistant and tool messages
hold their own part unions (`UserPart`, `AssistantPart`, `ToolPart`). The
part structs (`TextPart`, `FilePart`, `ReasoningPart`, `ToolCallPart`,
`ToolResultPart`) are each defined once. A provider sees exactly one file
part, whose `data` is the existing `FileData` union.
`convert_to_language_model_prompt` is the single conversion from user
messages: it folds the five user-facing file variants into `FilePart` and
rejects a part its role does not allow, or a system message that is not
plain text, with `InvalidPrompt`. `LanguageModelPromptMessage` is removed.

Why: this is the shape of upstream's `LanguageModelV4Message`. Before, every
provider received the user-facing part union for every role, so each
converter repeated five file arms and handled role and part combinations
that cannot occur. The user-facing `ModelMessage` and `ContentPart` and
their JSON are unchanged; the tool result fields and the two signature
fields keep their current form and are listed as remaining differences.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
…and tests

What: the vendor converters (OpenAI chat and Responses, Open Responses,
Anthropic, Bedrock, Google, Cohere, Mistral, xAI, Hugging Face), the replay
helpers, the web and replay tools and the remaining tests match on
`LanguageModelMessage` and the per-role parts. The five file arms in each
converter become one arm that matches `FilePart.data`; arms for role and
part combinations the types rule out are deleted. TypeScript types are
regenerated.

Why: follow-up to the core change. Request JSON for a given prompt is
unchanged; the recorded-cassette and upstream-sample tests pass without any
change to cassettes or fixtures.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS
The changelog is what binding authors read before upgrading; the prompt type
change alters the provider trait input and the recorded prompt JSON.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WGwanvo9WLU9HWD7sRR8TS

This branch has not been deployed

No deployments
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.

1 participant