fix: migrate mistralai to 2.x and remediate CVE-2025-67221 (orjson) (#17810)

## Summary
  
Migrates `mistralai` from `==0.4.2` to `>=2.7.2,<3.0.0` to unblock the
orjson CVE fix. The old SDK pinned `orjson>=3.9.10,<3.11`, preventing
upgrade to the patched version.
  
  | CVE | Severity | Package | Installed | Fixed in |
  |---|---|---|---|---|
  | CVE-2025-67221 | HIGH | orjson | 3.10.18 | 3.11.6 |

`mistralai` 2.x (the current maintained version) drops the orjson
dependency entirely. Added `orjson>=3.11.6` to `constraint-dependencies`
to pin the floor for remaining parent packages (`langgraph-sdk`,
`langsmith`, `ranx`).
This commit is contained in:
rayhan
2026-08-05 02:52:30 +01:00
committed by GitHub
parent 07d1c89e5e
commit 166758cb0f
5 changed files with 68 additions and 38 deletions

View File

@@ -27,7 +27,6 @@ from urllib.parse import urljoin
import json_repair
from json.decoder import JSONDecodeError
import litellm
import openai
from openai import AsyncOpenAI, OpenAI
from enum import StrEnum
@@ -994,9 +993,9 @@ class MistralChat(Base):
def __init__(self, key, model_name, base_url=None, **kwargs):
super().__init__(key, model_name, base_url=base_url, **kwargs)
from mistralai.client import MistralClient
from mistralai.client import Mistral
self.client = MistralClient(api_key=key)
self.client = Mistral(api_key=key)
self.model_name = model_name
def _clean_conf(self, gen_conf):
@@ -1008,7 +1007,7 @@ class MistralChat(Base):
def _chat(self, history, gen_conf=None, **kwargs):
gen_conf = dict(gen_conf or {})
gen_conf = self._clean_conf(gen_conf)
response = self.client.chat(model=self.model_name, messages=history, **gen_conf)
response = self.client.chat.complete(model=self.model_name, messages=history, **gen_conf)
if not response.choices:
raise ValueError("LLM returned empty response") # pact: guard empty choices list
ans = response.choices[0].message.content
@@ -1020,6 +1019,8 @@ class MistralChat(Base):
return ans, total_token_count_from_response(response)
def chat_streamly(self, system, history, gen_conf=None, **kwargs):
from mistralai.client.errors import MistralError
gen_conf = dict(gen_conf or {})
if system and history and history[0].get("role") != "system":
history.insert(0, {"role": "system", "content": system})
@@ -1027,8 +1028,9 @@ class MistralChat(Base):
ans = ""
total_tokens = 0
try:
response = self.client.chat_stream(model=self.model_name, messages=history, **gen_conf, **kwargs)
for resp in response:
response = self.client.chat.stream(model=self.model_name, messages=history, **gen_conf, **kwargs)
for event in response:
resp = event.data
if not resp.choices or not resp.choices[0].delta.content:
continue
ans = resp.choices[0].delta.content
@@ -1040,7 +1042,7 @@ class MistralChat(Base):
ans += LENGTH_NOTIFICATION_EN
yield ans
except openai.APIError as e:
except MistralError as e:
yield ans + "\n**ERROR**: " + str(e)
yield total_tokens

View File

@@ -611,9 +611,9 @@ class MistralEmbed(Base):
_FACTORY_NAME = "Mistral"
def __init__(self, key, model_name="mistral-embed", base_url=None):
from mistralai.client import MistralClient
from mistralai.client import Mistral
self.client = MistralClient(api_key=key)
self.client = Mistral(api_key=key)
self.model_name = model_name
def encode(self, texts: list):
@@ -628,7 +628,7 @@ class MistralEmbed(Base):
retry_max = 5
while retry_max > 0:
try:
res = self.client.embeddings(input=texts[i : i + batch_size], model=self.model_name)
res = self.client.embeddings.create(inputs=texts[i : i + batch_size], model=self.model_name)
ress.extend([d.embedding for d in res.data])
token_count += total_token_count_from_response(res)
break
@@ -648,7 +648,7 @@ class MistralEmbed(Base):
retry_max = 5
while retry_max > 0:
try:
res = self.client.embeddings(input=[truncate(text, DEFAULT_MAX_TOKENS)], model=self.model_name)
res = self.client.embeddings.create(inputs=[truncate(text, DEFAULT_MAX_TOKENS)], model=self.model_name)
return np.array(res.data[0].embedding), total_token_count_from_response(res)
except Exception as _e:
if retry_max == 1: