Skip to content

feat: add Mistral AI as a first-class provider route - #5295

Merged
Hmbown merged 3 commits into
Hmbown:mainfrom
xavierpestel-ai:codex/add-mistral-provider
Aug 9, 2026
Merged

feat: add Mistral AI as a first-class provider route#5295
Hmbown merged 3 commits into
Hmbown:mainfrom
xavierpestel-ai:codex/add-mistral-provider

Conversation

@xavierpestel-ai

@xavierpestel-ai xavierpestel-ai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Mistral AI (la Plateforme) as a first-class Codewhale provider route, authored by first-time contributor @xavierpestel-ai and retained as his own commit. The route defaults to mistral-code-latest and supports provider = "mistral", CODEWHALE_PROVIDER=mistral, and codewhale --provider mistral.

What ships

  • Provider metadata, aliases (mistral-ai, mistralai, la-plateforme), API-key guidance, config persistence, CLI selection, telemetry, and provider-scoped MISTRAL_API_KEY, MISTRAL_BASE_URL, and MISTRAL_MODEL overrides.
  • Current static model choices: mistral-code-latest, mistral-medium-latest, mistral-small-latest, and mistral-large-latest, with provider-owned aliases and catalog-derived context metadata. Deprecated Magistral IDs remain explicit-configuration compatibility routes rather than current picker recommendations.
  • Adjustable reasoning for Mistral Medium and Small on the documented first-party HTTPS /v1 hosts. Codewhale sends only the live-verified none/high effort values, parses streaming and non-streaming polymorphic thinking blocks, and replays the complete stored assistant thinking trace on later turns.
  • Native Magistral compatibility remains always-reasoning and never receives the adjustable effort field. Code and Large stay non-reasoning.
  • Provider/model capability metadata, the picker/model registry, generated web facts, configuration examples, provider docs, and the Unreleased changelog are aligned.

Maintainer hardening

The second commit keeps Xavier's implementation and authorship intact while closing the review gaps:

  • Mistral's polymorphic request/response dialect is enabled only for exact first-party Mistral HTTPS routes; a custom MISTRAL_BASE_URL keeps generic Chat Completions semantics.
  • Real Message -> PromptBuilder history preserves and reshapes stored thinking before the request is sent.
  • Streaming and non-streaming polymorphic parsing are route-isolated, so another OpenAI-compatible provider cannot have unrelated typed content reinterpreted as Mistral reasoning.
  • Mistral tool-call replay bypasses the DeepSeek placeholder sanitizer, preventing an invalid second reasoning_content dialect from being injected.
  • Shared capability metadata and MISTRAL_MODEL precedence now agree with the wire path.
  • The large stream-decoder test body moved into a test-only module so the repository source-structure budget remains green.

Boundaries

  • Codestral FIM (/v1/fim/completions) is not wired in this PR.
  • Custom Mistral-compatible gateways are not assumed to implement Mistral's proprietary reasoning block shape.
  • No maintainer test printed, copied, or modified a provider credential.

Verification

  • cargo fmt --all -- --check
  • git diff --check
  • cargo check --workspace --all-features --locked
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
  • RUST_MIN_STACK=16777216 cargo test --workspace --all-features --locked — exit 0; TUI library 10,097 passed / 0 failed / 11 ignored, PTY 68 passed / 0 failed / 3 ignored, and all remaining workspace/integration/doc tests green. The first sandboxed attempt could not bind localhost fixtures; the identical command passed when localhost test listeners were allowed.
  • cargo test --locked -p codewhale-tui --lib client::chat -- --nocapture — 100 passed / 0 failed, including prompt replay, tool-call history, non-stream route isolation, and stream-block route isolation.
  • Focused codewhale-config, codewhale-agent, and codewhale-cli Mistral/provider tests.
  • python3 scripts/check-provider-registry.py
  • python3 scripts/check-source-structure-budget.py — 683,066 owned Rust lines <= 685,165.
  • ./scripts/sync-changelog.sh --check
  • web/npm run check:facts

Closes #5304

@xavierpestel-ai
xavierpestel-ai requested a review from Hmbown as a code owner August 8, 2026 13:11
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Thanks @xavierpestel-ai for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

@xavierpestel-ai
xavierpestel-ai force-pushed the codex/add-mistral-provider branch from 9f0595f to 8c83276 Compare August 8, 2026 13:18

@Hmbown Hmbown left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks, Xavier — this is the right overall first-class-provider shape, and I want to preserve your commit/authorship and merge the work through this PR rather than reimplementing it elsewhere. The registry/config/env/docs coverage and the live tool-call smoke are especially useful.

