diff --git a/autogen/agentchat/contrib/gpt_assistant_agent.py b/autogen/agentchat/contrib/gpt_assistant_agent.py index 0dcad27b16d5..244f5ed81894 100644 --- a/autogen/agentchat/contrib/gpt_assistant_agent.py +++ b/autogen/agentchat/contrib/gpt_assistant_agent.py @@ -209,10 +209,12 @@ def _invoke_assistant( for message in pending_messages: if message["content"].strip() == "": continue + # Convert message roles to 'user' or 'assistant', by calling _map_role_for_api, to comply with OpenAI API spec + api_role = self._map_role_for_api(message["role"]) self._openai_client.beta.threads.messages.create( thread_id=assistant_thread.id, content=message["content"], - role=message["role"], + role=api_role, ) # Create a new run to get responses from the assistant @@ -240,6 +242,28 @@ def _invoke_assistant( self._unread_index[sender] = len(self._oai_messages[sender]) + 1 return True, response + def _map_role_for_api(self, role: str) -> str: + """ + Maps internal message roles to the roles expected by the OpenAI Assistant API. + + Args: + role (str): The role from the internal message. + + Returns: + str: The mapped role suitable for the API. + """ + if role in ["function", "tool"]: + return "assistant" + elif role == "system": + return "system" + elif role == "user": + return "user" + elif role == "assistant": + return "assistant" + else: + # Default to 'assistant' for any other roles not recognized by the API + return "assistant" + def _get_run_response(self, thread, run): """ Waits for and processes the response of a run from the OpenAI assistant. diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index eabe6d6d4606..e19cbd56de2b 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1658,8 +1658,8 @@ async def a_generate_function_call_reply( if messages is None: messages = self._oai_messages[sender] message = messages[-1] - if "function_call" in message: - func_call = message["function_call"] + func_call = message.get("function_call") + if func_call: func_name = func_call.get("name", "") func = self._function_map.get(func_name, None) if func and inspect.iscoroutinefunction(func): diff --git a/autogen/agentchat/groupchat.py b/autogen/agentchat/groupchat.py index 2ebdf95b7d37..c6355a13b94d 100644 --- a/autogen/agentchat/groupchat.py +++ b/autogen/agentchat/groupchat.py @@ -1398,13 +1398,12 @@ async def a_resume( if not message_speaker_agent and message["name"] == self.name: message_speaker_agent = self - # Add previous messages to each agent (except their own messages and the last message, as we'll kick off the conversation with it) + # Add previous messages to each agent (except the last message, as we'll kick off the conversation with it) if i != len(messages) - 1: for agent in self._groupchat.agents: - if agent.name != message["name"]: - await self.a_send( - message, self._groupchat.agent_by_name(agent.name), request_reply=False, silent=True - ) + await self.a_send( + message, self._groupchat.agent_by_name(agent.name), request_reply=False, silent=True + ) # Add previous message to the new groupchat, if it's an admin message the name may not match so add the message directly if message_speaker_agent: diff --git a/autogen/coding/jupyter/docker_jupyter_server.py b/autogen/coding/jupyter/docker_jupyter_server.py index 83455e272380..e5896014a613 100644 --- a/autogen/coding/jupyter/docker_jupyter_server.py +++ b/autogen/coding/jupyter/docker_jupyter_server.py @@ -8,7 +8,7 @@ import uuid from pathlib import Path from types import TracebackType -from typing import Dict, Optional, Type, Union +from typing import Any, Dict, Optional, Type, Union import docker @@ -59,6 +59,7 @@ def __init__( stop_container: bool = True, docker_env: Dict[str, str] = {}, token: Union[str, GenerateToken] = GenerateToken(), + **docker_kwargs: Any, ): """Start a Jupyter kernel gateway server in a Docker container. @@ -77,6 +78,7 @@ def __init__( token (Union[str, GenerateToken], optional): Token to use for authentication. If GenerateToken is used, a random token will be generated. Empty string will be unauthenticated. + docker_kwargs (Any): Additional keyword arguments to pass to the docker container. """ if container_name is None: container_name = f"autogen-jupyterkernelgateway-{uuid.uuid4()}" @@ -118,6 +120,7 @@ def __init__( environment=env, publish_all_ports=True, name=container_name, + **docker_kwargs, ) _wait_for_ready(container) container_ports = container.ports diff --git a/autogen/coding/jupyter/jupyter_client.py b/autogen/coding/jupyter/jupyter_client.py index b3de374fce9b..787009dafe2f 100644 --- a/autogen/coding/jupyter/jupyter_client.py +++ b/autogen/coding/jupyter/jupyter_client.py @@ -39,6 +39,10 @@ def _get_headers(self) -> Dict[str, str]: return {} return {"Authorization": f"token {self._connection_info.token}"} + def _get_cookies(self) -> str: + cookies = self._session.cookies.get_dict() + return "; ".join([f"{name}={value}" for name, value in cookies.items()]) + def _get_api_base_url(self) -> str: protocol = "https" if self._connection_info.use_https else "http" port = f":{self._connection_info.port}" if self._connection_info.port else "" @@ -87,7 +91,7 @@ def restart_kernel(self, kernel_id: str) -> None: def get_kernel_client(self, kernel_id: str) -> JupyterKernelClient: ws_url = f"{self._get_ws_base_url()}/api/kernels/{kernel_id}/channels" - ws = websocket.create_connection(ws_url, header=self._get_headers()) + ws = websocket.create_connection(ws_url, header=self._get_headers(), cookie=self._get_cookies()) return JupyterKernelClient(ws) diff --git a/autogen/logger/file_logger.py b/autogen/logger/file_logger.py index 37bbbd25a523..07c9c3b76a76 100644 --- a/autogen/logger/file_logger.py +++ b/autogen/logger/file_logger.py @@ -90,7 +90,7 @@ def log_chat_completion( thread_id = threading.get_ident() source_name = None if isinstance(source, str): - source_name = source + source_name = getattr(source, "name", "unknown") else: source_name = source.name try: diff --git a/autogen/oai/client.py b/autogen/oai/client.py index 3ae37257b21e..4b77815e7eb7 100644 --- a/autogen/oai/client.py +++ b/autogen/oai/client.py @@ -12,10 +12,12 @@ from autogen.cache import Cache from autogen.io.base import IOStream from autogen.logger.logger_utils import get_current_ts -from autogen.oai.openai_utils import OAI_PRICE1K, get_key, is_valid_api_key +from autogen.oai.openai_utils import OAI_PRICE1K, get_key from autogen.runtime_logging import log_chat_completion, log_new_client, log_new_wrapper, logging_enabled from autogen.token_count_utils import count_token +from .rate_limiters import RateLimiter, TimeRateLimiter + TOOL_ENABLED = False try: import openai @@ -163,11 +165,7 @@ class OpenAIClient: def __init__(self, client: Union[OpenAI, AzureOpenAI]): self._oai_client = client - if ( - not isinstance(client, openai.AzureOpenAI) - and str(client.base_url).startswith(OPEN_API_BASE_URL_PREFIX) - and not is_valid_api_key(self._oai_client.api_key) - ): + if not isinstance(client, openai.AzureOpenAI) and str(client.base_url).startswith(OPEN_API_BASE_URL_PREFIX): logger.warning( "The API key specified is not a valid OpenAI format; it won't work with the OpenAI-hosted model." ) @@ -207,7 +205,9 @@ def create(self, params: Dict[str, Any]) -> ChatCompletion: """ iostream = IOStream.get_default() - completions: Completions = self._oai_client.chat.completions if "messages" in params else self._oai_client.completions # type: ignore [attr-defined] + completions: Completions = ( + self._oai_client.chat.completions if "messages" in params else self._oai_client.completions + ) # type: ignore [attr-defined] # If streaming is enabled and has messages, then iterate over the chunks of the response. if params.get("stream", False) and "messages" in params: response_contents = [""] * params.get("n", 1) @@ -279,7 +279,12 @@ def create(self, params: Dict[str, Any]) -> ChatCompletion: # Prepare the final ChatCompletion object based on the accumulated data model = chunk.model.replace("gpt-35", "gpt-3.5") # hack for Azure API - prompt_tokens = count_token(params["messages"], model) + try: + prompt_tokens = count_token(params["messages"], model) + except NotImplementedError as e: + # Catch token calculation error if streaming with customized models. + logger.warning(str(e)) + prompt_tokens = 0 response = ChatCompletion( id=chunk.id, model=chunk.model, @@ -422,8 +427,11 @@ def __init__(self, *, config_list: Optional[List[Dict[str, Any]]] = None, **base self._clients: List[ModelClient] = [] self._config_list: List[Dict[str, Any]] = [] + self._rate_limiters: List[Optional[RateLimiter]] = [] if config_list: + self._initialize_rate_limiters(config_list) + config_list = [config.copy() for config in config_list] # make a copy before modifying for config in config_list: self._register_default_client(config, openai_config) # could modify the config @@ -744,6 +752,7 @@ def yes_or_no_filter(context, response): return response continue # filter is not passed; try the next config try: + self._throttle_api_calls(i) request_ts = get_current_ts() response = client.create(params) except APITimeoutError as err: @@ -1037,3 +1046,20 @@ def extract_text_or_completion_object( A list of text, or a list of ChatCompletion objects if function_call/tool_calls are present. """ return response.message_retrieval_function(response) + + def _throttle_api_calls(self, idx: int) -> None: + """Rate limit api calls.""" + if self._rate_limiters[idx]: + limiter = self._rate_limiters[idx] + + assert limiter is not None + limiter.sleep() + + def _initialize_rate_limiters(self, config_list: List[Dict[str, Any]]) -> None: + for config in config_list: + # Instantiate the rate limiter + if "api_rate_limit" in config: + self._rate_limiters.append(TimeRateLimiter(config["api_rate_limit"])) + del config["api_rate_limit"] + else: + self._rate_limiters.append(None) diff --git a/autogen/oai/cohere.py b/autogen/oai/cohere.py index 3d38d86425fb..e9a89c9cabd8 100644 --- a/autogen/oai/cohere.py +++ b/autogen/oai/cohere.py @@ -148,7 +148,6 @@ def create(self, params: Dict) -> ChatCompletion: client_name = params.get("client_name") or "autogen-cohere" # Parse parameters to the Cohere API's parameters cohere_params = self.parse_params(params) - # Convert AutoGen messages to Cohere messages cohere_messages, preamble, final_message = oai_messages_to_cohere_messages(messages, params, cohere_params) @@ -169,6 +168,7 @@ def create(self, params: Dict) -> ChatCompletion: cohere_finish = "" max_retries = 5 + for attempt in range(max_retries): ans = None try: @@ -176,6 +176,7 @@ def create(self, params: Dict) -> ChatCompletion: response = client.chat_stream(**cohere_params) else: response = client.chat(**cohere_params) + except CohereRateLimitError as e: raise RuntimeError(f"Cohere exception occurred: {e}") else: @@ -303,6 +304,15 @@ def extract_to_cohere_tool_results(tool_call_id: str, content_output: str, all_t return temp_tool_results +def is_recent_tool_call(messages: list[Dict[str, Any]], tool_call_index: int): + messages_length = len(messages) + if tool_call_index == messages_length - 1: + return True + elif messages[tool_call_index + 1].get("role", "").lower() not in ("chatbot"): + return True + return False + + def oai_messages_to_cohere_messages( messages: list[Dict[str, Any]], params: Dict[str, Any], cohere_params: Dict[str, Any] ) -> tuple[list[dict[str, Any]], str, str]: @@ -322,7 +332,7 @@ def oai_messages_to_cohere_messages( cohere_messages = [] preamble = "" - + cohere_tool_names = set() # Tools if "tools" in params: cohere_tools = [] @@ -353,6 +363,7 @@ def oai_messages_to_cohere_messages( "description": tool["function"]["description"], "parameter_definitions": parameters, } + cohere_tool_names.add(tool["function"]["name"] or "") cohere_tools.append(cohere_tool) @@ -370,31 +381,48 @@ def oai_messages_to_cohere_messages( # 'content' field renamed to 'message' # tools go into tools parameter # tool_results go into tool_results parameter - messages_length = len(messages) for index, message in enumerate(messages): + if not message["content"]: + continue + if "role" in message and message["role"] == "system": # System message if preamble == "": preamble = message["content"] else: preamble = preamble + "\n" + message["content"] - elif "tool_calls" in message: + + elif message.get("tool_calls"): # Suggested tool calls, build up the list before we put it into the tool_results - for tool_call in message["tool_calls"]: + message_tool_calls = [] + for tool_call in message["tool_calls"] or []: + if (not tool_call.get("function", {}).get("name")) or tool_call.get("function", {}).get( + "name" + ) not in cohere_tool_names: + new_message = { + "role": "CHATBOT", + "message": message.get("name") + ":" + message["content"] + str(message["tool_calls"]), + } + cohere_messages.append(new_message) + continue + tool_calls.append(tool_call) + message_tool_calls.append( + { + "name": tool_call.get("function", {}).get("name"), + "parameters": json.loads(tool_call.get("function", {}).get("arguments") or "null"), + } + ) + + if not message_tool_calls: + continue # We also add the suggested tool call as a message new_message = { "role": "CHATBOT", - "message": message["content"], - "tool_calls": [ - { - "name": tool_call_.get("function", {}).get("name"), - "parameters": json.loads(tool_call_.get("function", {}).get("arguments") or "null"), - } - for tool_call_ in message["tool_calls"] - ], + "message": message.get("name") + ":" + message["content"], + "tool_calls": message_tool_calls, } cohere_messages.append(new_message) @@ -402,10 +430,19 @@ def oai_messages_to_cohere_messages( if not (tool_call_id := message.get("tool_call_id")): continue - # Convert the tool call to a result content_output = message["content"] + if tool_call_id not in [tool_call["id"] for tool_call in tool_calls]: + + new_message = { + "role": "CHATBOT", + "message": content_output, + } + cohere_messages.append(new_message) + continue + + # Convert the tool call to a result tool_results_chat_turn = extract_to_cohere_tool_results(tool_call_id, content_output, tool_calls) - if (index == messages_length - 1) or (messages[index + 1].get("role", "").lower() in ("user", "tool")): + if is_recent_tool_call(messages, index): # If the tool call is the last message or the next message is a user/tool message, this is a recent tool call. # So, we pass it into tool_results. tool_results.extend(tool_results_chat_turn) @@ -420,7 +457,7 @@ def oai_messages_to_cohere_messages( # Standard text message new_message = { "role": "USER" if message["role"] == "user" else "CHATBOT", - "message": message["content"], + "message": message.get("name") + ":" + message.get("content"), } cohere_messages.append(new_message) @@ -436,7 +473,7 @@ def oai_messages_to_cohere_messages( # So, we add a CHATBOT 'continue' message, if so. # Changed key from "content" to "message" (jaygdesai/autogen_Jay) if cohere_messages[-1]["role"].lower() == "user": - cohere_messages.append({"role": "CHATBOT", "message": "Please continue."}) + cohere_messages.append({"role": "CHATBOT", "message": "Please go ahead and follow the instructions!"}) # We return a blank message when we have tool results # TODO: Check what happens if tool_results aren't the latest message @@ -449,7 +486,7 @@ def oai_messages_to_cohere_messages( if cohere_messages[-1]["role"] == "USER": return cohere_messages[0:-1], preamble, cohere_messages[-1]["message"] else: - return cohere_messages, preamble, "Please continue." + return cohere_messages, preamble, "Please go ahead and follow the instructions!" def calculate_cohere_cost(input_tokens: int, output_tokens: int, model: str) -> float: diff --git a/autogen/oai/openai_utils.py b/autogen/oai/openai_utils.py index 41b94324118a..3844795c24f5 100644 --- a/autogen/oai/openai_utils.py +++ b/autogen/oai/openai_utils.py @@ -99,19 +99,6 @@ def get_key(config: Dict[str, Any]) -> str: return json.dumps(config, sort_keys=True) -def is_valid_api_key(api_key: str) -> bool: - """Determine if input is valid OpenAI API key. - - Args: - api_key (str): An input string to be validated. - - Returns: - bool: A boolean that indicates if input is valid OpenAI API key. - """ - api_key_re = re.compile(r"^sk-([A-Za-z0-9]+(-+[A-Za-z0-9]+)*-)?[A-Za-z0-9]{32,}$") - return bool(re.fullmatch(api_key_re, api_key)) - - def get_config_list( api_keys: List[str], base_urls: Optional[List[str]] = None, diff --git a/autogen/oai/rate_limiters.py b/autogen/oai/rate_limiters.py new file mode 100644 index 000000000000..4b84a7f99400 --- /dev/null +++ b/autogen/oai/rate_limiters.py @@ -0,0 +1,36 @@ +import time +from typing import Protocol + + +class RateLimiter(Protocol): + def sleep(self, *args, **kwargs): ... + + +class TimeRateLimiter: + """A class to implement a time-based rate limiter. + + This rate limiter ensures that a certain operation does not exceed a specified frequency. + It can be used to limit the rate of requests sent to a server or the rate of any repeated action. + """ + + def __init__(self, rate: float): + """ + Args: + rate (int): The frequency of the time-based rate limiter (NOT time interval). + """ + self._time_interval_seconds = 1.0 / rate + self._last_time_called = 0.0 + + def sleep(self, *args, **kwargs): + """Synchronously waits until enough time has passed to allow the next operation. + + If the elapsed time since the last operation is less than the required time interval, + this method will block the execution by sleeping for the remaining time. + """ + if self._elapsed_time() < self._time_interval_seconds: + time.sleep(self._time_interval_seconds - self._elapsed_time()) + + self._last_time_called = time.perf_counter() + + def _elapsed_time(self): + return time.perf_counter() - self._last_time_called diff --git a/test/agentchat/test_function_and_tool_calling.py b/test/agentchat/test_function_and_tool_calling.py index 4e0775d014c0..97727fe5c321 100644 --- a/test/agentchat/test_function_and_tool_calling.py +++ b/test/agentchat/test_function_and_tool_calling.py @@ -33,6 +33,7 @@ async def _a_tool_func_error(arg1: str, arg2: str) -> str: _tool_use_message_1 = { "role": "assistant", "content": None, + "function_call": None, "tool_calls": [ { "id": "1", @@ -56,6 +57,7 @@ async def _a_tool_func_error(arg1: str, arg2: str) -> str: _tool_use_message_1_bad_json = { "role": "assistant", "content": None, + "function_call": None, "tool_calls": [ { "id": "1", diff --git a/test/oai/test_client.py b/test/oai/test_client.py index 443ec995de48..bd8b072e6127 100755 --- a/test/oai/test_client.py +++ b/test/oai/test_client.py @@ -4,6 +4,7 @@ import shutil import sys import time +from types import SimpleNamespace import pytest @@ -31,6 +32,40 @@ OAI_CONFIG_LIST = "OAI_CONFIG_LIST" +class _MockClient: + def __init__(self, config, **kwargs): + pass + + def create(self, params): + # can create my own data response class + # here using SimpleNamespace for simplicity + # as long as it adheres to the ModelClientResponseProtocol + + response = SimpleNamespace() + response.choices = [] + response.model = "mock_model" + + text = "this is a dummy text response" + choice = SimpleNamespace() + choice.message = SimpleNamespace() + choice.message.content = text + choice.message.function_call = None + response.choices.append(choice) + return response + + def message_retrieval(self, response): + choices = response.choices + return [choice.message.content for choice in choices] + + def cost(self, response) -> float: + response.cost = 0 + return 0 + + @staticmethod + def get_usage(response): + return {} + + @pytest.mark.skipif(skip, reason="openai>=1 not installed") def test_aoai_chat_completion(): config_list = config_list_from_json( @@ -322,6 +357,32 @@ def test_cache(): assert not os.path.exists(os.path.join(cache_dir, str(LEGACY_DEFAULT_CACHE_SEED))) +def test_throttled_api_calls(): + # Api calling limited at 0.2 request per second, or 1 request per 5 seconds + rate = 1 / 5.0 + + config_list = [ + { + "model": "mock_model", + "model_client_cls": "_MockClient", + # Adding a timeout to catch false positives + "timeout": 1 / rate, + "api_rate_limit": rate, + } + ] + + client = OpenAIWrapper(config_list=config_list, cache_seed=None) + client.register_model_client(_MockClient) + + n_loops = 2 + current_time = time.time() + for _ in range(n_loops): + client.create(messages=[{"role": "user", "content": "hello"}]) + + min_expected_time = (n_loops - 1) / rate + assert time.time() - current_time > min_expected_time + + if __name__ == "__main__": # test_aoai_chat_completion() # test_oai_tool_calling_extraction() @@ -329,5 +390,6 @@ def test_cache(): test_completion() # # test_cost() # test_usage_summary() - # test_legacy_cache() - # test_cache() + test_legacy_cache() + test_cache() + test_throttled_api_calls() diff --git a/test/oai/test_rate_limiters.py b/test/oai/test_rate_limiters.py new file mode 100644 index 000000000000..a04429c0dea2 --- /dev/null +++ b/test/oai/test_rate_limiters.py @@ -0,0 +1,21 @@ +import time + +import pytest + +from autogen.oai.rate_limiters import TimeRateLimiter + + +@pytest.mark.parametrize("execute_n_times", range(5)) +def test_time_rate_limiter(execute_n_times): + current_time_seconds = time.time() + + rate = 1 + rate_limiter = TimeRateLimiter(rate) + + n_loops = 2 + for _ in range(n_loops): + rate_limiter.sleep() + + total_time = time.time() - current_time_seconds + min_expected_time = (n_loops - 1) / rate + assert total_time >= min_expected_time diff --git a/test/oai/test_utils.py b/test/oai/test_utils.py index fd81d3f9f548..b7aeaa5c2549 100755 --- a/test/oai/test_utils.py +++ b/test/oai/test_utils.py @@ -12,7 +12,7 @@ from conftest import MOCK_OPEN_AI_API_KEY import autogen # noqa: E402 -from autogen.oai.openai_utils import DEFAULT_AZURE_API_VERSION, filter_config, is_valid_api_key +from autogen.oai.openai_utils import DEFAULT_AZURE_API_VERSION, filter_config # Example environment variables ENV_VARS = { @@ -414,24 +414,5 @@ def test_tags(): assert len(list_5) == 0 -def test_is_valid_api_key(): - assert not is_valid_api_key("") - assert not is_valid_api_key("sk-") - assert not is_valid_api_key("SK-") - assert not is_valid_api_key("sk-asajsdjsd2") - assert not is_valid_api_key("FooBar") - assert not is_valid_api_key("sk-asajsdjsd22372%23kjdfdfdf2329ffUUDSDS") - assert is_valid_api_key("sk-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS") - assert is_valid_api_key("sk-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS1212121221212sssXX") - assert is_valid_api_key("sk-proj-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert is_valid_api_key("sk-0-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert is_valid_api_key("sk-aut0gen-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert is_valid_api_key("sk-aut0-gen-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert is_valid_api_key("sk-aut0--gen-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert not is_valid_api_key("sk-aut0-gen--asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert not is_valid_api_key("sk--aut0-gen-asajsdjsd22372X23kjdfdfdf2329ffUUDSDS12121212212") - assert is_valid_api_key(MOCK_OPEN_AI_API_KEY) - - if __name__ == "__main__": pytest.main() diff --git a/website/docs/FAQ.mdx b/website/docs/FAQ.mdx index 2798ae9375b2..a367a9b20635 100644 --- a/website/docs/FAQ.mdx +++ b/website/docs/FAQ.mdx @@ -37,7 +37,15 @@ Yes. You currently have two options: - Autogen can work with any API endpoint which complies with OpenAI-compatible RESTful APIs - e.g. serving local LLM via FastChat or LM Studio. Please check https://microsoft.github.io/autogen/blog/2023/07/14/Local-LLMs for an example. - You can supply your own custom model implementation and use it with Autogen. Please check https://microsoft.github.io/autogen/blog/2024/01/26/Custom-Models for more information. -## Handle Rate Limit Error and Timeout Error +## Handling API Rate Limits + +### Setting the API Rate Limit + +You can set the `api_rate_limit` in a `config_list` for an agent, which will be used to control the rate at which API requests are sent. + +- `api_rate_limit` (float): the maximum number of API requests allowed per second. + +### Handle Rate Limit Error and Timeout Error You can set `max_retries` to handle rate limit error. And you can set `timeout` to handle timeout error. They can all be specified in `llm_config` for an agent, which will be used in the OpenAI client for LLM inference. They can be set differently for different clients if they are set in the `config_list`. diff --git a/website/docs/installation/Optional-Dependencies.md b/website/docs/installation/Optional-Dependencies.md index 820b8f18827f..7d17ce50e37d 100644 --- a/website/docs/installation/Optional-Dependencies.md +++ b/website/docs/installation/Optional-Dependencies.md @@ -9,7 +9,7 @@ the option `redis`: pip install "pyautogen[redis]" ``` -See [LLM Caching](Use-Cases/agent_chat.md#llm-caching) for details. +See [LLM Caching](/docs/topics/llm-caching) for details. ## IPython Code Executor diff --git a/website/docs/topics/code-execution/user-defined-functions.ipynb b/website/docs/topics/code-execution/user-defined-functions.ipynb index ade02dfc0837..7697f7393c68 100644 --- a/website/docs/topics/code-execution/user-defined-functions.ipynb +++ b/website/docs/topics/code-execution/user-defined-functions.ipynb @@ -166,7 +166,7 @@ ], "source": [ "code = f\"\"\"\n", - "from {LocalCommandLineCodeExecutor.FUNCTIONS_MODULE} import add_two_numbers\n", + "from {executor.functions_module} import add_two_numbers\n", "\n", "print(add_two_numbers(1, 2))\n", "\"\"\"\n", @@ -207,7 +207,7 @@ ], "source": [ "code = f\"\"\"\n", - "from {LocalCommandLineCodeExecutor.FUNCTIONS_MODULE} import load_data\n", + "from {executor.functions_module} import load_data\n", "\n", "print(load_data())\n", "\"\"\"\n", @@ -359,12 +359,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n", + "\u001B[33mcode_executor_agent\u001B[0m (to code_writer):\n", "\n", "Please use the load_data function to load the data and please calculate the average age of all people.\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[33mcode_writer\u001b[0m (to code_executor_agent):\n", + "\u001B[33mcode_writer\u001B[0m (to code_executor_agent):\n", "\n", "Below is the python code to load the data using the `load_data()` function and calculate the average age of all people. \n", "\n", @@ -385,21 +385,21 @@ "This code starts by importing the `load_data()` function. It then uses this function to load the data into a variable `df`. Afterwards, it calculates the average (mean) of the 'age' column in the DataFrame, before printing the result.\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...\u001b[0m\n", - "\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n", + "\u001B[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...\u001B[0m\n", + "\u001B[33mcode_executor_agent\u001B[0m (to code_writer):\n", "\n", "exitcode: 0 (execution succeeded)\n", "Code output: The average age is 30.75\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[33mcode_writer\u001b[0m (to code_executor_agent):\n", + "\u001B[33mcode_writer\u001B[0m (to code_executor_agent):\n", "\n", "Great! The code worked fine. So, the average age of all people in the dataset is 30.75 years.\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[33mcode_executor_agent\u001b[0m (to code_writer):\n", + "\u001B[33mcode_executor_agent\u001B[0m (to code_writer):\n", "\n", "\n", "\n", diff --git a/website/docs/topics/llm_configuration.ipynb b/website/docs/topics/llm_configuration.ipynb index 0c094f6531ed..a9c42592a866 100644 --- a/website/docs/topics/llm_configuration.ipynb +++ b/website/docs/topics/llm_configuration.ipynb @@ -63,6 +63,7 @@ " \n", " - `model` (str, required): The identifier of the model to be used, such as 'gpt-4', 'gpt-3.5-turbo'.\n", " - `api_key` (str, optional): The API key required for authenticating requests to the model's API endpoint.\n", + " - `api_rate_limit` (float, optional): Specifies the maximum number of API requests permitted per second.\n", " - `base_url` (str, optional): The base URL of the API endpoint. This is the root address where API calls are directed.\n", " - `tags` (List[str], optional): Tags which can be used for filtering.\n", "\n", @@ -72,6 +73,7 @@ " {\n", " \"model\": \"gpt-4\",\n", " \"api_key\": os.environ['OPENAI_API_KEY']\n", + " \"api_rate_limit\": 60.0, // Set to allow up to 60 API requests per second.\n", " }\n", " ]\n", " ```\n", @@ -80,6 +82,7 @@ " - `model` (str, required): The deployment to be used. The model corresponds to the deployment name on Azure OpenAI.\n", " - `api_key` (str, optional): The API key required for authenticating requests to the model's API endpoint.\n", " - `api_type`: `azure`\n", + " - `api_rate_limit` (float, optional): Specifies the maximum number of API requests permitted per second.\n", " - `base_url` (str, optional): The base URL of the API endpoint. This is the root address where API calls are directed.\n", " - `api_version` (str, optional): The version of the Azure API you wish to use.\n", " - `tags` (List[str], optional): Tags which can be used for filtering.\n", @@ -100,6 +103,7 @@ " \n", " - `model` (str, required): The identifier of the model to be used, such as 'llama-7B'.\n", " - `api_key` (str, optional): The API key required for authenticating requests to the model's API endpoint.\n", + " - `api_rate_limit` (float, optional): Specifies the maximum number of API requests permitted per second.\n", " - `base_url` (str, optional): The base URL of the API endpoint. This is the root address where API calls are directed.\n", " - `tags` (List[str], optional): Tags which can be used for filtering.\n", "\n", diff --git a/website/yarn.lock b/website/yarn.lock index fd85d2a07d16..0aeb603db8d1 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2582,22 +2582,6 @@ dependencies: "@types/ms" "*" -"@types/eslint-scope@^3.7.3": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "8.56.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" - integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - "@types/estree-jsx@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.3.tgz#f8aa833ec986d82b8271a294a92ed1565bf2c66a" @@ -2605,7 +2589,7 @@ dependencies: "@types/estree" "*" -"@types/estree@*", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -2695,7 +2679,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2917,10 +2901,10 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: "@webassemblyjs/helper-numbers" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" @@ -2935,10 +2919,10 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== "@webassemblyjs/helper-numbers@1.11.6": version "1.11.6" @@ -2954,15 +2938,15 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/ieee754@1.11.6": version "1.11.6" @@ -2983,59 +2967,59 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-api-error" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -3056,10 +3040,10 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn-jsx@^5.0.0: version "5.3.2" @@ -3501,14 +3485,14 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.1, browserslist@^4.22.2: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.1, browserslist@^4.22.2: version "4.22.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== @@ -4582,10 +4566,10 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.15.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5316,7 +5300,7 @@ graceful-fs@4.2.10: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -7153,11 +7137,11 @@ micromark@^4.0.0: micromark-util-types "^2.0.0" micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": @@ -9858,7 +9842,7 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7, terser-webpack-plugin@^5.3.9: +terser-webpack-plugin@^5.3.10, terser-webpack-plugin@^5.3.9: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== @@ -10336,10 +10320,10 @@ vfile@^6.0.0, vfile@^6.0.1: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -10442,33 +10426,32 @@ webpack-sources@^3.2.2, webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.73.0, webpack@^5.88.1: - version "5.89.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" - integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" webpackbar@^5.0.2: