from typing import Optional, List
from langchain_core.language_models.llms import LLM
from hugchat import hugchat
from hugchat.login import Login
from dotenv import load_dotenv
import os

class ConcreteHuggingChatLLM(LLM):
    def __init__(self, model_index=0):
        self.chatbot = None  # Define the chatbot attribute first

        load_dotenv()
        hf_email = os.getenv("HF_EMAIL")
        hf_password = os.getenv("HF_PASSWORD")

        if not hf_email or not hf_password:
            raise ValueError("HF_EMAIL and HF_PASSWORD must be set in the .env file")

        cookie_path_dir = "./cookies/"
        sign = Login(hf_email, hf_password)
        cookies = sign.login(cookie_dir_path=cookie_path_dir, save_cookies=True)

        self.chatbot = hugchat.ChatBot(cookies=cookies.get_dict())
        self.chatbot.switch_llm(model_index)

    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        response = self.chatbot.chat(prompt)
        return response.text

    @property
    def _identifying_params(self):
        """Utility property for forcing cache keys to be unique"""
        return {}

    @property
    def _llm_type(self):
        return "hugging_chat"


# Example usage
if __name__ == "__main__":
    llm = ConcreteHuggingChatLLM()

    # Use the ConcreteHuggingChatLLM
    print(llm("Hello, how are you?"))  # Example of using the invoke method

    # Stream the response
    #for chunk in llm.stream("Tell me a joke"):
    #    print(chunk, end="", flush=True)

    # Get available models
    #print(llm.get_available_models())

    # Switch to a different model
    #llm.switch_model(1)

    # Create a new conversation
    #llm.new_conversation()

    # Get conversation info
    #info = llm.get_conversation_info()
    #print(info.id, info.title, info.model, info.system_prompt, info.history)

    # Search for an assistant
    #assistant = llm.search_assistant("ChatGPT")
    #llm.new_conversation(assistant=assistant, switch_to=True)

    # Get assistant list
    #assistant_list = llm.get_assistant_list()
    #print(assistant_list)

    # Delete all conversations (DANGER!)
    #llm.delete_all_conversations()

The provided code is a Python class implementation of a ConcreteHuggingChatLLM, which is a subclass of the LLM class. This class integrates the Hugging Chat API to interact with a Large Language Model (LLM). Let's go through the code step by step:

Import Statements:
from typing import Optional, List
from langchain_core.language_models.llms import LLM
from hugchat import hugchat
from hugchat.login import Login
from dotenv import load_env
import os
The necessary modules are imported, including typing for type annotations, LLM from langchain_core, hugchat and its Login class for interacting with the Hugging Chat API, and dotenv for loading environment variables.
ConcreteHuggingChatLLM Class Definition:
class ConcreteHuggingChatLLM(LLM):
    def __init__(self, model_index=0):
        self.chatbot = None  # Define the chatbot attribute first
The class ConcreteHuggingChatLLM inherits from the LLM class. It has an init method that initializes the chatbot attribute.
Load Environment Variables:
load_dotenv()
hf_email = os.getenv("HF_EMAIL")
hf_password = os.getenv("HF_PASSWORD")

if not hf_email or not hf_password:
    raise ValueError("HF_EMAIL and HF_PASSWORD must be set in the .env file")
The code loads environment variables from a .env file using the load_dotenv() function. It then retrieves the HF_EMAIL and HF_PASSWORD environment variables. If either of them is not set, it raises a ValueError.
Login to Hugging Chat API:
cookie_path_dir = "./cookies/"
sign = Login(hf_email, hf_password)
cookies = sign.login(cookie_dir_path=cookie_path_dir, save_cookies=True)
The code creates a Login instance with the provided email and password. It then calls the login method to authenticate with the Hugging Chat API and obtain cookies for subsequent requests.
Initialize Chatbot:
self.chatbot = hugchat.ChatBot(cookies=cookies.get_dict())
self.chatbot.switch_llm(model_index)
The chatbot attribute is initialized with an instance of the ChatBot class from the hugchat module. The cookies obtained from the login step are passed to the ChatBot constructor. The switch_llm method is called to select the desired LLM model based on the provided model_index.
_call Method:
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
    response = self.chatbot.chat(prompt)
    return response.text