I found three current Mistral-contract mismatches that need correction before we run the full matrix:

  1. Several model IDs are documentation-page slugs rather than API IDs. In crates/agent/src/lib.rs, the current API IDs are mistral-medium-3-5 (not mistral-medium-3.5-26-04) and mistral-small-2603 (not mistral-small-4-0-26-03). The Nemo dated ID is open-mistral-nemo-2407, not mistral-nemo-2407; that model is also already past its documented deprecation date, as is pixtral-large-2411, so I would omit both from a new static "shipped" list unless there is a concrete compatibility reason to keep them. Official cards: https://docs.mistral.ai/models/model-cards/mistral-medium-3-5-26-04, https://docs.mistral.ai/models/model-cards/mistral-small-4-0-26-03, https://docs.mistral.ai/models/model-cards/mistral-nemo-12b-24-07, https://docs.mistral.ai/models/model-cards/pixtral-large-24-11.

  2. The context metadata is reversed. Current Codestral / codestral-latest is 128k, not 256k, while Mistral Medium 3.5, Mistral Small 4, and Mistral Large 3 are 256k, not 128k. Those values directly affect compaction/output budgeting, so this is runtime correctness rather than cosmetic metadata. Official cards: https://docs.mistral.ai/models/model-cards/codestral-25-08 and https://docs.mistral.ai/models/model-cards/mistral-large-3-25-12 (plus the Medium/Small cards above).

  3. Reasoning is silently disabled even though the API supports it. The Chat Completions schema accepts reasoning_effort, and Mistral explicitly documents adjustable reasoning for mistral-medium-3-5 and mistral-small-latest: https://docs.mistral.ai/studio-api/conversations/reasoning. Right now every Mistral model is marked supports_reasoning: false and apply_reasoning_effort no-ops, so a user's selected effort is silently ignored. Please either wire the supported models end-to-end (request plus thinking-chunk streaming/history handling), or narrow the advertised model surface and state the limitation truthfully rather than claiming Mistral Chat Completions does not accept the field.

Please also rebase onto current main; this head is six commits behind, including the final 0.9.5 telemetry/release fixes. The old full-suite caveat in the PR body is no longer current—our exact release/main gates are green—so rerun the current targeted provider checks after the corrections. No need to expose or resend any API key.

Once you push the corrected head, ping me here. I’ll approve the first-contributor Actions run and carry this through review so your authorship stays intact.

@xavierpestel-ai
xavierpestel-ai force-pushed the codex/add-mistral-provider branch 2 times, most recently from 0617711 to 012e28c Compare August 8, 2026 20:00
@xavierpestel-ai

xavierpestel-ai commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Hmbown — all three points landed. Rebased onto 91bca01a9 and pushed a corrected head at 012e28c00. Summary of what changed, with one live-catalog note per topic.

1. Model IDs — updated against /v1/models

I re-queried the authenticated GET https://api.mistral.ai/v1/models catalog rather than reading model-card slugs. The static registry now ships:

  • mistral-code-latest (default; aliases include codestral-latest, codestral, mistral-code for backward compatibility)
  • mistral-medium-latest (aliases include mistral-medium, mistral-medium-3-5)
  • mistral-small-latest (aliases include mistral-small, mistral-small-2603)
  • magistral-small-latest (aliases include magistral, mistral-small-magistral) — included because the live catalog advertises reasoning: true on /v1/models
  • mistral-large-latest (alias mistral-large)

mistral-nemo-2407 and pixtral-large-2411 are no longer shipped as static registry models.

2. Context windows — updated

I rechecked the context metadata against live /v1/models because my first pass had the direction wrong. The current catalog I saw reports 256000 for mistral-code-latest and 262144 for the current Medium / Small / Magistral / Large rows. The 128K windows I've seen elsewhere match the mistral-medium-2505 / mistral-medium-2508 snapshots that are deprecated 2026-08-31. Updated crates/tui/src/models.rs with a comment pinning the source and date.

3. Reasoning — wired end-to-end, model-aware

