mirror of
				https://github.com/microsoft/autogen.git
				synced 2025-11-04 03:39:52 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			843 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			843 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
{
 | 
						||
 "cells": [
 | 
						||
  {
 | 
						||
   "attachments": {},
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {
 | 
						||
    "slideshow": {
 | 
						||
     "slide_type": "slide"
 | 
						||
    }
 | 
						||
   },
 | 
						||
   "source": [
 | 
						||
    "# Agent Chat with custom model loading\n",
 | 
						||
    "\n",
 | 
						||
    "In this notebook, we demonstrate how a custom model can be defined and loaded, and what protocol it needs to comply to.\n",
 | 
						||
    "\n",
 | 
						||
    "**NOTE: Depending on what model you use, you may need to play with the default prompts of the Agent's**\n",
 | 
						||
    "\n",
 | 
						||
    "## Requirements\n",
 | 
						||
    "\n",
 | 
						||
    "````{=mdx}\n",
 | 
						||
    ":::info Requirements\n",
 | 
						||
    "Some extra dependencies are needed for this notebook, which can be installed via pip:\n",
 | 
						||
    "\n",
 | 
						||
    "```bash\n",
 | 
						||
    "pip install pyautogen torch transformers sentencepiece\n",
 | 
						||
    "```\n",
 | 
						||
    "\n",
 | 
						||
    "For more information, please refer to the [installation guide](/docs/installation/).\n",
 | 
						||
    ":::\n",
 | 
						||
    "````"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "from types import SimpleNamespace\n",
 | 
						||
    "\n",
 | 
						||
    "from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig\n",
 | 
						||
    "\n",
 | 
						||
    "import autogen\n",
 | 
						||
    "from autogen import AssistantAgent, UserProxyAgent"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Create and configure the custom model\n",
 | 
						||
    "\n",
 | 
						||
    "A custom model class can be created in many ways, but needs to adhere to the `ModelClient` protocol and response structure which is defined in client.py and shown below.\n",
 | 
						||
    "\n",
 | 
						||
    "The response protocol has some minimum requirements, but can be extended to include any additional information that is needed.\n",
 | 
						||
    "Message retrieval therefore can be customized, but needs to return a list of strings or a list of `ModelClientResponseProtocol.Choice.Message` objects.\n",
 | 
						||
    "\n",
 | 
						||
    "\n",
 | 
						||
    "```python\n",
 | 
						||
    "class ModelClient(Protocol):\n",
 | 
						||
    "    \"\"\"\n",
 | 
						||
    "    A client class must implement the following methods:\n",
 | 
						||
    "    - create must return a response object that implements the ModelClientResponseProtocol\n",
 | 
						||
    "    - cost must return the cost of the response\n",
 | 
						||
    "    - get_usage must return a dict with the following keys:\n",
 | 
						||
    "        - prompt_tokens\n",
 | 
						||
    "        - completion_tokens\n",
 | 
						||
    "        - total_tokens\n",
 | 
						||
    "        - cost\n",
 | 
						||
    "        - model\n",
 | 
						||
    "\n",
 | 
						||
    "    This class is used to create a client that can be used by OpenAIWrapper.\n",
 | 
						||
    "    The response returned from create must adhere to the ModelClientResponseProtocol but can be extended however needed.\n",
 | 
						||
    "    The message_retrieval method must be implemented to return a list of str or a list of messages from the response.\n",
 | 
						||
    "    \"\"\"\n",
 | 
						||
    "\n",
 | 
						||
    "    RESPONSE_USAGE_KEYS = [\"prompt_tokens\", \"completion_tokens\", \"total_tokens\", \"cost\", \"model\"]\n",
 | 
						||
    "\n",
 | 
						||
    "    class ModelClientResponseProtocol(Protocol):\n",
 | 
						||
    "        class Choice(Protocol):\n",
 | 
						||
    "            class Message(Protocol):\n",
 | 
						||
    "                content: Optional[str]\n",
 | 
						||
    "\n",
 | 
						||
    "            message: Message\n",
 | 
						||
    "\n",
 | 
						||
    "        choices: List[Choice]\n",
 | 
						||
    "        model: str\n",
 | 
						||
    "\n",
 | 
						||
    "    def create(self, params) -> ModelClientResponseProtocol:\n",
 | 
						||
    "        ...\n",
 | 
						||
    "\n",
 | 
						||
    "    def message_retrieval(\n",
 | 
						||
    "        self, response: ModelClientResponseProtocol\n",
 | 
						||
    "    ) -> Union[List[str], List[ModelClient.ModelClientResponseProtocol.Choice.Message]]:\n",
 | 
						||
    "        \"\"\"\n",
 | 
						||
    "        Retrieve and return a list of strings or a list of Choice.Message from the response.\n",
 | 
						||
    "\n",
 | 
						||
    "        NOTE: if a list of Choice.Message is returned, it currently needs to contain the fields of OpenAI's ChatCompletion Message object,\n",
 | 
						||
    "        since that is expected for function or tool calling in the rest of the codebase at the moment, unless a custom agent is being used.\n",
 | 
						||
    "        \"\"\"\n",
 | 
						||
    "        ...\n",
 | 
						||
    "\n",
 | 
						||
    "    def cost(self, response: ModelClientResponseProtocol) -> float:\n",
 | 
						||
    "        ...\n",
 | 
						||
    "\n",
 | 
						||
    "    @staticmethod\n",
 | 
						||
    "    def get_usage(response: ModelClientResponseProtocol) -> Dict:\n",
 | 
						||
    "        \"\"\"Return usage summary of the response using RESPONSE_USAGE_KEYS.\"\"\"\n",
 | 
						||
    "        ...\n",
 | 
						||
    "```\n"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Example of simple custom client\n",
 | 
						||
    "\n",
 | 
						||
    "Following the huggingface example for using [Mistral's Open-Orca](https://huggingface.co/Open-Orca/Mistral-7B-OpenOrca)\n",
 | 
						||
    "\n",
 | 
						||
    "For the response object, python's `SimpleNamespace` is used to create a simple object that can be used to store the response data, but any object that follows the `ClientResponseProtocol` can be used.\n"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "# custom client with custom model loader\n",
 | 
						||
    "\n",
 | 
						||
    "\n",
 | 
						||
    "class CustomModelClient:\n",
 | 
						||
    "    def __init__(self, config, **kwargs):\n",
 | 
						||
    "        print(f\"CustomModelClient config: {config}\")\n",
 | 
						||
    "        self.device = config.get(\"device\", \"cpu\")\n",
 | 
						||
    "        self.model = AutoModelForCausalLM.from_pretrained(config[\"model\"]).to(self.device)\n",
 | 
						||
    "        self.model_name = config[\"model\"]\n",
 | 
						||
    "        self.tokenizer = AutoTokenizer.from_pretrained(config[\"model\"], use_fast=False)\n",
 | 
						||
    "        self.tokenizer.pad_token_id = self.tokenizer.eos_token_id\n",
 | 
						||
    "\n",
 | 
						||
    "        # params are set by the user and consumed by the user since they are providing a custom model\n",
 | 
						||
    "        # so anything can be done here\n",
 | 
						||
    "        gen_config_params = config.get(\"params\", {})\n",
 | 
						||
    "        self.max_length = gen_config_params.get(\"max_length\", 256)\n",
 | 
						||
    "\n",
 | 
						||
    "        print(f\"Loaded model {config['model']} to {self.device}\")\n",
 | 
						||
    "\n",
 | 
						||
    "    def create(self, params):\n",
 | 
						||
    "        if params.get(\"stream\", False) and \"messages\" in params:\n",
 | 
						||
    "            raise NotImplementedError(\"Local models do not support streaming.\")\n",
 | 
						||
    "        else:\n",
 | 
						||
    "            num_of_responses = params.get(\"n\", 1)\n",
 | 
						||
    "\n",
 | 
						||
    "            # can create my own data response class\n",
 | 
						||
    "            # here using SimpleNamespace for simplicity\n",
 | 
						||
    "            # as long as it adheres to the ClientResponseProtocol\n",
 | 
						||
    "\n",
 | 
						||
    "            response = SimpleNamespace()\n",
 | 
						||
    "\n",
 | 
						||
    "            inputs = self.tokenizer.apply_chat_template(\n",
 | 
						||
    "                params[\"messages\"], return_tensors=\"pt\", add_generation_prompt=True\n",
 | 
						||
    "            ).to(self.device)\n",
 | 
						||
    "            inputs_length = inputs.shape[-1]\n",
 | 
						||
    "\n",
 | 
						||
    "            # add inputs_length to max_length\n",
 | 
						||
    "            max_length = self.max_length + inputs_length\n",
 | 
						||
    "            generation_config = GenerationConfig(\n",
 | 
						||
    "                max_length=max_length,\n",
 | 
						||
    "                eos_token_id=self.tokenizer.eos_token_id,\n",
 | 
						||
    "                pad_token_id=self.tokenizer.eos_token_id,\n",
 | 
						||
    "            )\n",
 | 
						||
    "\n",
 | 
						||
    "            response.choices = []\n",
 | 
						||
    "            response.model = self.model_name\n",
 | 
						||
    "\n",
 | 
						||
    "            for _ in range(num_of_responses):\n",
 | 
						||
    "                outputs = self.model.generate(inputs, generation_config=generation_config)\n",
 | 
						||
    "                # Decode only the newly generated text, excluding the prompt\n",
 | 
						||
    "                text = self.tokenizer.decode(outputs[0, inputs_length:])\n",
 | 
						||
    "                choice = SimpleNamespace()\n",
 | 
						||
    "                choice.message = SimpleNamespace()\n",
 | 
						||
    "                choice.message.content = text\n",
 | 
						||
    "                choice.message.function_call = None\n",
 | 
						||
    "                response.choices.append(choice)\n",
 | 
						||
    "\n",
 | 
						||
    "            return response\n",
 | 
						||
    "\n",
 | 
						||
    "    def message_retrieval(self, response):\n",
 | 
						||
    "        \"\"\"Retrieve the messages from the response.\"\"\"\n",
 | 
						||
    "        choices = response.choices\n",
 | 
						||
    "        return [choice.message.content for choice in choices]\n",
 | 
						||
    "\n",
 | 
						||
    "    def cost(self, response) -> float:\n",
 | 
						||
    "        \"\"\"Calculate the cost of the response.\"\"\"\n",
 | 
						||
    "        response.cost = 0\n",
 | 
						||
    "        return 0\n",
 | 
						||
    "\n",
 | 
						||
    "    @staticmethod\n",
 | 
						||
    "    def get_usage(response):\n",
 | 
						||
    "        # returns a dict of prompt_tokens, completion_tokens, total_tokens, cost, model\n",
 | 
						||
    "        # if usage needs to be tracked, else None\n",
 | 
						||
    "        return {}"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "attachments": {},
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Set your API Endpoint\n",
 | 
						||
    "\n",
 | 
						||
    "The [`config_list_from_json`](https://microsoft.github.io/autogen/docs/reference/oai/openai_utils#config_list_from_json) function loads a list of configurations from an environment variable or a json file.\n",
 | 
						||
    "\n",
 | 
						||
    "It first looks for an environment variable of a specified name (\"OAI_CONFIG_LIST\" in this example), which needs to be a valid json string. If that variable is not found, it looks for a json file with the same name. It filters the configs by models (you can filter by other keys as well).\n",
 | 
						||
    "\n",
 | 
						||
    "The json looks like the following:\n",
 | 
						||
    "```json\n",
 | 
						||
    "[\n",
 | 
						||
    "    {\n",
 | 
						||
    "        \"model\": \"gpt-4\",\n",
 | 
						||
    "        \"api_key\": \"<your OpenAI API key here>\"\n",
 | 
						||
    "    },\n",
 | 
						||
    "    {\n",
 | 
						||
    "        \"model\": \"gpt-4\",\n",
 | 
						||
    "        \"api_key\": \"<your Azure OpenAI API key here>\",\n",
 | 
						||
    "        \"base_url\": \"<your Azure OpenAI API base here>\",\n",
 | 
						||
    "        \"api_type\": \"azure\",\n",
 | 
						||
    "        \"api_version\": \"2024-02-01\"\n",
 | 
						||
    "    },\n",
 | 
						||
    "    {\n",
 | 
						||
    "        \"model\": \"gpt-4-32k\",\n",
 | 
						||
    "        \"api_key\": \"<your Azure OpenAI API key here>\",\n",
 | 
						||
    "        \"base_url\": \"<your Azure OpenAI API base here>\",\n",
 | 
						||
    "        \"api_type\": \"azure\",\n",
 | 
						||
    "        \"api_version\": \"2024-02-01\"\n",
 | 
						||
    "    }\n",
 | 
						||
    "]\n",
 | 
						||
    "```\n",
 | 
						||
    "\n",
 | 
						||
    "You can set the value of config_list in any way you prefer. Please refer to this [notebook](https://github.com/microsoft/autogen/blob/main/notebook/oai_openai_utils.ipynb) for full code examples of the different methods."
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Set the config for the custom model\n",
 | 
						||
    "\n",
 | 
						||
    "You can add any paramteres that are needed for the custom model loading in the same configuration list.\n",
 | 
						||
    "\n",
 | 
						||
    "It is important to add the `model_client_cls` field and set it to a string that corresponds to the class name: `\"CustomModelClient\"`.\n",
 | 
						||
    "\n",
 | 
						||
    "```json\n",
 | 
						||
    "{\n",
 | 
						||
    "    \"model\": \"Open-Orca/Mistral-7B-OpenOrca\",\n",
 | 
						||
    "    \"model_client_cls\": \"CustomModelClient\",\n",
 | 
						||
    "    \"device\": \"cuda\",\n",
 | 
						||
    "    \"n\": 1,\n",
 | 
						||
    "    \"params\": {\n",
 | 
						||
    "        \"max_length\": 1000,\n",
 | 
						||
    "    }\n",
 | 
						||
    "},\n",
 | 
						||
    "```"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "config_list_custom = autogen.config_list_from_json(\n",
 | 
						||
    "    \"OAI_CONFIG_LIST\",\n",
 | 
						||
    "    filter_dict={\"model_client_cls\": [\"CustomModelClient\"]},\n",
 | 
						||
    ")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "attachments": {},
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Construct Agents\n",
 | 
						||
    "\n",
 | 
						||
    "Consturct a simple conversation between a User proxy and an Assistent agent"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "assistant = AssistantAgent(\"assistant\", llm_config={\"config_list\": config_list_custom})\n",
 | 
						||
    "user_proxy = UserProxyAgent(\n",
 | 
						||
    "    \"user_proxy\",\n",
 | 
						||
    "    code_execution_config={\n",
 | 
						||
    "        \"work_dir\": \"coding\",\n",
 | 
						||
    "        \"use_docker\": False,  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.\n",
 | 
						||
    "    },\n",
 | 
						||
    ")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Register the custom client class to the assistant agent"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "assistant.register_model_client(model_client_cls=CustomModelClient)"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "user_proxy.initiate_chat(assistant, message=\"Write python code to print Hello World!\")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Register a custom client class with a pre-loaded model\n",
 | 
						||
    "\n",
 | 
						||
    "If you want to have more control over when the model gets loaded, you can load the model yourself and pass it as an argument to the CustomClient during registration"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "# custom client with custom model loader\n",
 | 
						||
    "\n",
 | 
						||
    "\n",
 | 
						||
    "class CustomModelClientWithArguments(CustomModelClient):\n",
 | 
						||
    "    def __init__(self, config, loaded_model, tokenizer, **kwargs):\n",
 | 
						||
    "        print(f\"CustomModelClientWithArguments config: {config}\")\n",
 | 
						||
    "\n",
 | 
						||
    "        self.model_name = config[\"model\"]\n",
 | 
						||
    "        self.model = loaded_model\n",
 | 
						||
    "        self.tokenizer = tokenizer\n",
 | 
						||
    "\n",
 | 
						||
    "        self.device = config.get(\"device\", \"cpu\")\n",
 | 
						||
    "\n",
 | 
						||
    "        gen_config_params = config.get(\"params\", {})\n",
 | 
						||
    "        self.max_length = gen_config_params.get(\"max_length\", 256)\n",
 | 
						||
    "        print(f\"Loaded model {config['model']} to {self.device}\")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "# load model here\n",
 | 
						||
    "\n",
 | 
						||
    "\n",
 | 
						||
    "config = config_list_custom[0]\n",
 | 
						||
    "device = config.get(\"device\", \"cpu\")\n",
 | 
						||
    "loaded_model = AutoModelForCausalLM.from_pretrained(config[\"model\"]).to(device)\n",
 | 
						||
    "tokenizer = AutoTokenizer.from_pretrained(config[\"model\"], use_fast=False)\n",
 | 
						||
    "tokenizer.pad_token_id = tokenizer.eos_token_id"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "markdown",
 | 
						||
   "metadata": {},
 | 
						||
   "source": [
 | 
						||
    "## Add the config of the new custom model\n",
 | 
						||
    "\n",
 | 
						||
    "```json\n",
 | 
						||
    "{\n",
 | 
						||
    "    \"model\": \"Open-Orca/Mistral-7B-OpenOrca\",\n",
 | 
						||
    "    \"model_client_cls\": \"CustomModelClientWithArguments\",\n",
 | 
						||
    "    \"device\": \"cuda\",\n",
 | 
						||
    "    \"n\": 1,\n",
 | 
						||
    "    \"params\": {\n",
 | 
						||
    "        \"max_length\": 1000,\n",
 | 
						||
    "    }\n",
 | 
						||
    "},\n",
 | 
						||
    "```"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "config_list_custom = autogen.config_list_from_json(\n",
 | 
						||
    "    \"OAI_CONFIG_LIST\",\n",
 | 
						||
    "    filter_dict={\"model_client_cls\": [\"CustomModelClientWithArguments\"]},\n",
 | 
						||
    ")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "assistant = AssistantAgent(\"assistant\", llm_config={\"config_list\": config_list_custom})"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "assistant.register_model_client(\n",
 | 
						||
    "    model_client_cls=CustomModelClientWithArguments,\n",
 | 
						||
    "    loaded_model=loaded_model,\n",
 | 
						||
    "    tokenizer=tokenizer,\n",
 | 
						||
    ")"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  {
 | 
						||
   "cell_type": "code",
 | 
						||
   "execution_count": null,
 | 
						||
   "metadata": {},
 | 
						||
   "outputs": [],
 | 
						||
   "source": [
 | 
						||
    "user_proxy.initiate_chat(assistant, message=\"Write python code to print Hello World!\")"
 | 
						||
   ]
 | 
						||
  }
 | 
						||
 ],
 | 
						||
 "metadata": {
 | 
						||
  "front_matter": {
 | 
						||
   "description": "Define and laod a custom model",
 | 
						||
   "tags": [
 | 
						||
    "custom model"
 | 
						||
   ]
 | 
						||
  },
 | 
						||
  "kernelspec": {
 | 
						||
   "display_name": "Python 3",
 | 
						||
   "language": "python",
 | 
						||
   "name": "python3"
 | 
						||
  },
 | 
						||
  "language_info": {
 | 
						||
   "codemirror_mode": {
 | 
						||
    "name": "ipython",
 | 
						||
    "version": 3
 | 
						||
   },
 | 
						||
   "file_extension": ".py",
 | 
						||
   "mimetype": "text/x-python",
 | 
						||
   "name": "python",
 | 
						||
   "nbconvert_exporter": "python",
 | 
						||
   "pygments_lexer": "ipython3",
 | 
						||
   "version": "3.9.5"
 | 
						||
  },
 | 
						||
  "vscode": {
 | 
						||
   "interpreter": {
 | 
						||
    "hash": "949777d72b0d2535278d3dc13498b2535136f6dfe0678499012e853ee9abcab1"
 | 
						||
   }
 | 
						||
  },
 | 
						||
  "widgets": {
 | 
						||
   "application/vnd.jupyter.widget-state+json": {
 | 
						||
    "state": {
 | 
						||
     "2d910cfd2d2a4fc49fc30fbbdc5576a7": {
 | 
						||
      "model_module": "@jupyter-widgets/base",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "LayoutModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/base",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "LayoutModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "LayoutView",
 | 
						||
       "align_content": null,
 | 
						||
       "align_items": null,
 | 
						||
       "align_self": null,
 | 
						||
       "border_bottom": null,
 | 
						||
       "border_left": null,
 | 
						||
       "border_right": null,
 | 
						||
       "border_top": null,
 | 
						||
       "bottom": null,
 | 
						||
       "display": null,
 | 
						||
       "flex": null,
 | 
						||
       "flex_flow": null,
 | 
						||
       "grid_area": null,
 | 
						||
       "grid_auto_columns": null,
 | 
						||
       "grid_auto_flow": null,
 | 
						||
       "grid_auto_rows": null,
 | 
						||
       "grid_column": null,
 | 
						||
       "grid_gap": null,
 | 
						||
       "grid_row": null,
 | 
						||
       "grid_template_areas": null,
 | 
						||
       "grid_template_columns": null,
 | 
						||
       "grid_template_rows": null,
 | 
						||
       "height": null,
 | 
						||
       "justify_content": null,
 | 
						||
       "justify_items": null,
 | 
						||
       "left": null,
 | 
						||
       "margin": null,
 | 
						||
       "max_height": null,
 | 
						||
       "max_width": null,
 | 
						||
       "min_height": null,
 | 
						||
       "min_width": null,
 | 
						||
       "object_fit": null,
 | 
						||
       "object_position": null,
 | 
						||
       "order": null,
 | 
						||
       "overflow": null,
 | 
						||
       "padding": null,
 | 
						||
       "right": null,
 | 
						||
       "top": null,
 | 
						||
       "visibility": null,
 | 
						||
       "width": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "454146d0f7224f038689031002906e6f": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "HBoxModel",
 | 
						||
      "state": {
 | 
						||
       "_dom_classes": [],
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "HBoxModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/controls",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "HBoxView",
 | 
						||
       "box_style": "",
 | 
						||
       "children": [
 | 
						||
        "IPY_MODEL_e4ae2b6f5a974fd4bafb6abb9d12ff26",
 | 
						||
        "IPY_MODEL_577e1e3cc4db4942b0883577b3b52755",
 | 
						||
        "IPY_MODEL_b40bdfb1ac1d4cffb7cefcb870c64d45"
 | 
						||
       ],
 | 
						||
       "layout": "IPY_MODEL_dc83c7bff2f241309537a8119dfc7555",
 | 
						||
       "tabbable": null,
 | 
						||
       "tooltip": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "577e1e3cc4db4942b0883577b3b52755": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "FloatProgressModel",
 | 
						||
      "state": {
 | 
						||
       "_dom_classes": [],
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "FloatProgressModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/controls",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "ProgressView",
 | 
						||
       "bar_style": "success",
 | 
						||
       "description": "",
 | 
						||
       "description_allow_html": false,
 | 
						||
       "layout": "IPY_MODEL_2d910cfd2d2a4fc49fc30fbbdc5576a7",
 | 
						||
       "max": 1,
 | 
						||
       "min": 0,
 | 
						||
       "orientation": "horizontal",
 | 
						||
       "style": "IPY_MODEL_74a6ba0c3cbc4051be0a83e152fe1e62",
 | 
						||
       "tabbable": null,
 | 
						||
       "tooltip": null,
 | 
						||
       "value": 1
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "6086462a12d54bafa59d3c4566f06cb2": {
 | 
						||
      "model_module": "@jupyter-widgets/base",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "LayoutModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/base",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "LayoutModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "LayoutView",
 | 
						||
       "align_content": null,
 | 
						||
       "align_items": null,
 | 
						||
       "align_self": null,
 | 
						||
       "border_bottom": null,
 | 
						||
       "border_left": null,
 | 
						||
       "border_right": null,
 | 
						||
       "border_top": null,
 | 
						||
       "bottom": null,
 | 
						||
       "display": null,
 | 
						||
       "flex": null,
 | 
						||
       "flex_flow": null,
 | 
						||
       "grid_area": null,
 | 
						||
       "grid_auto_columns": null,
 | 
						||
       "grid_auto_flow": null,
 | 
						||
       "grid_auto_rows": null,
 | 
						||
       "grid_column": null,
 | 
						||
       "grid_gap": null,
 | 
						||
       "grid_row": null,
 | 
						||
       "grid_template_areas": null,
 | 
						||
       "grid_template_columns": null,
 | 
						||
       "grid_template_rows": null,
 | 
						||
       "height": null,
 | 
						||
       "justify_content": null,
 | 
						||
       "justify_items": null,
 | 
						||
       "left": null,
 | 
						||
       "margin": null,
 | 
						||
       "max_height": null,
 | 
						||
       "max_width": null,
 | 
						||
       "min_height": null,
 | 
						||
       "min_width": null,
 | 
						||
       "object_fit": null,
 | 
						||
       "object_position": null,
 | 
						||
       "order": null,
 | 
						||
       "overflow": null,
 | 
						||
       "padding": null,
 | 
						||
       "right": null,
 | 
						||
       "top": null,
 | 
						||
       "visibility": null,
 | 
						||
       "width": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "74a6ba0c3cbc4051be0a83e152fe1e62": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "ProgressStyleModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "ProgressStyleModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "StyleView",
 | 
						||
       "bar_color": null,
 | 
						||
       "description_width": ""
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "7d3f3d9e15894d05a4d188ff4f466554": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "HTMLStyleModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "HTMLStyleModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "StyleView",
 | 
						||
       "background": null,
 | 
						||
       "description_width": "",
 | 
						||
       "font_size": null,
 | 
						||
       "text_color": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "b40bdfb1ac1d4cffb7cefcb870c64d45": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "HTMLModel",
 | 
						||
      "state": {
 | 
						||
       "_dom_classes": [],
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "HTMLModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/controls",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "HTMLView",
 | 
						||
       "description": "",
 | 
						||
       "description_allow_html": false,
 | 
						||
       "layout": "IPY_MODEL_f1355871cc6f4dd4b50d9df5af20e5c8",
 | 
						||
       "placeholder": "",
 | 
						||
       "style": "IPY_MODEL_ca245376fd9f4354af6b2befe4af4466",
 | 
						||
       "tabbable": null,
 | 
						||
       "tooltip": null,
 | 
						||
       "value": " 1/1 [00:00<00:00, 44.69it/s]"
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "ca245376fd9f4354af6b2befe4af4466": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "HTMLStyleModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "HTMLStyleModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "StyleView",
 | 
						||
       "background": null,
 | 
						||
       "description_width": "",
 | 
						||
       "font_size": null,
 | 
						||
       "text_color": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "dc83c7bff2f241309537a8119dfc7555": {
 | 
						||
      "model_module": "@jupyter-widgets/base",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "LayoutModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/base",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "LayoutModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "LayoutView",
 | 
						||
       "align_content": null,
 | 
						||
       "align_items": null,
 | 
						||
       "align_self": null,
 | 
						||
       "border_bottom": null,
 | 
						||
       "border_left": null,
 | 
						||
       "border_right": null,
 | 
						||
       "border_top": null,
 | 
						||
       "bottom": null,
 | 
						||
       "display": null,
 | 
						||
       "flex": null,
 | 
						||
       "flex_flow": null,
 | 
						||
       "grid_area": null,
 | 
						||
       "grid_auto_columns": null,
 | 
						||
       "grid_auto_flow": null,
 | 
						||
       "grid_auto_rows": null,
 | 
						||
       "grid_column": null,
 | 
						||
       "grid_gap": null,
 | 
						||
       "grid_row": null,
 | 
						||
       "grid_template_areas": null,
 | 
						||
       "grid_template_columns": null,
 | 
						||
       "grid_template_rows": null,
 | 
						||
       "height": null,
 | 
						||
       "justify_content": null,
 | 
						||
       "justify_items": null,
 | 
						||
       "left": null,
 | 
						||
       "margin": null,
 | 
						||
       "max_height": null,
 | 
						||
       "max_width": null,
 | 
						||
       "min_height": null,
 | 
						||
       "min_width": null,
 | 
						||
       "object_fit": null,
 | 
						||
       "object_position": null,
 | 
						||
       "order": null,
 | 
						||
       "overflow": null,
 | 
						||
       "padding": null,
 | 
						||
       "right": null,
 | 
						||
       "top": null,
 | 
						||
       "visibility": null,
 | 
						||
       "width": null
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "e4ae2b6f5a974fd4bafb6abb9d12ff26": {
 | 
						||
      "model_module": "@jupyter-widgets/controls",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "HTMLModel",
 | 
						||
      "state": {
 | 
						||
       "_dom_classes": [],
 | 
						||
       "_model_module": "@jupyter-widgets/controls",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "HTMLModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/controls",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "HTMLView",
 | 
						||
       "description": "",
 | 
						||
       "description_allow_html": false,
 | 
						||
       "layout": "IPY_MODEL_6086462a12d54bafa59d3c4566f06cb2",
 | 
						||
       "placeholder": "",
 | 
						||
       "style": "IPY_MODEL_7d3f3d9e15894d05a4d188ff4f466554",
 | 
						||
       "tabbable": null,
 | 
						||
       "tooltip": null,
 | 
						||
       "value": "100%"
 | 
						||
      }
 | 
						||
     },
 | 
						||
     "f1355871cc6f4dd4b50d9df5af20e5c8": {
 | 
						||
      "model_module": "@jupyter-widgets/base",
 | 
						||
      "model_module_version": "2.0.0",
 | 
						||
      "model_name": "LayoutModel",
 | 
						||
      "state": {
 | 
						||
       "_model_module": "@jupyter-widgets/base",
 | 
						||
       "_model_module_version": "2.0.0",
 | 
						||
       "_model_name": "LayoutModel",
 | 
						||
       "_view_count": null,
 | 
						||
       "_view_module": "@jupyter-widgets/base",
 | 
						||
       "_view_module_version": "2.0.0",
 | 
						||
       "_view_name": "LayoutView",
 | 
						||
       "align_content": null,
 | 
						||
       "align_items": null,
 | 
						||
       "align_self": null,
 | 
						||
       "border_bottom": null,
 | 
						||
       "border_left": null,
 | 
						||
       "border_right": null,
 | 
						||
       "border_top": null,
 | 
						||
       "bottom": null,
 | 
						||
       "display": null,
 | 
						||
       "flex": null,
 | 
						||
       "flex_flow": null,
 | 
						||
       "grid_area": null,
 | 
						||
       "grid_auto_columns": null,
 | 
						||
       "grid_auto_flow": null,
 | 
						||
       "grid_auto_rows": null,
 | 
						||
       "grid_column": null,
 | 
						||
       "grid_gap": null,
 | 
						||
       "grid_row": null,
 | 
						||
       "grid_template_areas": null,
 | 
						||
       "grid_template_columns": null,
 | 
						||
       "grid_template_rows": null,
 | 
						||
       "height": null,
 | 
						||
       "justify_content": null,
 | 
						||
       "justify_items": null,
 | 
						||
       "left": null,
 | 
						||
       "margin": null,
 | 
						||
       "max_height": null,
 | 
						||
       "max_width": null,
 | 
						||
       "min_height": null,
 | 
						||
       "min_width": null,
 | 
						||
       "object_fit": null,
 | 
						||
       "object_position": null,
 | 
						||
       "order": null,
 | 
						||
       "overflow": null,
 | 
						||
       "padding": null,
 | 
						||
       "right": null,
 | 
						||
       "top": null,
 | 
						||
       "visibility": null,
 | 
						||
       "width": null
 | 
						||
      }
 | 
						||
     }
 | 
						||
    },
 | 
						||
    "version_major": 2,
 | 
						||
    "version_minor": 0
 | 
						||
   }
 | 
						||
  }
 | 
						||
 },
 | 
						||
 "nbformat": 4,
 | 
						||
 "nbformat_minor": 2
 | 
						||
}
 |