Skip to content

chore(deps): migrate to mistralai 2.x - #26

Merged
r-uben merged 1 commit into
mainfrom
chore/24-mistralai-2x
Aug 13, 2026
Merged

chore(deps): migrate to mistralai 2.x#26
r-uben merged 1 commit into
mainfrom
chore/24-mistralai-2x

Conversation

@r-uben

@r-uben r-uben commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Closes #24.

Summary

Bumps mistralai to 2.x and fixes the one import that moved. Two lines. This
makes the OCR 4 parameters reachable; it does not use them — that is #23.

-    "mistralai>=1.0.0,<2.0.0",
+    "mistralai>=2.9,<3.0",

-from mistralai import Mistral
+from mistralai.client import Mistral

Correction to #24: three of the four stated risks are not real

I wrote #24 before probing the 2.x internals. Having done so, the risk section
there is overstated and this PR is far more mechanical than advertised.

Retry classification does not change. MistralError is shape-identical in
1.12.4 and 2.9.3 — both set status_code on the instance in __init__ — so
_is_retryable reads the same value and returns the same verdict. Ran the exact
function body against a real SDKError on both SDKs:

status 1.12.4 2.9.3
429 / 500 / 503 retry retry
401 / 400 / 422 fail fast fail fast

Worth recording separately: two limbs of _is_retryable
(processor.py:259-268) are dead on both versions. No RateLimitError /
InternalServerError / ServiceUnavailableError class exists in either SDK, so
the name-matching loop never fires; and neither SDKError has a .response
attribute (it is .raw_response), so that probe always yields None. The
status gate works only via the getattr(error, "status_code", None) fallback.
Pre-existing, not introduced here, and deliberately left alone to keep this
diff at two lines — filed as a follow-up rather than folded in.

OCRPageObject is not frozen in 2.x, so the chunked-PDF page.index
reassignment (processor.py:386) still works. files.upload(file=, purpose=), files.delete(file_id=) and Mistral(api_key=) are unchanged.

Test plan — observed results

  • uv run pytest141 passed on 2.9.3. No test file modified.
  • uv run ruff check . → All checks passed. ruff format --check . → 18 files
    already formatted.
  • uv run mypy mistral_ocr/ --ignore-missing-imports → 18 errors vs 17 on main.
    The one extra is another line of the same pre-existing complaint at
    processor.py:281 (untyped **ocr_kwargs splat); 2.x's signature has one more
    parameter, so the splat is reported once more. Same root cause, no new defect
    class — and it is incidentally proof the new params are present. CI has
    continue-on-error: true on this step.

End-to-end parity, real API, same 2-page PDF on both SDKs:

  • parity.md — byte-identical (cmp clean)
  • file tree — identical
  • metadata.json — identical excluding timestamps, including the
    fingerprint
    : fp:e1ece34158a0d63d4464880bc4bcc517184904c858d19c70dab0735df2caa39b

That satisfies #24's done-when, including the requirement that resume state
survive the upgrade untouched.

Scope / non-goals

No new flags, no blocks/confidence, no _run_fingerprint change, no default
model change. #23 remains blocked on nothing but itself after this merges.


Summary by cubic

Migrates mistralai to 2.x to expose OCR v4 parameters for later use, with no runtime behavior change. Old: mistralai 1.x with from mistralai import Mistral; new: mistralai>=2.9,<3.0 with from mistralai.client import Mistral.

Written for commit 3e359bf. Summary will update on new commits.

Review in cubic