The _call method is overridden from the LLM class. It takes a prompt string as input and uses the chatbot instance to send the prompt to the LLM. The response from the LLM is then returned as a string.
_identifying_params and _llm_type Properties:
@property
def _identifying_params(self):
    """Utility property for forcing cache keys to be unique"""
    return {}

@property
def _llm_type(self):
    return "hugging_chat"
These properties are used to provide identifying information about the LLM instance. _identifying_params returns an empty dictionary, indicating that there are no additional parameters that affect the behavior of the LLM. _llm_type returns the string "hugging_chat", indicating the type of LLM being used.
Example Usage:
if __name__ == "__main__":
    llm = ConcreteHuggingChatLLM()

    # Use the ConcreteHuggingChatLLM
    print(llm("Hello, how are you?"))  # Example of using the invoke method
The example usage section demonstrates how to create an instance of ConcreteHuggingChatLLM and use it to generate a response to the prompt "Hello, how are you?". The generated response is then printed to the console.
Overall, this code provides a concrete implementation of an LLM using the Hugging Chat API, allowing users to interact with a Large Language Model and obtain responses to prompts.

i still got this error "Traceback (most recent call last):
  File "C:\Users\AYOUB\Desktop\HugChat-huggignface_api\hugchat_api.txt", line 42, in <module>
    llm = ConcreteHuggingChatLLM()
          ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\AYOUB\Desktop\HugChat-huggignface_api\hugchat_api.txt", line 10, in __init__
    self.chatbot = None  # Define the chatbot attribute first
    ^^^^^^^^^^^^
  File "C:\Users\AYOUB\Desktop\HugChat-huggignface_api\myenv\Lib\site-packages\pydantic\v1\main.py", line 357, in __setattr__
    raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"')
ValueError: "ConcreteHuggingChatLLM" object has no field "chatbot""

here is the detailed context that contains all the content of the python scritps imported in the previous script , here is the context "context :{{{
"here is the content of the file from langchain_core.language_models.llms import LLM":
    """Base LLM abstract class.

    The purpose of this class is to expose a simpler interface for working
    with LLMs, rather than expect the user to implement the full _generate method.
    """

    @abstractmethod
    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """Run the LLM on the given prompt and input."""

    async def _acall(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """Run the LLM on the given prompt and input."""
        return await run_in_executor(
            None,
            self._call,
            prompt,
            stop,
            run_manager.get_sync() if run_manager else None,
            **kwargs,
        )

    def _generate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        """Run the LLM on the given prompt and input."""
        # TODO: add caching here.
        generations = []
        new_arg_supported = inspect.signature(self._call).parameters.get("run_manager")
        for prompt in prompts:
            text = (
                self._call(prompt, stop=stop, run_manager=run_manager, **kwargs)
                if new_arg_supported
                else self._call(prompt, stop=stop, **kwargs)
            )
            generations.append([Generation(text=text)])
        return LLMResult(generations=generations)

    async def _agenerate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        """Run the LLM on the given prompt and input."""
        generations = []
        new_arg_supported = inspect.signature(self._acall).parameters.get("run_manager")
        for prompt in prompts:
            text = (
                await self._acall(prompt, stop=stop, run_manager=run_manager, **kwargs)
                if new_arg_supported
                else await self._acall(prompt, stop=stop, **kwargs)
            )
            generations.append([Generation(text=text)])
        return LLMResult(generations=generations)}"
