Giter Club home page Giter Club logo

client-python's Introduction

Mistral Python Client

Migration warning

This documentation is for Mistral AI SDK v1. You can find more details on how to migrate from v0 to v1 here

API Key Setup

Before you begin, you will need a Mistral AI API key.

  1. Get your own Mistral API Key: https://docs.mistral.ai/#api-access
  2. Set your Mistral API Key as an environment variable. You only need to do this once.
# set Mistral API Key (using zsh for example)
$ echo 'export MISTRAL_API_KEY=[your_key_here]' >> ~/.zshenv

# reload the environment (or just quit and open a new terminal)
$ source ~/.zshenv

SDK Installation

PIP

pip install mistralai

Poetry

poetry add mistralai

SDK Example Usage

Create Chat Completions

This example shows how to create chat completions.

# Synchronous Example
from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.chat.complete(model="mistral-small-latest", messages=[
    {
        "content": "Who is the best French painter? Answer in one short sentence.",
        "role": "user",
    },
])

if res is not None:
    # handle response
    pass

The same SDK client can also be used to make asychronous requests by importing asyncio.

# Asynchronous Example
import asyncio
from mistralai import Mistral
import os

async def main():
    s = Mistral(
        api_key=os.getenv("MISTRAL_API_KEY", ""),
    )
    res = await s.chat.complete_async(model="mistral-small-latest", messages=[
        {
            "content": "Who is the best French painter? Answer in one short sentence.",
            "role": "user",
        },
    ])
    if res is not None:
        # handle response
        pass

asyncio.run(main())

Upload a file

This example shows how to upload a file.

# Synchronous Example
from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.files.upload(file={
    "file_name": "your_file_here",
    "content": open("<file_path>", "rb"),
})

if res is not None:
    # handle response
    pass

The same SDK client can also be used to make asychronous requests by importing asyncio.

# Asynchronous Example
import asyncio
from mistralai import Mistral
import os

async def main():
    s = Mistral(
        api_key=os.getenv("MISTRAL_API_KEY", ""),
    )
    res = await s.files.upload_async(file={
        "file_name": "your_file_here",
        "content": open("<file_path>", "rb"),
    })
    if res is not None:
        # handle response
        pass

asyncio.run(main())

Create Agents Completions

This example shows how to create agents completions.

# Synchronous Example
from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.agents.complete(messages=[
    {
        "content": "Who is the best French painter? Answer in one short sentence.",
        "role": "user",
    },
], agent_id="<value>")

if res is not None:
    # handle response
    pass

The same SDK client can also be used to make asychronous requests by importing asyncio.

# Asynchronous Example
import asyncio
from mistralai import Mistral
import os

async def main():
    s = Mistral(
        api_key=os.getenv("MISTRAL_API_KEY", ""),
    )
    res = await s.agents.complete_async(messages=[
        {
            "content": "Who is the best French painter? Answer in one short sentence.",
            "role": "user",
        },
    ], agent_id="<value>")
    if res is not None:
        # handle response
        pass

asyncio.run(main())

More examples

You can run the examples in the examples/ directory using poetry run or by entering the virtual environment using poetry shell.

Providers' SDKs Example Usage

Azure AI

Prerequisites

Before you begin, ensure you have AZUREAI_ENDPOINT and an AZURE_API_KEY. To obtain these, you will need to deploy Mistral on Azure AI. See instructions for deploying Mistral on Azure AI here.

Here's a basic example to get you started. You can also run the example in the examples directory.

import asyncio
import os

from mistralai_azure import MistralAzure

client = MistralAzure(
    azure_api_key=os.getenv("AZURE_API_KEY", ""),
    azure_endpoint=os.getenv("AZURE_ENDPOINT", "")
)

async def main() -> None:
    res = await client.chat.complete_async( 
        max_tokens= 100,
        temperature= 0.5,
        messages= [
            {
                "content": "Hello there!",
                "role": "user"
            }
        ]
    )
    print(res)

asyncio.run(main())

The documentation for the Azure SDK is available here.

Google Cloud

Prerequisites

Before you begin, you will need to create a Google Cloud project and enable the Mistral API. To do this, follow the instructions here.

To run this locally you will also need to ensure you are authenticated with Google Cloud. You can do this by running

gcloud auth application-default login

Step 1: Install

Install the extras dependencies specific to Google Cloud:

pip install mistralai[gcp]

Step 2: Example Usage

Here's a basic example to get you started.

import asyncio
from mistralai_gcp import MistralGoogleCloud

client = MistralGoogleCloud()


async def main() -> None:
    res = await client.chat.complete_async(
        model= "mistral-small-2402",
        messages= [
            {
                "content": "Hello there!",
                "role": "user"
            }
        ]
    )
    print(res)

asyncio.run(main())

The documentation for the GCP SDK is available here.

Available Resources and Operations

  • list - Get Fine Tuning Jobs
  • create - Create Fine Tuning Job
  • get - Get Fine Tuning Job
  • cancel - Cancel Fine Tuning Job
  • start - Start Fine Tuning Job

Server-sent event streaming

Server-sent events are used to stream content from certain operations. These operations will expose the stream as Generator that can be consumed using a simple for loop. The loop will terminate when the server no longer has any events to send and closes the underlying connection.

from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.chat.stream(model="mistral-small-latest", messages=[
    {
        "content": "Who is the best French painter? Answer in one short sentence.",
        "role": "user",
    },
])

if res is not None:
    for event in res:
        # handle event
        print(event, flush=True)

File uploads

Certain SDK methods accept file objects as part of a request body or multi-part request. It is possible and typically recommended to upload files as a stream rather than reading the entire contents into memory. This avoids excessive memory consumption and potentially crashing with out-of-memory errors when working with very large files. The following example demonstrates how to attach a file stream to a request.

Tip

For endpoints that handle file uploads bytes arrays can also be used. However, using streams is recommended for large files.

from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.files.upload(file={
    "file_name": "your_file_here",
    "content": open("<file_path>", "rb"),
})

if res is not None:
    # handle response
    pass

Retries

Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.

To change the default retry strategy for a single API call, simply provide a RetryConfig object to the call:

from mistral.utils import BackoffStrategy, RetryConfig
from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.models.list(,
    RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False))

