diff --git a/docs/WELCOME.md b/docs/WELCOME.md index 649d16692..31e0b220d 100644 --- a/docs/WELCOME.md +++ b/docs/WELCOME.md @@ -88,7 +88,7 @@ Make your first call to an LLM to extract the title and author of a book from th {% if provider == "Anthropic" %} ```python hl_lines="19-38 43" {% elif provider == "Mistral" %} - ```python hl_lines="19-40 45" + ```python hl_lines="21-46 51" {% elif provider == "Gemini" %} ```python hl_lines="19-57 62" {% elif provider == "Cohere" %} diff --git a/docs/WHY.md b/docs/WHY.md index b32b00c3a..a97f76fb9 100644 --- a/docs/WHY.md +++ b/docs/WHY.md @@ -94,7 +94,7 @@ Let's compare structured outputs using Mirascope vs. the official SDKs: {% if provider == "Anthropic" %} ```python hl_lines="19-38 43" {% elif provider == "Mistral" %} - ```python hl_lines="19-40 45" + ```python hl_lines="21-46 51" {% elif provider == "Gemini" %} ```python hl_lines="19-57 62" {% elif provider == "Cohere" %} diff --git a/docs/learn/async.md b/docs/learn/async.md index 5677c5517..8e4263afd 100644 --- a/docs/learn/async.md +++ b/docs/learn/async.md @@ -171,12 +171,14 @@ __Decorator Parameter:__ {% if provider == "LiteLLM" %} ```python - {% elif provider in ["OpenAI", "Mistral", "Vertex AI"] %} + {% elif provider in ["OpenAI", "Vertex AI"] %} ```python hl_lines="2 5" {% elif provider == "Azure AI" %} ```python hl_lines="1-2 8-10" {% elif provider == "Bedrock" %} ```python hl_lines="7-10 14" + {% elif provider == "Mistral" %} + ```python hl_lines="4 8" {% else %} ```python hl_lines="1 5" {% endif %} @@ -198,12 +200,14 @@ __Dynamic Configuration:__ {% if provider == "LiteLLM" %} ```python - {% elif provider in ["OpenAI", "Mistral", "Vertex AI"] %} + {% elif provider in ["OpenAI", "Vertex AI"] %} ```python hl_lines="2 11" {% elif provider == "Azure AI" %} ```python hl_lines="1-2 12-14" {% elif provider == "Bedrock" %} ```python hl_lines="5-8 17" + {% elif provider == "Mistral" %} + ```python hl_lines="4 15" {% else %} ```python hl_lines="1 11" {% endif %} @@ -217,12 +221,14 @@ __Dynamic Configuration:__ {% if provider == "LiteLLM" %} ```python - {% elif provider in ["OpenAI", "Mistral", "Vertex AI"] %} + {% elif provider in ["OpenAI", "Vertex AI"] %} ```python hl_lines="2 9" {% elif provider == "Azure AI" %} ```python hl_lines="1-2 10-12" {% elif provider == "Bedrock" %} ```python hl_lines="5-8 15" + {% elif provider == "Mistral" %} + ```python hl_lines="4 11" {% else %} ```python hl_lines="1 9" {% endif %} diff --git a/docs/learn/calls.md b/docs/learn/calls.md index 25f9f6a29..ac33673d2 100644 --- a/docs/learn/calls.md +++ b/docs/learn/calls.md @@ -58,6 +58,8 @@ Let's take a look at a basic example using Mirascope vs. official provider SDKs: ```python hl_lines="5-9" {% elif provider == "Azure AI" %} ```python hl_lines="11-18" + {% elif provider == "Mistral" %} + ```python hl_lines="10-15" {% else %} ```python hl_lines="7-11" {% endif %} @@ -360,12 +362,14 @@ You can pass a client to the `call` decorator using the `client` parameter: {% if provider == "LiteLLM" %} ```python - {% elif provider in ["OpenAI", "Mistral", "Vertex AI"] %} + {% elif provider in ["OpenAI", "Vertex AI"] %} ```python hl_lines="2 5" {% elif provider == "Azure AI" %} ```python hl_lines="1-2 8-10" {% elif provider == "Bedrock" %} ```python hl_lines="1 6" + {% elif provider == "Mistral" %} + ```python hl_lines="4 8" {% else %} ```python hl_lines="1 5" {% endif %} @@ -393,6 +397,8 @@ You can also configure the client dynamically at runtime through the dynamic con ```python hl_lines="1-2 12-14" {% elif provider == "Bedrock" %} ```python hl_lines="1 11" + {% elif provider == "Mistral" %} + ```python hl_lines="4 13" {% else %} ```python hl_lines="2 11" {% endif %} @@ -410,6 +416,8 @@ You can also configure the client dynamically at runtime through the dynamic con ```python hl_lines="1-2 10-11" {% elif provider == "Bedrock" %} ```python hl_lines="1 11" + {% elif provider == "Mistral" %} + ```python hl_lines="4 11" {% else %} ```python hl_lines="2 9" {% endif %} diff --git a/docs/learn/response_models.md b/docs/learn/response_models.md index 5e35202fb..72474d45d 100644 --- a/docs/learn/response_models.md +++ b/docs/learn/response_models.md @@ -42,7 +42,7 @@ Let's take a look at a basic example using Mirascope vs. official provider SDKs: {% if provider == "Anthropic" %} ```python hl_lines="19-38 43" {% elif provider == "Mistral" %} - ```python hl_lines="19-40 45" + ```python hl_lines="21-46 51" {% elif provider == "Gemini" %} ```python hl_lines="19-57 62" {% elif provider == "Cohere" %} diff --git a/docs/learn/tools.md b/docs/learn/tools.md index 31f8ec871..c73a7d582 100644 --- a/docs/learn/tools.md +++ b/docs/learn/tools.md @@ -95,6 +95,8 @@ Let's take a look at a basic example of each using Mirascope vs. official provid ```python hl_lines="16-22 31-43 45-47" {% elif provider == "Vertex AI" %} ```python hl_lines="6-12 18-34 36-43" + {% elif provider == "Mistral" %} + ```python hl_lines="10-16 23-36 38-44" {% else %} ```python hl_lines="8-14 21-34 36-38" {% endif %} diff --git a/examples/learn/async/custom_client/decorator/mistral/base_message_param.py b/examples/learn/async/custom_client/decorator/mistral/base_message_param.py index 388f0614a..ec8278325 100644 --- a/examples/learn/async/custom_client/decorator/mistral/base_message_param.py +++ b/examples/learn/async/custom_client/decorator/mistral/base_message_param.py @@ -1,7 +1,11 @@ +import os + from mirascope.core import BaseMessageParam, mistral -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralAsyncClient()) +@mistral.call( + "mistral-large-latest", client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]) +) async def recommend_book_async(genre: str) -> list[BaseMessageParam]: return [BaseMessageParam(role="user", content=f"Recommend a {genre} book")] diff --git a/examples/learn/async/custom_client/decorator/mistral/messages.py b/examples/learn/async/custom_client/decorator/mistral/messages.py index 50e317af3..72e6b0e1f 100644 --- a/examples/learn/async/custom_client/decorator/mistral/messages.py +++ b/examples/learn/async/custom_client/decorator/mistral/messages.py @@ -1,7 +1,11 @@ +import os + from mirascope.core import Messages, mistral -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralAsyncClient()) +@mistral.call( + "mistral-large-latest", client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]) +) async def recommend_book_async(genre: str) -> Messages.Type: return Messages.User(f"Recommend a {genre} book") diff --git a/examples/learn/async/custom_client/decorator/mistral/shorthand.py b/examples/learn/async/custom_client/decorator/mistral/shorthand.py index 42eb2d2e5..81461eae3 100644 --- a/examples/learn/async/custom_client/decorator/mistral/shorthand.py +++ b/examples/learn/async/custom_client/decorator/mistral/shorthand.py @@ -1,7 +1,11 @@ +import os + from mirascope.core import mistral -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralAsyncClient()) +@mistral.call( + "mistral-large-latest", client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]) +) async def recommend_book_async(genre: str) -> str: return f"Recommend a {genre} book" diff --git a/examples/learn/async/custom_client/decorator/mistral/string_template.py b/examples/learn/async/custom_client/decorator/mistral/string_template.py index 5f78a877f..6370e3d5b 100644 --- a/examples/learn/async/custom_client/decorator/mistral/string_template.py +++ b/examples/learn/async/custom_client/decorator/mistral/string_template.py @@ -1,7 +1,11 @@ +import os + from mirascope.core import mistral, prompt_template -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralAsyncClient()) +@mistral.call( + "mistral-large-latest", client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]) +) @prompt_template("Recommend a {genre} book") async def recommend_book_async(genre: str): ... diff --git a/examples/learn/async/custom_client/dynamic_configuration/mistral/base_message_param.py b/examples/learn/async/custom_client/dynamic_configuration/mistral/base_message_param.py index 928205493..63d275c44 100644 --- a/examples/learn/async/custom_client/dynamic_configuration/mistral/base_message_param.py +++ b/examples/learn/async/custom_client/dynamic_configuration/mistral/base_message_param.py @@ -1,12 +1,14 @@ +import os + from mirascope.core import BaseMessageParam, mistral -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral @mistral.call("mistral-large-latest") -async def recommend_book(genre: str) -> mistral.AsyncMistralDynamicConfig: +async def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { "messages": [ BaseMessageParam(role="user", content=f"Recommend a {genre} book") ], - "client": MistralAsyncClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/async/custom_client/dynamic_configuration/mistral/messages.py b/examples/learn/async/custom_client/dynamic_configuration/mistral/messages.py index 1629c06b5..7f6334797 100644 --- a/examples/learn/async/custom_client/dynamic_configuration/mistral/messages.py +++ b/examples/learn/async/custom_client/dynamic_configuration/mistral/messages.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import Messages, mistral -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral @mistral.call("mistral-large-latest") -async def recommend_book(genre: str) -> mistral.AsyncMistralDynamicConfig: +async def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { "messages": [Messages.User(f"Recommend a {genre} book")], - "client": MistralAsyncClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/async/custom_client/dynamic_configuration/mistral/shorthand.py b/examples/learn/async/custom_client/dynamic_configuration/mistral/shorthand.py index 7a3005be7..54ee78fe4 100644 --- a/examples/learn/async/custom_client/dynamic_configuration/mistral/shorthand.py +++ b/examples/learn/async/custom_client/dynamic_configuration/mistral/shorthand.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import mistral, Messages -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral @mistral.call("mistral-large-latest") -async def recommend_book(genre: str) -> mistral.AsyncMistralDynamicConfig: +async def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { "messages": [Messages.User(f"Recommend a {genre} book")], - "client": MistralAsyncClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/async/custom_client/dynamic_configuration/mistral/string_template.py b/examples/learn/async/custom_client/dynamic_configuration/mistral/string_template.py index e74019b73..3a0d9dc3f 100644 --- a/examples/learn/async/custom_client/dynamic_configuration/mistral/string_template.py +++ b/examples/learn/async/custom_client/dynamic_configuration/mistral/string_template.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import mistral, prompt_template -from mistralai.async_client import MistralAsyncClient +from mistralai import Mistral @mistral.call("mistral-large-latest") @prompt_template("Recommend a {genre} book") -async def recommend_book(genre: str) -> mistral.AsyncMistralDynamicConfig: +async def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { - "client": MistralAsyncClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/calls/basic_usage/mistral/official_sdk_call.py b/examples/learn/calls/basic_usage/mistral/official_sdk_call.py index 15a965fdb..31b920121 100644 --- a/examples/learn/calls/basic_usage/mistral/official_sdk_call.py +++ b/examples/learn/calls/basic_usage/mistral/official_sdk_call.py @@ -1,14 +1,18 @@ -from mistralai.client import MistralClient +import os +from typing import cast -client = MistralClient() +from mistralai import Mistral +client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) -def recommend_book(genre: str) -> str: - completion = client.chat( + +def recommend_book(genre: str) -> str | None: + completion = client.chat.complete( model="mistral-large-latest", messages=[{"role": "user", "content": f"Recommend a {genre} book"}], ) - return completion.choices[0].message.content + if completion and (choices := completion.choices): + return cast(str, choices[0].message.content) output = recommend_book("fantasy") diff --git a/examples/learn/calls/custom_client/decorator/mistral/base_message_param.py b/examples/learn/calls/custom_client/decorator/mistral/base_message_param.py index 3094488ab..521bf8539 100644 --- a/examples/learn/calls/custom_client/decorator/mistral/base_message_param.py +++ b/examples/learn/calls/custom_client/decorator/mistral/base_message_param.py @@ -1,7 +1,20 @@ +import os + from mirascope.core import BaseMessageParam, mistral -from mistralai.client import MistralClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralClient()) +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) def recommend_book(genre: str) -> list[BaseMessageParam]: return [BaseMessageParam(role="user", content=f"Recommend a {genre} book")] + + +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) +async def recommend_book_async(genre: str) -> list[BaseMessageParam]: + return [BaseMessageParam(role="user", content=f"Recommend a {genre} book")] diff --git a/examples/learn/calls/custom_client/decorator/mistral/messages.py b/examples/learn/calls/custom_client/decorator/mistral/messages.py index d305324ac..259095d19 100644 --- a/examples/learn/calls/custom_client/decorator/mistral/messages.py +++ b/examples/learn/calls/custom_client/decorator/mistral/messages.py @@ -1,7 +1,20 @@ +import os + from mirascope.core import Messages, mistral -from mistralai.client import MistralClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralClient()) +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) def recommend_book(genre: str) -> Messages.Type: return Messages.User(f"Recommend a {genre} book") + + +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) +async def recommend_book_async(genre: str) -> Messages.Type: + return Messages.User(f"Recommend a {genre} book") diff --git a/examples/learn/calls/custom_client/decorator/mistral/shorthand.py b/examples/learn/calls/custom_client/decorator/mistral/shorthand.py index 276702ba3..9c2f8e4c0 100644 --- a/examples/learn/calls/custom_client/decorator/mistral/shorthand.py +++ b/examples/learn/calls/custom_client/decorator/mistral/shorthand.py @@ -1,7 +1,20 @@ +import os + from mirascope.core import mistral -from mistralai.client import MistralClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralClient()) +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) def recommend_book(genre: str) -> str: return f"Recommend a {genre} book" + + +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) +async def recommend_book_async(genre: str) -> str: + return f"Recommend a {genre} book" diff --git a/examples/learn/calls/custom_client/decorator/mistral/string_template.py b/examples/learn/calls/custom_client/decorator/mistral/string_template.py index f63c6427c..9b37e5a0b 100644 --- a/examples/learn/calls/custom_client/decorator/mistral/string_template.py +++ b/examples/learn/calls/custom_client/decorator/mistral/string_template.py @@ -1,7 +1,20 @@ +import os + from mirascope.core import mistral, prompt_template -from mistralai.client import MistralClient +from mistralai import Mistral -@mistral.call("mistral-large-latest", client=MistralClient()) +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) @prompt_template("Recommend a {genre} book") def recommend_book(genre: str): ... + + +@mistral.call( + "mistral-large-latest", + client=Mistral(api_key=os.environ["MISTRAL_API_KEY"]), +) +@prompt_template("Recommend a {genre} book") +async def recommend_book_async(genre: str): ... diff --git a/examples/learn/calls/custom_client/dynamic_configuration/mistral/base_message_param.py b/examples/learn/calls/custom_client/dynamic_configuration/mistral/base_message_param.py index 6225caaef..229c02c93 100644 --- a/examples/learn/calls/custom_client/dynamic_configuration/mistral/base_message_param.py +++ b/examples/learn/calls/custom_client/dynamic_configuration/mistral/base_message_param.py @@ -1,5 +1,7 @@ +import os + from mirascope.core import BaseMessageParam, mistral -from mistralai.client import MistralClient +from mistralai import Mistral @mistral.call("mistral-large-latest") @@ -8,5 +10,5 @@ def recommend_book(genre: str) -> mistral.MistralDynamicConfig: "messages": [ BaseMessageParam(role="user", content=f"Recommend a {genre} book") ], - "client": MistralClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/calls/custom_client/dynamic_configuration/mistral/messages.py b/examples/learn/calls/custom_client/dynamic_configuration/mistral/messages.py index 5cf60d538..906ba9ae8 100644 --- a/examples/learn/calls/custom_client/dynamic_configuration/mistral/messages.py +++ b/examples/learn/calls/custom_client/dynamic_configuration/mistral/messages.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import Messages, mistral -from mistralai.client import MistralClient +from mistralai import Mistral @mistral.call("mistral-large-latest") def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { "messages": [Messages.User(f"Recommend a {genre} book")], - "client": MistralClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/calls/custom_client/dynamic_configuration/mistral/shorthand.py b/examples/learn/calls/custom_client/dynamic_configuration/mistral/shorthand.py index e0cacbad6..7db09cb5a 100644 --- a/examples/learn/calls/custom_client/dynamic_configuration/mistral/shorthand.py +++ b/examples/learn/calls/custom_client/dynamic_configuration/mistral/shorthand.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import mistral, Messages -from mistralai.client import MistralClient +from mistralai import Mistral @mistral.call("mistral-large-latest") def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { "messages": [Messages.User(f"Recommend a {genre} book")], - "client": MistralClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/calls/custom_client/dynamic_configuration/mistral/string_template.py b/examples/learn/calls/custom_client/dynamic_configuration/mistral/string_template.py index 8f981753a..b9eb73c72 100644 --- a/examples/learn/calls/custom_client/dynamic_configuration/mistral/string_template.py +++ b/examples/learn/calls/custom_client/dynamic_configuration/mistral/string_template.py @@ -1,10 +1,12 @@ +import os + from mirascope.core import mistral, prompt_template -from mistralai.client import MistralClient +from mistralai import Mistral @mistral.call("mistral-large-latest") @prompt_template("Recommend a {genre} book") def recommend_book(genre: str) -> mistral.MistralDynamicConfig: return { - "client": MistralClient(), + "client": Mistral(api_key=os.environ["MISTRAL_API_KEY"]), } diff --git a/examples/learn/calls/custom_messages/mistral_messages.py b/examples/learn/calls/custom_messages/mistral_messages.py index 928f3c548..9d1f71e5e 100644 --- a/examples/learn/calls/custom_messages/mistral_messages.py +++ b/examples/learn/calls/custom_messages/mistral_messages.py @@ -1,10 +1,10 @@ from mirascope.core import mistral -from mistralai.models.chat_completion import ChatMessage +from mistralai.models import UserMessage @mistral.call("mistral-large-latest") def recommend_book(genre: str) -> mistral.MistralDynamicConfig: - return {"messages": [ChatMessage(role="user", content=f"Recommend a {genre} book")]} + return {"messages": [UserMessage(role="user", content=f"Recommend a {genre} book")]} response = recommend_book("fantasy") diff --git a/examples/learn/response_models/basic_usage/mistral/official_sdk.py b/examples/learn/response_models/basic_usage/mistral/official_sdk.py index aa6f717d5..d20182e2f 100644 --- a/examples/learn/response_models/basic_usage/mistral/official_sdk.py +++ b/examples/learn/response_models/basic_usage/mistral/official_sdk.py @@ -1,8 +1,10 @@ -from mistralai.client import MistralClient -from mistralai.models.chat_completion import ToolChoice +import os +from typing import cast + +from mistralai import Mistral from pydantic import BaseModel -client = MistralClient() +client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) class Book(BaseModel): @@ -13,7 +15,7 @@ class Book(BaseModel): def extract_book(text: str) -> Book: - completion = client.chat( + completion = client.chat.complete( model="mistral-large-latest", messages=[{"role": "user", "content": f"Extract {text}"}], tools=[ @@ -33,10 +35,14 @@ def extract_book(text: str) -> Book: "type": "function", } ], - tool_choice=ToolChoice.any, + tool_choice="any", ) - if tool_calls := completion.choices[0].message.tool_calls: - return Book.model_validate_json(tool_calls[0].function.arguments) + if ( + completion + and (choices := completion.choices) + and (tool_calls := choices[0].message.tool_calls) + ): + return Book.model_validate_json(cast(str, tool_calls[0].function.arguments)) raise ValueError("No tool call found") diff --git a/examples/learn/tools/basic_usage/mistral/official_sdk.py b/examples/learn/tools/basic_usage/mistral/official_sdk.py index a114dc640..3d5d0fadf 100644 --- a/examples/learn/tools/basic_usage/mistral/official_sdk.py +++ b/examples/learn/tools/basic_usage/mistral/official_sdk.py @@ -1,8 +1,10 @@ import json +import os +from typing import cast -from mistralai.client import MistralClient +from mistralai import Mistral -client = MistralClient() +client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) def get_book_author(title: str) -> str: @@ -14,8 +16,8 @@ def get_book_author(title: str) -> str: return "Unknown" -def identify_author(book: str) -> str: - completion = client.chat( +def identify_author(book: str) -> str | None: + completion = client.chat.complete( model="mistral-large-latest", messages=[{"role": "user", "content": f"Who wrote {book}?"}], tools=[ @@ -33,10 +35,14 @@ def identify_author(book: str) -> str: } ], ) + if not completion or not completion.choices: + return None if tool_calls := completion.choices[0].message.tool_calls: if tool_calls[0].function.name == "get_book_author": - return get_book_author(**json.loads(tool_calls[0].function.arguments)) - return completion.choices[0].message.content + return get_book_author( + **json.loads(cast(str, tool_calls[0].function.arguments)) + ) + return cast(str, completion.choices[0].message.content) author = identify_author("The Name of the Wind") diff --git a/mirascope/core/anthropic/_call_kwargs.py b/mirascope/core/anthropic/_call_kwargs.py index 5dabce45b..67abd88bf 100644 --- a/mirascope/core/anthropic/_call_kwargs.py +++ b/mirascope/core/anthropic/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Anthropic call keyword arguments.""" +from collections.abc import Sequence + from anthropic.types import MessageParam, ToolParam from ..base import BaseCallKwargs @@ -8,4 +10,4 @@ class AnthropicCallKwargs(AnthropicCallParams, BaseCallKwargs[ToolParam]): model: str - messages: list[MessageParam] + messages: Sequence[MessageParam] diff --git a/mirascope/core/azure/_call_kwargs.py b/mirascope/core/azure/_call_kwargs.py index 6f32bc390..03cfb96b8 100644 --- a/mirascope/core/azure/_call_kwargs.py +++ b/mirascope/core/azure/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Azure call keyword arguments.""" +from collections.abc import Sequence + from azure.ai.inference.models import ChatCompletionsToolDefinition, ChatRequestMessage from ..base import BaseCallKwargs @@ -8,4 +10,4 @@ class AzureCallKwargs(AzureCallParams, BaseCallKwargs[ChatCompletionsToolDefinition]): model: str - messages: list[ChatRequestMessage] + messages: Sequence[ChatRequestMessage] diff --git a/mirascope/core/base/_utils/_setup_call.py b/mirascope/core/base/_utils/_setup_call.py index 83cc6bbf5..b4cffcde9 100644 --- a/mirascope/core/base/_utils/_setup_call.py +++ b/mirascope/core/base/_utils/_setup_call.py @@ -39,7 +39,7 @@ def setup_call( convert_common_call_params: ConvertCommonParamsFunc[_BaseCallParamsT], ) -> tuple[ str | None, - Sequence[BaseMessageParam | Any], + list[BaseMessageParam | Any], list[type[_BaseToolT]] | None, BaseCallKwargs, ]: @@ -50,6 +50,8 @@ def setup_call( if dynamic_config is not None: tools = dynamic_config.get("tools", tools) messages = dynamic_config.get("messages", None) + if messages is not None and not isinstance(messages, list): + messages = list(messages) dynamic_call_params = dynamic_config.get("call_params", None) if dynamic_call_params: call_kwargs |= dynamic_call_params diff --git a/mirascope/core/bedrock/_call_kwargs.py b/mirascope/core/bedrock/_call_kwargs.py index 5117e9406..826ec206b 100644 --- a/mirascope/core/bedrock/_call_kwargs.py +++ b/mirascope/core/bedrock/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Bedrock call keyword arguments.""" +from collections.abc import Sequence + from ..base import BaseCallKwargs from ._types import InternalBedrockMessageParam, ToolTypeDef from .call_params import BedrockCallParams @@ -7,4 +9,4 @@ class BedrockCallKwargs(BedrockCallParams, BaseCallKwargs[ToolTypeDef]): modelId: str - messages: list[InternalBedrockMessageParam] + messages: Sequence[InternalBedrockMessageParam] diff --git a/mirascope/core/gemini/_call_kwargs.py b/mirascope/core/gemini/_call_kwargs.py index c0a5d91be..37c212434 100644 --- a/mirascope/core/gemini/_call_kwargs.py +++ b/mirascope/core/gemini/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Gemini call keyword arguments.""" +from collections.abc import Sequence + from google.generativeai.types import ContentDict, Tool from ..base import BaseCallKwargs @@ -7,4 +9,4 @@ class GeminiCallKwargs(GeminiCallParams, BaseCallKwargs[Tool]): - contents: list[ContentDict] + contents: Sequence[ContentDict] diff --git a/mirascope/core/groq/_call_kwargs.py b/mirascope/core/groq/_call_kwargs.py index 1828bcf09..26398ec90 100644 --- a/mirascope/core/groq/_call_kwargs.py +++ b/mirascope/core/groq/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Groq call keyword arguments.""" +from collections.abc import Sequence + from groq.types.chat import ChatCompletionMessageParam, ChatCompletionToolParam from ..base import BaseCallKwargs @@ -8,4 +10,4 @@ class GroqCallKwargs(GroqCallParams, BaseCallKwargs[ChatCompletionToolParam]): model: str - messages: list[ChatCompletionMessageParam] + messages: Sequence[ChatCompletionMessageParam] diff --git a/mirascope/core/mistral/__init__.py b/mirascope/core/mistral/__init__.py index ed276f263..39ce1b4ed 100644 --- a/mirascope/core/mistral/__init__.py +++ b/mirascope/core/mistral/__init__.py @@ -2,7 +2,12 @@ from typing import TypeAlias -from mistralai.models.chat_completion import ChatMessage +from mistralai.models import ( + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +) from ..base import BaseMessageParam from ._call import mistral_call @@ -10,14 +15,15 @@ from .call_params import MistralCallParams from .call_response import MistralCallResponse from .call_response_chunk import MistralCallResponseChunk -from .dynamic_config import AsyncMistralDynamicConfig, MistralDynamicConfig +from .dynamic_config import MistralDynamicConfig from .stream import MistralStream from .tool import MistralTool -MistralMessageParam: TypeAlias = ChatMessage | BaseMessageParam +MistralMessageParam: TypeAlias = ( + AssistantMessage | SystemMessage | ToolMessage | UserMessage | BaseMessageParam +) __all__ = [ - "AsyncMistralDynamicConfig", "call", "MistralDynamicConfig", "MistralCallParams", diff --git a/mirascope/core/mistral/_call_kwargs.py b/mirascope/core/mistral/_call_kwargs.py index 69fd61374..550d68e6b 100644 --- a/mirascope/core/mistral/_call_kwargs.py +++ b/mirascope/core/mistral/_call_kwargs.py @@ -1,8 +1,14 @@ """This module contains the type definition for the Mistral call keyword arguments.""" +from collections.abc import Sequence from typing import Any -from mistralai.models.chat_completion import ChatMessage +from mistralai.models import ( + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +) from ..base import BaseCallKwargs from .call_params import MistralCallParams @@ -10,4 +16,4 @@ class MistralCallKwargs(MistralCallParams, BaseCallKwargs[dict[str, Any]]): model: str - messages: list[ChatMessage] + messages: Sequence[AssistantMessage | SystemMessage | ToolMessage | UserMessage] diff --git a/mirascope/core/mistral/_utils/_convert_message_params.py b/mirascope/core/mistral/_utils/_convert_message_params.py index 2d102b6c6..ae3f5c316 100644 --- a/mirascope/core/mistral/_utils/_convert_message_params.py +++ b/mirascope/core/mistral/_utils/_convert_message_params.py @@ -1,23 +1,45 @@ """Utility for converting `BaseMessageParam` to `ChatMessage`.""" -from mistralai.models.chat_completion import ChatMessage +from mistralai.models import ( + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +) from ...base import BaseMessageParam +def _make_message( + role: str, + **kwargs, # noqa: ANN003 +) -> AssistantMessage | SystemMessage | ToolMessage | UserMessage: + if role == "assistant": + return AssistantMessage(**kwargs) + elif role == "system": + return SystemMessage(**kwargs) + elif role == "tool": + return ToolMessage(**kwargs) + elif role == "user": + return UserMessage(**kwargs) + raise ValueError(f"Invalid role: {role}") + + def convert_message_params( - message_params: list[BaseMessageParam | ChatMessage], -) -> list[ChatMessage]: + message_params: list[ + BaseMessageParam | AssistantMessage | SystemMessage | ToolMessage | UserMessage + ], +) -> list[AssistantMessage | SystemMessage | ToolMessage | UserMessage]: converted_message_params = [] for message_param in message_params: - if isinstance(message_param, ChatMessage): + if not isinstance(message_param, BaseMessageParam): converted_message_params.append(message_param) elif isinstance(content := message_param.content, str): - converted_message_params.append(ChatMessage(**message_param.model_dump())) + converted_message_params.append(_make_message(**message_param.model_dump())) else: if len(content) != 1 or content[0].type != "text": raise ValueError("Mistral currently only supports text parts.") converted_message_params.append( - ChatMessage(role=message_param.role, content=content[0].text) + _make_message(role=message_param.role, content=content[0].text) ) return converted_message_params diff --git a/mirascope/core/mistral/_utils/_get_json_output.py b/mirascope/core/mistral/_utils/_get_json_output.py index 3e679c83b..447efed08 100644 --- a/mirascope/core/mistral/_utils/_get_json_output.py +++ b/mirascope/core/mistral/_utils/_get_json_output.py @@ -1,5 +1,7 @@ """Get the JSON output from a completion response.""" +from typing import cast + from ..call_response import MistralCallResponse from ..call_response_chunk import MistralCallResponseChunk @@ -11,8 +13,12 @@ def get_json_output( if isinstance(response, MistralCallResponse): if json_mode and response.content: return response.content - elif tool_calls := response.response.choices[0].message.tool_calls: - return tool_calls[0].function.arguments + elif ( + (choices := response.response.choices) + and choices + and (tool_calls := choices[0].message.tool_calls) + ): + return cast(str, tool_calls[0].function.arguments) raise ValueError("No tool call or JSON object found in response.") else: # raise ValueError("Mistral does not support structured streaming... :(") @@ -24,5 +30,5 @@ def get_json_output( and (function := tool_calls[0].function) and (arguments := function.arguments) is not None ): - return arguments + return cast(str, arguments) return "" diff --git a/mirascope/core/mistral/_utils/_handle_stream.py b/mirascope/core/mistral/_utils/_handle_stream.py index 60994ab37..df9c83afe 100644 --- a/mirascope/core/mistral/_utils/_handle_stream.py +++ b/mirascope/core/mistral/_utils/_handle_stream.py @@ -1,12 +1,12 @@ """Handles the stream of completion chunks.""" from collections.abc import AsyncGenerator, Generator +from typing import cast -from mistralai.models.chat_completion import ( - ChatCompletionStreamResponse, +from mistralai.models import ( + CompletionEvent, FunctionCall, ToolCall, - ToolType, ) from ..call_response_chunk import MistralCallResponseChunk @@ -14,7 +14,7 @@ def _handle_chunk( - chunk: ChatCompletionStreamResponse, + chunk: CompletionEvent, current_tool_call: ToolCall, current_tool_type: type[MistralTool] | None, tool_types: list[type[MistralTool]] | None, @@ -24,7 +24,7 @@ def _handle_chunk( type[MistralTool] | None, ]: """Handles a chunk of the stream.""" - if not tool_types or not (tool_calls := chunk.choices[0].delta.tool_calls): + if not tool_types or not (tool_calls := chunk.data.choices[0].delta.tool_calls): return None, current_tool_call, current_tool_type tool_call = tool_calls[0] @@ -38,7 +38,7 @@ def _handle_chunk( arguments="", name=tool_call.function.name if tool_call.function.name else "", ), - type=ToolType.function, + type="function", ) current_tool_type = None for tool_type in tool_types: @@ -58,30 +58,31 @@ def _handle_chunk( # Update arguments with each chunk if tool_call.function and tool_call.function.arguments: - current_tool_call.function.arguments += tool_call.function.arguments + current_tool_call.function.arguments += cast(str, tool_call.function.arguments) # pyright: ignore [reportOperatorIssue] return None, current_tool_call, current_tool_type def handle_stream( - stream: Generator[ChatCompletionStreamResponse, None, None], + stream: Generator[CompletionEvent, None, None], tool_types: list[type[MistralTool]] | None, ) -> Generator[tuple[MistralCallResponseChunk, MistralTool | None], None, None]: """Iterator over the stream and constructs tools as they are streamed.""" current_tool_call = ToolCall( - id="", function=FunctionCall(arguments="", name=""), type=ToolType.function + id="", function=FunctionCall(arguments="", name=""), type="function" ) current_tool_type = None + last_chuk_data = None for chunk in stream: - if not tool_types or not chunk.choices[0].delta.tool_calls: + if not tool_types or not chunk.data.choices[0].delta.tool_calls: if current_tool_type: yield ( - MistralCallResponseChunk(chunk=chunk), + MistralCallResponseChunk(chunk=chunk.data), current_tool_type.from_tool_call(current_tool_call), ) current_tool_type = None else: - yield MistralCallResponseChunk(chunk=chunk), None + yield MistralCallResponseChunk(chunk=chunk.data), None tool, current_tool_call, current_tool_type = _handle_chunk( chunk, current_tool_call, @@ -89,28 +90,36 @@ def handle_stream( tool_types, ) if tool is not None: - yield MistralCallResponseChunk(chunk=chunk), tool + yield MistralCallResponseChunk(chunk=chunk.data), tool + else: + last_chuk_data = chunk.data + if current_tool_type and last_chuk_data: + yield ( + MistralCallResponseChunk(chunk=last_chuk_data), + current_tool_type.from_tool_call(current_tool_call), + ) async def handle_stream_async( - stream: AsyncGenerator[ChatCompletionStreamResponse, None], + stream: AsyncGenerator[CompletionEvent, None], tool_types: list[type[MistralTool]] | None, ) -> AsyncGenerator[tuple[MistralCallResponseChunk, MistralTool | None], None]: """Async iterator over the stream and constructs tools as they are streamed.""" current_tool_call = ToolCall( - id="", function=FunctionCall(arguments="", name=""), type=ToolType.function + id="", function=FunctionCall(arguments="", name=""), type="function" ) current_tool_type = None + last_chuk_data = None async for chunk in stream: - if not tool_types or not chunk.choices[0].delta.tool_calls: + if not tool_types or not chunk.data.choices[0].delta.tool_calls: if current_tool_type: yield ( - MistralCallResponseChunk(chunk=chunk), + MistralCallResponseChunk(chunk=chunk.data), current_tool_type.from_tool_call(current_tool_call), ) current_tool_type = None else: - yield MistralCallResponseChunk(chunk=chunk), None + yield MistralCallResponseChunk(chunk=chunk.data), None tool, current_tool_call, current_tool_type = _handle_chunk( chunk, current_tool_call, @@ -118,4 +127,11 @@ async def handle_stream_async( tool_types, ) if tool is not None: - yield MistralCallResponseChunk(chunk=chunk), tool + yield MistralCallResponseChunk(chunk=chunk.data), tool + else: + last_chuk_data = chunk.data + if current_tool_type and last_chuk_data: + yield ( + MistralCallResponseChunk(chunk=last_chuk_data), + current_tool_type.from_tool_call(current_tool_call), + ) diff --git a/mirascope/core/mistral/_utils/_setup_call.py b/mirascope/core/mistral/_utils/_setup_call.py index f7f19c9e8..7ea289f82 100644 --- a/mirascope/core/mistral/_utils/_setup_call.py +++ b/mirascope/core/mistral/_utils/_setup_call.py @@ -1,29 +1,38 @@ """This module contains the setup_call function for Mistral tools.""" -import inspect +import os from collections.abc import ( Awaitable, Callable, ) from typing import Any, cast, overload -from mistralai.async_client import MistralAsyncClient -from mistralai.client import MistralClient -from mistralai.models.chat_completion import ( +from mistralai import Mistral +from mistralai.models import ( + AssistantMessage, ChatCompletionResponse, - ChatCompletionStreamResponse, - ChatMessage, + CompletionEvent, ResponseFormat, - ResponseFormats, - ToolChoice, + SystemMessage, + TextChunk, + ToolChoiceEnum, + ToolMessage, + UserMessage, ) -from ...base import BaseMessageParam, BaseTool, _utils -from ...base._utils import AsyncCreateFn, CreateFn, get_async_create_fn, get_create_fn +from ... import BaseMessageParam +from ...base import BaseTool, _utils +from ...base._utils import ( + AsyncCreateFn, + CreateFn, + fn_is_async, + get_async_create_fn, + get_create_fn, +) from ...base.call_params import CommonCallParams from .._call_kwargs import MistralCallKwargs from ..call_params import MistralCallParams -from ..dynamic_config import AsyncMistralDynamicConfig, MistralDynamicConfig +from ..dynamic_config import MistralDynamicConfig from ..tool import MistralTool from ._convert_common_call_params import convert_common_call_params from ._convert_message_params import convert_message_params @@ -33,19 +42,19 @@ def setup_call( *, model: str, - client: MistralAsyncClient | None, - fn: Callable[..., Awaitable[AsyncMistralDynamicConfig]], + client: Mistral | None, + fn: Callable[..., Awaitable[MistralDynamicConfig]], fn_args: dict[str, Any], - dynamic_config: AsyncMistralDynamicConfig, + dynamic_config: MistralDynamicConfig, tools: list[type[BaseTool] | Callable] | None, json_mode: bool, call_params: MistralCallParams | CommonCallParams, extract: bool, stream: bool, ) -> tuple[ - AsyncCreateFn[ChatCompletionResponse, ChatCompletionStreamResponse], + AsyncCreateFn[ChatCompletionResponse, CompletionEvent], str | None, - list[ChatMessage], + list[AssistantMessage | SystemMessage | ToolMessage | UserMessage], list[type[MistralTool]] | None, MistralCallKwargs, ]: ... @@ -55,7 +64,7 @@ def setup_call( def setup_call( *, model: str, - client: MistralClient | None, + client: Mistral | None, fn: Callable[..., MistralDynamicConfig], fn_args: dict[str, Any], dynamic_config: MistralDynamicConfig, @@ -65,9 +74,9 @@ def setup_call( extract: bool, stream: bool, ) -> tuple[ - CreateFn[ChatCompletionResponse, ChatCompletionStreamResponse], + CreateFn[ChatCompletionResponse, CompletionEvent], str | None, - list[ChatMessage], + list[AssistantMessage | SystemMessage | ToolMessage | UserMessage], list[type[MistralTool]] | None, MistralCallKwargs, ]: ... @@ -76,20 +85,20 @@ def setup_call( def setup_call( *, model: str, - client: MistralClient | MistralAsyncClient | None, - fn: Callable[..., MistralDynamicConfig | Awaitable[AsyncMistralDynamicConfig]], + client: Mistral | None, + fn: Callable[..., MistralDynamicConfig | Awaitable[MistralDynamicConfig]], fn_args: dict[str, Any], - dynamic_config: MistralDynamicConfig | AsyncMistralDynamicConfig, + dynamic_config: MistralDynamicConfig, tools: list[type[BaseTool] | Callable] | None, json_mode: bool, call_params: MistralCallParams | CommonCallParams, extract: bool, stream: bool, ) -> tuple[ - CreateFn[ChatCompletionResponse, ChatCompletionStreamResponse] - | AsyncCreateFn[ChatCompletionResponse, ChatCompletionStreamResponse], + CreateFn[ChatCompletionResponse, CompletionEvent] + | AsyncCreateFn[ChatCompletionResponse, CompletionEvent], str | None, - list[ChatMessage], + list[AssistantMessage | SystemMessage | ToolMessage | UserMessage], list[type[MistralTool]] | None, MistralCallKwargs, ]: @@ -103,31 +112,41 @@ def setup_call( convert_common_call_params, ) call_kwargs = cast(MistralCallKwargs, base_call_kwargs) - messages = cast(list[BaseMessageParam | ChatMessage], messages) + messages = cast( + list[ + BaseMessageParam + | AssistantMessage + | SystemMessage + | ToolMessage + | UserMessage + ], + messages, + ) messages = convert_message_params(messages) if json_mode: - call_kwargs["response_format"] = ResponseFormat( - type=ResponseFormats("json_object") - ) + call_kwargs["response_format"] = ResponseFormat(type="json_object") json_mode_content = _utils.json_mode_content( tool_types[0] if tool_types else None ) if messages[-1].role == "user": - messages[-1].content += json_mode_content + if isinstance(messages[-1].content, list): + messages[-1].content.append(TextChunk(text=json_mode_content)) + elif isinstance(messages[-1].content, str): + messages[-1].content += json_mode_content else: - messages.append(ChatMessage(role="user", content=json_mode_content.strip())) + messages.append(UserMessage(content=json_mode_content.strip())) call_kwargs.pop("tools", None) elif extract: assert tool_types, "At least one tool must be provided for extraction." - call_kwargs["tool_choice"] = cast(ToolChoice, ToolChoice.any) + call_kwargs["tool_choice"] = cast(ToolChoiceEnum, "any") call_kwargs |= {"model": model, "messages": messages} if client is None: - client = ( - MistralAsyncClient() if inspect.iscoroutinefunction(fn) else MistralClient() + client = Mistral(api_key=os.environ["MISTRAL_API_KEY"]) + if fn_is_async(fn): + create_or_stream = get_async_create_fn( + client.chat.complete_async, client.chat.stream_async ) - if isinstance(client, MistralAsyncClient): - create_or_stream = get_async_create_fn(client.chat, client.chat_stream) else: - create_or_stream = get_create_fn(client.chat, client.chat_stream) + create_or_stream = get_create_fn(client.chat.complete, client.chat.stream) return create_or_stream, prompt_template, messages, tool_types, call_kwargs diff --git a/mirascope/core/mistral/call_params.py b/mirascope/core/mistral/call_params.py index 4481c0017..47bac715f 100644 --- a/mirascope/core/mistral/call_params.py +++ b/mirascope/core/mistral/call_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from mistralai.models.chat_completion import ResponseFormat, ToolChoice +from mistralai.models import ResponseFormat, ToolChoice, ToolChoiceEnum from typing_extensions import NotRequired from ..base import BaseCallParams @@ -32,5 +32,5 @@ class MistralCallParams(BaseCallParams): safe_mode: NotRequired[bool | None] safe_prompt: NotRequired[bool | None] temperature: NotRequired[float | None] - tool_choice: NotRequired[ToolChoice | None] + tool_choice: NotRequired[ToolChoice | ToolChoiceEnum | None] top_p: NotRequired[float | None] diff --git a/mirascope/core/mistral/call_response.py b/mirascope/core/mistral/call_response.py index 8e1913677..1b329d4c8 100644 --- a/mirascope/core/mistral/call_response.py +++ b/mirascope/core/mistral/call_response.py @@ -3,16 +3,23 @@ usage docs: learn/calls.md#handling-responses """ -from typing import Any - -from mistralai.models.chat_completion import ChatCompletionResponse, ChatMessage -from mistralai.models.common import UsageInfo +from typing import Any, cast + +from mistralai import ChatCompletionChoice +from mistralai.models import ( + AssistantMessage, + ChatCompletionResponse, + SystemMessage, + ToolMessage, + UsageInfo, + UserMessage, +) from pydantic import computed_field from ..base import BaseCallResponse from ._utils import calculate_cost from .call_params import MistralCallParams -from .dynamic_config import AsyncMistralDynamicConfig, MistralDynamicConfig +from .dynamic_config import MistralDynamicConfig from .tool import MistralTool @@ -21,10 +28,10 @@ class MistralCallResponse( ChatCompletionResponse, MistralTool, dict[str, Any], - AsyncMistralDynamicConfig | MistralDynamicConfig, - ChatMessage, + MistralDynamicConfig, + AssistantMessage | SystemMessage | ToolMessage | UserMessage, MistralCallParams, - ChatMessage, + UserMessage, ] ): """A convenience wrapper around the Mistral `ChatCompletion` response. @@ -51,17 +58,21 @@ def recommend_book(genre: str) -> str: _provider = "mistral" + @property + def _response_choices(self) -> list[ChatCompletionChoice]: + return self.response.choices or [] + @property def content(self) -> str: """The content of the chat completion for the 0th choice.""" - return self.response.choices[0].message.content + return cast(str, self._response_choices[0].message.content) or "" @property def finish_reasons(self) -> list[str]: """Returns the finish reasons of the response.""" return [ choice.finish_reason if choice.finish_reason else "" - for choice in self.response.choices + for choice in self._response_choices ] @property @@ -96,9 +107,11 @@ def cost(self) -> float | None: @computed_field @property - def message_param(self) -> ChatMessage: + def message_param( + self, + ) -> AssistantMessage: """Returns the assistants's response as a message parameter.""" - return self.response.choices[0].message + return cast(AssistantMessage, self._response_choices[0].message) @computed_field @property @@ -108,7 +121,7 @@ def tools(self) -> list[MistralTool] | None: Raises: ValidationError: if the tool call doesn't match the tool's schema. """ - tool_calls = self.response.choices[0].message.tool_calls + tool_calls = self._response_choices[0].message.tool_calls if not self.tool_types or not tool_calls: return None @@ -136,7 +149,7 @@ def tool(self) -> MistralTool | None: @classmethod def tool_message_params( cls, tools_and_outputs: list[tuple[MistralTool, str]] - ) -> list[ChatMessage]: + ) -> list[ToolMessage]: """Returns the tool message parameters for tool call results. Args: @@ -147,8 +160,7 @@ def tool_message_params( The list of constructed `ChatMessage` parameters. """ return [ - ChatMessage( - role="tool", + ToolMessage( content=output, tool_call_id=tool.tool_call.id, name=tool._name(), diff --git a/mirascope/core/mistral/call_response_chunk.py b/mirascope/core/mistral/call_response_chunk.py index a496a5f95..4975bfa12 100644 --- a/mirascope/core/mistral/call_response_chunk.py +++ b/mirascope/core/mistral/call_response_chunk.py @@ -3,15 +3,12 @@ usage docs: learn/streams.md#handling-streamed-responses """ -from mistralai.models.chat_completion import ChatCompletionStreamResponse, FinishReason -from mistralai.models.common import UsageInfo +from mistralai.models import CompletionChunk, FinishReason, UsageInfo from ..base import BaseCallResponseChunk -class MistralCallResponseChunk( - BaseCallResponseChunk[ChatCompletionStreamResponse, FinishReason] -): +class MistralCallResponseChunk(BaseCallResponseChunk[CompletionChunk, FinishReason]): """A convenience wrapper around the Mistral `ChatCompletionChunk` streamed chunks. When calling the Mistral API using a function decorated with `mistral_call` and @@ -42,7 +39,10 @@ def content(self) -> str: delta = None if self.chunk.choices: delta = self.chunk.choices[0].delta - return delta.content if delta is not None and delta.content is not None else "" + + if delta is not None and isinstance(delta.content, str): + return delta.content + return "" @property def finish_reasons(self) -> list[FinishReason]: diff --git a/mirascope/core/mistral/dynamic_config.py b/mirascope/core/mistral/dynamic_config.py index 31bb32266..3a63d9f99 100644 --- a/mirascope/core/mistral/dynamic_config.py +++ b/mirascope/core/mistral/dynamic_config.py @@ -1,18 +1,20 @@ """This module defines the function return type for functions as LLM calls.""" -from mistralai.async_client import MistralAsyncClient -from mistralai.client import MistralClient -from mistralai.models.chat_completion import ChatMessage +from mistralai import Mistral +from mistralai.models import ( + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +) from ..base import BaseDynamicConfig, BaseMessageParam from .call_params import MistralCallParams -AsyncMistralDynamicConfig = BaseDynamicConfig[ - ChatMessage | BaseMessageParam, MistralCallParams, MistralAsyncClient -] - MistralDynamicConfig = BaseDynamicConfig[ - ChatMessage | BaseMessageParam, MistralCallParams, MistralClient + AssistantMessage | SystemMessage | ToolMessage | UserMessage | BaseMessageParam, + MistralCallParams, + Mistral, ] """The function return type for functions wrapped with the `mistral_call` decorator. diff --git a/mirascope/core/mistral/stream.py b/mirascope/core/mistral/stream.py index 282ab4596..ff3407b86 100644 --- a/mirascope/core/mistral/stream.py +++ b/mirascope/core/mistral/stream.py @@ -3,22 +3,25 @@ usage docs: learn/streams.md """ -from typing import Any +from typing import Any, cast -from mistralai.models.chat_completion import ( +from mistralai.models import ( + AssistantMessage, + ChatCompletionChoice, ChatCompletionResponse, - ChatCompletionResponseChoice, - ChatMessage, FinishReason, + SystemMessage, + ToolMessage, + UsageInfo, + UserMessage, ) -from mistralai.models.common import UsageInfo from ..base.stream import BaseStream from ._utils import calculate_cost from .call_params import MistralCallParams from .call_response import MistralCallResponse from .call_response_chunk import MistralCallResponseChunk -from .dynamic_config import AsyncMistralDynamicConfig, MistralDynamicConfig +from .dynamic_config import MistralDynamicConfig from .tool import MistralTool @@ -26,13 +29,13 @@ class MistralStream( BaseStream[ MistralCallResponse, MistralCallResponseChunk, - ChatMessage, - ChatMessage, - ChatMessage, - ChatMessage, + UserMessage, + AssistantMessage, + ToolMessage, + AssistantMessage | SystemMessage | ToolMessage | UserMessage, MistralTool, dict[str, Any], - AsyncMistralDynamicConfig | MistralDynamicConfig, + MistralDynamicConfig, MistralCallParams, FinishReason, ] @@ -66,9 +69,9 @@ def cost(self) -> float | None: def _construct_message_param( self, tool_calls: list | None = None, content: str | None = None - ) -> ChatMessage: - message_param = ChatMessage( - role="assistant", content=content if content else "", tool_calls=tool_calls + ) -> AssistantMessage: + message_param = AssistantMessage( + content=content if content else "", tool_calls=tool_calls ) return message_param @@ -87,13 +90,12 @@ def construct_call_response(self) -> MistralCallResponse: completion_tokens=int(self.output_tokens or 0), total_tokens=int(self.input_tokens or 0) + int(self.output_tokens or 0), ) + finish_reason = cast(FinishReason, (self.finish_reasons or [])[0]) completion = ChatCompletionResponse( id=self.id if self.id else "", choices=[ - ChatCompletionResponseChoice( - finish_reason=self.finish_reasons[0] - if self.finish_reasons - else None, + ChatCompletionChoice( + finish_reason=finish_reason, index=0, message=self.message_param, ) diff --git a/mirascope/core/mistral/tool.py b/mirascope/core/mistral/tool.py index bb67ce626..3d26f99cf 100644 --- a/mirascope/core/mistral/tool.py +++ b/mirascope/core/mistral/tool.py @@ -8,7 +8,7 @@ from typing import Any import jiter -from mistralai.models.chat_completion import ToolCall +from mistralai.models import ToolCall from pydantic.json_schema import SkipJsonSchema from ..base import BaseTool @@ -72,6 +72,8 @@ def from_tool_call(cls, tool_call: ToolCall) -> MistralTool: Args: tool_call: The Mistral tool call from which to construct this tool instance. """ - model_json = jiter.from_json(tool_call.function.arguments.encode()) + model_json = tool_call.function.arguments + if isinstance(model_json, str): + model_json = jiter.from_json(model_json.encode()) model_json["tool_call"] = tool_call.model_dump() return cls.model_validate(model_json) diff --git a/mirascope/core/openai/_call_kwargs.py b/mirascope/core/openai/_call_kwargs.py index d917af794..a30ee6802 100644 --- a/mirascope/core/openai/_call_kwargs.py +++ b/mirascope/core/openai/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the OpenAI call keyword arguments.""" +from collections.abc import Sequence + from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolParam from ..base import BaseCallKwargs @@ -8,4 +10,4 @@ class OpenAICallKwargs(OpenAICallParams, BaseCallKwargs[ChatCompletionToolParam]): model: str - messages: list[ChatCompletionMessageParam] + messages: Sequence[ChatCompletionMessageParam] diff --git a/mirascope/core/vertex/_call_kwargs.py b/mirascope/core/vertex/_call_kwargs.py index 003a8a08b..8e9381c72 100644 --- a/mirascope/core/vertex/_call_kwargs.py +++ b/mirascope/core/vertex/_call_kwargs.py @@ -1,5 +1,7 @@ """This module contains the type definition for the Vertex call keyword arguments.""" +from collections.abc import Sequence + from vertexai.generative_models import Content, Tool from ..base import BaseCallKwargs @@ -7,4 +9,4 @@ class VertexCallKwargs(VertexCallParams, BaseCallKwargs[Tool]): - contents: list[Content] + contents: Sequence[Content] diff --git a/pyproject.toml b/pyproject.toml index 5367df722..28be1b788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ hyperdx = ["hyperdx-opentelemetry>=0.1.0,<1"] langfuse = ["langfuse>=2.30.0,<3"] litellm = ["litellm>=1.42.12,<2"] logfire = ["logfire>=1.0.0,<3"] -mistral = ["mistralai>=0.4.2,<1"] +mistral = ["mistralai>=1.0.0,<2"] openai = ["openai>=1.6.0,<2"] opentelemetry = ["opentelemetry-api>=1.22.0,<2", "opentelemetry-sdk>=1.22.0,<2"] vertex = ["google-cloud-aiplatform>=1.38.0,<2"] diff --git a/tests/core/base/_utils/test_setup_call.py b/tests/core/base/_utils/test_setup_call.py index aa938312b..7b647d662 100644 --- a/tests/core/base/_utils/test_setup_call.py +++ b/tests/core/base/_utils/test_setup_call.py @@ -2,6 +2,8 @@ from typing import cast +import pytest + from mirascope.core.base import BaseCallParams, CommonCallParams from mirascope.core.base._utils._setup_call import setup_call from mirascope.core.base.dynamic_config import BaseDynamicConfig @@ -110,15 +112,16 @@ def convert_common_call_params(common_params: CommonCallParams) -> BaseCallParam } -def test_setup_call_with_custom_messages() -> None: +@pytest.mark.parametrize("messages_type", [list, tuple]) +def test_setup_call_with_custom_messages(messages_type) -> None: """Tests the `setup_call` function with custom messages.""" - custom_messages = [ + custom_messages = messages_type( { "role": "user", "content": [{"type": "text", "text": "Recommend a fantasy book."}], } - ] + ) dynamic_config: BaseDynamicConfig = {"messages": custom_messages} def fn() -> None: @@ -138,7 +141,7 @@ def convert_common_call_params(common_params: CommonCallParams) -> BaseCallParam convert_common_call_params, # pyright: ignore [reportArgumentType] ) assert template is None - assert custom_messages == messages + assert list(custom_messages) == messages assert tool_types is None assert call_kwargs == {} diff --git a/tests/core/mistral/_utils/test_convert_message_params.py b/tests/core/mistral/_utils/test_convert_message_params.py index 297836eeb..01e1d5b82 100644 --- a/tests/core/mistral/_utils/test_convert_message_params.py +++ b/tests/core/mistral/_utils/test_convert_message_params.py @@ -1,7 +1,13 @@ """Tests the `mistral._utils.convert_message_params` function.""" import pytest -from mistralai.models.chat_completion import ChatMessage +from mistralai.models import ( + AssistantMessage, + SystemMessage, + ToolMessage, + UserMessage, +) +from mistralai.types.basemodel import Unset from mirascope.core.base import AudioPart, BaseMessageParam, ImagePart, TextPart from mirascope.core.mistral._utils._convert_message_params import convert_message_params @@ -10,16 +16,34 @@ def test_convert_message_params() -> None: """Tests the `convert_message_params` function.""" - message_params: list[BaseMessageParam | ChatMessage] = [ - ChatMessage(role="user", content="Hello"), + message_params: list[ + BaseMessageParam | AssistantMessage | SystemMessage | ToolMessage | UserMessage + ] = [ + UserMessage(content="Hello"), BaseMessageParam(role="user", content="Hello"), BaseMessageParam(role="user", content=[TextPart(type="text", text="Hello")]), + BaseMessageParam(role="assistant", content="Hello"), + BaseMessageParam(role="system", content="Hello"), + BaseMessageParam(role="tool", content="Hello"), + AssistantMessage( + content="Hello", tool_calls=Unset(), prefix=False, role="assistant" + ), + SystemMessage(content="Hello", role="system"), + ToolMessage(content="Hello", tool_call_id=Unset(), name=Unset(), role="tool"), ] converted_message_params = convert_message_params(message_params) assert converted_message_params == [ - ChatMessage(role="user", content="Hello"), - ChatMessage(role="user", content="Hello"), - ChatMessage(role="user", content="Hello"), + UserMessage(content="Hello"), + UserMessage(role="user", content="Hello"), + UserMessage(role="user", content="Hello"), + AssistantMessage(content="Hello"), + SystemMessage(content="Hello"), + ToolMessage(content="Hello", tool_call_id=Unset(), name=Unset(), role="tool"), + AssistantMessage( + content="Hello", tool_calls=Unset(), prefix=False, role="assistant" + ), + SystemMessage(content="Hello", role="system"), + ToolMessage(content="Hello"), ] with pytest.raises( @@ -60,3 +84,13 @@ def test_convert_message_params() -> None: ), ] ) + + with pytest.raises( + ValueError, + match="Invalid role: invalid_role", + ): + convert_message_params( + [ + BaseMessageParam(role="invalid_role", content="Hello"), + ] + ) diff --git a/tests/core/mistral/_utils/test_get_json_output.py b/tests/core/mistral/_utils/test_get_json_output.py index 9b7739fac..453791b74 100644 --- a/tests/core/mistral/_utils/test_get_json_output.py +++ b/tests/core/mistral/_utils/test_get_json_output.py @@ -1,18 +1,17 @@ """Tests the `mistral._utils.get_json_output` module.""" import pytest -from mistralai.models.chat_completion import ( +from mistralai.models import ( + AssistantMessage, + ChatCompletionChoice, ChatCompletionResponse, - ChatCompletionResponseChoice, - ChatCompletionResponseStreamChoice, - ChatCompletionStreamResponse, - ChatMessage, + CompletionChunk, + CompletionResponseStreamChoice, DeltaMessage, FunctionCall, ToolCall, - ToolType, + UsageInfo, ) -from mistralai.models.common import UsageInfo from mirascope.core.mistral._utils._get_json_output import get_json_output from mirascope.core.mistral.call_response import MistralCallResponse @@ -21,21 +20,20 @@ def test_get_json_output_call_response() -> None: """Tests the `get_json_output` function with a call response.""" + tool_call = ToolCall( id="id", function=FunctionCall( name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) choices = [ - ChatCompletionResponseChoice( + ChatCompletionChoice( index=0, - message=ChatMessage( - role="assistant", content="json_output", tool_calls=[tool_call] - ), - finish_reason=None, + message=AssistantMessage(content="json_output", tool_calls=[tool_call]), + finish_reason="stop", ) ] completion = ChatCompletionResponse( @@ -66,8 +64,8 @@ def test_get_json_output_call_response() -> None: == '{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}' ) - completion.choices[0].message.content = "" - completion.choices[0].message.tool_calls = None + completion.choices[0].message.content = "" # pyright: ignore [reportOptionalSubscript] + completion.choices[0].message.tool_calls = None # pyright: ignore [reportOptionalSubscript] with pytest.raises( ValueError, match="No tool call or JSON object found in response." ): @@ -82,16 +80,16 @@ def test_get_json_output_call_response_chunk() -> None: arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', name="function", ), - type=ToolType.function, + type="function", ) choices = [ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage(content="json_output", tool_calls=[tool_call]), finish_reason=None, ) ] - chunk = ChatCompletionStreamResponse( + chunk = CompletionChunk( id="id", model="mistral-large-latest", choices=choices, diff --git a/tests/core/mistral/_utils/test_handle_stream.py b/tests/core/mistral/_utils/test_handle_stream.py index 8f18e8594..f0a420166 100644 --- a/tests/core/mistral/_utils/test_handle_stream.py +++ b/tests/core/mistral/_utils/test_handle_stream.py @@ -1,14 +1,13 @@ """Tests the `mistral._utils.handle_stream` module.""" import pytest -from mistralai.models.chat_completion import ( - ChatCompletionResponseStreamChoice, - ChatCompletionStreamResponse, +from mistralai.models import ( + CompletionChunk, + CompletionEvent, + CompletionResponseStreamChoice, DeltaMessage, - FinishReason, FunctionCall, ToolCall, - ToolType, ) from mirascope.core.mistral._utils._handle_stream import ( @@ -29,7 +28,7 @@ def call(self) -> None: @pytest.fixture() -def mock_chunks() -> list[ChatCompletionStreamResponse]: +def mock_chunks() -> list[CompletionChunk]: """Returns a list of mock `ChatCompletionStreamResponse` instances.""" new_tool_call = ToolCall( @@ -38,7 +37,7 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: arguments="", name="FormatBook", ), - type=ToolType.function, + type="function", ) tool_call = ToolCall( id="null", @@ -46,13 +45,13 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', name="FormatBook", ), - type=ToolType.function, + type="function", ) return [ - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage(content="content", tool_calls=None), finish_reason=None, @@ -62,10 +61,10 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, @@ -78,10 +77,10 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, @@ -94,10 +93,10 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, @@ -110,10 +109,10 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, @@ -126,13 +125,13 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage(content=None, tool_calls=None), - finish_reason=FinishReason.tool_calls, + finish_reason="tool_calls", ) ], created=0, @@ -142,10 +141,59 @@ def mock_chunks() -> list[ChatCompletionStreamResponse]: ] -def test_handle_stream(mock_chunks: list[ChatCompletionStreamResponse]) -> None: +@pytest.fixture() +def mock_chunks_onetime_tools() -> list[CompletionChunk]: + """Returns a list of mock `ChatCompletionStreamResponse` instances.""" + + tool_call = ToolCall( + id="id", + function=FunctionCall( + arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', + name="FormatBook", + ), + type="function", + ) + return [ + CompletionChunk( + id="id", + choices=[ + CompletionResponseStreamChoice( + index=0, + delta=DeltaMessage(content="content", tool_calls=None), + finish_reason=None, + ) + ], + created=0, + model="mistral-large-latest", + object="chat.completion.chunk", + ), + CompletionChunk( + id="id", + choices=[ + CompletionResponseStreamChoice( + index=0, + delta=DeltaMessage( + content=None, + tool_calls=[tool_call], + ), + finish_reason=None, + ) + ], + created=0, + model="mistral-large-latest", + object="chat.completion.chunk", + ), + ] + + +def test_handle_stream(mock_chunks: list[CompletionChunk]) -> None: """Tests the `handle_stream` function.""" - result = list(handle_stream((c for c in mock_chunks), tool_types=[FormatBook])) + result = list( + handle_stream( + (CompletionEvent(data=c) for c in mock_chunks), tool_types=[FormatBook] + ) + ) # Check we get three tuples back. # (chunk, None), (chunk, FormatBook), (chunk, FormatBook) assert len(result) == 3 @@ -166,13 +214,13 @@ def test_handle_stream(mock_chunks: list[ChatCompletionStreamResponse]) -> None: @pytest.mark.asyncio async def test_handle_stream_async( - mock_chunks: list[ChatCompletionStreamResponse], + mock_chunks: list[CompletionChunk], ) -> None: """Tests the `handle_stream_async` function.""" async def generator(): for chunk in mock_chunks: - yield chunk + yield CompletionEvent(data=chunk) result = [] async for t in handle_stream_async(generator(), tool_types=[FormatBook]): @@ -193,3 +241,49 @@ async def generator(): and tool.model_dump(exclude={"tool_call"}) == {"title": "The Name of the Wind", "author": "Patrick Rothfuss"} ) + + +def test_handle_stream_onetime_tools(mock_chunks_onetime_tools) -> None: + """Tests the `handle_stream` function.""" + + result = list( + handle_stream( + (CompletionEvent(data=c) for c in mock_chunks_onetime_tools), + tool_types=[FormatBook], + ) + ) + # Check we get three tuples back. + # (chunk, None), (chunk, FormatBook), (chunk, FormatBook) + assert len(result) == 2 + assert result[0][1] is None + assert ( + (tool := result[1][1]) is not None + and isinstance(tool, FormatBook) + and tool.model_dump(exclude={"tool_call"}) + == {"title": "The Name of the Wind", "author": "Patrick Rothfuss"} + ) + + +@pytest.mark.asyncio +async def test_handle_stream_async_onetime_tools( + mock_chunks_onetime_tools, +) -> None: + """Tests the `handle_stream_async` function.""" + + async def generator(): + for chunk in mock_chunks_onetime_tools: + yield CompletionEvent(data=chunk) + + result = [] + async for t in handle_stream_async(generator(), tool_types=[FormatBook]): + result.append(t) + # Check we get three tuples back. + # (chunk, None), (chunk, FormatBook), (chunk, FormatBook) + assert len(result) == 2 + assert result[0][1] is None + assert ( + (tool := result[1][1]) is not None + and isinstance(tool, FormatBook) + and tool.model_dump(exclude={"tool_call"}) + == {"title": "The Name of the Wind", "author": "Patrick Rothfuss"} + ) diff --git a/tests/core/mistral/_utils/test_setup_call.py b/tests/core/mistral/_utils/test_setup_call.py index ae15feba2..6ec7a5b8d 100644 --- a/tests/core/mistral/_utils/test_setup_call.py +++ b/tests/core/mistral/_utils/test_setup_call.py @@ -3,12 +3,13 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from mistralai.async_client import MistralAsyncClient -from mistralai.models.chat_completion import ( +from mistralai import Chat, Mistral +from mistralai.models import ( + AssistantMessage, ChatCompletionResponse, - ChatCompletionStreamResponse, - ChatMessage, - ToolChoice, + CompletionChunk, + TextChunk, + UserMessage, ) from mirascope.core.mistral._utils._convert_common_call_params import ( @@ -27,11 +28,7 @@ def mock_base_setup_call() -> MagicMock: @patch( - "mirascope.core.mistral._utils._setup_call.MistralClient.chat_stream", - return_value=MagicMock(), -) -@patch( - "mirascope.core.mistral._utils._setup_call.MistralClient.chat", + "mirascope.core.mistral._utils._setup_call.Mistral", return_value=MagicMock(), ) @patch( @@ -42,19 +39,19 @@ def mock_base_setup_call() -> MagicMock: def test_setup_call( mock_utils: MagicMock, mock_convert_message_params: MagicMock, - mock_mistral_chat: MagicMock, - mock_mistral_chat_stream: MagicMock, + mock_mistral: MagicMock, mock_base_setup_call: MagicMock, ) -> None: """Tests the `setup_call` function.""" mock_utils.setup_call = mock_base_setup_call mock_chat_iterator = MagicMock() mock_chat_iterator.__iter__.return_value = ["chat"] - mock_mistral_chat_stream.return_value = mock_chat_iterator + mock_mistral.chat = MagicMock() + mock_mistral.chat.stream.return_value = mock_chat_iterator fn = MagicMock() create, prompt_template, messages, tool_types, call_kwargs = setup_call( model="mistral-large-latest", - client=None, + client=mock_mistral, fn=fn, fn_args={}, dynamic_config=None, @@ -76,9 +73,9 @@ def test_setup_call( ) assert messages == mock_convert_message_params.return_value create(stream=False, **call_kwargs) - mock_mistral_chat.assert_called_once_with(**call_kwargs) + mock_mistral.chat.complete.assert_called_once_with(**call_kwargs) stream = create(stream=True, **call_kwargs) - mock_mistral_chat_stream.assert_called_once_with(**call_kwargs) + mock_mistral.chat.stream.assert_called_once_with(**call_kwargs) assert next(stream) == "chat" # pyright: ignore [reportArgumentType] @@ -97,7 +94,7 @@ async def test_async_setup_call( mock_mistral_chat = AsyncMock(spec=ChatCompletionResponse) mock_mistral_chat.__name__ = "chat" - mock_stream_response = AsyncMock(spec=ChatCompletionStreamResponse) + mock_stream_response = AsyncMock(spec=CompletionChunk) mock_stream_response.text = "chat" class AsyncMockIterator: @@ -115,13 +112,16 @@ async def __anext__(self): mock_iterator = AsyncMockIterator([mock_stream_response]) - mock_client = AsyncMock(spec=MistralAsyncClient, name="mock_client") - mock_client.chat_stream.return_value = mock_iterator - mock_client.chat.return_value = mock_mistral_chat + mock_client = MagicMock(spec=Mistral, name="mock_client") + mock_client.chat = MagicMock(spec=Chat) + mock_client.chat.stream_async = AsyncMock() + mock_client.chat.stream_async.return_value = mock_iterator + mock_client.chat.complete_async = AsyncMock() + mock_client.chat.complete_async.return_value = mock_mistral_chat mock_utils.setup_call = mock_base_setup_call - fn = MagicMock() + fn = AsyncMock() create, prompt_template, messages, tool_types, call_kwargs = setup_call( model="mistral-large-latest", client=mock_client, @@ -145,7 +145,6 @@ async def __anext__(self): mock_base_setup_call.return_value[1] ) - mock_mistral_chat.return_value = MagicMock(spec=ChatCompletionResponse) chat = await create(stream=False, **call_kwargs) stream = await create(stream=True, **call_kwargs) result = [] @@ -156,6 +155,26 @@ async def __anext__(self): assert isinstance(stream, AsyncMockIterator) +@pytest.mark.parametrize( + "base_messages,expected_last_message", + [ + ( + [UserMessage(content="test")], + UserMessage(content="test\n\njson_mode_content"), + ), + ( + [UserMessage(content=[TextChunk(text="test")])], + UserMessage( + content=[ + TextChunk(text="test", TYPE="text"), + TextChunk(text="\n\njson_mode_content", TYPE="text"), + ] + ), + ), + ([AssistantMessage(content="test")], UserMessage(content="json_mode_content")), + ], + ids=["string_content", "list_content", "assistant_message"], +) @patch( "mirascope.core.mistral._utils._setup_call.convert_message_params", new_callable=MagicMock, @@ -165,33 +184,28 @@ def test_setup_call_json_mode( mock_utils: MagicMock, mock_convert_message_params: MagicMock, mock_base_setup_call: MagicMock, + base_messages, + expected_last_message, ) -> None: - """Tests the `setup_call` function with JSON mode.""" + """Tests the setup_call function with JSON mode for different message types and content formats. + + Args: + mock_utils: Mock for utilities module + mock_convert_message_params: Mock for message parameter conversion + mock_base_setup_call: Mock for base setup call + base_messages: Initial messages to test + expected_last_message: Expected format of the last message after processing + """ + # Setup mocks mock_utils.setup_call = mock_base_setup_call mock_json_mode_content = MagicMock() mock_json_mode_content.return_value = "\n\njson_mode_content" mock_utils.json_mode_content = mock_json_mode_content - mock_base_setup_call.return_value[1] = [ChatMessage(role="user", content="test")] + mock_base_setup_call.return_value[1] = base_messages mock_base_setup_call.return_value[-1]["tools"] = MagicMock() mock_convert_message_params.side_effect = lambda x: x - _, _, messages, _, call_kwargs = setup_call( - model="mistral-large-latest", - client=None, - fn=MagicMock(), - fn_args={}, - dynamic_config=None, - tools=None, - json_mode=True, - call_params={}, - extract=False, - stream=False, - ) - assert messages[-1].content == "test\n\njson_mode_content" - assert "tools" not in call_kwargs - mock_base_setup_call.return_value[1] = [ - ChatMessage(role="assistant", content="test"), - ] + # Execute setup_call _, _, messages, _, call_kwargs = setup_call( model="mistral-large-latest", client=None, @@ -204,7 +218,10 @@ def test_setup_call_json_mode( extract=False, stream=False, ) - assert messages[-1] == ChatMessage(role="user", content="json_mode_content") + + # Verify results + assert messages[-1] == expected_last_message + assert "tools" not in call_kwargs @patch( @@ -231,4 +248,4 @@ def test_setup_call_extract( extract=True, stream=False, ) - assert "tool_choice" in call_kwargs and call_kwargs["tool_choice"] == ToolChoice.any + assert "tool_choice" in call_kwargs and call_kwargs["tool_choice"] == "any" diff --git a/tests/core/mistral/test_call_response.py b/tests/core/mistral/test_call_response.py index 03e904dad..9157f8535 100644 --- a/tests/core/mistral/test_call_response.py +++ b/tests/core/mistral/test_call_response.py @@ -1,15 +1,14 @@ """Tests the `mistral.call_response` module.""" -from mistralai.models.chat_completion import ( +from mistralai.models import ( + AssistantMessage, + ChatCompletionChoice, ChatCompletionResponse, - ChatCompletionResponseChoice, - ChatMessage, - FinishReason, FunctionCall, ToolCall, - ToolType, + ToolMessage, + UsageInfo, ) -from mistralai.models.common import UsageInfo from mirascope.core.mistral.call_response import MistralCallResponse from mirascope.core.mistral.tool import MistralTool @@ -18,10 +17,10 @@ def test_mistral_call_response() -> None: """Tests the `MistralCallResponse` class.""" choices = [ - ChatCompletionResponseChoice( + ChatCompletionChoice( index=0, - message=ChatMessage(role="assistant", content="content"), - finish_reason=FinishReason.stop, + message=AssistantMessage(content="content"), + finish_reason="stop", ) ] usage = UsageInfo(prompt_tokens=1, completion_tokens=1, total_tokens=2) @@ -56,9 +55,7 @@ def test_mistral_call_response() -> None: assert call_response.input_tokens == 1 assert call_response.output_tokens == 1 assert call_response.cost == 1.2e-5 - assert call_response.message_param == ChatMessage( - role="assistant", content="content" - ) + assert call_response.message_param == AssistantMessage(content="content") assert call_response.tools is None assert call_response.tool is None @@ -79,17 +76,15 @@ def call(self) -> str: name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) completion = ChatCompletionResponse( id="id", choices=[ - ChatCompletionResponseChoice( - finish_reason=FinishReason.stop, + ChatCompletionChoice( + finish_reason="stop", index=0, - message=ChatMessage( - role="assistant", content="content", tool_calls=[tool_call] - ), + message=AssistantMessage(content="content", tool_calls=[tool_call]), ) ], created=0, @@ -120,7 +115,7 @@ def call(self) -> str: output = tool.call() assert output == "The Name of the Wind by Patrick Rothfuss" assert call_response.tool_message_params([(tool, output)]) == [ - ChatMessage( + ToolMessage( role="tool", content=output, tool_call_id=tool_call.id, diff --git a/tests/core/mistral/test_call_response_chunk.py b/tests/core/mistral/test_call_response_chunk.py index 586bbba94..933e3928e 100644 --- a/tests/core/mistral/test_call_response_chunk.py +++ b/tests/core/mistral/test_call_response_chunk.py @@ -1,15 +1,13 @@ """Tests the `mistral.call_response_chunk` module.""" -from mistralai.models.chat_completion import ( - ChatCompletionResponseStreamChoice, - ChatCompletionStreamResponse, +from mistralai.models import ( + CompletionChunk, + CompletionResponseStreamChoice, DeltaMessage, - FinishReason, FunctionCall, ToolCall, - ToolType, + UsageInfo, ) -from mistralai.models.common import UsageInfo from mirascope.core.mistral.call_response_chunk import MistralCallResponseChunk @@ -19,17 +17,17 @@ def test_mistral_call_response_chunk() -> None: tool_call = ToolCall( id="id", function=FunctionCall(name="function", arguments='{"key": "value"}'), - type=ToolType.function, + type="function", ) choices = [ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage(content="content", tool_calls=[tool_call]), - finish_reason=FinishReason.stop, + finish_reason="stop", ) ] usage = UsageInfo(prompt_tokens=1, completion_tokens=1, total_tokens=2) - chunk = ChatCompletionStreamResponse( + chunk = CompletionChunk( id="id", choices=choices, created=0, @@ -49,7 +47,7 @@ def test_mistral_call_response_chunk() -> None: def test_mistral_call_response_chunk_no_choices_or_usage() -> None: """Tests the `MistralCallResponseChunk` class with None values.""" - chunk = ChatCompletionStreamResponse( + chunk = CompletionChunk( id="id", choices=[], created=0, diff --git a/tests/core/mistral/test_stream.py b/tests/core/mistral/test_stream.py index ad6e2cef1..dbfa0854c 100644 --- a/tests/core/mistral/test_stream.py +++ b/tests/core/mistral/test_stream.py @@ -1,19 +1,17 @@ """Tests the `mistral.stream` module.""" import pytest -from mistralai.models.chat_completion import ( +from mistralai import AssistantMessage +from mistralai.models import ( + ChatCompletionChoice, ChatCompletionResponse, - ChatCompletionResponseChoice, - ChatCompletionResponseStreamChoice, - ChatCompletionStreamResponse, - ChatMessage, + CompletionChunk, + CompletionResponseStreamChoice, DeltaMessage, - FinishReason, FunctionCall, ToolCall, - ToolType, + UsageInfo, ) -from mistralai.models.common import UsageInfo from mirascope.core.mistral.call_response import MistralCallResponse from mirascope.core.mistral.call_response_chunk import MistralCallResponseChunk @@ -40,14 +38,14 @@ def call(self) -> None: name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) usage = UsageInfo(prompt_tokens=1, completion_tokens=1, total_tokens=2) chunks = [ - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( delta=DeltaMessage(content="content", tool_calls=None), index=0, finish_reason=None, @@ -57,10 +55,10 @@ def call(self) -> None: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, @@ -84,7 +82,7 @@ def generator(): tool_call = ToolCall( id="id", function=FunctionCall(**tool_calls[0].function.model_dump()), - type=ToolType.function, + type="function", ) yield ( call_response_chunk, @@ -124,12 +122,11 @@ def generator(): name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) ) assert format_book.tool_call is not None - assert stream.message_param == ChatMessage( - role="assistant", + assert stream.message_param == AssistantMessage( content="content", tool_calls=[format_book.tool_call], ) @@ -151,14 +148,14 @@ def call(self) -> None: name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) usage = UsageInfo(prompt_tokens=1, completion_tokens=1, total_tokens=2) chunks = [ - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( delta=DeltaMessage(content="content", tool_calls=None), index=0, finish_reason=None, @@ -168,16 +165,16 @@ def call(self) -> None: model="mistral-large-latest", object="chat.completion.chunk", ), - ChatCompletionStreamResponse( + CompletionChunk( id="id", choices=[ - ChatCompletionResponseStreamChoice( + CompletionResponseStreamChoice( index=0, delta=DeltaMessage( content=None, tool_calls=[tool_call_delta], ), - finish_reason=FinishReason.stop, + finish_reason="stop", ) ], created=0, @@ -195,7 +192,7 @@ def generator(): tool_call = ToolCall( id="id", function=FunctionCall(**tool_calls[0].function.model_dump()), - type=ToolType.function, + type="function", ) yield ( call_response_chunk, @@ -227,17 +224,15 @@ def generator(): name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) completion = ChatCompletionResponse( id="id", choices=[ - ChatCompletionResponseChoice( - finish_reason=FinishReason.stop, + ChatCompletionChoice( + finish_reason="stop", index=0, - message=ChatMessage( - role="assistant", content="content", tool_calls=[tool_call] - ), + message=AssistantMessage(content="content", tool_calls=[tool_call]), ) ], created=0, diff --git a/tests/core/mistral/test_tool.py b/tests/core/mistral/test_tool.py index 26719a6f4..e9a547cf7 100644 --- a/tests/core/mistral/test_tool.py +++ b/tests/core/mistral/test_tool.py @@ -1,6 +1,6 @@ """Tests the `mistral.tool` module.""" -from mistralai.models.chat_completion import FunctionCall, ToolCall, ToolType +from mistralai.models import FunctionCall, ToolCall from mirascope.core.base.tool import BaseTool from mirascope.core.mistral.tool import MistralTool @@ -24,7 +24,7 @@ def call(self) -> str: name="FormatBook", arguments='{"title": "The Name of the Wind", "author": "Patrick Rothfuss"}', ), - type=ToolType.function, + type="function", ) tool = FormatBook.from_tool_call(tool_call) diff --git a/uv.lock b/uv.lock index 5a66bb594..44b08dbd5 100644 --- a/uv.lock +++ b/uv.lock @@ -13,10 +13,14 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_system == 'Windows' and sys_platform == 'linux'", "python_full_version == '3.11.*' and platform_system != 'Windows' and sys_platform != 'linux'", "python_full_version == '3.11.*' and platform_system == 'Windows' and sys_platform != 'linux'", - "python_full_version >= '3.12' and platform_system != 'Windows' and sys_platform == 'linux'", - "python_full_version >= '3.12' and platform_system == 'Windows' and sys_platform == 'linux'", - "python_full_version >= '3.12' and platform_system != 'Windows' and sys_platform != 'linux'", - "python_full_version >= '3.12' and platform_system == 'Windows' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and platform_system != 'Windows' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_system != 'Windows' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_system == 'Windows' and sys_platform == 'linux'", + "python_full_version >= '3.13' and platform_system == 'Windows' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_system != 'Windows' and sys_platform != 'linux'", + "python_full_version >= '3.13' and platform_system != 'Windows' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and platform_system == 'Windows' and sys_platform != 'linux'", + "python_full_version >= '3.13' and platform_system == 'Windows' and sys_platform != 'linux'", ] [[package]] @@ -1212,6 +1216,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4c/a3/ac312faeceffd2d8f86bc6dcb5c401188ba5a01bc88e69bed97578a0dfcd/durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38", size = 3461 }, ] +[[package]] +name = "eval-type-backport" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/ca/1601a9fa588867fe2ab6c19ed4c936929160d08a86597adf61bbd443fe57/eval_type_backport-0.2.0.tar.gz", hash = "sha256:68796cfbc7371ebf923f03bdf7bef415f3ec098aeced24e054b253a0e78f7b37", size = 8977 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/ac/aa3d8e0acbcd71140420bc752d7c9779cf3a2a3bb1d7ef30944e38b2cd39/eval_type_backport-0.2.0-py3-none-any.whl", hash = "sha256:ac2f73d30d40c5a30a80b8739a789d6bb5e49fdffa66d7912667e2015d9c9933", size = 5855 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -2299,6 +2312,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/3c/4f8791ee53ab9eeb0b022205aa79387119a74cc9429582ce04098e6fc540/json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f", size = 30109 }, ] +[[package]] +name = "jsonpath-python" +version = "1.0.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/49/e582e50b0c54c1b47e714241c4a4767bf28758bf90212248aea8e1ce8516/jsonpath-python-1.0.6.tar.gz", hash = "sha256:dd5be4a72d8a2995c3f583cf82bf3cd1a9544cfdabf2d22595b67aff07349666", size = 18121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8a/d63959f4eff03893a00e6e63592e3a9f15b9266ed8e0275ab77f8c7dbc94/jsonpath_python-1.0.6-py3-none-any.whl", hash = "sha256:1e3b78df579f5efc23565293612decee04214609208a2335884b3ee3f786b575", size = 7552 }, +] + [[package]] name = "jsonpointer" version = "3.0.0" @@ -3238,7 +3260,7 @@ requires-dist = [ { name = "langfuse", marker = "extra == 'langfuse'", specifier = ">=2.30.0,<3" }, { name = "litellm", marker = "extra == 'litellm'", specifier = ">=1.42.12,<2" }, { name = "logfire", marker = "extra == 'logfire'", specifier = ">=1.0.0,<3" }, - { name = "mistralai", marker = "extra == 'mistral'", specifier = ">=0.4.2,<1" }, + { name = "mistralai", marker = "extra == 'mistral'", specifier = ">=1.0.0,<2" }, { name = "numpy", marker = "extra == 'realtime'", specifier = ">=1.26.4,<2" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.6.0,<2" }, { name = "opentelemetry-api", marker = "extra == 'opentelemetry'", specifier = ">=1.22.0,<2" }, @@ -3290,16 +3312,19 @@ dev = [ [[package]] name = "mistralai" -version = "0.4.2" +version = "1.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "eval-type-backport" }, { name = "httpx" }, - { name = "orjson" }, + { name = "jsonpath-python" }, { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-inspect" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/20/4204f461588310b3a7ffbbbb7fa573493dc1c8185d376ee72516c04575bf/mistralai-0.4.2.tar.gz", hash = "sha256:5eb656710517168ae053f9847b0bb7f617eda07f1f93f946ad6c91a4d407fd93", size = 14234 } +sdist = { url = "https://files.pythonhosted.org/packages/99/60/098c39cdbd994a76b8d36966bc3719b83f7d75b9459fd532b1d5944cb8d4/mistralai-1.2.3.tar.gz", hash = "sha256:096b1406f62d8262d06d3f2f826714b2da87540c9e8d829864702918149c3615", size = 129710 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/fe/79dad76b8d94b62d9e2aab8446183190e1dc384c617d06c3c93307850e11/mistralai-0.4.2-py3-none-any.whl", hash = "sha256:63c98eea139585f0a3b2c4c6c09c453738bac3958055e6f2362d3866e96b0168", size = 20334 }, + { url = "https://files.pythonhosted.org/packages/8c/d7/629cdb94c53e37ac2c4d5955ca1555ea675af339baca8aff18e54a7998ec/mistralai-1.2.3-py3-none-any.whl", hash = "sha256:23902852829d1961f73cf1ecd387e8940f909f5b507c5f7fd32c7dae1a033119", size = 256913 }, ] [[package]] @@ -3713,6 +3738,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/07/8cbb75d6cfbe8712d8f7f6a5615f083c6e710ab916b748fbb20373ddb142/multiprocess-0.70.17-py311-none-any.whl", hash = "sha256:2884701445d0177aec5bd5f6ee0df296773e4fb65b11903b94c613fb46cfb7d1", size = 144346 }, { url = "https://files.pythonhosted.org/packages/a4/69/d3f343a61a2f86ef10ed7865a26beda7c71554136ce187b0384b1c2c9ca3/multiprocess-0.70.17-py312-none-any.whl", hash = "sha256:2818af14c52446b9617d1b0755fa70ca2f77c28b25ed97bdaa2c69a22c47b46c", size = 147990 }, { url = "https://files.pythonhosted.org/packages/c8/b7/2e9a4fcd871b81e1f2a812cd5c6fb52ad1e8da7bf0d7646c55eaae220484/multiprocess-0.70.17-py313-none-any.whl", hash = "sha256:20c28ca19079a6c879258103a6d60b94d4ffe2d9da07dda93fb1c8bc6243f522", size = 149843 }, + { url = "https://files.pythonhosted.org/packages/ae/d7/fd7a092fc0ab1845a1a97ca88e61b9b7cc2e9d6fcf0ed24e9480590c2336/multiprocess-0.70.17-py38-none-any.whl", hash = "sha256:1d52f068357acd1e5bbc670b273ef8f81d57863235d9fbf9314751886e141968", size = 132635 }, + { url = "https://files.pythonhosted.org/packages/f9/41/0618ac724b8a56254962c143759e04fa01c73b37aa69dd433f16643bd38b/multiprocess-0.70.17-py39-none-any.whl", hash = "sha256:c3feb874ba574fbccfb335980020c1ac631fbf2a3f7bee4e2042ede62558a021", size = 133359 }, ] [[package]] @@ -4651,8 +4678,6 @@ version = "6.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/c7/8c6872f7372eb6a6b2e4708b88419fb46b857f7a2e1892966b851cc79fc9/psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2", size = 508067 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/66/78c9c3020f573c58101dc43a44f6855d01bbbd747e24da2f0c4491200ea3/psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35", size = 249766 }, - { url = "https://files.pythonhosted.org/packages/e1/3f/2403aa9558bea4d3854b0e5e567bc3dd8e9fbc1fc4453c0aa9aafeb75467/psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1", size = 253024 }, { url = "https://files.pythonhosted.org/packages/0b/37/f8da2fbd29690b3557cca414c1949f92162981920699cd62095a984983bf/psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0", size = 250961 }, { url = "https://files.pythonhosted.org/packages/35/56/72f86175e81c656a01c4401cd3b1c923f891b31fbcebe98985894176d7c9/psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0", size = 287478 }, { url = "https://files.pythonhosted.org/packages/19/74/f59e7e0d392bc1070e9a70e2f9190d652487ac115bb16e2eff6b22ad1d24/psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd", size = 290455 }, @@ -4744,73 +4769,83 @@ wheels = [ [[package]] name = "pydantic" -version = "2.7.4" +version = "2.9.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/fc/ccd0e8910bc780f1a4e1ab15e97accbb1f214932e796cff3131f9a943967/pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52", size = 714127 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/ba/1b65c9cbc49e0c7cd1be086c63209e9ad883c2a409be4746c21db4263f41/pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0", size = 409017 }, + { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, ] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.23.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/d0/622cdfe12fb138d035636f854eb9dc414f7e19340be395799de87c1de6f6/pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864", size = 385098 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/73/af096181c7aeaf087c23f6cb45a545a1bb5b48b6da2b6b2c0c2d7b34f166/pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4", size = 1852698 }, - { url = "https://files.pythonhosted.org/packages/d1/ef/cf649d5e67a6baf6f5a745f7848484dd72b3b08896c1643cc54685937e52/pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26", size = 1769961 }, - { url = "https://files.pythonhosted.org/packages/07/a1/a0156c29cf3ee6b7db7907baa2666be42603fe87f518eb6b98fd982906ba/pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a", size = 1791174 }, - { url = "https://files.pythonhosted.org/packages/ca/14/d885398b4402c76da93df7034f2baaba56abc3ed432696a2d3ccbf9806da/pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851", size = 1781666 }, - { url = "https://files.pythonhosted.org/packages/9a/a6/b06114fcde6ec41aa5be8dcae863b7badffa75fbd77a4aba0847df4448ff/pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d", size = 1979128 }, - { url = "https://files.pythonhosted.org/packages/5f/ac/2a0a53a5df1243b670b3250a78673eb135f13a0a23e55d8e1fd68c54e314/pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724", size = 2870427 }, - { url = "https://files.pythonhosted.org/packages/be/44/18eec2ac121e195662ac0f48c9c2a7bc9e2175edf408004b42adfadfc095/pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be", size = 2049121 }, - { url = "https://files.pythonhosted.org/packages/81/f3/0e4fac63e28d03e311d2b80e9aecbe7c42fbc72d5eab5c4cc89126f74dc7/pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb", size = 1906294 }, - { url = "https://files.pythonhosted.org/packages/83/0c/0b04bede6cfefe56702ae4ac9683d08d43e5ee59a03afdb8573949357e63/pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c", size = 2010452 }, - { url = "https://files.pythonhosted.org/packages/a5/a9/8812dc9e573037eae07a7e42c4acaf3f0ce4e3c0430413727594da702f11/pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e", size = 2115369 }, - { url = "https://files.pythonhosted.org/packages/90/21/823245989645d8e38aba47cafa2f783e88c367fc5822af53694c80acca97/pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc", size = 1718679 }, - { url = "https://files.pythonhosted.org/packages/5c/d8/13ac833cb5ec401fb69c5c21acc291dc54bf05749f3501bf17ffdcd79542/pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0", size = 1912106 }, - { url = "https://files.pythonhosted.org/packages/25/ba/f8106ec853ed1e288490c39796322cb54f96685e53a1b302c6d7d5d3528e/pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d", size = 1852003 }, - { url = "https://files.pythonhosted.org/packages/6c/a2/229dbee765ce711cd42c056da497ca26f7267384362e6767ab3bcdafca79/pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4", size = 1769636 }, - { url = "https://files.pythonhosted.org/packages/4d/cd/f29239ba9e23a4cbe27ebac22652f9c93b3f2122bd8b96bd51f72bbf7009/pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4", size = 1790366 }, - { url = "https://files.pythonhosted.org/packages/17/24/517cb7e5839754b0855c50c92cdd8715919d05a360b94dc5cf181d5fe60d/pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2", size = 1781086 }, - { url = "https://files.pythonhosted.org/packages/90/63/9bf4ff4e014942387cb18a9d60d3b32ac892df9581b2b09c0d3eadd88fab/pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd", size = 1978642 }, - { url = "https://files.pythonhosted.org/packages/d2/71/3c9fd032695746c63fd95ea2765709c12d02e5332c9cc3972fa335e9f254/pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d", size = 2868673 }, - { url = "https://files.pythonhosted.org/packages/d6/4b/51141c5ea1874e7204efac93899eefcb690ff6772a92c9a6521b32e5b71f/pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8", size = 2048418 }, - { url = "https://files.pythonhosted.org/packages/90/2b/c06dbed36bcb247412de81a20931e42ebf6ddb6f1cf3efb28c6539358aef/pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951", size = 1905908 }, - { url = "https://files.pythonhosted.org/packages/70/97/64968701368ff84049f5c916f63e39d6afc15cbdb188fd9f412cba359ff4/pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2", size = 2009496 }, - { url = "https://files.pythonhosted.org/packages/ad/07/25167289bfb595cbe2aba706aa5c50083cff618662776d696e8574f03fc5/pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9", size = 2114671 }, - { url = "https://files.pythonhosted.org/packages/51/a0/a62e80edc26575627f3cae3e450c5afe31ce28b5333762eb3a3bba996675/pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558", size = 1718138 }, - { url = "https://files.pythonhosted.org/packages/00/29/62236e5e19b92c1b908089589300e5834a8fcb5f870ae4f2a08b17782142/pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b", size = 1911303 }, - { url = "https://files.pythonhosted.org/packages/f5/cb/83c4c1059fd04d72849a010654be036bdc01b31718f539e9e5601950bf38/pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805", size = 1779815 }, - { url = "https://files.pythonhosted.org/packages/e0/37/a46d4741a88f7223ef3ec68490e638fb93570127f28fd76cbc2c89c0a7ec/pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2", size = 1846298 }, - { url = "https://files.pythonhosted.org/packages/e9/93/46f546b40aba72683032c167aef9ccd568a54e28f28687081d10a6735294/pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef", size = 1766852 }, - { url = "https://files.pythonhosted.org/packages/e5/84/864556db336393f2433eda50b7e047a8d485b95dd48d1d277750ce0042bf/pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695", size = 1786599 }, - { url = "https://files.pythonhosted.org/packages/79/7b/a4f002cf3603672de868ae913dec7d561509bcd17401f4cc719ab8a46213/pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34", size = 1769558 }, - { url = "https://files.pythonhosted.org/packages/bc/36/c3e7665d84f692b095d11e9f4af71bfc339471b7153d22a7526584b75b92/pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1", size = 1976988 }, - { url = "https://files.pythonhosted.org/packages/c8/91/4bbb89ef7aade8095bbb26055c16d8026a3bbe97f6ad9a827f8265c00350/pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077", size = 2792471 }, - { url = "https://files.pythonhosted.org/packages/d8/36/5676d758ccb008de9ae48db1614ffaa381e1971677e4a7e365cb7004e61c/pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3", size = 2082097 }, - { url = "https://files.pythonhosted.org/packages/a0/d7/05be10426e0a338a84dc2699c143d8d1a3ad2a79aefe0dd0ce17a7dea81d/pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9", size = 1902982 }, - { url = "https://files.pythonhosted.org/packages/73/c8/784718a31831eda4446ba3d5c9d9a259a10d01f71f9684300f1bbac65d2f/pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c", size = 2001850 }, - { url = "https://files.pythonhosted.org/packages/83/f8/9d59e352b3d168984b2e67e347365afe9698f08b2167e9582a03abf4f961/pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8", size = 2115674 }, - { url = "https://files.pythonhosted.org/packages/0f/98/fb23c03b5b96a4e6196d24ebc05026dbcd3ec05e9b0c85ae34a3c40f6d58/pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07", size = 1721997 }, - { url = "https://files.pythonhosted.org/packages/d9/c9/85bbe091e0f0f5a99f38cda2774b810ac593fd36dae2f9d777e215d2fa6b/pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a", size = 1905580 }, - { url = "https://files.pythonhosted.org/packages/35/ea/fba944f8e29860a3e1d535223427a657e7b29118e114188b3e2fda02c69e/pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f", size = 1781973 }, - { url = "https://files.pythonhosted.org/packages/d8/a2/60588397688bbc2f720c987691656e2d667b8b8776da1726bad2960a0889/pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d", size = 1848601 }, - { url = "https://files.pythonhosted.org/packages/35/22/cf65f4a902c3b5ff6fcbd159fa626f95d56aaff8c318952e23af179e7e25/pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab", size = 1727473 }, - { url = "https://files.pythonhosted.org/packages/61/48/d392f839c2183a0408ef5f3455ffd8ebc21f3df2fbd3eecd7c7a9eee0ac7/pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4", size = 1789270 }, - { url = "https://files.pythonhosted.org/packages/93/ea/a1f7f8ec6f85566fff4e5848622d39bf52bd4ce4cb9f3e5e5d7bc1fe78ba/pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc", size = 1939141 }, - { url = "https://files.pythonhosted.org/packages/f4/63/97d408a298a21e41585372add1f0a2d902a46c0f7b3c8e8386b22429bb17/pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507", size = 1903294 }, - { url = "https://files.pythonhosted.org/packages/c3/3f/9669fd933f5e344e811193438ba688f7abe0c64beddd8ee52fa53dad68d0/pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef", size = 2006230 }, - { url = "https://files.pythonhosted.org/packages/b0/8a/c8a2e60482eebc5c878faf7067e63ef532d40b01870292a7da40506b2d5f/pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d", size = 2109883 }, - { url = "https://files.pythonhosted.org/packages/f5/6e/b753bb42bc8aff4fd34c6816f2a17e5e059217512e224a2aa31a1b2f8f93/pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5", size = 1917020 }, +sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835 }, + { url = "https://files.pythonhosted.org/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689 }, + { url = "https://files.pythonhosted.org/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748 }, + { url = "https://files.pythonhosted.org/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469 }, + { url = "https://files.pythonhosted.org/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246 }, + { url = "https://files.pythonhosted.org/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404 }, + { url = "https://files.pythonhosted.org/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940 }, + { url = "https://files.pythonhosted.org/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437 }, + { url = "https://files.pythonhosted.org/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129 }, + { url = "https://files.pythonhosted.org/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908 }, + { url = "https://files.pythonhosted.org/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278 }, + { url = "https://files.pythonhosted.org/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453 }, + { url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160 }, + { url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777 }, + { url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244 }, + { url = "https://files.pythonhosted.org/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607", size = 1805307 }, + { url = "https://files.pythonhosted.org/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", size = 2000663 }, + { url = "https://files.pythonhosted.org/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", size = 2655941 }, + { url = "https://files.pythonhosted.org/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", size = 2052105 }, + { url = "https://files.pythonhosted.org/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", size = 1919967 }, + { url = "https://files.pythonhosted.org/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", size = 1964291 }, + { url = "https://files.pythonhosted.org/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", size = 2109666 }, + { url = "https://files.pythonhosted.org/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", size = 1732940 }, + { url = "https://files.pythonhosted.org/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", size = 1916804 }, + { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459 }, + { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007 }, + { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245 }, + { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260 }, + { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872 }, + { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617 }, + { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831 }, + { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453 }, + { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793 }, + { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, + { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, + { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, + { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, + { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, + { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, + { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, + { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, + { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, + { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, + { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, + { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, + { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, + { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, + { url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 }, + { url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 }, + { url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 }, + { url = "https://files.pythonhosted.org/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963 }, + { url = "https://files.pythonhosted.org/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332 }, + { url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926 }, + { url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342 }, + { url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344 }, ] [[package]] @@ -4960,14 +4995,14 @@ wheels = [ [[package]] name = "python-dateutil" -version = "2.9.0.post0" +version = "2.8.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", size = 357324 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9", size = 247702 }, ] [[package]] @@ -6144,7 +6179,7 @@ name = "triton" version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock", marker = "platform_system != 'Windows'" }, + { name = "filelock", marker = "python_full_version < '3.13' and platform_system != 'Windows'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/45/27/14cc3101409b9b4b9241d2ba7deaa93535a217a211c86c4cc7151fb12181/triton-3.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1efef76935b2febc365bfadf74bcb65a6f959a9872e5bddf44cc9e0adce1e1a", size = 209376304 },