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
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ class MistralSettings(TypedDict, total=False):
api_key: Mistral API key. Resolved from ``MISTRAL_API_KEY``.
chat_model: Chat model name. Resolved from ``MISTRAL_CHAT_MODEL``.
server_url: Optional server URL override. Resolved from ``MISTRAL_SERVER_URL``.
Ignored when a Mistral SDK client is injected; the injected client's own
endpoint is reported instead.
"""

api_key: SecretString | None
Expand Down Expand Up @@ -389,20 +391,23 @@ def __init__(
required_fields=[] if client is not None or http_client is not None else ["api_key"],
api_key=api_key,
chat_model=model,
server_url=server_url,
server_url=None if client is not None else server_url,
env_file_path=env_file_path,
env_file_encoding=env_file_encoding,
)

self.model = mistral_settings.get("chat_model")
self.server_url = mistral_settings.get("server_url")
self._owns_client = not isinstance(client, Mistral)
self._client_closed = False
# Settings are only consulted for a client this wrapper creates; an
# injected SDK client has its own endpoint and requires no key.
self.server_url = mistral_settings.get("server_url")

if isinstance(client, Mistral):
# The injected SDK client owns the endpoint: requests go to it, so
# metadata must report its URL, not an ambient MISTRAL_SERVER_URL.
self.server_url = client.sdk_configuration.get_server_details()[0]
self.client = client
if self.server_url is None:
self.server_url = client.sdk_configuration.get_server_details()[0]
else:
client_kwargs: dict[str, Any] = {"timeout_ms": _DEFAULT_TIMEOUT_MS}
if resolved_api_key := mistral_settings.get("api_key"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class MistralEmbeddingSettings(TypedDict, total=False):
api_key: Mistral API key. Resolved from ``MISTRAL_API_KEY``.
embedding_model: Embedding model name. Resolved from ``MISTRAL_EMBEDDING_MODEL``.
server_url: Optional server URL override. Resolved from ``MISTRAL_SERVER_URL``.
Ignored when a Mistral SDK client is injected; the injected client's own
endpoint is reported instead.
"""

api_key: SecretString | None
Expand Down Expand Up @@ -146,19 +148,22 @@ def __init__(
required_fields=required_fields,
api_key=api_key,
embedding_model=model,
server_url=server_url,
server_url=None if client is not None else server_url,
env_file_path=env_file_path,
env_file_encoding=env_file_encoding,
)

self.model: str = mistral_settings["embedding_model"] # type: ignore[assignment]
self.server_url = mistral_settings.get("server_url")
self._owns_client = not isinstance(client, Mistral)
# Settings are only consulted for a client this wrapper creates; an
# injected SDK client has its own endpoint and requires no key.
self.server_url = mistral_settings.get("server_url")

if isinstance(client, Mistral):
# The injected SDK client owns the endpoint: requests go to it, so
# metadata must report its URL, not an ambient MISTRAL_SERVER_URL.
self.server_url = client.sdk_configuration.get_server_details()[0]
self.client = client
if self.server_url is None:
self.server_url = client.sdk_configuration.get_server_details()[0]
else:
client_kwargs: dict[str, Any] = {"timeout_ms": _DEFAULT_TIMEOUT_MS}
if resolved_api_key := mistral_settings.get("api_key"):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright (c) Microsoft. All rights reserved.

"""An injected Mistral SDK client owns its endpoint (#9965).

The wrapper used to resolve ``MISTRAL_SERVER_URL`` before inspecting the
injected client, so ``service_url()`` reported the ambient URL (used for
telemetry) while requests went to the injected client's endpoint.
"""

import os

from mistralai.client import Mistral

from agent_framework_mistral import MistralChatClient, MistralEmbeddingClient, RawMistralChatClient


def _make_injected_sdk(server_url: str) -> Mistral:
return Mistral(api_key="dummy", server_url=server_url)


def test_chat_service_url_uses_injected_client_endpoint(monkeypatch) -> None:
monkeypatch.setenv("MISTRAL_SERVER_URL", "https://env.example")
sdk = _make_injected_sdk("https://client.example")

client = RawMistralChatClient(client=sdk, model="mistral-test")

assert client.service_url() == "https://client.example"
assert client.server_url == "https://client.example"


def test_embedding_service_url_uses_injected_client_endpoint(monkeypatch) -> None:
monkeypatch.setenv("MISTRAL_SERVER_URL", "https://env.example")
sdk = _make_injected_sdk("https://client.example")

client = MistralEmbeddingClient(client=sdk, model="mistral-embed")

assert client.service_url() == "https://client.example"
assert client.server_url == "https://client.example"


def test_chat_service_url_uses_env_when_no_client(monkeypatch) -> None:
monkeypatch.setenv("MISTRAL_SERVER_URL", "https://env.example")
monkeypatch.setenv("MISTRAL_API_KEY", "env-key")

client = MistralChatClient()

assert client.service_url() == "https://env.example"


def test_owned_client_keeps_explicit_server_url(monkeypatch) -> None:
monkeypatch.setenv("MISTRAL_API_KEY", "env-key")
monkeypatch.setenv("MISTRAL_SERVER_URL", "https://env.example")

raw = RawMistralChatClient(model="mistral-test", server_url="https://explicit.example")
assert raw.server_url == "https://explicit.example"

wrapped = MistralChatClient(server_url="https://explicit.example")
assert wrapped.service_url() == "https://explicit.example"


def test_injected_client_consts_not_loaded(monkeypatch) -> None:
# No MISTRAL_API_KEY in the environment at all; injecting a client must not
# require one.
monkeypatch.delenv("MISTRAL_API_KEY", raising=False)
monkeypatch.delenv("MISTRAL_SERVER_URL", raising=False)
sdk = _make_injected_sdk("https://client.example")

chat = RawMistralChatClient(client=sdk, model="mistral-test")
embedding = MistralEmbeddingClient(client=sdk, model="mistral-embed")

assert chat._owns_client is False
assert embedding._owns_client is False
os.environ.pop("MISTRAL_API_KEY", None)
os.environ.pop("MISTRAL_SERVER_URL", None)
Loading