from typing import Any, Optional import backoff from dsp.modules.lm import LM from dsp.utils.settings import settings mistralai_api_error = None try: from mistralai import Mistral as MistralAI from mistralai.models.usermessage import UserMessage except ImportError: mistralai_api_error = Exception def backoff_hdlr(details): """Handler from https://pypi.org/project/backoff/""" print( "Backing off {wait:0.1f} seconds after {tries} tries " "calling function {target} with kwargs " "{kwargs}".format(**details), ) def giveup_hdlr(details): """wrapper function that decides when to give up on retry""" if "rate limits" in details.message: return False return True class Mistral(LM): """Wrapper around Mistral AI's API. Currently supported models include `mistral-small-latest`, `mistral-medium-latest`, `mistral-large-latest`. """ def __init__( self, model: str = "mistral-medium-latest", api_key: Optional[str] = None, **kwargs, ): """ Parameters ---------- model : str Which pre-trained model from Mistral AI to use? Choices are [`mistral-small-latest`, `mistral-medium-latest`, `mistral-large-latest`] api_key : str The API key for Mistral AI. **kwargs: dict Additional arguments to pass to the API provider. """ super().__init__(model) if mistralai_api_error == Exception: raise ImportError( "Not loading Mistral AI because it is not installed. Install it with `pip install mistralai`." ) self.client = MistralAI(api_key=api_key) self.provider = "mistral" self.kwargs = { "model": model, "temperature": 0.17, "max_tokens": 150, **kwargs, } self.history: list[dict[str, Any]] = [] def basic_request(self, prompt: str, **kwargs): """Basic request to Mistral AI's API.""" raw_kwargs = kwargs kwargs = { **self.kwargs, "messages": [UserMessage(role="user", content=prompt)], **kwargs, } # Mistral disallows "n" arguments n = kwargs.pop("n", None) if n is not None and n > 1 and kwargs["temperature"] == 0.0: kwargs["temperature"] = 0.7 response = self.client.chat.complete(**kwargs) history = { "prompt": prompt, "response": response, "kwargs": kwargs, "raw_kwargs": raw_kwargs, } self.history.append(history) return response @backoff.on_exception( backoff.expo, (mistralai_api_error), max_time=settings.backoff_time, on_backoff=backoff_hdlr, giveup=giveup_hdlr, ) def request(self, prompt: str, **kwargs): """Handles retrieval of completions from Mistral AI whilst handling API errors.""" prompt = prompt + "Follow the format only once !" return self.basic_request(prompt, **kwargs) def __call__( self, prompt: str, only_completed: bool = True, return_sorted: bool = False, **kwargs, ): assert only_completed, "for now" assert return_sorted is False, "for now" n = kwargs.pop("n", 1) completions = [] for _ in range(n): response = self.request(prompt, **kwargs) completions.append(response.choices[0].message.content) return completions