My earlier "does not accept reasoning_effort" claim is retracted. Wiring lives in crates/tui/src/client/chat.rs:

  • apply_mistral_route_reasoning_controls sits alongside the other route-shapers (apply_modelstudio_..., apply_openai_..., apply_zai_...). Sniffs the model via mistral_model_supports_reasoning (matches mistral-medium*, mistral-small*, magistral*) and only writes reasoning_effort for those. Non-reasoning routes (mistral-code-latest, mistral-large-latest) never see the field.
  • mistral_reasoning_effort_wire_value maps CodeWhale effort tiers to what Mistral accepts. In my live probes on mistral-medium-latest, none and high were accepted while unsupported tiers returned HTTP 400 code 3051 with a Pydantic-style enum error. Mapping: off/disabled/none/false → none; high/xhigh/max/highest/ultracode → high; intermediate tiers → field omitted so the route falls back to Mistral's own default. The conservative mapping avoids sending any tier I didn't verify as accepted.
  • extract_mistral_polymorphic_content handles Mistral's reasoning response shape: content: [{type: thinking, thinking: [{type: text, text: "..."}], closed: bool}, {type: text, text: "..."}]. Streaming delta.content toggles from this array to a plain string once the answer starts. Called from both parse_chat_message (non-streaming) and parse_sse_chunk_with_reasoning_style (streaming); flattens the thinking sub-array into a single ContentBlock::Thinking { thinking: String, .. } so no changes to the shared internal shape were needed.
  • reshape_mistral_messages_for_reasoning_replay reconstructs the polymorphic shape on outgoing assistant messages so the thinking trace survives multi-turn replay. Confirmed live that Mistral accepts the shape back as-is and reasons over the prior thinking + text, per https://docs.mistral.ai/studio-api/conversations/reasoning.

The ApiProvider::Mistral arms in the generic apply_reasoning_effort stay no-op on purpose — Mistral is route-shaped rather than provider-shaped, matching the pattern the ModelStudio / MiniMax / Zai / OpenAI / Moonshot routes already use.

I chose to wire rather than narrow because you offered both paths. If the added surface makes review harder, happy to split reasoning into a follow-up and land the smaller "add provider" first — just tell me.

4. Gates & tests

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --locked (documented allow list) — No issues found
  • python3 scripts/check-provider-registry.py
  • ✅ Added mod mistral_reasoning_tests in crates/tui/src/client/chat.rs — 5 unit tests covering the effort-tier mapping, the model-family gate, route-shaper behavior for reasoning vs non-reasoning routes, the polymorphic content extractor (plain string vs array-of-blocks), and the history-replay reshape. All 5 pass under cargo test -p codewhale-tui --lib client::chat::mistral_reasoning_tests.
  • ⚠️ cargo test --workspace --all-features --locked — 22 failures on my Mac, including examples like worktree::tests::expired_cleanup_* and runtime_api::tests::thread_summary_includes_workspace_branch_metadata, all failing on git commit in isolated tempdirs. Same failure count reproduced on origin/main at 91bca01a9 in my previous run. Given your note that the release gates are green upstream, I'm treating these as local lane/git-shell failures unless they reproduce elsewhere; happy to open a separate issue and to re-run if you want fresh evidence.

Live smoke: codewhale --provider mistral --model mistral-medium-latest exec "17 × 23?" returned a correct reasoning-mode answer; --model mistral-large-latest exec "..." succeeded without HTTP 400 (confirms the model-aware gate).

I chose the wiring path you offered; if this makes review too large, I can split the reasoning wiring into a follow-up and keep this PR to provider registration + model defaults.

@Hmbown Hmbown added the enhancement New feature or request label Aug 8, 2026
@Hmbown Hmbown added this to the v0.9.6 milestone Aug 8, 2026
@Hmbown Hmbown added the rust Pull requests that update rust code label Aug 8, 2026
Wire Mistral AI / la Plateforme into the shared provider registry, TUI
provider enum, provider-scoped config/env overrides, static model
registry, context-window metadata, reasoning wiring, docs, and
examples. The route uses Mistral's OpenAI-compatible Chat Completions
endpoint at https://api.mistral.ai/v1 with 'mistral-code-latest' as
the default model (Codestral coding model, 256K context).

Model IDs verified live against https://api.mistral.ai/v1/models: the
static registry ships 'mistral-code-latest' (accepts 'codestral-latest'
as alias for backward compatibility), 'mistral-medium-latest',
'mistral-small-latest', 'magistral-small-latest', and
'mistral-large-latest'. All models report 262144 (256K) context on
/v1/models except mistral-code-latest at 256000; earlier drafts of
this PR had those windows reversed.

