Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `ChatOllama()` can now be created without a `model`, so `ChatOllama().list_models()` works. Sending a message without a model still raises an error that lists the available models. (#166)
* `ChatGoogle()` now reports `finish_reason == "content_filter"` for Gemini's `IMAGE_SAFETY`, `IMAGE_PROHIBITED_CONTENT`, `IMAGE_RECITATION` and `LANGUAGE` finish reasons, instead of passing the raw Gemini string through, and reports `"tool_use"` (rather than `"success"`) when a turn ends with a tool request, as `ChatOpenAI()` does. (#473)
* The error raised for a tool request with invalid JSON arguments from `ChatOpenAI()` and `ChatOpenAICompletions()` (and the providers built on them) now shows the offending arguments, instead of the literal text `{args}`.
* `ChatMistral(seed=0)` now sends `random_seed: 0` instead of silently dropping the seed, and a seed no longer replaces an `extra_body` passed through `kwargs`: Mistral's `random_seed` is merged into it. (#475)
* `content_image_url()` with a `data:` URL no longer silently drops the `detail` argument: the setting is now carried by inline image content and forwarded to OpenAI's Responses and Chat Completions APIs (regular image URLs already forwarded it). Other providers continue to ignore `detail`. (#467)
* `ChatBedrock()` on a Converse model no longer drops redacted reasoning blocks when streaming. They were discarded as they arrived, so a streamed turn lost them, while a non-streamed one kept them. (#468)
* `ChatBedrockAnthropic()` now uses native structured outputs for `data_model` on Claude 4.5 and later, as `ChatAnthropic()` does. Its Bedrock model IDs (`anthropic.claude-...`, `us.anthropic.claude-...`) weren't recognized, so it fell back to forcing a tool call, which Claude Opus 5.5 and Sonnet 5.5 reject with a 400.
Expand Down
14 changes: 9 additions & 5 deletions chatlas/_provider_mistral.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Any, Optional, cast

from ._chat import Chat
from ._logging import log_model_default
Expand Down Expand Up @@ -170,12 +170,16 @@ def _chat_perform_args(
del kwargs2["stream_options"]

# Mistral wants random_seed, not seed
if seed := kwargs2.pop("seed", None):
if isinstance(seed, int):
kwargs2["extra_body"] = {"random_seed": seed}
elif seed is not None:
seed = kwargs2.pop("seed", None)
if seed is not None:
if not isinstance(seed, int):
raise ValueError(
"MistralProvider only accepts an integer seed, or None."
)
extra_body: dict[str, Any] = dict(
cast("dict[str, Any]", kwargs2.get("extra_body") or {})
)
extra_body["random_seed"] = seed
kwargs2["extra_body"] = extra_body

return kwargs2
15 changes: 15 additions & 0 deletions tests/test_provider_mistral.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,18 @@ def test_data_extraction():
@pytest.mark.vcr
def test_mistral_model_list():
assert_list_models(ChatMistral)


def test_mistral_seed_zero_is_sent_as_random_seed():
chat = ChatMistral(api_key="key", seed=0)
args = chat.provider._chat_perform_args(False, [], {})
assert "seed" not in args
assert args["extra_body"] == {"random_seed": 0}


def test_mistral_seed_keeps_user_extra_body():
chat = ChatMistral(api_key="key", seed=7)
args = chat.provider._chat_perform_args(
False, [], {}, kwargs={"extra_body": {"safe_prompt": True}}
)
assert args["extra_body"] == {"safe_prompt": True, "random_seed": 7}
Loading