if res is not None:
    # handle response
    pass

If you'd like to override the default retry strategy for all operations that support retries, you can use the retry_config optional parameter when initializing the SDK:

from mistral.utils import BackoffStrategy, RetryConfig
from mistralai import Mistral
import os

s = Mistral(
    retry_config=RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False),
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.models.list()

if res is not None:
    # handle response
    pass

Error Handling

Handling errors in this SDK should largely match your expectations. All operations return a response object or raise an error. If Error objects are specified in your OpenAPI Spec, the SDK will raise the appropriate Error type.

Error Object Status Code Content Type
models.HTTPValidationError 422 application/json
models.SDKError 4xx-5xx /

Example

from mistralai import Mistral, models
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)

res = None
try:
    res = s.models.list()

except models.HTTPValidationError as e:
    # handle exception
    raise(e)
except models.SDKError as e:
    # handle exception
    raise(e)

if res is not None:
    # handle response
    pass

Server Selection

Select Server by Name

You can override the default server globally by passing a server name to the server: str optional parameter when initializing the SDK client instance. The selected server will then be used as the default on the operations that use it. This table lists the names associated with the available servers:

Name Server Variables
prod https://api.mistral.ai None

Example

from mistralai import Mistral
import os

s = Mistral(
    server="prod",
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.models.list()

if res is not None:
    # handle response
    pass

Override Server URL Per-Client

The default server can also be overridden globally by passing a URL to the server_url: str optional parameter when initializing the SDK client instance. For example:

from mistralai import Mistral
import os

s = Mistral(
    server_url="https://api.mistral.ai",
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.models.list()

if res is not None:
    # handle response
    pass

Custom HTTP Client

The Python SDK makes API calls using the httpx HTTP library. In order to provide a convenient way to configure timeouts, cookies, proxies, custom headers, and other low-level configuration, you can initialize the SDK client with your own HTTP client instance. Depending on whether you are using the sync or async version of the SDK, you can pass an instance of HttpClient or AsyncHttpClient respectively, which are Protocol's ensuring that the client has the necessary methods to make API calls. This allows you to wrap the client with your own custom logic, such as adding custom headers, logging, or error handling, or you can just pass an instance of httpx.Client or httpx.AsyncClient directly.

For example, you could specify a header for every request that this sdk makes as follows:

from mistralai import Mistral
import httpx

http_client = httpx.Client(headers={"x-custom-header": "someValue"})
s = Mistral(client=http_client)

or you could wrap the client with your own custom logic:

from mistralai import Mistral
from mistralai.httpclient import AsyncHttpClient
import httpx

class CustomClient(AsyncHttpClient):
    client: AsyncHttpClient

    def __init__(self, client: AsyncHttpClient):
        self.client = client

    async def send(
        self,
        request: httpx.Request,
        *,
        stream: bool = False,
        auth: Union[
            httpx._types.AuthTypes, httpx._client.UseClientDefault, None
        ] = httpx.USE_CLIENT_DEFAULT,
        follow_redirects: Union[
            bool, httpx._client.UseClientDefault
        ] = httpx.USE_CLIENT_DEFAULT,
    ) -> httpx.Response:
        request.headers["Client-Level-Header"] = "added by client"

        return await self.client.send(
            request, stream=stream, auth=auth, follow_redirects=follow_redirects
        )

    def build_request(
        self,
        method: str,
        url: httpx._types.URLTypes,
        *,
        content: Optional[httpx._types.RequestContent] = None,
        data: Optional[httpx._types.RequestData] = None,
        files: Optional[httpx._types.RequestFiles] = None,
        json: Optional[Any] = None,
        params: Optional[httpx._types.QueryParamTypes] = None,
        headers: Optional[httpx._types.HeaderTypes] = None,
        cookies: Optional[httpx._types.CookieTypes] = None,
        timeout: Union[
            httpx._types.TimeoutTypes, httpx._client.UseClientDefault
        ] = httpx.USE_CLIENT_DEFAULT,
        extensions: Optional[httpx._types.RequestExtensions] = None,
    ) -> httpx.Request:
        return self.client.build_request(
            method,
            url,
            content=content,
            data=data,
            files=files,
            json=json,
            params=params,
            headers=headers,
            cookies=cookies,
            timeout=timeout,
            extensions=extensions,
        )

s = Mistral(async_client=CustomClient(httpx.AsyncClient()))

Authentication

Per-Client Security Schemes

This SDK supports the following security scheme globally:

Name Type Scheme Environment Variable
api_key http HTTP Bearer MISTRAL_API_KEY

To authenticate with the API the api_key parameter must be set when initializing the SDK client instance. For example:

from mistralai import Mistral
import os

s = Mistral(
    api_key=os.getenv("MISTRAL_API_KEY", ""),
)


res = s.models.list()

if res is not None:
    # handle response
    pass

Debugging

To emit debug logs for SDK requests and responses you can pass a logger object directly into your SDK object.

from mistralai import Mistral
import logging

logging.basicConfig(level=logging.DEBUG)
s = Mistral(debug_logger=logging.getLogger("mistralai"))

IDE Support

PyCharm

Generally, the SDK will work well with most IDEs out of the box. However, when using PyCharm, you can enjoy much better integration with Pydantic by installing an additional plugin.

Development

Contributions

While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.

client-python's People

Contributors

anjor avatar antoine-lizee avatar bam4d avatar charlescnorton avatar code-inflation avatar cool-rr avatar desaxce avatar erwinscholtens avatar fuegoio avatar gaspardbt avatar github-actions[bot] avatar jean-malo avatar jtpio avatar lionelchg avatar luo-anthony avatar mastikosa avatar pandora-s-git avatar patrickvonplaten avatar sophiamyang avatar sylvainmouquet avatar thehunmonkgroup avatar theophilegervet avatar tot0 avatar wilsonsilva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

client-python's Issues

ChatCompletionResponse Pydantic Enum Error

Code I had yesterday that functioned quit working today. This could be an inference endpoint change for mistral-large-latest but I figured I would list it here as well as it is probably easier (as in $$$) to fix the client rather than the endpoint.

This is easily reproduced with the function calling example in this repository. A simple cut and paste will do. That example and other function calling code produces:

"
pydantic_core._pydantic_core.ValidationError: 1 validation error for ChatCompletionResponse
choices.0.finish_reason
Input should be 'stop', 'length', 'error' or 'tool_calls' [type=enum, input_value='tool_call', input_type=str]
"

Where the endpoint is sending "tool_call" (singular) and the client is expecting an enum of "tool_calls" (plural) . Yesterday, (29 Feb 2024) all was well and the code worked fine. Was there an endpoint update?

tool_choice parameter does not match OpenAI API

The the Mistral docs Azure AI page links to a notebook that shows that Mistral on Azure is compatible with the openai python package https://github.com/Azure/azureml-examples/blob/main/sdk/python/foundation-models/mistral/openaisdk.ipynb Do you also intend for the Mistral-hosted API to be compatible with the openai python package?

If so, the tool_choice parameter in the API and this package needs to be updated to support choosing a specific tool. According to the openai docs the allowed options for tool_choice are "none", "auto", or {"type": "function", "function": {"name": "my_function"}}.

tool_choice
string or object

Optional
Controls which (if any) function is called by the model. none means the model will not call a function and instead generates a message. auto means the model can pick between generating a message or calling a function. Specifying a particular function via {"type": "function", "function": {"name": "my_function"}} forces the model to call that function.

none is the default when no functions are present. auto is the default if functions are present.

https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice

It appears that the Mistral API does not support the {"type": "function", "function": {"name": "my_function"}} format, returning error

UnprocessableEntityError: Error code: 422 - {'object': 'error', 'message': {'detail': [{'type': 'string_type', 'loc': ['body', 'tool_choice'], 'msg': 'Input should be a valid string', 'input': {'type': 'function', 'function': {'name': 'return_list_of_str'}}, 'url': 'https://errors.pydantic.dev/2.6/v/string_type'}]}, 'type': 'invalid_request_error', 'param': None, 'code': None}

This package shows just the string options (with additional "any")

class ToolChoice(str, Enum):
auto: str = "auto"
any: str = "any"
none: str = "none"

While the openai python package includes the option for a specific tool

https://github.com/openai/openai-python/blob/5cfb125acce0e8304d12bdd39b405071021db658/src/openai/types/chat/chat_completion_tool_choice_option_param.py#L12

Lack of support for tool_choice in the Mistral API makes it incompatible with the openai python package, which means tools like https://github.com/jackmpcollins/magentic have to add custom support for it.

Input should be 'stop' or 'length' [type=enum, input_value='error', input_type=str]

Seems like mistral is returning an error that is not handled by streaming.

  File "/home/jon/miniconda3/envs/h2ogpt/lib/python3.10/site-packages/langchain_core/language_models/chat_models.py", line 63, in generate_from_stream
    for chunk in stream:
  File "/home/jon/miniconda3/envs/h2ogpt/lib/python3.10/site-packages/langchain_mistralai/chat_models.py", line 310, in _stream
    for chunk in self.completion_with_retry(
  File "/home/jon/miniconda3/envs/h2ogpt/lib/python3.10/site-packages/mistralai/client.py", line 208, in chat_stream
    yield ChatCompletionStreamResponse(**json_streamed_response)
  File "/home/jon/miniconda3/envs/h2ogpt/lib/python3.10/site-packages/pydantic/main.py", line 171, in __init__
    self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for ChatCompletionStreamResponse
choices.0.finish_reason
  Input should be 'stop' or 'length' [type=enum, input_value='error', input_type=str]

Problem getting the response status text in all exceptions

In all of the following constructors to the exception, there is a error calling response.text:

async def _check_response_status_codes(self, response: Response) -> None:
if response.status_code in RETRY_STATUS_CODES:
raise MistralAPIStatusException.from_response(
response,
message=f"Status: {response.status_code}. Message: {response.text}",
)
elif 400 <= response.status_code < 500:
if response.stream:
await response.aread()
raise MistralAPIException.from_response(
response,
message=f"Status: {response.status_code}. Message: {response.text}",
)
elif response.status_code >= 500:
if response.stream:
await response.aread()
raise MistralException(
message=f"Status: {response.status_code}. Message: {response.text}",
)

File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/mistralai/client_base.py", line 137, in _check_response_status_codes
message=f"Status: {response.status_code}. Message: {response.text}",
File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/httpx/_models.py", line 573, in text
content = self.content
File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/httpx/_models.py", line 567, in content
raise ResponseNotRead()
httpx.ResponseNotRead: Attempted to access streaming response content, without having called read().

Is `user` the only `role`?

Hello,

I'm experimenting with the async with streaming example. Are there any other roles besides user? Specifically a system and/or assistant roles similar to OpenAI API?

When testing this:

async def main():
    api_key = '<my-api-key>'
    model = "mistral-medium"

    client = MistralAsyncClient(api_key=api_key)

    print("Chat response:")
    async for chunk in client.chat_stream(
        model=model,
        messages=[
            ChatMessage(
                role='system',
                content='You are an experty Python engineer.',
                role="user", 
                content="Tell a crafty, awesome Python joke."
            )
        ],
    ):
        if chunk.choices[0].delta.content is not None:
            print(chunk.choices[0].delta.content, end="")

    print("\n")

    await client.close()

I receive error:

File "E:\dataDocuments\projects\mistral\test.py", line 51
    ChatMessage(
        ^
SyntaxError: keyword argument repeated: role

How can system prompt be set or few-shot prompting be used?

Thank you

AsyncClient Client getting SSL cert error

I'm able to set up a sync client to get reponses from the api, but the `python3.11/site-packages/mistralai/async_client.py' is throwing a SSL error. I'm not behind a firewall or VPN ...

    raise MistralConnectionException(str(e)) from e
mistralai.exceptions.MistralConnectionException: Cannot connect to host api.mistral.ai:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)')]
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x104aec950>```

client_base.py overwrites logging.basicConfig

The following code does not produce a log:

import logging
from mistralai.client import MistralClient

logger = logging.getLogger( __name__ )
logger.info( "Hello world" )

This is because of these lines in client_base.py:

logging.basicConfig(
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    level=os.getenv("LOG_LEVEL", "ERROR"),
)

Modules should not setup the logging configuration, this should be removed or refactored so it is only called for __ main __ for example.

Problems with destructor.

There's a problem when the Mistral client is deleted.

Minimal example:

from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
import os
# Retrieve API key
try:
    api_key = os.environ["MISTRAL_API_KEY"]
except KeyError:
    print("Error: MISTRAL_API_KEY environment variable is not set.")
    exit()

client = MistralClient(api_key=api_key)

chat_response = client.chat(
    model="mistral-tiny",
    messages=[ChatMessage(role="user", content="How many fingers do you have?")],
    temperature=0.5, 
    max_tokens=10,
)
bot_response = chat_response.choices[0].message.content
print(bot_response)

Output

$ python3.10 minimalExample.py 
I don't have fingers, I'm
Exception ignored in: <function MistralClient.__del__ at 0x7f79bf7439a0>
Traceback (most recent call last):
  File ~/.local/lib/python3.10/site-packages/mistralai/client.py", line 49, in __del__
  File ~/.local/lib/python3.10/site-packages/httpx/_client.py", line 1272, in close
  File ~/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 243, in close
  File ~/.local/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 324, in close
  File ~/.local/lib/python3.10/site-packages/httpcore/_sync/connection.py", line 172, in close
TypeError: 'NoneType' object is not callable

Solution suggestions: in ./mistralai/client.py.
A temporary solution, e.g. replace

    def __del__(self) -> None:
        self._client.close()

by

    def __del__(self) -> None:
        if self._client and getattr(self._client, 'close', None):
            try:
                self._client.close()
            except Exception as e:
                pass

usage info not shown in streaming

usage info is supposed to show in the last chunk of streaming. But it didn't show.

client = MistralClient(api_key=api_key)

messages = [
    ChatMessage(role="user", content="What is the best French cheese?")
]

# With streaming
stream_response = client.chat_stream(model=model, messages=messages)

for chunk in stream_response:
    print(chunk.usage)

Documentation and packages out of synch

Pip package is not in sync with github and documentation. For example documentation has response_format and pip package MistralClient does not support it.

  1. Add version numbers to clients so easier to track and debug
  2. Update pip packages frequently along with releases of documentation and push to github.

SSL certificate Issue

Hi I want to use Mistral 7B model in my local system but when I'm running this specific piece of code
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
import os

api_key=os.getenv("MistralAI_API_KEY")
model = "mistral-large-latest"

client = MistralClient(api_key=api_key)

messages = [
ChatMessage(role="user", content="What is thecapital of india in one line?")
]

No streaming

chat_response = client.chat(
model=model,
messages=messages,
)

print(chat_response.choices[0].message.content)

I'm getting an error of mistralai.exceptions.MistralConnectionException: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:992) can anybody help or have any inputs of how to solve this

Client exception when working with pytorch.

from dotenv import load_dotenv
import torch
from mistralai.client import MistralClient

print(torch.tensor([1, 2, 3]))

load_dotenv()
mistral = MistralClient()

print("hello world")

The code above would raise an exception. The program is executed successfully but at the end the program meets an exception. I assume that this exception is coming during python's internal memory management.

Here is the output of the above program I get

tensor([1, 2, 3])
hello world
Exception ignored in: <function MistralClient.__del__ at 0x11015cc20>
Traceback (most recent call last):
  File "/Users/saurabhmahra/Experiments/pii_mapper/venv/lib/python3.12/site-packages/mistralai/client.py", line 46, in __del__
  File "/Users/saurabhmahra/Experiments/pii_mapper/venv/lib/python3.12/site-packages/httpx/_client.py", line 1258, in close
  File "/Users/saurabhmahra/Experiments/pii_mapper/venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 240, in close
  File "/Users/saurabhmahra/Experiments/pii_mapper/venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 313, in close
  File "/Users/saurabhmahra/Experiments/pii_mapper/venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 303, in _close_connections
TypeError: 'NoneType' object is not callable

I can get rid of the issue by just removing the pytorch part of the code.

from dotenv import load_dotenv
# import torch
from mistralai.client import MistralClient

# print(torch.tensor([1, 2, 3]))

load_dotenv()
mistral = MistralClient()

print("hello world")

My understanding of the error is that becuase of pytorch, the httpx client somehow become None and then the error appears because the during the memory management mistral_client object's _client attribute (httpx client) is None.

More info about my system
Python version 3.12.3
mistralai==0.1.8
torch==2.3.0
I am running a macbook air M2 2022 - 8GB with macOS version Sonoma 14.4.1

FYI: We are working on a client using posix shell/awk/curl

Hi mistral community,

We are working on the mistral API client in x-cmd.
x-cmd is a tool built with shell/awk and can leverage 500 portable pkgs.

https://x-cmd.com/mod/mistral

Users need to setup the token using 'x mistral init', then can use x mistral or @mistral in the command line.

We are also working on implementation of a local model management solution leveraging llamafile.

https://x-cmd.com/mod/llmf

Thanks you for your work in open source models.

Regards,
Li Junhao
[email protected]
24-05-14

Streaming chat responses loads multiple chunks after a while instead of one by one

The Mistral API Streaming chat responses load tens of chunks at once (hundreds of tokens) instead of receiving one chunk at a time.

This looks like an API issue, rather than a Python Client issue.

In this example, all of these chunks are received all at once when calling the API with curl.
I observed the same behavior through the Python Client.

curl -X POST https://api.mistral.ai/v1/chat/completions -H "Content-Type: application/json"   -H "Authorization: Bearer $MISTRAL_API_KEY" -d '{
  "model": "mistral-tiny",
  "messages": [
    {
      "role": "user",
      "content": "What is the best French cheese?"
    }
  ],
  "temperature": 0.7,
  "top_p": 1,
  "max_tokens": 1600,
  "stream": true,
  "safe_mode": false,
  "random_seed": null
}'
data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": "assistant"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "It"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "'s subject"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "ive to"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " determine"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " the \"best"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "\" French"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " cheese as it"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " largely depends on"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " personal taste preferences"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ". Here are"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " some popular French"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " cheeses"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ", each"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " with its unique"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " characteristics:\n"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "\n1."}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " Roquef"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "ort: A"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " classic blue-"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "veined"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " cheese from the"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " Massif Central"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " region, known"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " for"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " its strong"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ", pung"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "ent flavor and"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " distinctive tang"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ".\n\n"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "2. Cam"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "embert:"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " A soft,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " creamy,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " and earth"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "y cheese from"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " Normandy,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " famous for its"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " white rind"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " and rich,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " buttery"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " taste.\n"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "\n3."}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " Comté:"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " A nutty"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ", sweet,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " and slightly fru"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "ity cheese"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " from the Fr"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "anche-Com"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "té region,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " made from un"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "pasteur"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "ized cow"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "'s milk"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": ".\n\n"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "4. B"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "rie de Me"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "aux: A"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " soft,"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " bloomy-"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "rind cheese"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": " from the I"}, "finish_reason": null}]}

data: {"id": "cmpl-82b4ad8d22b44757a7a2d3e3ae6fb6a0", "object": "chat.completion.chunk", "created": 1702637377, "model": "mistral-tiny", "choices": [{"index": 0, "delta": {"role": null, "content": "le-de"}, "finish_reason": null}]}

Structure JSON outputs

Hello Team,

Are you planning to add the ability to get structured outputs from your models ?

I have a simple implementation of it from the client (by basically adding a prompt in the background), but I guess you guys would probably want to do most of it in the backend and just expose the functionality though a client parameter ?

Anyways, my client implementation use pydantic models to determine how the JSON output should look like. Here is a simple implementation. I can do a pull request and add that functionality if you guys see it fit.

import json
import os
import time
from typing import Optional, Type

from dotenv import load_dotenv
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
from pydantic import BaseModel, ConfigDict, ValidationError

load_dotenv()


class MistralLanguageModel:
    def __init__(
        self,
        api_key=os.environ["MISTRAL_API_KEY"],
        model="mistral-tiny",
        temperature=0.5,
    ):
        if api_key is None:
            raise ValueError(
                "The Mistral API KEY must be provided either as "
                "an argument or as an environment variable "
                "named 'MISTRAL_API_KEY'"
            )

        self.api_key = api_key
        self.model = model
        self.temperature = temperature
        self.client = MistralClient(api_key=self.api_key)

    def generate(
        self,
        prompt: str,
        output_format: Optional[Type[BaseModel]] = None,
        max_tokens: int = None,
    ):
        retry_delay = 0.1

        while True:
            try:
                system_message = "You are a helpful assistant."
                if output_format:
                    system_message += f" Respond in a JSON format that contains the following keys: {self._model_structure_repr(output_format)}"  # noqa

                messages = [
                    ChatMessage(role="system", content=system_message),
                    ChatMessage(role="user", content=prompt),
                ]
                params = {
                    "model": self.model,
                    "messages": messages,
                    "temperature": self.temperature,
                }

                if max_tokens is not None:
                    params["max_tokens"] = max_tokens

                response = self.client.chat(**params)
                response_content = response.choices[0].message.content

                if output_format:
                    if self._is_valid_json_for_model(response_content,
                                                     output_format):
                        return response_content
                else:
                    return response_content

            except Exception as e:
                print(f"Hit rate limit. Retrying in {retry_delay} seconds.")
                print(f"Error: {e}")
                time.sleep(retry_delay)
                retry_delay *= 2

    def _model_structure_repr(self, model: Type[BaseModel]) -> str:
        fields = model.__annotations__
        return ", ".join(f"{key}: {value}" for key, value in fields.items())

    def _is_valid_json_for_model(self, text: str, model: Type[BaseModel]) -> bool:  # noqa
        """
        Check if a text is valid JSON and if it respects the provided BaseModel. # noqa
        """
        model.model_config = ConfigDict(strict=True)

        try:
            parsed_data = json.loads(text)
            model(**parsed_data)
            return True
        except (json.JSONDecodeError, ValidationError):
            return False


# class Output(BaseModel):
#     first_name: str
#     last_name: str
#     city: str


# llm = MistralLanguageModel()
# prompt = 'Extract the requested  information from the following sentence: "Alice Johnson is visiting Rome."' # noqa
# response = llm.generate(prompt, output_format=Output)

# print(response)



``

Script works with mistral-tiny and mistral-small but no response when using mistral-medium

I run this script, It contains some calls to Mistral's API. These calls work perfectly when using the two smallest models, but not with the medium one.

import os
import requests
import json
from dotenv import load_dotenv
import yfinance as yf
from yahooquery import Ticker
from model.mistralStructured import MistralLanguageModel
from pydantic import BaseModel

load_dotenv()


class Output(BaseModel):
    company_name: str
    company_stock_ticker: str
    period_of_analysis: str
    filename_to_store_data: str


def get_company_news(company_name):
    params = {
        "engine": "google",
        "tbm": "nws",
        "q": company_name,
        "api_key": os.environ.get("SERPAPI_API_KEY"),
    }

    response = requests.get('https://serpapi.com/search', params=params)
    data = response.json()

    return data.get('news_results')


def write_news_to_file(news, filename):
    with open(filename, 'w') as file:
        for news_item in news:
            if news_item is not None:
                title = news_item.get('title', 'No title')
                link = news_item.get('link', 'No link')
                date = news_item.get('date', 'No date')
                file.write(f"Title: {title}\n")
                file.write(f"Link: {link}\n")
                file.write(f"Date: {date}\n\n")


def get_stock_evolution(company_name, period="1y"):
    # Get the stock information
    stock = yf.Ticker(company_name)

    # Get historical market data
    hist = stock.history(period=period)

    # Convert the DataFrame to a string with a specific format
    data_string = hist.to_string()

    # Append the string to the "investment.txt" file
    with open("investment.txt", "a") as file:
        file.write(f"\nStock Evolution for {company_name}:\n")
        file.write(data_string)
        file.write("\n")

    # Return the DataFrame
    return hist


def get_financial_statements(ticker):
    # Create a Ticker object
    company = Ticker(ticker)

    # Get financial data
    balance_sheet = company.balance_sheet().to_string()
    cash_flow = company.cash_flow(trailing=False).to_string()
    income_statement = company.income_statement().to_string()
    valuation_measures = str(company.valuation_measures)  # This one might already be a dictionary or string

    # Write data to file
    with open("investment.txt", "a") as file:
        file.write("\nBalance Sheet\n")
        file.write(balance_sheet)
        file.write("\nCash Flow\n")
        file.write(cash_flow)
        file.write("\nIncome Statement\n")
        file.write(income_statement)
        file.write("\nValuation Measures\n")
        file.write(valuation_measures)


def get_data(company_name, company_ticker, period="1y", filename="investment.txt"):
    news = get_company_news(company_name)
    if news:
        write_news_to_file(news, filename)
    else:
        print("No news found.")

    hist = get_stock_evolution(company_ticker)

    get_financial_statements(company_ticker)

    return hist


def financial_analyst(request):
    print(f"Received request: {request}")
    llm = MistralLanguageModel(model='mistral-medium')

    prompt = f"Given the user request, what is the company name and the company stock ticker ?: {request}?"

    response = llm.generate(prompt, output_format=Output)

    print(response)

    # Parse the arguments from a JSON string to a Python dictionary
    arguments = json.loads(response)
    print(arguments)
    company_name = arguments["company_name"]
    company_ticker = arguments["company_stock_ticker"]

    # Parse the return value from a JSON string to a Python dictionary
    hist = get_data(company_name, company_ticker)
    print(hist)

    with open("investment.txt", "r") as file:
        content = file.read()[:10000]

    second_prompt = f"Based on this request company: {request}, and these data, write an investment thesis: {response}, {content}"

    print("Second message:")

    try:
        second_response = llm.generate(second_prompt)

    except Exception as e:
        print(e)

    return (second_response, hist)

Missing Test

Hi team,

Are you working on the testing yet ? I don't want to start working on the tests if you are already doing it

Please provide a head start for cli/api context-history management, suggestion provided here.

Hello. I just signed up for Mistral api, it is very exciting.
And at docs.mistral.ai/
I saw the provided code for connecting with the api is not so verbose; one block of the curl.

It may be helpful for your python-using users to have a user-friendly way to get started with the AI
and the longer road of managing context history. So

Here is a colab for basic history management and recording. Sorry it is very crud but,
please use it if people find it useful.
https://github.com/lineality/simple_mistral_api_context_history_manager

You are very important, thank you for your work.

AttributeError in MistralClient when using ChatMessage with `model_dump()`

Environment:

Python version: 3.9
MistralAI package version: 0.0.8

Description:
Encountered an AttributeError when attempting to use the MistralClient.chat() method with ChatMessage objects. The error suggests that ChatMessage objects do not have a model_dump() method, which is expected by the _make_chat_request method in client_base.py.

Code to Reproduce:

import os
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage

api_key = 'your-api-key'
model = "mistral-tiny"

client = MistralClient(api_key=api_key)

chat_response = client.chat(
    model=model,
    messages=[ChatMessage(role="user", content="What is the best French cheese?")],
)
print(chat_response.choices[0].message.content)

Expected Behavior:
The chat() method should correctly process the ChatMessage object and return a chat completion response.

Actual Behavior:
An AttributeError is raised:

AttributeError: 'ChatMessage' object has no attribute 'model_dump'

This occurs at the line in client_base.py where it tries to call model_dump() on each ChatMessage object in the messages list.

Steps to Reproduce:

Initialize a MistralClient with a valid API key.
Attempt to send a chat request using the chat() method with a ChatMessage object.
Observe the AttributeError.

Make httpx dependency version less restrictive

Can we please replace

httpx = "^0.25.2"

with

httpx = "<1"

The lock on 0.25.* is very restrictive and httpx development is backwards compatible (assuming major version remains unchanged). <1 will lock on 0.*, allowing for safe minor version incremental.

mistralai 0.1.6
├── httpx >=0.25.2,<0.26.0

Relax pyarrow requirement to below 15

A recent PR pinned the minimum pyarrow version to ^15.0.0, but that's a fairly new version -- jan 21, 2024 -- and much of the pydata ecosystem is not there yet, e.g., the latest nvidia rapids is only 14.x, and much of the community is even earlier than that . This is preventing diretly including the mistral client in pydata builds as a managed dependency within louie.ai, and I'm assuming other systems would have the same challenge.

I'm guessing that pyarrow 15 format features are not necessary for the client. If so, I recommend going to the lowest supported, or if not known, removing this constraint.

References:

Make orjson an optional dependency

Thank you for your work! Can we please make orjson dependency optional? I have concerns about the quality of this dependency (compatibility, security) and would prefer to not use it in my projects.

For example ijl/orjson#452 talks about python segfault that was introduced in one of the releases, and it took multiple iterations to get it fixed properly:

You will also see that this project has 1-week stale auto-close policy, which is yet another red flag for me.

See:

My suggestion would be to drop the orjson dependency in exchange for https://jcristharif.com/msgspec and made it optional, just for the people who really need it - the performance gain here is most likely very small and for many people it's probably not worth the additional dependency/risk. We are not parsing megabytes/gigabytes of data - stdlib's json should do pretty well.

Python client use async with stream not work!

I use python client

use the code from document , use stream and async

It raise : httpx.ResponseNotRead: Attempted to access streaming response content, without having called read().

from mistralai.async_client import MistralAsyncClient
from mistralai.models.chat_completion import ChatMessage

api_key = os.environ["MISTRAL_API_KEY"]
model = "mistral-large-latest"

client = MistralAsyncClient(api_key=api_key)

messages = [
    ChatMessage(role="user", content="What is the best French cheese?")
]

# With async
async_response = client.chat_stream(model=model, messages=messages)

async for chunk in async_response: 
    print(chunk.choices[0].delta.content)
httpx.ResponseNotRead: Attempted to access streaming response content, without having called `read()`.
2024-02-29 15:13:08,972 ERROR sanic.error: Exception occurred while handling uri: 'http://localhost:9000/message/doris'
Traceback (most recent call last):
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/sanic/app.py", line 1385, in handle_request
    response = await response
  File "/Users/apple/SynologyDrive/code/rena_sanic/srf/views.py", line 71, in view
    return await self.dispatch(request, *args, **kwargs)
  File "/Users/apple/SynologyDrive/code/rena_sanic/srf/views.py", line 109, in dispatch
    response = await run_awaitable(handler, request=request, *args, **kwargs)
  File "/Users/apple/SynologyDrive/code/rena_sanic/srf/utils.py", line 95, in run_awaitable
    return await func(*args, **kwargs) if inspect.iscoroutinefunction(func) else func(*args, **kwargs)
  File "/Users/apple/SynologyDrive/code/rena_sanic/message/views.py", line 102, in post
    await ChatManager().ask_stream(conversation_id=conversation_id, response=responses, content=message,
  File "/Users/apple/SynologyDrive/code/rena_sanic/mages/logic/chat_manager.py", line 36, in ask_stream
    async for data in MistralComplete().complete_stream(model="mistral-large-latest", messages=content, temperature=0.7):
  File "/Users/apple/SynologyDrive/code/rena_sanic/mages/logic/completes/mistral.py", line 23, in complete_stream
    async for chunk in async_response:
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/mistralai/async_client.py", line 223, in chat_stream
    async for json_response in async_response:
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/mistralai/async_client.py", line 85, in _request
    self._check_streaming_response(response)
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/mistralai/client_base.py", line 145, in _check_streaming_response
    self._check_response_status_codes(response)
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/mistralai/client_base.py", line 137, in _check_response_status_codes
    message=f"Status: {response.status_code}. Message: {response.text}",
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/httpx/_models.py", line 573, in text
    content = self.content
  File "/Users/apple/opt/anaconda3/envs/rena-sanic-39/lib/python3.9/site-packages/httpx/_models.py", line 567, in content
    raise ResponseNotRead()
httpx.ResponseNotRead: Attempted to access streaming response content, without having called `read()`.

Custom logging level results in error upon importing client

Description

Using a custom logging level e.g. TRACE results in an ValueError when client_base.py is imported.

In client_base.py the logging level is configured by reading the LOG_LEVEL. The level parameter of the stdlib logging may only be DEBUG, INFO, WARNING, ERROR, CRITICAL or NOTSET thus resulting in an error when using a different log level.

Example

import os
os.environ["LOG_LEVEL"] = "TRACE"
from mistralai import client_base # ValueError: Unknown level: 'TRACE'

MistralAI python client package

What's the name of the python package? Or do you have to clone this repository to be able to use the MistralAI python client?

Unnecessary packaging dependency

We just noted that Mistral Python client included pandas as a hard dependency for one of the cookbook examples.

It should be either a soft or not a dependency.

image

Align request path with openai sdk

In our scenario, we want to support customer consuming Mistral endpoint with both Mistral AI sdk and OpenAI sdk, but when testing we found that there's a difference between these two clients.

In OpenAI sdk, if customer provides their own base_url, OpenAI sdk won't make any change to that url and there will be no default "v1" prefix added; only when customer doesn't provide base_url, OpenAI sdk will set the url to a default value "https://api.openai.com/v1", this is the code link: https://github.com/openai/openai-python/blob/main/src/openai/_client.py

While in Mistral AI sdk, it will by default add a "v1" prefix for API calls.

To support Mistral AI sdk, the server had to provide the API with "v1” prefix, this also means that customer needs to use different base_url for these 2 sdk:

  • OpenAI: https://<domain>/v1
  • MistralAI: https://<domain>

Of course, to mitigate this impact, we can do some trick in the server side to support both "v1/<api_path>" and "<api_path>", this will be an extra effort for any org who has the same scenario as us.

We'd like to know whether this is by design and whether it's possible to follow the openai sdk way to remove the default "v1" prefix in the request path?

Pydantic requirement is too restrictive

Pydantic 2.x series is still only a few months old.

Requiring Pydantic 2.x in this module creates unnecessary package conflicts with other Python packages that have not upgraded yet.

For example, although I've built a ChatMistralAI integration for Langchain, it cannot be merged into the codebase because of this dependency conflict:

langchain-ai/langchain#14775 (comment)

Langchain currently allows users to use either V1 or V2, perhaps you could do something similar during this transition period: https://github.com/langchain-ai/langchain/blob/master/docs/docs/guides/pydantic_compatibility.md

Safe_mode response in english for a question in French

Hello,
I am using mistralai client with mistral-small model with a question in French. I receive a response in French with no safe_mode whereas the response is in English with safe_mode enabled.
Code without safe_mode:

model = "mistral-small"
question = "De quel pays Paris est la capitale?"
messages = [ChatMessage(role="user", content=question)]
chat_response = client.chat(model=model, messages=messages, temperature=0.5, top_p=0.95, max_tokens=250, random_seed=0)
chat_response
resp = chat_response.choices[0].message.content.strip()
resp

Paris est la capitale de la France. C'est une ville magnifique connue pour sa beauté, sa riche histoire, sa culture, sa gastronomie et sa mode. Elle abrite de nombreux sites emblématiques tels que la Tour Eiffel, Notre-Dame, le Louvre et l'Arc de Triomphe. Paris est également réputée pour ses musées, ses théâtres, ses cafés et ses boutiques de luxe. C'est une destination touristique populaire qui attire des millions de visiteurs chaque année.

With safe_mode:

chat_response = client.chat(model=model, messages=messages, temperature=0.5, top_p=0.95, max_tokens=250, random_seed=0, safe_mode=True)
chat_response
resp = chat_response.choices[0].message.content.strip()
resp

Paris is the capital of France. Your question is in French, but I'm assuming you're asking about the capital city of France. I'm here to provide helpful, accurate, and positive responses while avoiding any harmful, unethical, or prejudiced content. Let me know if you have any other questions!

I would expect both responses in the same language.
Probably the point is more related to the model rather than the python client but I don't know a better place to ask it.
Thanks,
Thom

Remains in Waiting State on Windows 10, Linux system works

Environment

  • OS: Windows 10
  • Mistral version: [0.0.9]
  • Python version: [3.11]
  • Poetry version: [mistralai 0.0.1]

Description

I am encountering an issue where Mistral remains in a waiting state indefinitely on Windows 10. This happens after I've modified the import section of my code to include pyreadline3. Despite successfully installing pyreadline3 and using poetry as my dependency manager, Mistral does not proceed to an active state.

Steps to Reproduce

  1. Added the following lines at the beginning of my Python script:
    from pyreadline3 import Readline
    readline = Readline()

2、Installed pyreadline3 using poetry add pyreadline3.
3、Ran the script expecting Mistral to be functional.

Expected Behavior
Mistral should run as expected, similar to its behavior on a Linux system.

Actual Behavior
Mistral stays in a waiting state and does not proceed to execute any further actions.

Additional Context
The same codebase works without issues on a Linux environment.
Using the requests library to perform POST requests, I am able to receive responses from Mistral, which indicates that the API and network connections are functioning correctly.
Attempts to Fix
Ensured that pyreadline3 is properly installed and accessible within the poetry environment.
Ran the script with administrative privileges to rule out permission issues.
I would appreciate any guidance or suggestions on how to resolve this issue.

Timeouts with mistral-large-latest

Problems with testing identical prompt across different Mistral models, and having consistent issue with mistral-large-latest.

Prompt length as calculated by GPT tokenizer: 2,196 tokens and 11,331 characters

Note: this exact prompt works consistently with mistral-medium-2312 and mistral-medium-latest

So far, I have tried increasing timeout on client instantiation to ~5 minutes to see if that helped (though medium works fine on default).

I have tried both synchronous and streaming connections. Here is my current situation:

Error:

Unexpected exception (RemoteProtocolError): peer closed connection without sending complete message body (incomplete chunked read)

relevant code snippet:

stream_response = client.chat_stream(model=model, messages=messages, temperature=TEMPERATURE)
for chunk in stream_response:
  print(chunk)
  summary = summary + chunk.choices[0].delta.content

Python Langchain MistralAI partner package

The Python implementation of Langchain now has a MistralAI partner package:

https://github.com/langchain-ai/langchain/tree/master/libs/partners/mistralai
https://pypi.org/project/langchain-mistralai/

Which basically means you can use Langchain with the MistralAI API.

I wrote most of the integration code, hopefully I got it right :)

Would love if somebody from the MistralAI team could have a look and check my work!

I should note that there's currently no support for the list_models() and embeddings() methods of the underlying mistralai package -- I'll leave that as an exercise for another user.

Add endpoint to fetch total usage of the day

Hi there,

Are you considering adding an endpoint in your API in the future so that the client can fetch the total usage for the day? Because as you know, more and more, we're integrating LLMs into our tools. It could be interesting to monitor costs without having to go to our MistralAI account. What do you think? :)

Here is an example from OpenAI for this particular use case:

r = openai.api_requestor.APIRequestor();
resp = r.request("GET", '/usage?date=2024-02-07'); // or start_date and end_date

Feature Request: OpenAI API function support

I first want to mention that I do not want to support OpenAI or want to enable them in any way. That is particularly why I want Mistral to succeed. That being said, their API function has become somewhat standard in a lot of libraries and projects. By changing the 'api_base' argument, you can point it at any url, given that they support that format and functionality. It would make things easier for people switching over codebases and also allow extra functionality, like generating using JSON mode or function calls with pydantic + instructor.

Handling rate limits with mistral-embed

I've been working on embedding a large document with mistral-embed and have repeatedly reached the API rate limit (2M tokens/min). Could you provide any advice or best practices for managing these limits more effectively when using the Python client?

Appreciate your guidance.

Enhancing Efficiency in Context Management for Multiple Q&A Sessions with Mistral

I am currently exploring efficient methodologies for handling multiple question-and-answer interactions using Mistral, particularly in the context of processing audio transcripts. My aim is to streamline the process while maintaining cost-effectiveness.

In my current implementation, I use the Mistral medium model as demonstrated in the code snippet below:

model = "mistral-medium" client = MistralClient(api_key=mistral_api_key) chat_response = client.chat( model=model, messages=[ChatMessage(role="user", content=context_data_mistral)], )

This code allows me to retrieve responses for a set of 36 questions, which are part of the context data alongside the relevant audio transcript. The context data and questions are provided in the attached file [context_data_mistral.txt]

To obtain the answers, I execute:

chat_response.choices[0].message.content

However, my primary concern and the purpose of this enhancement request is to optimize the process of context data management. Specifically, I am looking for a way to send the context data to the Mistral client just once and then engage in multiple Q&A interactions without the need to resend the context for each new question. This approach would be akin to the session-based interaction experienced in the ChatGPT web interface, where initial context setting is followed by an ongoing dialogue.

My question is: How can I implement a session-like interaction in Mistral, where the context is established initially and maintained throughout subsequent Q&A exchanges? This enhancement would significantly improve the efficiency in number of input tokens used and as well as there might be significant improvement in answers that i get from the model.

context_data_mistral.txt

Mistral token counting/batching

Hi, I'm interested in making a PR so that the embedding methods chunk data to ensure each request has less than the maximum (16384) required by Mistral. Unfortunately, there doesn't seem to be a lightweight way to count tokens for Mistral, such as tiktoken for OpenAI models - I guess most of the time it's going to be safe to use tiktoken with a safety margin, but is there a way to count tokens accurately without a heavy library?

pydantic_core._pydantic_core.ValidationError: 14 validation errors for ModelList

Just started hitting this hour ago or so. Model info from mistralai endpoint is bad.

from mistralai.client import MistralClient
api_key = '<fill me>'
client = MistralClient(api_key=api_key)
client.list_models()

leads to:

pydantic_core._pydantic_core.ValidationError: 14 validation errors for ModelList
data.0.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-6b41fff...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.1.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-eae7f7f...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.2.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-0e1104a...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.3.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-d552e7f...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.4.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-42276f7...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.5.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-28d07b4...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.6.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-7614eb5...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.7.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-d5fa9c0...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.8.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-57a725b...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.9.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-810fd89...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.10.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-8511c22...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.11.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-815c8d0...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.12.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-601adc1...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing
data.13.permission.0.created
  Field required [type=missing, input_value={'id': 'modelperm-b880eb7...se, 'organization': '*'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/missing

Add support for HTTP proxies

When a script using the Mistral SDK is running on a environment requiring an HTTP proxy, it fails with the error :

mistralai.exceptions.MistralConnectionException: [Errno 104] Connection reset by peer

When creating the client, https://github.com/mistralai/client-python/blob/main/src/mistralai/client.py#L40-L43, adding the proxies attribute work for me :

self._client = Client(
            follow_redirects=True,
            timeout=self._timeout,
            proxies="http://localhost:3128",
            transport=HTTPTransport(retries=self._max_retries))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.