fix(stt,tts): restore mistralai — 2.4.8 is clean, ban lifted (#34841)

* docs(code-execution): document HERMES_* env narrowing + passthrough workaround

The execute_code sandbox-child env scrub (108397726, #27303) deliberately
dropped the broad HERMES_ prefix passthrough, keeping only an operational
4-var allowlist (HERMES_HOME/PROFILE/CONFIG/ENV). A script that relied on a
non-secret HERMES_* var (HERMES_BASE_URL, HERMES_KANBAN_DB, HERMES_*_WEBHOOK,
or a plugin-defined one) now sees it unset in the child.

Document the behavior change and the two recovery routes (terminal.env_passthrough
in config.yaml, or required_environment_variables in skill frontmatter), plus
the debug log line that surfaces the drop for diagnosis.

* fix(stt,tts): restore mistralai — 2.4.8 is clean, ban lifted

PyPI quarantined mistralai on 2026-05-12 after the malicious 2.4.6
release (Mini Shai-Hulud worm). 2.4.6 has since been removed from the
registry and clean releases resumed (2.4.7 2026-05-25, 2.4.8 2026-05-28).
This rolls back the blanket runtime ban so Voxtral STT + TTS work again,
following the restoration checklist the repo left in pyproject.toml.

Verified against the real SDK: 2.4.8 keeps the import path the code uses
(from mistralai.client import Mistral) and the audio.transcriptions.complete
/ audio.speech.complete surfaces.

Changes:
- pyproject.toml: re-add mistral extra pinned to mistralai==2.4.8; left
  OUT of [all] per the 2026-05-12 lazy-install policy (one quarantined
  release must not break fresh installs). uv.lock regenerated.
- tools/lazy_deps.py: add stt.mistral / tts.mistral entries so the SDK
  lazy-installs on first use (matches edge / elevenlabs).
- tools/transcription_tools.py: restore explicit-provider gate
  (_HAS_MISTRAL + key) and auto-detect entry (local>groq>openai>mistral>xai);
  _transcribe_mistral lazy-installs before import.
- tools/tts_tool.py: dispatcher routes back to _generate_mistral_tts;
  _import_mistral_client lazy-installs the SDK.
- hermes_cli/tools_config.py, hermes_cli/web_server.py: un-hide Mistral
  from the TTS provider picker and dashboard STT options.
- hermes_cli/security_advisories.py: KEEP the shai-hulud-2026-05 advisory
  (module policy forbids removal) — it is scoped to 2.4.6 only, so it
  still warns anyone with the poisoned build cached and never fires on
  2.4.8. Summary note updated to reflect the un-quarantine.
- tests: revert the disabled-behavior assertions added by the ban commit
  back to routing/positive expectations; add mistral to the
  lazy-installable-extras-excluded-from-[all] contract.

Reported by @SkYNewZ (#34503).

Validation: 189 targeted STT/TTS/lazy_deps/metadata tests pass; E2E with
the real mistralai 2.4.8 SDK routes both STT and TTS to mistral.
This commit is contained in:
Teknium
2026-05-29 13:24:12 -07:00
committed by GitHub
parent 781604ce4c
commit 3a2c03061c
12 changed files with 130 additions and 105 deletions

View File

@ -104,7 +104,9 @@ ADVISORIES: tuple[Advisory, ...] = (
"them to a hardcoded webhook. If you ran any Python process that "
"imported mistralai 2.4.6 — including hermes when configured "
"with provider=mistral for TTS or STT — assume those credentials "
"are exposed."
"are exposed. PyPI has since removed 2.4.6 and the project ships "
"clean releases again (2.4.7, 2.4.8); this advisory only fires if "
"the compromised 2.4.6 is still installed."
),
url="https://socket.dev/blog/mini-shai-hulud-worm-pypi",
compromised=(

View File

@ -244,9 +244,16 @@ TOOL_CATEGORIES = {
],
"tts_provider": "elevenlabs",
},
# Mistral (Voxtral TTS) temporarily hidden — `mistralai` PyPI
# package is currently quarantined (malicious 2.4.6 release on
# 2026-05-12). Restore this entry once PyPI un-quarantines.
# Mistral Voxtral TTS — `mistralai` SDK lazy-installs on first use.
{
"name": "Mistral (Voxtral TTS)",
"badge": "paid",
"tag": "Multilingual, native Opus",
"env_vars": [
{"key": "MISTRAL_API_KEY", "prompt": "Mistral API key", "url": "https://console.mistral.ai/"},
],
"tts_provider": "mistral",
},
{
"name": "Google Gemini TTS",
"badge": "preview",

View File

@ -320,9 +320,7 @@ _SCHEMA_OVERRIDES: Dict[str, Dict[str, Any]] = {
"stt.provider": {
"type": "select",
"description": "Speech-to-text provider",
# "mistral" temporarily removed — mistralai PyPI package quarantined
# (malicious 2.4.6 release on 2026-05-12). Restore once available.
"options": ["local", "openai"],
"options": ["local", "openai", "mistral"],
},
"display.skin": {
"type": "select",

View File

@ -117,22 +117,15 @@ sms = ["aiohttp==3.13.3"]
# to it, which is already provided by the `mcp` extra.
computer-use = ["mcp==1.26.0"]
acp = ["agent-client-protocol==0.9.0"]
# mistral: extra REMOVED 2026-05-12 — `mistralai` PyPI project quarantined
# after malicious 2.4.6 release (Mini Shai-Hulud worm). Every version of
# `mistralai` returns 404 on PyPI right now, so any pin we'd write is
# unresolvable, which breaks `uv lock --check` in CI.
#
# To restore once PyPI un-quarantines:
# 1. Verify the new release is clean (read the changelog, check Socket
# advisory page, confirm no malicious code review findings).
# 2. Add back: mistral = ["mistralai==<verified-version>"]
# 3. Re-enable Mistral in:
# - tools/lazy_deps.py (LAZY_DEPS["tts.mistral"], LAZY_DEPS["stt.mistral"])
# - hermes_cli/tools_config.py (un-hide from provider picker)
# - hermes_cli/web_server.py (re-add to dashboard STT options)
# - tools/transcription_tools.py / tools/tts_tool.py (drop disabled stubs)
# 4. Run `uv lock` to regenerate transitives.
# 5. Optionally re-add to [all] only after a few days of clean operation.
# mistral: Voxtral STT + TTS. Pinned to an exact verified-clean version.
# The `mistralai` PyPI project was quarantined 2026-05-12 after the malicious
# 2.4.6 release (Mini Shai-Hulud worm); 2.4.6 was removed from PyPI and the
# project is serving clean releases again (2.4.7 2026-05-25, 2.4.8 2026-05-28).
# Like other opt-in TTS/STT backends, this is lazy-installed via
# tools/lazy_deps.py (stt.mistral / tts.mistral) at first use — deliberately
# NOT re-added to [all] so a future quarantined release can't break fresh
# installs (see [all] policy comment below).
mistral = ["mistralai==2.4.8"]
bedrock = ["boto3==1.42.89"]
azure-identity = ["azure-identity==1.25.3"]
termux = [

View File

@ -73,6 +73,7 @@ def test_lazy_installable_extras_excluded_from_all():
"modal", "daytona",
"messaging", "slack", "matrix", "dingtalk", "feishu",
"honcho", "hindsight",
"mistral", # mistralai — Voxtral STT/TTS, lazy-installed (stt.mistral / tts.mistral)
}
all_extra_specs = optional_dependencies["all"]
for extra in lazy_covered_extras:

View File

@ -99,12 +99,6 @@ class TestProviderSelectionGate:
assert tt._get_provider({"enabled": True, "provider": "groq"}) == "groq"
def test_explicit_mistral_sees_dotenv(self):
"""Mistral STT is intentionally disabled (PyPI quarantine 2026-05-12).
Even with the dotenv key visible, explicit `provider: mistral` must
return "none" with a warning. Restore the previous behavior once
`mistralai` is un-quarantined on PyPI.
"""
from tools import transcription_tools as tt
with patch.object(tt, "_HAS_FASTER_WHISPER", False), \
@ -112,7 +106,7 @@ class TestProviderSelectionGate:
patch.object(tt, "_has_local_command", return_value=False), \
patch("hermes_cli.config.load_env",
return_value={"MISTRAL_API_KEY": "dotenv-secret"}):
assert tt._get_provider({"enabled": True, "provider": "mistral"}) == "none"
assert tt._get_provider({"enabled": True, "provider": "mistral"}) == "mistral"
def test_explicit_xai_sees_dotenv(self):
from tools import transcription_tools as tt

View File

@ -1010,23 +1010,16 @@ class TestTranscribeMistral:
# ============================================================================
class TestGetProviderMistral:
"""Mistral-specific provider selection tests.
Mistral STT is intentionally disabled in 2026-05-12+ while the
`mistralai` PyPI package is quarantined. These tests document that
explicit `provider: mistral` always returns "none" with a warning, and
that auto-detect skips mistral entirely.
"""
"""Mistral-specific provider selection tests."""
def test_mistral_when_key_and_sdk_available(self, monkeypatch):
"""Even with key + SDK, explicit mistral returns 'none' (disabled)."""
monkeypatch.setenv("MISTRAL_API_KEY", "test-key")
with patch("tools.transcription_tools._HAS_MISTRAL", True):
from tools.transcription_tools import _get_provider
assert _get_provider({"provider": "mistral"}) == "none"
assert _get_provider({"provider": "mistral"}) == "mistral"
def test_mistral_explicit_no_key_returns_none(self, monkeypatch):
"""Explicit mistral with no key returns none."""
"""Explicit mistral with no key returns none — no cross-provider fallback."""
monkeypatch.delenv("MISTRAL_API_KEY", raising=False)
with patch("tools.transcription_tools._HAS_MISTRAL", True):
from tools.transcription_tools import _get_provider
@ -1039,23 +1032,18 @@ class TestGetProviderMistral:
from tools.transcription_tools import _get_provider
assert _get_provider({"provider": "mistral"}) == "none"
def test_auto_detect_skips_mistral(self, monkeypatch):
"""Auto-detect intentionally skips mistral (quarantine workaround).
With no other provider available but MISTRAL_API_KEY set, the result
must be 'none' — mistral is no longer in the auto-detect chain.
"""
def test_auto_detect_mistral_after_openai(self, monkeypatch):
"""Auto-detect: mistral is tried after openai when both are unavailable."""
monkeypatch.delenv("GROQ_API_KEY", raising=False)
monkeypatch.delenv("VOICE_TOOLS_OPENAI_KEY", raising=False)
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("XAI_API_KEY", raising=False)
monkeypatch.setenv("MISTRAL_API_KEY", "test-key")
with patch("tools.transcription_tools._HAS_FASTER_WHISPER", False), \
patch("tools.transcription_tools._has_local_command", return_value=False), \
patch("tools.transcription_tools._HAS_OPENAI", False), \
patch("tools.transcription_tools._HAS_MISTRAL", True):
from tools.transcription_tools import _get_provider
assert _get_provider({}) == "none"
assert _get_provider({}) == "mistral"
def test_auto_detect_openai_preferred_over_mistral(self, monkeypatch):
"""Auto-detect: openai is preferred over mistral (both paid, openai more common)."""
@ -1329,13 +1317,8 @@ class TestGetProviderXAI:
from tools.transcription_tools import _get_provider
assert _get_provider({}) == "xai"
def test_auto_detect_mistral_skipped_xai_wins(self, monkeypatch):
"""Auto-detect skips mistral entirely (quarantine) — xai wins.
Even with MISTRAL_API_KEY set, mistral is no longer in the
auto-detect chain. xai is the next-best fallback when the
local/groq/openai chain is unavailable.
"""
def test_auto_detect_mistral_preferred_over_xai(self, monkeypatch):
"""Auto-detect: mistral is preferred over xai."""
monkeypatch.setenv("MISTRAL_API_KEY", "test-key")
monkeypatch.setenv("XAI_API_KEY", "xai-test")
monkeypatch.delenv("GROQ_API_KEY", raising=False)
@ -1346,7 +1329,7 @@ class TestGetProviderXAI:
patch("tools.transcription_tools._HAS_OPENAI", False), \
patch("tools.transcription_tools._HAS_MISTRAL", True):
from tools.transcription_tools import _get_provider
assert _get_provider({}) == "xai"
assert _get_provider({}) == "mistral"
def test_auto_detect_no_key_returns_none(self, monkeypatch):
"""Auto-detect: xai skipped when no key is set."""

View File

@ -162,34 +162,27 @@ class TestGenerateMistralTts:
class TestTtsDispatcherMistral:
def test_dispatcher_returns_disabled_error(
def test_dispatcher_routes_to_mistral(
self, tmp_path, mock_mistral_module, monkeypatch
):
"""Mistral TTS is intentionally disabled (PyPI quarantine 2026-05-12).
The dispatcher must short-circuit with a clear status message before
attempting any SDK import, even when MISTRAL_API_KEY is set and a
mock SDK is wired in. Restore routing once `mistralai` is
un-quarantined on PyPI.
"""
import json
from tools.tts_tool import text_to_speech_tool
monkeypatch.setenv("MISTRAL_API_KEY", "test-key")
mock_mistral_module.audio.speech.complete.return_value = MagicMock(
audio_data=base64.b64encode(b"audio").decode()
)
output_path = str(tmp_path / "out.mp3")
with patch("tools.tts_tool._load_tts_config", return_value={"provider": "mistral"}):
result = json.loads(text_to_speech_tool("Hello", output_path=output_path))
assert result["success"] is False
assert "temporarily disabled" in result["error"]
assert "quarantined" in result["error"]
# SDK must not have been called.
mock_mistral_module.audio.speech.complete.assert_not_called()
assert result["success"] is True
assert result["provider"] == "mistral"
mock_mistral_module.audio.speech.complete.assert_called_once()
def test_dispatcher_returns_error_when_sdk_not_installed(self, tmp_path, monkeypatch):
"""Same disabled message regardless of SDK presence."""
import json
from tools.tts_tool import text_to_speech_tool
@ -203,7 +196,7 @@ class TestTtsDispatcherMistral:
)
assert result["success"] is False
assert "temporarily disabled" in result["error"]
assert "mistralai" in result["error"]
class TestCheckTtsRequirementsMistral:

View File

@ -97,15 +97,16 @@ LAZY_DEPS: dict[str, tuple[str, ...]] = {
# (see comment at top of [project.dependencies]). When bumping, update
# both this map AND the corresponding extra in pyproject.toml.
#
# NOTE: tts.mistral / stt.mistral entries are intentionally absent —
# the `mistralai` PyPI project is quarantined as of 2026-05-12 (Mini
# Shai-Hulud worm). Re-add when PyPI restores a clean release; see
# comment in pyproject.toml above the (removed) `mistral` extra for
# the full restoration checklist.
# mistralai pin tracks the `mistral` extra in pyproject.toml. PyPI
# quarantined the project 2026-05-12 (malicious 2.4.6, Mini Shai-Hulud);
# 2.4.6 was removed and clean releases resumed (2.4.7, 2.4.8). Voxtral
# STT + TTS share the same SDK.
"tts.mistral": ("mistralai==2.4.8",),
"tts.edge": ("edge-tts==7.2.7",),
"tts.elevenlabs": ("elevenlabs==1.59.0",),
# ─── Speech-to-text providers ──────────────────────────────────────────
"stt.mistral": ("mistralai==2.4.8",),
"stt.faster_whisper": (
"faster-whisper==1.2.1",
"sounddevice==0.5.5",

View File

@ -792,16 +792,11 @@ def _get_provider(stt_config: dict) -> str:
return "none"
if provider == "mistral":
# `mistralai` PyPI package was quarantined on 2026-05-12 after a
# malicious 2.4.6 release. Refuse to use this provider until it's
# available again so we surface a clear message instead of an
# opaque ImportError mid-call.
if _HAS_MISTRAL and get_env_value("MISTRAL_API_KEY"):
return "mistral"
logger.warning(
"STT provider 'mistral' (Voxtral Transcribe) is temporarily "
"disabled — `mistralai` PyPI package is quarantined "
"(malicious 2.4.6 release on 2026-05-12). Falling back to "
"another provider. Set stt.provider in config.yaml to 'local' "
"or 'openai' to silence this warning."
"STT provider 'mistral' configured but mistralai package "
"not installed or MISTRAL_API_KEY not set"
)
return "none"
@ -817,9 +812,7 @@ def _get_provider(stt_config: dict) -> str:
return provider # Unknown — let it fail downstream
# --- Auto-detect (no explicit provider): local > groq > openai > xai ---
# mistral is intentionally skipped while `mistralai` is quarantined on
# PyPI (malicious 2.4.6 release on 2026-05-12).
# --- Auto-detect (no explicit provider): local > groq > openai > mistral > xai ---
if _HAS_FASTER_WHISPER:
return "local"
@ -834,6 +827,12 @@ def _get_provider(stt_config: dict) -> str:
if _HAS_OPENAI and _has_openai_audio_backend():
logger.info("No local STT available, using OpenAI Whisper API")
return "openai"
# Only auto-select Mistral if the SDK is already present — don't trigger a
# lazy-install during passive auto-detection. Explicit `provider: mistral`
# (above) does lazy-install on first transcription call.
if _HAS_MISTRAL and get_env_value("MISTRAL_API_KEY"):
logger.info("No local STT available, using Mistral Voxtral Transcribe API")
return "mistral"
try:
from tools.xai_http import resolve_xai_http_credentials
@ -1371,6 +1370,11 @@ def _transcribe_mistral(file_path: str, model_name: str) -> Dict[str, Any]:
return {"success": False, "transcript": "", "error": "MISTRAL_API_KEY not set"}
try:
try:
from tools.lazy_deps import ensure as _lazy_ensure
_lazy_ensure("stt.mistral", prompt=False)
except ImportError:
pass
from mistralai.client import Mistral
with Mistral(api_key=api_key) as client:

View File

@ -121,7 +121,20 @@ def _import_openai_client():
return OpenAIClient
def _import_mistral_client():
"""Lazy import Mistral client. Returns the class or raises ImportError."""
"""Lazy import Mistral client. Returns the class or raises ImportError.
Calls :func:`tools.lazy_deps.ensure` first so the ``mistralai`` SDK gets
installed on demand if the user picked Mistral as their STT/TTS provider
but never ran the post-setup hook (e.g. enabled it by editing config.yaml
directly). Mirrors the ElevenLabs lazy-import path.
"""
try:
from tools.lazy_deps import ensure
ensure("tts.mistral", prompt=False)
except ImportError:
pass
except Exception as e: # FeatureUnavailable or any unexpected error
raise ImportError(str(e))
from mistralai.client import Mistral
return Mistral
@ -1974,21 +1987,16 @@ def text_to_speech_tool(
_generate_xai_tts(text, file_str, tts_config)
elif provider == "mistral":
# `mistralai` PyPI package was quarantined on 2026-05-12 after a
# malicious 2.4.6 release. Surface a clear status message instead
# of attempting an import that would either fail or pull a stale
# cached package.
try:
_import_mistral_client()
except ImportError:
return json.dumps({
"success": False,
"error": (
"Mistral Voxtral TTS is temporarily disabled. The "
"`mistralai` PyPI package was quarantined on 2026-05-12 "
"after a malicious 2.4.6 release. Switch tts.provider in "
"config.yaml to 'edge', 'elevenlabs', 'openai', 'minimax', "
"'gemini', 'xai', 'neutts', or 'kittentts'. Mistral "
"support will return once PyPI un-quarantines the package."
),
"error": "Mistral provider selected but 'mistralai' package not installed. "
"Run: pip install 'hermes-agent[mistral]'"
}, ensure_ascii=False)
logger.info("Generating speech with Mistral Voxtral TTS...")
_generate_mistral_tts(text, file_str, tts_config)
elif provider == "gemini":
logger.info("Generating speech with Google Gemini TTS...")

43
uv.lock generated
View File

@ -1243,6 +1243,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/97/a8/c070e1340636acb38d4e6a7e45c46d168a462b48b9b3257e14ca0e5af79b/environs-14.6.0-py3-none-any.whl", hash = "sha256:f8fb3d6c6a55872b0c6db077a28f5a8c7b8984b7c32029613d44cef95cfc0812", size = 17205, upload-time = "2026-02-20T04:02:07.299Z" },
]
[[package]]
name = "eval-type-backport"
version = "0.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fb/a3/cafafb4558fd638aadfe4121dc6cefb8d743368c085acb2f521df0f3d9d7/eval_type_backport-0.3.1.tar.gz", hash = "sha256:57e993f7b5b69d271e37482e62f74e76a0276c82490cf8e4f0dffeb6b332d5ed", size = 9445, upload-time = "2025-12-02T11:51:42.987Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/22/fdc2e30d43ff853720042fa15baa3e6122722be1a7950a98233ebb55cd71/eval_type_backport-0.3.1-py3-none-any.whl", hash = "sha256:279ab641905e9f11129f56a8a78f493518515b83402b860f6f06dd7c011fdfa8", size = 6063, upload-time = "2025-12-02T11:51:41.665Z" },
]
[[package]]
name = "exa-py"
version = "2.10.2"
@ -1715,6 +1724,9 @@ messaging = [
{ name = "slack-bolt" },
{ name = "slack-sdk" },
]
mistral = [
{ name = "mistralai" },
]
modal = [
{ name = "modal" },
]
@ -1840,6 +1852,7 @@ requires-dist = [
{ name = "mcp", marker = "extra == 'computer-use'", specifier = "==1.26.0" },
{ name = "mcp", marker = "extra == 'dev'", specifier = "==1.26.0" },
{ name = "mcp", marker = "extra == 'mcp'", specifier = "==1.26.0" },
{ name = "mistralai", marker = "extra == 'mistral'", specifier = "==2.4.8" },
{ name = "modal", marker = "extra == 'modal'", specifier = "==1.3.4" },
{ name = "numpy", marker = "extra == 'voice'", specifier = "==2.4.3" },
{ name = "openai", specifier = "==2.24.0" },
@ -1876,7 +1889,7 @@ requires-dist = [
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'web'", specifier = "==0.41.0" },
{ name = "youtube-transcript-api", marker = "extra == 'youtube'", specifier = "==1.2.4" },
]
provides-extras = ["anthropic", "exa", "firecrawl", "parallel-web", "fal", "edge-tts", "modal", "daytona", "hindsight", "dev", "messaging", "cron", "slack", "matrix", "wecom", "cli", "tts-premium", "voice", "pty", "honcho", "mcp", "homeassistant", "sms", "computer-use", "acp", "bedrock", "azure-identity", "termux", "termux-all", "dingtalk", "feishu", "google", "youtube", "web", "all"]
provides-extras = ["anthropic", "exa", "firecrawl", "parallel-web", "fal", "edge-tts", "modal", "daytona", "hindsight", "dev", "messaging", "cron", "slack", "matrix", "wecom", "cli", "tts-premium", "voice", "pty", "honcho", "mcp", "homeassistant", "sms", "computer-use", "acp", "mistral", "bedrock", "azure-identity", "termux", "termux-all", "dingtalk", "feishu", "google", "youtube", "web", "all"]
[[package]]
name = "hf-xet"
@ -2206,6 +2219,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
]
[[package]]
name = "jsonpath-python"
version = "1.1.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/18/4ca8742534a5993ff383f7602e325ce2d5d7cc93d72ac5e1cdedbea8a458/jsonpath_python-1.1.6.tar.gz", hash = "sha256:dded9932b4ec41fb8726e09c83afa4e6be618f938c2db287cc2a81723c639671", size = 88178, upload-time = "2026-05-07T01:26:34.482Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/55/8a/1270a6803bd821cbfcdda387eaa13cb41a7b1f7b9bd145979b3bfb9d6cb7/jsonpath_python-1.1.6-py3-none-any.whl", hash = "sha256:a1c50afd8d3fbbaf47a4873bc890dcb3c15da96f5c020327977d844d8731a2d4", size = 14453, upload-time = "2026-05-07T01:26:33.306Z" },
]
[[package]]
name = "jsonschema"
version = "4.26.0"
@ -2408,6 +2430,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "mistralai"
version = "2.4.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "eval-type-backport" },
{ name = "httpx" },
{ name = "jsonpath-python" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "pydantic" },
{ name = "python-dateutil" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/1c/04119828a3da3be8c79efbe59035a621ae22af873c1ee5a4200355025aa6/mistralai-2.4.8.tar.gz", hash = "sha256:4f27b9b7dfd564ae111d3d9992d2a8ad1454aaf3e7675554c686aa3bb89617e2", size = 464443, upload-time = "2026-05-28T10:00:45.72Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/74/2a/d9952a97596ff9570ff7f486084ebfc5637b1bcf62084b97c0f8415713fc/mistralai-2.4.8-py3-none-any.whl", hash = "sha256:edc445c8b5edf332d45db6c708cd1e4d3f62e6eba5d2e8bf3969bdc5117f6472", size = 1110598, upload-time = "2026-05-28T10:00:43.939Z" },
]
[[package]]
name = "modal"
version = "1.3.4"