for the file "from hugchat.login import Login" , here is its content {import requests
import os
import json
import logging
import re


class Login:
    
    def __init__(self, email: str, passwd: str = "") -> None:
        self.DEFAULT_PATH_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "usercookies")
        self.DEFAULT_COOKIE_PATH = self.DEFAULT_PATH_DIR + os.path.join(f"{email}.json")

        self.email: str = email
        self.passwd: str = passwd
        self.headers = {
            "Referer": "https://huggingface.co/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.64",
        }
        self.cookies = requests.sessions.RequestsCookieJar()

    def login(self, cookie_dir_path: str = None, save_cookies: bool = False) -> requests.sessions.RequestsCookieJar:
        '''
        Login to huggingface.co with given email and password.
        - If cookie_dir_path is given, load cookies from the path. If path is not exist, raise an exception.
        - If save_cookies is True, save cookies to the givev path (if cookie_dir_path is not given, save to default path `./usercookies`).
        - Return cookies if login success, otherwise raise an exception.
        '''
        
        if not cookie_dir_path:
            cookie_dir_path = self.DEFAULT_PATH_DIR

        # validate cookies content before use
        if os.path.exists(cookie_dir_path) and os.path.exists(self._get_cookie_path(cookie_dir_path)):
            with open(self._get_cookie_path(cookie_dir_path),'r+') as f_cookies:
                cookies = json.load(f_cookies)
                try:
                    list(cookies.keys()).index('token')
                    list(cookies.keys()).index('hf-chat')
                    return self.load_cookies(cookie_dir_path)
                except Exception as e:
                    print('error during validating cookies')

        self._sign_in_with_email()
        location = self._get_auth_url()
        if self._grant_auth(location):
            if save_cookies:
                self.save_cookies(cookie_dir_path)
            return self.cookies
        else:
            raise Exception(f"Grant auth fatal, please check your email or password\ncookies gained: \n{self.cookies}")
        
    def save_cookies(self, cookie_dir_path: str = './usercookies') -> str:
        '''
        cookies will be saved into: cookie_dir_path/<email>.json
        '''
        return self.saveCookiesToDir(cookie_dir_path)
        
    def saveCookiesToDir(self, cookie_dir_path: str = './usercookies') -> str:
        """
        alias of save_cookies
        """
        cookie_dir_path = self.DEFAULT_PATH_DIR if not cookie_dir_path else cookie_dir_path
        if not cookie_dir_path.endswith("/"):
            cookie_dir_path += "/"
        cookie_path = cookie_dir_path + f"{self.email}.json"
        if not os.path.exists(cookie_dir_path):
            logging.info("Cookie directory not exist, creating...")
            os.makedirs(cookie_dir_path)
        logging.info(f"Cookie store path: {cookie_path}")

        with open(cookie_path, "w", encoding="utf-8") as f:
            f.write(json.dumps(self.cookies.get_dict()))
        return cookie_path
    
    def load_cookies(self, cookie_dir_path: str = './usercookies') -> requests.sessions.RequestsCookieJar:
        '''
        cookies will be loaded from: cookie_dir_path/<email>.json
        '''
        return self.loadCookiesFromDir(cookie_dir_path)
    
    def loadCookiesFromDir(self, cookie_dir_path: str = './usercookies') -> requests.sessions.RequestsCookieJar:
        """
        alias of load_cookies
        """
        cookie_dir_path = self.DEFAULT_PATH_DIR if not cookie_dir_path else cookie_dir_path
        cookie_path = self._get_cookie_path(cookie_dir_path)
        if not cookie_path:
            raise Exception(f"Cookie not found. please check the path given: {cookie_dir_path}.\n" +
                            f"Cookie file must be named like this: 'your_email'+'.json': '{self.email}.json'")

        with open(cookie_path, "r", encoding="utf-8") as f:
            try:
                js = json.loads(f.read())
                for i in js.keys():
                    self.cookies.set(i, js[i])
                    logging.info(f"{i} loaded")
                return self.cookies
            except:
                raise Exception("load cookies from files fatal. Please check the format")
    
    def _request_get(self, url: str, params=None, allow_redirects=True) -> requests.Response:
        res = requests.get(
            url,
            params=params,
            headers=self.headers,
            cookies=self.cookies,
            allow_redirects=allow_redirects,
        )
        self._refresh_cookies(res.cookies)
        return res

    def _request_post(self, url: str, headers=None, params=None, data=None, stream=False,
                     allow_redirects=True) -> requests.Response:
        res = requests.post(
            url,
            stream=stream,
            params=params,
            data=data,
            headers=self.headers if headers is None else headers,
            cookies=self.cookies,
            allow_redirects=allow_redirects
        )
        self._refresh_cookies(res.cookies)
        return res

    def _refresh_cookies(self, cookies: requests.sessions.RequestsCookieJar):
        dic = cookies.get_dict()
        for i in dic:
            self.cookies.set(i, dic[i])

    def _sign_in_with_email(self):
        """
        Login through your email and password.
        PS: I found that it doesn't have any type of encryption till now,
        which could expose your password to the internet.
        """
        url = "https://huggingface.co/login"
        data = {
            "username": self.email,
            "password": self.passwd,
        }
        res = self._request_post(url=url, data=data, allow_redirects=False)
        if res.status_code == 400:
            raise Exception("wrong username or password")

    def _get_auth_url(self):
        url = "https://huggingface.co/chat/login"
        headers = {
            "Referer": "https://huggingface.co/chat/login",
            "User-Agent": self.headers["User-Agent"],
            "Content-Type": "application/x-www-form-urlencoded"
        }
        res = self._request_post(url, headers=headers, allow_redirects=False)
        if res.status_code == 200:
            # location = res.headers.get("Location", None)
            location = res.json()["location"]
            if location:
                return location
            else:
                raise Exception("No authorize url found, please check your email or password.")
        elif res.status_code == 303:
            location = res.headers.get("Location")
            if location:
                return location
            else:
                raise Exception("No authorize url found, please check your email or password.")
        else:
            raise Exception("Something went wrong!")

    def _grant_auth(self, url: str) -> int:
        res = self._request_get(url, allow_redirects=False)
        if res.headers.__contains__("location"):
            location = res.headers["location"]
            res = self._request_get(location, allow_redirects=False)
            if res.cookies.__contains__("hf-chat"):
                return 1
        # raise Exception("grantAuth fatal")
        if res.status_code != 200:
            raise Exception("grant auth fatal!")
        csrf = re.findall('/oauth/authorize.*?name="csrf" value="(.*?)"', res.text)
        if len(csrf) == 0:
            raise Exception("No csrf found!")
        data = {
            "csrf": csrf[0]
        }

        res = self._request_post(url, data=data, allow_redirects=False)
        if res.status_code != 303:
            raise Exception(f"get hf-chat cookies fatal! - {res.status_code}")
        else:
            location = res.headers.get("Location")
        res = self._request_get(location, allow_redirects=False)
        if res.status_code != 302:
            raise Exception(f"get hf-chat cookie fatal! - {res.status_code}")
        else:
            return 1

    def _get_cookie_path(self, cookie_dir_path) -> str:
        if not cookie_dir_path.endswith("/"):
            cookie_dir_path += "/"
        if not os.path.exists(cookie_dir_path):
            return ""
        files = os.listdir(cookie_dir_path)
        for i in files:
            if i == f"{self.email}.json":
                return cookie_dir_path + i
        return ""

}" and for the file from hugchat import hugchat here is its content:"
    """Base LLM abstract class.

    The purpose of this class is to expose a simpler interface for working
    with LLMs, rather than expect the user to implement the full _generate method.
    """

    @abstractmethod
    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """Run the LLM on the given prompt and input."""

    async def _acall(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """Run the LLM on the given prompt and input."""
        return await run_in_executor(
            None,
            self._call,
            prompt,
            stop,
            run_manager.get_sync() if run_manager else None,
            **kwargs,
        )

    def _generate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        """Run the LLM on the given prompt and input."""
        # TODO: add caching here.
        generations = []
        new_arg_supported = inspect.signature(self._call).parameters.get("run_manager")
        for prompt in prompts:
            text = (
                self._call(prompt, stop=stop, run_manager=run_manager, **kwargs)
                if new_arg_supported
                else self._call(prompt, stop=stop, **kwargs)
            )
            generations.append([Generation(text=text)])
        return LLMResult(generations=generations)

    async def _agenerate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        """Run the LLM on the given prompt and input."""
        generations = []
        new_arg_supported = inspect.signature(self._acall).parameters.get("run_manager")
        for prompt in prompts:
            text = (
                await self._acall(prompt, stop=stop, run_manager=run_manager, **kwargs)
                if new_arg_supported
                else await self._acall(prompt, stop=stop, **kwargs)
            )
            generations.append([Generation(text=text)])
        return LLMResult(generations=generations)
""
and for these files in hugchat 'from .message import Message
from . import exceptions' , here is there content "from typing import Generator, Union

from .exceptions import ChatError, ModelOverloadedError
import json

RESPONSE_TYPE_FINAL = "finalAnswer"
RESPONSE_TYPE_STREAM = "stream"
RESPONSE_TYPE_WEB = "webSearch"
RESPONSE_TYPE_STATUS = "status"
MSGTYPE_ERROR = "error"

MSGSTATUS_PENDING = 0
MSGSTATUS_RESOLVED = 1
MSGSTATUS_REJECTED = 2


class WebSearchSource:
    title: str
    link: str
    hostname: str

    def __str__(self):
        return json.dumps({
            "title": self.title,
            "link": self.link,
            "hostname": self.hostname,
        })


class Message(Generator):
    """
    :Args:
        * g: Generator
        * _stream_yield_all: bool = False
        * web_search: bool = False
        - web_search_sources: list[WebSearchSource] = list()
        - text: str = ""
        - web_search_done: bool = not web_search
        - msg_status: int = MSGSTATUS_PENDING
        - error: Union[Exception, None] = None

    A wrapper of `Generator` that receives and process the response

    :Example:
    .. code-block:: python

        msg = bot.chat(...)

        # stream process
        for res in msg:
            ... # process
        else:
            if msg.done() == MSGSTATUS_REJECTED:
                raise msg.error

        # or simply use:
        final = msg.wait_until_done()
    """

    g: Generator
    _stream_yield_all: bool = False
    web_search: bool = False

    web_search_sources: list = []
    text: str = ""  # For backward compatibility, we have to reserve the `text` field.
    web_search_done: bool = not web_search
    msg_status: int = MSGSTATUS_PENDING
    error: Union[Exception, None] = None

    def __init__(
        self,
        g: Generator,
        _stream_yield_all: bool = False,
        web_search: bool = False,
    ) -> None:
        self.g = g
        self._stream_yield_all = _stream_yield_all
        self.web_search = web_search

    def _filterResponse(self, obj: dict):
        if not obj.__contains__("type"):
            if obj.__contains__("message"):
                raise ChatError(f"Server returns an error: {obj['message']}")
            else:
                raise ChatError(f"No `type` and `message` returned: {obj}")

    def __next__(self) -> dict:
        if self.msg_status == MSGSTATUS_RESOLVED:
            raise StopIteration
        elif self.msg_status == MSGSTATUS_REJECTED:
            if self.error is not None:
                raise self.error
            else:
                raise Exception(
                    "Message stauts is `Rejected` but no error found")

        try:
            a: dict = next(self.g)
            self._filterResponse(a)
            t: str = a["type"]
            message_type: str = ""
            if t == RESPONSE_TYPE_FINAL:
                self.text = a["text"]
                self.msg_status = MSGSTATUS_RESOLVED
            elif t == RESPONSE_TYPE_WEB:
                # gracefully pass unparseable webpages
                if message_type != MSGTYPE_ERROR and a.__contains__("sources"):
                    self.web_search_sources.clear()
                    sources = a["sources"]
                    for source in sources:
                        wss = WebSearchSource()
                        wss.title = source["title"]
                        wss.link = source["link"]
                        wss.hostname = source["hostname"]
                        self.web_search_sources.append(wss)
            elif "messageType" in a:
                message_type: str = a["messageType"]
                if message_type == MSGTYPE_ERROR:
                    self.error = ChatError(a["message"])
                    self.msg_status = MSGSTATUS_REJECTED
                if t == RESPONSE_TYPE_STREAM:
                    self.web_search_done = True
                elif t == RESPONSE_TYPE_STATUS:
                    pass
            else:
                if "Model is overloaded" in str(a):
                    self.error = ModelOverloadedError(
                        "Model is overloaded, please try again later or switch to another model."
                    )
                    self.msg_status = MSGSTATUS_REJECTED
                elif a.__contains__(MSGTYPE_ERROR):
                    self.error = ChatError(a[MSGTYPE_ERROR])
                    self.msg_status = MSGSTATUS_REJECTED
                else:
                    self.error = ChatError(f"Unknown json response: {a}")

            # If _stream_yield_all is True, yield all responses from the server.
            if self._stream_yield_all or t == RESPONSE_TYPE_STREAM:
                return a
            else:
                return self.__next__()
        except StopIteration:
            if self.msg_status == MSGSTATUS_PENDING:
                self.error = ChatError(
                    "Stream of responses has abruptly ended (final answer has not been received)."
                )
                raise self.error
            pass
        except Exception as e:
            # print("meet error: ", str(e))
            self.error = e
            self.msg_status = MSGSTATUS_REJECTED
            raise self.error

    def __iter__(self):
        return self

    def throw(
        self,
        __typ,
        __val=None,
        __tb=None,
    ):
        return self.g.throw(__typ, __val, __tb)

    def send(self, __value):
        return self.g.send(__value)

    def get_final_text(self) -> str:
        """
        :Return:
            - self.text
        """
        return self.text

    def get_search_sources(self) -> list:
        """
        :Return:
            - self.web_search_sources
        """
        return self.web_search_sources

    def search_enabled(self) -> bool:
        """
        :Return:
            - self.web_search
        """
        return self.web_search

    def wait_until_done(self) -> str:
        """
        :Return:
            - self.text if resolved else raise error

        wait until every response is resolved
        """
        while not self.is_done():
            self.__next__()
        if self.is_done() == MSGSTATUS_RESOLVED:
            return self.text
        elif self.error is not None:
            raise self.error
        else:
            raise Exception("Rejected but no error captured!")

    def is_done(self):
        """
        :Return:
            - self.msg_status

        3 status:
        - MSGSTATUS_PENDING = 0    # running
        - MSGSTATUS_RESOLVED = 1   # done with no error(maybe?)
        - MSGSTATUS_REJECTED = 2   # error raised
        """
        return self.msg_status

    def is_done_search(self):
        """
        :Return:
            - self.web_search_done

        web search result will be set to `done` once the token is received
        """
        return self.web_search_done

    def __str__(self):
        return self.wait_until_done()

    def __getitem__(self, key: str) -> str:
        print("_getitem_")
        self.wait_until_done()
        print("done")
        if key == "text":
            return self.text
        elif key == "web_search":
            return self.web_search
        elif key == "web_search_sources":
            return self.web_search_sources

    def __add__(self, other: str) -> str:
        self.wait_until_done()
        return self.text + other

    def __radd__(self, other: str) -> str:
        self.wait_until_done()
        return other + self.text

    def __iadd__(self, other: str) -> str:
        self.wait_until_done()
        self.text += other
        return self.text


if __name__ == "__main__":
    pass
"and "class ModelOverloadedError(Exception):
    """
    HF Model Overloaded Error

    Raised when hf return response `{"error":"Model is overloaded", "error_type":"overloaded"}`
    """
    pass


class ChatBotInitError(Exception):
    """
    ChatBot Init Error

    Raised when chatbot init failed
    """
    pass


class CreateConversationError(Exception):
    """
    Create Conversation Error

    Raised when create conversation failed
    """
    pass


class InvalidConversationIDError(Exception):
    """
    Invalid Conversation ID Error

    Raised when using a invalid conversation id
    """
    pass


class DeleteConversationError(Exception):
    """
    Delete Conversation Error

    Raised when delete conversation failed
    """
    pass


class ChatError(Exception):
    """
    Chat Error

    Raised when chat failed
    """
    pass
"
and for login file in the hugchat library , here is its content "import requests
import os
import json
import logging
import re


class Login:
    
    def __init__(self, email: str, passwd: str = "") -> None:
        self.DEFAULT_PATH_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "usercookies")
        self.DEFAULT_COOKIE_PATH = self.DEFAULT_PATH_DIR + os.path.join(f"{email}.json")

        self.email: str = email
        self.passwd: str = passwd
        self.headers = {
            "Referer": "https://huggingface.co/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.64",
        }
        self.cookies = requests.sessions.RequestsCookieJar()

    def login(self, cookie_dir_path: str = None, save_cookies: bool = False) -> requests.sessions.RequestsCookieJar:
        '''
        Login to huggingface.co with given email and password.
        - If cookie_dir_path is given, load cookies from the path. If path is not exist, raise an exception.
        - If save_cookies is True, save cookies to the givev path (if cookie_dir_path is not given, save to default path `./usercookies`).
        - Return cookies if login success, otherwise raise an exception.
        '''
        
        if not cookie_dir_path:
            cookie_dir_path = self.DEFAULT_PATH_DIR

        # validate cookies content before use
        if os.path.exists(cookie_dir_path) and os.path.exists(self._get_cookie_path(cookie_dir_path)):
            with open(self._get_cookie_path(cookie_dir_path),'r+') as f_cookies:
                cookies = json.load(f_cookies)
                try:
                    list(cookies.keys()).index('token')
                    list(cookies.keys()).index('hf-chat')
                    return self.load_cookies(cookie_dir_path)
                except Exception as e:
                    print('error during validating cookies')

        self._sign_in_with_email()
        location = self._get_auth_url()
        if self._grant_auth(location):
            if save_cookies:
                self.save_cookies(cookie_dir_path)
            return self.cookies
        else:
            raise Exception(f"Grant auth fatal, please check your email or password\ncookies gained: \n{self.cookies}")
        
    def save_cookies(self, cookie_dir_path: str = './usercookies') -> str:
        '''
        cookies will be saved into: cookie_dir_path/<email>.json
        '''
        return self.saveCookiesToDir(cookie_dir_path)
        
    def saveCookiesToDir(self, cookie_dir_path: str = './usercookies') -> str:
        """
        alias of save_cookies
        """
        cookie_dir_path = self.DEFAULT_PATH_DIR if not cookie_dir_path else cookie_dir_path
        if not cookie_dir_path.endswith("/"):
            cookie_dir_path += "/"
        cookie_path = cookie_dir_path + f"{self.email}.json"
        if not os.path.exists(cookie_dir_path):
            logging.info("Cookie directory not exist, creating...")
            os.makedirs(cookie_dir_path)
        logging.info(f"Cookie store path: {cookie_path}")

        with open(cookie_path, "w", encoding="utf-8") as f:
            f.write(json.dumps(self.cookies.get_dict()))
        return cookie_path
    
    def load_cookies(self, cookie_dir_path: str = './usercookies') -> requests.sessions.RequestsCookieJar:
        '''
        cookies will be loaded from: cookie_dir_path/<email>.json
        '''
        return self.loadCookiesFromDir(cookie_dir_path)
    
    def loadCookiesFromDir(self, cookie_dir_path: str = './usercookies') -> requests.sessions.RequestsCookieJar:
        """
        alias of load_cookies
        """
        cookie_dir_path = self.DEFAULT_PATH_DIR if not cookie_dir_path else cookie_dir_path
        cookie_path = self._get_cookie_path(cookie_dir_path)
        if not cookie_path:
            raise Exception(f"Cookie not found. please check the path given: {cookie_dir_path}.\n" +
                            f"Cookie file must be named like this: 'your_email'+'.json': '{self.email}.json'")

        with open(cookie_path, "r", encoding="utf-8") as f:
            try:
                js = json.loads(f.read())
                for i in js.keys():
                    self.cookies.set(i, js[i])
                    logging.info(f"{i} loaded")
                return self.cookies
            except:
                raise Exception("load cookies from files fatal. Please check the format")
    
    def _request_get(self, url: str, params=None, allow_redirects=True) -> requests.Response:
        res = requests.get(
            url,
            params=params,
            headers=self.headers,
            cookies=self.cookies,
            allow_redirects=allow_redirects,
        )
        self._refresh_cookies(res.cookies)
        return res

    def _request_post(self, url: str, headers=None, params=None, data=None, stream=False,
                     allow_redirects=True) -> requests.Response:
        res = requests.post(
            url,
            stream=stream,
            params=params,
            data=data,
            headers=self.headers if headers is None else headers,
            cookies=self.cookies,
            allow_redirects=allow_redirects
        )
        self._refresh_cookies(res.cookies)
        return res

    def _refresh_cookies(self, cookies: requests.sessions.RequestsCookieJar):
        dic = cookies.get_dict()
        for i in dic:
            self.cookies.set(i, dic[i])

    def _sign_in_with_email(self):
        """
        Login through your email and password.
        PS: I found that it doesn't have any type of encryption till now,
        which could expose your password to the internet.
        """
        url = "https://huggingface.co/login"
        data = {
            "username": self.email,
            "password": self.passwd,
        }
        res = self._request_post(url=url, data=data, allow_redirects=False)
        if res.status_code == 400:
            raise Exception("wrong username or password")

    def _get_auth_url(self):
        url = "https://huggingface.co/chat/login"
        headers = {
            "Referer": "https://huggingface.co/chat/login",
            "User-Agent": self.headers["User-Agent"],
            "Content-Type": "application/x-www-form-urlencoded"
        }
        res = self._request_post(url, headers=headers, allow_redirects=False)
        if res.status_code == 200:
            # location = res.headers.get("Location", None)
            location = res.json()["location"]
            if location:
                return location
            else:
                raise Exception("No authorize url found, please check your email or password.")
        elif res.status_code == 303:
            location = res.headers.get("Location")
            if location:
                return location
            else:
                raise Exception("No authorize url found, please check your email or password.")
        else:
            raise Exception("Something went wrong!")

    def _grant_auth(self, url: str) -> int:
        res = self._request_get(url, allow_redirects=False)
        if res.headers.__contains__("location"):
            location = res.headers["location"]
            res = self._request_get(location, allow_redirects=False)
            if res.cookies.__contains__("hf-chat"):
                return 1
        # raise Exception("grantAuth fatal")
        if res.status_code != 200:
            raise Exception("grant auth fatal!")
        csrf = re.findall('/oauth/authorize.*?name="csrf" value="(.*?)"', res.text)
        if len(csrf) == 0:
            raise Exception("No csrf found!")
        data = {
            "csrf": csrf[0]
        }

        res = self._request_post(url, data=data, allow_redirects=False)
        if res.status_code != 303:
            raise Exception(f"get hf-chat cookies fatal! - {res.status_code}")
        else:
            location = res.headers.get("Location")
        res = self._request_get(location, allow_redirects=False)
        if res.status_code != 302:
            raise Exception(f"get hf-chat cookie fatal! - {res.status_code}")
        else:
            return 1

    def _get_cookie_path(self, cookie_dir_path) -> str:
        if not cookie_dir_path.endswith("/"):
            cookie_dir_path += "/"
        if not os.path.exists(cookie_dir_path):
            return ""
        files = os.listdir(cookie_dir_path)
        for i in files:
            if i == f"{self.email}.json":
                return cookie_dir_path + i
        return ""
"}}}"