Skip to content

feat(ai-mistral): support document (PDF) input via document_url#912

Open
jan-kubica wants to merge 1 commit into
TanStack:mainfrom
jan-kubica:feat/ai-mistral-document-input
Open

feat(ai-mistral): support document (PDF) input via document_url#912
jan-kubica wants to merge 1 commit into
TanStack:mainfrom
jan-kubica:feat/ai-mistral-document-input

Conversation

@jan-kubica

@jan-kubica jan-kubica commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

The Mistral text adapter is half-wired for document (PDF) input: messageToWire already snake-cases a document_url part, but convertContentPartToMistral never produces one — it throws "Supported types: text, image" for any document content part — and model-meta declares no document input modality. As a result, document content parts can't be sent to Mistral even though the API supports them and the other provider adapters (Anthropic, Gemini, Bedrock, OpenAI) already expose PDF input.

This PR closes that gap.

Changes

  • adapters/text.ts: add a document branch to convertContentPartToMistral, mirroring the existing image branch — hosted URLs pass through, inline data bytes are wrapped in a data:<mime>;base64,… URL — returning { type: 'document_url', documentUrl } (which messageToWire already maps to the wire document_url). Updated the fallback error message accordingly.
  • model-meta.ts: add 'document' to the supports.input modality union and to the vision-capable models that already declare image.
  • tests/document-input.test.ts: unit tests for the new mapping (hosted URL, inline base64, already-formatted data: URL).

Verification

Checked against Mistral's own API (not just docs):

  • GET /v1/models reports vision: true for the affected models.
  • document_url round-trips a PDF end-to-end on mistral-small-latest and mistral-medium-latest, for both a hosted URL and an inline base64 data: URL — the model returns the document's text.
  • nx build ai-mistral (typecheck + dts) passes; new unit tests pass; eslint clean on the changed files.