The 1.x SDK cannot express the OCR 4 payload: ocr.process has no
include_blocks or confidence_scores_granularity, and OCRPageObject carries
no blocks/confidence_scores. Both exist in 2.x. This makes them reachable;
it does not use them (see #23).

The migration is the import root plus the pin. mistralai 2.x is a namespace
package, so 'from mistralai import Mistral' becomes
'from mistralai.client import Mistral'.

Verified non-issues, contrary to the risks listed in #24:

- Retry classification is unchanged. MistralError is shape-identical across
  1.12.4 and 2.9.3 (status_code set on the instance in __init__), so
  _is_retryable gates on the same value and returns the same verdict for
  429/500/503 (retry) and 401/400/422 (fail fast) on both.
- OCRPageObject is not frozen in 2.x, so the chunked-PDF page.index
  reassignment still works.
- files.upload(file=, purpose=) / files.delete(file_id=) and
  Mistral(api_key=) are unchanged.

Closes #24
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@r-uben, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f11fa352-5004-4195-9f04-0e98b02fa0c2

📥 Commits

Reviewing files that changed from the base of the PR and between 45bc3f6 and 3e359bf.

📒 Files selected for processing (2)
  • mistral_ocr/processor.py
  • pyproject.toml

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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Migrate to mistralai 2.x (pin + import path update)

⚙️ Configuration changes ✨ Enhancement 🕐 Less than 10 minutes

Grey Divider

AI Description

• Bump mistralai dependency to >=2.9,<3.0 to enable OCR v4 payload support.
• Update Mistral import to the new 2.x namespace package location.
• Keep runtime behavior unchanged; this is a mechanical compatibility migration.
Diagram

graph TD
  A["pyproject.toml"] --> B{{"mistralai 2.x SDK"}} --> C["mistral_ocr/processor.py"] --> D{{"Mistral API"}}
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Stay on mistralai 1.x and hand-roll OCR v4 HTTP calls
  • ➕ Avoids SDK upgrade risks and churn
  • ➕ Keeps existing import surface stable
  • ➖ Duplicates auth/retry/error handling already in the SDK
  • ➖ Harder to maintain and keep parity with upstream API changes
2. Introduce a thin internal client adapter interface
  • ➕ Isolates SDK-specific imports/behavior behind one module
  • ➕ Reduces future migration cost if SDK changes again
  • ➖ More code/abstraction than warranted for a two-line migration
  • ➖ Requires designing and maintaining an interface now

Recommendation: Proceed with the current approach: upgrade to mistralai 2.x and fix the import path. It’s the lowest-effort path that unlocks OCR v4-reachable parameters while preserving existing behavior; heavier alternatives (custom HTTP or an adapter layer) add maintenance cost disproportionate to the current change.

Files changed (2) +2 / -2

Other (2) +2 / -2
processor.pyUpdate Mistral import for mistralai 2.x namespace layout +1/-1

Update Mistral import for mistralai 2.x namespace layout

• Switches 'Mistral' import from 'mistralai' root to 'mistralai.client' to match the 2.x package structure. This is a compatibility change aligned with the dependency bump.

mistral_ocr/processor.py

pyproject.tomlBump mistralai dependency constraint to 2.9.x +1/-1

Bump mistralai dependency constraint to 2.9.x

• Updates the pinned dependency range from mistralai 1.x to '>=2.9,<3.0'. This makes OCR v4-related SDK parameters/types available for follow-up work.

pyproject.toml

@r-uben
r-uben merged commit bb4b8b0 into main Aug 13, 2026
6 checks passed
@r-uben
r-uben deleted the chore/24-mistralai-2x branch August 13, 2026 23:02
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Deprecated mistralai.client import path 🐞 Bug ≡ Correctness
Description
The PR imports Mistral from mistralai.client, a submodule path that was the pre-2.x internal
layout; in mistralai 2.x the officially documented and stable import is `from mistralai import
Mistral (top-level export), and importing from the internal client` submodule is fragile and may
break in a future minor/patch release without notice since it is not part of the public API surface.
This contradicts the PR description itself, which states the fix should move to `from mistralai
import Mistral`, but the actual diff does the opposite (moves away from the top-level import into
the submodule).
Code

mistral_ocr/processor.py[49]

+from mistralai.client import Mistral
Evidence
The PR description explicitly states the intended change is from mistralai import Mistral
(top-level), but the actual code diff applies the reverse change, importing from mistralai.client
instead. This mismatch between stated intent and actual code is a strong signal of an
inverted/incorrect diff that will import from an internal, undocumented module path in the 2.x
package layout.

mistral_ocr/processor.py[49-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The diff changes the import statement to `from mistralai.client import Mistral`, but the PR description states the intended fix is the opposite: `from mistralai import Mistral` (top-level import). Importing from the internal `mistralai.client` submodule relies on undocumented internal structure of the 2.x SDK and can break in future point releases.

## Issue Context
The PR's own description block shows the diff as `-from mistralai import Mistral` / `+from mistralai.client import Mistral`, but describes wanting to move to the top-level import. The correct migration for mistralai 2.x is to import `Mistral` from the package root, matching how `Mistral(api_key=...)` is documented in mistralai 2.x usage examples.

## Fix Focus Areas
- mistral_ocr/processor.py[49-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
Review mode: 🚀 Fast: This is a small, localized dependency/import migration with no substantive logic changes, and the stated compatibility and test evidence substantially contain the risk.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread mistral_ocr/processor.py
from typing import Any

from mistralai import Mistral
from mistralai.client import Mistral

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Deprecated mistralai.client import path 🐞 Bug ≡ Correctness

The PR imports Mistral from mistralai.client, a submodule path that was the pre-2.x internal
layout; in mistralai 2.x the officially documented and stable import is `from mistralai import
Mistral (top-level export), and importing from the internal client` submodule is fragile and may
break in a future minor/patch release without notice since it is not part of the public API surface.
This contradicts the PR description itself, which states the fix should move to `from mistralai
import Mistral`, but the actual diff does the opposite (moves away from the top-level import into
the submodule).
Agent Prompt
## Issue description
The diff changes the import statement to `from mistralai.client import Mistral`, but the PR description states the intended fix is the opposite: `from mistralai import Mistral` (top-level import). Importing from the internal `mistralai.client` submodule relies on undocumented internal structure of the 2.x SDK and can break in future point releases.

## Issue Context
The PR's own description block shows the diff as `-from mistralai import Mistral` / `+from mistralai.client import Mistral`, but describes wanting to move to the top-level import. The correct migration for mistralai 2.x is to import `Mistral` from the package root, matching how `Mistral(api_key=...)` is documented in mistralai 2.x usage examples.

## Fix Focus Areas
- mistral_ocr/processor.py[49-49]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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.

chore(deps): migrate to mistralai 2.x (prerequisite for #23)

1 participant