Reasoning is wired end-to-end for the three models that advertise
'reasoning: true' on /v1/models — mistral-medium-latest,
mistral-small-latest, and magistral-small-latest. Codewhale sends
'reasoning_effort' (Mistral currently accepts 'none' or 'high' only;
intermediate tiers return HTTP 400 code 3051), parses the polymorphic
'content: [{type: thinking, thinking: [{type: text, text: ...}],
closed: bool}, {type: text, text: ...}]' shape emitted by reasoning
models, and replays the thinking trace back into multi-turn history
per docs.mistral.ai/capabilities/reasoning. Non-reasoning models
(mistral-code-latest, mistral-large-latest) never receive the field
because Mistral would reject it. FIM (/v1/fim/completions) is not
wired.

Provider aliases: mistral-ai, mistralai, la-plateforme. Env vars:
MISTRAL_API_KEY, MISTRAL_BASE_URL, MISTRAL_MODEL. Auth via API key
from https://console.mistral.ai/api-keys, config, or 'codewhale auth
set'.

Test env-poisoning: EnvGuard captures/removes/restores MISTRAL_* so
tests stay reproducible when a user has these vars exported in their
shell.

Validation:
- cargo fmt --all -- --check
- cargo clippy --workspace --all-targets --all-features --locked (with
  the documented allow list) -- No issues found
- cargo test --workspace --all-features --locked -- 22 pre-existing
  failures in crates/tui git-shell tests (worktree init failing on
  'git commit' in isolated tempdirs), verified identical count on
  origin/main at 91bca01 and unrelated to this change
- python3 scripts/check-provider-registry.py -- passed
- codewhale --provider mistral --model mistral-medium-latest exec
  against api.mistral.ai returned a correct reasoning-mode response
- codewhale --provider mistral --model mistral-large-latest exec
  succeeded without HTTP 400 code 3051 (verifies the model-aware
  reasoning gate)
- TUI smoke previously validated: /status shows mistral +
  mistral-code-latest, /provider lists Mistral, tool call end-to-end

Assisted by Codex CLI for implementation and multiple Oracle review
passes (correctness + convention + Hunter's inline review) that
surfaced the ProviderArg clap enum gap, the ModelRegistry silent
fallthrough to DeepSeek, the Codestral context-window regression, the
EnvGuard env-poisoning flake, and the model-ID / context-window /
reasoning-support mistakes from the initial docs-slug pass now
corrected against the live /v1/models catalog.
@xavierpestel-ai
xavierpestel-ai force-pushed the codex/add-mistral-provider branch from 012e28c to f157d34 Compare August 9, 2026 00:04
CodeWhale Bot added 2 commits August 8, 2026 17:23
Scope Mistral's polymorphic reasoning and replay behavior to exact first-party HTTPS routes, preserve stored thinking across real prompt construction, and keep DeepSeek's sanitizer from injecting a second dialect into tool-call history.

Align the current model registry, provider-scoped model override, generated facts, docs, and focused route-isolation tests. Split the large stream decoder test module so the source-structure gate remains below budget.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Keep the checked source-candidate contract in sync with the generated 41-provider registry so the full web suite validates the new first-class route.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

@Hmbown Hmbown left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Approved on exact head 9bac910. Thank you, Xavier, for taking the initial review seriously and for contributing the first-class Mistral route. Your original authored commit remains intact; the maintainer follow-ups route-isolate first-party reasoning shapes, keep custom gateways generic, cover stored-thinking replay and tool-call history, and align generated provider facts. Cargo check, clippy, the full all-features workspace suite, focused provider tests, the complete web suite/build, and exact-head Linux/macOS/Windows CI are green. This supersedes my earlier changes-requested review.

@Hmbown
Hmbown merged commit f864ca6 into Hmbown:main Aug 9, 2026
20 checks passed
@Hmbown Hmbown mentioned this pull request Aug 9, 2026
15 tasks
pull Bot pushed a commit to TheTechOddBug/DeepSeek-TUI that referenced this pull request Aug 9, 2026
Bump the workspace, every published crate, the npm CLI package and its
codewhaleBinaryVersion, the runtime SDK, and the VS Code extension to 0.9.6 —
the four version sources the release workflow cross-checks before it will
tag.

The changelog entry describes v0.9.6 as what it is: a subtractive release.
The guards that interrupted live work, the per-mode prompt doctrine, and the
deterministic second compaction system are gone; a truncated provider
response can no longer be recorded as a finished answer. Most of these were
found by running v0.9.5 against Terminal-Bench 2.1 beside Pi 0.8.41 on the
same model, effort, endpoint, and task digests, then reading the trials
Codewhale lost — so the entry names those trials rather than describing the
fixes in the abstract.

Mistral AI ships in this release; Xavier Pestel (@xavierpestel-ai) is
credited in the contributors section for Hmbown#5295.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.9.6: add Mistral as a first-class provider route

2 participants