Notes

  • document is added to the four models that already declare image, since Mistral routes both through the same vision pipeline; mistral-small-latest and mistral-medium-latest were verified live. Happy to narrow or expand the set (e.g. Mistral's API now also reports vision: true for mistral-large-latest, magistral-*, and ministral-*) if maintainers prefer.
  • Scope is limited to the document input path; no changes to the OCR endpoint or other modalities.

Summary by CodeRabbit

  • New Features

    • Added support for document/PDF inputs in Mistral vision-capable models.
    • Document content can now be sent as a hosted URL or inline data, matching existing media handling.
  • Bug Fixes

    • Improved input validation so document parts are no longer rejected for supported models.
    • Updated supported input types for affected Mistral models to include documents.

The Mistral text adapter already snake-cased a `document_url` part in
messageToWire, but convertContentPartToMistral never produced one and
threw for any `document` content part, and model-meta declared no
`document` input modality. Add the document branch (mirroring the image
path: hosted URLs pass through, inline bytes become a data: URL) and
declare `document` input for the vision-capable models, matching how
the other provider adapters expose PDF input.

Verified against Mistral's API: /v1/models reports vision=true for these
models, and document_url round-trips a PDF on mistral-small-latest and
mistral-medium-latest (both hosted-URL and inline base64).
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds document (PDF) input support to the Mistral adapter: content parts of type document are converted to document_url payloads with data: URL normalization for inline bytes. Model metadata for four vision-capable models is updated to declare document as a supported input modality. Includes tests and a changeset.

Changes

Document input support

Layer / File(s) Summary
Document content-part conversion
packages/ai-mistral/src/adapters/text.ts
Adds handling for document content parts, converting them to document_url payloads (with base64 data: URL normalization), and updates the unsupported-content error message to mention document and vision-model guidance.
Model metadata modality updates
packages/ai-mistral/src/model-meta.ts
Widens the ModelMeta input schema to allow 'document' and updates supports.input for mistral-medium-latest, mistral-small-latest, pixtral-large-latest, and pixtral-12b-2409 to include 'document'.
Tests and changeset
packages/ai-mistral/tests/document-input.test.ts, .changeset/mistral-document-input.md
Adds tests verifying document part conversion for hosted URLs, base64 data, and pre-formatted data URLs, plus a changeset documenting the minor version bump.

Estimated code review effort: 2 (Simple) | ~10 minutes

Related PRs: None identified.

Suggested labels: ai-mistral, enhancement

Suggested reviewers: None identified.


🐰 A PDF slips in through the door,
Wrapped in data: like never before,
Pixtral peeks, Mistral nods "oh yes",
Document parts now pass the test,
Hop along — one more modality to adore!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: adding document/PDF input support for Mistral via document_url.
Description check ✅ Passed The description is thorough, but it omits the template's Checklist and Release Impact sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ai-mistral/src/adapters/text.ts (1)

1031-1033: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant type assertion flagged by ESLint.

part is already typed as ContentPart in the function signature, so (part as ContentPart).type doesn't change anything and just adds noise. part.type works directly.

🧹 Proposed fix
-    throw new Error(
-      `Mistral text adapter does not support content part of type '${(part as ContentPart).type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest or mistral-small-latest) for images and documents.`,
-    )
+    throw new Error(
+      `Mistral text adapter does not support content part of type '${part.type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest or mistral-small-latest) for images and documents.`,
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mistral/src/adapters/text.ts` around lines 1031 - 1033, Remove
the unnecessary type assertion in the Mistral text adapter error path by using
the existing ContentPart-typed variable directly in the thrown error message. In
the text adapter logic that builds the unsupported-part error, update the
reference from the casted form to the plain part.type access so ESLint no longer
flags redundant assertions.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-mistral/src/adapters/text.ts`:
- Line 1032: The unsupported content error in the Mistral text adapter is too
narrow and points users only to mistral-medium-latest and mistral-small-latest.
Update the message in the text adapter logic to reflect all vision-capable
models that support images/documents, including pixtral-large-latest and
pixtral-12b-2409 as defined in model-meta.ts. Keep the wording aligned with the
actual model capability list so the error guidance matches the supported models.

---

Nitpick comments:
In `@packages/ai-mistral/src/adapters/text.ts`:
- Around line 1031-1033: Remove the unnecessary type assertion in the Mistral
text adapter error path by using the existing ContentPart-typed variable
directly in the thrown error message. In the text adapter logic that builds the
unsupported-part error, update the reference from the casted form to the plain
part.type access so ESLint no longer flags redundant assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27f03394-7c56-455d-97fb-edaf29ac9337

📥 Commits

Reviewing files that changed from the base of the PR and between e3de949 and e45dd3c.

📒 Files selected for processing (4)
  • .changeset/mistral-document-input.md
  • packages/ai-mistral/src/adapters/text.ts
  • packages/ai-mistral/src/model-meta.ts
  • packages/ai-mistral/tests/document-input.test.ts


throw new Error(
`Mistral text adapter does not support content part of type '${(part as ContentPart).type}'. Supported types: text, image. Use a vision-capable model (pixtral-large-latest, pixtral-12b-2409, mistral-medium-latest, or mistral-small-latest) for images.`,
`Mistral text adapter does not support content part of type '${(part as ContentPart).type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest or mistral-small-latest) for images and documents.`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Error message understates which models actually support images/documents.

The message only names mistral-medium-latest and mistral-small-latest, but model-meta.ts shows pixtral-large-latest and pixtral-12b-2409 also declare vision in their features and now include document in supports.input. Users on Pixtral models hitting this error would be pointed to the wrong models.

📝 Proposed fix
-      `Mistral text adapter does not support content part of type '${(part as ContentPart).type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest or mistral-small-latest) for images and documents.`,
+      `Mistral text adapter does not support content part of type '${part.type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest, mistral-small-latest, pixtral-large-latest, or pixtral-12b-2409) for images and documents.`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`Mistral text adapter does not support content part of type '${(part as ContentPart).type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest or mistral-small-latest) for images and documents.`,
`Mistral text adapter does not support content part of type '${part.type}'. Supported types: text, image, document. Use a vision-capable model (mistral-medium-latest, mistral-small-latest, pixtral-large-latest, or pixtral-12b-2409) for images and documents.`,
🧰 Tools
🪛 ESLint

[error] 1032-1032: This assertion is unnecessary since it does not change the type of the expression.

(@typescript-eslint/no-unnecessary-type-assertion)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mistral/src/adapters/text.ts` at line 1032, The unsupported
content error in the Mistral text adapter is too narrow and points users only to
mistral-medium-latest and mistral-small-latest. Update the message in the text
adapter logic to reflect all vision-capable models that support
images/documents, including pixtral-large-latest and pixtral-12b-2409 as defined
in model-meta.ts. Keep the wording aligned with the actual model capability list
so the error guidance matches the supported models.

@nx-cloud

nx-cloud Bot commented Jul 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit e45dd3c

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-10 08:38:20 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@912

@tanstack/ai-acp

npm i https://pkg.pr.new/@tanstack/ai-acp@912

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@912

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@912

@tanstack/ai-bedrock

npm i https://pkg.pr.new/@tanstack/ai-bedrock@912

@tanstack/ai-claude-code

npm i https://pkg.pr.new/@tanstack/ai-claude-code@912

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@912

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@912

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@912

@tanstack/ai-codex

npm i https://pkg.pr.new/@tanstack/ai-codex@912

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@912

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@912

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@912

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@912

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@912

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@912

@tanstack/ai-grok-build

npm i https://pkg.pr.new/@tanstack/ai-grok-build@912

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@912

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@912

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@912

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@912

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@912

@tanstack/ai-mistral

npm i https://pkg.pr.new/@tanstack/ai-mistral@912

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@912

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@912

@tanstack/ai-opencode

npm i https://pkg.pr.new/@tanstack/ai-opencode@912

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@912

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@912

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@912

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@912

@tanstack/ai-sandbox

npm i https://pkg.pr.new/@tanstack/ai-sandbox@912

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-sandbox-cloudflare@912

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/@tanstack/ai-sandbox-daytona@912

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/@tanstack/ai-sandbox-docker@912

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/@tanstack/ai-sandbox-local-process@912

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/@tanstack/ai-sandbox-sprites@912

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-vercel@912

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@912

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@912

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@912

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@912

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@912

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@912

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@912

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@912

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@912

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@912

commit: e45dd3c

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.

2 participants