From b30af906be535c6687d96e1461a8e4e639c606d1 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 09:31:11 -0500 Subject: [PATCH 01/26] add message field --- autogen/agentchat/chat.py | 50 +- .../agentchat/contrib/compressible_agent.py | 19 +- .../contrib/math_user_proxy_agent.py | 8 +- .../contrib/retrieve_user_proxy_agent.py | 30 +- autogen/agentchat/conversable_agent.py | 179 ++- autogen/agentchat/user_proxy_agent.py | 1 - notebook/agentchat_MathChat.ipynb | 774 ++++++++++- notebook/agentchat_RetrieveChat.ipynb | 20 +- ...at_auto_feedback_from_code_execution.ipynb | 1145 ++++++++-------- notebook/agentchat_compression.ipynb | 1041 +++++--------- notebook/agentchat_groupchat_RAG.ipynb | 140 +- notebook/agentchat_qdrant_RetrieveChat.ipynb | 1219 ++++++----------- notebook/agentchats_sequential_chats.ipynb | 317 ++++- test/agentchat/test_assistant_agent.py | 30 +- test/agentchat/test_chats.py | 87 +- test/agentchat/test_math_user_proxy_agent.py | 12 +- .../blog/2023-10-18-RetrieveChat/index.mdx | 3 +- 17 files changed, 2686 insertions(+), 2389 deletions(-) diff --git a/autogen/agentchat/chat.py b/autogen/agentchat/chat.py index 6d2858c18c2e..72175b880909 100644 --- a/autogen/agentchat/chat.py +++ b/autogen/agentchat/chat.py @@ -117,13 +117,17 @@ def __post_carryover_processing(chat_info: Dict[str, Any]): if isinstance(chat_info["carryover"], list) else chat_info["carryover"] ) + message = chat_info.get("message") + if isinstance(message, str): + print_message = message + elif callable(message): + print_message = "Callable: " + message.__name__ + elif message is None: + print_message = "None" print(colored("\n" + "*" * 80, "blue"), flush=True, sep="") print( colored( - "Start a new chat with the following message: \n" - + chat_info.get("message") - + "\n\nWith the following carryover: \n" - + print_carryover, + "Starting a new chat....\n\nMessage:\n" + print_message + "\n\nCarryover: \n" + print_carryover, "blue", ), flush=True, @@ -138,35 +142,19 @@ def initiate_chats(chat_queue: List[Dict[str, Any]]) -> List[ChatResult]: chat_queue (List[Dict]): a list of dictionaries containing the information of the chats. Each dictionary should contain the input arguments for `ConversableAgent.initiate_chat`. More specifically, each dictionary could include the following fields: - - recipient: the recipient agent. - "sender": the sender agent. - "recipient": the recipient agent. - - clear_history (bool): whether to clear the chat history with the agent. Default is True. - - silent (bool or None): (Experimental) whether to print the messages for this conversation. Default is False. - - cache (Cache or None): the cache client to be used for this conversation. Default is None. - - max_turns (int or None): the maximum number of turns for the chat. If None, the chat will continue until a termination condition is met. Default is None. - - "message" needs to be provided if the `generate_init_message` method is not overridden. - Otherwise, input() will be called to get the initial message. - - "summary_method": a string or callable specifying the method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". - - Supported string are "last_msg" and "reflection_with_llm": - when set "last_msg", it returns the last message of the dialog as the summary. - when set "reflection_with_llm", it returns a summary extracted using an llm client. - `llm_config` must be set in either the recipient or sender. - "reflection_with_llm" requires the llm_config to be set in either the sender or the recipient. - - A callable summary_method should take the recipient and sender agent in a chat as input and return a string of summary. E.g, - ```python - def my_summary_method( - sender: ConversableAgent, - recipient: ConversableAgent, - ): - return recipient.last_message(sender)["content"] - ``` - - "summary_prompt": This filed can be used to specify the prompt used to extract a summary when summary_method is "reflection_with_llm". - Default is None and the following default prompt will be used when "summary_method" is set to "reflection_with_llm": - "Identify and extract the final solution to the originally asked question based on the conversation." - - "carryover": It can be used to specify the carryover information to be passed to this chat. - If provided, we will combine this carryover with the "message" content when generating the initial chat - message in `generate_init_message`. + - "clear_history" (bool): whether to clear the chat history with the agent. Default is True. + - "silent" (bool or None): (Experimental) whether to print the messages for this conversation. Default is False. + - "cache" (Cache or None): the cache client to be used for this conversation. Default is None. + - "max_turns" (int or None): the maximum number of turns for the chat. If None, the chat will continue until a termination condition is met. Default is None. + - "summary_method" (str or callable): a string or callable specifying the method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". + - "summary_args" (dict): a dictionary of arguments to be passed to the summary_method. Default is {}. + - "message" (str, callable or None): if None, input() will be called to get the initial message. + - **context: additional context information to be passed to the chat. + - "carryover": It can be used to specify the carryover information to be passed to this chat. + If provided, we will combine this carryover with the "message" content when generating the initial chat + message in `generate_init_message`. Returns: (list): a list of ChatResult objects corresponding to the finished chats in the chat_queue. diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index e196773effc9..a652ff0e90bb 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -156,9 +156,10 @@ def _set_compress_config(self, compress_config: Optional[Dict] = False): if not (isinstance(trigger_count, int) or isinstance(trigger_count, float)) or trigger_count <= 0: raise ValueError("trigger_count must be a positive number.") if isinstance(trigger_count, float) and 0 < trigger_count <= 1: - self.compress_config["trigger_count"] = int( - trigger_count * get_max_token_limit(self.llm_config["model"]) - ) + # _model = self.llm_config["model"] + # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. + _model = "gpt-3.5-turbo" + self.compress_config["trigger_count"] = int(trigger_count * get_max_token_limit(_model)) trigger_count = self.compress_config["trigger_count"] init_count = self._compute_init_token_count() if trigger_count < init_count: @@ -217,10 +218,13 @@ def _compute_init_token_count(self): return 0 func_count = 0 + # _model = self.llm_config["model"] + # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. + _model = "gpt-3.5-turbo" if "functions" in self.llm_config: - func_count = num_tokens_from_functions(self.llm_config["functions"], self.llm_config["model"]) + func_count = num_tokens_from_functions(self.llm_config["functions"], _model) - return func_count + count_token(self._oai_system_message, self.llm_config["model"]) + return func_count + count_token(self._oai_system_message, _model) def _manage_history_on_token_limit(self, messages, token_used, max_token_allowed, model): """Manage the message history with different modes when token limit is reached. @@ -295,13 +299,14 @@ def on_oai_token_limit( TODO: async compress TODO: maintain a list for old oai messages (messages before compression) """ - llm_config = self.llm_config if config is None else config if self.compress_config is False: return False, None if messages is None: messages = self._oai_messages[sender] - model = llm_config["model"] + # model = llm_config["model"] + # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. + model = "gpt-3.5-turbo" init_token_count = self._compute_init_token_count() token_used = init_token_count + count_token(messages, model) final, compressed_messages = self._manage_history_on_token_limit( diff --git a/autogen/agentchat/contrib/math_user_proxy_agent.py b/autogen/agentchat/contrib/math_user_proxy_agent.py index 67c86daf05d1..4a5d01fc2924 100644 --- a/autogen/agentchat/contrib/math_user_proxy_agent.py +++ b/autogen/agentchat/contrib/math_user_proxy_agent.py @@ -177,7 +177,8 @@ def __init__( self._previous_code = "" self.last_reply = None - def generate_init_message(self, problem, prompt_type="default", customized_prompt=None): + @staticmethod + def message_generator(sender, recipient, context): """Generate a prompt for the assistant agent with the given problem and prompt. Args: @@ -198,7 +199,10 @@ def generate_init_message(self, problem, prompt_type="default", customized_promp Returns: str: the generated prompt ready to be sent to the assistant agent. """ - self._reset() + sender._reset() + problem = context.get("problem") + prompt_type = context.get("prompt_type", "default") + customized_prompt = context.get("customized_prompt", None) if customized_prompt is not None: return customized_prompt + problem return PROMPTS[prompt_type] + problem diff --git a/autogen/agentchat/contrib/retrieve_user_proxy_agent.py b/autogen/agentchat/contrib/retrieve_user_proxy_agent.py index facb64d07efa..da86145e17e3 100644 --- a/autogen/agentchat/contrib/retrieve_user_proxy_agent.py +++ b/autogen/agentchat/contrib/retrieve_user_proxy_agent.py @@ -415,23 +415,19 @@ def retrieve_docs(self, problem: str, n_results: int = 20, search_string: str = self._results = results print("doc_ids: ", results["ids"]) - def generate_init_message(self, problem: str, n_results: int = 20, search_string: str = ""): - """Generate an initial message with the given problem and prompt. - - Args: - problem (str): the problem to be solved. - n_results (int): the number of results to be retrieved. - search_string (str): only docs containing this string will be retrieved. - - Returns: - str: the generated prompt ready to be sent to the assistant agent. - """ - self._reset() - self.retrieve_docs(problem, n_results, search_string) - self.problem = problem - self.n_results = n_results - doc_contents = self._get_context(self._results) - message = self._generate_message(doc_contents, self._task) + @staticmethod + def message_generator(sender, recipient, context): + sender._reset() + + problem = context.get("problem", "") + n_results = context.get("n_results", 20) + search_string = context.get("search_string", "") + + sender.retrieve_docs(problem, n_results, search_string) + sender.problem = problem + sender.n_results = n_results + doc_contents = sender._get_context(sender._results) + message = sender._generate_message(doc_contents, sender._task) return message def run_code(self, code, **kwargs): diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index b503403d0258..3c0e01ddb7c8 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -61,7 +61,6 @@ class ConversableAgent(LLMAgent): To modify the way to get human input, override `get_human_input` method. To modify the way to execute code blocks, single code block, or function call, override `execute_code_blocks`, `run_code`, and `execute_function` methods respectively. - To customize the initial message when a conversation starts, override `generate_init_message` method. """ DEFAULT_CONFIG = {} # An empty configuration @@ -557,10 +556,11 @@ def _process_message_before_send( def send( self, - message: Union[Dict, str], + message: Union[Dict, str, Callable], recipient: Agent, request_reply: Optional[bool] = None, silent: Optional[bool] = False, + **context, ) -> ChatResult: """Send a message to another agent. @@ -590,7 +590,8 @@ def send( recipient (Agent): the recipient of the message. request_reply (bool or None): whether to request a reply from the recipient. silent (bool or None): (Experimental) whether to print the message sent. - + **context: the context of the message, which will be passed to [OpenAIWrapper.create](../oai/client#create). + #TODO: shall we also support callable message and context in "send" Raises: ValueError: if the message can't be converted into a valid ChatCompletion message. @@ -846,13 +847,16 @@ def initiate_chat( silent: Optional[bool] = False, cache: Optional[Cache] = None, max_turns: Optional[int] = None, + summary_method: Optional[Union[str, Callable]] = DEFAULT_summary_method, + summary_args: Optional[dict] = {}, + message: Optional[Union[str, Callable]] = None, **context, ) -> ChatResult: """Initiate a chat with the recipient agent. Reset the consecutive auto reply counter. If `clear_history` is True, the chat history with the recipient agent will be cleared. - `generate_init_message` is called to generate the initial message for the agent. + Args: recipient: the recipient agent. @@ -862,9 +866,7 @@ def initiate_chat( max_turns (int or None): the maximum number of turns for the chat between the two agents. One turn means one conversation round trip. Note that this is different from [max_consecutive_auto_reply](#max_consecutive_auto_reply) which is the maximum number of consecutive auto replies; and it is also different from [max_rounds in GroupChat](./groupchat#groupchat-objects) which is the maximum number of rounds in a group chat session. If max_turns is set to None, the chat will continue until a termination condition is met. Default is None. - **context: any context information. It has the following reserved fields: - "message": a str of message. Needs to be provided. Otherwise, input() will be called to get the initial message. - "summary_method": a string or callable specifying the method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". + summary_method (string or callable) : a method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". - Supported string are "last_msg" and "reflection_with_llm": when set "last_msg", it returns the last message of the dialog as the summary. when set "reflection_with_llm", it returns a summary extracted using an llm client. @@ -875,14 +877,28 @@ def initiate_chat( def my_summary_method( sender: ConversableAgent, recipient: ConversableAgent, + summary_args: dict, ): return recipient.last_message(sender)["content"] ``` - "summary_prompt": a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflext + summary_args (dict): a dictionary of arguments to be passed to the summary_method. + E.g., a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflext on the conversation and extract a summary when summary_method is "reflection_with_llm". Default is DEFAULT_summary_prompt, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." - "carryover": a string or a list of string to specify the carryover information to be passed to this chat. It can be a string or a list of string. - If provided, we will combine this carryover with the "message" content when generating the initial chat + message (str or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. + - If a string is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. + - If a callable is provided, it will be called to get the initial message. E.g, + ```python + def my_message(sender, receiver, context): + carryover = context.get("carryover", "") + if isinstance(message, list): + carryover = carryover[-1] + final_msg = "Write a blogpost." + "\nContext: \n" + carryover + return final_msg + ``` + **context: any context information. It has the following reserved fields: + "carryover": a string or a list of string to specify the carryover information to be passed to this chat. + If provided, we will combine this carryover (by attaching a "context: "string and the carryover content after the message content) with the "message" content when generating the initial chat message in `generate_init_message`. Raises: @@ -891,8 +907,10 @@ def my_summary_method( Returns: ChatResult: an ChatResult object. """ + # TODO: check _chat_info = context.copy() _chat_info["recipient"] = recipient + _chat_info["sender"] = self consolidate_chat_info(_chat_info, uniform_sender=self) for agent in [self, recipient]: agent._raise_exception_on_async_reply_functions() @@ -902,7 +920,10 @@ def my_summary_method( self._prepare_chat(recipient, clear_history, reply_at_receive=False) for _ in range(max_turns): if _ == 0: - msg2send = self.generate_init_message(**context) + if isinstance(message, Callable): + msg2send = message(_chat_info["sender"], _chat_info["recipient"], context) + else: + msg2send = self.generate_init_message(message, **context) else: msg2send = self.generate_reply(messages=self.chat_messages[recipient], sender=recipient) if msg2send is None: @@ -910,11 +931,15 @@ def my_summary_method( self.send(msg2send, recipient, request_reply=True, silent=silent) else: self._prepare_chat(recipient, clear_history) - self.send(self.generate_init_message(**context), recipient, silent=silent) + if isinstance(message, Callable): + msg2send = message(_chat_info["sender"], _chat_info["recipient"], context) + else: + msg2send = self.generate_init_message(message, **context) + self.send(msg2send, recipient, silent=silent) summary = self._summarize_chat( - context.get("summary_method", ConversableAgent.DEFAULT_summary_method), + summary_method, + summary_args, recipient, - prompt=context.get("summary_prompt"), cache=cache, ) for agent in [self, recipient]: @@ -935,6 +960,9 @@ async def a_initiate_chat( silent: Optional[bool] = False, cache: Optional[Cache] = None, max_turns: Optional[int] = None, + summary_method: Optional[Union[str, Callable]] = DEFAULT_summary_method, + summary_args: Optional[dict] = {}, + message: Optional[Union[str, Callable]] = None, **context, ) -> ChatResult: """(async) Initiate a chat with the recipient agent. @@ -950,6 +978,7 @@ async def a_initiate_chat( """ _chat_info = context.copy() _chat_info["recipient"] = recipient + _chat_info["sender"] = self consolidate_chat_info(_chat_info, uniform_sender=self) for agent in [self, recipient]: agent.previous_cache = agent.client_cache @@ -958,7 +987,10 @@ async def a_initiate_chat( self._prepare_chat(recipient, clear_history, reply_at_receive=False) for _ in range(max_turns): if _ == 0: - msg2send = await self.a_generate_init_message(**context) + if isinstance(message, Callable): + msg2send = message(_chat_info["sender"], _chat_info["recipient"], context) + else: + msg2send = await self.a_generate_init_message(message, **context) else: msg2send = await self.a_generate_reply(messages=self.chat_messages[recipient], sender=recipient) if msg2send is None: @@ -966,11 +998,15 @@ async def a_initiate_chat( await self.a_send(msg2send, recipient, request_reply=True, silent=silent) else: self._prepare_chat(recipient, clear_history) - await self.a_send(await self.a_generate_init_message(**context), recipient, silent=silent) + if isinstance(message, Callable): + msg2send = message(_chat_info["sender"], _chat_info["recipient"], context) + else: + msg2send = await self.a_generate_init_message(message, **context) + await self.a_send(msg2send, recipient, silent=silent) summary = self._summarize_chat( - context.get("summary_method", ConversableAgent.DEFAULT_summary_method), + summary_method, + summary_args, recipient, - prompt=context.get("summary_prompt"), cache=cache, ) for agent in [self, recipient]: @@ -987,8 +1023,8 @@ async def a_initiate_chat( def _summarize_chat( self, summary_method, + summary_args, recipient: Optional[Agent] = None, - prompt: Optional[str] = None, cache: Optional[Cache] = None, ) -> str: """Get a chat summary from an agent participating in a chat. @@ -1003,32 +1039,56 @@ def my_summary_method( ): return recipient.last_message(sender)["content"] ``` + summary_args (dict): a dictionary of arguments to be passed to the summary_method. recipient: the recipient agent in a chat. prompt (str): the prompt used to get a summary when summary_method is "reflection_with_llm". Returns: str: a chat summary from the agent. """ - agent = self if recipient is None else recipient summary = "" if summary_method is None: return summary + if "cache" not in summary_args: + summary_args["cache"] = cache if summary_method == "reflection_with_llm": - prompt = ConversableAgent.DEFAULT_summary_prompt if prompt is None else prompt - if not isinstance(prompt, str): - raise ValueError("The summary_prompt must be a string.") - msg_list = agent.chat_messages_for_summary(self) - try: - summary = self._reflection_with_llm(prompt, msg_list, llm_agent=agent, cache=cache) - except BadRequestError as e: - warnings.warn(f"Cannot extract summary using reflection_with_llm: {e}", UserWarning) - elif summary_method == "last_msg" or summary_method is None: - try: - summary = agent.last_message(self)["content"].replace("TERMINATE", "") - except (IndexError, AttributeError) as e: - warnings.warn(f"Cannot extract summary using last_msg: {e}", UserWarning) - elif isinstance(summary_method, Callable): - summary = summary_method(recipient, self) + summary_method = self._relfection_with_llm_as_summary + elif summary_method == "last_msg": + summary_method = self._last_msg_as_summary + + if isinstance(summary_method, Callable): + summary = summary_method(self, recipient, summary_args) + else: + raise ValueError( + "If not None, the summary_method must be a string from [`reflection_with_llm`, `last_msg`] or a callable." + ) + return summary + + @staticmethod + def _last_msg_as_summary(sender, recipient, summary_args) -> str: + """Get a chat summary from the last message of the recipient.""" + try: + summary = recipient.last_message(sender)["content"].replace("TERMINATE", "") + except (IndexError, AttributeError) as e: + warnings.warn(f"Cannot extract summary using last_msg: {e}. Using an empty as summary.", UserWarning) + summary = "" + return summary + + @staticmethod + def _relfection_with_llm_as_summary(sender, recipient, summary_args): + prompt = summary_args.get("summary_prompt") + prompt = ConversableAgent.DEFAULT_summary_prompt if prompt is None else prompt + if not isinstance(prompt, str): + raise ValueError("The summary_prompt must be a string.") + msg_list = recipient.chat_messages_for_summary(sender) + agent = sender if recipient is None else recipient + try: + summary = sender._reflection_with_llm(prompt, msg_list, llm_agent=agent, cache=summary_args.get("cache")) + except BadRequestError as e: + warnings.warn( + f"Cannot extract summary using reflection_with_llm: {e}. Using an empty as summary.", UserWarning + ) + summary = "" return summary def _reflection_with_llm( @@ -1061,7 +1121,6 @@ def _reflection_with_llm( def initiate_chats(self, chat_queue: List[Dict[str, Any]]) -> List[ChatResult]: """(Experimental) Initiate chats with multiple agents. - TODO: add async version of this method. Args: chat_queue (List[Dict]): a list of dictionaries containing the information of the chats. @@ -2074,7 +2133,7 @@ async def a_execute_function(self, func_call): "content": str(content), } - def generate_init_message(self, **context) -> Union[str, Dict]: + def generate_init_message(self, message, **context) -> Union[str, Dict]: """Generate the initial message for the agent. TODO: offer a way to customize initial message without overriding this function. @@ -2082,49 +2141,31 @@ def generate_init_message(self, **context) -> Union[str, Dict]: If not overridden, "message" needs to be provided in the context, or input() will be called to get the initial message. Args: + message (str): the message to be processed. **context: any context information. It has the following reserved fields: - "message": a str of message. - "summary_method": a string or callable specifying the method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". - - Supported string are "last_msg" and "reflection_with_llm": - when set "last_msg", it returns the last message of the dialog as the summary. - when set "reflection_with_llm", it returns a summary extracted using an llm client. - `llm_config` must be set in either the recipient or sender. - "reflection_with_llm" requires the llm_config to be set in either the sender or the recipient. - - A callable summary_method should take the recipient and sender agent in a chat as input and return a string of summary. E.g, - ```python - def my_summary_method( - sender: ConversableAgent, - recipient: ConversableAgent, - ): - return recipient.last_message(sender)["content"] - ``` - When both the sender and the recipient have an llm client, the recipient's llm client will be used. - "summary_prompt": a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflext - on the conversation and extract a summary when summary_method is "reflection_with_llm". - Default is DEFAULT_summary_prompt, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." "carryover": a string or a list of string to specify the carryover information to be passed to this chat. It can be a string or a list of string. If provided, we will combine this carryover with the "message" content when generating the initial chat message. """ - if "message" not in context: - context["message"] = self.get_human_input(">") - self._process_carryover(context) - return context["message"] + if message is None: + message = self.get_human_input(">") + self._process_carryover(message, context) + return message - def _process_carryover(self, context): - carryover = context.get("carryover", "") + def _process_carryover(self, message, context): + carryover = context.get("carryover") if carryover: # if carryover is string if isinstance(carryover, str): - context["message"] = context["message"] + "\nContext: \n" + carryover + message += "\nContext: \n" + carryover elif isinstance(carryover, list): - context["message"] = context["message"] + "\nContext: \n" + ("\n").join([t for t in carryover]) + message += "\nContext: \n" + ("\n").join([t for t in carryover]) else: raise warnings.warn( "Carryover should be a string or a list of strings. Not adding carryover to the message." ) - async def a_generate_init_message(self, **context) -> Union[str, Dict]: + async def a_generate_init_message(self, message, **context) -> Union[str, Dict]: """Generate the initial message for the agent. TODO: offer a way to customize initial message without overriding this function. @@ -2134,10 +2175,10 @@ async def a_generate_init_message(self, **context) -> Union[str, Dict]: Args: Please refer to `generate_init_message` for the description of the arguments. """ - if "message" not in context: - context["message"] = await self.a_get_human_input(">") - self._process_carryover(context) - return context["message"] + if message is None: + message = await self.a_get_human_input(">") + self._process_carryover(message, context) + return message def register_function(self, function_map: Dict[str, Union[Callable, None]]): """Register functions to the agent. diff --git a/autogen/agentchat/user_proxy_agent.py b/autogen/agentchat/user_proxy_agent.py index dc68c6ec6d0a..12e11570562b 100644 --- a/autogen/agentchat/user_proxy_agent.py +++ b/autogen/agentchat/user_proxy_agent.py @@ -14,7 +14,6 @@ class UserProxyAgent(ConversableAgent): To modify the way to get human input, override `get_human_input` method. To modify the way to execute code blocks, single code block, or function call, override `execute_code_blocks`, `run_code`, and `execute_function` methods respectively. - To customize the initial message when a conversation starts, override `generate_init_message` method. """ # Default UserProxyAgent.description values, based on human_input_mode diff --git a/notebook/agentchat_MathChat.ipynb b/notebook/agentchat_MathChat.ipynb index bfc44fcd1bcb..42c81c763a57 100644 --- a/notebook/agentchat_MathChat.ipynb +++ b/notebook/agentchat_MathChat.ipynb @@ -48,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -59,21 +59,21 @@ "\n", "config_list = autogen.config_list_from_json(\n", " \"OAI_CONFIG_LIST\",\n", - " filter_dict={\n", - " \"model\": {\n", - " \"gpt-4\",\n", - " \"gpt4\",\n", - " \"gpt-4-32k\",\n", - " \"gpt-4-32k-0314\",\n", - " \"gpt-4-32k-v0314\",\n", - " \"gpt-3.5-turbo\",\n", - " \"gpt-3.5-turbo-16k\",\n", - " \"gpt-3.5-turbo-0301\",\n", - " \"chatgpt-35-turbo-0301\",\n", - " \"gpt-35-turbo-v0301\",\n", - " \"gpt\",\n", - " }\n", - " },\n", + " # filter_dict={\n", + " # \"model\": {\n", + " # \"gpt-4\",\n", + " # \"gpt4\",\n", + " # \"gpt-4-32k\",\n", + " # \"gpt-4-32k-0314\",\n", + " # \"gpt-4-32k-v0314\",\n", + " # \"gpt-3.5-turbo\",\n", + " # \"gpt-3.5-turbo-16k\",\n", + " # \"gpt-3.5-turbo-0301\",\n", + " # \"chatgpt-35-turbo-0301\",\n", + " # \"gpt-35-turbo-v0301\",\n", + " # \"gpt\",\n", + " # }\n", + " # },\n", ")" ] }, @@ -120,12 +120,12 @@ "source": [ "## Construct agents for MathChat\n", "\n", - "We start by initializing the `AssistantAgent` and `MathUserProxyAgent`. The system message needs to be set to \"You are a helpful assistant.\" for MathChat. The detailed instructions are given in the user message. Later we will use the `MathUserProxyAgent.generate_init_message` to combine the instructions and a math problem for an initial message to be sent to the LLM assistant." + "We start by initializing the `AssistantAgent` and `MathUserProxyAgent`. The system message needs to be set to \"You are a helpful assistant.\" for MathChat. The detailed instructions are given in the user message. Later we will use the `MathUserProxyAgent.message_generator` to combine the instructions and a math problem for an initial message to be sent to the LLM assistant." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -170,9 +170,244 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Let's use Python to solve a math problem.\n", + "\n", + "Query requirements:\n", + "You should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\n", + "You can use packages like sympy to help you.\n", + "You must follow the formats below to write your code:\n", + "```python\n", + "# your code\n", + "```\n", + "\n", + "First state the key idea to solve the problem. You may choose from three ways to solve the problem:\n", + "Case 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\n", + "Case 2: If the problem is mostly reasoning, you can solve it by yourself directly.\n", + "Case 3: If the problem cannot be handled in the above two ways, please follow this process:\n", + "1. Solve the problem step by step (do not over-divide the steps).\n", + "2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\n", + "3. Wait for me to give the results.\n", + "4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\n", + "\n", + "After all the queries are run and you get the answer, put the answer in \\boxed{}.\n", + "\n", + "Problem:\n", + "Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "Key idea: To solve the inequality $(2x+10)(x+3)<(3x+9)(x+8)$, we will expand both sides, simplify the resulting expression, and then solve for $x$. This process may involve factoring a quadratic expression and finding the critical points where the inequality may change signs. We will analyze the intervals between these points to determine where the inequality holds true.\n", + "\n", + "Let's solve the problem step by step:\n", + "\n", + "Step 1: Expand both sides of the inequality.\n", + "\n", + "```python\n", + "from sympy import symbols, expand\n", + "\n", + "x = symbols('x')\n", + "lhs = expand((2*x + 10) * (x + 3))\n", + "rhs = expand((3*x + 9) * (x + 8))\n", + "```\n", + "\n", + "Step 2: Subtract one side of the inequality from the other to move all terms to one side.\n", + "\n", + "```python\n", + "inequality = lhs - rhs\n", + "```\n", + "\n", + "Step 3: Factor the resulting expression to find the critical points (where the expression is equal to zero).\n", + "\n", + "```python\n", + "from sympy import solve\n", + "\n", + "critical_points = solve(inequality, x)\n", + "```\n", + "\n", + "Step 4: Determine intervals based on the critical points and test the intervals to find where the inequality holds true.\n", + "\n", + "```python\n", + "from sympy import solveset, S\n", + "\n", + "# finding the intervals where the inequality holds true\n", + "solution_intervals = solveset(inequality, x, domain=S.Reals)\n", + "```\n", + "\n", + "Step 5: Output the solution in interval notation.\n", + "\n", + "```python\n", + "print(solution_intervals)\n", + "```\n", + "\n", + "This will give us the solution in interval notation. Now, let's run this code step by step. Please run Step 1 to calculate the expanded forms of both sides of the inequality.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "No output found. Make sure you print the results.\n", + "Error: Traceback (most recent call last):\n", + " File \"\", line 3, in \n", + " inequality = lhs - rhs\n", + "NameError: name 'lhs' is not defined\n", + "\n", + "Error: Traceback (most recent call last):\n", + " File \"\", line 7, in \n", + " critical_points = solve(inequality, x)\n", + "NameError: name 'inequality' is not defined\n", + "\n", + "Error: Traceback (most recent call last):\n", + " File \"\", line 12, in \n", + " solution_intervals = solveset(inequality, x, domain=S.Reals)\n", + "NameError: name 'inequality' is not defined\n", + "\n", + "Error: Traceback (most recent call last):\n", + " File \"\", line 16, in \n", + " print(solution_intervals)\n", + "NameError: name 'solution_intervals' is not defined\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "I apologize for the misunderstanding. Let's correct the code by first expanding and printing out the left-hand side and the right-hand side of the inequality. Afterward, we will proceed to the next steps sequentially. Here is the corrected code for Step 1:\n", + "\n", + "```python\n", + "from sympy import symbols, expand\n", + "\n", + "x = symbols('x')\n", + "lhs = expand((2*x + 10) * (x + 3))\n", + "rhs = expand((3*x + 9) * (x + 8))\n", + "# Print the expanded left-hand side and right-hand side\n", + "print(\"Expanded LHS:\", lhs)\n", + "print(\"Expanded RHS:\", rhs)\n", + "```\n", + "\n", + "Run this code to get the expanded forms. After we confirm the expanded results are correct, we will move on to Step 2.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Expanded LHS: 2*x**2 + 16*x + 30\n", + "Expanded RHS: 3*x**2 + 33*x + 72\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The expanded forms of the left-hand side (LHS) and the right-hand side (RHS) of the inequality are correct:\n", + "\n", + "LHS: $2x^2 + 16x + 30$\n", + "\n", + "RHS: $3x^2 + 33x + 72$\n", + "\n", + "Now, let's proceed to Step 2, where we will calculate the expression resulting from subtracting the RHS from the LHS, and then move on to factoring that expression.\n", + "\n", + "```python\n", + "from sympy import simplify\n", + "\n", + "# Compute the difference between RHS and LHS\n", + "inequality_expression = simplify(lhs - rhs)\n", + "\n", + "# Print the simplified inequality expression\n", + "print(\"Inequality expression (LHS - RHS):\", inequality_expression)\n", + "```\n", + "\n", + "Run this code to obtain the simplified inequality expression. Once we have it, we can determine the next steps.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Inequality expression (LHS - RHS): -x**2 - 17*x - 42\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The simplified expression from subtracting the RHS from the LHS is correct:\n", + "\n", + "Inequality Expression: $-x^2 - 17x - 42$\n", + "\n", + "Next, we will factor this expression to determine the critical points where the expression equals zero.\n", + "\n", + "```python\n", + "from sympy import factor\n", + "\n", + "# Factor the inequality expression\n", + "factored_expression = factor(inequality_expression)\n", + "\n", + "# Print the factored inequality expression\n", + "print(\"Factored Inequality Expression:\", factored_expression)\n", + "```\n", + "\n", + "Please run this code to obtain the critical points, and we will continue from there.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Factored Inequality Expression: -(x + 3)*(x + 14)\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The factored form of the inequality expression is correct:\n", + "\n", + "Factored Inequality Expression: $-(x + 3)(x + 14)$\n", + "\n", + "The critical points are the values of $x$ for which this expression equals zero, which are $x = -3$ and $x = -14$.\n", + "\n", + "Now, we will determine the intervals formed by these critical points and analyze the sign of the inequality expression within each interval to find where the original inequality holds true.\n", + "\n", + "```python\n", + "from sympy import solveset, S\n", + "\n", + "# Determine in which intervals the inequality holds true\n", + "solution_intervals = solveset(inequality_expression < 0, x, domain=S.Reals)\n", + "\n", + "# Print the solution in interval notation\n", + "print(\"Solution Intervals:\", solution_intervals)\n", + "```\n", + "\n", + "Running this code will provide us with the intervals where the inequality is satisfied, expressed in interval notation. Please execute it to get the final answer.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Solution Intervals: Union(Interval.open(-oo, -14), Interval.open(-3, oo))\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The solution intervals where the original inequality $(2x+10)(x+3)<(3x+9)(x+8)$ holds true are:\n", + "\n", + "Solution Intervals: $(-\\infty, -14) \\cup (-3, \\infty)$\n", + "\n", + "So the complete solution to the problem in interval notation is:\n", + "\n", + "$$\\boxed{(-\\infty, -14) \\cup (-3, \\infty)}$$\n", + "\n", + "This indicates that the inequality is satisfied for all $x$ values less than $-14$ and for all $x$ values greater than $-3$.\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Let's use Python to solve a math problem.\\n\\nQuery requirements:\\nYou should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\\nYou can use packages like sympy to help you.\\nYou must follow the formats below to write your code:\\n```python\\n# your code\\n```\\n\\nFirst state the key idea to solve the problem. You may choose from three ways to solve the problem:\\nCase 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\\nCase 2: If the problem is mostly reasoning, you can solve it by yourself directly.\\nCase 3: If the problem cannot be handled in the above two ways, please follow this process:\\n1. Solve the problem step by step (do not over-divide the steps).\\n2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\\n3. Wait for me to give the results.\\n4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\\n\\nAfter all the queries are run and you get the answer, put the answer in \\\\boxed{}.\\n\\nProblem:\\nFind all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\", 'role': 'assistant'}, {'content': \"Key idea: To solve the inequality $(2x+10)(x+3)<(3x+9)(x+8)$, we will expand both sides, simplify the resulting expression, and then solve for $x$. This process may involve factoring a quadratic expression and finding the critical points where the inequality may change signs. We will analyze the intervals between these points to determine where the inequality holds true.\\n\\nLet's solve the problem step by step:\\n\\nStep 1: Expand both sides of the inequality.\\n\\n```python\\nfrom sympy import symbols, expand\\n\\nx = symbols('x')\\nlhs = expand((2*x + 10) * (x + 3))\\nrhs = expand((3*x + 9) * (x + 8))\\n```\\n\\nStep 2: Subtract one side of the inequality from the other to move all terms to one side.\\n\\n```python\\ninequality = lhs - rhs\\n```\\n\\nStep 3: Factor the resulting expression to find the critical points (where the expression is equal to zero).\\n\\n```python\\nfrom sympy import solve\\n\\ncritical_points = solve(inequality, x)\\n```\\n\\nStep 4: Determine intervals based on the critical points and test the intervals to find where the inequality holds true.\\n\\n```python\\nfrom sympy import solveset, S\\n\\n# finding the intervals where the inequality holds true\\nsolution_intervals = solveset(inequality, x, domain=S.Reals)\\n```\\n\\nStep 5: Output the solution in interval notation.\\n\\n```python\\nprint(solution_intervals)\\n```\\n\\nThis will give us the solution in interval notation. Now, let's run this code step by step. Please run Step 1 to calculate the expanded forms of both sides of the inequality.\", 'role': 'user'}, {'content': 'No output found. Make sure you print the results.\\nError: Traceback (most recent call last):\\n File \"\", line 3, in \\n inequality = lhs - rhs\\nNameError: name \\'lhs\\' is not defined\\n\\nError: Traceback (most recent call last):\\n File \"\", line 7, in \\n critical_points = solve(inequality, x)\\nNameError: name \\'inequality\\' is not defined\\n\\nError: Traceback (most recent call last):\\n File \"\", line 12, in \\n solution_intervals = solveset(inequality, x, domain=S.Reals)\\nNameError: name \\'inequality\\' is not defined\\n\\nError: Traceback (most recent call last):\\n File \"\", line 16, in \\n print(solution_intervals)\\nNameError: name \\'solution_intervals\\' is not defined', 'role': 'assistant'}, {'content': 'I apologize for the misunderstanding. Let\\'s correct the code by first expanding and printing out the left-hand side and the right-hand side of the inequality. Afterward, we will proceed to the next steps sequentially. Here is the corrected code for Step 1:\\n\\n```python\\nfrom sympy import symbols, expand\\n\\nx = symbols(\\'x\\')\\nlhs = expand((2*x + 10) * (x + 3))\\nrhs = expand((3*x + 9) * (x + 8))\\n# Print the expanded left-hand side and right-hand side\\nprint(\"Expanded LHS:\", lhs)\\nprint(\"Expanded RHS:\", rhs)\\n```\\n\\nRun this code to get the expanded forms. After we confirm the expanded results are correct, we will move on to Step 2.', 'role': 'user'}, {'content': 'Expanded LHS: 2*x**2 + 16*x + 30\\nExpanded RHS: 3*x**2 + 33*x + 72', 'role': 'assistant'}, {'content': 'The expanded forms of the left-hand side (LHS) and the right-hand side (RHS) of the inequality are correct:\\n\\nLHS: $2x^2 + 16x + 30$\\n\\nRHS: $3x^2 + 33x + 72$\\n\\nNow, let\\'s proceed to Step 2, where we will calculate the expression resulting from subtracting the RHS from the LHS, and then move on to factoring that expression.\\n\\n```python\\nfrom sympy import simplify\\n\\n# Compute the difference between RHS and LHS\\ninequality_expression = simplify(lhs - rhs)\\n\\n# Print the simplified inequality expression\\nprint(\"Inequality expression (LHS - RHS):\", inequality_expression)\\n```\\n\\nRun this code to obtain the simplified inequality expression. Once we have it, we can determine the next steps.', 'role': 'user'}, {'content': 'Inequality expression (LHS - RHS): -x**2 - 17*x - 42', 'role': 'assistant'}, {'content': 'The simplified expression from subtracting the RHS from the LHS is correct:\\n\\nInequality Expression: $-x^2 - 17x - 42$\\n\\nNext, we will factor this expression to determine the critical points where the expression equals zero.\\n\\n```python\\nfrom sympy import factor\\n\\n# Factor the inequality expression\\nfactored_expression = factor(inequality_expression)\\n\\n# Print the factored inequality expression\\nprint(\"Factored Inequality Expression:\", factored_expression)\\n```\\n\\nPlease run this code to obtain the critical points, and we will continue from there.', 'role': 'user'}, {'content': 'Factored Inequality Expression: -(x + 3)*(x + 14)', 'role': 'assistant'}, {'content': 'The factored form of the inequality expression is correct:\\n\\nFactored Inequality Expression: $-(x + 3)(x + 14)$\\n\\nThe critical points are the values of $x$ for which this expression equals zero, which are $x = -3$ and $x = -14$.\\n\\nNow, we will determine the intervals formed by these critical points and analyze the sign of the inequality expression within each interval to find where the original inequality holds true.\\n\\n```python\\nfrom sympy import solveset, S\\n\\n# Determine in which intervals the inequality holds true\\nsolution_intervals = solveset(inequality_expression < 0, x, domain=S.Reals)\\n\\n# Print the solution in interval notation\\nprint(\"Solution Intervals:\", solution_intervals)\\n```\\n\\nRunning this code will provide us with the intervals where the inequality is satisfied, expressed in interval notation. Please execute it to get the final answer.', 'role': 'user'}, {'content': 'Solution Intervals: Union(Interval.open(-oo, -14), Interval.open(-3, oo))', 'role': 'assistant'}, {'content': 'The solution intervals where the original inequality $(2x+10)(x+3)<(3x+9)(x+8)$ holds true are:\\n\\nSolution Intervals: $(-\\\\infty, -14) \\\\cup (-3, \\\\infty)$\\n\\nSo the complete solution to the problem in interval notation is:\\n\\n$$\\\\boxed{(-\\\\infty, -14) \\\\cup (-3, \\\\infty)}$$\\n\\nThis indicates that the inequality is satisfied for all $x$ values less than $-14$ and for all $x$ values greater than $-3$.', 'role': 'user'}], summary='The solution intervals where the original inequality $(2x+10)(x+3)<(3x+9)(x+8)$ holds true are:\\n\\nSolution Intervals: $(-\\\\infty, -14) \\\\cup (-3, \\\\infty)$\\n\\nSo the complete solution to the problem in interval notation is:\\n\\n$$\\\\boxed{(-\\\\infty, -14) \\\\cup (-3, \\\\infty)}$$\\n\\nThis indicates that the inequality is satisfied for all $x$ values less than $-14$ and for all $x$ values greater than $-3$.', cost=({'total_cost': 0.26039999999999996, 'gpt-4': {'cost': 0.26039999999999996, 'prompt_tokens': 6476, 'completion_tokens': 1102, 'total_tokens': 7578}}, {'total_cost': 0}), human_input=[])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# given a math problem, we use the mathproxyagent to generate a prompt to be sent to the assistant as the initial message.\n", "# the assistant receives the message and generates a response. The response will be sent back to the mathproxyagent for processing.\n", @@ -180,7 +415,7 @@ "math_problem = (\n", " \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\"\n", ")\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem)" + "mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem)" ] }, { @@ -199,12 +434,130 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Let's use Python to solve a math problem.\n", + "\n", + "Query requirements:\n", + "You should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\n", + "You can use packages like sympy to help you.\n", + "You must follow the formats below to write your code:\n", + "```python\n", + "# your code\n", + "```\n", + "\n", + "First state the key idea to solve the problem. You may choose from three ways to solve the problem:\n", + "Case 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\n", + "Case 2: If the problem is mostly reasoning, you can solve it by yourself directly.\n", + "Case 3: If the problem cannot be handled in the above two ways, please follow this process:\n", + "1. Solve the problem step by step (do not over-divide the steps).\n", + "2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\n", + "3. Wait for me to give the results.\n", + "4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\n", + "\n", + "After all the queries are run and you get the answer, put the answer in \\boxed{}.\n", + "\n", + "Problem:\n", + "For what negative value of $k$ is there exactly one solution to the system of equations \\begin{align*}\n", + "y &= 2x^2 + kx + 6 \\\\\n", + "y &= -x + 4?\n", + "\\end{align*}\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The key idea to solve this problem is to set the two equations equal to each other and find the value of $k$ such that the resulting quadratic equation has exactly one solution. A quadratic equation has exactly one solution if and only if its discriminant is zero.\n", + "\n", + "The system of equations is:\n", + "\\begin{align*}\n", + "y &= 2x^2 + kx + 6 \\quad (1) \\\\\n", + "y &= -x + 4 \\quad (2)\n", + "\\end{align*}\n", + "\n", + "Setting these two equations equal to each other gives us:\n", + "$$ 2x^2 + kx + 6 = -x + 4. $$\n", + "\n", + "Now, we simplify the equation and equate it to zero to form a quadratic equation in the standard form $ax^2 + bx + c = 0$:\n", + "$$ 2x^2 + (k + 1)x + (6 - 4) = 0. $$\n", + "\n", + "The discriminant of a quadratic equation $ax^2 + bx + c = 0$ is given by $\\Delta = b^2 - 4ac$. For there to be exactly one solution, we need $\\Delta = 0$. We will determine the value of $k$ that makes the discriminant zero.\n", + "\n", + "Let's calculate the discriminant with the values we have:\n", + "\\begin{align*}\n", + "a &= 2, \\\\\n", + "b &= k + 1, \\\\\n", + "c &= 2.\n", + "\\end{align*}\n", + "\n", + "The discriminant is then:\n", + "$$ \\Delta = (k + 1)^2 - 4(2)(2). $$\n", + "\n", + "We set the discriminant equal to zero and solve for $k$:\n", + "$$ (k + 1)^2 - 16 = 0. $$\n", + "\n", + "I will now write a Python code using sympy to solve for $k$:\n", + "\n", + "```python\n", + "from sympy import symbols, Eq, solve\n", + "\n", + "# Define the symbol k\n", + "k = symbols('k')\n", + "\n", + "# Define the discriminant\n", + "discriminant = (k + 1)**2 - 4*2*2\n", + "\n", + "# Equation for the discriminant to be zero\n", + "eq = Eq(discriminant, 0)\n", + "\n", + "# Solve for k\n", + "k_value = solve(eq, k)\n", + "print(\"The value of k for which there is exactly one solution to the system of equations is:\", k_value)\n", + "```\n", + "\n", + "Running this code will give us the negative value of $k$ for which there is exactly one solution to the system of equations.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "The value of k for which there is exactly one solution to the system of equations is: [-5, 3]\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "Given that we are interested in the negative value of \\(k\\) which results in exactly one solution to the system of equations, from the two values you've reported \\([-5, 3]\\), the negative value is \\(-5\\).\n", + "\n", + "Thus, the correct value of \\(k\\) is \\(-5\\).\n", + "\n", + "We can insert this result into a boxed format:\n", + "\n", + "\\[\n", + "\\boxed{k = -5}\n", + "\\]\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Let's use Python to solve a math problem.\\n\\nQuery requirements:\\nYou should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\\nYou can use packages like sympy to help you.\\nYou must follow the formats below to write your code:\\n```python\\n# your code\\n```\\n\\nFirst state the key idea to solve the problem. You may choose from three ways to solve the problem:\\nCase 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\\nCase 2: If the problem is mostly reasoning, you can solve it by yourself directly.\\nCase 3: If the problem cannot be handled in the above two ways, please follow this process:\\n1. Solve the problem step by step (do not over-divide the steps).\\n2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\\n3. Wait for me to give the results.\\n4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\\n\\nAfter all the queries are run and you get the answer, put the answer in \\\\boxed{}.\\n\\nProblem:\\nFor what negative value of $k$ is there exactly one solution to the system of equations \\\\begin{align*}\\ny &= 2x^2 + kx + 6 \\\\\\\\\\ny &= -x + 4?\\n\\\\end{align*}\", 'role': 'assistant'}, {'content': 'The key idea to solve this problem is to set the two equations equal to each other and find the value of $k$ such that the resulting quadratic equation has exactly one solution. A quadratic equation has exactly one solution if and only if its discriminant is zero.\\n\\nThe system of equations is:\\n\\\\begin{align*}\\ny &= 2x^2 + kx + 6 \\\\quad (1) \\\\\\\\\\ny &= -x + 4 \\\\quad (2)\\n\\\\end{align*}\\n\\nSetting these two equations equal to each other gives us:\\n$$ 2x^2 + kx + 6 = -x + 4. $$\\n\\nNow, we simplify the equation and equate it to zero to form a quadratic equation in the standard form $ax^2 + bx + c = 0$:\\n$$ 2x^2 + (k + 1)x + (6 - 4) = 0. $$\\n\\nThe discriminant of a quadratic equation $ax^2 + bx + c = 0$ is given by $\\\\Delta = b^2 - 4ac$. For there to be exactly one solution, we need $\\\\Delta = 0$. We will determine the value of $k$ that makes the discriminant zero.\\n\\nLet\\'s calculate the discriminant with the values we have:\\n\\\\begin{align*}\\na &= 2, \\\\\\\\\\nb &= k + 1, \\\\\\\\\\nc &= 2.\\n\\\\end{align*}\\n\\nThe discriminant is then:\\n$$ \\\\Delta = (k + 1)^2 - 4(2)(2). $$\\n\\nWe set the discriminant equal to zero and solve for $k$:\\n$$ (k + 1)^2 - 16 = 0. $$\\n\\nI will now write a Python code using sympy to solve for $k$:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\n# Define the symbol k\\nk = symbols(\\'k\\')\\n\\n# Define the discriminant\\ndiscriminant = (k + 1)**2 - 4*2*2\\n\\n# Equation for the discriminant to be zero\\neq = Eq(discriminant, 0)\\n\\n# Solve for k\\nk_value = solve(eq, k)\\nprint(\"The value of k for which there is exactly one solution to the system of equations is:\", k_value)\\n```\\n\\nRunning this code will give us the negative value of $k$ for which there is exactly one solution to the system of equations.', 'role': 'user'}, {'content': 'The value of k for which there is exactly one solution to the system of equations is: [-5, 3]', 'role': 'assistant'}, {'content': \"Given that we are interested in the negative value of \\\\(k\\\\) which results in exactly one solution to the system of equations, from the two values you've reported \\\\([-5, 3]\\\\), the negative value is \\\\(-5\\\\).\\n\\nThus, the correct value of \\\\(k\\\\) is \\\\(-5\\\\).\\n\\nWe can insert this result into a boxed format:\\n\\n\\\\[\\n\\\\boxed{k = -5}\\n\\\\]\", 'role': 'user'}], summary=\"Given that we are interested in the negative value of \\\\(k\\\\) which results in exactly one solution to the system of equations, from the two values you've reported \\\\([-5, 3]\\\\), the negative value is \\\\(-5\\\\).\\n\\nThus, the correct value of \\\\(k\\\\) is \\\\(-5\\\\).\\n\\nWe can insert this result into a boxed format:\\n\\n\\\\[\\n\\\\boxed{k = -5}\\n\\\\]\", cost=({'total_cost': 0.33227999999999996, 'gpt-4': {'cost': 0.33227999999999996, 'prompt_tokens': 7678, 'completion_tokens': 1699, 'total_tokens': 9377}}, {'total_cost': 0.33227999999999996, 'gpt-4': {'cost': 0.33227999999999996, 'prompt_tokens': 7678, 'completion_tokens': 1699, 'total_tokens': 9377}}), human_input=[])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "math_problem = \"For what negative value of $k$ is there exactly one solution to the system of equations \\\\begin{align*}\\ny &= 2x^2 + kx + 6 \\\\\\\\\\ny &= -x + 4?\\n\\\\end{align*}\"\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem)" + "mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem)" ] }, { @@ -220,12 +573,154 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Let's use Python to solve a math problem.\n", + "\n", + "Query requirements:\n", + "You should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\n", + "You can use packages like sympy to help you.\n", + "You must follow the formats below to write your code:\n", + "```python\n", + "# your code\n", + "```\n", + "\n", + "First state the key idea to solve the problem. You may choose from three ways to solve the problem:\n", + "Case 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\n", + "Case 2: If the problem is mostly reasoning, you can solve it by yourself directly.\n", + "Case 3: If the problem cannot be handled in the above two ways, please follow this process:\n", + "1. Solve the problem step by step (do not over-divide the steps).\n", + "2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\n", + "3. Wait for me to give the results.\n", + "4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\n", + "\n", + "After all the queries are run and you get the answer, put the answer in \\boxed{}.\n", + "\n", + "Problem:\n", + "Find all positive integer values of $c$ such that the equation $x^2-7x+c=0$ only has roots that are real and rational. Express them in decreasing order, separated by commas.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "To solve this problem, we want to find all positive integer values of $c$ for which the equation $x^2-7x+c=0$ has real and rational roots. For this, we need to use the discriminant of a quadratic equation, which is given by the formula $\\Delta = b^2 - 4ac$. For the equation to have real and rational roots, the discriminant must be a perfect square.\n", + "\n", + "In the given equation, $a=1, b=-7$, and $c$ is the constant we're looking for. \n", + "\n", + "The steps to solve this problem are as follows:\n", + "\n", + "1. Write the discriminant condition for the equation to have real and rational roots.\n", + "2. Find the range of possible $c$ values by considering the constraints.\n", + "3. Enumerate through possible $c$ values to find the ones where the discriminant is a perfect square.\n", + "4. Return the values of $c$ in decreasing order, separated by commas.\n", + "\n", + "Let's start with step one and use Python to find all $c$ values that satisfy the condition for $\\Delta$ being a perfect square:\n", + "\n", + "```python\n", + "from sympy import symbols, solve\n", + "\n", + "# Define the variable\n", + "c = symbols('c', integer=True)\n", + "\n", + "# The discriminant of the quadratic equation\n", + "discriminant = (-7)**2 - 4*1*c\n", + "\n", + "# List to hold all possible c values with rational roots\n", + "c_values = []\n", + "\n", + "# Finding the range for c:\n", + "# Since c is positive and we want the discriminant to be non-negative (to have real roots),\n", + "# the smallest possible square for the discriminant is 0, which gives us the upper bound for c.\n", + "upper_bound = solve(discriminant, c)[0]\n", + "\n", + "# Check all integer values up to the upper bound\n", + "for i in range(1, upper_bound + 1):\n", + " # If the discriminant is a perfect square, then the roots will be rational.\n", + " if ((-7)**2 - 4*1*i).is_square:\n", + " c_values.append(i)\n", + "\n", + "# Output the result in decreasing order\n", + "c_values.sort(reverse=True)\n", + "print(', '.join(map(str, c_values)))\n", + "```\n", + "\n", + "This code will output all positive integer values of $c$ for which the roots of the equation are real and rational, sorted in decreasing order.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Error: Traceback (most recent call last):\n", + " File \"\", line 1103, in \n", + " upper_bound = solve(discriminant, c)[0]\n", + "IndexError: list index out of range\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "I apologize for the confusion in the earlier Python code. Let me correct the code by directly computing the upper bound for $c$. Since we are looking for positive integer values of $c$, we can infer that for $c > (\\frac{7}{2})^2$, the discriminant will start to become negative, thus no longer yielding real roots. Therefore, the upper bound for $c$ will be $\\left\\lfloor(\\frac{7}{2})^2\\right\\rfloor = 12$. Let's modify the code to reflect this new approach.\n", + "\n", + "Firstly, it's important to note that the discriminant, $\\Delta = b^2 - 4ac$, must be a non-negative perfect square for the equation $ax^2 + bx + c = 0$ to have real and rational roots. In our case, $a = 1$ and $b = -7$, so we have $\\Delta = (-7)^2 - 4c = 49 - 4c$.\n", + "\n", + "We also know that a perfect square is always non-negative, so $49 - 4c \\geq 0$. From this inequality, we can find the upper bound for the value of $c$. Now, let's implement this as Python code:\n", + "\n", + "```python\n", + "# Define the variable and the list to store valid c values\n", + "upper_bound = (7**2) // 4 # This is the floor division of 49 by 4, which yields 12\n", + "c_values = []\n", + "\n", + "# Iterate over the possible c values and check if the discriminant is a perfect square\n", + "for i in range(1, upper_bound + 1):\n", + " discriminant = 49 - 4 * i\n", + " # Check if the discriminant is a non-negative perfect square\n", + " if discriminant >= 0 and (int(discriminant**0.5))**2 == discriminant:\n", + " c_values.append(i)\n", + "\n", + "# Output the result in decreasing order\n", + "c_values.sort(reverse=True)\n", + "print(', '.join(map(str, c_values)))\n", + "```\n", + "\n", + "This corrected code will enumerate through all possible $c$ values starting from 1 up to the calculated upper bound, checking whether the discriminant is a perfect square for each value of $c$. It will output the valid $c$ values in decreasing order, as the problem requests.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "12, 10, 6\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "The correct values for $c$ in decreasing order are indeed $12, 10, 6$. This is because these values of $c$ result in discriminants of $49 - 4c$ that are perfect squares, specifically $1$, $9$, and $25$ respectively, which correspond to the real, rational roots of the quadratic equation given by $x^2 - 7x + c = 0$.\n", + "\n", + "So, we can express the final answer as:\n", + "\n", + "\\[\n", + "\\boxed{12, 10, 6}\n", + "\\]\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Let's use Python to solve a math problem.\\n\\nQuery requirements:\\nYou should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\\nYou can use packages like sympy to help you.\\nYou must follow the formats below to write your code:\\n```python\\n# your code\\n```\\n\\nFirst state the key idea to solve the problem. You may choose from three ways to solve the problem:\\nCase 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\\nCase 2: If the problem is mostly reasoning, you can solve it by yourself directly.\\nCase 3: If the problem cannot be handled in the above two ways, please follow this process:\\n1. Solve the problem step by step (do not over-divide the steps).\\n2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\\n3. Wait for me to give the results.\\n4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\\n\\nAfter all the queries are run and you get the answer, put the answer in \\\\boxed{}.\\n\\nProblem:\\nFind all positive integer values of $c$ such that the equation $x^2-7x+c=0$ only has roots that are real and rational. Express them in decreasing order, separated by commas.\", 'role': 'assistant'}, {'content': \"To solve this problem, we want to find all positive integer values of $c$ for which the equation $x^2-7x+c=0$ has real and rational roots. For this, we need to use the discriminant of a quadratic equation, which is given by the formula $\\\\Delta = b^2 - 4ac$. For the equation to have real and rational roots, the discriminant must be a perfect square.\\n\\nIn the given equation, $a=1, b=-7$, and $c$ is the constant we're looking for. \\n\\nThe steps to solve this problem are as follows:\\n\\n1. Write the discriminant condition for the equation to have real and rational roots.\\n2. Find the range of possible $c$ values by considering the constraints.\\n3. Enumerate through possible $c$ values to find the ones where the discriminant is a perfect square.\\n4. Return the values of $c$ in decreasing order, separated by commas.\\n\\nLet's start with step one and use Python to find all $c$ values that satisfy the condition for $\\\\Delta$ being a perfect square:\\n\\n```python\\nfrom sympy import symbols, solve\\n\\n# Define the variable\\nc = symbols('c', integer=True)\\n\\n# The discriminant of the quadratic equation\\ndiscriminant = (-7)**2 - 4*1*c\\n\\n# List to hold all possible c values with rational roots\\nc_values = []\\n\\n# Finding the range for c:\\n# Since c is positive and we want the discriminant to be non-negative (to have real roots),\\n# the smallest possible square for the discriminant is 0, which gives us the upper bound for c.\\nupper_bound = solve(discriminant, c)[0]\\n\\n# Check all integer values up to the upper bound\\nfor i in range(1, upper_bound + 1):\\n # If the discriminant is a perfect square, then the roots will be rational.\\n if ((-7)**2 - 4*1*i).is_square:\\n c_values.append(i)\\n\\n# Output the result in decreasing order\\nc_values.sort(reverse=True)\\nprint(', '.join(map(str, c_values)))\\n```\\n\\nThis code will output all positive integer values of $c$ for which the roots of the equation are real and rational, sorted in decreasing order.\", 'role': 'user'}, {'content': 'Error: Traceback (most recent call last):\\n File \"\", line 1103, in \\n upper_bound = solve(discriminant, c)[0]\\nIndexError: list index out of range', 'role': 'assistant'}, {'content': \"I apologize for the confusion in the earlier Python code. Let me correct the code by directly computing the upper bound for $c$. Since we are looking for positive integer values of $c$, we can infer that for $c > (\\\\frac{7}{2})^2$, the discriminant will start to become negative, thus no longer yielding real roots. Therefore, the upper bound for $c$ will be $\\\\left\\\\lfloor(\\\\frac{7}{2})^2\\\\right\\\\rfloor = 12$. Let's modify the code to reflect this new approach.\\n\\nFirstly, it's important to note that the discriminant, $\\\\Delta = b^2 - 4ac$, must be a non-negative perfect square for the equation $ax^2 + bx + c = 0$ to have real and rational roots. In our case, $a = 1$ and $b = -7$, so we have $\\\\Delta = (-7)^2 - 4c = 49 - 4c$.\\n\\nWe also know that a perfect square is always non-negative, so $49 - 4c \\\\geq 0$. From this inequality, we can find the upper bound for the value of $c$. Now, let's implement this as Python code:\\n\\n```python\\n# Define the variable and the list to store valid c values\\nupper_bound = (7**2) // 4 # This is the floor division of 49 by 4, which yields 12\\nc_values = []\\n\\n# Iterate over the possible c values and check if the discriminant is a perfect square\\nfor i in range(1, upper_bound + 1):\\n discriminant = 49 - 4 * i\\n # Check if the discriminant is a non-negative perfect square\\n if discriminant >= 0 and (int(discriminant**0.5))**2 == discriminant:\\n c_values.append(i)\\n\\n# Output the result in decreasing order\\nc_values.sort(reverse=True)\\nprint(', '.join(map(str, c_values)))\\n```\\n\\nThis corrected code will enumerate through all possible $c$ values starting from 1 up to the calculated upper bound, checking whether the discriminant is a perfect square for each value of $c$. It will output the valid $c$ values in decreasing order, as the problem requests.\", 'role': 'user'}, {'content': '12, 10, 6', 'role': 'assistant'}, {'content': 'The correct values for $c$ in decreasing order are indeed $12, 10, 6$. This is because these values of $c$ result in discriminants of $49 - 4c$ that are perfect squares, specifically $1$, $9$, and $25$ respectively, which correspond to the real, rational roots of the quadratic equation given by $x^2 - 7x + c = 0$.\\n\\nSo, we can express the final answer as:\\n\\n\\\\[\\n\\\\boxed{12, 10, 6}\\n\\\\]', 'role': 'user'}], summary='The correct values for $c$ in decreasing order are indeed $12, 10, 6$. This is because these values of $c$ result in discriminants of $49 - 4c$ that are perfect squares, specifically $1$, $9$, and $25$ respectively, which correspond to the real, rational roots of the quadratic equation given by $x^2 - 7x + c = 0$.\\n\\nSo, we can express the final answer as:\\n\\n\\\\[\\n\\\\boxed{12, 10, 6}\\n\\\\]', cost=({'total_cost': 0.47181, 'gpt-4': {'cost': 0.47181, 'prompt_tokens': 10187, 'completion_tokens': 2770, 'total_tokens': 12957}}, {'total_cost': 0.47181, 'gpt-4': {'cost': 0.47181, 'prompt_tokens': 10187, 'completion_tokens': 2770, 'total_tokens': 12957}}), human_input=[])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "math_problem = \"Find all positive integer values of $c$ such that the equation $x^2-7x+c=0$ only has roots that are real and rational. Express them in decreasing order, separated by commas.\"\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem)" + "mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem)" ] }, { @@ -238,12 +733,14 @@ "\n", "MathChat allows different prompts that instruct the assistant to solve the problem.\n", "\n", - "Check out `MathUserProxyAgent.generate_init_message(problem, prompt_type='default', customized_prompt=None)`:\n", + "Check out `MathUserProxyAgent.message_generator`:\n", + "\n", "- You may choose from `['default', 'python', 'two_tools']` for parameter `prompt_type`. We include two more prompts in the paper: \n", " 1. `'python'` is a simplified prompt from the default prompt that uses Python only. \n", " 2. `'two_tools'` further allows the selection of Python or Wolfram Alpha based on this simplified `python` prompt. Note that this option requires a Wolfram Alpha API key and put it in `wolfram.txt`.\n", "\n", - "- You can also input your customized prompt if needed: `mathproxyagent.generate_init_message(problem, customized_prompt=\"Your customized prompt\")`. Since this mathproxyagent detects '\\boxed{}' as termination, you need to have a similar termination sentence in the prompt: \"If you get the answer, put the answer in \\\\boxed{}.\". If the customized is provided, the `prompt_type` will be ignored.\n", + "- You can also input your customized prompt if needed.\n", + "Since this mathproxyagent detects '\\boxed{}' as termination, you need to have a similar termination sentence in the prompt: \"If you get the answer, put the answer in \\\\boxed{}.\". If the customized is provided, the `prompt_type` will be ignored.\n", "\n", "\n", "### Example 4 (Use the \"python\" prompt):\n", @@ -265,13 +762,220 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Let's use Python to solve a math problem.\n", + "\n", + "Query requirements:\n", + "You should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\n", + "You can use packages like sympy to help you.\n", + "You must follow the formats below to write your code:\n", + "```python\n", + "# your code\n", + "```\n", + "\n", + "Please follow this process:\n", + "1. Solve the problem step by step (do not over-divide the steps).\n", + "2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\n", + "3. Wait for me to give the results.\n", + "4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\n", + "\n", + "After all the queries are run and you get the answer, put the answer in \\boxed{}.\n", + "\n", + "Problem: Problem: If $725x + 727y = 1500$ and $729x+ 731y = 1508$, what is the value of $x - y$ ?\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "To start solving the system of linear equations, we'll begin by eliminating one of the variables. We can do this by multiplying both equations by certain coefficients so that the coefficients of one of the variables are the same in both equations. Then, we can subtract the equations to eliminate that variable.\n", + "\n", + "First, let's multiply the first equation by $731$ and the second equation by $727$, so that the $y$ terms will have the same coefficient and can be subtracted out.\n", + "\n", + "Here are the modified equations:\n", + "\n", + "1) $725x \\cdot 731 + 727y \\cdot 731 = 1500 \\cdot 731$\n", + "\n", + "2) $729x \\cdot 727 + 731y \\cdot 727 = 1508 \\cdot 727$\n", + "\n", + "We'll subtract equation 2 from equation 1 after this modification.\n", + "\n", + "Let's compute the new coefficients and constants:\n", + "\n", + "For the first equation:\n", + "- New coefficient of $x$: $725 \\cdot 731$\n", + "- New coefficient of $y$: $727 \\cdot 731$\n", + "- New constant term: $1500 \\cdot 731$\n", + "\n", + "For the second equation:\n", + "- New coefficient of $x$: $729 \\cdot 727$\n", + "- New coefficient of $y$: $731 \\cdot 727$\n", + "- New constant term: $1508 \\cdot 727$\n", + "\n", + "Now, we can subtract the second equation from the first one.\n", + "\n", + "The Python code will be:\n", + "\n", + "```python\n", + "from sympy import symbols, Eq, solve\n", + "\n", + "# Define the symbolic variables\n", + "x, y = symbols('x y')\n", + "\n", + "# Calculate the new coefficients and constant terms\n", + "coeff_x1 = 725 * 731\n", + "coeff_y1 = 727 * 731\n", + "const1 = 1500 * 731\n", + "\n", + "coeff_x2 = 729 * 727\n", + "coeff_y2 = 731 * 727\n", + "const2 = 1508 * 727\n", + "\n", + "# Create the modified equations\n", + "eq1_mod = Eq(coeff_x1*x + coeff_y1*y, const1)\n", + "eq2_mod = Eq(coeff_x2*x + coeff_y2*y, const2)\n", + "\n", + "# Subtract the second equation from the first\n", + "eq_subtraction = Eq(eq1_mod.lhs - eq2_mod.lhs, eq1_mod.rhs - eq2_mod.rhs)\n", + "\n", + "print(eq_subtraction)\n", + "```\n", + "\n", + "Please run this code to get the modified equation after subtraction.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "Eq(-8*x, 184)\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "Great, now we have the simplified equation:\n", + "\n", + "$$-8x = 184$$\n", + "\n", + "To solve for $x$, we will divide both sides of the equation by $-8$. Let’s do that calculation now.\n", + "\n", + "$$ x = \\frac{184}{-8} $$\n", + "\n", + "Let's go ahead and perform this division.\n", + "\n", + "```python\n", + "# Calculate the value of x\n", + "x_value = 184 / -8\n", + "\n", + "print(x_value)\n", + "```\n", + "\n", + "Please run this code to find the value of $x$.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "-23.0\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "We've established that $x = -23$.\n", + "\n", + "Next, we can substitute the value of $x$ into either of the original equations to solve for $y$. Let's substitute $x$ into the first equation:\n", + "\n", + "$$\n", + "725x + 727y = 1500\n", + "$$\n", + "\n", + "Substituting $x = -23$ gives us:\n", + "\n", + "$$\n", + "725(-23) + 727y = 1500\n", + "$$\n", + "\n", + "Now we'll solve for $y$ using this equation. The Python code for this step is:\n", + "\n", + "```python\n", + "# Substitute x into the first original equation to solve for y\n", + "x_value = -23\n", + "\n", + "# Define the equation\n", + "eq_for_y = Eq(725 * x_value + 727 * y, 1500)\n", + "\n", + "# Solve for y\n", + "y_value = solve(eq_for_y, y)\n", + "\n", + "print(y_value)\n", + "```\n", + "\n", + "Please execute this code to calculate the value of $y$.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "[25]\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "We've found that $y = 25$. Now that we have both $x = -23$ and $y = 25$, we can compute $x - y$:\n", + "\n", + "$$ x - y = -23 - 25 $$\n", + "\n", + "Let's perform this calculation:\n", + "\n", + "```python\n", + "# Calculate x - y\n", + "result = x_value - 25\n", + "\n", + "print(result)\n", + "```\n", + "\n", + "Please run the above code to find the value of $x - y$.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", + "\n", + "-48\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", + "\n", + "With the results obtained, we have:\n", + "\n", + "$$ x - y = -48 $$\n", + "\n", + "Therefore, the value of $x - y$ is $-48$.\n", + "\n", + "We can put this answer inside a box using LaTeX notation:\n", + "\n", + "$$ \\boxed{x - y = -48} $$\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Let's use Python to solve a math problem.\\n\\nQuery requirements:\\nYou should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\\nYou can use packages like sympy to help you.\\nYou must follow the formats below to write your code:\\n```python\\n# your code\\n```\\n\\nPlease follow this process:\\n1. Solve the problem step by step (do not over-divide the steps).\\n2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\\n3. Wait for me to give the results.\\n4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\\n\\nAfter all the queries are run and you get the answer, put the answer in \\\\boxed{}.\\n\\nProblem: Problem: If $725x + 727y = 1500$ and $729x+ 731y = 1508$, what is the value of $x - y$ ?\", 'role': 'assistant'}, {'content': \"To start solving the system of linear equations, we'll begin by eliminating one of the variables. We can do this by multiplying both equations by certain coefficients so that the coefficients of one of the variables are the same in both equations. Then, we can subtract the equations to eliminate that variable.\\n\\nFirst, let's multiply the first equation by $731$ and the second equation by $727$, so that the $y$ terms will have the same coefficient and can be subtracted out.\\n\\nHere are the modified equations:\\n\\n1) $725x \\\\cdot 731 + 727y \\\\cdot 731 = 1500 \\\\cdot 731$\\n\\n2) $729x \\\\cdot 727 + 731y \\\\cdot 727 = 1508 \\\\cdot 727$\\n\\nWe'll subtract equation 2 from equation 1 after this modification.\\n\\nLet's compute the new coefficients and constants:\\n\\nFor the first equation:\\n- New coefficient of $x$: $725 \\\\cdot 731$\\n- New coefficient of $y$: $727 \\\\cdot 731$\\n- New constant term: $1500 \\\\cdot 731$\\n\\nFor the second equation:\\n- New coefficient of $x$: $729 \\\\cdot 727$\\n- New coefficient of $y$: $731 \\\\cdot 727$\\n- New constant term: $1508 \\\\cdot 727$\\n\\nNow, we can subtract the second equation from the first one.\\n\\nThe Python code will be:\\n\\n```python\\nfrom sympy import symbols, Eq, solve\\n\\n# Define the symbolic variables\\nx, y = symbols('x y')\\n\\n# Calculate the new coefficients and constant terms\\ncoeff_x1 = 725 * 731\\ncoeff_y1 = 727 * 731\\nconst1 = 1500 * 731\\n\\ncoeff_x2 = 729 * 727\\ncoeff_y2 = 731 * 727\\nconst2 = 1508 * 727\\n\\n# Create the modified equations\\neq1_mod = Eq(coeff_x1*x + coeff_y1*y, const1)\\neq2_mod = Eq(coeff_x2*x + coeff_y2*y, const2)\\n\\n# Subtract the second equation from the first\\neq_subtraction = Eq(eq1_mod.lhs - eq2_mod.lhs, eq1_mod.rhs - eq2_mod.rhs)\\n\\nprint(eq_subtraction)\\n```\\n\\nPlease run this code to get the modified equation after subtraction.\", 'role': 'user'}, {'content': 'Eq(-8*x, 184)', 'role': 'assistant'}, {'content': \"Great, now we have the simplified equation:\\n\\n$$-8x = 184$$\\n\\nTo solve for $x$, we will divide both sides of the equation by $-8$. Let’s do that calculation now.\\n\\n$$ x = \\\\frac{184}{-8} $$\\n\\nLet's go ahead and perform this division.\\n\\n```python\\n# Calculate the value of x\\nx_value = 184 / -8\\n\\nprint(x_value)\\n```\\n\\nPlease run this code to find the value of $x$.\", 'role': 'user'}, {'content': '-23.0', 'role': 'assistant'}, {'content': \"We've established that $x = -23$.\\n\\nNext, we can substitute the value of $x$ into either of the original equations to solve for $y$. Let's substitute $x$ into the first equation:\\n\\n$$\\n725x + 727y = 1500\\n$$\\n\\nSubstituting $x = -23$ gives us:\\n\\n$$\\n725(-23) + 727y = 1500\\n$$\\n\\nNow we'll solve for $y$ using this equation. The Python code for this step is:\\n\\n```python\\n# Substitute x into the first original equation to solve for y\\nx_value = -23\\n\\n# Define the equation\\neq_for_y = Eq(725 * x_value + 727 * y, 1500)\\n\\n# Solve for y\\ny_value = solve(eq_for_y, y)\\n\\nprint(y_value)\\n```\\n\\nPlease execute this code to calculate the value of $y$.\", 'role': 'user'}, {'content': '[25]', 'role': 'assistant'}, {'content': \"We've found that $y = 25$. Now that we have both $x = -23$ and $y = 25$, we can compute $x - y$:\\n\\n$$ x - y = -23 - 25 $$\\n\\nLet's perform this calculation:\\n\\n```python\\n# Calculate x - y\\nresult = x_value - 25\\n\\nprint(result)\\n```\\n\\nPlease run the above code to find the value of $x - y$.\", 'role': 'user'}, {'content': '-48', 'role': 'assistant'}, {'content': 'With the results obtained, we have:\\n\\n$$ x - y = -48 $$\\n\\nTherefore, the value of $x - y$ is $-48$.\\n\\nWe can put this answer inside a box using LaTeX notation:\\n\\n$$ \\\\boxed{x - y = -48} $$', 'role': 'user'}], summary='With the results obtained, we have:\\n\\n$$ x - y = -48 $$\\n\\nTherefore, the value of $x - y$ is $-48$.\\n\\nWe can put this answer inside a box using LaTeX notation:\\n\\n$$ \\\\boxed{x - y = -48} $$', cost=({'total_cost': 0.6470400000000001, 'gpt-4': {'cost': 0.6470400000000001, 'prompt_tokens': 14178, 'completion_tokens': 3695, 'total_tokens': 17873}}, {'total_cost': 0.6470400000000001, 'gpt-4': {'cost': 0.6470400000000001, 'prompt_tokens': 14178, 'completion_tokens': 3695, 'total_tokens': 17873}}), human_input=[])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# we set the prompt_type to \"python\", which is a simplified version of the default prompt.\n", "math_problem = \"Problem: If $725x + 727y = 1500$ and $729x+ 731y = 1508$, what is the value of $x - y$ ?\"\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem, prompt_type=\"python\")" + "mathproxyagent.initiate_chat(\n", + " assistant, message=mathproxyagent.message_generator, problem=math_problem, prompt_type=\"python\"\n", + ")" ] }, { @@ -299,7 +1003,9 @@ "\n", "# we set the prompt_type to \"two_tools\", which allows the assistant to select wolfram alpha when necessary.\n", "math_problem = \"Find all numbers $a$ for which the graph of $y=x^2+a$ and the graph of $y=ax$ intersect. Express your answer in interval notation.\"\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem, prompt_type=\"two_tools\")" + "mathproxyagent.initiate_chat(\n", + " assistant, message=mathproxyagent.message_generator, problem=math_problem, prompt_type=\"two_tools\"\n", + ")" ] } ], @@ -319,7 +1025,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.10.13" }, "orig_nbformat": 4 }, diff --git a/notebook/agentchat_RetrieveChat.ipynb b/notebook/agentchat_RetrieveChat.ipynb index 92620a55b331..52c469dedf1e 100644 --- a/notebook/agentchat_RetrieveChat.ipynb +++ b/notebook/agentchat_RetrieveChat.ipynb @@ -465,7 +465,7 @@ "# With human-in-loop, the conversation will continue until the user says \"exit\".\n", "code_problem = \"How can I use FLAML to perform a classification task and use spark to do parallel training. Train 30 seconds and force cancel jobs if time limit is reached.\"\n", "ragproxyagent.initiate_chat(\n", - " assistant, problem=code_problem, search_string=\"spark\"\n", + " assistant, message=ragproxyagent.message_generator, problem=code_problem, search_string=\"spark\"\n", ") # search_string is used as an extra filter for the embeddings search, in this case, we only want to search documents that contain \"spark\"." ] }, @@ -1020,7 +1020,7 @@ "assistant.reset()\n", "\n", "qa_problem = \"Who is the author of FLAML?\"\n", - "ragproxyagent.initiate_chat(assistant, problem=qa_problem)" + "ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=qa_problem)" ] }, { @@ -1433,7 +1433,7 @@ "# set `human_input_mode` to be `ALWAYS`, so the agent will ask for human input at every step.\n", "ragproxyagent.human_input_mode = \"ALWAYS\"\n", "code_problem = \"how to build a time series forecasting model for stock price using FLAML?\"\n", - "ragproxyagent.initiate_chat(assistant, problem=code_problem)" + "ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=code_problem)" ] }, { @@ -1991,7 +1991,9 @@ "# set `human_input_mode` to be `ALWAYS`, so the agent will ask for human input at every step.\n", "ragproxyagent.human_input_mode = \"ALWAYS\"\n", "qa_problem = \"Is there a function named `tune_automl` in FLAML?\"\n", - "ragproxyagent.initiate_chat(assistant, problem=qa_problem) # type \"exit\" to exit the conversation" + "ragproxyagent.initiate_chat(\n", + " assistant, message=ragproxyagent.message_generator, problem=qa_problem\n", + ") # type \"exit\" to exit the conversation" ] }, { @@ -2582,7 +2584,7 @@ " assistant.reset()\n", "\n", " qa_problem = questions[i]\n", - " ragproxyagent.initiate_chat(assistant, problem=qa_problem, n_results=30)" + " ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=qa_problem, n_results=30)" ] }, { @@ -3009,14 +3011,16 @@ " assistant.reset()\n", "\n", " qa_problem = questions[i]\n", - " ragproxyagent.initiate_chat(assistant, problem=qa_problem, n_results=10)" + " ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=qa_problem, n_results=10)" ] } ], "metadata": { "front_matter": { - "tags": ["RAG"], - "description": "Explore the use of AutoGen's RetrieveChat for tasks like code generation from docstrings, answering complex questions with human feedback, and exploiting features like Update Context, custom prompts, and few-shot learning." + "description": "Explore the use of AutoGen's RetrieveChat for tasks like code generation from docstrings, answering complex questions with human feedback, and exploiting features like Update Context, custom prompts, and few-shot learning.", + "tags": [ + "RAG" + ] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", diff --git a/notebook/agentchat_auto_feedback_from_code_execution.ipynb b/notebook/agentchat_auto_feedback_from_code_execution.ipynb index 1598c66f8f0d..276413d21f34 100644 --- a/notebook/agentchat_auto_feedback_from_code_execution.ipynb +++ b/notebook/agentchat_auto_feedback_from_code_execution.ipynb @@ -37,6 +37,7 @@ "\n", "from IPython import get_ipython\n", "from IPython.display import display, Image\n", + "import csv\n", "\n", "import autogen\n", "\n", @@ -84,8 +85,6 @@ "What date is today? Compare the year-to-date gain for META and TESLA.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant What date is today? Compare the year-to-date gain for META and TESLA. None\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", "To get the current date, we can use Python's `datetime` module. After that, we will need to retrieve the year-to-date (YTD) gain for both META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.). We can do this by fetching the stock prices from the beginning of the year and the current stock prices, then calculating the percentage change.\n", @@ -107,53 +106,32 @@ "Please save the above code in a file named `get_current_date.py` and execute it to get today's date. After that, we will proceed to the next step of fetching the stock data.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", - "recipitent sending user_proxy To get the current date, we can use Python's `datetime` module. After that, we will need to retrieve the year-to-date (YTD) gain for both META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.). We can do this by fetching the stock prices from the beginning of the year and the current stock prices, then calculating the percentage change.\n", - "\n", - "First, let's write a Python script to get the current date:\n", - "\n", - "```python\n", - "# filename: get_current_date.py\n", - "\n", - "from datetime import datetime\n", - "\n", - "# Get the current date\n", - "current_date = datetime.now()\n", - "\n", - "# Print the current date in YYYY-MM-DD format\n", - "print(current_date.strftime('%Y-%m-%d'))\n", - "```\n", - "\n", - "Please save the above code in a file named `get_current_date.py` and execute it to get today's date. After that, we will proceed to the next step of fetching the stock data. None\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", "exitcode: 0 (execution succeeded)\n", "Code output: \n", - "2024-02-16\n", + "2024-03-03\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant exitcode: 0 (execution succeeded)\n", - "Code output: \n", - "2024-02-16\n", - " None\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "Great, today's date is 2024-02-16.\n", + "Great, today's date is March 3, 2024. Now, let's proceed to the next step, which is to fetch the stock prices for META and TESLA.\n", "\n", - "Next, we will fetch the stock prices for META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.) from the beginning of the year until today. To do this, we can use the `yfinance` library in Python, which allows us to retrieve historical stock data.\n", + "We will use Python to retrieve the stock data. For this purpose, we can use the `yfinance` library, which allows us to fetch historical market data from Yahoo Finance. If `yfinance` is not installed on your system, you will need to install it using `pip install yfinance`.\n", "\n", - "I will provide you with a Python script that will:\n", - "1. Fetch the stock prices for META and TESLA from the beginning of the year (2024-01-01) to today (2024-02-16).\n", - "2. Calculate the YTD gain for each stock.\n", - "\n", - "Please execute the following Python script:\n", + "Here's the Python script to fetch the YTD gain for META and TESLA:\n", "\n", "```python\n", - "# filename: compare_ytd_gains.py\n", + "# filename: ytd_gain_comparison.py\n", "\n", "import yfinance as yf\n", "from datetime import datetime\n", @@ -161,167 +139,60 @@ "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", "tickers = [\"META\", \"TSLA\"]\n", "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", + "# Define the start of the year\n", + "start_of_year = datetime(datetime.now().year, 1, 1)\n", + "\n", + "# Get the current date\n", + "current_date = datetime.now()\n", "\n", "# Function to calculate YTD gain\n", "def calculate_ytd_gain(ticker):\n", " # Fetch historical data for the ticker\n", - " data = yf.download(ticker, start=start_date, end=end_date)\n", + " data = yf.download(ticker, start=start_of_year, end=current_date)\n", + " \n", + " # Get the first available closing price of the year (approximation of the price at the start of the year)\n", + " start_price = data['Close'].iloc[0]\n", " \n", - " # Get the first and last closing prices\n", - " initial_price = data['Close'].iloc[0]\n", - " final_price = data['Close'].iloc[-1]\n", + " # Get the most recent closing price\n", + " end_price = data['Close'].iloc[-1]\n", " \n", " # Calculate the YTD gain\n", - " ytd_gain = ((final_price - initial_price) / initial_price) * 100\n", + " ytd_gain = ((end_price - start_price) / start_price) * 100\n", + " \n", " return ytd_gain\n", "\n", - "# Calculate and print the YTD gains for both stocks\n", - "ytd_gains = {}\n", + "# Calculate and print the YTD gain for each ticker\n", "for ticker in tickers:\n", - " ytd_gains[ticker] = calculate_ytd_gain(ticker)\n", + " ytd_gain = calculate_ytd_gain(ticker)\n", + " print(f\"{ticker} YTD Gain: {ytd_gain:.2f}%\")\n", "\n", - "print(f\"YTD gain for META: {ytd_gains['META']:.2f}%\")\n", - "print(f\"YTD gain for TSLA: {ytd_gains['TSLA']:.2f}%\")\n", "```\n", "\n", - "Before running this script, ensure that you have the `yfinance` library installed. If it's not installed, you can install it using the following command:\n", - "\n", - "```sh\n", - "pip install yfinance\n", - "```\n", - "\n", - "After installing `yfinance`, save the script as `compare_ytd_gains.py` and execute it to compare the YTD gains for META and TESLA.\n", + "Please save the above code in a file named `ytd_gain_comparison.py` and execute it. The script will output the YTD gain for both META and TESLA. If `yfinance` is not installed, you will need to install it first by running `pip install yfinance`.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", "\u001b[31m\n", ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 1 (inferred language is sh)...\u001b[0m\n", - "recipitent sending user_proxy Great, today's date is 2024-02-16.\n", - "\n", - "Next, we will fetch the stock prices for META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.) from the beginning of the year until today. To do this, we can use the `yfinance` library in Python, which allows us to retrieve historical stock data.\n", - "\n", - "I will provide you with a Python script that will:\n", - "1. Fetch the stock prices for META and TESLA from the beginning of the year (2024-01-01) to today (2024-02-16).\n", - "2. Calculate the YTD gain for each stock.\n", - "\n", - "Please execute the following Python script:\n", - "\n", - "```python\n", - "# filename: compare_ytd_gains.py\n", - "\n", - "import yfinance as yf\n", - "from datetime import datetime\n", - "\n", - "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", - "tickers = [\"META\", \"TSLA\"]\n", - "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", - "\n", - "# Function to calculate YTD gain\n", - "def calculate_ytd_gain(ticker):\n", - " # Fetch historical data for the ticker\n", - " data = yf.download(ticker, start=start_date, end=end_date)\n", - " \n", - " # Get the first and last closing prices\n", - " initial_price = data['Close'].iloc[0]\n", - " final_price = data['Close'].iloc[-1]\n", - " \n", - " # Calculate the YTD gain\n", - " ytd_gain = ((final_price - initial_price) / initial_price) * 100\n", - " return ytd_gain\n", - "\n", - "# Calculate and print the YTD gains for both stocks\n", - "ytd_gains = {}\n", - "for ticker in tickers:\n", - " ytd_gains[ticker] = calculate_ytd_gain(ticker)\n", - "\n", - "print(f\"YTD gain for META: {ytd_gains['META']:.2f}%\")\n", - "print(f\"YTD gain for TSLA: {ytd_gains['TSLA']:.2f}%\")\n", - "```\n", - "\n", - "Before running this script, ensure that you have the `yfinance` library installed. If it's not installed, you can install it using the following command:\n", - "\n", - "```sh\n", - "pip install yfinance\n", - "```\n", - "\n", - "After installing `yfinance`, save the script as `compare_ytd_gains.py` and execute it to compare the YTD gains for META and TESLA. None\n", "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", "exitcode: 0 (execution succeeded)\n", "Code output: \n", - "YTD gain for META: 39.78%\n", - "YTD gain for TSLA: -19.31%\n", - "\n", - "Requirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\n", - "Requirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\n", - "Requirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\n", - "Requirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\n", - "Requirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\n", - "Requirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\n", - "Requirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\n", - "Requirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\n", - "Requirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\n", - "Requirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\n", - "Requirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\n", - "Requirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\n", - "Requirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\n", - "Requirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\n", - "Requirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\n", + "META YTD Gain: 45.05%\n", + "TSLA YTD Gain: -18.43%\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant exitcode: 0 (execution succeeded)\n", - "Code output: \n", - "YTD gain for META: 39.78%\n", - "YTD gain for TSLA: -19.31%\n", - "\n", - "Requirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\n", - "Requirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\n", - "Requirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\n", - "Requirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\n", - "Requirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\n", - "Requirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\n", - "Requirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\n", - "Requirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\n", - "Requirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\n", - "Requirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\n", - "Requirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\n", - "Requirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\n", - "Requirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\n", - "Requirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\n", - "Requirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\n", - " None\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "The year-to-date (YTD) gain for META (Meta Platforms, Inc.) is approximately 39.78%, and the YTD loss for TESLA (Tesla, Inc.) is approximately -19.31%. This means that so far this year, META's stock price has increased by about 39.78%, while TESLA's stock price has decreased by about 19.31%.\n", + "The year-to-date (YTD) gain for META (Meta Platforms, Inc.) is 45.05%, indicating that the stock price has increased by this percentage since the beginning of the year.\n", + "\n", + "On the other hand, TESLA (Tesla, Inc.) has a YTD loss of -18.43%, which means that the stock price has decreased by this percentage since the start of the year.\n", "\n", - "Please note that stock prices are subject to market fluctuations, and the values provided are based on the closing prices from the start of the year to today's date.\n", + "In summary, as of today, March 3, 2024, META has had a significant gain since the beginning of the year, while TESLA has experienced a decline.\n", "\n", "TERMINATE\n", "\n", - "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n" + "--------------------------------------------------------------------------------\n" ] } ], @@ -386,9 +257,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "Chat history: [{'content': 'What date is today? Compare the year-to-date gain for META and TESLA.', 'role': 'assistant'}, {'content': \"To get the current date, we can use Python's `datetime` module. After that, we will need to retrieve the year-to-date (YTD) gain for both META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.). We can do this by fetching the stock prices from the beginning of the year and the current stock prices, then calculating the percentage change.\\n\\nFirst, let's write a Python script to get the current date:\\n\\n```python\\n# filename: get_current_date.py\\n\\nfrom datetime import datetime\\n\\n# Get the current date\\ncurrent_date = datetime.now()\\n\\n# Print the current date in YYYY-MM-DD format\\nprint(current_date.strftime('%Y-%m-%d'))\\n```\\n\\nPlease save the above code in a file named `get_current_date.py` and execute it to get today's date. After that, we will proceed to the next step of fetching the stock data.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n2024-02-16\\n', 'role': 'assistant'}, {'content': 'Great, today\\'s date is 2024-02-16.\\n\\nNext, we will fetch the stock prices for META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.) from the beginning of the year until today. To do this, we can use the `yfinance` library in Python, which allows us to retrieve historical stock data.\\n\\nI will provide you with a Python script that will:\\n1. Fetch the stock prices for META and TESLA from the beginning of the year (2024-01-01) to today (2024-02-16).\\n2. Calculate the YTD gain for each stock.\\n\\nPlease execute the following Python script:\\n\\n```python\\n# filename: compare_ytd_gains.py\\n\\nimport yfinance as yf\\nfrom datetime import datetime\\n\\n# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\\ntickers = [\"META\", \"TSLA\"]\\n\\n# Define the start and end dates for the YTD period\\nstart_date = datetime(2024, 1, 1)\\nend_date = datetime(2024, 2, 16)\\n\\n# Function to calculate YTD gain\\ndef calculate_ytd_gain(ticker):\\n # Fetch historical data for the ticker\\n data = yf.download(ticker, start=start_date, end=end_date)\\n \\n # Get the first and last closing prices\\n initial_price = data[\\'Close\\'].iloc[0]\\n final_price = data[\\'Close\\'].iloc[-1]\\n \\n # Calculate the YTD gain\\n ytd_gain = ((final_price - initial_price) / initial_price) * 100\\n return ytd_gain\\n\\n# Calculate and print the YTD gains for both stocks\\nytd_gains = {}\\nfor ticker in tickers:\\n ytd_gains[ticker] = calculate_ytd_gain(ticker)\\n\\nprint(f\"YTD gain for META: {ytd_gains[\\'META\\']:.2f}%\")\\nprint(f\"YTD gain for TSLA: {ytd_gains[\\'TSLA\\']:.2f}%\")\\n```\\n\\nBefore running this script, ensure that you have the `yfinance` library installed. If it\\'s not installed, you can install it using the following command:\\n\\n```sh\\npip install yfinance\\n```\\n\\nAfter installing `yfinance`, save the script as `compare_ytd_gains.py` and execute it to compare the YTD gains for META and TESLA.', 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\nYTD gain for META: 39.78%\\nYTD gain for TSLA: -19.31%\\n\\nRequirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\\nRequirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\\nRequirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\\nRequirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\\nRequirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\\nRequirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\\nRequirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\\nRequirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\\nRequirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\\nRequirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\\nRequirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\\nRequirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\\nRequirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\\nRequirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\\nRequirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\\nRequirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\\nRequirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\\nRequirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\\nRequirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\\nRequirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\\nRequirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\\n', 'role': 'assistant'}, {'content': \"The year-to-date (YTD) gain for META (Meta Platforms, Inc.) is approximately 39.78%, and the YTD loss for TESLA (Tesla, Inc.) is approximately -19.31%. This means that so far this year, META's stock price has increased by about 39.78%, while TESLA's stock price has decreased by about 19.31%.\\n\\nPlease note that stock prices are subject to market fluctuations, and the values provided are based on the closing prices from the start of the year to today's date.\\n\\nTERMINATE\", 'role': 'user'}]\n", - "Summary: Today's date is 2024-02-16. The year-to-date gain for META (Meta Platforms, Inc.) is approximately 39.78%, while TESLA (Tesla, Inc.) has experienced a year-to-date loss of about -19.31%.\n", - "Cost info: ({'total_cost': 0.21233999999999997, 'gpt-4': {'cost': 0.21233999999999997, 'prompt_tokens': 5348, 'completion_tokens': 865, 'total_tokens': 6213}}, {'total_cost': 0.1866, 'gpt-4': {'cost': 0.1866, 'prompt_tokens': 4858, 'completion_tokens': 681, 'total_tokens': 5539}})\n" + "Chat history: [{'content': 'What date is today? Compare the year-to-date gain for META and TESLA.', 'role': 'assistant'}, {'content': \"To get the current date, we can use Python's `datetime` module. After that, we will need to retrieve the year-to-date (YTD) gain for both META (Meta Platforms, Inc.) and TESLA (Tesla, Inc.). We can do this by fetching the stock prices from the beginning of the year and the current stock prices, then calculating the percentage change.\\n\\nFirst, let's write a Python script to get the current date:\\n\\n```python\\n# filename: get_current_date.py\\n\\nfrom datetime import datetime\\n\\n# Get the current date\\ncurrent_date = datetime.now()\\n\\n# Print the current date in YYYY-MM-DD format\\nprint(current_date.strftime('%Y-%m-%d'))\\n```\\n\\nPlease save the above code in a file named `get_current_date.py` and execute it to get today's date. After that, we will proceed to the next step of fetching the stock data.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n2024-03-03\\n', 'role': 'assistant'}, {'content': 'Great, today\\'s date is March 3, 2024. Now, let\\'s proceed to the next step, which is to fetch the stock prices for META and TESLA.\\n\\nWe will use Python to retrieve the stock data. For this purpose, we can use the `yfinance` library, which allows us to fetch historical market data from Yahoo Finance. If `yfinance` is not installed on your system, you will need to install it using `pip install yfinance`.\\n\\nHere\\'s the Python script to fetch the YTD gain for META and TESLA:\\n\\n```python\\n# filename: ytd_gain_comparison.py\\n\\nimport yfinance as yf\\nfrom datetime import datetime\\n\\n# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\\ntickers = [\"META\", \"TSLA\"]\\n\\n# Define the start of the year\\nstart_of_year = datetime(datetime.now().year, 1, 1)\\n\\n# Get the current date\\ncurrent_date = datetime.now()\\n\\n# Function to calculate YTD gain\\ndef calculate_ytd_gain(ticker):\\n # Fetch historical data for the ticker\\n data = yf.download(ticker, start=start_of_year, end=current_date)\\n \\n # Get the first available closing price of the year (approximation of the price at the start of the year)\\n start_price = data[\\'Close\\'].iloc[0]\\n \\n # Get the most recent closing price\\n end_price = data[\\'Close\\'].iloc[-1]\\n \\n # Calculate the YTD gain\\n ytd_gain = ((end_price - start_price) / start_price) * 100\\n \\n return ytd_gain\\n\\n# Calculate and print the YTD gain for each ticker\\nfor ticker in tickers:\\n ytd_gain = calculate_ytd_gain(ticker)\\n print(f\"{ticker} YTD Gain: {ytd_gain:.2f}%\")\\n\\n```\\n\\nPlease save the above code in a file named `ytd_gain_comparison.py` and execute it. The script will output the YTD gain for both META and TESLA. If `yfinance` is not installed, you will need to install it first by running `pip install yfinance`.', 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\nMETA YTD Gain: 45.05%\\nTSLA YTD Gain: -18.43%\\n', 'role': 'assistant'}, {'content': 'The year-to-date (YTD) gain for META (Meta Platforms, Inc.) is 45.05%, indicating that the stock price has increased by this percentage since the beginning of the year.\\n\\nOn the other hand, TESLA (Tesla, Inc.) has a YTD loss of -18.43%, which means that the stock price has decreased by this percentage since the start of the year.\\n\\nIn summary, as of today, March 3, 2024, META has had a significant gain since the beginning of the year, while TESLA has experienced a decline.\\n\\nTERMINATE', 'role': 'user'}]\n", + "Summary: Today's date is March 3, 2024. The year-to-date (YTD) gain for META (Meta Platforms, Inc.) is 45.05%, indicating an increase in stock price since the beginning of the year. In contrast, TESLA (Tesla, Inc.) has a YTD loss of -18.43%, showing a decrease in stock price over the same period.\n", + "Cost info: ({'total_cost': 0.14834999999999998, 'gpt-4': {'cost': 0.14834999999999998, 'prompt_tokens': 3267, 'completion_tokens': 839, 'total_tokens': 4106}}, {'total_cost': 0})\n" ] } ], @@ -418,47 +289,56 @@ "text": [ "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "Plot a chart of their stock price change YTD and save to stock_price_ytd.png.\n", + "Plot a chart of their stock price change YTD. Save the data to stock_price_ytd.csv, and save the plot to stock_price_ytd.png.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant Plot a chart of their stock price change YTD and save to stock_price_ytd.png. None\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "To plot a chart of the YTD stock price changes for META and TESLA and save it to a file named `stock_price_ytd.png`, we will use Python with libraries such as `matplotlib` for plotting and `yfinance` for fetching the stock data.\n", + "To plot a chart of the stock price change YTD for META and TESLA, and to save the data to `stock_price_ytd.csv` and the plot to `stock_price_ytd.png`, we will use Python with the `yfinance`, `pandas`, and `matplotlib` libraries. If `matplotlib` is not installed on your system, you will need to install it using `pip install matplotlib`.\n", "\n", - "Here is the Python script that will perform the task:\n", + "Here's the Python script to fetch the stock data, save it to a CSV file, plot the chart, and save the plot to a PNG file:\n", "\n", "```python\n", "# filename: plot_stock_price_ytd.py\n", "\n", "import yfinance as yf\n", + "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from datetime import datetime\n", "\n", "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", "tickers = [\"META\", \"TSLA\"]\n", "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", + "# Define the start of the year\n", + "start_of_year = datetime(datetime.now().year, 1, 1)\n", "\n", - "# Fetch historical data for both tickers\n", - "data = yf.download(tickers, start=start_date, end=end_date)['Close']\n", + "# Get the current date\n", + "current_date = datetime.now()\n", "\n", - "# Normalize the data to 100 (percentage change) based on the first closing price\n", - "normalized_data = (data / data.iloc[0]) * 100\n", + "# Initialize a dictionary to store data\n", + "stock_data = {}\n", "\n", - "# Plot the data\n", - "plt.figure(figsize=(14, 7))\n", + "# Fetch historical data for each ticker\n", "for ticker in tickers:\n", - " plt.plot(normalized_data.index, normalized_data[ticker], label=ticker)\n", + " stock_data[ticker] = yf.download(ticker, start=start_of_year, end=current_date)\n", + "\n", + "# Combine the closing prices of each stock into a single DataFrame\n", + "combined_data = pd.DataFrame({\n", + " ticker: data['Close']\n", + " for ticker, data in stock_data.items()\n", + "})\n", + "\n", + "# Save the combined data to CSV\n", + "combined_data.to_csv('stock_price_ytd.csv')\n", + "\n", + "# Plot the normalized stock price change YTD\n", + "normalized_data = (combined_data / combined_data.iloc[0]) * 100\n", + "normalized_data.plot(figsize=(10, 5))\n", "\n", - "# Add titles and labels\n", + "# Set plot title and labels\n", "plt.title('Stock Price Change YTD')\n", "plt.xlabel('Date')\n", "plt.ylabel('Normalized Price (Base 100)')\n", - "plt.legend()\n", "\n", "# Save the plot to a PNG file\n", "plt.savefig('stock_price_ytd.png')\n", @@ -467,335 +347,425 @@ "plt.show()\n", "```\n", "\n", - "Before running this script, ensure that you have the `matplotlib` library installed. If it's not installed, you can install it using the following command:\n", - "\n", - "```sh\n", - "pip install matplotlib\n", - "```\n", - "\n", - "After installing `matplotlib`, save the script as `plot_stock_price_ytd.py` and execute it. The script will display the chart and save it as `stock_price_ytd.png` in the current directory.\n", + "Please save the above code in a file named `plot_stock_price_ytd.py` and execute it. The script will fetch the stock data, save it to `stock_price_ytd.csv`, plot the chart, and save the plot to `stock_price_ytd.png`. If `matplotlib` is not installed, you will need to install it first by running `pip install matplotlib`.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", "\u001b[31m\n", ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", - "recipitent sending user_proxy To plot a chart of the YTD stock price changes for META and TESLA and save it to a file named `stock_price_ytd.png`, we will use Python with libraries such as `matplotlib` for plotting and `yfinance` for fetching the stock data.\n", - "\n", - "Here is the Python script that will perform the task:\n", - "\n", - "```python\n", - "# filename: plot_stock_price_ytd.py\n", - "\n", - "import yfinance as yf\n", - "import matplotlib.pyplot as plt\n", - "from datetime import datetime\n", - "\n", - "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", - "tickers = [\"META\", \"TSLA\"]\n", - "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", - "\n", - "# Fetch historical data for both tickers\n", - "data = yf.download(tickers, start=start_date, end=end_date)['Close']\n", - "\n", - "# Normalize the data to 100 (percentage change) based on the first closing price\n", - "normalized_data = (data / data.iloc[0]) * 100\n", + "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "# Plot the data\n", - "plt.figure(figsize=(14, 7))\n", - "for ticker in tickers:\n", - " plt.plot(normalized_data.index, normalized_data[ticker], label=ticker)\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "Figure(1000x500)\n", "\n", - "# Add titles and labels\n", - "plt.title('Stock Price Change YTD')\n", - "plt.xlabel('Date')\n", - "plt.ylabel('Normalized Price (Base 100)')\n", - "plt.legend()\n", "\n", - "# Save the plot to a PNG file\n", - "plt.savefig('stock_price_ytd.png')\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "# Show the plot\n", - "plt.show()\n", - "```\n", + "The script has successfully executed and created a chart showing the stock price change YTD for META and TESLA. It has also saved the data to `stock_price_ytd.csv` and the plot to `stock_price_ytd.png`.\n", "\n", - "Before running this script, ensure that you have the `matplotlib` library installed. If it's not installed, you can install it using the following command:\n", + "You should now have a CSV file with the stock price data and a PNG image with the plotted chart. The chart is normalized to show the percentage change in stock prices from the beginning of the year, with the starting price indexed to 100 for comparison purposes.\n", "\n", - "```sh\n", - "pip install matplotlib\n", - "```\n", + "TERMINATE\n", "\n", - "After installing `matplotlib`, save the script as `plot_stock_price_ytd.py` and execute it. The script will display the chart and save it as `stock_price_ytd.png` in the current directory. None\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "# followup of the previous question\n", + "user_proxy.send(\n", + " recipient=assistant,\n", + " message=\"\"\"Plot a chart of their stock price change YTD. Save the data to stock_price_ytd.csv, and save the plot to stock_price_ytd.png.\"\"\",\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's display the generated figure." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAH0CAYAAACuKActAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAACsdUlEQVR4nOzdd3hTdf/G8Xe6d9oCXexdNsgSZIhUlgsFEQUF3PqgPxyo6OMeKG4coI8oLhwMFyqKDEFBNsiUVTZtKd175Pz+OG2gtqUF2iZt79d15Upyzsk5nxQCvfNdFsMwDERERERERETEoVwcXYCIiIiIiIiIKKCLiIiIiIiIOAUFdBEREREREREnoIAuIiIiIiIi4gQU0EVEREREREScgAK6iIiIiIiIiBNQQBcRERERERFxAgroIiIiIiIiIk5AAV1ERERERETECSigi4iIiIiIiDgBBXQRERERERERJ6CALiIiIiIiIuIEFNBFREREREREnIACuoiIiIiIiIgTUEAXERERERERcQIK6CIiIiIiIiJOQAFdRERERERExAkooIuIiIiIiIg4AQV0ERERERERESeggC4iIiIiIiLiBBTQRURERERERJyAArqIiIiIiIiIE1BAFxEREREREXECCugiIiIiIiIiTkABXURERERERMQJKKCLiIiIiIiIOAEFdBEREREREREnoIAuIiIiIiIi4gQU0EVEREREREScgAK6iIiIiIiIiBNQQBcRERERERFxAgroIiIiIiIiIk5AAV1ERERERETECSigi4iIiIiIiDgBBXQRERERERERJ6CALiIiIiIiIuIEFNBFREREREREnIACuoiI1GrLly/HYrEwb948h1x/9uzZWCwWDhw44JDrl8fFF19M+/btHV2GiIhIjaeALiIiVW7r1q2MHDmSxo0b4+XlRf369bn00kt56623ihz3wgsv8O233zqmyHI4cOAAFovFfnN1daVRo0ZcffXVbN682dHllSklJYWnn36aTp064efnh7e3N+3bt+fhhx/m2LFjji6vSuTm5tKhQweaN29OZmZmsf0HDhzAx8eHa6+9tsif9Zluy5cvL/Z3w93dnbp169K7d28effRRDh065IB3KyIizs5iGIbh6CJERKT2WLVqFQMGDKBRo0aMGzeOsLAwDh8+zF9//cW+ffvYu3ev/Vg/Pz9GjhzJ7NmzK62e5cuXM2DAAObOncvIkSPP6rUHDhygadOmXH/99QwbNoz8/Hx27tzJjBkzyM7O5q+//qJz585nPEd+fj65ubl4enpisVjO452cnf379xMVFcWhQ4e49tpr6dOnDx4eHvz999988cUXBAcHs3v3bsBsQY+Pj2fbtm1VVl9VWr16NRdddBGPPPIIL7zwQpF9l19+OX/88Qc7d+5kyZIlRfZ98sknLF68mE8//bTI9ksvvZTMzMwifzdsNhuJiYmsW7eOBQsWYLFYmDVrFqNHj6709yciItWHm6MLEBGR2uX555/HarWybt06AgMDi+yLi4tzTFHn6YILLmDs2LH25xdddBFXXnklM2bM4L333ivxNenp6fj6+uLq6oqrq2tVlQpAXl4e11xzDbGxsSxfvpw+ffoU2f/888/z0ksvVWlNjtSrVy/uvPNOXnnlFcaMGUO7du0AmD9/Pj/++CPvvvsu4eHhRf6MAf766y8WL15cbDtgH7Lw778bAAcPHmTQoEGMGzeONm3a0KlTp8p5YyIiUu2oi7uIiFSpffv20a5du2LhHCAkJMT+2GKxkJ6ezscff2zvJjx+/Hj7/k2bNjF06FACAgLw8/Nj4MCB/PXXX8XOmZSUxH333UeTJk3w9PSkQYMG3HTTTcTHx5daY3Z2NpdffjlWq5VVq1ad9Xu85JJLAIiOjgZOjTP//fffufvuuwkJCaFBgwZF9v17DPrPP/9M//798ff3JyAggO7duzNnzpwix6xZs4YhQ4ZgtVrx8fGhf//+/Pnnn2XWN3/+fLZs2cJjjz1WLJwDBAQE8PzzzxfbvmPHDgYMGICPjw/169dn2rRpRfbn5OTwxBNP0LVrV6xWK76+vvTt25dly5YVOa6w+/crr7zC+++/T/PmzfH09KR79+6sW7eu2HXnzp1L27Zt8fLyon379nzzzTeMHz+eJk2aFDnOZrPxxhtv0K5dO7y8vAgNDeWOO+4gMTGxzJ/J1KlTqVu3LnfeeSeGYZCWlsakSZPs4b0iNW7cmNmzZ5OTk1PsZygiIrWbWtBFRKRKNW7cmNWrV7Nt27YzTjz26aefcuutt9KjRw9uv/12AJo3bw7A9u3b6du3LwEBATz00EO4u7vz3nvvcfHFF/P777/Ts2dPANLS0ujbty87d+7k5ptv5oILLiA+Pp7vv/+eI0eOULdu3WLXzczM5KqrrmL9+vX89ttvdO/e/azf4759+wCoU6dOke1333039erV44knniA9Pb3U18+ePZubb76Zdu3aMWXKFAIDA9m0aROLFi3ihhtuAGDp0qUMHTqUrl278uSTT+Li4sJHH33EJZdcwsqVK+nRo0ep5//+++8BuPHGG8v9nhITExkyZAjXXHMNo0aNYt68eTz88MN06NCBoUOHAuaY9g8++IDrr7+e2267jdTUVGbNmsXgwYNZu3Ztse7+c+bMITU1lTvuuAOLxcK0adO45ppr2L9/P+7u7gD8+OOPXHfddXTo0IGpU6eSmJjILbfcQv369YvVeMcddzB79mwmTJjAvffeS3R0NG+//TabNm3izz//tJ+zJFarlenTp3PttdfywQcfsGPHDmJjY/n5558rZehBr169aN68OYsXL67wc4uISDVmiIiIVKFff/3VcHV1NVxdXY1evXoZDz30kPHLL78YOTk5xY719fU1xo0bV2z78OHDDQ8PD2Pfvn32bceOHTP8/f2Nfv362bc98cQTBmAsWLCg2DlsNpthGIaxbNkyAzDmzp1rpKamGv379zfq1q1rbNq0qcz3Eh0dbQDG008/bZw4ccKIiYkxli9fbnTp0sUAjPnz5xuGYRgfffSRARh9+vQx8vLyipyjcF90dLRhGIaRlJRk+Pv7Gz179jQyMzNLrNlmsxktW7Y0Bg8ebN9mGIaRkZFhNG3a1Lj00kvPWHeXLl0Mq9Va5vsr1L9/fwMwPvnkE/u27OxsIywszBgxYoR9W15enpGdnV3ktYmJiUZoaKhx880327cV/tzq1KljJCQk2Ld/9913BmD88MMP9m0dOnQwGjRoYKSmptq3LV++3ACMxo0b27etXLnSAIzPP/+8yPUXLVpU4vbSXH755YbVajVcXV2NKVOmnPHY//znP0Zpv0oVvseXX3651NdfddVVBmAkJyeXqzYREan51MVdRESq1KWXXsrq1au58sor2bJlC9OmTWPw4MHUr1/f3rJ7Jvn5+fz6668MHz6cZs2a2beHh4dzww038Mcff5CSkgKYXbk7derE1VdfXew8/24VTU5OZtCgQezatYvly5eXObnb6Z588knq1atHWFgYF198Mfv27eOll17immuuKXLcbbfdVuZ488WLF5OamsojjzyCl5dXiTVv3ryZPXv2cMMNN3Dy5Eni4+OJj48nPT2dgQMHsmLFCmw2W6nXSElJwd/fv9zvD8wJ+04fS+3h4UGPHj3Yv3+/fZurqyseHh6A2d08ISGBvLw8unXrxsaNG4ud87rrriMoKMj+vG/fvgD2cx47doytW7dy00034efnZz+uf//+dOjQoci55s6di9Vq5dJLL7X/POLj4+natSt+fn7FutmX5p133iEnJ4eGDRvy+OOPl+s156rwPaWmplbqdUREpPpQF3cREaly3bt3Z8GCBeTk5LBlyxa++eYbXn/9dUaOHMnmzZtp27Ztqa89ceIEGRkZtG7duti+Nm3aYLPZOHz4MO3atWPfvn2MGDGiXDVNmjSJrKwsNm3aZJ8krLxuv/12rr32WlxcXAgMDKRdu3Z4enoWO65p06Zlnquwe/yZuv/v2bMHgHHjxpV6THJycpHwe7qAgIAiwbo8GjRoUOxLjaCgIP7+++8i2z7++GNeffVVdu3aRW5urn17Se+9UaNGxc4H2MeMHzx4EIAWLVoUe22LFi2KhP49e/aQnJxcZB6D05V3AsJGjRoREhJCu3bt8Pb2LtdrzlVaWhrAWX9ZIiIiNZcCuoiIOIyHhwfdu3ene/futGrVigkTJjB37lyefPLJKq/lqquu4ssvv+TFF1/kk08+wcWl/J3MWrZsSVRUVJnHVVTgK2wdf/nll0tt6T+9xfnfIiMj2bRpE4cPH6Zhw4blumZpLf/Gaau1fvbZZ4wfP57hw4czefJkQkJCcHV1ZerUqfYvHs72nOVls9kICQnh888/L3F/vXr1zvqclW3btm2EhIQQEBDg6FJERMRJKKCLiIhT6NatGwDHjx+3bytpcq569erh4+PDP//8U2zfrl27cHFxsYfO5s2bl3vt7uHDhzNo0CDGjx+Pv78/M2bMOJe3cd4KJ8Lbtm1biS3Hpx8TEBBQri8G/u2KK67giy++4LPPPmPKlCnnXuy/zJs3j2bNmtnX+S50rl+4NG7cGIC9e/cW2/fvbc2bN+e3337joosuqvSW74qwevVq9u3bV+ISbSIiUntpDLqIiFSpZcuWldhC+tNPPwEU6bru6+tLUlJSkeNcXV0ZNGgQ3333XZGlyWJjY5kzZw59+vSxt0iOGDHC3oX+30qq4aabbmL69OnMnDmThx9++Fze3nkbNGgQ/v7+TJ06laysrCL7Cmvu2rUrzZs355VXXrF3kz7diRMnzniNkSNH0qFDB55//nlWr15dbH9qaiqPPfbYWdde2CJ++s92zZo1JV6jPCIiImjfvj2ffPJJkff5+++/s3Xr1iLHjho1ivz8fJ599tli58nLyyv298iRDh48yPjx4/Hw8GDy5MmOLkdERJyIWtBFRKRK3XPPPWRkZHD11VcTGRlJTk4Oq1at4quvvqJJkyZMmDDBfmzXrl357bffeO2114iIiKBp06b07NmT5557jsWLF9OnTx/uvvtu3NzceO+998jOzi6yrvTkyZOZN28e1157LTfffDNdu3YlISGB77//npkzZ9KpU6di9U2cOJGUlBQee+wxrFYrjz76aJX8XAoFBATw+uuvc+utt9K9e3duuOEGgoKC2LJlCxkZGXz88ce4uLjwwQcfMHToUNq1a8eECROoX78+R48eZdmyZQQEBPDDDz+Ueg13d3cWLFhAVFQU/fr1Y9SoUVx00UW4u7uzfft25syZQ1BQUIlroZ/J5ZdfzoIFC7j66qu57LLLiI6OZubMmbRt27bELxLK44UXXuCqq67ioosuYsKECSQmJvL222/Tvn37Iufs378/d9xxB1OnTmXz5s0MGjQId3d39uzZw9y5c3nzzTcZOXLkOdVwPjZu3Mhnn32GzWYjKSmJdevWMX/+fCwWC59++ikdO3as8ppERMSJOXIKeRERqX1+/vln4+abbzYiIyMNPz8/w8PDw2jRooVxzz33GLGxsUWO3bVrl9GvXz/D29vbAIosubZx40Zj8ODBhp+fn+Hj42MMGDDAWLVqVbHrnTx50pg4caJRv359w8PDw2jQoIExbtw4Iz4+3jCMosusne6hhx4yAOPtt98u9b2UZyktwzi1lNq6detK3Ve4zFqh77//3ujdu7fh7e1tBAQEGD169DC++OKLIsds2rTJuOaaa4w6deoYnp6eRuPGjY1Ro0YZS5YsOWM9hRITE40nnnjC6NChg+Hj42N4eXkZ7du3N6ZMmWIcP37cflz//v2Ndu3aFXv9uHHjiix1ZrPZjBdeeMFo3Lix4enpaXTp0sVYuHBhsePO9HMDjCeffLLIti+//NKIjIw0PD09jfbt2xvff/+9MWLECCMyMrLY699//32ja9euhre3t+Hv72906NDBeOihh4xjx46V62diGIbRuHFj47LLLivzuPIss1Z4c3NzM4KDg42ePXsaU6ZMMQ4ePFjuekREpPawGMY5zMQiIiIi4kCdO3emXr16LF682NGliIiIVBiNQRcRERGnlZubS15eXpFty5cvZ8uWLVx88cWOKUpERKSSqAVdREREnNaBAweIiopi7NixREREsGvXLmbOnInVamXbtm3UqVPH0SWKiIhUGE0SJyIiIk4rKCiIrl278sEHH3DixAl8fX257LLLePHFFxXORUSkxlELuoiIiIiIiIgT0Bh0ERERERERESeggC4iIiIiIiLiBDQGvZax2WwcO3YMf39/LBaLo8sREREREREHMQyD1NRUIiIicHFR260zUECvZY4dO0bDhg0dXYaIiIiIiDiJw4cP06BBA0eXISig1zr+/v6A+SEMCAhwcDUiIiIiIuIoKSkpNGzY0J4RxPEU0GuZwm7tAQEBCugiIiIiIqKhr05EAw1EREREREREnIACuoiIiIiIiIgTUEAXERERERERcQIagy4lys/PJzc319Fl1Bju7u64uro6ugwREREREXFiCuhShGEYxMTEkJSU5OhSapzAwEDCwsI0CYeIiIiIiJRIAV2KKAznISEh+Pj4KExWAMMwyMjIIC4uDoDw8HAHVyQiIiIiIs5IAV3s8vPz7eG8Tp06ji6nRvH29gYgLi6OkJAQdXcXEREREZFiNEmc2BWOOffx8XFwJTVT4c9VY/tFRERERKQkCuhSjLq1Vw79XEVERERE5EwU0EVEREREREScgAK6iIiIiIhIFTEMw9EliBNTQJcaYfz48VgsFu68885i+/7zn/9gsVgYP358kWP/fRsyZAjLly8vcd/pt+XLlwNw5MgRPDw8aN++fRW+UxERERGprrLz8hn30ToW74h1dCnipBTQpcZo2LAhX375JZmZmfZtWVlZzJkzh0aNGhU5dsiQIRw/frzI7YsvvqB3795Fto0aNarYsb179wZg9uzZjBo1ipSUFNasWVOl71VEREREqp+nf9jBit0nmDxvCylZmjhYitMya1JjXHDBBezbt48FCxYwZswYABYsWECjRo1o2rRpkWM9PT0JCwsr8Tynb/f29iY7O7vYsYZh8NFHH/Huu+/SoEEDZs2aRc+ePSv4HYmIiIhITfHVukPMWXMIiwXeuK4zAV7uji5JnJACupyRYRhk5uZX+XW93V3Padbzm2++mY8++sge0D/88EMmTJhg75ZeUZYtW0ZGRgZRUVHU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VruIiFSImrDEmoiIiDNTQC9FWFgYa9euJTIyssT9a9euJTS0/OumpqWlsXfvXvvz6OhoNm/eTHBwMHXq1OH555/nyiuvJDw8nPj4eN555x2OHj3KtddeC0CbNm0YMmQIt912GzNnziQ3N5eJEycyevTocs/gLiIicj7Ugi4iIlK5FNBL8eCDD3L77bezYcMGBg4caA/jsbGxLFmyhP/973+88sor5T7f+vXrGTBggP15YbfzcePGMXPmTHbt2sXHH39MfHw8derUoXv37qxcuZJ27drZX/P5558zceJEBg4ciIuLCyNGjGD69OkV9I5FRETOTC3oIiIilUvroJ/BV199xeuvv86GDRvIz88HwNXVla5du3L//fczatQoB1d49rTWoYiInIvcfBut//szNgPWPjqQkAAvR5ckIiLnSdnA+agF/Qyuu+46rrvuOnJzc4mPjwegbt26Fb7uuYiIiLOLSc7CZoCHqwt1/TS3iYiISGVQQC8Hd3d3goOD7Y9FRERqm8Lu7RGBXri4FJ9AVURERM6f1kE/g8WLFzNs2DCCgoLw8fHBx8eHoKAghg0bxm+//ebo8kRERKqMJogTERGpfAropfj4448ZNmwYVquV119/nYULF7Jw4UJef/11AgMDGTZsGJ9++qmjyxQREakSmiBORESk8qmLeymef/553njjDf7zn/8U2zd+/Hj69OnDM888w4033uiA6kRERKqWvQU90MfBlYiIiNRcakEvxaFDh4iKiip1/8CBAzly5EgVViQiIuI4x5JPjUEXERGRyqGAXop27doxa9asUvd/+OGHtG3btgorEhERcRyNQRcREal86uJeildffZXLL7+cRYsWERUVRWhoKACxsbEsWbKE/fv38+OPPzq4ShERkcpnGIZ9DHoDdXEXERGpNAropbj44ovZtm0bM2bM4K+//iImJgaAsLAwhg4dyp133kmTJk0cW6SIiEgViE/LITvPhsUCYVZ1cRcREaksCuhn0KRJE1566SVHlyEiIuJQha3nof5eeLhpdJyIiEhl0f+yIiIickbHkjRBnIiISFVQQD9HW7ZswdXV1dFliIiIVLpTE8Rp/LmIiEhlUkA/D4ZhOLoEERGRSlfYxb1+oGZwFxERqUwag16Ka6655oz7k5OTsVgsVVSNiIiI4xzREmsiIiJVQgG9FD/88AOXXnqpfXm1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++6TJk2aiK+vryQlJdX4fugBs1QX81QPs7RuPL7qYZbqYp7qYp7aYfZEd44NehVdvHhRBg0aJG5ubjJy5Ejp2LGjuLq6KoPOqVOnxMPDQ9555x0RESktLVW+193dXRYvXiwiIkVFRTJo0CBp1aqVfPXVVzW+H3rALNXFPNXDLK0bj696mKW6mKe6mKd2mD3R3WODXgVFRUUyduxYGT58uJw7d055PDg4WMaNGyciIgUFBTJnzhypU6eOpKWliciN62169uwpL730kvJ9hw8frsHq9YVZqot5qodZWjceX/UwS3UxT3UxT+0weyJ18Br0Kqhbty6cnZ0xbtw4tG7dGhUVFQCA0NBQpKSkQETQoEEDPP/883jooYfw7LPP4sKFCzAYDEhLS0NOTg4GDx6sPF/nzp012hPtMUt1MU/1MEvrxuOrHmapLuapLuapHWZPpA7eB72Kbr73ovn+jCNHjkS9evUQFRWlbJeRkYFevXqhoqICQUFBOHDgANq3b4/Vq1ejefPmWpWvK8xSXcxTPczSuvH4qodZqot5qot5aofZE909Nuh3oUePHnj55ZcxduxYGI1GAICdnR3Onj2LI0eOIDExEQEBARg7dqzGleofs1QX81QPs7RuPL7qYZbqYp7qYp7aYfZE1cMG/Q6dO3cOISEh2LFjhzIFp6ysDE5OThpXVvswS3UxT/UwS+vG46seZqku5qku5qkdZk9UfbwGvZrM72fs27cP9evXVwabmTNnIiIiAjk5OVqWV6swS3UxT/UwS+vG46seZqku5qku5qkdZk905xy0LqC2MRgMAIBDhw4hLCwM3333HSZMmIDi4mLExsbCzc1N4wprD2apLuapHmZp3Xh81cMs1cU81cU8tcPsie5CzS0Ybz1KSkqkTZs2YjAYxNnZWf7xj39oXVKtxSzVxTzVwyytG4+vepilupinupindpg90Z3hNeh36PHHH0fbtm2xaNEiuLi4aF1OrcYs1cU81cMsrRuPr3qYpbqYp7qYp3aYPVH1sUG/Q5WVlbC3t9e6DKvALNXFPNXDLK0bj696mKW6mKe6mKd2mD1R9bFBJyIiIiIiItIBruJOREREREREpANs0ImIiIiIiIh0gA06ERERERERkQ6wQSciIiIiIiLSATboRERERERERDrABp2IiIiIiIhIB9igExEREREREekAG3QiIqJaaty4cTAYDDAYDHB0dETz5s3x+OOPY8WKFTAajVV+npiYGDRq1OjeFUpERERVwgadiIioFhswYAAyMzORmpqKnTt34rHHHkNERAQGDRqEiooKrcsjIiKiamCDTkREVIs5OzvD3d0dHh4eeOihh/C3v/0NW7Zswc6dOxETEwMAWLRoETp27Ih69erB09MTr776KgoLCwEACQkJeOGFF5Cfn6+cjX///fcBAKWlpZg2bRo8PDxQr149dOnSBQkJCdrsKBERkQ1gg05ERGRlevfujYCAAGzcuBEAYGdnh2XLluGXX37B559/jt27d2P69OkAgJCQECxZsgQNGzZEZmYmMjMzMW3aNABAeHg4Dh48iK+++go//fQThg0bhgEDBuDMmTOa7RsREZE1M4iIaF0EERERVd+4ceOQl5eHzZs33/K1ESNG4KeffkJycvItX1u/fj0mTZqEy5cvAzBdg/76668jLy9P2SYtLQ0PPPAA0tLS0LJlS+Xxvn37Ijg4GHPnzlV9f4iIiGydg9YFEBERkfpEBAaDAQAQHx+PefPm4eTJkygoKEBFRQWuXbuG4uJi1K1b9w+//8SJE6isrES7du0sHi8tLUWTJk3uef1ERES2iA06ERGRFUpJSUHr1q2RmpqKQYMG4ZVXXsEHH3yAxo0bY9++fRg/fjzKysr+tEEvLCyEvb09jhw5Ant7e4uv1a9fvyZ2gYiIyOawQSciIrIyu3fvxokTJxAZGYkjR47AaDRi4cKFsLMzLT2zdu1ai+2dnJxQWVlp8VhgYCAqKyuRk5ODRx55pMZqJyIismVs0ImIiGqx0tJSZGVlobKyEtnZ2YiLi8O8efMwaNAgjBkzBj///DPKy8vx0Ucf4cknn8T+/fvx2WefWTyHt7c3CgsLsWvXLgQEBKBu3bpo164dRo4ciTFjxmDhwoUIDAzEpUuXsGvXLvj7+2PgwIEa7TEREZH14iruREREtVhcXBxatGgBb29vDBgwAHv27MGyZcuwZcsW2NvbIyAgAIsWLcL8+fPh5+eHVatWYd68eRbPERISgkmTJmH48OFo1qwZFixYAABYuXIlxowZgzfeeAMPPvggBg8ejKSkJHh5eWmxq0RERFaPq7gTERERERER6QDPoBMRERERERHpABt0IiIiIiIiIh1gg05ERERERESkA2zQiYiIiIiIiHSADToRERERERGRDrBBJyIiIiIiItIBNuhEREREREREOsAGnYiIiIiIiEgH2KATERERERER6QAbdCIiIiIiIiIdYINOREREREREpANs0ImIiIiIiIh0gA06ERERERERkQ6wQSciIiIiIiLSATboRERERERERDrABp2IiIiIiIhIB9igExEREREREekAG3QiIiIiIiIiHWCDTkRERERERKQDbNCJiIiIiIiIdIANOhEREREREZEO/H8SLfcXJN0ADwAAAABJRU5ErkJggg==", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "try:\n", + " image = Image(filename=\"coding/stock_price_ytd.png\")\n", + " display(image)\n", + "except FileNotFoundError:\n", + " print(\"Image not found. Please check the file name and modify if necessary.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's display the raw data collected and saved from previous chat as well." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Date', 'META', 'TSLA']\n", + "['2024-01-02', '346.2900085449219', '248.4199981689453']\n", + "['2024-01-03', '344.4700012207031', '238.4499969482422']\n", + "['2024-01-04', '347.1199951171875', '237.92999267578125']\n", + "['2024-01-05', '351.95001220703125', '237.49000549316406']\n", + "['2024-01-08', '358.6600036621094', '240.4499969482422']\n", + "['2024-01-09', '357.42999267578125', '234.9600067138672']\n", + "['2024-01-10', '370.4700012207031', '233.94000244140625']\n", + "['2024-01-11', '369.6700134277344', '227.22000122070312']\n", + "['2024-01-12', '374.489990234375', '218.88999938964844']\n", + "['2024-01-16', '367.4599914550781', '219.91000366210938']\n", + "['2024-01-17', '368.3699951171875', '215.5500030517578']\n", + "['2024-01-18', '376.1300048828125', '211.8800048828125']\n", + "['2024-01-19', '383.45001220703125', '212.19000244140625']\n", + "['2024-01-22', '381.7799987792969', '208.8000030517578']\n", + "['2024-01-23', '385.20001220703125', '209.13999938964844']\n", + "['2024-01-24', '390.70001220703125', '207.8300018310547']\n", + "['2024-01-25', '393.17999267578125', '182.6300048828125']\n", + "['2024-01-26', '394.1400146484375', '183.25']\n", + "['2024-01-29', '401.0199890136719', '190.92999267578125']\n", + "['2024-01-30', '400.05999755859375', '191.58999633789062']\n", + "['2024-01-31', '390.1400146484375', '187.2899932861328']\n", + "['2024-02-01', '394.7799987792969', '188.86000061035156']\n", + "['2024-02-02', '474.989990234375', '187.91000366210938']\n", + "['2024-02-05', '459.4100036621094', '181.05999755859375']\n", + "['2024-02-06', '454.7200012207031', '185.10000610351562']\n", + "['2024-02-07', '469.5899963378906', '187.5800018310547']\n", + "['2024-02-08', '470.0', '189.55999755859375']\n", + "['2024-02-09', '468.1099853515625', '193.57000732421875']\n", + "['2024-02-12', '468.8999938964844', '188.1300048828125']\n", + "['2024-02-13', '460.1199951171875', '184.02000427246094']\n", + "['2024-02-14', '473.2799987792969', '188.7100067138672']\n", + "['2024-02-15', '484.0299987792969', '200.4499969482422']\n", + "['2024-02-16', '473.32000732421875', '199.9499969482422']\n", + "['2024-02-20', '471.75', '193.75999450683594']\n", + "['2024-02-21', '468.0299987792969', '194.77000427246094']\n", + "['2024-02-22', '486.1300048828125', '197.41000366210938']\n", + "['2024-02-23', '484.0299987792969', '191.97000122070312']\n", + "['2024-02-26', '481.739990234375', '199.39999389648438']\n", + "['2024-02-27', '487.04998779296875', '199.72999572753906']\n", + "['2024-02-28', '484.0199890136719', '202.0399932861328']\n", + "['2024-02-29', '490.1300048828125', '201.8800048828125']\n", + "['2024-03-01', '502.29998779296875', '202.63999938964844']\n" + ] + } + ], + "source": [ + "# Path to your CSV file\n", + "file_path = \"coding/stock_price_ytd.csv\"\n", + "\n", + "with open(file_path, mode=\"r\", encoding=\"utf-8\") as file:\n", + " # Read each line in the file\n", + " for line in file:\n", + " # Split the line into a list using the comma as a separator\n", + " row = line.strip().split(\",\")\n", + " # Print the list representing the current row\n", + " print(row)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example Task: Use User Defined Message Function to let Agents Analyze data Collected\n", + "\n", + "Let's create a user defined message to let the agents analyze the raw data and write a blogpost. The function is supposed to take `sender`, `recipient` and `context` as inputs and outputs a string of message.\n", + "\n", + "**kwargs from `initiate_chat` will be used as `context`. Take the following code as an example, the `context` includes a field `file_name` as provided in `initiate_chat`. In the user defined message function `my_message_generator`, we are reading data from the file specified by this filename.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "exitcode: 1 (execution failed)\n", - "Code output: \n", - "Traceback (most recent call last):\n", - " File \"plot_stock_price_ytd.py\", line 4, in \n", - " import matplotlib.pyplot as plt\n", - "ModuleNotFoundError: No module named 'matplotlib'\n", + "Analyze the data and write a brief but engaging blog post. \n", + " Data: \n", + "Date,META,TSLA\n", + "2024-01-02,346.2900085449219,248.4199981689453\n", + "2024-01-03,344.4700012207031,238.4499969482422\n", + "2024-01-04,347.1199951171875,237.92999267578125\n", + "2024-01-05,351.95001220703125,237.49000549316406\n", + "2024-01-08,358.6600036621094,240.4499969482422\n", + "2024-01-09,357.42999267578125,234.9600067138672\n", + "2024-01-10,370.4700012207031,233.94000244140625\n", + "2024-01-11,369.6700134277344,227.22000122070312\n", + "2024-01-12,374.489990234375,218.88999938964844\n", + "2024-01-16,367.4599914550781,219.91000366210938\n", + "2024-01-17,368.3699951171875,215.5500030517578\n", + "2024-01-18,376.1300048828125,211.8800048828125\n", + "2024-01-19,383.45001220703125,212.19000244140625\n", + "2024-01-22,381.7799987792969,208.8000030517578\n", + "2024-01-23,385.20001220703125,209.13999938964844\n", + "2024-01-24,390.70001220703125,207.8300018310547\n", + "2024-01-25,393.17999267578125,182.6300048828125\n", + "2024-01-26,394.1400146484375,183.25\n", + "2024-01-29,401.0199890136719,190.92999267578125\n", + "2024-01-30,400.05999755859375,191.58999633789062\n", + "2024-01-31,390.1400146484375,187.2899932861328\n", + "2024-02-01,394.7799987792969,188.86000061035156\n", + "2024-02-02,474.989990234375,187.91000366210938\n", + "2024-02-05,459.4100036621094,181.05999755859375\n", + "2024-02-06,454.7200012207031,185.10000610351562\n", + "2024-02-07,469.5899963378906,187.5800018310547\n", + "2024-02-08,470.0,189.55999755859375\n", + "2024-02-09,468.1099853515625,193.57000732421875\n", + "2024-02-12,468.8999938964844,188.1300048828125\n", + "2024-02-13,460.1199951171875,184.02000427246094\n", + "2024-02-14,473.2799987792969,188.7100067138672\n", + "2024-02-15,484.0299987792969,200.4499969482422\n", + "2024-02-16,473.32000732421875,199.9499969482422\n", + "2024-02-20,471.75,193.75999450683594\n", + "2024-02-21,468.0299987792969,194.77000427246094\n", + "2024-02-22,486.1300048828125,197.41000366210938\n", + "2024-02-23,484.0299987792969,191.97000122070312\n", + "2024-02-26,481.739990234375,199.39999389648438\n", + "2024-02-27,487.04998779296875,199.72999572753906\n", + "2024-02-28,484.0199890136719,202.0399932861328\n", + "2024-02-29,490.1300048828125,201.8800048828125\n", + "2024-03-01,502.29998779296875,202.63999938964844\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant exitcode: 1 (execution failed)\n", - "Code output: \n", - "Traceback (most recent call last):\n", - " File \"plot_stock_price_ytd.py\", line 4, in \n", - " import matplotlib.pyplot as plt\n", - "ModuleNotFoundError: No module named 'matplotlib'\n", - " None\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "It appears that the `matplotlib` library is not installed in your Python environment. To proceed, you need to install the `matplotlib` library. Please run the following command to install it:\n", - "\n", - "```sh\n", - "pip install matplotlib\n", - "```\n", + "To write a blog post, we need to analyze the data to identify trends, significant changes, and any other notable points. We will start by calculating the percentage change for both META and TSLA stocks from the beginning to the end of the data provided. This will give us an idea of the overall performance of each stock over the period.\n", "\n", - "After the installation is complete, please try running the `plot_stock_price_ytd.py` script again. Here is the script for your convenience:\n", + "Let's write a Python script to calculate the percentage change for both stocks.\n", "\n", "```python\n", - "# filename: plot_stock_price_ytd.py\n", + "# filename: stock_analysis.py\n", "\n", - "import yfinance as yf\n", - "import matplotlib.pyplot as plt\n", - "from datetime import datetime\n", + "import pandas as pd\n", + "from io import StringIO\n", + "\n", + "# Data provided as a CSV string\n", + "data = \"\"\"\n", + "Date,META,TSLA\n", + "2024-01-02,346.2900085449219,248.4199981689453\n", + "2024-01-03,344.4700012207031,238.4499969482422\n", + "2024-01-04,347.1199951171875,237.92999267578125\n", + "2024-01-05,351.95001220703125,237.49000549316406\n", + "2024-01-08,358.6600036621094,240.4499969482422\n", + "2024-01-09,357.42999267578125,234.9600067138672\n", + "2024-01-10,370.4700012207031,233.94000244140625\n", + "2024-01-11,369.6700134277344,227.22000122070312\n", + "2024-01-12,374.489990234375,218.88999938964844\n", + "2024-01-16,367.4599914550781,219.91000366210938\n", + "2024-01-17,368.3699951171875,215.5500030517578\n", + "2024-01-18,376.1300048828125,211.8800048828125\n", + "2024-01-19,383.45001220703125,212.19000244140625\n", + "2024-01-22,381.7799987792969,208.8000030517578\n", + "2024-01-23,385.20001220703125,209.13999938964844\n", + "2024-01-24,390.70001220703125,207.8300018310547\n", + "2024-01-25,393.17999267578125,182.6300048828125\n", + "2024-01-26,394.1400146484375,183.25\n", + "2024-01-29,401.0199890136719,190.92999267578125\n", + "2024-01-30,400.05999755859375,191.58999633789062\n", + "2024-01-31,390.1400146484375,187.2899932861328\n", + "2024-02-01,394.7799987792969,188.86000061035156\n", + "2024-02-02,474.989990234375,187.91000366210938\n", + "2024-02-05,459.4100036621094,181.05999755859375\n", + "2024-02-06,454.7200012207031,185.10000610351562\n", + "2024-02-07,469.5899963378906,187.5800018310547\n", + "2024-02-08,470.0,189.55999755859375\n", + "2024-02-09,468.1099853515625,193.57000732421875\n", + "2024-02-12,468.8999938964844,188.1300048828125\n", + "2024-02-13,460.1199951171875,184.02000427246094\n", + "2024-02-14,473.2799987792969,188.7100067138672\n", + "2024-02-15,484.0299987792969,200.4499969482422\n", + "2024-02-16,473.32000732421875,199.9499969482422\n", + "2024-02-20,471.75,193.75999450683594\n", + "2024-02-21,468.0299987792969,194.77000427246094\n", + "2024-02-22,486.1300048828125,197.41000366210938\n", + "2024-02-23,484.0299987792969,191.97000122070312\n", + "2024-02-26,481.739990234375,199.39999389648438\n", + "2024-02-27,487.04998779296875,199.72999572753906\n", + "2024-02-28,484.0199890136719,202.0399932861328\n", + "2024-02-29,490.1300048828125,201.8800048828125\n", + "2024-03-01,502.29998779296875,202.63999938964844\n", + "\"\"\"\n", + "\n", + "# Read the data into a pandas DataFrame\n", + "df = pd.read_csv(StringIO(data))\n", + "\n", + "# Calculate the percentage change for META and TSLA\n", + "meta_start = df['META'].iloc[0]\n", + "meta_end = df['META'].iloc[-1]\n", + "meta_change = ((meta_end - meta_start) / meta_start) * 100\n", + "\n", + "tsla_start = df['TSLA'].iloc[0]\n", + "tsla_end = df['TSLA'].iloc[-1]\n", + "tsla_change = ((tsla_end - tsla_start) / tsla_start) * 100\n", + "\n", + "# Print the results\n", + "print(f\"META stock changed by {meta_change:.2f}% from the beginning to the end of the period.\")\n", + "print(f\"TSLA stock changed by {tsla_change:.2f}% from the beginning to the end of the period.\")\n", + "```\n", "\n", - "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", - "tickers = [\"META\", \"TSLA\"]\n", + "Please run this script to calculate the percentage change for both META and TSLA stocks. Once we have the results, we can proceed to write the blog post.\n", "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "# Fetch historical data for both tickers\n", - "data = yf.download(tickers, start=start_date, end=end_date)['Close']\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "META stock changed by 45.05% from the beginning to the end of the period.\n", + "TSLA stock changed by -18.43% from the beginning to the end of the period.\n", "\n", - "# Normalize the data to 100 (percentage change) based on the first closing price\n", - "normalized_data = (data / data.iloc[0]) * 100\n", "\n", - "# Plot the data\n", - "plt.figure(figsize=(14, 7))\n", - "for ticker in tickers:\n", - " plt.plot(normalized_data.index, normalized_data[ticker], label=ticker)\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "# Add titles and labels\n", - "plt.title('Stock Price Change YTD')\n", - "plt.xlabel('Date')\n", - "plt.ylabel('Normalized Price (Base 100)')\n", - "plt.legend()\n", + "Based on the analysis, we can see that META stock experienced a significant increase of 45.05% from the beginning to the end of the period, while TSLA stock saw a decrease of 18.43% in the same timeframe. This information will be the foundation for our blog post.\n", "\n", - "# Save the plot to a PNG file\n", - "plt.savefig('stock_price_ytd.png')\n", + "Here's a draft for the blog post:\n", "\n", - "# Show the plot\n", - "plt.show()\n", - "```\n", + "---\n", "\n", - "Execute the script after installing `matplotlib` to generate and save the chart.\n", + "**A Tale of Two Stocks: META Soars as TSLA Dips in Early 2024**\n", "\n", - "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is sh)...\u001b[0m\n", - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 1 (inferred language is python)...\u001b[0m\n", - "recipitent sending user_proxy It appears that the `matplotlib` library is not installed in your Python environment. To proceed, you need to install the `matplotlib` library. Please run the following command to install it:\n", + "As we navigate through the early months of 2024, the stock market has presented a mixed bag of performances, with some companies experiencing remarkable growth and others facing downward trends. Two notable examples that have caught the attention of investors are META and TSLA.\n", "\n", - "```sh\n", - "pip install matplotlib\n", - "```\n", + "META, the social media giant, has seen its stock price skyrocket by an impressive 45.05% since the start of the year. This surge can be attributed to a series of successful product launches and strategic acquisitions, positioning the company at the forefront of innovation and profitability. Investors have shown their confidence in META's future, as the company continues to diversify its revenue streams and expand its global reach.\n", "\n", - "After the installation is complete, please try running the `plot_stock_price_ytd.py` script again. Here is the script for your convenience:\n", + "On the other hand, TSLA, the electric vehicle pioneer, has faced a challenging period with its stock price declining by 18.43%. The dip reflects concerns over production delays, increased competition in the electric vehicle market, and a series of high-profile controversies surrounding its CEO. Despite these setbacks, loyal supporters of TSLA believe in the company's long-term vision and its potential to rebound as it addresses these challenges.\n", "\n", - "```python\n", - "# filename: plot_stock_price_ytd.py\n", + "The contrasting trajectories of META and TSLA highlight the volatile nature of the stock market, where fortunes can shift rapidly based on consumer sentiment, market conditions, and internal company developments. As investors continue to monitor these stocks, the coming months will be crucial in determining whether META can maintain its upward momentum and if TSLA can steer back onto the path of growth.\n", "\n", - "import yfinance as yf\n", - "import matplotlib.pyplot as plt\n", - "from datetime import datetime\n", + "For those looking to invest, the current landscape serves as a reminder of the importance of due diligence and a balanced portfolio. While the allure of quick gains is tempting, the market's unpredictability necessitates a strategy that can withstand the ebbs and flows of stock valuations.\n", "\n", - "# Define the tickers for Meta Platforms, Inc. and Tesla, Inc.\n", - "tickers = [\"META\", \"TSLA\"]\n", + "As we move forward into 2024, all eyes will be on these two industry titans, as their stories unfold and continue to shape the investment world.\n", "\n", - "# Define the start and end dates for the YTD period\n", - "start_date = datetime(2024, 1, 1)\n", - "end_date = datetime(2024, 2, 16)\n", + "---\n", "\n", - "# Fetch historical data for both tickers\n", - "data = yf.download(tickers, start=start_date, end=end_date)['Close']\n", + "This blog post provides a narrative based on the data analysis we performed. It should engage readers by discussing the recent performance of both stocks and offering a broader perspective on market volatility.\n", "\n", - "# Normalize the data to 100 (percentage change) based on the first closing price\n", - "normalized_data = (data / data.iloc[0]) * 100\n", + "TERMINATE\n", "\n", - "# Plot the data\n", - "plt.figure(figsize=(14, 7))\n", - "for ticker in tickers:\n", - " plt.plot(normalized_data.index, normalized_data[ticker], label=ticker)\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "def my_message_generator(sender, recipient, context):\n", + " # your CSV file\n", + " file_name = context.get(\"file_name\")\n", + " with open(file_name, mode=\"r\", encoding=\"utf-8\") as file:\n", + " file_content = file.read()\n", + " return \"Analyze the data and write a brief but engaging blog post. \\n Data: \\n\" + file_content\n", + "\n", + "\n", + "# followup of the previous question\n", + "chat_res = user_proxy.initiate_chat(\n", + " recipient=assistant,\n", + " message=my_message_generator,\n", + " file_name=\"coding/stock_price_ytd.csv\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check the summary of the chat" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Based on the analysis, we can see that META stock experienced a significant increase of 45.05% from the beginning to the end of the period, while TSLA stock saw a decrease of 18.43% in the same timeframe. This information will be the foundation for our blog post.\n", "\n", - "# Add titles and labels\n", - "plt.title('Stock Price Change YTD')\n", - "plt.xlabel('Date')\n", - "plt.ylabel('Normalized Price (Base 100)')\n", - "plt.legend()\n", + "Here's a draft for the blog post:\n", "\n", - "# Save the plot to a PNG file\n", - "plt.savefig('stock_price_ytd.png')\n", + "---\n", "\n", - "# Show the plot\n", - "plt.show()\n", - "```\n", + "**A Tale of Two Stocks: META Soars as TSLA Dips in Early 2024**\n", "\n", - "Execute the script after installing `matplotlib` to generate and save the chart. None\n", - "\u001b[33muser_proxy\u001b[0m (to assistant):\n", + "As we navigate through the early months of 2024, the stock market has presented a mixed bag of performances, with some companies experiencing remarkable growth and others facing downward trends. Two notable examples that have caught the attention of investors are META and TSLA.\n", "\n", - "exitcode: 0 (execution succeeded)\n", - "Code output: \n", - "Collecting matplotlib\n", - " Downloading matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl.metadata (5.8 kB)\n", - "Collecting contourpy>=1.0.1 (from matplotlib)\n", - " Using cached contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl.metadata (5.8 kB)\n", - "Collecting cycler>=0.10 (from matplotlib)\n", - " Using cached cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", - "Collecting fonttools>=4.22.0 (from matplotlib)\n", - " Downloading fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl.metadata (159 kB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/159.1 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.0/159.1 kB\u001b[0m \u001b[31m1.0 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━\u001b[0m \u001b[32m153.6/159.1 kB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m159.1/159.1 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting kiwisolver>=1.3.1 (from matplotlib)\n", - " Using cached kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl.metadata (6.4 kB)\n", - "Requirement already satisfied: numpy<2,>=1.21 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (1.26.4)\n", - "Requirement already satisfied: packaging>=20.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (23.2)\n", - "Collecting pillow>=8 (from matplotlib)\n", - " Using cached pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl.metadata (9.7 kB)\n", - "Collecting pyparsing>=2.3.1 (from matplotlib)\n", - " Using cached pyparsing-3.1.1-py3-none-any.whl.metadata (5.1 kB)\n", - "Requirement already satisfied: python-dateutil>=2.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (2.8.2)\n", - "Requirement already satisfied: six>=1.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", - "Downloading matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl (7.5 MB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/7.5 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[91m━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.3/7.5 MB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.9/7.5 MB\u001b[0m \u001b[31m11.2 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/7.5 MB\u001b[0m \u001b[31m14.7 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/7.5 MB\u001b[0m \u001b[31m21.4 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━\u001b[0m \u001b[32m5.8/7.5 MB\u001b[0m \u001b[31m29.7 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.5/7.5 MB\u001b[0m \u001b[31m33.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hUsing cached contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl (242 kB)\n", - "Using cached cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", - "Downloading fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl (2.8 MB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/2.8 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m81.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hUsing cached kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl (66 kB)\n", - "Using cached pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl (3.3 MB)\n", - "Using cached pyparsing-3.1.1-py3-none-any.whl (103 kB)\n", - "Installing collected packages: pyparsing, pillow, kiwisolver, fonttools, cycler, contourpy, matplotlib\n", - "Successfully installed contourpy-1.2.0 cycler-0.12.1 fonttools-4.49.0 kiwisolver-1.4.5 matplotlib-3.8.3 pillow-10.2.0 pyparsing-3.1.1\n", - "\n", - "Figure(1400x700)\n", + "META, the social media giant, has seen its stock price skyrocket by an impressive 45.05% since the start of the year. This surge can be attributed to a series of successful product launches and strategic acquisitions, positioning the company at the forefront of innovation and profitability. Investors have shown their confidence in META's future, as the company continues to diversify its revenue streams and expand its global reach.\n", "\n", + "On the other hand, TSLA, the electric vehicle pioneer, has faced a challenging period with its stock price declining by 18.43%. The dip reflects concerns over production delays, increased competition in the electric vehicle market, and a series of high-profile controversies surrounding its CEO. Despite these setbacks, loyal supporters of TSLA believe in the company's long-term vision and its potential to rebound as it addresses these challenges.\n", "\n", - "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant exitcode: 0 (execution succeeded)\n", - "Code output: \n", - "Collecting matplotlib\n", - " Downloading matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl.metadata (5.8 kB)\n", - "Collecting contourpy>=1.0.1 (from matplotlib)\n", - " Using cached contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl.metadata (5.8 kB)\n", - "Collecting cycler>=0.10 (from matplotlib)\n", - " Using cached cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", - "Collecting fonttools>=4.22.0 (from matplotlib)\n", - " Downloading fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl.metadata (159 kB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/159.1 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.0/159.1 kB\u001b[0m \u001b[31m1.0 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━\u001b[0m \u001b[32m153.6/159.1 kB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m159.1/159.1 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting kiwisolver>=1.3.1 (from matplotlib)\n", - " Using cached kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl.metadata (6.4 kB)\n", - "Requirement already satisfied: numpy<2,>=1.21 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (1.26.4)\n", - "Requirement already satisfied: packaging>=20.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (23.2)\n", - "Collecting pillow>=8 (from matplotlib)\n", - " Using cached pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl.metadata (9.7 kB)\n", - "Collecting pyparsing>=2.3.1 (from matplotlib)\n", - " Using cached pyparsing-3.1.1-py3-none-any.whl.metadata (5.1 kB)\n", - "Requirement already satisfied: python-dateutil>=2.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from matplotlib) (2.8.2)\n", - "Requirement already satisfied: six>=1.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", - "Downloading matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl (7.5 MB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/7.5 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[91m━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.3/7.5 MB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.9/7.5 MB\u001b[0m \u001b[31m11.2 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/7.5 MB\u001b[0m \u001b[31m14.7 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/7.5 MB\u001b[0m \u001b[31m21.4 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━\u001b[0m \u001b[32m5.8/7.5 MB\u001b[0m \u001b[31m29.7 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.5/7.5 MB\u001b[0m \u001b[31m33.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hUsing cached contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl (242 kB)\n", - "Using cached cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", - "Downloading fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl (2.8 MB)\n", - "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/2.8 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m81.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hUsing cached kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl (66 kB)\n", - "Using cached pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl (3.3 MB)\n", - "Using cached pyparsing-3.1.1-py3-none-any.whl (103 kB)\n", - "Installing collected packages: pyparsing, pillow, kiwisolver, fonttools, cycler, contourpy, matplotlib\n", - "Successfully installed contourpy-1.2.0 cycler-0.12.1 fonttools-4.49.0 kiwisolver-1.4.5 matplotlib-3.8.3 pillow-10.2.0 pyparsing-3.1.1\n", - "\n", - "Figure(1400x700)\n", - " None\n", - "\u001b[33massistant\u001b[0m (to user_proxy):\n", + "The contrasting trajectories of META and TSLA highlight the volatile nature of the stock market, where fortunes can shift rapidly based on consumer sentiment, market conditions, and internal company developments. As investors continue to monitor these stocks, the coming months will be crucial in determining whether META can maintain its upward momentum and if TSLA can steer back onto the path of growth.\n", "\n", - "The script has successfully executed, and the chart displaying the stock price change YTD for META and TESLA has been plotted and saved as `stock_price_ytd.png`.\n", + "For those looking to invest, the current landscape serves as a reminder of the importance of due diligence and a balanced portfolio. While the allure of quick gains is tempting, the market's unpredictability necessitates a strategy that can withstand the ebbs and flows of stock valuations.\n", "\n", - "You should now have the file `stock_price_ytd.png` in your current directory, which contains the chart of the YTD stock price changes for both companies.\n", + "As we move forward into 2024, all eyes will be on these two industry titans, as their stories unfold and continue to shape the investment world.\n", "\n", - "TERMINATE\n", + "---\n", "\n", - "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n" + "This blog post provides a narrative based on the data analysis we performed. It should engage readers by discussing the recent performance of both stocks and offering a broader perspective on market volatility.\n", + "\n", + "\n" ] } ], "source": [ - "# followup of the previous question\n", - "user_proxy.send(\n", - " recipient=assistant,\n", - " message=\"\"\"Plot a chart of their stock price change YTD and save to stock_price_ytd.png.\"\"\",\n", - ")" + "print(chat_res.summary)" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Let's display the generated figure." + "Let's check how much the above chat cost" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, "metadata": {}, "outputs": [ { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABXgAAAK8CAYAAABV1dcbAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAADDjElEQVR4nOzdd3hUZfrG8Xtmkkz6pJGEQELvvYlIFxDEhgI2UEFsP0VXcdXV3VXXVREbyoqCimLDgoJdFAWkCEiX3iGhJJSQ3iYz5/dHyEgMSIJJTib5fq5rrsyc855znhNwJHfeeV6LYRiGAAAAAAAAAABex2p2AQAAAAAAAACAc0PACwAAAAAAAABeioAXAAAAAAAAALwUAS8AAAAAAAAAeCkCXgAAAAAAAADwUgS8AAAAAAAAAOClCHgBAAAAAAAAwEsR8AIAAAAAAACAlyLgBQAAAAAAAAAvRcALAAAAAAAAAF6KgBcAAAAAAAAAvBQBLwAAAAAAAAB4KQJeAAAAAAAAAPBSBLwAAAAAAAAA4KUIeAEAAAAAAADASxHwAgAAAAAAAICXIuAFAAAAAAAAAC9FwAsAAAAAAAAAXoqAFwAAAAAAAAC8FAEvAAAAAAAAAHgpAl4AAAAAAAAA8FIEvAAAAAAAAADgpQh4AQAAAAAAAMBLEfACAAAAAAAAgJci4AUAAAAAAAAAL0XACwAAAAAAAABeioAXAAAAAAAAALwUAS8AAAAAAAAAeCkCXgAAAAAAAADwUgS8AAAAAAAAAOClCHgBAAAAAAAAwEsR8AIAAAAAAACAlyLgBQAAAAAAAAAvRcALAAAAAAAAAF6KgBcAAAAAAAAAvBQBLwAAAAAAAAB4KQJeAAAAAAAAAPBSBLwAAAAAAAAA4KUIeAEAAAAAAADASxHwAgAAAAAAAICXIuAFAAAAAAAAAC9FwAsAAAAAAAAAXoqAFwAAAAAAAAC8FAEvAAAAAAAAAHgpAl4AAAAAAAAA8FIEvAAAAAAAAADgpQh4AQAAAAAAAMBLEfACAAAAAAAAgJci4AUAAAAAAAAAL0XACwAAAAAAAABeioAXAAAAAAAAALwUAS8AAAAAAAAAeCkCXgAAAAAAAADwUgS8AAAAAAAAAOClCHgBAAAAAAAAwEsR8AIAAAAAAACAlyLgBQAAAAAAAAAvRcALAAAAAAAAAF6KgBcAAAAAAAAAvBQBLwAAAAAAAAB4KQJeAAAAAAAAAPBSBLwAAAAos0WLFslisejTTz815fozZ86UxWLRvn37TLl+WfTr109t27Y1uwwAAADUEgS8AAAA1dzGjRs1YsQINWjQQP7+/qpXr54GDRqk//3vfyXGPf300/r888/NKbIM9u3bJ4vF4nnYbDYlJCToyiuv1Pr1680u76wyMjL0n//8Rx06dFBwcLACAgLUtm1bPfTQQzp06JDZ5VUJp9Opdu3aqUmTJsrNzS21f9++fQoMDNTIkSNL/Fn/2WPRokWl/m74+voqKipKF1xwgR555BElJiaacLcAAADewWIYhmF2EQAAADi9X375Rf3791dCQoJuuukmxcbGKikpSStWrNDu3bu1a9cuz9jg4GCNGDFCM2fOrLR6Fi1apP79+2v27NkaMWJEuY7dt2+fGjVqpOuuu05Dhw6Vy+XS1q1b9dprryk/P18rVqxQx44d//QcLpdLTqdTdrtdFovlL9xJ+ezZs0cDBw5UYmKiRo4cqV69esnPz0+//fabPvzwQ0VERGjHjh2SimbwHjt2TJs2baqy+qrS8uXL1bNnT/3jH//Q008/XWLfpZdeqqVLl2rr1q366aefSux79913NX/+fL333nsltg8aNEi5ubkl/m643W6dOHFCq1at0pw5c2SxWDRjxgxde+21lX5/AAAA3sbH7AIAAABwZk899ZQcDodWrVqlsLCwEvuOHDliTlF/UefOnTV69GjP6549e+ryyy/Xa6+9punTp5/2mOzsbAUFBclms8lms1VVqZKkwsJCXXXVVUpJSdGiRYvUq1evEvufeuopTZo0qUprMlOPHj10xx136Pnnn9eoUaPUpk0bSdJnn32mb775Rq+++qrq1q1b4s9YklasWKH58+eX2i7J03Ljj383JGn//v266KKLdNNNN6lVq1bq0KFD5dwYAACAl6JFAwAAQDW2e/dutWnTplS4K0nR0dGe5xaLRdnZ2XrnnXc8H3MfM2aMZ/+6det08cUXKzQ0VMHBwRowYIBWrFhR6pxpaWm677771LBhQ9ntdtWvX1833nijjh07dsYa8/Pzdemll8rhcOiXX34p9z1eeOGFkqS9e/dK+r3P7s8//6w777xT0dHRql+/fol9f+zB+91336lv374KCQlRaGiounXrplmzZpUYs3LlSg0ZMkQOh0OBgYHq27evli1bdtb6PvvsM23YsEH//Oc/S4W7khQaGqqnnnqq1PYtW7aof//+CgwMVL169fTss8+W2F9QUKBHH31UXbp0kcPhUFBQkHr37q2FCxeWGFfcvuD555/X66+/riZNmshut6tbt25atWpVqevOnj1brVu3lr+/v9q2bau5c+dqzJgxatiwYYlxbrdbL730ktq0aSN/f3/FxMTo9ttv14kTJ876PZk4caKioqJ0xx13yDAMZWVl6d577/WEvxWpQYMGmjlzpgoKCkp9DwEAAMAMXgAAgGqtQYMGWr58uTZt2vSnC3e99957uuWWW3TeeefptttukyQ1adJEkrR582b17t1boaGhevDBB+Xr66vp06erX79++vnnn9W9e3dJUlZWlnr37q2tW7fq5ptvVufOnXXs2DF9+eWXOnDggKKiokpdNzc3V1dccYVWr16tH3/8Ud26dSv3Pe7evVuSFBkZWWL7nXfeqTp16ujRRx9Vdnb2GY+fOXOmbr75ZrVp00YPP/ywwsLCtG7dOs2bN0/XX3+9JGnBggW6+OKL1aVLFz322GOyWq16++23deGFF2rJkiU677zzznj+L7/8UpJ0ww03lPmeTpw4oSFDhuiqq67S1VdfrU8//VQPPfSQ2rVrp4svvlhSUU/fN998U9ddd51uvfVWZWZmasaMGRo8eLB+/fXXUu0qZs2apczMTN1+++2yWCx69tlnddVVV2nPnj3y9fWVJH3zzTe65ppr1K5dO02cOFEnTpzQuHHjVK9evVI13n777Zo5c6bGjh2re+65R3v37tUrr7yidevWadmyZZ5zno7D4dCUKVM0cuRIvfnmm9qyZYtSUlL03XffVUrrjB49eqhJkyaaP39+hZ8bAADA6xkAAACotn744QfDZrMZNpvN6NGjh/Hggw8a33//vVFQUFBqbFBQkHHTTTeV2j5s2DDDz8/P2L17t2fboUOHjJCQEKNPnz6ebY8++qghyZgzZ06pc7jdbsMwDGPhwoWGJGP27NlGZmam0bdvXyMqKspYt27dWe9l7969hiTjP//5j3H06FEjOTnZWLRokdGpUydDkvHZZ58ZhmEYb7/9tiHJ6NWrl1FYWFjiHMX79u7daxiGYaSlpRkhISFG9+7djdzc3NPW7Ha7jWbNmhmDBw/2bDMMw8jJyTEaNWpkDBo06E/r7tSpk+FwOM56f8X69u1rSDLeffddz7b8/HwjNjbWGD58uGdbYWGhkZ+fX+LYEydOGDExMcbNN9/s2Vb8fYuMjDRSU1M927/44gtDkvHVV195trVr186oX7++kZmZ6dm2aNEiQ5LRoEEDz7YlS5YYkowPPvigxPXnzZt32u1ncumllxoOh8Ow2WzGww8//Kdj77rrLuNMP34U3+Nzzz13xuOvuOIKQ5KRnp5eptoAAABqC1o0AAAAVGODBg3S8uXLdfnll2vDhg169tlnNXjwYNWrV88zs/TPuFwu/fDDDxo2bJgaN27s2V63bl1df/31Wrp0qTIyMiQVtSLo0KGDrrzyylLn+eOszPT0dF100UXatm2bFi1adNbF0U712GOPqU6dOoqNjVW/fv20e/duTZo0SVdddVWJcbfeeutZ++3Onz9fmZmZ+sc//iF/f//T1rx+/Xrt3LlT119/vY4fP65jx47p2LFjys7O1oABA7R48WK53e4zXiMjI0MhISFlvj+paMG7U3vJ+vn56bzzztOePXs822w2m/z8/CQVtUtITU1VYWGhunbtqrVr15Y65zXXXKPw8HDP6969e0uS55yHDh3Sxo0bdeONNyo4ONgzrm/fvmrXrl2Jc82ePVsOh0ODBg3yfD+OHTumLl26KDg4uFSbiDOZOnWqCgoKFB8fr3//+99lOuZcFd9TZmZmpV4HAADA29CiAQAAoJrr1q2b5syZo4KCAm3YsEFz587V5MmTNWLECK1fv16tW7c+47FHjx5VTk6OWrRoUWpfq1at5Ha7lZSUpDZt2mj37t0aPnx4mWq69957lZeXp3Xr1nkW2Sqr2267TSNHjpTValVYWJjatGkju91ealyjRo3Oeq7i9g5/1r5i586dkqSbbrrpjGPS09NLhKenCg0NLRHMlkX9+vVLheLh4eH67bffSmx755139MILL2jbtm1yOp2e7ae794SEhFLnk+Tpmbt//35JUtOmTUsd27Rp0xKh8c6dO5Wenl6ij/OpyrqAX0JCgqKjo9WmTRsFBASU6ZhzlZWVJUnlDtsBAABqOgJeAAAAL+Hn56du3bqpW7duat68ucaOHavZs2frscceq/JarrjiCn300Ud65pln9O6778pqLfsHw5o1a6aBAweedVxFBYbFs3Ofe+65M840PnXG6x+1bNlS69atU1JSkuLj48t0zTPNPDYMw/P8/fff15gxYzRs2DA98MADio6Ols1m08SJEz3BdXnPWVZut1vR0dH64IMPTru/Tp065T5nZdu0aZOio6MVGhpqdikAAADVCgEvAACAF+ratask6fDhw55tp1vcqk6dOgoMDNT27dtL7du2bZusVqsntGzSpIk2bdpUpusPGzZMF110kcaMGaOQkBC99tpr53Ibf1nxQnKbNm067czVU8eEhoaWKVj+o8suu0wffvih3n//fT388MPnXuwffPrpp2rcuLHmzJlT4s/uXAP7Bg0aSJJ27dpVat8ftzVp0kQ//vijevbsWekzbyvC8uXLtXv37hJtLwAAAFCEHrwAAADV2MKFC087Q/Pbb7+VpBKtF4KCgpSWllZinM1m00UXXaQvvvhC+/bt82xPSUnRrFmz1KtXL8+MyOHDh3taQPzR6Wq48cYbNWXKFE2bNk0PPfTQudzeX3bRRRcpJCREEydOVF5eXol9xTV36dJFTZo00fPPP+/5mP+pjh49+qfXGDFihNq1a6ennnpKy5cvL7U/MzNT//znP8tde/GM3FO/tytXrjztNcoiLi5Obdu21bvvvlviPn/++Wdt3LixxNirr75aLpdL//3vf0udp7CwsNTfIzPt379fY8aMkZ+fnx544AGzywEAAKh2mMELAABQjd19993KycnRlVdeqZYtW6qgoEC//PKLPv74YzVs2FBjx471jO3SpYt+/PFHvfjii4qLi1OjRo3UvXt3Pfnkk5o/f7569eqlO++8Uz4+Ppo+fbry8/P17LPPeo5/4IEH9Omnn2rkyJG6+eab1aVLF6WmpurLL7/UtGnT1KFDh1L1jR8/XhkZGfrnP/8ph8OhRx55pEq+L8VCQ0M1efJk3XLLLerWrZuuv/56hYeHa8OGDcrJydE777wjq9WqN998UxdffLHatGmjsWPHql69ejp48KAWLlyo0NBQffXVV2e8hq+vr+bMmaOBAweqT58+uvrqq9WzZ0/5+vpq8+bNmjVrlsLDw/XUU0+Vq/ZLL71Uc+bM0ZVXXqlLLrlEe/fu1bRp09S6devTBtFl8fTTT+uKK65Qz549NXbsWJ04cUKvvPKK2rZtW+Kcffv21e23366JEydq/fr1uuiii+Tr66udO3dq9uzZevnllzVixIhzquGvWLt2rd5//3253W6lpaVp1apV+uyzz2SxWPTee++pffv2VV4TAABAdUfACwAAUI09//zzmj17tr799lu9/vrrKigoUEJCgu68807961//UlhYmGfsiy++qNtuu03/+te/lJubq5tuukndu3dXmzZttGTJEj388MOaOHGi3G63unfvrvfff1/du3f3HB8cHKwlS5boscce09y5c/XOO+8oOjpaAwYMUP369c9Y4yOPPKL09HRPyHvXXXdV5reklHHjxik6OlrPPPOM/vvf/8rX11ctW7bUfffd5xnTr18/LV++XP/973/1yiuvKCsrS7Gxserevbtuv/32s16jadOmWr9+vSZPnqy5c+fq888/l9vtVtOmTXXLLbfonnvuKXfdY8aMUXJysqZPn67vv/9erVu31vvvv6/Zs2dr0aJF5T6f9Hs7iccff1z/+Mc/1KxZM82cOVPvvPOONm/eXGLstGnT1KVLF02fPl2PPPKIfHx81LBhQ40ePVo9e/Y8p+v/VR9++KE+/PBD+fj4KDQ0VM2aNdO9996rO+64o9QicwAAAChiMc5lVQYAAAAAXqNjx46qU6eO5s+fb3YpAAAAqGD04AUAAABqCKfTqcLCwhLbFi1apA0bNqhfv37mFAUAAIBKxQxeAAAAoIbYt2+fBg4cqNGjRysuLk7btm3TtGnT5HA4tGnTJkVGRppdIgAAACoYPXgBAACAGiI8PFxdunTRm2++qaNHjyooKEiXXHKJnnnmGcJdAACAGooZvAAAAAAAAADgpejBCwAAAAAAAABeioAXAAAAAAAAALwUPXjhldxutw4dOqSQkBBZLBazywEAAAAAAKgQhmEoMzNTcXFxslqZm4mzI+CFVzp06JDi4+PNLgMAAAAAAKBSJCUlqX79+maXAS9AwAuvFBISIqnozS40NNTkagAAAAAAACpGRkaG4uPjPdkHcDYEvPBKxW0ZQkNDCXgBAAAAAECNQ0tKlBWNPAAAAAAAAADASxHwAgAAAAAAAICXIuAFAAAAAAAAAC9FD17UaC6XS06n0+wyagxfX1/ZbDazywAAAAAAAMBJBLyokQzDUHJystLS0swupcYJCwtTbGwszd4BAAAAAACqAQJe1EjF4W50dLQCAwMJIyuAYRjKycnRkSNHJEl169Y1uSIAAAAAAAAQ8KLGcblcnnA3MjLS7HJqlICAAEnSkSNHFB0dTbsGAAAAAAAAk7HIGmqc4p67gYGBJldSMxV/X+ltDAAAAAAAYD4CXtRYtGWoHHxfAQAAAAAAqg8CXgAAAAAAAADwUgS8AAAAAAAAAOClCHiBamTMmDGyWCy64447Su276667ZLFYNGbMmBJj//gYMmSIFi1adNp9pz4WLVokSTpw4ID8/PzUtm3bKrxTAAAAAAAAVAQfswsAUFJ8fLw++ugjTZ48WQEBAZKkvLw8zZo1SwkJCSXGDhkyRG+//XaJbXa7XUFBQTp8+LBn29/+9jdlZGSUGBsRESFJmjlzpq6++motXrxYK1euVPfu3Svr1gAAAAAAAFDBCHiBaqZz587avXu35syZo1GjRkmS5syZo4SEBDVq1KjEWLvdrtjY2NOe59TtAQEBys/PLzXWMAy9/fbbevXVV1W/fn3NmDGDgBcAAAAAAMCLEPCiVjAMQ7lOV5VfN8DXJovFUu7jbr75Zr399tuegPett97S2LFjPW0VKsrChQuVk5OjgQMHql69errgggs0efJkBQUFVeh1AAAAAAAAUDkIeFEuixcv1nPPPac1a9bo8OHDmjt3roYNG3basXfccYemT5+uyZMn69577/VsT01N1d13362vvvpKVqtVw4cP18svv6zg4OBKqzvX6VLrR7+vtPOfyZYnBivQr/z/mY0ePVoPP/yw9u/fL0latmyZPvroo1IB79dff13q+/bII4/okUceKdN1ZsyYoWuvvVY2m01t27ZV48aNNXv2bE+fXwAAAAAAAFRvBLwol+zsbHXo0EE333yzrrrqqjOOmzt3rlasWKG4uLhS+0aNGqXDhw9r/vz5cjqdGjt2rG677TbNmjWrMkv3KnXq1NEll1yimTNnyjAMXXLJJYqKiio1rn///nrttddKbCvurXs2aWlpmjNnjpYuXerZNnr0aM2YMYOAFwAAAAAAwEsQ8KJcLr74Yl188cV/OubgwYO6++679f333+uSSy4psW/r1q2aN2+eVq1apa5du0qS/ve//2no0KF6/vnnTxsIV4QAX5u2PDG4Us59tuueq5tvvlnjx4+XJE2dOvW0Y4KCgtS0adNzOv+sWbOUl5dXoueuYRhyu93asWOHmjdvfk7nBQAAAAAAQNUh4EWFcrvduuGGG/TAAw+oTZs2pfYvX75cYWFhnnBXkgYOHCir1aqVK1fqyiuvrJS6LBbLObVKMNOQIUNUUFAgi8WiwYMrPpyeMWOG7r///lKzde+880699dZbeuaZZyr8mgAAAAAAAKhY3pV4odqbNGmSfHx8dM8995x2f3JysqKjo0ts8/HxUUREhJKTk8943vz8fOXn53teZ2RkVEzB1ZjNZtPWrVs9z08nPz+/1PfNx8fntO0cTrV+/XqtXbtWH3zwgVq2bFli33XXXacnnnhCTz75pHx8eIsAAAAAAACozqxmF4CaY82aNXr55Zc1c+ZMWSyWCj33xIkT5XA4PI/4+PgKPX91FRoaqtDQ0DPunzdvnurWrVvi0atXr7Oed8aMGWrdunWpcFeSrrzySh05ckTffvvtX6odAAAAAICqlFNQqFveWa11iSfMLgWoUhbDMAyzi4B3slgsmjt3roYNGyZJeumllzRhwgRZrb//3sDlcslqtSo+Pl779u3TW2+9pfvvv18nTvz+ZltYWCh/f3/Nnj37jC0aTjeDNz4+Xunp6aUC0Ly8PO3du1eNGjWSv79/Bd4xJL6/AAAAAIDqJ7fApbEzf9WKPamKc/hr4QP9ZPc593VxzJSRkSGHw3HazAM4HT5/jQpzww03aODAgSW2DR48WDfccIPGjh0rSerRo4fS0tK0Zs0adenSRZK0YMECud3uEot9/ZHdbpfdbq+84gEAAAAAgFfKc7p0y7urtGJPqoLtPpo6qrPXhrvAuSDgRblkZWVp165dntd79+7V+vXrFRERoYSEBEVGRpYY7+vrq9jYWLVo0UKS1KpVKw0ZMkS33nqrpk2bJqfTqfHjx+vaa69VXFxcld4LAAAAAADwbnlOl259d7WW7TquID+b3rm5mzolhJtdFlCl6MGLclm9erU6deqkTp06SZImTJigTp066dFHHy3zOYoX9howYICGDh2qXr166fXXX6+skgEAAAAAQA2UX+jS7e+t0ZKdxxToZ9PMm89TlwYRZpcFVDlm8KJc+vXrp/K0bd63b1+pbREREZo1a1YFVgUAAAAAAGqT/EKX/u/9tfp5x1EF+Nr09phu6taQcBe1EzN4AQAAAAAA4DUKCt2664N1WrDtiPx9rZoxpqu6N448+4FADUXACwAAAAAAAK/gdLl194dr9ePWFNl9rJpxUzdd0CTK7LIAUxHwAgAAAAAAoNpzuty658N1+n5zivx8rHrjxq7q2ZRwFyDgBQAAAAAAQLVW6HLrvo/X67tNyfKzWTX9hi7q07yO2WUB1QIBLwAAAAAAAKotl9vQ/bM36OvfDsvXZtFrozurf4tos8sCqg0CXqAasFgsf/p4/PHHJUlz587V+eefL4fDoZCQELVp00b33nuv5zwzZ85UWFhYma7ZsmVL2e12JScnV/wNAQAAAABQAVxuQw/M3qAv1h+Sj9Wiqdd31oBWMWaXBVQrBLxANXD48GHP46WXXlJoaGiJbX//+9/1008/6ZprrtHw4cP166+/as2aNXrqqafkdDrLfb2lS5cqNzdXI0aM0DvvvFMJdwQAAAAAwF/jdht66LPfNGfdQdmsFr1yfSdd1CbW7LKAasfH7AIASLGxv/8PyuFwyGKxlNgmSV999ZV69uypBx54wLOtefPmGjZsWLmvN2PGDF1//fXq27ev/va3v+mhhx4659oBAAAAAKhobrehh+ds1KdrDshmtWjKtZ00pG1ds8sCqiVm8AJeIjY2Vps3b9amTZv+0nkyMzM1e/ZsjR49WoMGDVJ6erqWLFlSQVUCAAAAAPDXuN2G/vn5Jn28OklWi/TSNR11SXvCXeBMmMGL2sEwJGdO1V/XN1CyWCrkVHfffbeWLFmidu3aqUGDBjr//PN10UUXadSoUbLb7WU+z0cffaRmzZqpTZs2kqRrr71WM2bMUO/evSukTgAAAAAAzpVhGHr0y0368NdEWS3S5Gs66rIOcWaXBVRrBLyoHZw50tMm/A/hkUOSX1CFnCooKEjffPONdu/erYULF2rFihW6//779fLLL2v58uUKDAws03neeustjR492vN69OjR6tu3r/73v/8pJCSkQmoFAAAAAKC8DMPQf77aovdXJMpikZ4b0UFXdKxndllAtUeLBsDLNGnSRLfccovefPNNrV27Vlu2bNHHH39cpmO3bNmiFStW6MEHH5SPj498fHx0/vnnKycnRx999FElVw4AAAAAwOkZhqH/fr1VM3/ZJ4tFmjS8vYZ3qW92WYBXYAYvagffwKLZtGZctxI1bNhQgYGBys7OLtP4GTNmqE+fPpo6dWqJ7W+//bZmzJihW2+9tTLKBAAAAADgjAzD0MTvtumtZXslSROvbKeru8abXBXgPQh4UTtYLBXWKsEsjz/+uHJycjR06FA1aNBAaWlpmjJlipxOpwYNGuQZ53K5tH79+hLH2u12NW3aVO+9956eeOIJtW3btsT+W265RS+++KI2b97s6c0LAAAA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yMjIcns0Llq0SB9//LG+/fbb4nWbNm3SE088oZ9++kl+fhV7CxQdPzIyssLPXVEpKSmKj48vddyibfXq1Tut46NiGHPuY+7GG2/UgQMH1K1bN1mWpby8PN1zzz20aKhE3jTmKiI6OlqTJk3SX/7yF2VnZ+s///mPLrroIi1ZskSdOnWq8uf3Bt485qZNm6ZffvlFb775Zrn7PP7444qJiTlpGxHgVNSG99/mzZv16quvaty4cSc9dlnHTU9P19GjRxUcHFyhGnF6GHPuY+6JJ55Qenq6WrVqJV9fX+Xn5+uFF17QTTfdVKH6cXLeNOYqIiEhQe+8847at2+vtLQ0jRs3TomJiVqzZo3OPPPM0z4+cDxm8KJGuv/++7V69Wp99NFHp3yM1atXa8CAARo5cqR69eolyfSBvPHGGzVq1Ci1bNmyzMe9//77qlOnTvHy008/nXINx2vTpk3xcfm0uGZhzLmbN2+eXnzxRb3++utavny5PvvsM3377bd67rnnKq02b8eYc5eQkKC7775bnTt3VmJiot555x0lJiZqwoQJlVabt/PWMTd37lzddttteuutt9SmTZsyj/HSSy/po48+0ueff84FUFAlnP7+2717t/r06aNrr71Wf/3rX4vXH3vce+6559ReGKoEY87dtGnT9P777+uDDz7Q8uXLNXXqVI0bN05Tp071uDaUjTHnrmvXrho8eLA6duyoHj166LPPPlPDhg1P+GEzcDqYwYsa54EHHtA333yjH3/80e2TraioKOXk5Cg1NdXtE8C9e/eWuvDY2rVr1bNnT9111116+umni9cfPnxYv/76q1asWKEHHnhAkvmk0bIs+fn56fvvv9cVV1yhLl26FD+mcePGSk5OLn6u6Ohot+euyNU7i3z33XfKzc2VpOLZC1FRUW5XES06btE2VD3GXOkxN2LECN1yyy268847JUnt2rVTRkaG7rrrLj311FPy8eHzwdPhbWPuVJ133nlasGDBaR0DhreOufnz56t///6aMGGCBg8eXObjx40bp5deekmzZs1S+/btK/y8QEU5/f23Z88eXXzxxUpMTHS7IKskrVy5svh2eHh48esq698Z4eHhzN6tJoy50mPuscce0xNPPKFBgwZJMv+23b59u0aPHq0hQ4aU/8tEhXjbmDsV/v7+Ouecc7R58+ZTPgZwQva1/wXcFRQUWPfff78VExNjbdy4sdT2ogbtn376afG69evXl2rQvnr1aqtRo0bWY489VuoY+fn51qpVq9yWe++910pISLBWrVplHTlypNzaoqKirHHjxhWvS0tLq9QLXuXk5BSvGz58OBdZqwaMufLHXKdOnaxhw4a5Pe6DDz6wgoODrby8vAo9D0rz1jF3rPIuslaWpKQk66qrrvL4OVDCm8fc3LlzrdDQUOu1114rd58xY8ZY4eHhJ7zQC3CqasP7b9euXVaLFi2sQYMGVfjv/2HDhllt27Z1W3fDDTdwkbVqwJgrcfyYq1+/vvX666+77fPiiy9aLVq0qNBzoGzeOuaOVd5F1o6Xl5dnJSQkWA8//LDHzwFUBAEvaox7773XioiIsObNm2clJycXL5mZmcX73HPPPVZcXJw1Z84c69dff7W6du1qde3atXj7qlWrrIYNG1o333yz2zH27dtX7vNW5AqclmVZL730klW3bl3ryy+/tH7//XdrwIABVnx8vHX06NHifbZv326tWLHCGjVqlFWnTh1rxYoV1ooVK6zDhw+Xe9zU1FQrMjLSuuWWW6zVq1dbH330kRUSEmK9+eabxftkZ2cXHys6Otp69NFHrRUrVlibNm06ad0oH2Ou/DE3cuRIKywszPrwww+tLVu2WN9//73VvHlz67rrrjtp3Sift445y7KsNWvWWCtWrLD69+9vXXTRRcWPKzJhwgTriy++sDZt2mStWrXKeuihhywfHx9r1qxZJ60b5fPWMTdnzhwrJCTEGj58uFvNBw8edHvugIAA69NPP3Xb52RjGagop7//du3aZZ111llWz549rV27drk9/4ls2bLFCgkJsR577DFr3bp11r/+9S/L19fXmjFjRvE+hw8fLn4vS7LGjx9vrVixwtq+fftJ60b5GHPlj7khQ4ZYjRs3tr755htr69at1meffWadccYZpSY0wDPeOuYsyyo+h3Xu3Nm68cYbrRUrVlhr1qwp3j5q1Chr5syZ1h9//GEtW7bMGjRokBUUFOS2D1CZCHhRY0gqc5k8eXLxPkePHrXuu+8+q169elZISIh11VVXuZ18R44cWeYxmjRpUu7zVvQvh4KCAmvEiBFWZGSkFRgYaPXs2dPasGGD2z5Dhgwp8/nnzp17wmP/9ttvVrdu3azAwECrcePG1ksvveS2fevWrWUet0ePHietG+Vj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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "({'total_cost': 0.7510199999999999, 'gpt-4': {'cost': 0.7510199999999999, 'prompt_tokens': 14984, 'completion_tokens': 5025, 'total_tokens': 20009}}, {'total_cost': 0.3678, 'gpt-4': {'cost': 0.3678, 'prompt_tokens': 7478, 'completion_tokens': 2391, 'total_tokens': 9869}})\n" + ] } ], "source": [ - "try:\n", - " image = Image(filename=\"coding/stock_price_ytd.png\")\n", - " display(image)\n", - "except FileNotFoundError:\n", - " print(\"Image not found. Please check the file name and modify if necessary.\")" + "print(chat_res.cost)" ] }, { @@ -812,7 +782,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -821,13 +791,6 @@ " super().__init__(name, **kwargs)\n", " self._ipython = get_ipython()\n", "\n", - " def generate_init_message(self, *args, **kwargs) -> Union[str, Dict]:\n", - " return (\n", - " super().generate_init_message(*args, **kwargs)\n", - " + \"\"\"\n", - "If you suggest code, the code will be executed in IPython.\"\"\"\n", - " )\n", - "\n", " def run_code(self, code, **kwargs):\n", " result = self._ipython.run_cell(\"%%capture --no-display cap\\n\" + code)\n", " log = self._ipython.ev(\"cap.stdout\")\n", @@ -849,17 +812,35 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The implementation overrides three functions in `UserProxyAgent`:\n", + "The implementation overrides two functions in `UserProxyAgent`:\n", "* constructor. We get the ipython instance as the code execution environment.\n", - "* `generate_init_message`. We generate a modified initial message to send to the assistant agent, by adding the info that the execution will be performed in IPython.\n", "* `run_code`. We execute the code with the ipython instance.\n", "\n", + "In addition, we create a **user defined message function** shown below to generate the initiate message to the chat. In this function, we append the raw message, carryover information (both of which are provide via `context`), and a string specifing IPython execution together as the final message." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def my_ipy_message_generator(sender, recipient, context):\n", + " raw_message = context.get(\"raw_message\", \"\")\n", + " carryover = context.get(\"carryover\", \"\")\n", + " return raw_message + carryover + \"If you suggest code, the code will be executed in IPython.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "With the new `IPythonUserProxyAgent`, we are able to run the code within the current notebook environment and display the plot directly." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -868,70 +849,130 @@ "text": [ "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", "\n", - "Plot a chart of META and TESLA stock price gain YTD\n", - "If you suggest code, the code will be executed in IPython.\n", + "Plot a chart of META and TESLA stock price gain YTDI have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.If you suggest code, the code will be executed in IPython.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant Plot a chart of META and TESLA stock price gain YTD\n", - "If you suggest code, the code will be executed in IPython. None\n", "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", "\n", - "To plot a chart of META (Facebook's parent company, Meta Platforms, Inc.) and TESLA (Tesla, Inc.) stock price gain year-to-date (YTD), we can use Python with libraries such as `pandas` for data manipulation and `matplotlib` or `plotly` for plotting. We will also use `yfinance` to fetch historical stock data.\n", + "To plot a chart of META and TESLA stock price gain YTD using the data from `stock_price_ytd.csv`, we will follow these steps:\n", "\n", - "Here's the plan:\n", - "1. Install the `yfinance` library if it's not already installed.\n", - "2. Fetch the YTD stock price data for META and TESLA.\n", - "3. Calculate the YTD gain for each stock.\n", - "4. Plot the YTD gain on a chart.\n", + "1. Read the CSV file into a DataFrame using pandas.\n", + "2. Plot the data using matplotlib.\n", "\n", - "First, let's install `yfinance` and import the necessary libraries. Execute the following code:\n", + "Here is the Python code to execute in IPython:\n", "\n", "```python\n", - "# Install yfinance if not already installed\n", - "!pip install yfinance\n", - "\n", - "import yfinance as yf\n", + "# filename: plot_stock_price_ytd.py\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", - "from datetime import datetime\n", "\n", - "# Check if today's date is required or the last trading day\n", - "today = datetime.today().strftime('%Y-%m-%d')\n", + "# Read the CSV file into a DataFrame\n", + "df = pd.read_csv('coding/stock_price_ytd.csv')\n", + "\n", + "# Check if the necessary columns 'META' and 'TESLA' are present\n", + "if 'META' in df.columns and 'TESLA' in df.columns:\n", + " # Plot the stock price gain YTD for META and TESLA\n", + " plt.figure(figsize=(10, 5))\n", + " plt.plot(df['META'], label='META')\n", + " plt.plot(df['TESLA'], label='TESLA')\n", + " plt.title('META and TESLA Stock Price Gain YTD')\n", + " plt.xlabel('Date')\n", + " plt.ylabel('Stock Price Gain')\n", + " plt.legend()\n", + " plt.grid(True)\n", + " plt.show()\n", + "else:\n", + " print(\"The required columns 'META' and 'TESLA' are not present in the CSV file.\")\n", + "```\n", + "\n", + "Please execute this code in IPython to generate the plot. If there are any issues with the code, let me know, and I will provide the necessary corrections.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", + "\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "The required columns 'META' and 'TESLA' are not present in the CSV file.\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", + "\n", + "It seems that the expected column names 'META' and 'TESLA' are not present in the CSV file. To resolve this, we need to first inspect the CSV file to understand its structure and identify the correct column names. Let's write a Python code snippet to print the first few rows of the CSV file. This will help us determine the correct column names to use for plotting.\n", "\n", - "# Fetch YTD stock data for META and TESLA\n", - "meta_data = yf.download('META', start='2023-01-01', end=today)\n", - "tesla_data = yf.download('TSLA', start='2023-01-01', end=today)\n", + "Please execute the following code:\n", "\n", - "# Calculate the YTD gain for each stock\n", - "meta_ytd_gain = (meta_data['Close'] - meta_data['Close'].iloc[0]) / meta_data['Close'].iloc[0] * 100\n", - "tesla_ytd_gain = (tesla_data['Close'] - tesla_data['Close'].iloc[0]) / tesla_data['Close'].iloc[0] * 100\n", + "```python\n", + "# filename: inspect_csv_columns.py\n", + "import pandas as pd\n", + "\n", + "# Read the CSV file into a DataFrame\n", + "df = pd.read_csv('coding/stock_price_ytd.csv')\n", + "\n", + "# Print the first few rows of the DataFrame to inspect the column names\n", + "print(df.head())\n", + "```\n", + "\n", + "After executing this code, please provide the output so that I can suggest the next steps based on the actual column names in the CSV file.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", + "\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + " Date META TSLA\n", + "0 2024-01-02 346.290009 248.419998\n", + "1 2024-01-03 344.470001 238.449997\n", + "2 2024-01-04 347.119995 237.929993\n", + "3 2024-01-05 351.950012 237.490005\n", + "4 2024-01-08 358.660004 240.449997\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", + "\n", + "Thank you for providing the output. It appears that the column names for the stock prices are 'META' and 'TSLA', not 'TESLA'. With this information, we can now plot the chart correctly using the actual column names.\n", + "\n", + "Here is the updated Python code to plot the chart:\n", + "\n", + "```python\n", + "# filename: plot_stock_price_ytd_corrected.py\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", "\n", - "# Plot the YTD gain on a chart\n", - "plt.figure(figsize=(14, 7))\n", - "plt.plot(meta_ytd_gain.index, meta_ytd_gain, label='META YTD Gain %')\n", - "plt.plot(tesla_ytd_gain.index, tesla_ytd_gain, label='TESLA YTD Gain %')\n", - "plt.title('META vs TESLA Stock Price Gain YTD')\n", + "# Read the CSV file into a DataFrame\n", + "df = pd.read_csv('coding/stock_price_ytd.csv')\n", + "\n", + "# Plot the stock price gain YTD for META and TSLA\n", + "plt.figure(figsize=(10, 5))\n", + "plt.plot(df['Date'], df['META'], label='META')\n", + "plt.plot(df['Date'], df['TSLA'], label='TSLA')\n", + "plt.title('META and TSLA Stock Price Gain YTD')\n", "plt.xlabel('Date')\n", - "plt.ylabel('Gain %')\n", + "plt.ylabel('Stock Price Gain')\n", + "plt.xticks(rotation=45) # Rotate the x-axis labels for better readability\n", "plt.legend()\n", "plt.grid(True)\n", + "plt.tight_layout() # Adjust layout to prevent clipping of labels\n", "plt.show()\n", "```\n", "\n", - "This code will install `yfinance`, fetch the YTD stock data for META and TESLA, calculate the YTD gain, and plot it on a chart. Please execute the code in your IPython environment.\n", + "Please execute this code in IPython to generate the plot. If there are any further issues, let me know, and I will address them.\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", "\u001b[31m\n", ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n" ] }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABJgAAAJwCAYAAADFk9tlAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd1yV9fvH8dc5cNggogwHIG5x59acKZqjoWW2zHap7fW1aVnZt1/btNKvaUObjsrU3Jq5zb33RlyAgMDhnPv3x4GjBCjIwQP2fj4e58E59/zc53wgz9V1XbfJMAwDERERERERERGRy2R29wBERERERERERKRsU4BJRERERERERESKRQEmEREREREREREpFgWYRERERERERESkWBRgEhERERERERGRYlGASUREREREREREikUBJhERERERERERKRYFmEREREREREREpFgUYBIRERERERERkWJRgElERESkjBk0aBABAQFuO3+nTp3o1KmT287vasOHD8dkMrl7GCIiImWaAkwiIiL5mDhxIiaTCZPJxNKlS/OsNwyDyMhITCYTvXv3zrUuZ7/8Ho888giLFi266DYXPi7Uv39/TCYTL7zwQolee2F16tSpUNcwfPhwAKpVq1bgNj169Mh17KVLl3L99ddTpUoVfHx8iIqKok+fPkyePDnXdiaTiaFDhxZ6zGPGjMFkMtGqVasiXavdbufrr7+mVatWhISEEBgYSO3atRk4cCArVqxwbrd161aGDx/O/v37i3T8KyknmJLz8PPzIzY2lpdffpnk5GR3D++SNm7cyL333ktMTAw+Pj4EBATQpEkTnn/+efbu3evu4fHOO+9gMpn4448/8l3fs2dPypUrR1xcXKF+fwYNGgTk/n0zm80EBQVRp04d7r77bubOnXsFr1BERCR/nu4egIiISGnm4+PD5MmTufbaa3MtX7x4MYcPH8bb2zvf/bp168bAgQPzLK9duzbR0dF88803uZYPGzaMgIAAXnrppXyPl5yczG+//Ua1atX47rvvnF9i3emll17igQcecL5evXo1n3zyCS+++CL16tVzLm/UqJHzeZMmTXjmmWfyHKty5crO5z/99BO33XYbTZo04YknnqB8+fLs27ePJUuWMG7cOO64447LHvOkSZOoVq0aq1atYvfu3dSsWbNQ+z3++OOMHj2aG2+8kTvvvBNPT0927NjBrFmzqF69Oq1btwYcAabXX3+dTp06Ua1atcse55Xw2WefERAQQEpKCnPmzOGtt95iwYIF/PXXX5ecW3PmzLlCo8xt3LhxPProo1SsWJE777yTunXrkpWVxebNm/n666/56KOPOHfuHB4eHkU67ssvv8x//vMfl4zxmWeeYfLkyQwePJjNmzfj6+vrXPfTTz8xa9YsRo8eTdOmTXP9jdi3bx+vvvoqDz30EO3bt3cur1GjhvN51apVGTlyJACpqans3r2bqVOn8u2339K/f3++/fZbLBaLS65DRESkyAwRERHJY8KECQZg9O3b16hYsaJhtVpzrX/wwQeNZs2aGdHR0UavXr1yrQOMIUOGFOl89evXNzp27Fjg+i+//NKwWCzGggULDMBYtGhRkY5/Jfz0008GYCxcuDDf9fm9V/mJjY016tevb2RkZORZd/z48Vyvi/Je79271wCMqVOnGqGhocbw4cMLtV98fLxhMpmMBx98MM86u92ea0yXeg9c5Z577jH8/f0va9/XXnvNAIwTJ07kWt63b18DMJYtW1bgvqmpqZd1Tlf466+/DA8PD6NDhw5GcnJynvXnzp0zXn75ZSMrK8sNo8tt+fLlhtlsNoYNG+ZclpycbFSuXNlo3bq1YbPZ8uyzevVqAzAmTJiQ7zE7duxo1K9fP8/yrKwsY/DgwQZgPP/88y67BhERkaJSiZyIiMhF3H777Zw6dSpXCUpmZiY///xzsTJpimrSpEl069aNzp07U69ePSZNmnTJfaxWKyEhIdx777151iUnJ+Pj48Ozzz7rXDZq1Cjq16+Pn58f5cuXp3nz5nlK0q6EPXv20KJFC7y8vPKsCwsLu+zjTpo0ifLly9OrVy9uueWWQr2H4MgsMQyDdu3a5VlnMpmcY5o4cSK33norAJ07d3aWMy1atMi5/ZgxY6hfvz7e3t5UrlyZIUOGkJiYmOe4K1eupGfPnpQvXx5/f38aNWrExx9/fNFxrl+/ntDQUDp16kRKSkqhru1CXbp0cV4vOEqyGjRowNq1a+nQoQN+fn68+OKLznX/7MGUnp7O8OHDqV27Nj4+PlSqVIm+ffuyZ88e5zZ2u52PPvqI+vXr4+PjQ3h4OA8//DBnzpy55Phef/11TCYTkyZNIjAwMM96Hx8fRowYkSt76c8//+TWW28lKioKb29vIiMjeeqppzh37lyuffPrwZRTfjl9+nQaNGiAt7c39evXZ/bs2Zcca+vWrXnkkUd477332Lp1K+DIkkpISGDs2LGYza77J7iHhweffPIJsbGxfPrppyQlJbns2CIiIkWhAJOIiMhFVKtWjTZt2vDdd985l82aNYukpCQGDBhQ4H7p6emcPHkyzyMzM7PIYzh69CgLFy7k9ttvBxxBr59//vmSx7JYLNx8881Mnz49z7bTp08nIyPDeQ3jxo3j8ccfJzY2lo8++ojXX3+dJk2asHLlyiKP92KsVmu+78uFX/ijo6OZP38+hw8fdum5J02aRN++ffHy8uL2229n165drF69+pL7RUdHA47yprS0tAK369ChA48//jgAL774It988w3ffPONs1xw+PDhDBkyhMqVK/P+++/Tr18/vvjiC+Li4rBarc7jzJ07lw4dOrB161aeeOIJ3n//fTp37syMGTMKPPfq1avp0qULTZs2ZdasWZfVADwnEFShQgXnslOnTnH99dfTpEkTPvroIzp37pzvvjabjd69e/P666/TrFkz3n//fZ544gmSkpLYvHmzc7uHH36Y5557jnbt2vHxxx9z7733MmnSJLp3757rPfintLQ0FixYQKdOnahatWqhrynnM3v00UcZNWoU3bt3Z9SoUfmWr+Zn6dKlDB48mAEDBvDuu++Snp5Ov379OHXq1CX3HTlyJKGhoTz88MOsXbuW0aNH8+yzz9KwYcNCj7+wPDw8uP3220lLS8u3Z5yIiMgV4e4UKhERkdIop0Ru9erVxqeffmoEBgYaaWlphmEYxq233mp07tzZMIz8y76AAh/fffddvue7WInce++9Z/j6+jrLgnbu3GkAxrRp0y55HX/88YcBGL/99luu5T179jSqV6/ufH3jjTfmW35TFIUpkSvofRk5cqRzu/HjxxuA4eXlZXTu3Nl45ZVXjD///DPfsiIKWSK3Zs0aAzDmzp1rGIajtK1q1arGE088UahrGzhwoAEY5cuXN26++WbjvffeM7Zt21bo9yAhIcHw8vIy4uLicl3Hp59+agDGl19+aRiGo9wpJibGiI6ONs6cOZPrGHa73fn8whK5pUuXGkFBQUavXr2M9PT0S15LToncjh07jBMnThj79u0zvvjiC8Pb29sIDw93lsF17NjRAIzPP/88zzE6duyYa75++eWXBmB88MEHebbN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nuTTTz/Nsy5n3P3798dmszFixIg822RlZeW53n969dVXsdls3HXXXfmWyv3z/cnJPrpwuWEYfPzxx5e8nrLEZrPx+OOPs23bNh5//PGLlsyKiIiUJGUwiYiIFFJRyrJ27tzpLG+7UHh4ON26dSvUMSZNmoSHh4cz+PBPN9xwAy+99BLff/89Tz/99EWPddtttzFq1Chee+01GjZsSL169XKtj4uLIyIignbt2hEeHs62bdv49NNPczUXd4UjR47k+74EBARw0003AY4G6TExMfTp04caNWqQmprKvHnz+O2335yBlAutWbOGN998M88xO3XqxJEjRzh79iw33HBDvuNp3bo1oaGhTJo0KU/QLcfhw4dp2bIlXbp04brrriMiIoKEhAS+++47NmzYwJNPPuksNWvSpAkeHh7897//JSkpCW9vb7p06UJYWBjDhg3j9ddfp0ePHtxwww3s2LGDMWPG0KJFC+666y7AEQz87LPP6NOnD02aNOHee++lUqVKbN++nS1btvDHH3/kGZ+vry8zZsygS5cuXH/99SxevLhQPZxcaeDAgXz99dc8/fTTrFq1ivbt2zs/t8GDB3PjjTfSsWNHHn74YUaOHMn69euJi4vDYrGwa9cufvrpJz7++OOL9jZr3749n376KY899hi1atXizjvvpG7dumRmZrJz504mTZqEl5cXERERANStW5caNWrw7LPPcuTIEYKCgpgyZUqR+ieVNklJSc7fn7S0NHbv3s3UqVPZs2cPAwYMyDd4JyIicsW46/Z1IiIipdmECRMMwFi9evVFt4uOjjZ69eqVaxlQ4OPCW7tfqH79+rnWZWZmGhUqVDDat29/0fPHxMQYTZs2veT12O12IzIy0gCMN998M8/6L774wujQoYNRoUIFw9vb26hRo4bx3HPPGUlJSZc8do6ffvrJAIyFCxfmuz46OrrA9yU6Otq53XfffWcMGDDAqFGjhuHr62v4+PgYsbGxxksvvWQkJyfnOubF3usRI0YYffr0MXx8fIzU1NQCxz1o0CDDYrEYJ0+ezHd9cnKy8fHHHxvdu3c3qlatalgsFiMwMNBo06aNMW7cOMNut+fafty4cUb16tUNDw+PPO/Hp59+atStW9ewWCxGeHi48eijjxpnzpzJc86lS5ca3bp1MwIDAw1/f3+jUaNGxqhRo5zr77nnHsPf3z/XPidPnjRiY2ONiIgIY9euXQVe72uvvWYAxokTJwrcxjAMo2PHjkb9+vULXPfPuZyWlma89NJLRkxMjGGxWIyIiAjjlltuMfbs2ZNru7FjxxrNmjUzfH19jcDAQKNhw4bG888/bxw9evSi48mxbt06Y+DAgUZUVJTh5eXlfH+eeeYZY/fu3bm23bp1q9G1a1cjICDAqFixovHggw8aGzZsMABjwoQJed6TCwHGkCFD8pw/OjrauOeeewo11guPfan3e/Xq1XnGdaGOHTvmmt8BAQFGrVq1jLvuusuYM2dOoccjIiJSUkyGUQq7FIqIiIiIiIiISJmhHkwiIiIiIiIiIlIsCjCJiIiIiIiIiEixKMAkIiIiIiIiIiLFogCTiIiIiIiIiIgUiwJMIiIiIiIiIiJSLAowiYiIiIiIiIhIsXi6ewClgd1u5+jRowQGBmIymdw9HBERERERERERlzAMg7Nnz1K5cmXM5hLMMzLcaMyYMUbDhg2NwMBAIzAw0GjdurUxc+ZM5/pz584ZgwcPNkJCQgx/f3+jb9++Rnx8fK5jHDhwwOjZs6fh6+trhIaGGs8++6xhtVqLNI5Dhw4ZgB566KGHHnrooYceeuihhx566KHHVfk4dOiQS2I5BXFrBlPVqlV55513qFWrFoZh8NVXX3HjjTeybt066tevz1NPPcXvv//OTz/9RLly5Rg6dCh9+/blr7/+AsBms9GrVy8iIiJYtmwZx44dY+DAgVgsFt5+++1CjyMwMBCAQ4cOERQUVCLX6kpWq5U5c+YQFxeHxWJx93CkDNHcEVfRXBJX0nySkqB5Ja6iuSSupPkkxXU5cyg5OZnIyEhn7KOkuDXA1KdPn1yv33rrLT777DNWrFhB1apVGT9+PJMnT6ZLly4ATJgwgXr16rFixQpat27NnDlz2Lp1K/PmzSM8PJwmTZowYsQIXnjhBYYPH46Xl1ehxpFTFhcUFFRmAkx+fn4EBQXpj5IUieaOuIrmkriS5pOUBM0rcRXNJXElzScpruLMoZJuCVRqejDZbDZ++uknUlNTadOmDWvXrsVqtdK1a1fnNnXr1iUqKorly5fTunVrli9fTsOGDQkPD3du0717dx599FG2bNlC06ZN8z1XRkYGGRkZztfJycmA44OyWq0ldIWukzPGsjBWKV00d8RVNJfElTSfpCRoXomraC6JK2k+SXFdzhy6UvPN7QGmTZs20aZNG9LT0wkICGDatGnExsayfv16vLy8CA4OzrV9eHg48fHxAMTHx+cKLuWsz1lXkJEjR/L666/nWT5nzhz8/PyKeUVXzty5c909BCmjNHfEVTSXxJU0n6QkaF6Jq2guiStpPklxFWUOpaWlleBIznN7gKlOnTqsX7+epKQkfv75Z+655x4WL15couccNmwYTz/9tPN1Tj1iXFxcmSmRmzt3Lt26dVNapRSJ5o64iuaSuJLmk5QEzStxFc0lcSXNJymuy5lDOVVbJc3tASYvLy9q1qwJQLNmzVi9ejUff/wxt912G5mZmSQmJubKYjp+/DgREREAREREsGrVqlzHO378uHNdQby9vfH29s6z3GKxFPgB2Wy2UpPGaLPZ8PT0xGazlewtBuWq4eHhgafn+V/3i811kaLQXBJX0nySkqB5Ja6iuSSupPkkxVWUOXSl5prbA0z/ZLfbycjIoFmzZlgsFubPn0+/fv0A2LFjBwcPHqRNmzYAtGnThrfeeouEhATCwsIAR5pYUFAQsbGxLhtTSkoKhw8fxjAMlx2zOAzDICIigkOHDpV4ky65evj5+REaGuruYYiIiIiIiMhVyK0BpmHDhnH99dcTFRXF2bNnmTx5MosWLeKPP/6gXLly3H///Tz99NOEhIQQFBTEY489Rps2bWjdujUAcXFxxMbGcvfdd/Puu+8SHx/Pyy+/zJAhQ/LNULocNpuNw4cPO7+cl4aAjt1uJyUlhYCAAGUwySUZhkFmZiYnTpzg4MGD7h6OiIiIiIiIXIXcGmBKSEhg4MCBHDt2jHLlytGoUSP++OMPunXrBsCHH36I2WymX79+ZGRk0L17d8aMGePc38PDgxkzZvDoo4/Spk0b/P39ueeee3jjjTdcNkar1YphGISGhuLr6+uy4xaH3W4nMzMTHx8fBZikUHx9fbFYLOzfvx8PDw93D0dERERERESuMm4NMI0fP/6i6318fBg9ejSjR48ucJvo6Ghmzpzp6qHlURoyl0SKIycYqbksIiIiIiIirqb0FxERERERERERKRYFmEREREREREREpFgUYBJxkYkTJxIcHOzuYYiIiIiIiIhccQowXaUGDRqEyWTikUceybNuyJAhmEwmBg0alGf7fz569OjBokWL8l134WPRokUAHD58GC8vLxo0aHDR8RmGQdeuXenevXuedWPGjCE4OJiuXbte9JzVqlUDoFOnTs5l3t7eVKlShT59+jB16tRCvVfx8fE88cQT1KxZEx8fH8LDw2nXrh2fffYZaWlphToGwG233cbOnTsLvX1+9u/fT4cOHfD396dDhw7s378/1/revXszZcqUYp1DRERERERExNUUYLqKRUZG8v3333Pu3DnnsvT0dCZPnkxUVFSe7Xv06MGxY8dyPb777jvatm2ba1n//v3zbNu2bVvAkcXTv39/kpOTWblyZYFjM5lMTJgwgZUrV/LFF184l+/bt4/nn3+eUaNGMWXKlFznAJgwYYLz9erVq537Pfjggxw7dow9e/YwZcoUYmNjGTBgAA899NBF36O9e/fStGlT5syZw9tvv826detYvnw5zz//PDNmzGDevHmFe7Nx3KktLCys0Nvn55lnnqFKlSqsX7+eSpUq8eyzzzrX/fDDD867KoqIiIiIiIiUJm69i1xZZBgG56w2t5zb1+JRpDuAXXPNNezZs4epU6dy5513AjB16lSioqKIiYnJs723tzcRERH5HuvC5b6+vmRkZOTZ1jAMJkyYwJgxY6hatSrjx4+nVatWBY4vMjKSjz/+mKFDhxIXF0e1atW4//77iYuL4+677wagXLlyufYJDg7Od4x+fn7O5VWrVqV169bUrVuX++67j/79+9O1a9d8xzB48GA8PT1Zs2YN/v7+zuXVq1fnxhtvxDAM57IPPviACRMmsHfvXkJCQujTpw/vvvsuAQEBgCO49uSTT5KYmAjA8OHDmT59Os888wyvvPIKZ86c4frrr2fcuHEEBgbmO55t27bxwQcfUKtWLQYNGuQMMCUmJvLyyy+zYMGCAt9PEREREREREXdRgKmIzlltxL76h1vOvfWN7vh5Fe0ju++++5gwYYIzwPTll19y7733OkvaXGnhwoWkpaXRtWtXqlSpQtu2bfnwww9zBW7+6Z577mHatGncd9999O3bl82bN7NlyxaXjOeee+7hmWeeYerUqfkGmE6dOuXMXCpojBcG9MxmM5988gkxMTHs3buXwYMH8/zzzzNmzJgCx7Bnzx6mT5/OjBkzOHPmDP379+edd97hrbfeynf7xo0bM2/ePOLi4pgzZw6NGjUC4LnnnmPIkCFERkYW5S0QERERERERuSJUIneVu+uuu1i6dCkHDhzgwIED/PXXX9x11135bjtjxgwCAgJyPd5+++1Cn2v8+PEMGDAADw8PGjRoQPXq1fnpp58uud/YsWPZvHkzTz75JGPHjiU0NLTQ57wYs9lM7dq18/QxyrF7924Mw6BOnTq5llesWNF5/S+88IJz+ZNPPknnzp2pVq0aXbp04c033+THH3+86BjsdjsTJ06kQYMGtG/fnrvvvpv58+cXuP17773H9u3bqVatGrt27eK9995jyZIlrF+/noEDB9K/f3+qV6/OI488QmZmZuHfDBEREREREZESpAymIvK1eLD1jbyNqa/UuYsqNDSUXr16MXHiRAzDoFevXlSsWDHfbTt37sxnn32Wa1lISEihzpOYmMjUqVNZunSpc9ldd93F+PHjczUTz09YWBgPP/ww06dP56abbirU+QrLMIwilRUCrFq1Crvdzp133klGRoZz+bx58xg5ciTbt28nOTmZrKws0tPTSUtLw8/PL99jVatWLVc5XKVKlUhISCjw3FWqVGHGjBnO1xkZGXTv3p2vvvqKN998k8DAQHbs2EGPHj344osveOyxx4p0bSIiIiIiIiIlQQGmIjKZTEUuU3O3++67j6FDhwIwevToArfz9/enZs2al3WOyZMnk56enqvnkmEY2O12du7cSe3atS+6v6enJ56ern1fbTYbu3btokWLFvmur1mzJiaTiR07duRaXr16dcDRayrH/v376d27N48++ihvvfUWISEhLF26lPvvv5/MzMwCA0wWiyXXa5PJhN1uL/Q1vP3228TFxdGsWTMefPBB3nzzTSwWC3379mXBggUKMImIiIiIiEipoBK5f4EePXqQmZmJ1Wqle/eSyb4aP348zzzzDOvXr3c+NmzYQPv27fnyyy9L5JyX8tVXX3HmzJkC77pWoUIFunXrxqeffkpqaupFj7V27Vrsdjvvv/8+rVu3pnbt2hw9erQkhu20bds2Jk+ezIgRIwBHwMxqtQJgtVqx2dzTbF5ERERERETkn8pWKo5cFg8PD7Zt2+Z8XpCMjAzi4+NzLfP09CywpC7H+vXr+fvvv5k0aRJ169bNte7222/njTfe4M0333R5htKF0tLSiI+PJysri8OHDzNt2jQ+/PBDHn30UTp37lzgfmPGjKFdu3Y0b96c4cOH06hRI8xmM6tXr2b79u00a9YMcGQ7Wa1WRo0aRZ8+ffjrr7/4/PPPS+x6DMPgoYceytUkvV27dowbN47atWvz9ddfc/vtt5fY+UVERERERESKQhlM/xJBQUEEBQVddJvZs2dTqVKlXI9rr732ksceP348sbGxeYJLADfffDMJCQnMnDnzssdeGOPGjaNSpUrUqFGDvn37snXrVn744YeL3uENoEaNGqxbt46uXbsybNgwGjduTPPmzRk1ahTPPvusM3uocePGfPDBB/z3v/+lQYMGTJo0iZEjR5bY9YwdO5bw8HB69+7tXDZ8+HBnGWLNmjUZMmRIiZ1fRERERETk3274r1vo+fGf/L7xmLuHUiaYDMMw3D0Id0tOTqZcuXIkJSXlCcKkp6ezb98+YmJi8PHxcdMIc7Pb7SQnJxMUFITZrBihFE56ejp79+5l3759xMXF5ekPJVIUVquVmTNn0rNnT80lKTbNJykJmlfiKppL4kqaT2VLv8+WsfbAGT65vSk3NK7s7uEAlzeHLhbzcCVFJ0RERERERERELmAYBruOnwWgdniAm0dTNijAJCIiIiIiIiJygRNnM0hOz8JsgpiK/u4eTpmgAJOIiIiIiIiIyAV2Hk8BoFoFf7w9C75ZlpynAJOIiIiIiIiIyAV2JTjK42qGqTyusBRgEhERERERERG5wK4ERwZTLfVfKjQFmERERERERERELrA7u0SuVligm0dSdijAJCIiIiIiIiKSzTAMdmaXyCmDqfAUYBIRERERERERyXYyJZPENCsmE9QIVYCpsBRgEhERERERERHJltPgOyrEDx+L7iBXWAowiZSwiRMnEhwc7O5hiIiIiIiISCHszmnwrTvIFYkCTFchk8l00cfw4cPZv39/getXrFgBgM1m45133qFu3br4+voSEhJCq1at+N///uc816BBg7jpppsuOabDhw/j5eVFgwYNLrqdYRh07dqV7t2751k3ZswYgoOD6dq160Wvr1q1agB06tTJuczb25sqVarQp08fpk6dWqj3MT4+nieeeIKaNWvi4+NDeHg47dq147PPPiMtLa1QxwC47bbb2LlzZ6G3z8/+/fvp0KED/v7+dOjQgf379+da37t3b6ZMmVKsc4iIiIiIiJQ2S3aeYPORpCt6zl3ZDb5rqsF3kSjAdBU6duyY8/HRRx8RFBSUa9mzzz7r3HbevHm51h07doxmzZoB8Prrr/Phhx8yYsQItm7dysKFC3nooYdITEws8pgm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QogIUgaTOynAJCIiIiIiIiKlTk6JnOdFSuRyVC3vV9LDkUtQiZyIiIiIiIiIlDo5JXJel8hgktJBn5KIiIiIiIiIlDqFLZGT0kGfkoiIiIiIiIiUOkUpkRP3U4BJREREREREREodlciVLfqURERERERERKTUyVKJXJmiT0lERERERERESp3MLJXIlSUKMImIiIiIiIhIqWO1qUSuLNGnJCIiIiIiIiKlTk6JnDKYygYFmERERERERESkVDEMw5nBpB5MZYM+JREREREREREpVXKCS6AAU1mhT0lERERERERESpWc8jgAi0rkygQFmERERERERESkVLFmKYOprNGnJCIiIiIiIiKlivWCDCZPszKYygIFmERERERERESkVLHaHAEmi4cJk0kBprJAASYRERERERERKVVySuRUHld26JMSERERERERkVIlp0ROAaayQ5+UiIiIiIiIiJQqF5bISdmgAJOIiIiIiIiIlCoqkSt73PpJjRw5khYtWhAYGEhYWBg33XQTO3bsyLVNp06dMJlMuR6PPPJIrm0OHjxIr1698PPzIywsjOeee46srKwreSkiIiIiIiIi4iIqkSt7PN158sWLFzNkyBBatGhBVlYWL774InFxcWzduhV/f3/ndg8++CBvvPGG87Wfn5/zuc1mo1evXkRERLBs2TKOHTvGwIEDsVgsvP3221f0ekRERERERESk+KxZjgCTp0rkygy3Bphmz56d6/XEiRMJCwtj7dq1dOjQwbncz8+PiIiIfI8xZ84ctm7dyrx58wgPD6dJkyaMGDGCF154geHDh+Pl5VWi1yAiIiIiIiIirmW1OUrkvJTBVGa4NcD0T0lJSQCEhITkWj5p0iS+/fZbIiIi6NOnD6+88oozi2n58uU0bNiQ8PBw5/bdu3fn0UcfZcuWLTRt2jTPeTIyMsjIyHC+Tk5OBsBqtWK1Wl1+Xa6WM8ayMFYpXTR3xFU0l8SVNJ+kJGheiatoLokraT4VXnpmJgCeZpPerwtczhy6Uu+fyTAM44qc6RLsdjs33HADiYmJLF261Ll87NixREdHU7lyZTZu3MgLL7xAy5YtmTp1KgAPPfQQBw4c4I8//nDuk5aWhr+/PzNnzuT666/Pc67hw4fz+uuv51k+efLkXOV3IiIiIiIiInLlbTpt4n87PKgWYPBUQ5u7h1OmpaWlcccdd5CUlERQUFCJnafUZDANGTKEzZs35wougSOAlKNhw4ZUqlSJ6667jj179lCjRo3LOtewYcN4+umnna+Tk5OJjIwkLi6uRN9sV7FarcydO5du3bphsVjcPRwpQzR3xFU0l8SVNJ+kJGheiatoLokraT4VnmlzPOzYSGjFEHr2bOHu4ZQalzOHcqq2SlqpCDANHTqUGTNmsGTJEqpWrXrRbVu1agXA7t27qVGjBhEREaxatSrXNsePHwcosG+Tt7c33t7eeZZbLJYy9Ute1sYrpYfmjriK5pK4kuaTlATNK3EVzSVxJc2nSzNMjt5L3p4eeq/yUZQ5dKXeP7d2yzIMg6FDhzJt2jQWLFhATEzMJfdZv349AJUqVQKgTZs2bNq0iYSEBOc2c+fOJSgoiNjY2BIZt4iIiIiIiIiUnEzdRa7McWsG05AhQ5g8eTK//PILgYGBxMfHA1CuXDl8fX3Zs2cPkydPpmfPnlSoUIGNGzfy1FNP0aFDBxo1agRAXFwcsbGx3H333bz77rvEx8fz8ssvM2TIkHyzlERERERERESkdMu5i5xFd5ErM9z6SX322WckJSXRqVMnKlWq5Hz88MMPAHh5eTFv3jzi4uKoW7cuzzzzDP369eO3335zHsPDw4MZM2bg4eFBmzZtuOuuuxg4cCBvvPGGuy5LRERERERERIohy+7IYLIog6nMcGsG06VuYBcZGcnixYsveZzo6GhmzpzpqmGJiIiIiIiIiBvllMgpg6ns0CclIiIiIiIiIqVKll0lcmWNPikRERERERERKVWsWSqRK2sUYBIRERERERGRUsVqU4lcWaNPSkRERERERERKFatK5MocfVIiIiIiIiIiUqrklMh5qkSuzFCASURERERERERKlZwSOS9lMJUZ+qREREREREREpFRRiVzZo09KREREREREREoVlciVPQowiYiIiIiIiEipohK5skeflIiIiIiIiIiUKiqRK3v0SYmIiIiIiIhIqaISubJHASYRERERERERKVVySuSUwVR26JMSERERERERkVIlK7tETj2Yyg59UiIiIiIiIiJSqmSqRK7MUYBJREREREREREoVlciVPfqkRERERERERKRUUYlc2aNPSkRERERERERKFZXIlT0KMImIiIiIiIhIqaISubJHn5SIiIiIiIiIlCo5JXIKMJUd+qREREREREREpFSxZuVkMKlErqxQgElEREREREREShWrMpjKHH1SIiIiIiIiIlKqnO/BpAymskIBJhEREREREREpVc6XyClsUVbokxIRERERERGRUkUlcmWPPikRERERERERKTUMw3CWyHmqRK7MUIBJREREREREREoNm93AcCQw4aUMpjJDn5SIiIiIiIiIlBpZ2eVxoBK5skSflIiIiIiIiIiUGpnZ5XGgErmyRAEmERERERERESk1cu4gB2AxK2xRVuiTEhEREREREZFSI6dEztNswmxWBlNZoQCTiIiIiIiIiJQamVm6g1xZpACTiIiIiIiIiJQa1uweTGrwXbbo0xIRERERERGRUiOnRM5LAaYyRZ+WiIiIiIiIiJQaKpErmxRgEhEREREREZFSQyVyZZM+LREREREREREpNVQiVzbp0xIRERERERGRUsOqErkySQEmERERERERESk1MlUiVybp0xIRERERERGRUiPL5iiRU4CpbNGnJSIiIiIiIiKlxvkm3yqRK0sUYBIRERERERGRK2r5nlN8MHcnx5LO5VlntSuDqSzydPcAREREREREROTfY9uxZO6duIp0q50vFu/h3nYxPNalJv7ejhDF+SbfCjCVJfq0REREREREROSKSE638ui3a0m32gny8SQjy87ni/cwctY25zY5JXJeKpErUxRgEhEREREREZESZxgGz/64gf2n0qgS7Mui5zrzxo31AVi2+5RzO5XIlU36tERERERERESkxI1dspc5W4/j5WFmzJ3XEOLvRY8GEQDsP5XKuUwboBK5skqfloiIiIiIiIiUqBV7T/Hf2dsBeLVPLI0jgwEIDfCmgr8XdgN2JZwFdBe5skoBJhEREREREREpMQnJ6QydvA67ATc3rcKdraKc60wmE3UrBQKw/ZgjwJSVXSLnpQymMkWfloiIiIiIiIiUCKvNzpDJf3MyJYM64YG8dXMDTKbcmUl1woMA2BafDECms0ROGUxliQJMIiIiIiIiIlIi3p29ndX7zxDg7clnd12Dn5dnnm1yMph2xP+zRE4hi7JEn5aIiIiIiIiIuNzMTccY9+c+AN67tRHVQwPy3a5eRHYG07FkDMMgI0sBprIob+hQREREREREROQynUzJ4NMFu5m08gAAD3WoTo8GlQrcvlZ4AGYTnEmzknA2g0U7EgCoWUBASkonBZhERERERERExCWW7TnJQ1+v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", + "image/png": "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Xf5w//33V9i2rO6DBw8aFovFmD59ern1W7duNby8vCosP112drYBGKNHj66wLjMz0zh+/LjrcfrP/W//DRiGYXz22WcGYPz4448VPpMDBw64lsXHx1fYLj093fDx8TEeeuihM9Z6up9//tkwm81GeHi4ERoaaqSmpp5x298zvNwwDGPjxo0GYDz44INVqk1E5EKlM90iIg3I9ddfT2FhIfPmzSM3N5d58+adc2j51VdfzaJFiyo8Bg0aVO33nzlzJqNGjSIoKAiA1q1b06NHjyoPMffz83M9z8zMJDs7m379+vHLL79U2HbIkCG0bNnS9bpLly4EBwezf/9+AOx2OwsWLGD06NE0a9bMtV379u0ZPnx4tY/tbIYOHcrq1au56qqr2Lx5My+++CLDhw+nSZMmfPPNN+eVOXv2bMxmc7kztTfddBPz588nMzPznPtPmzaNWbNm0a1bNxYsWMDf//53evToQffu3dm5c+c591+/fj3p6encc889+Pr6upaPGjWKdu3auUYfHD9+nB9//JHbb7+93OcMVHoDvW3btjFgwAASEhJYvHgxYWFh56ylzJAhQ2jUqBFxcXHceOONBAYGMnfuXJo0aVJuu4kTJ54z66uvvsJkMvHkk09WWFdW95w5c3A4HFx//fXlRoHExMTQunVrli5desb8nJwcAAIDAyusGzhwII0aNXI93nzzTde60/8NlI1C6dOnD0Cl/w5+q0OHDvTr18/1ulGjRrRt29b17+Jcevfuzd13301GRgbPPffceY1AqKqyzyY3N9dt7yEiUh/o7uUiIg1Io0aNGDJkCLNmzaKgoAC73c5111131n2aNm1a5Ttin83OnTvZuHEj48aNIykpybV84MCBvPnmm+Tk5JzzLsXz5s3jmWeeYdOmTRQXF7uWV9a8/bbBAwgLC3M1pMePH6ewsJDWrVtX2K5t27YkJiZW+diqolevXsyZM4eSkhI2b97M3LlzeeWVV7juuuvYtGkTHTp0qFbep59+Su/evTl58qRryHy3bt0oKSnhiy++4K677jpnxk033cRNN91ETk4OP//8MzNmzGDWrFlceeWVbNu2rVwz/VuHDh0CnJ/Vb7Vr1841PV1ZM9epU6cqHdeVV15JdHQ0CxYsqLQhPZs333yTNm3a4OXlRXR0NG3btsVsLn/+wMvLi6ZNm54za9++fTRu3Jjw8PAzbrN3714Mw6j0ZwjAarWecd+yPzzl5eVVWPfuu++Sm5tLWloat9xyS7l1GRkZTJs2jdmzZ5Oenl5uXXZ29hnfr8y5/l1URdl1/j179qzyPuej7LMp+6xERBoqNd0iIg3M2LFjufPOO0lNTWXEiBGV3tzLHT799FPAeV3ogw8+WGH9V199xYQJE864/4oVK7jqqqvo378/b731FrGxsVitVj766CNmzZpVYXuLxVJpjlGF617dydvbm169etGrVy/atGnDhAkT+OKLLyo9o3ome/fuZd26dQCVNnwzZ86sUtNdJjg4mKFDhzJ06FCsVisff/wxP//8MwMGDKhyRk0ZM2YMH3/8MTNnzuRPf/pTtfbt3bv3ORtBHx+fCo34+XI4HJhMJubPn1/pz9vZ/mgQEhJCbGxspdf5l13jffpN0Mpcf/31rFq1iocffpiuXbsSGBiIw+HgiiuuqNKUZ/X130Vlyj6bVq1a1XElIiLupaZbRKSBueaaa/jTn/7EmjVr+Pzzz2vlPQ3DYNasWQwaNIh77rmnwvqnn36amTNnnrXp/uqrr/D19WXBggXl5h7+6KOPzqumsrto7927t8K63bt3n1dmdZU1iCkpKdXab+bMmVitVj755JMKTdRPP/3E66+/TnJycqVnNatS08cff+yq6UxzqJfNG717927XXcLL7N6927W+RYsWAFW+idw//vEPvLy8uOeeewgKCqrVO+ufrmXLlixYsICMjIwznu1u2bIlhmHQvHlz2rRpU+33GDVqFP/+979Zu3YtvXv3Puf2mZmZLFmyhGnTpvHEE0+4llf2M9wQfPLJJ5hMJoYOHVrXpYiIuJWu6RYRaWACAwN5++23mTp1KldeeWWtvOfKlSs5ePAgEyZM4LrrrqvwuOGGG1i6dCnHjh07Y4bFYsFkMmG3213LDh48yNdff31eNVksFoYPH87XX39NcnKya/nOnTtZsGDBeWWeydKlSys9k1g2hL2yIdpnM3PmTPr168cNN9xQ4bN8+OGHAcrdpf63CgoKWL16daXr5s+fX66mgIAAgAp34u7ZsydRUVG888475Yb6z58/n507dzJq1CjA+ceN/v378+GHH5b7nKHys6smk4n33nuP6667jvHjx5/3Ne+/15gxYzAMg2nTplVYV1b3tddei8ViYdq0aRWOxTCMCtN4/dZf//pX/P39uf322yudEu23mWV/YPnt8ldfffWcx3Ohef7551m4cCE33HDDGYfvi4g0FDrTLSLSAI0fP77K2+7Zs8c1NPx00dHRVT4DNXPmTCwWi6sR+62rrrqKv//978yePZvJkydXus2oUaN4+eWXueKKKxg7dizp6em8+eabtGrVii1btlT5eE43bdo0vv/+e/r168c999xDaWkpb7zxBh07djzvzMrcd999FBQUcM0119CuXTtKSkpYtWoVn3/+OQkJCRXO8CclJfHMM89UyOnWrRuRkZEkJSVx7733VvpeTZo0oXv37sycOZNHHnmk0m0KCgq45JJL6NOnD1dccQVxcXFkZWXx9ddfs2LFCkaPHk23bt0A59nc0NBQ3nnnHYKCgggICODiiy+mefPmvPDCC0yYMIEBAwZw0003kZaWxmuvvUZCQkK5Swhef/11LrvsMrp3785dd91F8+bNOXjwIN999x2bNm2qUJ/ZbObTTz9l9OjRXH/99SQmJlY4m+5ugwYN4tZbb+X1119n7969ruHbK1asYNCgQdx77720bNmSZ555hilTpnDw4EFGjx5NUFAQBw4cYO7cudx111385S9/OeN7tG7dmlmzZnHTTTfRtm1bbr75Zi666CIMw+DAgQPMmjULs9nsugY9ODiY/v378+KLL2Kz2WjSpAkLFy50zUN+ISotLXX996WoqIhDhw7xzTffsGXLFgYNGsR7771XxxWKiNSCurhluoiI1JzTpww7m+pOGXam6bF+O3VWSUmJERERYfTr1++s79+8eXOjW7duZ93mgw8+MFq3bm34+PgY7dq1Mz766CPXVFO/rXvSpEmVHuP48ePLLVu+fLnRo0cPw9vb22jRooXxzjvvVJp5LmebMmz+/PnG7bffbrRr184IDAw0vL29jVatWhn33XefkZaWVqHGM33md9xxh3HfffcZgLFv374z1jJ16lQDMDZv3lzpepvNZrz//vvG6NGjjfj4eMPHx8fw9/c3unXrZvzjH/8wiouLy23/v//9z+jQoYPh5eVVYfqwzz//3OjWrZvh4+NjhIeHGzfffLNriq7Tbdu2zbjmmmuM0NBQw9fX12jbtq3x+OOPu9afPmVYmYKCAmPAgAFGYGCgsWbNmjMeb1V/xs82RdVvpwwzDMMoLS01/vGPfxjt2rUzvL29jUaNGhkjRoyoMLXaV199ZVx22WVGQECAERAQYLRr186YNGmSsXv37rPWUyYpKcmYOHGi0apVK8PX19fw8/Mz2rVrZ9x9993Gpk2bym175MgR1+cYEhJi/PGPfzSOHTtWYWquM00ZVtl0dAMGDDjjz25lqvp5V2XKsNN/vv39/Y2EhARjzJgxxpdffmnY7fYq1yQiciEzGUY9vLOGiIiIiIiISAOga7pFRERERERE3ERNt4iIiIiIiIibqOkWERERERERcRM13SIiIiIiIiJuoqZbRERERERExE3UdIuIiIiIiIi4iVddF1AfOBwOjh07RlBQECaTqa7LERERERERkXrOMAxyc3Np3LgxZvOZz2er6QaOHTtGXFxcXZchIiIiIiIiF5jDhw/TtGnTM65X0w0EBQUBzg8rODi4jqupnM1mY+HChQwbNgyr1Vrv8tyR6Wl57sis73nuyPS0PHdk1vc8d2R6Wp47Mj0tzx2Z9T3PHZmelueOTE/Lc0dmfc9zR6an5blLTk4OcXFxrn7yTNR0g2tIeXBwcL1uuv39/QkODq6xH+SazHNHpqfluSOzvue5I9PT8tyRWd/z3JHpaXnuyPS0PHdk1vc8d2R6Wp47Mj0tzx2Z9T3PHZmeludu57pEWTdSExEREREREXETNd0iIiIiIiIibqKmW0RERERERMRNdE13Fdntdmw2W529v81mw8vLi6KiIux2e73Lq26m1WrFYrHUyPuKiIiIiIjUV2q6z8EwDFJTU8nKyqrzOmJiYjh8+HCNzCVe03nnkxkaGkpMTIzmRhcRERERkQZLTfc5lDXcUVFR+Pv711mD6HA4yMvLIzAw8KwTr9dVXnUyDcOgoKCA9PR0AGJjY2vk/UVEREREROobNd1nYbfbXQ13REREndbicDgoKSnB19e3xprumsyrbqafnx8A6enpREVFaai5iIiIiIg0SLqR2lmUXcPt7+9fx5U0TGWfa11eKy8iIiIiIuJOarqrQNccu4c+VxERERERaejUdIuIiIiIiIi4iZpuERERERERETep06Z76tSpmEymco927dq51hcVFTFp0iQiIiIIDAxkzJgxpKWllctITk5m1KhR+Pv7ExUVxcMPP0xpaWltH0q9c9ttt2Eymbj77rsrrJs0aRIWi4V77rmn3La/fVxxxRUsW7as0nWnP5YtWwbAkSNHiIqKokuXLrV5qCIiIiIi0sA4jLqu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zy0iIuIJ1HSLiIhIrdH13CIi4mnUdIuIiEityC60sf2YrucWERHPoqZbREREasXaAxk4DGgRGUB0sG9dlyMiIlIr1HSLiIhIrSgbWt5HZ7lFRMSDqOkWERGRWqGbqImIiCdS0y0iIiJul5lfws6UHAD6qOkWEREPoqZbRERE3O7nA86z3K2jAmkU5FPH1YiIiNQeNd0iIiLidpoqTEREPJWabhEREXE7Xc8tIiKeSk23iIiIuNWJvGL2pOUBcLGabhER8TBqukVERMSt1pw6y90uJojwAO86rkZERKR2qekWERERt9L13CIi4snUdIuIiIhb6XpuERHxZGq6RURExG3ScorYfzwfkwkubq6mW0REPI+abhEREXGbsuu5OzYOJsTfWsfViIiI1D413SIiIuI2ruu5NbRcREQ8lJpuERERcRvX9dy6iZqIiHgoNd0iIiLiFseyCjl0sgCL2USvhPC6LkdERKROqOkWERERtygbWt6pSQhBvrqeW0REPJOabhEREXELTRUmIiKipltERETcxHUTNV3PLSIiHkxNt4iIiNS4wxkFHM0qxMtsomd8WF2XIyIiUmfUdIuIiEiNKzvLfVFcKAE+XnVcjYiISN1R0y0iIiI1Ttdzi4iIOKnpFhERkRplGIau5xYRETlFTbeIiIjUqIMnC0jNKcLbYqaHrucWEREPp6ZbREREalTZWe6uzULxtVrquBoREZG6paZbREREapSu5xYREfmVmm4RERGpMbqeW0REpDw13SIiIlJj9h3P50ReMT5eZro1C63rckREROqcmm4RERGpMT8fyACgR3wYPl66nltERERNt4iIiNSYNQcyAV3PLSIiUqbeNN3PP/88JpOJBx54wLWsqKiISZMmERERQWBgIGPGjCEtLa3cfsnJyYwaNQp/f3+ioqJ4+OGHKS0treXqRURExGH8eqZb13OLiIg41Yume926dbz77rt06dKl3PIHH3yQb7/9li+++ILly5dz7Ngxrr32Wtd6u93OqFGjKCkpYdWqVXz88cfMmDGDJ554orYPQURExOOlFkBmgQ0/q4UuTUPruhwREZF6wauuC8jLy+Pmm2/m/fff55lnnnEtz87O5oMPPmDWrFlcfvnlAHz00Ue0b9+eNWvW0KdPHxYuXMiOHTtYvHgx0dHRdO3alaeffppHHnmEqVOn4u3tXVeHJSIi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MJtNRAT6EBHoAwQBzjsCJmZtZ2T3JhoKLiIiIiIi4iZVarodDkelz0VERERERETkzM58O+tK2Gw2Bg8ezN69e91Vj4iIiIiIiEiDUa2m22q1smXLFnfVIiIiIiIiItKgVKvpBrjlllvOetdxEREREREREXGq9pRhpaWlfPjhhyxevJgePXoQEBBQbv3LL79cY8WJiIiIiIiIXMiq3XRv27aN7t27A7Bnz55y6842h7OIiIiIiIiIp6l207106VJ31CEiIiIiIiLS4FSr6f7888/55ptvKCkpYfDgwdx9993uqktERERERETkglflpvvtt99m0qRJtG7dGj8/P+bMmcO+ffv4xz/+4c76RERERERERC5YVb57+b/+9S+efPJJdu/ezaZNm/j4449566233FmbiIiIiIiIyAWtyk33/v37GT9+vOv12LFjKS0tJSUlxS2FiYiIiIiIiFzoqtx0FxcXl5sezGw24+3tTWFhoVsKExEREREREbnQVetGao8//jj+/v6u1yUlJUyfPp2QkBDXMs3TLSIiIiIiIuJU5aa7f//+7N69u9yySy65hP3797tea55uERERERERkV9VueletmyZG8sQERERERERaXiqfE23iIiIiIiIiFSPmm4RERERERERN1HTLSIiIiIiIuImarpFRERERERE3KTaTbfNZjvjuhMnTvyuYkREREREREQakmo33TfeeCOGYVRYnpaWxsCBA2uiJhEREREREZEGodpNd3JyMv/3f/9XbllqaioDBw6kXbt2NVaYiIiIiIiIyIWu2k13YmIiq1atYvLkyQAcO3aMAQMG0LlzZ/773//WeIEiIiIiIiIiFyqv6u7QqFEjFi5cyGWXXQbAvHnz6N69OzNnzsRs1n3ZRERERERERMpUu+kGiIuLY9GiRfTr14+hQ4fyySefYDKZaro2ERERERERkQtalZrusLCwSpvqgoICvv32WyIiIlzLMjIyaq46ERERERERkQtYlZruV1991c1liIiIiIiIiDQ8VWq6x48f7+46RERERERERBqc87p7+YIFCyosX7hwIfPnz6+RokREREREREQagmo33Y8++ih2u73CcofDwaOPPlojRYmIiIiIiIg0BNVuuvfu3UuHDh0qLG/Xrh1JSUk1UpSIiIiIiIhIQ1DtpjskJIT9+/dXWJ6UlERAQECNFCUiIiIiIiLSEFS76b766qt54IEH2Ldvn2tZUlISDz30EFdddVWNFiciIiIiIiJyIat20/3iiy8SEBBAu3btaN68Oc2bN6d9+/ZERETw0ksvuaNGERERERERkQtSlaYMO11ISAirVq1i0aJFbN68GT8/P7p06UL//v3dUZ+IiIiIiIjIBavaTTeAyWRi2LBhDBs2rKbrEREREREREWkwqj28HGD58uVceeWVtGrVilatWnHVVVexYsWKmq5NRERERERE5IJW7ab7008/ZciQIfj7+3P//fdz//334+fnx+DBg5k1a1a1st5++226dOlCcHAwwcHB9O3bl/nz57vWFxUVMWnSJCIiIggMDGTMmDGkpaWVy0hOTmbUqFH4+/sTFRXFww8/TGlpaXUPS0RERERERKTGVXt4+fTp03nxxRd58MEHXcvuv/9+Xn75ZZ5++mnGjh1b5aymTZvy/PPP07p1awzD4OOPP+bqq69m48aNdOzYkQcffJDvvvuOL774gpCQEO69916uvfZaVq5cCYDdbmfUqFHExMSwatUqUlJSGDduHFarlWeffba6hyYiIiIiIiJSo6p9pnv//v1ceeWVFZZfddVVHDhwoFpZV155JSNHjqR169a0adOG6dOnExgYyJo1a8jOzuaDDz7g5Zdf5vLLL6dHjx589NFHrFq1ijVr1gCwcOFCduzYwaeffkrXrl0ZMWIETz/9NG+++SYlJSXVPTQRERERERGRGlXtpjsuLo4lS5ZUWL548WLi4uLOuxC73c7s2bPJz8+nb9++bNiwAZvNxpAhQ1zbtGvXjmbNmrF69WoAVq9eTefOnYmOjnZtM3z4cHJycti+fft51yIiIiIiIiJSE6o9vPyhhx7i/vvvZ9OmTVxyySUArFy5khkzZvDaa69Vu4CtW7fSt29fioqKCAwMZO7cuXTo0IFNmzbh7e1NaGhoue2jo6NJTU0FIDU1tVzDXba+bN2ZFBcXU1xc7Hqdk5MDgM1mw2azVfsYakNZXTVVX03nuSPT0/LckVnf89yR6Wl57sis73nuyPS0PHdkelqeOzLre547Mj0tzx2Znpbnjsz6nueOTE/Lc5eq1mcyDMOobvjcuXP55z//yc6dOwFo3749Dz/8MFdffXV1oygpKSE5OZns7Gy+/PJL/v3vf7N8+XI2bdrEhAkTyjXHAL1792bQoEG88MIL3HXXXRw6dIgFCxa41hcUFBAQEEBiYiIjRoyo9D2nTp3KtGnTKiyfNWsW/v7+1T4GERERERER8SwFBQWMHTuW7OxsgoODz7jdec3Tfc0113DNNdecd3Gn8/b2plWrVgD06NGDdevW8dprr3HDDTdQUlJCVlZWubPdaWlpxMTEABATE8PatWvL5ZXd3bxsm8pMmTKFyZMnu17n5OQQFxfHsGHDzvph1SWbzcaiRYsYOnQoVqu13uW5I9PT8tyRWd/z3JHpaXnuyKzvee7I9LQ8d2R6Wp47Mut7njsyPS3PHZmelueOzPqe545MT8tzl7IR0+dS7aa7RYsWrFu3joiIiHLLs7Ky6N69O/v3769uZDkOh4Pi4mJ69OiB1WplyZIljBkzBoDdu3eTnJxM3759Aejbty/Tp08nPT2dqKgoABYtWkRwcDAdOnQ443v4+Pjg4+NTYbnVaq3X31So+Rrdccz1vcb6nueOzPqe545MT8tzR2Z9z3NHpqfluSPT0/LckVnf89yR6Wl57sj0tDx3ZNb3PHdkelpeTatqbdVuug8ePIjdbq+wvLi4mKNHj1Yra8qUKYwYMYJmzZqRm5vLrFmzWLZsGQsWLCAkJIQ77riDyZMnEx4eTnBwMPfddx99+/alT58+AAwbNowOHTpw66238uKLL5Kamspjjz3GpEmTKm2qRURERERERGpTlZvub775xvW8rCkuY7fbWbJkCQkJCdV68/T0dMaNG0dKSgohISF06dKFBQsWMHToUABeeeUVzGYzY8aMobi4mOHDh/PWW2+59rdYLMybN4+JEyfSt29fAgICGD9+PE899VS16hARERERERFxhyo33aNHjwbAZDIxfvz4cuusVisJCQn885//rNabf/DBB2dd7+vry5tvvsmbb755xm3i4+NJTEys1vuKiIiIiIiI1IYqN90OhwOA5s2bs27dOiIjI91WlIiIiIiIiEhDUO1rug8cOOCOOkREREREREQaHHNVN1y9ejXz5s0rt+w///kPzZs3JyoqirvuuqvCnNoiIiIiIiIinqzKTfdTTz3F9u3bXa+3bt3KHXfcwZAhQ3j00Uf59ttvee6559xSpIiIiIiIiMiFqMpN96ZNmxg8eLDr9ezZs7n44ot5//33mTx5Mq+//jr//e9/3VKkiIiIiIiIyIWoyk13ZmYm0dHRrtfLly9nxIgRrte9evXi8OHDNVudiIiIiIiIyAWsyk13dHS06yZqJSUl/PLLL/Tp08e1Pjc3F6vVWvMVioiIiIiIiFygqtx0jxw5kkcffZQVK1YwZcoU/P396devn2v9li1baNmypVuKFBEREREREbkQVXnKsKeffpprr72WAQMGEBgYyMcff4y3t7dr/YcffsiwYcPcUqSIiIiIiIjIhajKTXdkZCQ//vgj2dnZBAYGYrFYyq3/4osvCAwMrPECRURERERERC5UVW66y4SEhFS6PDw8/HcXIyIiIiIiItKQVPmabhERERERERGpHjXdIiIiIiIiIm6ipltERERERETETdR0i4iIiIiIiLiJmm4RERERERERN1HTLSIiIiIiIuImarpFRERERERE3ERNt4iIiIiIiIibqOkWERERERERcRM13SIiIiIiIiJuoqZbRERERERExE3UdIuIiIiIiIi4iZpuERERERERETdR0y0iIiIiIiLiJmq6RURERERERNxETbeIiIiIiIiIm6jpFhEREREREXETNd0iIiIiIiIibqKmW0RERERERMRN1HSLiIiIiIiIuImabhERERERERE3UdMtIiIiIiIi4iZqukVERERERETcRE23iIiIiIiIiJuo6RYRERERERFxEzXdIiIiIiIiIm6ipltERERERETETdR0i4iIiIiIiLhJnTbdzz33HL169SIoKIioqChGjx7N7t27y20zcOBATCZTucfdd99dbpvk5GRGjRqFv78/UVFRPPzww5SWltbmoYiIiIiIiIhU4FWXb758+XImTZpEr169KC0t5W9/+xvDhg1jx44dBAQEuLa78847eeqpp1yv/f39Xc/tdjujRo0iJiaGVatWkZKSwrhx47BarTz77LO1ejwiIiIiIiIip6vTpvv7778v93rGjBlERUWxYcMG+vfv71ru7+9PTExMpRkLFy5kx44dLF68mOjoaLp27crTTz/NI488wtSpU/H29nbrMYiIiIiIiIicSb26pjs7OxuA8PDwcstnzpxJZGQknTp1YsqUKRQUFLjWrV69ms6dOxMdHe1aNnz4cHJycti+fXvtFC4iIiIiIiJSiTo90306h8PBAw88wKWXXkqnTp1cy8eOHUt8fDyNGzdmy5YtPPLII+zevZs5c+YAkJqaWq7hBlyvU1NTK32v4uJiiouLXa9zcnIAsNls2Gy2Gj2u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//79HY4m7WybrT+XLVtGXbp0sY0f4YxvzJgxtjYTyUVUt27d6OLFi3YZu3Xr5nBnriOftc2XLl2iuLg423eYSP6u9OvXj95//30iune5Pnv2LL344ov37DC6G1f6L2ecrvRfJfksFku5tm8c+YofkXe1/3Lks67nzp8/X67+68SJEzRw4EBq164dtWvXjt59913ba7du3Ure3t40bdo0u/FwHnvsMZo8ebLDcVLu9r333nuUk5Nj9xpX+gdnfETO9w2uOIszY8YMeuqppxwePChtHiYkJLjcPziTz5W+wVG+4judjhw54lLfUJbzypUrFB8f71L/oMR6paJw0V3FSUlJoUaNGtELL7xAZ86coS1bttDEiRNJr9fbTl+1LqiHDh2izp0708KFC8nb29t2qoWrvuLk5eXRkiVLqE6dOnYDaDjjsw5O8uWXX1L37t3vyVPS3sbSnNYBKBxlrF+/vsM9cM60OTU1lbp06UIDBgygMWPG0FNPPUW1atWy6yDK8ul0Ovriiy/uef2VK1doyZIlVLt2bYcbAhcuXKDQ0FCqWbMmPfroo7aCt3hBdubMGRozZgy1bt2agoODqV+/fhQYGOgwX2lOR1g7rzlz5lBgYKDDz9kVX2JiIkVERNC0adNKXA5La7N1pX316lV66aWXKCQkhPz9/alTp05Uv359h+/tSj4i+UhPaGgoPf/88yUePXDGmZOTQxMnTqTu3btTz549acKECVS7dm2Hn0tpbXaU4ezZs7R48WIKDAy0rWiKc+rUKapTpw4tXLiQ9u7dS6+99hp16dLFdolE8RWYM32Dq06isvuHsnxERF999RX16NHDqf6hJN+3337rsD1l9Q3O+C5cuEDjx4+nRo0aUd26dal3794UFBRU4jLmasay+oeTJ09S7dq16aWXXqLExETavHkztWzZ0m6j4ujRoxQREUGdOnWidu3a0ciRI8nf3/+e085L8xVvj3V5tH4XZ8+eTbVq1XL4GV++fJnq169PCxYsoIMHD9J7771HgwcPppo1a9p2vhbfuUckD5YYFBRkVwC66rRubG3dupX+8pe/0PLly8nb29vhxlZJvoCAAPrf//5n12Zr3qVLl1JwcLDDz6Q0359//klE8sjCnTt3ptatW1PLli1p2LBhVLNmzRL7bFfmI5G8USxJEn399dcu+QICAmy+ixcvUu3atWnEiBE0b948mjVrFgUGBjpcbkrzFR/Mz8rNmzdpyZIlVK9ePYenMVtvDenv70/PPfdcqSPGO9t/ueIkKrv/Ks1n7Qtd6b/Km6+k/qskX/H1qKv918mTJ6lOnTo0b9482rJlC02fPp3CwsLsLunavXs3BQUFUe/eval///40adIkql69OsXFxTntc1T8OtM/OOOzfjbO9A3OOO9e7xmNRlq6dCnVq1fP4WUOzszDEydOUJcuXahVq1Zl9g+uzEOisvsGZ3zJyclO9w0lOdu0aVPipYBl9Q9KrFfcARfdVZzo6Ghq166d3WlA+fn59Pzzz5OXlxf99NNPtukHDhwgSZKoVq1aJXYuzvrMZjNt3bqVnnjiCWrUqFGJHXRpPr1eb7sWufiesbJO/XUl45YtW2jSpEnUoEGDcmX08vKyHe1MSEigZ555hoYNG0azZs0q8ZoxV/JdvHiRli5dSg0bNnSYz3pd5tixY2n9+vU0YMAAGjFihMPCOz09nY4fP06vv/46bdiwgc6dO+cwX1lOR/znP/8hSZLKlfFuzp49S5IkUUBAQLnbbO1M8/PzKTU1lTZs2EA7d+50eMp7edpLRPTFF1843NBy1ln8GvZ33nmHJk2aRIsXL3a4ceRqxrS0NFq5ciU1atTI4Uo4OzubRo0aRTNnzrSbPnjwYIejVjvTN7jq3LZtW6n9g7M+o9F4zyUp7shXVt/gjM/6GWdkZFBcXBy9+eab9OWXX5Z4LaSrGcvqHzIzM+nBBx+85/S7AQMG0BdffEHbtm2zFYWJiYn0ww8/0Pz58+nvf/+7w+WwLN+PP/5oO7Jhbfv69etL7BuI5A3wyMhIu8uezp8/T5MmTaJq1arZ/s/qe/vtt0v1ueIkkpdDSZKodu3aJRZjzvosFgtt3ryZRo8eXeIOvrJ8vr6+to3X5ORk2r59Oy1atIj+8Y9/2B0ZLG9Gk8lk+44sXLiwxPVUWRmtR4qPHTtGQ4YMoT59+tCkSZNK7BPL8hX/nOPj42nRokVUt25dh/MwNzeXJk+eTJMmTaIVK1ZQREQEzZ07t8Qi1Jn+y1VnWf2XMz7r+tmZ/svVfGX1X2X5iq9Hne2/MjIy6KGHHqI5c+bYTe/UqZPteu3i43GsW7eOpkyZQi+88ILDQscZ392U1j+46nOmb3DV+fXXX9PEiRMpODi43BmLH1iIiooqtX8ozzwkKrlvcOUztt7Joqy+wdWMJ06cKLV/IFJmveIOuOiu4uzZs4ckSbpnQ8hisdAzzzxD/v7+to2uy5cvU/fu3Uvdy+OK7+rVq/Tuu++Wuje2NN+cOXPsfEq1efXq1SUWoM74atSoYeucrKcYlnYNlyv5CgoKKDo62m6Qj7v56quv6OOPPyYiou+++85hEerqYF9lOR1R2vVdrvjS0tJo3LhxDo/OuuJzpc2u5HP2NmHOOO++9q60zK5kNBqNlJycbDegTnGs90i17tSyHvVbv349jRgxgojsN/6Sk5PL7BtcdV65cqXU/sFVX1mUp82rVq0qsW9wxlf89jNKZMzPz6fjx4+X2D/k5OTQhg0b7E6lfe2110ij0VB4eDh16tSJdDpdiUc8yuPT6/V2G6jp6emlDrj03XffkVarvecynKtXr9LYsWOpefPmdrfQO378eJnrBFec586do5CQEIdH2crjS0xMpPnz55d63aIzPlduielqRiulraec8VnXYdZLlEobEMqVfDk5OfTLL7+UOA+sg1Fu3LiRiIg++OCDUotQZ/ovV51XrlyhdevWldh/ueorq58oT5tL67+c9bmyHo2Pj6exY8faboVl3R5atGgRTZ061dbOu50lvYczvrs9x44dK7F/cNV39uxZCg4OLrVvcNWZmJhIL730Uok70Jydh67cqs2VfFZvSX2Ds/ms2zTWU91L6xtczZiTk0M7d+4stY9UYr3iDrjoruKYTCZ68MEHacKECbY9PtYO7sqVK/Tggw/Sq6++anf9nTt8dw/cUhHfihUrnHKVN2NZKxVnfWaz2al2O9tmVwtlK998842tILMe5SwsLLRdtyqCsySf9TpTV0dUv1/5lJiHJRXGSmcsfv2udXndsGED9evXz26a9ToyZ0ZYddZpPbJT1jLurK+sa+fKm6+sHSzO+rKzs53K54qz+AA1pVH8+r7//ve/VLt2bfrhhx8oIyOD0tPTacSIETRgwADKz893qs9xty8lJYW6du1KixcvvudzPHDgAHXu3NnhZTfudJa1bLvqK2u5ccXn7HrA3c7y+Epb77n7c777ntvvvfeerWi07uQxGAy2jW5n+i9nndbxC8qaj874ioqK7hnN2135yloOnfU5m89isdhdBmOdP2vWrKHx48fbvbaksWRE8Vn77LtHu66I07rcl/a5uOIr6RJLUXwljT1x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", "text/plain": [ - "
" + "
" ] }, "metadata": {}, @@ -941,133 +982,31 @@ "name": "stdout", "output_type": "stream", "text": [ - "recipitent sending ipython_user_proxy To plot a chart of META (Facebook's parent company, Meta Platforms, Inc.) and TESLA (Tesla, Inc.) stock price gain year-to-date (YTD), we can use Python with libraries such as `pandas` for data manipulation and `matplotlib` or `plotly` for plotting. We will also use `yfinance` to fetch historical stock data.\n", - "\n", - "Here's the plan:\n", - "1. Install the `yfinance` library if it's not already installed.\n", - "2. Fetch the YTD stock price data for META and TESLA.\n", - "3. Calculate the YTD gain for each stock.\n", - "4. Plot the YTD gain on a chart.\n", - "\n", - "First, let's install `yfinance` and import the necessary libraries. Execute the following code:\n", - "\n", - "```python\n", - "# Install yfinance if not already installed\n", - "!pip install yfinance\n", - "\n", - "import yfinance as yf\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "from datetime import datetime\n", - "\n", - "# Check if today's date is required or the last trading day\n", - "today = datetime.today().strftime('%Y-%m-%d')\n", - "\n", - "# Fetch YTD stock data for META and TESLA\n", - "meta_data = yf.download('META', start='2023-01-01', end=today)\n", - "tesla_data = yf.download('TSLA', start='2023-01-01', end=today)\n", - "\n", - "# Calculate the YTD gain for each stock\n", - "meta_ytd_gain = (meta_data['Close'] - meta_data['Close'].iloc[0]) / meta_data['Close'].iloc[0] * 100\n", - "tesla_ytd_gain = (tesla_data['Close'] - tesla_data['Close'].iloc[0]) / tesla_data['Close'].iloc[0] * 100\n", - "\n", - "# Plot the YTD gain on a chart\n", - "plt.figure(figsize=(14, 7))\n", - "plt.plot(meta_ytd_gain.index, meta_ytd_gain, label='META YTD Gain %')\n", - "plt.plot(tesla_ytd_gain.index, tesla_ytd_gain, label='TESLA YTD Gain %')\n", - "plt.title('META vs TESLA Stock Price Gain YTD')\n", - "plt.xlabel('Date')\n", - "plt.ylabel('Gain %')\n", - "plt.legend()\n", - "plt.grid(True)\n", - "plt.show()\n", - "```\n", - "\n", - "This code will install `yfinance`, fetch the YTD stock data for META and TESLA, calculate the YTD gain, and plot it on a chart. Please execute the code in your IPython environment. None\n", "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", "\n", "exitcode: 0 (execution succeeded)\n", "Code output: \n", - "Requirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\n", - "Requirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\n", - "Requirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\n", - "Requirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\n", - "Requirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\n", - "Requirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\n", - "Requirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\n", - "Requirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\n", - "Requirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\n", - "Requirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\n", - "Requirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\n", - "Requirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\n", - "Requirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\n", - "Requirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\n", - "Requirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\n", - "/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\n", - " df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\n", - " df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\n", - "[*********************100%%**********************] 1 of 1 completed\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n", - "recipitent sending assistant exitcode: 0 (execution succeeded)\n", - "Code output: \n", - "Requirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\n", - "Requirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\n", - "Requirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\n", - "Requirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\n", - "Requirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\n", - "Requirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\n", - "Requirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\n", - "Requirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\n", - "Requirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\n", - "Requirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\n", - "Requirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\n", - "Requirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\n", - "Requirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\n", - "Requirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\n", - "Requirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\n", - "/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\n", - " df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\n", - " df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\n", - "[*********************100%%**********************] 1 of 1 completed\n", - " None\n", "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", "\n", - "The code has executed successfully, and the `yfinance` library has been used to fetch the YTD stock data for META and TESLA. The YTD gain for each stock has been calculated, and a chart should have been plotted to visualize the stock price gains.\n", + "It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\n", "\n", - "Since the code has been executed without any errors and the chart should have been displayed in your IPython environment, this completes the task.\n", + "If you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\n", "\n", "TERMINATE\n", "\n", - "--------------------------------------------------------------------------------\n", - "self.reply_at_receive[sender]self.reply_at_receive[sender] True\n" + "--------------------------------------------------------------------------------\n" ] }, { "data": { "text/plain": [ - "ChatResult(chat_history=[{'content': 'Plot a chart of META and TESLA stock price gain YTD\\nIf you suggest code, the code will be executed in IPython.', 'role': 'assistant'}, {'content': \"To plot a chart of META (Facebook's parent company, Meta Platforms, Inc.) and TESLA (Tesla, Inc.) stock price gain year-to-date (YTD), we can use Python with libraries such as `pandas` for data manipulation and `matplotlib` or `plotly` for plotting. We will also use `yfinance` to fetch historical stock data.\\n\\nHere's the plan:\\n1. Install the `yfinance` library if it's not already installed.\\n2. Fetch the YTD stock price data for META and TESLA.\\n3. Calculate the YTD gain for each stock.\\n4. Plot the YTD gain on a chart.\\n\\nFirst, let's install `yfinance` and import the necessary libraries. Execute the following code:\\n\\n```python\\n# Install yfinance if not already installed\\n!pip install yfinance\\n\\nimport yfinance as yf\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\nfrom datetime import datetime\\n\\n# Check if today's date is required or the last trading day\\ntoday = datetime.today().strftime('%Y-%m-%d')\\n\\n# Fetch YTD stock data for META and TESLA\\nmeta_data = yf.download('META', start='2023-01-01', end=today)\\ntesla_data = yf.download('TSLA', start='2023-01-01', end=today)\\n\\n# Calculate the YTD gain for each stock\\nmeta_ytd_gain = (meta_data['Close'] - meta_data['Close'].iloc[0]) / meta_data['Close'].iloc[0] * 100\\ntesla_ytd_gain = (tesla_data['Close'] - tesla_data['Close'].iloc[0]) / tesla_data['Close'].iloc[0] * 100\\n\\n# Plot the YTD gain on a chart\\nplt.figure(figsize=(14, 7))\\nplt.plot(meta_ytd_gain.index, meta_ytd_gain, label='META YTD Gain %')\\nplt.plot(tesla_ytd_gain.index, tesla_ytd_gain, label='TESLA YTD Gain %')\\nplt.title('META vs TESLA Stock Price Gain YTD')\\nplt.xlabel('Date')\\nplt.ylabel('Gain %')\\nplt.legend()\\nplt.grid(True)\\nplt.show()\\n```\\n\\nThis code will install `yfinance`, fetch the YTD stock data for META and TESLA, calculate the YTD gain, and plot it on a chart. Please execute the code in your IPython environment.\", 'role': 'user'}, {'content': \"exitcode: 0 (execution succeeded)\\nCode output: \\nRequirement already satisfied: yfinance in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (0.2.36)\\r\\nRequirement already satisfied: pandas>=1.3.0 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.2.0)\\r\\nRequirement already satisfied: numpy>=1.16.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.26.4)\\r\\nRequirement already satisfied: requests>=2.31 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.31.0)\\r\\nRequirement already satisfied: multitasking>=0.0.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (0.0.11)\\r\\nRequirement already satisfied: lxml>=4.9.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (5.1.0)\\r\\nRequirement already satisfied: appdirs>=1.4.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.4.4)\\r\\nRequirement already satisfied: pytz>=2022.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2024.1)\\r\\nRequirement already satisfied: frozendict>=2.3.4 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (2.4.0)\\r\\nRequirement already satisfied: peewee>=3.16.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (3.17.1)\\r\\nRequirement already satisfied: beautifulsoup4>=4.11.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (4.12.3)\\r\\nRequirement already satisfied: html5lib>=1.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from yfinance) (1.1)\\r\\nRequirement already satisfied: soupsieve>1.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\\r\\nRequirement already satisfied: six>=1.9 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\\r\\nRequirement already satisfied: webencodings in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\\r\\nRequirement already satisfied: python-dateutil>=2.8.2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\\r\\nRequirement already satisfied: tzdata>=2022.7 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from pandas>=1.3.0->yfinance) (2024.1)\\r\\nRequirement already satisfied: charset-normalizer<4,>=2 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.3.2)\\r\\nRequirement already satisfied: idna<4,>=2.5 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (3.6)\\r\\nRequirement already satisfied: urllib3<3,>=1.21.1 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (1.26.18)\\r\\nRequirement already satisfied: certifi>=2017.4.17 in /Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages (from requests>=2.31->yfinance) (2024.2.2)\\r\\n/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\\n df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\\n\\r[*********************100%%**********************] 1 of 1 completed\\n/Users/qingyunwu/miniconda3/envs/ag/lib/python3.10/site-packages/yfinance/utils.py:775: FutureWarning: The 'unit' keyword in TimedeltaIndex construction is deprecated and will be removed in a future version. Use pd.to_timedelta instead.\\n df.index += _pd.TimedeltaIndex(dst_error_hours, 'h')\\n\\r[*********************100%%**********************] 1 of 1 completed\\n\", 'role': 'assistant'}, {'content': 'The code has executed successfully, and the `yfinance` library has been used to fetch the YTD stock data for META and TESLA. The YTD gain for each stock has been calculated, and a chart should have been plotted to visualize the stock price gains.\\n\\nSince the code has been executed without any errors and the chart should have been displayed in your IPython environment, this completes the task.\\n\\nTERMINATE', 'role': 'user'}], summary='The code has executed successfully, and the `yfinance` library has been used to fetch the YTD stock data for META and TESLA. The YTD gain for each stock has been calculated, and a chart should have been plotted to visualize the stock price gains.\\n\\nSince the code has been executed without any errors and the chart should have been displayed in your IPython environment, this completes the task.\\n\\n', cost=({'total_cost': 0.6870599999999999, 'gpt-4': {'cost': 0.6870599999999999, 'prompt_tokens': 18324, 'completion_tokens': 2289, 'total_tokens': 20613}}, {'total_cost': 0.6154499999999999, 'gpt-4': {'cost': 0.6154499999999999, 'prompt_tokens': 17333, 'completion_tokens': 1591, 'total_tokens': 18924}}), human_input=[])" + "ChatResult(chat_id=None, chat_history=[{'content': 'Plot a chart of META and TESLA stock price gain YTDI have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.If you suggest code, the code will be executed in IPython.', 'role': 'assistant'}, {'content': 'To plot a chart of META and TESLA stock price gain YTD using the data from `stock_price_ytd.csv`, we will follow these steps:\\n\\n1. Read the CSV file into a DataFrame using pandas.\\n2. Plot the data using matplotlib.\\n\\nHere is the Python code to execute in IPython:\\n\\n```python\\n# filename: plot_stock_price_ytd.py\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv(\\'coding/stock_price_ytd.csv\\')\\n\\n# Check if the necessary columns \\'META\\' and \\'TESLA\\' are present\\nif \\'META\\' in df.columns and \\'TESLA\\' in df.columns:\\n # Plot the stock price gain YTD for META and TESLA\\n plt.figure(figsize=(10, 5))\\n plt.plot(df[\\'META\\'], label=\\'META\\')\\n plt.plot(df[\\'TESLA\\'], label=\\'TESLA\\')\\n plt.title(\\'META and TESLA Stock Price Gain YTD\\')\\n plt.xlabel(\\'Date\\')\\n plt.ylabel(\\'Stock Price Gain\\')\\n plt.legend()\\n plt.grid(True)\\n plt.show()\\nelse:\\n print(\"The required columns \\'META\\' and \\'TESLA\\' are not present in the CSV file.\")\\n```\\n\\nPlease execute this code in IPython to generate the plot. If there are any issues with the code, let me know, and I will provide the necessary corrections.', 'role': 'user'}, {'content': \"exitcode: 0 (execution succeeded)\\nCode output: \\nThe required columns 'META' and 'TESLA' are not present in the CSV file.\\n\", 'role': 'assistant'}, {'content': \"It seems that the expected column names 'META' and 'TESLA' are not present in the CSV file. To resolve this, we need to first inspect the CSV file to understand its structure and identify the correct column names. Let's write a Python code snippet to print the first few rows of the CSV file. This will help us determine the correct column names to use for plotting.\\n\\nPlease execute the following code:\\n\\n```python\\n# filename: inspect_csv_columns.py\\nimport pandas as pd\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv('coding/stock_price_ytd.csv')\\n\\n# Print the first few rows of the DataFrame to inspect the column names\\nprint(df.head())\\n```\\n\\nAfter executing this code, please provide the output so that I can suggest the next steps based on the actual column names in the CSV file.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n Date META TSLA\\n0 2024-01-02 346.290009 248.419998\\n1 2024-01-03 344.470001 238.449997\\n2 2024-01-04 347.119995 237.929993\\n3 2024-01-05 351.950012 237.490005\\n4 2024-01-08 358.660004 240.449997\\n', 'role': 'assistant'}, {'content': \"Thank you for providing the output. It appears that the column names for the stock prices are 'META' and 'TSLA', not 'TESLA'. With this information, we can now plot the chart correctly using the actual column names.\\n\\nHere is the updated Python code to plot the chart:\\n\\n```python\\n# filename: plot_stock_price_ytd_corrected.py\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv('coding/stock_price_ytd.csv')\\n\\n# Plot the stock price gain YTD for META and TSLA\\nplt.figure(figsize=(10, 5))\\nplt.plot(df['Date'], df['META'], label='META')\\nplt.plot(df['Date'], df['TSLA'], label='TSLA')\\nplt.title('META and TSLA Stock Price Gain YTD')\\nplt.xlabel('Date')\\nplt.ylabel('Stock Price Gain')\\nplt.xticks(rotation=45) # Rotate the x-axis labels for better readability\\nplt.legend()\\nplt.grid(True)\\nplt.tight_layout() # Adjust layout to prevent clipping of labels\\nplt.show()\\n```\\n\\nPlease execute this code in IPython to generate the plot. If there are any further issues, let me know, and I will address them.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n', 'role': 'assistant'}, {'content': 'It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\\n\\nIf you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\\n\\nTERMINATE', 'role': 'user'}], summary='It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\\n\\nIf you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\\n\\n', cost=({'total_cost': 1.0460099999999999, 'gpt-4': {'cost': 1.0460099999999999, 'prompt_tokens': 21049, 'completion_tokens': 6909, 'total_tokens': 27958}}, {'total_cost': 0.6627899999999999, 'gpt-4': {'cost': 0.6627899999999999, 'prompt_tokens': 13543, 'completion_tokens': 4275, 'total_tokens': 17818}}), human_input=[])" ] }, - "execution_count": 7, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -1086,18 +1025,20 @@ "# the assistant receives a message from the user, which contains the task description\n", "ipy_user.initiate_chat(\n", " assistant,\n", - " message=\"\"\"Plot a chart of META and TESLA stock price gain YTD\"\"\",\n", + " message=my_ipy_message_generator,\n", + " raw_message=\"\"\"Plot a chart of META and TESLA stock price gain YTD\"\"\",\n", + " carryover=\"I have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.\",\n", ")" ] } ], "metadata": { "front_matter": { - "tags": [ - "code generation", - "debugging" - ], - "description": "Use conversable language learning model agents to solve tasks and provide automatic feedback through a comprehensive example of writing, executing, and debugging Python code to compare stock price changes." + "description": "Use conversable language learning model agents to solve tasks and provide automatic feedback through a comprehensive example of writing, executing, and debugging Python code to compare stock price changes.", + "tags": [ + "code generation", + "debugging" + ] }, "kernelspec": { "display_name": "Python 3", diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index 3ba57a728b20..d6a7181f0195 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -78,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -145,7 +145,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -181,55 +181,37 @@ "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", "\n", - "First, we need to consider both sides of the inequality as two separate equations. Then subtract one from the other to set this result equal to zero. This would allow us to find the critical points, i.e., the places where the inequality changes its nature (from less to more or vice versa). Then we find the intervals.\n", + "Key Idea:\n", + "To solve this inequality $(2x + 10)(x + 3) < (3x + 9)(x + 8)$, we'll first expand both sides of the inequality, then collect all terms on one side to form a quadratic inequality. After simplifying, we will factor the quadratic expression if possible. Then, we'll determine the critical points of the inequality by setting the factors to zero. Finally, we'll use a sign chart or test values within intervals determined by the critical points to find the intervals where the original inequality is satisfied.\n", "\n", - "Here's how you can solve this problem in Python:\n", + "Case 1 applies here, so I will write a Python program using sympy that simplifies the inequality, factors the resulting expression, solves for the critical points, and then determines the intervals where the inequality holds true.\n", "\n", "```python\n", - "from sympy import *\n", - "from sympy.abc import x\n", - "\n", - "# define the equation\n", - "equation = (2*x+10)*(x+3) - (3*x+9)*(x+8)\n", - "\n", - "# set the equation equal to zero to find the critical points\n", - "critical_points = solve(equation, x)\n", - "\n", - "# Sort the critical_points\n", - "critical_points = sorted(critical_points)\n", + "from sympy import symbols, solve, simplify\n", "\n", - "# define a function to test the intervals\n", - "def test_intervals(interval):\n", - " test_num = sum(interval)/2 # get the mid point of the interval\n", - " return equation.subs(x, test_num)\n", + "# Define the variable\n", + "x = symbols('x')\n", "\n", - "# define the intervals based on the critical points\n", - "intervals = [(-oo, critical_points[0]), (critical_points[0], critical_points[1]), (critical_points[1], oo)]\n", + "# Given inequality\n", + "lhs = (2*x + 10)*(x + 3)\n", + "rhs = (3*x + 9)*(x + 8)\n", "\n", - "solution = []\n", + "# Move all terms to one side of the inequality\n", + "inequality = simplify(lhs - rhs) < 0\n", "\n", - "# loop through the intervals, if the result is less than zero, it means it's a valid interval\n", - "for i in intervals:\n", - " if test_intervals(i) < 0:\n", - " solution.append(i)\n", + "# Solve the inequality\n", + "solutions = solve(inequality, x)\n", "\n", - "# print the solution in interval notation\n", - "for interval in solution:\n", - " print(interval)\n", + "# Print the solution using interval notation\n", + "print(solutions)\n", "```\n", "\n", - "Replace oo with infinity when interpreting the result. Also, keep in mind that in interval notation, parentheses denote that the endpoint is not included in the set, and brackets denote that the end point is included in the set. Thus, (a, b) means \"greater than a and less than b\", [a, b] means \"greater than or equal to a and less than or equal to b\".\n", + "Running this code will provide us with the solution in interval notation. We'll express the final answer in the requested format afterward.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", "\n", - "Error: Traceback (most recent call last):\n", - " File \"\", line 25, in \n", - " if test_intervals(i) < 0:\n", - " return func(self, other)\n", - " return StrictLessThan(self, other)\n", - " raise TypeError(\"Invalid NaN comparison\")\n", - "TypeError: Invalid NaN comparison\n", + "((-oo < x) & (x < -14)) | ((-3 < x) & (x < oo))\n", "\n", "--------------------------------------------------------------------------------\n" ] @@ -247,296 +229,26 @@ "text": [ "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", "\n", - "I apologize for the oversight. It seems like the function is returning NaN error. Because we are dealing with real numbers, NaN (Not a Number) error could arise from operations that don't return a defined real number. \n", - "\n", - "However, in this case, we it seems there's a problem with the comparison of symbolic terms with zero in the function test_intervals.\n", - "\n", - "Let's correct this approach. The error arises from trying to compare a symbolic expression to zero directly. Instead, we can create a numeric function from our symbolic expression, and use this function to test the intervals. \n", - "\n", - "Here is the revised code:\n", - "\n", - "```python\n", - "from sympy import *\n", - "from sympy.abc import x\n", - "\n", - "# define the equation\n", - "equation = expand((2*x+10)*(x+3) - (3*x+9)*(x+8))\n", - "\n", - "# set the equation equal to zero to find the critical points\n", - "critical_points = solve(equation, x)\n", - "\n", - "# Convert the points to standard python floats, so that they can be sorted numerically\n", - "critical_points = [N(i) for i in critical_points]\n", - "\n", - "# Sort the critical_points\n", - "critical_points = sorted(critical_points)\n", - "\n", - "# define a function to test the intervals\n", - "def test_intervals(interval):\n", - " test_num = N(sum(interval)/2) # get the mid point of the interval\n", - " return equation.subs(x, test_num)\n", - "\n", - "# define the intervals based on the critical points\n", - "intervals = [(-oo, critical_points[0]), (critical_points[0], critical_points[1]), (critical_points[1], oo)]\n", - "\n", - "# Now, let's create a numeric function from this symbolic equation\n", - "numeric_equation = lambdify(x, equation)\n", - "\n", - "solution = []\n", - "\n", - "# loop through the intervals, first find the mid point of each interval, \n", - "# then if numeric result for this mid point is less than zero, it means it's a valid interval\n", - "for interval in intervals:\n", - " mid_point = sum(interval) / 2 \n", - " if numeric_equation(mid_point) < 0:\n", - " solution.append(interval)\n", - "\n", - "# print the solution in interval notation\n", - "for interval in solution:\n", - " print(interval)\n", - "```\n", - "\n", - "Remember to replace `oo` with infinity while interpreting the output.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", - "\n", - "Error: Traceback (most recent call last):\n", - " File \"\", line 36, in \n", - " if numeric_equation(mid_point) < 0:\n", - " return func(self, other)\n", - " return StrictLessThan(self, other)\n", - " raise TypeError(\"Invalid NaN comparison\")\n", - "TypeError: Invalid NaN comparison\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[35m******************************Start compressing the following content:******************************\u001b[0m\n", - "To be compressed:\n", - "##ASSISTANT## First, we need to consider both sides of the inequality as two separate equations. Then subtract one from the other to set this result equal to zero. This would allow us to find the critical points, i.e., the places where the inequality changes its nature (from less to more or vice versa). Then we find the intervals.\n", - "\n", - "Here's how you can solve this problem in Python:\n", - "\n", - "```python\n", - "from sympy import *\n", - "from sympy.abc import x\n", - "\n", - "# define the equation\n", - "equation = (2*x+10)*(x+3) - (3*x+9)*(x+8)\n", - "\n", - "# set the equation equal to zero to find the critical points\n", - "critical_points = solve(equation, x)\n", - "\n", - "# Sort the critical_points\n", - "critical_points = sorted(critical_points)\n", - "\n", - "# define a function to test the intervals\n", - "def test_intervals(interval):\n", - " test_num = sum(interval)/2 # get the mid point of the interval\n", - " return equation.subs(x, test_num)\n", - "\n", - "# define the intervals based on the critical points\n", - "intervals = [(-oo, critical_points[0]), (critical_points[0], critical_points[1]), (critical_points[1], oo)]\n", - "\n", - "solution = []\n", - "\n", - "# loop through the intervals, if the result is less than zero, it means it's a valid interval\n", - "for i in intervals:\n", - " if test_intervals(i) < 0:\n", - " solution.append(i)\n", - "\n", - "# print the solution in interval notation\n", - "for interval in solution:\n", - " print(interval)\n", - "```\n", - "\n", - "Replace oo with infinity when interpreting the result. Also, keep in mind that in interval notation, parentheses denote that the endpoint is not included in the set, and brackets denote that the end point is included in the set. Thus, (a, b) means \"greater than a and less than b\", [a, b] means \"greater than or equal to a and less than or equal to b\".\n", - "##USER## Error: Traceback (most recent call last):\n", - " File \"\", line 25, in \n", - " if test_intervals(i) < 0:\n", - " return func(self, other)\n", - " return StrictLessThan(self, other)\n", - " raise TypeError(\"Invalid NaN comparison\")\n", - "TypeError: Invalid NaN comparison\n", - "\n", - "\u001b[35m******************************Content after compressing:******************************\u001b[0m\n", - "##ASSISTANT##\n", - "The assistant provides instructions to solve an inequality, which involves considering separate equations for the inequality components, finding the differing point (set to zero), identifying critical points (where nature of inequality changes), and determining the intervals. \n", - "\n", - "They also share Python code for the process, using sympy library. Steps include defining the equation, finding critical points by setting equation to zero, sorting these points, defining a function to test the intervals, determining intervals based on critical points, and looping through intervals to identify valid solutions. The assistant defines 'oo' as infinity and clarifies the use of parentheses and brackets to denote exclusion or inclusion of endpoints in interval notation.\n", - "\n", - "##CODE##\n", - "Python script to solve the given inequality and display the solution using interval notation.\n", - "\n", - "##USER##\n", - "User encounters 'TypeError: Invalid NaN comparison', indicating there's an error when comparing a test interval to zero in the Python script shared by the assistant.\n", - " \u001b[35m\n", - "********************************************************************************\u001b[0m\n", - "\u001b[35mToken Count (including 13 tokens from system msg and function descriptions). Before compression : 1294 | After: 1045\u001b[0m\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", - "\n", - "I apologize for the confusion. There's an error with the calculation of mid_point when the interval is from negative infinity to a real number or a real number to infinity. In these circumstances, the calculation will return a NaN which cannot be used in comparison. We could handle these issues specifically.\n", - "\n", - "Let's refine the script:\n", - "\n", - "```python\n", - "from sympy import *\n", - "from sympy.abc import x\n", - "\n", - "# define the equation\n", - "equation = expand((2*x+10)*(x+3) - (3*x+9)*(x+8))\n", - "\n", - "# set the equation equal to zero to find the critical points\n", - "critical_points = solve(equation, x)\n", - "\n", - "# Convert the points to standard python floats, so that they can be sorted numerically\n", - "critical_points = [N(i) for i in critical_points]\n", - "\n", - "# Sort the critical_points\n", - "critical_points = sorted(critical_points)\n", - "\n", - "# Now, let's create a numeric function from this symbolic equation\n", - "numeric_equation = lambdify(x, equation)\n", - "\n", - "solution = []\n", - "\n", - "# Define the intervals based on the critical points\n", - "intervals = [(-oo, critical_points[0]), (critical_points[0], critical_points[1]), (critical_points[1], oo)]\n", - "\n", - "# Loop through the intervals and compute the mid point of each interval\n", - "# If the mid point is NaN (occurs when dealing with infinity), then specific values are used for testing\n", - "for interval in intervals:\n", - " if interval[0] == -oo:\n", - " test_value = interval[1] - 1\n", - " elif interval[1] == oo:\n", - " test_value = interval[0] + 1\n", - " else:\n", - " test_value = sum(interval) / 2\n", - "\n", - " # If numeric result for test_value is less than zero, it's a valid interval\n", - " if numeric_equation(test_value) < 0:\n", - " solution.append(interval)\n", - "\n", - "# Print out the solution intervals\n", - "for interval in solution:\n", - " print(interval)\n", - "```\n", - "\n", - "The script now handles the special cases when dealing with infinity and should return the valid intervals.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mmathproxyagent\u001b[0m (to assistant):\n", - "\n", - "(-oo, -14.0000000000000)\n", - "(-3.00000000000000, oo)\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[35m******************************Start compressing the following content:******************************\u001b[0m\n", - "To be compressed:\n", - "##ASSISTANT##\n", - "The assistant provides instructions to solve an inequality, which involves considering separate equations for the inequality components, finding the differing point (set to zero), identifying critical points (where nature of inequality changes), and determining the intervals. \n", - "\n", - "They also share Python code for the process, using sympy library. Steps include defining the equation, finding critical points by setting equation to zero, sorting these points, defining a function to test the intervals, determining intervals based on critical points, and looping through intervals to identify valid solutions. The assistant defines 'oo' as infinity and clarifies the use of parentheses and brackets to denote exclusion or inclusion of endpoints in interval notation.\n", - "\n", - "##CODE##\n", - "Python script to solve the given inequality and display the solution using interval notation.\n", - "\n", - "##USER##\n", - "User encounters 'TypeError: Invalid NaN comparison', indicating there's an error when comparing a test interval to zero in the Python script shared by the assistant.\n", - "\n", - "##ASSISTANT## I apologize for the oversight. It seems like the function is returning NaN error. Because we are dealing with real numbers, NaN (Not a Number) error could arise from operations that don't return a defined real number. \n", - "\n", - "However, in this case, we it seems there's a problem with the comparison of symbolic terms with zero in the function test_intervals.\n", + "The solution obtained from running the Python code suggests that the values of $x$ that satisfy the inequality $(2x + 10)(x + 3) < (3x + 9)(x + 8)$ are in the intervals $(-\\infty, -14)$ and $(-3, \\infty)$.\n", "\n", - "Let's correct this approach. The error arises from trying to compare a symbolic expression to zero directly. Instead, we can create a numeric function from our symbolic expression, and use this function to test the intervals. \n", + "Therefore, the answer in interval notation is:\n", "\n", - "Here is the revised code:\n", - "\n", - "```python\n", - "from sympy import *\n", - "from sympy.abc import x\n", - "\n", - "# define the equation\n", - "equation = expand((2*x+10)*(x+3) - (3*x+9)*(x+8))\n", - "\n", - "# set the equation equal to zero to find the critical points\n", - "critical_points = solve(equation, x)\n", - "\n", - "# Convert the points to standard python floats, so that they can be sorted numerically\n", - "critical_points = [N(i) for i in critical_points]\n", - "\n", - "# Sort the critical_points\n", - "critical_points = sorted(critical_points)\n", - "\n", - "# define a function to test the intervals\n", - "def test_intervals(interval):\n", - " test_num = N(sum(interval)/2) # get the mid point of the interval\n", - " return equation.subs(x, test_num)\n", - "\n", - "# define the intervals based on the critical points\n", - "intervals = [(-oo, critical_points[0]), (critical_points[0], critical_points[1]), (critical_points[1], oo)]\n", - "\n", - "# Now, let's create a numeric function from this symbolic equation\n", - "numeric_equation = lambdify(x, equation)\n", - "\n", - "solution = []\n", - "\n", - "# loop through the intervals, first find the mid point of each interval, \n", - "# then if numeric result for this mid point is less than zero, it means it's a valid interval\n", - "for interval in intervals:\n", - " mid_point = sum(interval) / 2 \n", - " if numeric_equation(mid_point) < 0:\n", - " solution.append(interval)\n", - "\n", - "# print the solution in interval notation\n", - "for interval in solution:\n", - " print(interval)\n", - "```\n", - "\n", - "Remember to replace `oo` with infinity while interpreting the output.\n", - "##USER## Error: Traceback (most recent call last):\n", - " File \"\", line 36, in \n", - " if numeric_equation(mid_point) < 0:\n", - " return func(self, other)\n", - " return StrictLessThan(self, other)\n", - " raise TypeError(\"Invalid NaN comparison\")\n", - "TypeError: Invalid NaN comparison\n", - "\n", - "\u001b[35m******************************Content after compressing:******************************\u001b[0m\n", - "##ASSISTANT##\n", - "The assistant provides steps to solve an inequality, including considering separate equations, finding the differing point (set to zero), identifying critical points, and determining intervals. They provide Python code using sympy for the process, defining 'oo' as infinity and explaining usage of parentheses and brackets in interval notation. \n", - "\n", - "##CODE##\n", - "The assistant provides Python code for solving an inequality and displaying the solution in interval notation.\n", - "\n", - "##USER##\n", - "The user reports encountering a 'TypeError: Invalid NaN comparison' when comparing a test interval to zero in the script provided by the assistant.\n", - "\n", - "##ASSISTANT##\n", - "The assistant apologizes for the oversight and suggests the issue might lie in the comparison of symbolic terms with zero. The assistant then provides revised code, explaining it creates a numeric function from the symbolic expression to test the intervals. It's highlighted to replace 'oo' with infinity when interpreting the output.\n", - "\n", - "##CODE##\n", - "Revised Python code is given by the assistant to solve a TypeError that occurred in the initial code.\n", - "\n", - "##USER##\n", - "The user reports another TypeError ('Invalid NaN comparison') when executing the revised code provided by the assistant. \u001b[35m\n", - "********************************************************************************\u001b[0m\n", - "\u001b[35mToken Count (including 13 tokens from system msg and function descriptions). Before compression : 1494 | After: 1013\u001b[0m\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33massistant\u001b[0m (to mathproxyagent):\n", - "\n", - "Great! You have got the correct answer.\n", - "\n", - "So, for $x$ in the interval $(-\\infty, -14]$ and $(-3, +\\infty)$, the inequality $(2x+10)(x+3)<(3x+9)(x+8)$ is satisfied. \n", - "\n", - "In interval notation, this result can be written as $(-\\infty, -14] \\cup (-3, +\\infty)$.\n", - "\n", - "The final answer is:\n", - "\n", - "\\boxed{(-\\infty, -14] \\cup (-3, +\\infty)}\n", + "$$\n", + "\\boxed{(-\\infty, -14) \\cup (-3, \\infty)}\n", + "$$\n", "\n", "--------------------------------------------------------------------------------\n" ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Let's use Python to solve a math problem.\\n\\nQuery requirements:\\nYou should always use the 'print' function for the output and use fractions/radical forms instead of decimals.\\nYou can use packages like sympy to help you.\\nYou must follow the formats below to write your code:\\n```python\\n# your code\\n```\\n\\nFirst state the key idea to solve the problem. You may choose from three ways to solve the problem:\\nCase 1: If the problem can be solved with Python code directly, please write a program to solve it. You can enumerate all possible arrangements if needed.\\nCase 2: If the problem is mostly reasoning, you can solve it by yourself directly.\\nCase 3: If the problem cannot be handled in the above two ways, please follow this process:\\n1. Solve the problem step by step (do not over-divide the steps).\\n2. Take out any queries that can be asked through Python (for example, any calculations or equations that can be calculated).\\n3. Wait for me to give the results.\\n4. Continue if you think the result is correct. If the result is invalid or unexpected, please correct your query or reasoning.\\n\\nAfter all the queries are run and you get the answer, put the answer in \\\\boxed{}.\\n\\nProblem:\\nFind all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\", 'role': 'assistant'}, {'content': \"Key Idea:\\nTo solve this inequality $(2x + 10)(x + 3) < (3x + 9)(x + 8)$, we'll first expand both sides of the inequality, then collect all terms on one side to form a quadratic inequality. After simplifying, we will factor the quadratic expression if possible. Then, we'll determine the critical points of the inequality by setting the factors to zero. Finally, we'll use a sign chart or test values within intervals determined by the critical points to find the intervals where the original inequality is satisfied.\\n\\nCase 1 applies here, so I will write a Python program using sympy that simplifies the inequality, factors the resulting expression, solves for the critical points, and then determines the intervals where the inequality holds true.\\n\\n```python\\nfrom sympy import symbols, solve, simplify\\n\\n# Define the variable\\nx = symbols('x')\\n\\n# Given inequality\\nlhs = (2*x + 10)*(x + 3)\\nrhs = (3*x + 9)*(x + 8)\\n\\n# Move all terms to one side of the inequality\\ninequality = simplify(lhs - rhs) < 0\\n\\n# Solve the inequality\\nsolutions = solve(inequality, x)\\n\\n# Print the solution using interval notation\\nprint(solutions)\\n```\\n\\nRunning this code will provide us with the solution in interval notation. We'll express the final answer in the requested format afterward.\", 'role': 'user'}, {'content': '((-oo < x) & (x < -14)) | ((-3 < x) & (x < oo))', 'role': 'assistant'}, {'content': 'The solution obtained from running the Python code suggests that the values of $x$ that satisfy the inequality $(2x + 10)(x + 3) < (3x + 9)(x + 8)$ are in the intervals $(-\\\\infty, -14)$ and $(-3, \\\\infty)$.\\n\\nTherefore, the answer in interval notation is:\\n\\n$$\\n\\\\boxed{(-\\\\infty, -14) \\\\cup (-3, \\\\infty)}\\n$$', 'role': 'user'}], summary='The solution obtained from running the Python code suggests that the values of $x$ that satisfy the inequality $(2x + 10)(x + 3) < (3x + 9)(x + 8)$ are in the intervals $(-\\\\infty, -14)$ and $(-3, \\\\infty)$.\\n\\nTherefore, the answer in interval notation is:\\n\\n$$\\n\\\\boxed{(-\\\\infty, -14) \\\\cup (-3, \\\\infty)}\\n$$', cost=({'total_cost': 0.052199999999999996, 'gpt-4': {'cost': 0.052199999999999996, 'prompt_tokens': 954, 'completion_tokens': 393, 'total_tokens': 1347}}, {'total_cost': 0.052199999999999996, 'gpt-4': {'cost': 0.052199999999999996, 'prompt_tokens': 954, 'completion_tokens': 393, 'total_tokens': 1347}}), human_input=[])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -569,7 +281,7 @@ "math_problem = (\n", " \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\"\n", ")\n", - "mathproxyagent.initiate_chat(assistant, problem=math_problem)" + "mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem)" ] }, { @@ -582,7 +294,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -599,37 +311,7 @@ "\u001b[32m***** Suggested function Call: python *****\u001b[0m\n", "Arguments: \n", "{\n", - " \"cell\": \n", - " \"import matplotlib.pyplot as plt\n", - "\n", - " # Define agent texts\n", - " agent1_texts = ['Hello there!', 'Nice to meet you.', 'How can I assist you?']\n", - " agent2_texts = ['Hey!', 'Nice meeting you too.', 'Could you help me solve a problem?']\n", - "\n", - " # Define agent y positions\n", - " agent1_y = [3, 2, 1]\n", - " agent2_y = [3, 2, 1]\n", - "\n", - " # Create figure and axis\n", - " fig, ax = plt.subplots()\n", - "\n", - " # Plot Agent 1 texts\n", - " for i, text in enumerate(agent1_texts):\n", - " ax.text(0, agent1_y[i], text, fontsize=12, ha='right')\n", - "\n", - " # Plot Agent 2 texts\n", - " for i, text in enumerate(agent2_texts):\n", - " ax.text(1, agent2_y[i], text, fontsize=12, ha='left')\n", - "\n", - " # Set plot attributes\n", - " ax.set_xlim(-0.5, 1.5)\n", - " ax.set_ylim(0, 4)\n", - " ax.set_xticks([0, 1])\n", - " ax.set_xticklabels(['Agent 1', 'Agent 2'])\n", - " ax.set_yticks([])\n", - " ax.spines['top'].set_visible(False)\n", - " ax.spines['right'].set_visible(False)\n", - " ax.spines['bottom'].set_visible(False)\"\n", + " \"cell\": \"import matplotlib.pyplot as plt\\nimport numpy as np\\n\\nfig, ax = plt.subplots()\\n\\n# Define the agents as circles\\nagent1 = plt.Circle((0.4, 0.5), 0.1, color='blue')\\nagent2 = plt.Circle((0.6, 0.5), 0.1, color='red')\\n\\n# Draw the agents\\nax.add_artist(agent1)\\nax.add_artist(agent2)\\n\\n# Example dialog boxes\\nplt.text(0.28, 0.6, \\\"Hello!\\\", fontsize=12, bbox=dict(facecolor='white', alpha=0.5))\\nplt.text(0.58, 0.6, \\\"Hi there!\\\", fontsize=12, bbox=dict(facecolor='white', alpha=0.5))\\n\\n# Set the limits and remove axes\\nax.set_xlim(0, 1)\\nax.set_ylim(0, 1)\\nax.axis('off')\\n\"\n", "}\n", "\u001b[32m*******************************************\u001b[0m\n", "\n", @@ -639,72 +321,18 @@ ] }, { - "ename": "IndentationError", - "evalue": "unexpected indent (1440792568.py, line 4)", - "output_type": "error", - "traceback": [ - "\u001b[0;36m Cell \u001b[0;32mIn[3], line 4\u001b[0;36m\u001b[0m\n\u001b[0;31m agent1_texts = ['Hello there!', 'Nice to meet you.', 'How can I assist you?']\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m unexpected indent\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", - "\n", - "\u001b[32m***** Response from calling function \"python\" *****\u001b[0m\n", - "None\n", - "unexpected indent (1440792568.py, line 4)\n", - "\u001b[32m***************************************************\u001b[0m\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", - "\n", - "\u001b[32m***** Suggested function Call: python *****\u001b[0m\n", - "Arguments: \n", - "{\n", - " \"cell\": \n", - " \"import matplotlib.pyplot as plt\n", - "\n", - "# Define agent texts\n", - "agent1_texts = ['Hello there!', 'Nice to meet you.', 'How can I assist you?']\n", - "agent2_texts = ['Hey!', 'Nice meeting you too.', 'Could you help me solve a problem?']\n", - "\n", - "# Define agent y positions\n", - "agent1_y = [3, 2, 1]\n", - "agent2_y = [3, 2, 1]\n", - "\n", - "# Create figure and axis\n", - "fig, ax = plt.subplots()\n", - "\n", - "# Plot Agent 1 texts\n", - "for i, text in enumerate(agent1_texts):\n", - " ax.text(0, agent1_y[i], text, fontsize=12, ha='right')\n", - "\n", - "# Plot Agent 2 texts\n", - "for i, text in enumerate(agent2_texts):\n", - " ax.text(1, agent2_y[i], text, fontsize=12, ha='left')\n", - "\n", - "# Set plot attributes\n", - "ax.set_xlim(-0.5, 1.5)\n", - "ax.set_ylim(0, 4)\n", - "ax.set_xticks([0, 1])\n", - "ax.set_xticklabels(['Agent 1', 'Agent 2'])\n", - "ax.set_yticks([])\n", - "ax.spines['top'].set_visible(False)\n", - "ax.spines['right'].set_visible(False)\n", - "ax.spines['bottom'].set_visible(False)\"\n", - "}\n", - "\u001b[32m*******************************************\u001b[0m\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[35m\n", - ">>>>>>>> EXECUTING FUNCTION python...\u001b[0m\n" - ] + "data": { + "text/plain": [ + "(0.0, 1.0, 0.0, 1.0)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAGFCAYAAABg2vAPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdR0lEQVR4nO3de3SU9Z3H8c8zMyEkIjcN18YGAkGE2iO4si1QoSpC16VILSq6HjheIko99ZYK1kUUQZG2dk2VrOi6eNl1o1h6WkWqBRQQXStdFQ0GMRQpYkAS0IEwycz+8esQQhBzmzxP8n2/zomcTJLJb5xn8rznufweL5FIJAQAAMwK+T0AAADgL2IAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMI4YAADAOGIAAADjiAEAAIwjBgAAMC7i9wDQdlVWVioajfo9jFaTmZmpLl26+D2Mds3aMpVqLLNoKGIATVJZWanCwkLFYjG/h9Jq0tLSNHPmTP64pojFZSrVWGbRUMQAmiQajSoWi2ny5MnKysryezgpV15ermXLlikajfKHNUWsLVOpxjKLxiAG0CxZWVnq3bu338NAO8IyBbQ+DiAEAMA4YgAAAOOIAQAAjCMG0OZMmzZNOTk5dW7zPE933nmnL+NBcOXk5GjatGlN/vlp06apU6dOLTcgIKCIAaTM448/Ls/z9NZbbx3z62PGjNHQoUNbeVT1rV69Wp7nafXq1X4PBceRquUpGo3qzjvvbHPPPwGMlsTZBADarc2bNysUOv57nmg0qrlz50pyQQFYxJYBAO1Wenq60tLS/B7GcSUSCR04cMDvYcA4YgCB8uSTT2r48OHKyMhQ9+7ddckll2j79u1Nuq+NGzdqwoQJ6ty5szp16qRzzjlHGzZsaOERI8i+7piBsrKywxMczZ07V57nHXPz+44dOzRp0iR16tRJWVlZuuWWW1RTU1Pne+LxuB544AENGTJEHTt2VM+ePZWfn6+9e/fWG9MFF1ygl156SWeeeaYyMjJUVFQkSaqoqNBPf/pTZWdnKz09XQMGDNB9992neDze/P8ZwHGwmwApV1lZqd27d9e7/ehpZ++55x7dcccdmjJliq666iqVl5frwQcf1Pe+9z1t3LhRXbt2bfDv3LRpk0aPHq3OnTuroKBAaWlpKioq0pgxY7RmzRqNGDGiuQ8LPmno8tQQWVlZevjhhzVjxgxdeOGFmjx5siTp9NNPP/w9NTU1Ov/88zVixAgtWrRIL7/8sn7xi18oNzdXM2bMOPx9+fn5evzxxzV9+nTdcMMN+vjjj1VYWKiNGzdq3bp1dbZQbN68WZdeeqny8/N19dVXa9CgQYpGozr77LO1Y8cO5efn65RTTtH69es1a9Ys7dy5Uw888ECjHx/QUMQAUu7cc8/9yq8NGTJEkrRt2zbNmTNH8+bN0+zZsw9/ffLkyTrjjDP00EMP1bn96/z85z9XLBbT2rVr1b9/f0nSFVdcoUGDBqmgoEBr1qxp4qOB3xqyPDXUCSecoIsuukgzZszQ6aefrssvv7ze9xw8eFAXX3yx7rjjDknStddeq2HDhunRRx89HANr167VkiVL9NRTT2nq1KmHf3bs2LEaP368iouL69y+ZcsWrVixQueff/7h2+bNm6ePPvpIGzdu1MCBAyW5wOjTp4/uv/9+3XzzzcrOzm7U4wMaihhAyv3mN79RXl5evdtvvvnmw5taly1bpng8rilTptR519erVy8NHDhQq1atanAM1NTUaOXKlZo0adLhEJCk3r17a+rUqXrkkUe0b98+de7cuZmPDH5oyPLU0q699to6n48ePVpPPPHE4c+Li4vVpUsXnXfeeXWW3+HDh6tTp05atWpVnRjo169fnRBI3sfo0aPVrVu3Ovdx7rnn6t5779Wrr76qyy67rKUfGiCJGEArOOuss3TmmWfWu/3IP3qlpaVKJBKH3xEdrTEHgZWXlysajWrQoEH1vjZ48GDF43Ft37690e8iEQwNWZ5aUseOHetdOKlbt251jgUoLS1VZWWlevToccz7+Oyzz+p83q9fv3rfU1paqnfeeecrL9J09H0ALYkYQCDE43F5nqcXX3xR4XC43teZ+AV+OdbyeLR4PK4ePXroqaeeOubXj17BZ2RkHPM+zjvvPBUUFBzzPo61NQRoKcQAAiE3N1eJREL9+vVr9h+9rKwsZWZmavPmzfW+VlJSolAoVGff65gxY5RIJJr1O9F2eZ7X7PvIzc3Vyy+/rJEjRx5zRd/Q+/jiiy+Oe0zEkVhm0ZI4tRCBMHnyZIXDYc2dO7feH7lEIqE9e/Y0+L7C4bDGjRun5cuXq6ys7PDtu3bt0tNPP61Ro0bVOV6gsrJSJSUlikajzX4caHsyMzMludP6mmrKlCmqqanR3XffXe9r1dXVDbrvKVOm6PXXX9dLL71U72sVFRWqrq4+/HksFlNJSUlKdovAJrYMIBByc3M1b948zZo1S2VlZZo0aZJOPPFEffzxx3r++ed1zTXX6JZbbmnw/c2bN09//OMfNWrUKF133XWKRCIqKipSVVWVFi5cWOd7n3/+eU2fPl2rVq065gx0VVXSrl1SWZn03HNSRoZ06JD7qK6W0tKkDh2k9HT3b6dO0oAB7iM9vZn/Y1Df559LJSXuCaipqf3Yt086cED63/+VwmEpEpFiMXdbVdVXPhkZGRk67bTT9MwzzygvL0/du3fX0KFDGzW18dlnn638/HwtWLBAf/nLXzRu3DilpaWptLRUxcXF+vWvf62LLrrouPdx66236ne/+50uuOACTZs2TcOHD9eXX36pd999V88++6zKysp08sknS3LzHgwePFhz5sxhSmK0CGIAgXHbbbcpLy9Pv/rVrw5PD5udna1x48Zp4sSJjbqvIUOG6LXXXtOsWbO0YMECxeNxjRgxQk8++eRXzjFQWSlt3izt2eM+ysul3bulaFTav19avVr6z/903+t5UnKW20Si9uNInif17SsNHiydeqqUl1f7kZ3t1lc4hnhcqqiofSL27HFPxIoV7uvPPiv16VP7/cnN/Hv3uifrhRdqn4wvv3RP6oIFUmamdPLJUlaWdNJJtR/dumnJkiX6yU9+ohtvvFGHDh3SnDlzGn2dg8WLF2v48OEqKirS7NmzFYlElJOTo8svv1wjR4782p/PzMzUmjVrNH/+fBUXF2vp0qXq3Lmz8vLyNHfuXHXp0qVR4wEaw0uw4wlNsHPnThUVFSk/P1+9e/f2eziNlkhIn37q1hPvv+9W/MlXgue5jyMnfdu/f6eee65I27blS2r8401Lc1sRkr8jLU0aMUKaPFn64Q+lI86AtGnrVu184gkV/eEPyh8zRr3/vun+cHW15CmDoVDdevM8qUcPV22DBkm9etUGRhvW1l+jaF1sGYAZNTXStm1uC/MHH7h3+6FQ3ZW+dOx3+c119OR4sZi0fr20bp10001uPfSjH7kwGD68XayLji+RkN56S1q+3O17KSlxt+fk1F3xJxItGwLSsZ/wXbtcEa5eLZ14Yu3mnG9+k004MIEYQLtWVSWVlrp1zYcfut3MRwaAn1O+H/m7P/jAbcmeN0/q2bN2i8H3v++2IrQLsZj0pz+5AFi2zK2Aw+H6K3+/JJ+Q/ftdqLz5pjsIJC/PhcHAgRwEgnaLGEC7FI1Kb7whbdjggiAoAXA8yXXirl3SI49IDz8sfeMb0uzZ0vTpUseO/o6vyQ4elB57TJo/X9qxwx3YlzwyPkUzBjZbciE5dMjtR3rvPRcC//iPbv9OcjcG0E5waiHalf37pZdekn75S+nVV10ISMENgK+SXFfu2CFdf7074HDRIumLL/wdV6Ps3+8GnZ0tzZwp/e1v7vYjTpFrE5ILT1WVW6h++Utp5Ur3+IB2gi0DaBcqKqS1a6W333aft7WV/1dJbjXfvVsqKHC7EW68UbrhBqlbN3/H9pX27pX+7d/cSnP/fn83/be0RMLFzIYN7mPYMGnUKKkRV9QEgogtA2jTqqvdMV8PPuhCIB5vPyFwtETCnf54111Sbq47zTFQ69lEwg0qN9cNct++gA2wBSUXtLffdgvf6tVtb4sHcARiAG3WRx9JhYXSmjVu13N7jYCjJU/DnzbNvSndtMnvEcntUx81yg2qosLWk1FT4xbCwkK3UAJtELsJ0Czl5eWt/jurqqRXXnHHdXle67z5jEZb/3EeT/Ixv/mm9O1vS//6r+5Aw0hrv6Krq92BgXfdVXs+ZDOfkPK2Oi30/v3SQw9Jp50mnXOO72ce+PHaRNvFpENoksrKShUWFip29An0KbZ7t7RqlTtboLWX3MrKNFVUzJQUvJngPM8d6P4//+POQGgVn3wiTZni9p23wJNRKamwa1fF2vpMe57nzjYYO9bNeOijtLQ0zZw5k9kL8bWIATRZZWVlq17cZ+lS6fbb3XrHn63QmQpiCCRFIm6+nOJi98Y0pV55Rfrxj9274RbcV14pqY1uF6grFHJRMH++9C//4tswMjMzCQE0CDGAwIvHpZ/9zJ2lhuNLXi/h8cdTuA5autRNfCDZOTagOW69VbrvPgPTSqItIwYQaLGYdOWV0hNP+D2StmfRIunmm1Nwp7fe2sJ3asAVV0hLlrSj6STR3hADCKyDB6ULL3STCLGUNk1BgXTvvS3wpjSRcJtn7r+/RcZljudJ48e7aZjb7FSSaM84tRCBVFMjXXqpm+iNEGi6hQvdbutmu+ceQqA5EglXtVOnBncKZpjGlgEETiLhZq99+GFCoKU89ljtbv4m/fCVV7boeMzyPOm669xERRxDgAAhBhA4994rzZrl9yjal1BI+v3vpQkTGvmDL7wg/fM/c6BgS1uwQLrtNr9HARxGDCBQVq1yp8WxVLYsz3OnHb73nrtuUINs3y4NGeKujsQT0rI8z13OecwYv0cCSOKYAQTI55+74wTYetryEgk3UdNllzVwl3VNjdu/feAAIZAKnucW9s8/93skgCRiAAGRSLjd0rt3s0U6Vaqrpddea+BxgAsXustAcvGd1IjHpfJy6eqriS0EArsJEAi//a07jRCpF4lIJSXu4oLHtGWLNHgwIdBann9emjTJ71HAOLYMwHeHDkk33VQ7ex5Sr6CgqV9EiwqF3MxQrXyND+Bo/PmF74qKpLIydg+0lupqN/fN+vXH+OK6de6dKlsFWkc8Lm3d6l4EgI/YTQBfRaNS375SRYXfI7ElHJZGjHDr/jpGjpTeeIOJcVpbt27uKpCZmX6PBEaxZQC+euYZQsAPNTVuy8A77xxx4//9n7uREGh9e/e6608DPiEG4KvCQo4V8EskIi1efMQNixe7G9H6QiE3KyHgE3YTwDd//rN05pl+j8K2jAxp1y7pRO2XevZ08wrAP3/+szRsmN+jgEG8J4Nviot5I+q3Awfc9XO0YgUh4LdIxL0oAB8QA/DNihUctO63SER6+WW5/1Bm/qqudi8KwAfsJoAv9uyRsrKYfC0IsrOlvyay3dHs8JfnuZkJTzrJ75HAGLYMwBerVxMCQRHaXkYIBEUiIa1Z4/coYBAxAF+UlLBVOihOVYnfQ0BScq5ooJURA/DF1q1+jwBJudqqhLhUZGDw4oAPiAH4orSUgweDIje0VTUhNtMEQnW1e3EArYy/APDFtm1+jwBJfeOfyBOzDgYGLw74gC0D8MWhQ36PAEkdVKWQuEpUYPDigA+IAfiCKxQGByEQMLw44ANiAL5IS/N7BEg6pA4cQBgkvDjgA2IAvuje3e8RIKnS66a4F/Z7GEjixQEfEAPwxaBBXK0wKLaqv0IeM0AFQijkXhxAK+PPMXzRv78U5s1oIGxJ9FcoztkEgRAOuxcH0MqIAfgiN1eKxfweBSTpI+X6PQQkxWLEAHxBDMAX3/mO3yNAUmn6t5To2NHvYSDpu9/1ewQwiBiAL4YOlU4+2e9RIBSSRn2/g7wxYziIIwiysqQhQ/weBQzi1Q9feJ40fjwXK/JbIiGdf77+/h/4KhJxLwqP0zzR+ogB+Oaf/onrE/gtkZAmTJD0gx8w2Y3fqqvd8wD4wEskuKo8/HHwoNSrl1RZ6fdIbAqH3e7pV1/9+w2jR0vr1xMFfunaVfr0Uyk93e+RwCC2DMA3HTtKV1/NKYZ+qamRrr/+iBuuv54Q8Es47F4MhAB8wpYB+GrLFmngQL9HYVP37tLOnVKHDn+/oapK6t1b2rvX13GZVVoqDRjg9yhgFFsG4KsBA6RLLuFAwtbmedLttx8RApJ7V3r77RzA1toiEenSSwkB+IotA/BdWZnbOsDBhK0jFJL69nVvROttla6qciulv/2NXQatJS1N+vBDKSfH75HAMLYMwHc5OdKNN3Kae2uJx6VFi75i93R6uvsiIdA6QiG38BMC8BlbBhAIFRXSqadK5eWsh1IpEnGzP65Zc5y9AYmEdPbZ0uuvs7kmlUIhN8nQ5s1Sly5+jwbG8V4MgdC1q/Rf/+XWQ0gNz5MyM6WnnvqawwI8z31TRgbHD6RSIiH9938TAggEYgCBMXas9LOfsf5JlURCeuwxKTu7Ad+cne2+mTpLDc+TbrtNGjPG75EAkthNgICJxaSRI6WNG9lC3ZI8T7rqKunf/72RP3jNNdKSJURBSwqHpWHDpHXr3MGDQAAQAwic8nLprLOkTz4hCFpCOCydc470+983Yd0Ti7l5o//0JzdLEZonEnFbXd54wx0vAAQEMYBA2rpV+od/cFMVsw5qukhE+ta33JTDnTo18U6++MJNVfzuuzwZzREOu4Nj3nxT6t/f79EAdXDMAAKpf39p5Uo3ZTHTFTdN8k3oihXNCAHJ/fCKFdIpp/BkNFU47BbmlSsJAQQSMYDAGj7c7Vbt1o0ZChsrHJaGDJE2bJB69GiBO+zZ051qOHQoQdBYkYhbiNetc8cKAAFEDCDQvv1tt3v1G99gHdRQoZDbqv/aay0UAkk9e7r9DaNHM0NUQ0UibuF98023MAMBxSsagde/vwuCM85gHdQQF1/stuqfeGIK7rxzZ3fnF1+cgjtvZzzPLbRvvCH16+f3aIDj4k8r2oQePdxW1hkz3OfMRVBXOOzehBYWuvmCUnol3PR090sKC90vZZNNXcmF8/rrpbVrW3jzDJAanE2ANmfZMmn6dCka5dRDyW0tycmRiot92CX99tvSj3/srjbFPNIujk44QfqP/5AuvNDv0QANxpYBtDmTJ0vvvy9NnOg+t7rrIBJxj/2mm6R33vHp2LRhw9wvT15pyupWguRCOHGitGkTIYA2hy0DaNNefFG69lpp+3Y7k+R5nnus3/mOVFTk5hEIhHffdTMWbthQO0gLPM+ddrl4sTR+vN+jAZrE6HsqtBcTJkglJdLcue6Aufa8lSD52Pr0cVuh164NUAhIbjDr1rnB9enjbmvPB3eEQm6hmztX+uADQgBtGlsG0G7s2yc99JC0cKG7JHJ7WbJDIbc7PjdXuuMOaerUNjClfSwmPf20dPfd0kcf1T6I9sDz3LwBBQXuiNbOnf0eEdBsxADanWhUevRRaf586dNP2+56KBx2s/8OHSrNmeN2Q7e5XfI1Ne6Iz7vukt57r/ZBtTXJhahXL2n2bOnKK931oIF2ghhAu3XokPTkk9LSpW4Cnng8+OuiSMSdIdGxo9sFctVV7t82v7U9kXAHeCxZ4v49eLD2wQZVcmFJzuJ0xRXS5ZdLHTr4PTKgxREDMGHvXumFF6Tf/lb6wx+kAweCsy5KrnNOOsmdKfHDH7qrDHbs6PfIUuTgQemVV6Tly91Wgz17gvNkJMeRkeGu1jhpkvSDH7jdAkA7RgzAnKoqadWq2nXRZ5+5d97J9UAqXxHJs+9iMff5wIHSj37kAuCss9r3AZDHFI+7qXqXL5eee04qLXW3p6W5Qkrl/p2jn/SePV2NTZwojR2b4pmbgGAhBmBaPO7mzXn7bWnzZvfx/vvSX/9auzvh6BV4Q6Sl1Q2L9HQ3rfKQIVJenvv47nddDOAIpaXS+vXShx+6j02b3PWsq6rc15Mr8MY+GUeGRTjsTgU87TTp1FPdkzFsmLsyVpvfHwM0DTEAHEMs5ibVS66TPvxQ2rJF+vJLt15KfsRibkWfnu52JWdkuIPLBw5065hBg9y/ffsafNffUuJxaccOV2rJJ6O01J0+cuCAOzgk+YSkpdU+IenpbjbAAQNqCywvz03XGPjTMYDWRQwAAGAc71UAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACMIwYAADCOGAAAwDhiAAAA44gBAACM+3/SEA9yKFMnowAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -719,61 +347,19 @@ "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", "\n", "\u001b[32m***** Response from calling function \"python\" *****\u001b[0m\n", - "None\n", + "(0.0, 1.0, 0.0, 1.0)\n", "\u001b[32m***************************************************\u001b[0m\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[35m******************************Start compressing the following content:******************************\u001b[0m\n", - "To be compressed:\n", - "##FUNCTION_CALL## \n", - "Name: python\n", - "Args: {\n", - " \"cell\": \n", - " \"import matplotlib.pyplot as plt\n", - "\n", - " # Define agent texts\n", - " agent1_texts = ['Hello there!', 'Nice to meet you.', 'How can I assist you?']\n", - " agent2_texts = ['Hey!', 'Nice meeting you too.', 'Could you help me solve a problem?']\n", - "\n", - " # Define agent y positions\n", - " agent1_y = [3, 2, 1]\n", - " agent2_y = [3, 2, 1]\n", - "\n", - " # Create figure and axis\n", - " fig, ax = plt.subplots()\n", - "\n", - " # Plot Agent 1 texts\n", - " for i, text in enumerate(agent1_texts):\n", - " ax.text(0, agent1_y[i], text, fontsize=12, ha='right')\n", - "\n", - " # Plot Agent 2 texts\n", - " for i, text in enumerate(agent2_texts):\n", - " ax.text(1, agent2_y[i], text, fontsize=12, ha='left')\n", - "\n", - " # Set plot attributes\n", - " ax.set_xlim(-0.5, 1.5)\n", - " ax.set_ylim(0, 4)\n", - " ax.set_xticks([0, 1])\n", - " ax.set_xticklabels(['Agent 1', 'Agent 2'])\n", - " ax.set_yticks([])\n", - " ax.spines['top'].set_visible(False)\n", - " ax.spines['right'].set_visible(False)\n", - " ax.spines['bottom'].set_visible(False)\"\n", - "}\n", - "##FUNCTION_RETURN## (from function \"python\"): \n", - "None\n", - "unexpected indent (1440792568.py, line 4)\n", - "\n", - "\u001b[35m******************************Content after compressing:******************************\u001b[0m\n", - "##FUNCTION_CALL##\n", - "Name: python\n", - "Args: Executing a block of Python code that imports the matplotlib.pyplot library for graphing and plotting. It defines texts for two agents and sets y positions. It also creates a figure and plots the agent texts on a graph with specific attributes.\n", - " \n", - "##FUNCTION_RETURN## (from function \"python\"):\n", - "None. Execution failed due to an unexpected indentation error at line 4.\n", - " \u001b[35m\n", - "********************************************************************************\u001b[0m\n", - "\u001b[35mToken Count (including 107 tokens from system msg and function descriptions). Before compression : 821 | After: 564\u001b[0m\n", + "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", + "\n", + "The two agents have been drawn, each represented as a circle, and an example of their dialogue is displayed above them. Since `plt.show()` was not to be included, the plot is not displayed here, but the agents along with their dialogue would appear within the figure's coordinate system, which extends from 0 to 1 on both the x and y axes.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33muser_proxy\u001b[0m (to chatbot):\n", + "\n", + "\n", + "\n", "--------------------------------------------------------------------------------\n", "\u001b[33mchatbot\u001b[0m (to user_proxy):\n", "\n", @@ -781,6 +367,16 @@ "\n", "--------------------------------------------------------------------------------\n" ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': \"Draw two agents chatting with each other with an example dialog. Don't add plt.show().\", 'role': 'assistant'}, {'function_call': {'arguments': '{\\n \"cell\": \"import matplotlib.pyplot as plt\\\\nimport numpy as np\\\\n\\\\nfig, ax = plt.subplots()\\\\n\\\\n# Define the agents as circles\\\\nagent1 = plt.Circle((0.4, 0.5), 0.1, color=\\'blue\\')\\\\nagent2 = plt.Circle((0.6, 0.5), 0.1, color=\\'red\\')\\\\n\\\\n# Draw the agents\\\\nax.add_artist(agent1)\\\\nax.add_artist(agent2)\\\\n\\\\n# Example dialog boxes\\\\nplt.text(0.28, 0.6, \\\\\"Hello!\\\\\", fontsize=12, bbox=dict(facecolor=\\'white\\', alpha=0.5))\\\\nplt.text(0.58, 0.6, \\\\\"Hi there!\\\\\", fontsize=12, bbox=dict(facecolor=\\'white\\', alpha=0.5))\\\\n\\\\n# Set the limits and remove axes\\\\nax.set_xlim(0, 1)\\\\nax.set_ylim(0, 1)\\\\nax.axis(\\'off\\')\\\\n\"\\n}', 'name': 'python'}, 'content': None, 'role': 'assistant'}, {'content': '(0.0, 1.0, 0.0, 1.0)', 'name': 'python', 'role': 'function'}, {'content': \"The two agents have been drawn, each represented as a circle, and an example of their dialogue is displayed above them. Since `plt.show()` was not to be included, the plot is not displayed here, but the agents along with their dialogue would appear within the figure's coordinate system, which extends from 0 to 1 on both the x and y axes.\", 'role': 'user'}, {'content': '', 'role': 'assistant'}, {'content': 'TERMINATE', 'role': 'user'}], summary='', cost=({'total_cost': 0.04767, 'gpt-4': {'cost': 0.04767, 'prompt_tokens': 973, 'completion_tokens': 308, 'total_tokens': 1281}}, {'total_cost': 0.04767, 'gpt-4': {'cost': 0.04767, 'prompt_tokens': 973, 'completion_tokens': 308, 'total_tokens': 1281}}), human_input=[])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -886,7 +482,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -897,331 +493,330 @@ "\n", "Show me the YTD gain of 10 largest technology companies as of today.\n", "\n", - "--------------------------------------------------------------------------------\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "To fulfill your request, we first need a list of the 10 largest technology companies by market cap. Unfortunately, real-time financial data is gated behind paywalls, so it's difficult to get up-to-date reliable information through coding here. \n", + "To compute the Year-To-Date (YTD) gains of the 10 largest technology companies, I can fetch the latest stock price and the closing price from the last trading day of the previous year. Then calculate the percentage increase for each company.\n", "\n", - "However, I can provide you a script to download YTD performance for a given list of companies if you already know the list. Below is an example using Yahoo Finance, for which Python has a usable API.\n", + "First, we should fetch the current stock prices and the closing prices as of the last trading day of the previous year for these companies. For this, we can use a financial data API like Alpha Vantage, Yahoo Finance, or similar, which would require an API key and internet access, but I can't perform actions that require internet access.\n", "\n", - "Frequently mentioned largest technology companies include: Apple (AAPL), Microsoft (MSFT), Alphabet (GOOGL), Amazon (AMZN), Meta (FB), Tesla (TSLA), Alibaba group holding (BABA), Taiwan Semiconductor (TSM), Tencent (TCEHY), Oracle (ORCL). Adjust as necessary for your purpose.\n", + "Instead, I will provide you with Python code that you'd need to run on your local machine. This code utilizes the `yfinance` Python library, which is widely used for retrieving historical market data from Yahoo Finance. If you don't have `yfinance` installed, you'll need to install it by running `pip install yfinance` in your command line.\n", "\n", - "First, we need to install yfinance module:\n", - "\n", - "```sh\n", - "pip install yfinance\n", - "```\n", - "\n", - "Then, you can use this Python script to get the YTD performance. \n", - "\n", - "This Python script first gets the price at the beginning of the year, and then the most recent price. The difference between those two prices, divided by the starting price, gives the YTD performance.\n", + "Here is the code you'll need to execute:\n", "\n", "```python\n", - "# filename: ytd_gain.py\n", + "# filename: ytd_gains.py\n", "\n", "import yfinance as yf\n", - "from datetime import datetime\n", - "\n", - "# Define the tickers\n", - "tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'FB', 'TSLA', 'BABA', 'TSM', 'TCEHY', 'ORCL']\n", - "now = datetime.now()\n", - "\n", - "# Define the current year\n", - "current_year = now.year\n", - "\n", - "# Iterate through each ticker\n", - "for ticker in tickers:\n", - " # Download the year-to-date data for this ticker\n", - " ticker_data = yf.download(ticker, start=f'{current_year}-01-01', end=f'{now.year}-{now.month}-{now.day}')\n", - "\n", - " # Get the close price at the beginning of the year\n", - " initial_price = ticker_data['Close'][0]\n", - "\n", - " # Get the most recent close price\n", - " most_recent_price = ticker_data['Close'][-1]\n", - "\n", - " # Calculate the year-to-date return\n", - " ytd_return = (most_recent_price - initial_price) / initial_price * 100\n", - "\n", - " # Print the result\n", - " print(f'The YTD return for {ticker} is {ytd_return:.2f}%')\n", - "```\n", - "\n", - "Run the file in shell:\n", + "from datetime import datetime, timedelta\n", + "\n", + "# Define the ticker symbols for the 10 largest tech companies.\n", + "# This is a sample list and may not represent the current top 10 companies.\n", + "# You would need to replace this with the actual tickers of the top 10 tech companies.\n", + "tech_companies = [\"AAPL\", \"MSFT\", \"GOOGL\", \"AMZN\", \"FB\", \"TSLA\", \"NVDA\", \"V\", \"ADBE\", \"INTC\"]\n", + "\n", + "# Compute the last day of the last year\n", + "end_of_last_year = datetime(datetime.now().year - 1, 12, 31)\n", + "\n", + "# Retrieve the data and calculate YTD gain for each company\n", + "ytd_gains = {}\n", + "for symbol in tech_companies:\n", + " try:\n", + " # Fetch historical data\n", + " stock = yf.Ticker(symbol)\n", + " last_price = stock.history(period=\"1d\")['Close'][-1]\n", + " prev_close = stock.history(start=end_of_last_year, end=end_of_last_year + timedelta(days=1))['Close'][0]\n", + "\n", + " # Calculate YTD gain\n", + " ytd_gain = ((last_price - prev_close) / prev_close) * 100\n", + " ytd_gains[symbol] = ytd_gain\n", + " except Exception as e:\n", + " # Handle errors by skipping the company and printing an error message\n", + " print(f\"Error retrieving data for {symbol}: {e}\")\n", + "\n", + "# Print the YTD gains\n", + "for symbol, gain in ytd_gains.items():\n", + " print(f\"{symbol}: {gain:.2f}% YTD Gain\")\n", "\n", - "```sh\n", - "python ytd_gain.py\n", "```\n", "\n", - "The output will be the YTD gain (%) of each company to the console. Please note that actual results will depend on the input list of tickers and the current date.\n", + "Make sure that `yfinance` is installed and then run this Python script (`ytd_gains.py`). The script will print out the YTD gains for the listed technology companies as a percentage.\n", "\n", + "Note that the list of the 10 largest technology companies must be updated to reflect the current market situation. If you do not have the updated list, let me know, and I can attempt to retrieve this information for you using different methods.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[31m\n", ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is sh)...\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 1 (inferred language is python)...\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "exitcode: 1 (execution failed)\n", + "exitcode: 0 (execution succeeded)\n", "Code output: \n", - "Requirement already satisfied: yfinance in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (0.2.31)\n", - "Requirement already satisfied: pandas>=1.3.0 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (2.1.2)\n", - "Requirement already satisfied: numpy>=1.16.5 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (1.26.1)\n", - "Requirement already satisfied: requests>=2.31 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (2.31.0)\n", - "Requirement already satisfied: multitasking>=0.0.7 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (0.0.11)\n", - "Requirement already satisfied: lxml>=4.9.1 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (4.9.3)\n", - "Requirement already satisfied: appdirs>=1.4.4 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (1.4.4)\n", - "Requirement already satisfied: pytz>=2022.5 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (2023.3.post1)\n", - "Requirement already satisfied: frozendict>=2.3.4 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (2.3.8)\n", - "Requirement already satisfied: peewee>=3.16.2 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (3.17.0)\n", - "Requirement already satisfied: beautifulsoup4>=4.11.1 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (4.12.2)\n", - "Requirement already satisfied: html5lib>=1.1 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from yfinance) (1.1)\n", - "Requirement already satisfied: soupsieve>1.2 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)\n", - "Requirement already satisfied: six>=1.9 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from html5lib>=1.1->yfinance) (1.16.0)\n", - "Requirement already satisfied: webencodings in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from html5lib>=1.1->yfinance) (0.5.1)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from pandas>=1.3.0->yfinance) (2.8.2)\n", - "Requirement already satisfied: tzdata>=2022.1 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from pandas>=1.3.0->yfinance) (2023.3)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from requests>=2.31->yfinance) (3.3.1)\n", - "Requirement already satisfied: idna<4,>=2.5 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from requests>=2.31->yfinance) (3.4)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from requests>=2.31->yfinance) (2.0.7)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages (from requests>=2.31->yfinance) (2023.7.22)\n", - "\n", - "ytd_gain.py:19: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " initial_price = ticker_data['Close'][0]\n", - "ytd_gain.py:22: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " most_recent_price = ticker_data['Close'][-1]\n", - "ytd_gain.py:19: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " initial_price = ticker_data['Close'][0]\n", - "ytd_gain.py:22: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " most_recent_price = ticker_data['Close'][-1]\n", - "ytd_gain.py:19: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " initial_price = ticker_data['Close'][0]\n", - "ytd_gain.py:22: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " most_recent_price = ticker_data['Close'][-1]\n", - "ytd_gain.py:19: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " initial_price = ticker_data['Close'][0]\n", - "ytd_gain.py:22: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n", - " most_recent_price = ticker_data['Close'][-1]\n", - "\n", - "1 Failed download:\n", - "['FB']: Exception('%ticker%: No timezone found, symbol may be delisted')\n", - "Traceback (most recent call last):\n", - " File \"ytd_gain.py\", line 19, in \n", - " initial_price = ticker_data['Close'][0]\n", - " File \"/home/ykw5399/.conda/envs/dev2/lib/python3.9/site-packages/pandas/core/series.py\", line 1037, in __getitem__\n", - " return self._values[key]\n", - "IndexError: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for AAPL: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for MSFT: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for GOOGL: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for AMZN: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for FB: index -1 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for TSLA: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for NVDA: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for V: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for ADBE: index 0 is out of bounds for axis 0 with size 0\n", + "Error retrieving data for INTC: index 0 is out of bounds for axis 0 with size 0\n", "\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "There seems to be an issue with the ticker symbol 'FB'. The error message shows 'FB': No timezone found, symbol may be delisted. This is because 'FB' has been recently changed to 'META' so the symbol 'FB' is causing the issue.\n", - "\n", - "We can replace 'FB' with 'META' and rerun the Python script. The error messages for series indexing are warnings rather than errors. It seems recent changes in pandas library caused these warnings. To resolve this, `iloc` function will be utilized to get the first and last items of the Series.\n", + "It seems that the script encountered an error when trying to fetch data for the provided ticker symbols. This might be because either the market is closed and the latest data is not yet available, or because the dates specified do not have available data (e.g., the end of last year might have been a weekend or holiday).\n", "\n", - "Here is the updated Python script:\n", + "Let's adjust the code to be more robust by checking for valid trading days around the end of last year and to handle cases where the stock data might not be available. Here's the updated code:\n", "\n", "```python\n", - "# filename: ytd_gain.py\n", + "# filename: ytd_gains.py\n", "\n", "import yfinance as yf\n", "from datetime import datetime\n", "\n", - "# Define the tickers\n", - "tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META', 'TSLA', 'BABA', 'TSM', 'TCEHY', 'ORCL']\n", - "now = datetime.now()\n", + "def get_last_valid_trading_day(year):\n", + " last_day = datetime(year, 12, 31)\n", + " # Start moving backwards from December 31st to find the last valid trading day of the year\n", + " while True:\n", + " stock_data = yf.download('SPY', start=last_day.strftime('%Y-%m-%d'), end=(last_day + timedelta(days=1)).strftime('%Y-%m-%d'))\n", + " if not stock_data.empty:\n", + " return last_day\n", + " last_day -= timedelta(days=1)\n", + "\n", + "# Define the ticker symbols for the 10 largest tech companies.\n", + "tech_companies = [\"AAPL\", \"MSFT\", \"GOOGL\", \"AMZN\", \"FB\", \"TSLA\", \"NVDA\", \"V\", \"ADBE\", \"INTC\"]\n", + "\n", + "# Compute the last valid trading day of the last year\n", + "end_of_last_year = get_last_valid_trading_day(datetime.now().year - 1)\n", + "\n", + "# Retrieve the data and calculate YTD gain for each company\n", + "ytd_gains = {}\n", + "for symbol in tech_companies:\n", + " try:\n", + " # Fetch historical data\n", + " stock = yf.Ticker(symbol)\n", + " last_price = stock.history(period='1d')['Close'].iloc[-1]\n", + " prev_close = stock.history(start=end_of_last_year, end=end_of_last_year + timedelta(days=1))['Close'].iloc[0]\n", + "\n", + " # Calculate YTD gain\n", + " ytd_gain = ((last_price - prev_close) / prev_close) * 100\n", + " ytd_gains[symbol] = ytd_gain\n", + " except Exception as e:\n", + " # Handle errors by skipping the company and printing an error message\n", + " print(f\"Error retrieving data for {symbol}: {e}\")\n", + "\n", + "# Print the YTD gains\n", + "for symbol, gain in ytd_gains.items():\n", + " print(f\"{symbol}: {gain:.2f}% YTD Gain\")\n", + "```\n", "\n", - "# Define the current year\n", - "current_year = now.year\n", + "Please execute the updated `ytd_gains.py` script. The function `get_last_valid_trading_day` will find the last valid trading day of the previous year to handle non-trading days correctly, and the fetch for current price will get the latest available data point even if the market has not yet closed for today.\n", "\n", - "# Iterate through each ticker\n", - "for ticker in tickers:\n", - " # Download the year-to-date data for this ticker\n", - " ticker_data = yf.download(ticker, start=f'{current_year}-01-01', end=f'{now.year}-{now.month}-{now.day}')\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - " # Get the close price at the beginning of the year\n", - " initial_price = ticker_data['Close'].iloc[0]\n", + "exitcode: 1 (execution failed)\n", + "Code output: \n", + "Traceback (most recent call last):\n", + " File \"ytd_gains.py\", line 19, in \n", + " end_of_last_year = get_last_valid_trading_day(datetime.now().year - 1)\n", + " File \"ytd_gains.py\", line 10, in get_last_valid_trading_day\n", + " stock_data = yf.download('SPY', start=last_day.strftime('%Y-%m-%d'), end=(last_day + timedelta(days=1)).strftime('%Y-%m-%d'))\n", + "NameError: name 'timedelta' is not defined\n", "\n", - " # Get the most recent close price\n", - " most_recent_price = ticker_data['Close'].iloc[-1]\n", "\n", - " # Calculate the year-to-date return\n", - " ytd_return = (most_recent_price - initial_price) / initial_price * 100\n", + "--------------------------------------------------------------------------------\n", + "\u001b[35mToken Count (including 468 tokens from system msg and function descriptions). Before compression : 2115 | After: 1223\u001b[0m\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - " # Print the result\n", - " print(f'The YTD return for {ticker} is {ytd_return:.2f}%')\n", - "```\n", + "It seems I forgot to import the `timedelta` class from the `datetime` module. I will correct this oversight and provide the updated code. Here is the corrected code including the missed import:\n", "\n", - "Run the file in shell:\n", + "```python\n", + "# filename: ytd_gains.py\n", "\n", - "```sh\n", - "python ytd_gain.py\n", + "import yfinance as yf\n", + "from datetime import datetime, timedelta\n", + "\n", + "def get_last_valid_trading_day(year):\n", + " last_day = datetime(year, 12, 31)\n", + " # Start moving backwards from December 31st to find the last valid trading day of the year\n", + " while True:\n", + " stock_data = yf.download('SPY', start=last_day.strftime('%Y-%m-%d'), end=(last_day + timedelta(days=1)).strftime('%Y-%m-%d'))\n", + " if not stock_data.empty:\n", + " return last_day\n", + " last_day -= timedelta(days=1)\n", + "\n", + "# Define the ticker symbols for the 10 largest tech companies.\n", + "tech_companies = [\"AAPL\", \"MSFT\", \"GOOGL\", \"AMZN\", \"FB\", \"TSLA\", \"NVDA\", \"V\", \"ADBE\", \"INTC\"]\n", + "\n", + "# Compute the last valid trading day of the last year\n", + "end_of_last_year = get_last_valid_trading_day(datetime.now().year - 1)\n", + "\n", + "# Retrieve the data and calculate YTD gain for each company\n", + "ytd_gains = {}\n", + "for symbol in tech_companies:\n", + " try:\n", + " # Fetch historical data\n", + " stock = yf.Ticker(symbol)\n", + " last_price = stock.history(period='1d')['Close'].iloc[-1]\n", + " prev_close = stock.history(start=end_of_last_year, end=end_of_last_year + timedelta(days=1))['Close'].iloc[0]\n", + "\n", + " # Calculate YTD gain\n", + " ytd_gain = ((last_price - prev_close) / prev_close) * 100\n", + " ytd_gains[symbol] = ytd_gain\n", + " except Exception as e:\n", + " # Handle errors by skipping the company and printing an error message\n", + " print(f\"Error retrieving data for {symbol}: {e}\")\n", + "\n", + "# Print the YTD gains\n", + "for symbol, gain in ytd_gains.items():\n", + " print(f\"{symbol}: {gain:.2f}% YTD Gain\")\n", "```\n", "\n", - "This script will print the YTD gain (%) of each company to the console.\n", + "Please execute the corrected `ytd_gains.py` script. This should resolve the `NameError` and provide you with the YTD gain for each company.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[31m\n", ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 1 (inferred language is sh)...\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", "exitcode: 0 (execution succeeded)\n", "Code output: \n", + "Error retrieving data for FB: single positional indexer is out-of-bounds\n", + "AAPL: -6.57% YTD Gain\n", + "MSFT: 10.70% YTD Gain\n", + "GOOGL: -1.83% YTD Gain\n", + "AMZN: 17.30% YTD Gain\n", + "TSLA: -18.45% YTD Gain\n", + "NVDA: 66.15% YTD Gain\n", + "V: 8.96% YTD Gain\n", + "ADBE: -4.30% YTD Gain\n", + "INTC: -12.54% YTD Gain\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for AAPL is 41.24%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for MSFT is 47.26%\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for GOOGL is 44.86%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for AMZN is 61.50%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for META is 152.20%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TSLA is 103.48%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for BABA is -7.25%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TSM is 23.99%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TCEHY is -12.16%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for ORCL is 29.06%\n", - "\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for AAPL is 41.24%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for MSFT is 47.26%\n", - "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for GOOGL is 44.86%\n", + "--------------------------------------------------------------------------------\n", + "\u001b[35mToken Count (including 468 tokens from system msg and function descriptions). Before compression : 1865 | After: 1133\u001b[0m\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for AMZN is 61.50%\n", + "It looks like an error occurred while retrieving data for FB, which stands for Facebook. This can happen if the ticker symbol has changed or if there was an issue with the data for the date range we were trying to fetch. Facebook, Inc. has been renamed to Meta Platforms, Inc., and its stock symbol is now \"META.\" \n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for META is 152.20%\n", + "I will update the list of technology companies to include \"META\" instead of \"FB\" and run the code again:\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TSLA is 103.48%\n", + "```python\n", + "# filename: ytd_gains_updated.py\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for BABA is -7.25%\n", + "import yfinance as yf\n", + "from datetime import datetime, timedelta\n", + "\n", + "def get_last_valid_trading_day(year):\n", + " last_day = datetime(year, 12, 31)\n", + " # Start moving backwards from December 31st to find the last valid trading day of the year\n", + " while True:\n", + " stock_data = yf.download('SPY', start=last_day.strftime('%Y-%m-%d'), end=(last_day + timedelta(days=1)).strftime('%Y-%m-%d'))\n", + " if not stock_data.empty:\n", + " return last_day\n", + " last_day -= timedelta(days=1)\n", + "\n", + "# Define the ticker symbols for the 10 largest tech companies with FB replaced by META.\n", + "tech_companies = [\"AAPL\", \"MSFT\", \"GOOGL\", \"AMZN\", \"META\", \"TSLA\", \"NVDA\", \"V\", \"ADBE\", \"INTC\"]\n", + "\n", + "# Compute the last valid trading day of the last year\n", + "end_of_last_year = get_last_valid_trading_day(datetime.now().year - 1)\n", + "\n", + "# Retrieve the data and calculate YTD gain for each company\n", + "ytd_gains = {}\n", + "for symbol in tech_companies:\n", + " try:\n", + " # Fetch historical data\n", + " stock = yf.Ticker(symbol)\n", + " last_price = stock.history(period='1d')['Close'].iloc[-1]\n", + " prev_close = stock.history(start=end_of_last_year, end=end_of_last_year + timedelta(days=1))['Close'].iloc[0]\n", + "\n", + " # Calculate YTD gain\n", + " ytd_gain = ((last_price - prev_close) / prev_close) * 100\n", + " ytd_gains[symbol] = ytd_gain\n", + " except Exception as e:\n", + " # Handle errors by skipping the company and printing an error message\n", + " print(f\"Error retrieving data for {symbol}: {e}\")\n", + "\n", + "# Print the YTD gains\n", + "for symbol, gain in ytd_gains.items():\n", + " print(f\"{symbol}: {gain:.2f}% YTD Gain\")\n", + "```\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TSM is 23.99%\n", + "Please execute the updated `ytd_gains_updated.py` script to get the YTD gain of Meta Platforms, Inc. along with the other technology companies.\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for TCEHY is -12.16%\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> USING AUTO REPLY...\u001b[0m\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33muser_proxy\u001b[0m (to assistant):\n", "\n", - "[*********************100%%**********************] 1 of 1 completed\n", - "The YTD return for ORCL is 29.06%\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "AAPL: -6.57% YTD Gain\n", + "MSFT: 10.70% YTD Gain\n", + "GOOGL: -1.83% YTD Gain\n", + "AMZN: 17.30% YTD Gain\n", + "META: 42.06% YTD Gain\n", + "TSLA: -18.45% YTD Gain\n", + "NVDA: 66.15% YTD Gain\n", + "V: 8.96% YTD Gain\n", + "ADBE: -4.30% YTD Gain\n", + "INTC: -12.54% YTD Gain\n", "\n", "\n", "--------------------------------------------------------------------------------\n", - "\u001b[35mToken Count (including 468 tokens from system msg and function descriptions). Before compression : 3684 | After: 1509\u001b[0m\n", + "\u001b[35mToken Count (including 468 tokens from system msg and function descriptions). Before compression : 1828 | After: 1186\u001b[0m\n", "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to user_proxy):\n", "\n", - "It appears that you've run the script twice, as the results are duplicated. From the code output, here are the Year-to-Date (YTD) returns for the 10 largest technology companies:\n", + "The YTD (Year-To-Date) gain for the 10 largest technology companies as of today are as follows:\n", "\n", - "1. AAPL (Apple): 41.24% gain\n", - "2. MSFT (Microsoft): 47.26% gain\n", - "3. GOOGL (Alphabet Class A): 44.86% gain\n", - "4. AMZN (Amazon): 61.50% gain\n", - "5. META (Meta Platforms, previously Facebook): 152.20% gain\n", - "6. TSLA (Tesla): 103.48% gain\n", - "7. BABA (Alibaba): -7.25% loss\n", - "8. TSM (Taiwan Semiconductor Manufacturing): 23.99% gain\n", - "9. TCEHY (Tencent Holdings): -12.16% loss\n", - "10. ORCL (Oracle): 29.06% gain\n", + "1. Apple Inc. (AAPL): -6.57% YTD Gain\n", + "2. Microsoft Corporation (MSFT): 10.70% YTD Gain\n", + "3. Alphabet Inc. (GOOGL): -1.83% YTD Gain\n", + "4. Amazon.com, Inc. (AMZN): 17.30% YTD Gain\n", + "5. Meta Platforms, Inc. (META, formerly FB): 42.06% YTD Gain\n", + "6. Tesla, Inc. (TSLA): -18.45% YTD Gain\n", + "7. NVIDIA Corporation (NVDA): 66.15% YTD Gain\n", + "8. Visa Inc. (V): 8.96% YTD Gain\n", + "9. Adobe Inc. (ADBE): -4.30% YTD Gain\n", + "10. Intel Corporation (INTC): -12.54% YTD Gain\n", "\n", - "Please note, the percentage change could have slight differences due to market volatility and the exact times the prices were taken. \n", + "These YTD gains reflect the percentage change in the stock price of each company from the last trading day of the previous year to the most recent available trading data.\n", "\n", - "If everything looks good, let's wrap up. If you need any more help with other tasks, just let me know! \n", + "If you need any further assistance, please let me know.\n", "\n", "TERMINATE\n", "\n", - "--------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> NO HUMAN INPUT RECEIVED.\u001b[0m\n" ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': 'Show me the YTD gain of 10 largest technology companies as of today.', 'role': 'assistant'}, {'content': 'It looks like an error occurred while retrieving data for FB, which stands for Facebook. This can happen if the ticker symbol has changed or if there was an issue with the data for the date range we were trying to fetch. Facebook, Inc. has been renamed to Meta Platforms, Inc., and its stock symbol is now \"META.\" \\n\\nI will update the list of technology companies to include \"META\" instead of \"FB\" and run the code again:\\n\\n```python\\n# filename: ytd_gains_updated.py\\n\\nimport yfinance as yf\\nfrom datetime import datetime, timedelta\\n\\ndef get_last_valid_trading_day(year):\\n last_day = datetime(year, 12, 31)\\n # Start moving backwards from December 31st to find the last valid trading day of the year\\n while True:\\n stock_data = yf.download(\\'SPY\\', start=last_day.strftime(\\'%Y-%m-%d\\'), end=(last_day + timedelta(days=1)).strftime(\\'%Y-%m-%d\\'))\\n if not stock_data.empty:\\n return last_day\\n last_day -= timedelta(days=1)\\n\\n# Define the ticker symbols for the 10 largest tech companies with FB replaced by META.\\ntech_companies = [\"AAPL\", \"MSFT\", \"GOOGL\", \"AMZN\", \"META\", \"TSLA\", \"NVDA\", \"V\", \"ADBE\", \"INTC\"]\\n\\n# Compute the last valid trading day of the last year\\nend_of_last_year = get_last_valid_trading_day(datetime.now().year - 1)\\n\\n# Retrieve the data and calculate YTD gain for each company\\nytd_gains = {}\\nfor symbol in tech_companies:\\n try:\\n # Fetch historical data\\n stock = yf.Ticker(symbol)\\n last_price = stock.history(period=\\'1d\\')[\\'Close\\'].iloc[-1]\\n prev_close = stock.history(start=end_of_last_year, end=end_of_last_year + timedelta(days=1))[\\'Close\\'].iloc[0]\\n\\n # Calculate YTD gain\\n ytd_gain = ((last_price - prev_close) / prev_close) * 100\\n ytd_gains[symbol] = ytd_gain\\n except Exception as e:\\n # Handle errors by skipping the company and printing an error message\\n print(f\"Error retrieving data for {symbol}: {e}\")\\n\\n# Print the YTD gains\\nfor symbol, gain in ytd_gains.items():\\n print(f\"{symbol}: {gain:.2f}% YTD Gain\")\\n```\\n\\nPlease execute the updated `ytd_gains_updated.py` script to get the YTD gain of Meta Platforms, Inc. along with the other technology companies.', 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\nAAPL: -6.57% YTD Gain\\nMSFT: 10.70% YTD Gain\\nGOOGL: -1.83% YTD Gain\\nAMZN: 17.30% YTD Gain\\nMETA: 42.06% YTD Gain\\nTSLA: -18.45% YTD Gain\\nNVDA: 66.15% YTD Gain\\nV: 8.96% YTD Gain\\nADBE: -4.30% YTD Gain\\nINTC: -12.54% YTD Gain\\n', 'role': 'assistant'}, {'content': 'The YTD (Year-To-Date) gain for the 10 largest technology companies as of today are as follows:\\n\\n1. Apple Inc. (AAPL): -6.57% YTD Gain\\n2. Microsoft Corporation (MSFT): 10.70% YTD Gain\\n3. Alphabet Inc. (GOOGL): -1.83% YTD Gain\\n4. Amazon.com, Inc. (AMZN): 17.30% YTD Gain\\n5. Meta Platforms, Inc. (META, formerly FB): 42.06% YTD Gain\\n6. Tesla, Inc. (TSLA): -18.45% YTD Gain\\n7. NVIDIA Corporation (NVDA): 66.15% YTD Gain\\n8. Visa Inc. (V): 8.96% YTD Gain\\n9. Adobe Inc. (ADBE): -4.30% YTD Gain\\n10. Intel Corporation (INTC): -12.54% YTD Gain\\n\\nThese YTD gains reflect the percentage change in the stock price of each company from the last trading day of the previous year to the most recent available trading data.\\n\\nIf you need any further assistance, please let me know.\\n\\nTERMINATE', 'role': 'user'}], summary='The YTD (Year-To-Date) gain for the 10 largest technology companies as of today are as follows:\\n\\n1. Apple Inc. (AAPL): -6.57% YTD Gain\\n2. Microsoft Corporation (MSFT): 10.70% YTD Gain\\n3. Alphabet Inc. (GOOGL): -1.83% YTD Gain\\n4. Amazon.com, Inc. (AMZN): 17.30% YTD Gain\\n5. Meta Platforms, Inc. (META, formerly FB): 42.06% YTD Gain\\n6. Tesla, Inc. (TSLA): -18.45% YTD Gain\\n7. NVIDIA Corporation (NVDA): 66.15% YTD Gain\\n8. Visa Inc. (V): 8.96% YTD Gain\\n9. Adobe Inc. (ADBE): -4.30% YTD Gain\\n10. Intel Corporation (INTC): -12.54% YTD Gain\\n\\nThese YTD gains reflect the percentage change in the stock price of each company from the last trading day of the previous year to the most recent available trading data.\\n\\nIf you need any further assistance, please let me know.\\n\\n', cost=({'total_cost': 0.31437, 'gpt-4': {'cost': 0.31437, 'prompt_tokens': 5401, 'completion_tokens': 2539, 'total_tokens': 7940}}, {'total_cost': 0.31437, 'gpt-4': {'cost': 0.31437, 'prompt_tokens': 5401, 'completion_tokens': 2539, 'total_tokens': 7940}}), human_input=[''])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -1295,7 +890,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.10.13" }, "orig_nbformat": 4 }, diff --git a/notebook/agentchat_groupchat_RAG.ipynb b/notebook/agentchat_groupchat_RAG.ipynb index 50d35f5a9d82..4d3e734ea7d0 100644 --- a/notebook/agentchat_groupchat_RAG.ipynb +++ b/notebook/agentchat_groupchat_RAG.ipynb @@ -35,14 +35,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "LLM models: ['gpt-35-turbo', 'gpt-35-turbo-0613']\n" + "LLM models: ['gpt-4-1106-preview', 'gpt-4-turbo-preview', 'gpt-4-0613', 'gpt-35-turbo-0613', 'gpt-35-turbo-1106']\n" ] } ], @@ -75,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -206,7 +206,8 @@ " boss_aid.problem = message if not hasattr(boss_aid, \"problem\") else boss_aid.problem\n", " _, ret_msg = boss_aid._generate_retrieve_user_reply(message)\n", " else:\n", - " ret_msg = boss_aid.generate_init_message(message, n_results=n_results)\n", + " _context = {\"problem\": message, \"n_results\": n_results}\n", + " ret_msg = boss_aid.message_generator(boss_aid, None, _context)\n", " return ret_msg if ret_msg else message\n", "\n", " boss_aid.human_input_mode = \"NEVER\" # Disable human input for boss_aid since it only retrieves content.\n", @@ -249,7 +250,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -269,110 +270,49 @@ "text": [ "\u001b[33mSenior_Python_Engineer\u001b[0m (to chat_manager):\n", "\n", - "To use Spark for parallel training in FLAML, you need to set up a Spark cluster and configure FLAML to use Spark as the backend. Here's a sample code to demonstrate how to use Spark for parallel training in FLAML:\n", + "To use Apache Spark for parallel training in FLAML, you need to use the `flaml.tune.run` function. Here is a sample code:\n", "\n", "```python\n", - "from flaml import AutoML\n", - "from pyspark.sql import SparkSession\n", + "from flaml import tune\n", "\n", - "# Create a Spark session\n", - "spark = SparkSession.builder \\\n", - " .appName(\"FLAML with Spark\") \\\n", - " .getOrCreate()\n", + "# Define your training function\n", + "def training_function(config):\n", + " # your training code here\n", + " pass\n", "\n", - "# Load your data into a Spark DataFrame\n", - "data = spark.read.format(\"csv\").option(\"header\", \"true\").load(\"your_data.csv\")\n", - "\n", - "# Initialize FLAML with Spark backend\n", - "automl = AutoML()\n", - "automl.initialize(spark=spark)\n", - "\n", - "# Specify the search space and other settings\n", - "settings = {\n", - " \"time_budget\": 60, # total time in seconds\n", - " \"metric\": 'accuracy',\n", - " \"task\": 'classification',\n", - " \"log_file_name\": 'flaml.log',\n", + "# Define your search space\n", + "search_space = {\n", + " \"lr\": tune.loguniform(1e-4, 1e-1),\n", + " \"momentum\": tune.uniform(0.1, 0.9),\n", "}\n", "\n", - "# Train and tune the model using FLAML\n", - "automl.fit(data=data, **settings)\n", - "\n", - "# Get the best model and its hyperparameters\n", - "best_model = automl.best_model\n", - "best_config = automl.best_config\n", - "\n", - "# Print the best model and its hyperparameters\n", - "print(\"Best model:\", best_model)\n", - "print(\"Best hyperparameters:\", best_config)\n", - "\n", - "# Terminate the Spark session\n", - "spark.stop()\n", - "```\n", - "\n", - "Make sure to replace `\"your_data.csv\"` with the path to your actual data file. Adjust the `settings` dictionary according to your requirements.\n", - "\n", - "This code initializes a Spark session, loads the data into a Spark DataFrame, and then uses FLAML's `AutoML` class to train and tune a model in parallel using Spark. Finally, it prints the best model and its hyperparameters.\n", - "\n", - "Remember to install FLAML and PySpark before running this code.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "To use Spark for parallel training in FLAML, you need to set up a Spark cluster and configure FLAML to use Spark as the backend. Here's a sample code to demonstrate how to use Spark for parallel training in FLAML:\n", - "\n", - "```python\n", - "from flaml import AutoML\n", - "from pyspark.sql import SparkSession\n", - "\n", - "# Create a Spark session\n", - "spark = SparkSession.builder \\\n", - " .appName(\"FLAML with Spark\") \\\n", - " .getOrCreate()\n", + "# Use SparkTrials for parallelization\n", + "from ray.tune import SparkTrials\n", "\n", - "# Load your data into a Spark DataFrame\n", - "data = spark.read.format(\"csv\").option(\"header\", \"true\").load(\"your_data.csv\")\n", + "spark_trials = SparkTrials(parallelism=2)\n", "\n", - "# Initialize FLAML with Spark backend\n", - "automl = AutoML()\n", - "automl.initialize(spark=spark)\n", - "\n", - "# Specify the search space and other settings\n", - "settings = {\n", - " \"time_budget\": 60, # total time in seconds\n", - " \"metric\": 'accuracy',\n", - " \"task\": 'classification',\n", - " \"log_file_name\": 'flaml.log',\n", - "}\n", - "\n", - "# Train and tune the model using FLAML\n", - "automl.fit(data=data, **settings)\n", - "\n", - "# Get the best model and its hyperparameters\n", - "best_model = automl.best_model\n", - "best_config = automl.best_config\n", + "analysis = tune.run(\n", + " training_function,\n", + " config=search_space,\n", + " num_samples=10,\n", + " scheduler=tune.schedulers.FIFOScheduler(),\n", + " progress_reporter=tune.JupyterNotebookReporter(overwrite=True),\n", + " trial_executor=spark_trials,\n", + ")\n", "\n", - "# Print the best model and its hyperparameters\n", - "print(\"Best model:\", best_model)\n", - "print(\"Best hyperparameters:\", best_config)\n", + "print(\"Best config: \", analysis.get_best_config(metric=\"accuracy\", mode=\"max\"))\n", "\n", - "# Terminate the Spark session\n", - "spark.stop()\n", + "# Get a dataframe for analyzing trial results.\n", + "df = analysis.results_df\n", "```\n", "\n", - "Make sure to replace `\"your_data.csv\"` with the path to your actual data file. Adjust the `settings` dictionary according to your requirements.\n", - "\n", - "This code initializes a Spark session, loads the data into a Spark DataFrame, and then uses FLAML's `AutoML` class to train and tune a model in parallel using Spark. Finally, it prints the best model and its hyperparameters.\n", + "In this code, `training_function` is your training function, which should take a `config` argument. This `config` argument is a dictionary that includes hyperparameters for your model. The `search_space` is a dictionary that defines the search space for your hyperparameters.\n", "\n", - "Remember to install FLAML and PySpark before running this code.\n", + "The `tune.run` function is used to start the hyperparameter tuning. The `config` argument is your search space, `num_samples` is the number of times to sample from the search space, and `scheduler` is the scheduler for the trials. The `trial_executor` argument is set to `spark_trials` to use Spark for parallelization.\n", "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mCode_Reviewer\u001b[0m (to chat_manager):\n", + "The `analysis.get_best_config` function is used to get the best hyperparameters found during the tuning. The `analysis.results_df` gives a dataframe that contains the results of all trials.\n", "\n", - "Looks good to me! The code demonstrates how to use Spark for parallel training in FLAML. It initializes a Spark session, loads the data into a Spark DataFrame, and then uses FLAML's `AutoML` class to train and tune a model in parallel using Spark. Finally, it prints the best model and its hyperparameters. Just make sure to replace `\"your_data.csv\"` with the actual path to the data file and adjust the `settings` dictionary as needed. \n", - "\n", - "If there are no further questions, I will terminate.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33mProduct_Manager\u001b[0m (to chat_manager):\n", + "Please note that you need to have Apache Spark and Ray installed and properly configured in your environment to run this code.\n", "\n", "TERMINATE\n", "\n", @@ -1108,9 +1048,13 @@ } ], "metadata": { - "front_matter": { - "tags": ["group chat", "orchestration", "RAG"], - "description": "Implement and manage a multi-agent chat system using AutoGen, where AI assistants retrieve information, generate code, and interact collaboratively to solve complex tasks, especially in areas not covered by their training data." + "front_matter": { + "description": "Implement and manage a multi-agent chat system using AutoGen, where AI assistants retrieve information, generate code, and interact collaboratively to solve complex tasks, especially in areas not covered by their training data.", + "tags": [ + "group chat", + "orchestration", + "RAG" + ] }, "kernelspec": { "display_name": "flaml", @@ -1127,7 +1071,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/notebook/agentchat_qdrant_RetrieveChat.ipynb b/notebook/agentchat_qdrant_RetrieveChat.ipynb index e1152e750ee3..5dd644761bf7 100644 --- a/notebook/agentchat_qdrant_RetrieveChat.ipynb +++ b/notebook/agentchat_qdrant_RetrieveChat.ipynb @@ -207,14 +207,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "models to use: ['gpt-3.5-turbo']\n" + "models to use: ['gpt-4-1106-preview', 'gpt-4-turbo-preview', 'gpt-4-0613', 'gpt-35-turbo-0613', 'gpt-35-turbo-1106']\n" ] } ], @@ -286,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -317,7 +317,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -351,12 +351,17 @@ " max_consecutive_auto_reply=10,\n", " retrieve_config={\n", " \"task\": \"code\",\n", - " \"docs_path\": \"~/path/to/FLAML/website/docs/reference\", # change this to your own path, such as https://raw.githubusercontent.com/microsoft/autogen/main/README.md\n", + " \"docs_path\": [\n", + " \"https://raw.githubusercontent.com/microsoft/flaml/main/README.md\",\n", + " \"https://raw.githubusercontent.com/microsoft/FLAML/main/website/docs/Research.md\",\n", + " ], # change this to your own path, such as https://raw.githubusercontent.com/microsoft/autogen/main/README.md\n", " \"chunk_token_size\": 2000,\n", " \"model\": config_list[0][\"model\"],\n", " \"client\": QdrantClient(\":memory:\"),\n", " \"embedding_model\": \"BAAI/bge-small-en-v1.5\",\n", " },\n", + " # code_execution_config={\n", + " # \"use_docker\": False,}\n", ")" ] }, @@ -384,12 +389,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[32mAdding doc_id 69 to context.\u001b[0m\n", + "Trying to create collection.\n", "\u001b[32mAdding doc_id 0 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 47 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 64 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 65 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 21 to context.\u001b[0m\n", + "\u001b[32mAdding doc_id 2 to context.\u001b[0m\n", + "\u001b[32mAdding doc_id 1 to context.\u001b[0m\n", "\u001b[33mragproxyagent\u001b[0m (to assistant):\n", "\n", "You're a retrieve augmented coding assistant. You answer user's questions based on your own knowledge and the\n", @@ -404,320 +407,268 @@ "\n", "User's question is: Is there a function called tune_automl?\n", "\n", - "Context is: {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/autogen/agentchat/contrib/math_user_proxy_agent\",\n", - " \"reference/autogen/agentchat/contrib/retrieve_assistant_agent\",\n", - " \"reference/autogen/agentchat/contrib/retrieve_user_proxy_agent\"\n", - " ],\n", - " \"label\": \"autogen.agentchat.contrib\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/autogen/agentchat/agent\",\n", - " \"reference/autogen/agentchat/assistant_agent\",\n", - " \"reference/autogen/agentchat/conversable_agent\",\n", - " \"reference/autogen/agentchat/groupchat\",\n", - " \"reference/autogen/agentchat/user_proxy_agent\"\n", - " ],\n", - " \"label\": \"autogen.agentchat\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/autogen/oai/completion\",\n", - " \"reference/autogen/oai/openai_utils\"\n", - " ],\n", - " \"label\": \"autogen.oai\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/autogen/code_utils\",\n", - " \"reference/autogen/math_utils\",\n", - " \"reference/autogen/retrieve_utils\"\n", - " ],\n", - " \"label\": \"autogen\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/nlp/huggingface/trainer\",\n", - " \"reference/automl/nlp/huggingface/training_args\",\n", - " \"reference/automl/nlp/huggingface/utils\"\n", - " ],\n", - " \"label\": \"automl.nlp.huggingface\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/automl/nlp/utils\"\n", - " ],\n", - " \"label\": \"automl.nlp\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/spark/metrics\",\n", - " \"reference/automl/spark/utils\"\n", - " ],\n", - " \"label\": \"automl.spark\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/task/task\",\n", - " \"reference/automl/task/time_series_task\"\n", - " ],\n", - " \"label\": \"automl.task\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/time_series/sklearn\",\n", - " \"reference/automl/time_series/tft\",\n", - " \"reference/automl/time_series/ts_data\",\n", - " \"reference/automl/time_series/ts_model\"\n", - " ],\n", - " \"label\": \"automl.time_series\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/automl/automl\",\n", - " \"reference/automl/data\",\n", - " \"reference/automl/ml\",\n", - " \"reference/automl/model\",\n", - " \"reference/automl/state\"\n", - " ],\n", - " \"label\": \"automl\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/default/estimator\",\n", - " \"reference/default/greedy\",\n", - " \"reference/default/portfolio\",\n", - " \"reference/default/suggest\"\n", - " ],\n", - " \"label\": \"default\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/onlineml/autovw\",\n", - " \"reference/onlineml/trial\",\n", - " \"reference/onlineml/trial_runner\"\n", - " ],\n", - " \"label\": \"onlineml\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/scheduler/online_scheduler\",\n", - " \"reference/tune/scheduler/trial_scheduler\"\n", - " ],\n", - " \"label\": \"tune.scheduler\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/searcher/blendsearch\",\n", - " \"reference/tune/searcher/cfo_cat\",\n", - " \"reference/tune/searcher/flow2\",\n", - " \"reference/tune/searcher/online_searcher\",\n", - " \"reference/tune/searcher/search_thread\",\n", - " \"reference/tune/searcher/suggestion\",\n", - " \"reference/tune/searcher/variant_generator\"\n", - " ],\n", - " \"label\": \"tune.searcher\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/spark/utils\"\n", - " ],\n", - " \"label\": \"tune.spark\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/tune/analysis\",\n", - " \"reference/tune/sample\",\n", - " \"reference/tune/space\",\n", - " \"reference/tune/trial\",\n", - " \"reference/tune/trial_runner\",\n", - " \"reference/tune/tune\",\n", - " \"reference/tune/utils\"\n", - " ],\n", - " \"label\": \"tune\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/config\"\n", - " ],\n", - " \"label\": \"Reference\",\n", - " \"type\": \"category\"\n", - "}\n", - "---\n", - "sidebar_label: config\n", - "title: config\n", - "---\n", + "Context is: [![PyPI version](https://badge.fury.io/py/FLAML.svg)](https://badge.fury.io/py/FLAML)\n", + "![Conda version](https://img.shields.io/conda/vn/conda-forge/flaml)\n", + "[![Build](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml/badge.svg)](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml)\n", + "![Python Version](https://img.shields.io/badge/3.8%20%7C%203.9%20%7C%203.10-blue)\n", + "[![Downloads](https://pepy.tech/badge/flaml)](https://pepy.tech/project/flaml)\n", + "[![](https://img.shields.io/discord/1025786666260111483?logo=discord&style=flat)](https://discord.gg/Cppx2vSPVP)\n", + "\n", "\n", - "!\n", - "* Copyright (c) Microsoft Corporation. All rights reserved.\n", - "* Licensed under the MIT License.\n", "\n", - "#### PENALTY\n", + "# A Fast Library for Automated Machine Learning & Tuning\n", "\n", - "penalty term for constraints\n", + "

\n", + " \n", + "
\n", + "

\n", "\n", + ":fire: Heads-up: We have migrated [AutoGen](https://microsoft.github.io/autogen/) into a dedicated [github repository](https://github.com/microsoft/autogen). Alongside this move, we have also launched a dedicated [Discord](https://discord.gg/pAbnFJrkgZ) server and a [website](https://microsoft.github.io/autogen/) for comprehensive documentation.\n", "\n", - "---\n", - "sidebar_label: trial_scheduler\n", - "title: tune.scheduler.trial_scheduler\n", - "---\n", + ":fire: The automated multi-agent chat framework in [AutoGen](https://microsoft.github.io/autogen/) is in preview from v2.0.0.\n", "\n", - "## TrialScheduler Objects\n", + ":fire: FLAML is highlighted in OpenAI's [cookbook](https://github.com/openai/openai-cookbook#related-resources-from-around-the-web).\n", + "\n", + ":fire: [autogen](https://microsoft.github.io/autogen/) is released with support for ChatGPT and GPT-4, based on [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673).\n", + "\n", + ":fire: FLAML supports Code-First AutoML & Tuning – Private Preview in [Microsoft Fabric Data Science](https://learn.microsoft.com/en-us/fabric/data-science/).\n", "\n", - "```python\n", - "class TrialScheduler()\n", - "```\n", "\n", - "Interface for implementing a Trial Scheduler class.\n", + "## What is FLAML\n", + "FLAML is a lightweight Python library for efficient automation of machine\n", + "learning and AI operations. It automates workflow based on large language models, machine learning models, etc.\n", + "and optimizes their performance.\n", "\n", - "#### CONTINUE\n", + "* FLAML enables building next-gen GPT-X applications based on multi-agent conversations with minimal effort. It simplifies the orchestration, automation and optimization of a complex GPT-X workflow. It maximizes the performance of GPT-X models and augments their weakness.\n", + "* For common machine learning tasks like classification and regression, it quickly finds quality models for user-provided data with low computational resources. It is easy to customize or extend. Users can find their desired customizability from a smooth range.\n", + "* It supports fast and economical automatic tuning (e.g., inference hyperparameters for foundation models, configurations in MLOps/LMOps workflows, pipelines, mathematical/statistical models, algorithms, computing experiments, software configurations), capable of handling large search space with heterogeneous evaluation cost and complex constraints/guidance/early stopping.\n", "\n", - "Status for continuing trial execution\n", + "FLAML is powered by a series of [research studies](https://microsoft.github.io/FLAML/docs/Research/) from Microsoft Research and collaborators such as Penn State University, Stevens Institute of Technology, University of Washington, and University of Waterloo.\n", "\n", - "#### PAUSE\n", + "FLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source, cross-platform machine learning framework for .NET.\n", "\n", - "Status for pausing trial execution\n", + "## Installation\n", "\n", - "#### STOP\n", + "FLAML requires **Python version >= 3.8**. It can be installed from pip:\n", "\n", - "Status for stopping trial execution\n", + "```bash\n", + "pip install flaml\n", + "```\n", "\n", + "Minimal dependencies are installed without extra options. You can install extra options based on the feature you need. For example, use the following to install the dependencies needed by the [`autogen`](https://microsoft.github.io/autogen/) package.\n", + "```bash\n", + "pip install \"flaml[autogen]\"\n", + "```\n", "\n", - "---\n", - "sidebar_label: retrieve_user_proxy_agent\n", - "title: autogen.agentchat.contrib.retrieve_user_proxy_agent\n", - "---\n", + "Find more options in [Installation](https://microsoft.github.io/FLAML/docs/Installation).\n", + "Each of the [`notebook examples`](https://github.com/microsoft/FLAML/tree/main/notebook) may require a specific option to be installed.\n", "\n", - "## RetrieveUserProxyAgent Objects\n", + "## Quickstart\n", "\n", + "* (New) The [autogen](https://microsoft.github.io/autogen/) package enables the next-gen GPT-X applications with a generic multi-agent conversation framework.\n", + "It offers customizable and conversable agents which integrate LLMs, tools and human.\n", + "By automating chat among multiple capable agents, one can easily make them collectively perform tasks autonomously or with human feedback, including tasks that require using tools via code. For example,\n", "```python\n", - "class RetrieveUserProxyAgent(UserProxyAgent)\n", + "from flaml import autogen\n", + "assistant = autogen.AssistantAgent(\"assistant\")\n", + "user_proxy = autogen.UserProxyAgent(\"user_proxy\")\n", + "user_proxy.initiate_chat(assistant, message=\"Show me the YTD gain of 10 largest technology companies as of today.\")\n", + "# This initiates an automated chat between the two agents to solve the task\n", "```\n", "\n", - "#### \\_\\_init\\_\\_\n", + "Autogen also helps maximize the utility out of the expensive LLMs such as ChatGPT and GPT-4. It offers a drop-in replacement of `openai.Completion` or `openai.ChatCompletion` with powerful functionalites like tuning, caching, templating, filtering. For example, you can optimize generations by LLM with your own tuning data, success metrics and budgets.\n", + "```python\n", + "# perform tuning\n", + "config, analysis = autogen.Completion.tune(\n", + " data=tune_data,\n", + " metric=\"success\",\n", + " mode=\"max\",\n", + " eval_func=eval_func,\n", + " inference_budget=0.05,\n", + " optimization_budget=3,\n", + " num_samples=-1,\n", + ")\n", + "# perform inference for a test instance\n", + "response = autogen.Completion.create(context=test_instance, **config)\n", + "```\n", + "* With three lines of code, you can start using this economical and fast\n", + "AutoML engine as a [scikit-learn style estimator](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML).\n", "\n", "```python\n", - "def __init__(name=\"RetrieveChatAgent\",\n", - " is_termination_msg: Optional[Callable[\n", - " [Dict], bool]] = _is_termination_msg_retrievechat,\n", - " human_input_mode: Optional[str] = \"ALWAYS\",\n", - " retrieve_config: Optional[Dict] = None,\n", - " **kwargs)\n", + "from flaml import AutoML\n", + "automl = AutoML()\n", + "automl.fit(X_train, y_train, task=\"classification\")\n", "```\n", "\n", - "**Arguments**:\n", - "\n", - "- `name` _str_ - name of the agent.\n", - "- `human_input_mode` _str_ - whether to ask for human inputs every time a message is received.\n", - " Possible values are \"ALWAYS\", \"TERMINATE\", \"NEVER\".\n", - " (1) When \"ALWAYS\", the agent prompts for human input every time a message is received.\n", - " Under this mode, the conversation stops when the human input is \"exit\",\n", - " or when is_termination_msg is True and there is no human input.\n", - " (2) When \"TERMINATE\", the agent only prompts for human input only when a termination message is received or\n", - " the number of auto reply reaches the max_consecutive_auto_reply.\n", - " (3) When \"NEVER\", the agent will never prompt for human input. Under this mode, the conversation stops\n", - " when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.\n", - "- `retrieve_config` _dict or None_ - config for the retrieve agent.\n", - " To use default config, set to None. Otherwise, set to a dictionary with the following keys:\n", - " - task (Optional, str): the task of the retrieve chat. Possible values are \"code\", \"qa\" and \"default\". System\n", - " prompt will be different for different tasks. The default value is `default`, which supports both code and qa.\n", - " - client (Optional, chromadb.Client): the chromadb client.\n", - " If key not provided, a default client `chromadb.Client()` will be used.\n", - " - docs_path (Optional, str): the path to the docs directory. It can also be the path to a single file,\n", - " or the url to a single file. If key not provided, a default path `./docs` will be used.\n", - " - collection_name (Optional, str): the name of the collection.\n", - " If key not provided, a default name `flaml-docs` will be used.\n", - " - model (Optional, str): the model to use for the retrieve chat.\n", - " If key not provided, a default model `gpt-4` will be used.\n", - " - chunk_token_size (Optional, int): the chunk token size for the retrieve chat.\n", - " If key not provided, a default size `max_tokens * 0.4` will be used.\n", - " - context_max_tokens (Optional, int): the context max token size for the retrieve chat.\n", - " If key not provided, a default size `max_tokens * 0.8` will be used.\n", - " - chunk_mode (Optional, str): the chunk mode for the retrieve chat. Possible values are\n", - " \"multi_lines\" and \"one_line\". If key not provided, a default mode `multi_lines` will be used.\n", - " - must_break_at_empty_line (Optional, bool): chunk will only break at empty line if True. Default is True.\n", - " If chunk_mode is \"one_line\", this parameter will be ignored.\n", - " - embedding_model (Optional, str): the embedding model to use for the retrieve chat.\n", - " If key not provided, a default model `all-MiniLM-L6-v2` will be used. All available models\n", - " can be found at `https://www.sbert.net/docs/pretrained_models.html`. The default model is a\n", - " fast model. If you want to use a high performance model, `all-mpnet-base-v2` is recommended.\n", - " - customized_prompt (Optional, str): the customized prompt for the retrieve chat. Default is None.\n", - "- `**kwargs` _dict_ - other kwargs in [UserProxyAgent](user_proxy_agent#__init__).\n", - "\n", - "#### generate\\_init\\_message\n", + "* You can restrict the learners and use FLAML as a fast hyperparameter tuning\n", + "tool for XGBoost, LightGBM, Random Forest etc. or a [customized learner](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML#estimator-and-search-space).\n", "\n", "```python\n", - "def generate_init_message(problem: str,\n", - " n_results: int = 20,\n", - " search_string: str = \"\")\n", + "automl.fit(X_train, y_train, task=\"classification\", estimator_list=[\"lgbm\"])\n", "```\n", "\n", - "Generate an initial message with the given problem and prompt.\n", + "* You can also run generic hyperparameter tuning for a [custom function](https://microsoft.github.io/FLAML/docs/Use-Cases/Tune-User-Defined-Function).\n", "\n", - "**Arguments**:\n", + "```python\n", + "from flaml import tune\n", + "tune.run(evaluation_function, config={…}, low_cost_partial_config={…}, time_budget_s=3600)\n", + "```\n", "\n", - "- `problem` _str_ - the problem to be solved.\n", - "- `n_results` _int_ - the number of results to be retrieved.\n", - "- `search_string` _str_ - only docs containing this string will be retrieved.\n", - " \n", + "* [Zero-shot AutoML](https://microsoft.github.io/FLAML/docs/Use-Cases/Zero-Shot-AutoML) allows using the existing training API from lightgbm, xgboost etc. while getting the benefit of AutoML in choosing high-performance hyperparameter configurations per task.\n", "\n", - "**Returns**:\n", + "```python\n", + "from flaml.default import LGBMRegressor\n", "\n", - "- `str` - the generated prompt ready to be sent to the assistant agent.\n", + "# Use LGBMRegressor in the same way as you use lightgbm.LGBMRegressor.\n", + "estimator = LGBMRegressor()\n", + "# The hyperparameters are automatically set according to the training data.\n", + "estimator.fit(X_train, y_train)\n", + "```\n", "\n", + "## Documentation\n", "\n", - "---\n", - "sidebar_label: retrieve_assistant_agent\n", - "title: autogen.agentchat.contrib.retrieve_assistant_agent\n", - "---\n", + "You can find a detailed documentation about FLAML [here](https://microsoft.github.io/FLAML/).\n", "\n", - "## RetrieveAssistantAgent Objects\n", + "In addition, you can find:\n", "\n", - "```python\n", - "class RetrieveAssistantAgent(AssistantAgent)\n", + "- [Research](https://microsoft.github.io/FLAML/docs/Research) and [blogposts](https://microsoft.github.io/FLAML/blog) around FLAML.\n", + "\n", + "- [Discord](https://discord.gg/Cppx2vSPVP).\n", + "\n", + "- [Contributing guide](https://microsoft.github.io/FLAML/docs/Contribute).\n", + "\n", + "- ML.NET documentation and tutorials for [Model Builder](https://learn.microsoft.com/dotnet/machine-learning/tutorials/predict-prices-with-model-builder), [ML.NET CLI](https://learn.microsoft.com/dotnet/machine-learning/tutorials/sentiment-analysis-cli), and [AutoML API](https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/how-to-use-the-automl-api).\n", + "\n", + "## Contributing\n", + "\n", + "This project welcomes contributions and suggestions. Most contributions require you to agree to a\n", + "Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\n", + "the rights to use your contribution. For details, visit .\n", + "\n", + "If you are new to GitHub [here](https://help.github.com/categories/collaborating-with-issues-and-pull-requests/) is a detailed help source on getting involved with development on GitHub.\n", + "# Research\n", + "\n", + "For technical details, please check our research publications.\n", + "\n", + "* [FLAML: A Fast and Lightweight AutoML Library](https://www.microsoft.com/en-us/research/publication/flaml-a-fast-and-lightweight-automl-library/). Chi Wang, Qingyun Wu, Markus Weimer, Erkang Zhu. MLSys 2021.\n", + "\n", + "```bibtex\n", + "@inproceedings{wang2021flaml,\n", + " title={FLAML: A Fast and Lightweight AutoML Library},\n", + " author={Chi Wang and Qingyun Wu and Markus Weimer and Erkang Zhu},\n", + " year={2021},\n", + " booktitle={MLSys},\n", + "}\n", + "```\n", + "\n", + "* [Frugal Optimization for Cost-related Hyperparameters](https://arxiv.org/abs/2005.01571). Qingyun Wu, Chi Wang, Silu Huang. AAAI 2021.\n", + "\n", + "```bibtex\n", + "@inproceedings{wu2021cfo,\n", + " title={Frugal Optimization for Cost-related Hyperparameters},\n", + " author={Qingyun Wu and Chi Wang and Silu Huang},\n", + " year={2021},\n", + " booktitle={AAAI},\n", + "}\n", "```\n", "\n", - "(Experimental) Retrieve Assistant agent, designed to solve a task with LLM.\n", + "* [Economical Hyperparameter Optimization With Blended Search Strategy](https://www.microsoft.com/en-us/research/publication/economical-hyperparameter-optimization-with-blended-search-strategy/). Chi Wang, Qingyun Wu, Silu Huang, Amin Saied. ICLR 2021.\n", "\n", - "RetrieveAssistantAgent is a subclass of AssistantAgent configured with a default system message.\n", - "The default system message is designed to solve a task with LLM,\n", - "including suggesting python code blocks and debugging.\n", - "`human_input_mode` is default to \"NEVER\"\n", - "and `code_execution_config` is default to False.\n", - "This agent doesn't execute code by default, and expects the user to execute the code.\n", + "```bibtex\n", + "@inproceedings{wang2021blendsearch,\n", + " title={Economical Hyperparameter Optimization With Blended Search Strategy},\n", + " author={Chi Wang and Qingyun Wu and Silu Huang and Amin Saied},\n", + " year={2021},\n", + " booktitle={ICLR},\n", + "}\n", + "```\n", "\n", + "* [An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models](https://aclanthology.org/2021.acl-long.178.pdf). Susan Xueqing Liu, Chi Wang. ACL 2021.\n", "\n", - "---\n", - "sidebar_label: utils\n", - "title: automl.nlp.huggingface.utils\n", - "---\n", + "```bibtex\n", + "@inproceedings{liuwang2021hpolm,\n", + " title={An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models},\n", + " author={Susan Xueqing Liu and Chi Wang},\n", + " year={2021},\n", + " booktitle={ACL},\n", + "}\n", + "```\n", "\n", - "#### todf\n", + "* [ChaCha for Online AutoML](https://www.microsoft.com/en-us/research/publication/chacha-for-online-automl/). Qingyun Wu, Chi Wang, John Langford, Paul Mineiro and Marco Rossi. ICML 2021.\n", "\n", - "```python\n", - "def todf(X, Y, column_name)\n", + "```bibtex\n", + "@inproceedings{wu2021chacha,\n", + " title={ChaCha for Online AutoML},\n", + " author={Qingyun Wu and Chi Wang and John Langford and Paul Mineiro and Marco Rossi},\n", + " year={2021},\n", + " booktitle={ICML},\n", + "}\n", + "```\n", + "\n", + "* [Fair AutoML](https://arxiv.org/abs/2111.06495). Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2111.06495 (2021).\n", + "\n", + "```bibtex\n", + "@inproceedings{wuwang2021fairautoml,\n", + " title={Fair AutoML},\n", + " author={Qingyun Wu and Chi Wang},\n", + " year={2021},\n", + " booktitle={ArXiv preprint arXiv:2111.06495},\n", + "}\n", + "```\n", + "\n", + "* [Mining Robust Default Configurations for Resource-constrained AutoML](https://arxiv.org/abs/2202.09927). Moe Kayali, Chi Wang. ArXiv preprint arXiv:2202.09927 (2022).\n", + "\n", + "```bibtex\n", + "@inproceedings{kayaliwang2022default,\n", + " title={Mining Robust Default Configurations for Resource-constrained AutoML},\n", + " author={Moe Kayali and Chi Wang},\n", + " year={2022},\n", + " booktitle={ArXiv preprint arXiv:2202.09927},\n", + "}\n", + "```\n", + "\n", + "* [Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives](https://openreview.net/forum?id=0Ij9_q567Ma). Shaokun Zhang, Feiran Jia, Chi Wang, Qingyun Wu. ICLR 2023 (notable-top-5%).\n", + "\n", + "```bibtex\n", + "@inproceedings{zhang2023targeted,\n", + " title={Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives},\n", + " author={Shaokun Zhang and Feiran Jia and Chi Wang and Qingyun Wu},\n", + " booktitle={International Conference on Learning Representations},\n", + " year={2023},\n", + " url={https://openreview.net/forum?id=0Ij9_q567Ma},\n", + "}\n", + "```\n", + "\n", + "* [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673). Chi Wang, Susan Xueqing Liu, Ahmed H. Awadallah. ArXiv preprint arXiv:2303.04673 (2023).\n", + "\n", + "```bibtex\n", + "@inproceedings{wang2023EcoOptiGen,\n", + " title={Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference},\n", + " author={Chi Wang and Susan Xueqing Liu and Ahmed H. Awadallah},\n", + " year={2023},\n", + " booktitle={ArXiv preprint arXiv:2303.04673},\n", + "}\n", + "```\n", + "\n", + "* [An Empirical Study on Challenging Math Problem Solving with GPT-4](https://arxiv.org/abs/2306.01337). Yiran Wu, Feiran Jia, Shaokun Zhang, Hangyu Li, Erkang Zhu, Yue Wang, Yin Tat Lee, Richard Peng, Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2306.01337 (2023).\n", + "\n", + "```bibtex\n", + "@inproceedings{wu2023empirical,\n", + " title={An Empirical Study on Challenging Math Problem Solving with GPT-4},\n", + " author={Yiran Wu and Feiran Jia and Shaokun Zhang and Hangyu Li and Erkang Zhu and Yue Wang and Yin Tat Lee and Richard Peng and Qingyun Wu and Chi Wang},\n", + " year={2023},\n", + " booktitle={ArXiv preprint arXiv:2306.01337},\n", + "}\n", "```\n", "\n", - "todf converts Y from any format (list, pandas.Series, numpy array) to a DataFrame before being returned\n", "\n", + "When you submit a pull request, a CLA bot will automatically determine whether you need to provide\n", + "a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\n", + "provided by the bot. You will only need to do this once across all repos using our CLA.\n", + "\n", + "This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n", + "For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\n", + "contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n", "\n", "\n", "\n", @@ -725,10 +676,42 @@ "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to ragproxyagent):\n", "\n", - "No, there is no function called `tune_automl` in the given context.\n", + "Based on the context provided, which is about the FLAML library, there is no direct reference to a function specifically called `tune_automl`. However, FLAML does offer functionality for automated machine learning (AutoML) and hyperparameter tuning.\n", + "\n", + "The closest reference to an AutoML tuning operation in the given context is shown in the Quickstart section, which demonstrates how to use FLAML as a scikit-learn style estimator for machine learning tasks like classification and regression. It does talk about automated machine learning and tuning, but doesn't mention a function `tune_automl` by name.\n", + "\n", + "If you are looking for a way to perform tuning with FLAML, the context indicates you can use the `tune` module to run generic hyperparameter tuning for a custom function, as demonstrated in the Quickstart section:\n", + "\n", + "```python\n", + "from flaml import tune\n", + "tune.run(evaluation_function, config={…}, low_cost_partial_config={…}, time_budget_s=3600)\n", + "```\n", + "\n", + "This is not called `tune_automl` but rather just `tune.run`.\n", + "\n", + "If you need confirmation on whether a function called `tune_automl` specifically exists, the FLAML documentation or its API reference should be checked. If documentation is not enough to confirm and you require to look into the actual code or a structured list of functionalities provided by FLAML, that information isn't available in the given context.\n", + "\n", + "In that case, the instruction should be: `UPDATE CONTEXT`.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[32mUpdating context and resetting conversation.\u001b[0m\n", + "\u001b[32mNo more context, will terminate.\u001b[0m\n", + "\u001b[33mragproxyagent\u001b[0m (to assistant):\n", + "\n", + "TERMINATE\n", "\n", "--------------------------------------------------------------------------------\n" ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': 'TERMINATE', 'role': 'assistant'}], summary='', cost=({'total_cost': 0.12719999999999998, 'gpt-4': {'cost': 0.12719999999999998, 'prompt_tokens': 3634, 'completion_tokens': 303, 'total_tokens': 3937}}, {'total_cost': 0.12719999999999998, 'gpt-4': {'cost': 0.12719999999999998, 'prompt_tokens': 3634, 'completion_tokens': 303, 'total_tokens': 3937}}), human_input=[])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -736,7 +719,7 @@ "assistant.reset()\n", "\n", "qa_problem = \"Is there a function called tune_automl?\"\n", - "ragproxyagent.initiate_chat(assistant, problem=qa_problem)" + "ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=qa_problem)" ] }, { @@ -756,22 +739,16 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "\u001b[32mAdding doc_id 2 to context.\u001b[0m\n", "\u001b[32mAdding doc_id 0 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 21 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 47 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 35 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 41 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 69 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 34 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 22 to context.\u001b[0m\n", - "\u001b[32mAdding doc_id 51 to context.\u001b[0m\n", + "\u001b[32mAdding doc_id 1 to context.\u001b[0m\n", "\u001b[33mragproxyagent\u001b[0m (to assistant):\n", "\n", "You're a retrieve augmented coding assistant. You answer user's questions based on your own knowledge and the\n", @@ -786,568 +763,268 @@ "\n", "User's question is: Who is the author of FLAML?\n", "\n", - "Context is: ---\n", - "sidebar_label: config\n", - "title: config\n", - "---\n", - "\n", - "!\n", - "* Copyright (c) Microsoft Corporation. All rights reserved.\n", - "* Licensed under the MIT License.\n", - "\n", - "#### PENALTY\n", - "\n", - "penalty term for constraints\n", - "\n", - "\n", - "---\n", - "sidebar_label: utils\n", - "title: automl.nlp.huggingface.utils\n", - "---\n", - "\n", - "#### todf\n", - "\n", - "```python\n", - "def todf(X, Y, column_name)\n", - "```\n", - "\n", - "todf converts Y from any format (list, pandas.Series, numpy array) to a DataFrame before being returned\n", - "\n", - "\n", - "---\n", - "sidebar_label: trial_scheduler\n", - "title: tune.scheduler.trial_scheduler\n", - "---\n", - "\n", - "## TrialScheduler Objects\n", - "\n", - "```python\n", - "class TrialScheduler()\n", - "```\n", - "\n", - "Interface for implementing a Trial Scheduler class.\n", - "\n", - "#### CONTINUE\n", - "\n", - "Status for continuing trial execution\n", - "\n", - "#### PAUSE\n", - "\n", - "Status for pausing trial execution\n", - "\n", - "#### STOP\n", - "\n", - "Status for stopping trial execution\n", + "Context is: # Research\n", "\n", + "For technical details, please check our research publications.\n", "\n", - "---\n", - "sidebar_label: space\n", - "title: tune.space\n", - "---\n", + "* [FLAML: A Fast and Lightweight AutoML Library](https://www.microsoft.com/en-us/research/publication/flaml-a-fast-and-lightweight-automl-library/). Chi Wang, Qingyun Wu, Markus Weimer, Erkang Zhu. MLSys 2021.\n", "\n", - "#### is\\_constant\n", - "\n", - "```python\n", - "def is_constant(space: Union[Dict, List]) -> bool\n", - "```\n", - "\n", - "Whether the search space is all constant.\n", - "\n", - "**Returns**:\n", - "\n", - " A bool of whether the search space is all constant.\n", - "\n", - "#### define\\_by\\_run\\_func\n", - "\n", - "```python\n", - "def define_by_run_func(trial,\n", - " space: Dict,\n", - " path: str = \"\") -> Optional[Dict[str, Any]]\n", - "```\n", - "\n", - "Define-by-run function to create the search space.\n", - "\n", - "**Returns**:\n", - "\n", - " A dict with constant values.\n", - "\n", - "#### unflatten\\_hierarchical\n", - "\n", - "```python\n", - "def unflatten_hierarchical(config: Dict, space: Dict) -> Tuple[Dict, Dict]\n", - "```\n", - "\n", - "Unflatten hierarchical config.\n", - "\n", - "#### add\\_cost\\_to\\_space\n", - "\n", - "```python\n", - "def add_cost_to_space(space: Dict, low_cost_point: Dict, choice_cost: Dict)\n", + "```bibtex\n", + "@inproceedings{wang2021flaml,\n", + " title={FLAML: A Fast and Lightweight AutoML Library},\n", + " author={Chi Wang and Qingyun Wu and Markus Weimer and Erkang Zhu},\n", + " year={2021},\n", + " booktitle={MLSys},\n", + "}\n", "```\n", "\n", - "Update the space in place by adding low_cost_point and choice_cost.\n", - "\n", - "**Returns**:\n", + "* [Frugal Optimization for Cost-related Hyperparameters](https://arxiv.org/abs/2005.01571). Qingyun Wu, Chi Wang, Silu Huang. AAAI 2021.\n", "\n", - " A dict with constant values.\n", - "\n", - "#### normalize\n", - "\n", - "```python\n", - "def normalize(config: Dict,\n", - " space: Dict,\n", - " reference_config: Dict,\n", - " normalized_reference_config: Dict,\n", - " recursive: bool = False)\n", + "```bibtex\n", + "@inproceedings{wu2021cfo,\n", + " title={Frugal Optimization for Cost-related Hyperparameters},\n", + " author={Qingyun Wu and Chi Wang and Silu Huang},\n", + " year={2021},\n", + " booktitle={AAAI},\n", + "}\n", "```\n", "\n", - "Normalize config in space according to reference_config.\n", - "\n", - "Normalize each dimension in config to [0,1].\n", - "\n", - "#### indexof\n", + "* [Economical Hyperparameter Optimization With Blended Search Strategy](https://www.microsoft.com/en-us/research/publication/economical-hyperparameter-optimization-with-blended-search-strategy/). Chi Wang, Qingyun Wu, Silu Huang, Amin Saied. ICLR 2021.\n", "\n", - "```python\n", - "def indexof(domain: Dict, config: Dict) -> int\n", + "```bibtex\n", + "@inproceedings{wang2021blendsearch,\n", + " title={Economical Hyperparameter Optimization With Blended Search Strategy},\n", + " author={Chi Wang and Qingyun Wu and Silu Huang and Amin Saied},\n", + " year={2021},\n", + " booktitle={ICLR},\n", + "}\n", "```\n", "\n", - "Find the index of config in domain.categories.\n", - "\n", - "#### complete\\_config\n", + "* [An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models](https://aclanthology.org/2021.acl-long.178.pdf). Susan Xueqing Liu, Chi Wang. ACL 2021.\n", "\n", - "```python\n", - "def complete_config(partial_config: Dict,\n", - " space: Dict,\n", - " flow2,\n", - " disturb: bool = False,\n", - " lower: Optional[Dict] = None,\n", - " upper: Optional[Dict] = None) -> Tuple[Dict, Dict]\n", + "```bibtex\n", + "@inproceedings{liuwang2021hpolm,\n", + " title={An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models},\n", + " author={Susan Xueqing Liu and Chi Wang},\n", + " year={2021},\n", + " booktitle={ACL},\n", + "}\n", "```\n", "\n", - "Complete partial config in space.\n", - "\n", - "**Returns**:\n", + "* [ChaCha for Online AutoML](https://www.microsoft.com/en-us/research/publication/chacha-for-online-automl/). Qingyun Wu, Chi Wang, John Langford, Paul Mineiro and Marco Rossi. ICML 2021.\n", "\n", - " config, space.\n", - "\n", - "\n", - "---\n", - "sidebar_label: search_thread\n", - "title: tune.searcher.search_thread\n", - "---\n", - "\n", - "## SearchThread Objects\n", - "\n", - "```python\n", - "class SearchThread()\n", + "```bibtex\n", + "@inproceedings{wu2021chacha,\n", + " title={ChaCha for Online AutoML},\n", + " author={Qingyun Wu and Chi Wang and John Langford and Paul Mineiro and Marco Rossi},\n", + " year={2021},\n", + " booktitle={ICML},\n", + "}\n", "```\n", "\n", - "Class of global or local search thread.\n", + "* [Fair AutoML](https://arxiv.org/abs/2111.06495). Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2111.06495 (2021).\n", "\n", - "#### \\_\\_init\\_\\_\n", - "\n", - "```python\n", - "def __init__(mode: str = \"min\",\n", - " search_alg: Optional[Searcher] = None,\n", - " cost_attr: Optional[str] = TIME_TOTAL_S,\n", - " eps: Optional[float] = 1.0)\n", + "```bibtex\n", + "@inproceedings{wuwang2021fairautoml,\n", + " title={Fair AutoML},\n", + " author={Qingyun Wu and Chi Wang},\n", + " year={2021},\n", + " booktitle={ArXiv preprint arXiv:2111.06495},\n", + "}\n", "```\n", "\n", - "When search_alg is omitted, use local search FLOW2.\n", + "* [Mining Robust Default Configurations for Resource-constrained AutoML](https://arxiv.org/abs/2202.09927). Moe Kayali, Chi Wang. ArXiv preprint arXiv:2202.09927 (2022).\n", "\n", - "#### suggest\n", - "\n", - "```python\n", - "def suggest(trial_id: str) -> Optional[Dict]\n", + "```bibtex\n", + "@inproceedings{kayaliwang2022default,\n", + " title={Mining Robust Default Configurations for Resource-constrained AutoML},\n", + " author={Moe Kayali and Chi Wang},\n", + " year={2022},\n", + " booktitle={ArXiv preprint arXiv:2202.09927},\n", + "}\n", "```\n", "\n", - "Use the suggest() of the underlying search algorithm.\n", - "\n", - "#### on\\_trial\\_complete\n", + "* [Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives](https://openreview.net/forum?id=0Ij9_q567Ma). Shaokun Zhang, Feiran Jia, Chi Wang, Qingyun Wu. ICLR 2023 (notable-top-5%).\n", "\n", - "```python\n", - "def on_trial_complete(trial_id: str,\n", - " result: Optional[Dict] = None,\n", - " error: bool = False)\n", + "```bibtex\n", + "@inproceedings{zhang2023targeted,\n", + " title={Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives},\n", + " author={Shaokun Zhang and Feiran Jia and Chi Wang and Qingyun Wu},\n", + " booktitle={International Conference on Learning Representations},\n", + " year={2023},\n", + " url={https://openreview.net/forum?id=0Ij9_q567Ma},\n", + "}\n", "```\n", "\n", - "Update the statistics of the thread.\n", - "\n", - "#### reach\n", + "* [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673). Chi Wang, Susan Xueqing Liu, Ahmed H. Awadallah. ArXiv preprint arXiv:2303.04673 (2023).\n", "\n", - "```python\n", - "def reach(thread) -> bool\n", + "```bibtex\n", + "@inproceedings{wang2023EcoOptiGen,\n", + " title={Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference},\n", + " author={Chi Wang and Susan Xueqing Liu and Ahmed H. Awadallah},\n", + " year={2023},\n", + " booktitle={ArXiv preprint arXiv:2303.04673},\n", + "}\n", "```\n", "\n", - "Whether the incumbent can reach the incumbent of thread.\n", - "\n", - "#### can\\_suggest\n", - "\n", - "```python\n", - "@property\n", - "def can_suggest() -> bool\n", - "```\n", + "* [An Empirical Study on Challenging Math Problem Solving with GPT-4](https://arxiv.org/abs/2306.01337). Yiran Wu, Feiran Jia, Shaokun Zhang, Hangyu Li, Erkang Zhu, Yue Wang, Yin Tat Lee, Richard Peng, Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2306.01337 (2023).\n", "\n", - "Whether the thread can suggest new configs.\n", - "\n", - "\n", - "{\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/autogen/agentchat/contrib/math_user_proxy_agent\",\n", - " \"reference/autogen/agentchat/contrib/retrieve_assistant_agent\",\n", - " \"reference/autogen/agentchat/contrib/retrieve_user_proxy_agent\"\n", - " ],\n", - " \"label\": \"autogen.agentchat.contrib\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/autogen/agentchat/agent\",\n", - " \"reference/autogen/agentchat/assistant_agent\",\n", - " \"reference/autogen/agentchat/conversable_agent\",\n", - " \"reference/autogen/agentchat/groupchat\",\n", - " \"reference/autogen/agentchat/user_proxy_agent\"\n", - " ],\n", - " \"label\": \"autogen.agentchat\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/autogen/oai/completion\",\n", - " \"reference/autogen/oai/openai_utils\"\n", - " ],\n", - " \"label\": \"autogen.oai\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/autogen/code_utils\",\n", - " \"reference/autogen/math_utils\",\n", - " \"reference/autogen/retrieve_utils\"\n", - " ],\n", - " \"label\": \"autogen\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/nlp/huggingface/trainer\",\n", - " \"reference/automl/nlp/huggingface/training_args\",\n", - " \"reference/automl/nlp/huggingface/utils\"\n", - " ],\n", - " \"label\": \"automl.nlp.huggingface\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/automl/nlp/utils\"\n", - " ],\n", - " \"label\": \"automl.nlp\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/spark/metrics\",\n", - " \"reference/automl/spark/utils\"\n", - " ],\n", - " \"label\": \"automl.spark\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/task/task\",\n", - " \"reference/automl/task/time_series_task\"\n", - " ],\n", - " \"label\": \"automl.task\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/automl/time_series/sklearn\",\n", - " \"reference/automl/time_series/tft\",\n", - " \"reference/automl/time_series/ts_data\",\n", - " \"reference/automl/time_series/ts_model\"\n", - " ],\n", - " \"label\": \"automl.time_series\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/automl/automl\",\n", - " \"reference/automl/data\",\n", - " \"reference/automl/ml\",\n", - " \"reference/automl/model\",\n", - " \"reference/automl/state\"\n", - " ],\n", - " \"label\": \"automl\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/default/estimator\",\n", - " \"reference/default/greedy\",\n", - " \"reference/default/portfolio\",\n", - " \"reference/default/suggest\"\n", - " ],\n", - " \"label\": \"default\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/onlineml/autovw\",\n", - " \"reference/onlineml/trial\",\n", - " \"reference/onlineml/trial_runner\"\n", - " ],\n", - " \"label\": \"onlineml\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/scheduler/online_scheduler\",\n", - " \"reference/tune/scheduler/trial_scheduler\"\n", - " ],\n", - " \"label\": \"tune.scheduler\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/searcher/blendsearch\",\n", - " \"reference/tune/searcher/cfo_cat\",\n", - " \"reference/tune/searcher/flow2\",\n", - " \"reference/tune/searcher/online_searcher\",\n", - " \"reference/tune/searcher/search_thread\",\n", - " \"reference/tune/searcher/suggestion\",\n", - " \"reference/tune/searcher/variant_generator\"\n", - " ],\n", - " \"label\": \"tune.searcher\",\n", - " \"type\": \"category\"\n", - " },\n", - " {\n", - " \"items\": [\n", - " \"reference/tune/spark/utils\"\n", - " ],\n", - " \"label\": \"tune.spark\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/tune/analysis\",\n", - " \"reference/tune/sample\",\n", - " \"reference/tune/space\",\n", - " \"reference/tune/trial\",\n", - " \"reference/tune/trial_runner\",\n", - " \"reference/tune/tune\",\n", - " \"reference/tune/utils\"\n", - " ],\n", - " \"label\": \"tune\",\n", - " \"type\": \"category\"\n", - " },\n", - " \"reference/config\"\n", - " ],\n", - " \"label\": \"Reference\",\n", - " \"type\": \"category\"\n", + "```bibtex\n", + "@inproceedings{wu2023empirical,\n", + " title={An Empirical Study on Challenging Math Problem Solving with GPT-4},\n", + " author={Yiran Wu and Feiran Jia and Shaokun Zhang and Hangyu Li and Erkang Zhu and Yue Wang and Yin Tat Lee and Richard Peng and Qingyun Wu and Chi Wang},\n", + " year={2023},\n", + " booktitle={ArXiv preprint arXiv:2306.01337},\n", "}\n", - "---\n", - "sidebar_label: utils\n", - "title: tune.utils\n", - "---\n", - "\n", - "#### choice\n", - "\n", - "```python\n", - "def choice(categories: Sequence, order=None)\n", "```\n", "\n", - "Sample a categorical value.\n", - "Sampling from ``tune.choice([1, 2])`` is equivalent to sampling from\n", - "``np.random.choice([1, 2])``\n", + "[![PyPI version](https://badge.fury.io/py/FLAML.svg)](https://badge.fury.io/py/FLAML)\n", + "![Conda version](https://img.shields.io/conda/vn/conda-forge/flaml)\n", + "[![Build](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml/badge.svg)](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml)\n", + "![Python Version](https://img.shields.io/badge/3.8%20%7C%203.9%20%7C%203.10-blue)\n", + "[![Downloads](https://pepy.tech/badge/flaml)](https://pepy.tech/project/flaml)\n", + "[![](https://img.shields.io/discord/1025786666260111483?logo=discord&style=flat)](https://discord.gg/Cppx2vSPVP)\n", + "\n", "\n", - "**Arguments**:\n", "\n", - "- `categories` _Sequence_ - Sequence of categories to sample from.\n", - "- `order` _bool_ - Whether the categories have an order. If None, will be decided autoamtically:\n", - " Numerical categories have an order, while string categories do not.\n", + "# A Fast Library for Automated Machine Learning & Tuning\n", "\n", + "

\n", + " \n", + "
\n", + "

\n", "\n", - "---\n", - "sidebar_label: trainer\n", - "title: automl.nlp.huggingface.trainer\n", - "---\n", + ":fire: Heads-up: We have migrated [AutoGen](https://microsoft.github.io/autogen/) into a dedicated [github repository](https://github.com/microsoft/autogen). Alongside this move, we have also launched a dedicated [Discord](https://discord.gg/pAbnFJrkgZ) server and a [website](https://microsoft.github.io/autogen/) for comprehensive documentation.\n", "\n", - "## TrainerForAuto Objects\n", + ":fire: The automated multi-agent chat framework in [AutoGen](https://microsoft.github.io/autogen/) is in preview from v2.0.0.\n", "\n", - "```python\n", - "class TrainerForAuto(Seq2SeqTrainer)\n", - "```\n", + ":fire: FLAML is highlighted in OpenAI's [cookbook](https://github.com/openai/openai-cookbook#related-resources-from-around-the-web).\n", "\n", - "#### evaluate\n", + ":fire: [autogen](https://microsoft.github.io/autogen/) is released with support for ChatGPT and GPT-4, based on [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673).\n", "\n", - "```python\n", - "def evaluate(eval_dataset=None, ignore_keys=None, metric_key_prefix=\"eval\")\n", - "```\n", + ":fire: FLAML supports Code-First AutoML & Tuning – Private Preview in [Microsoft Fabric Data Science](https://learn.microsoft.com/en-us/fabric/data-science/).\n", "\n", - "Overriding transformers.Trainer.evaluate by saving metrics and checkpoint path.\n", "\n", + "## What is FLAML\n", + "FLAML is a lightweight Python library for efficient automation of machine\n", + "learning and AI operations. It automates workflow based on large language models, machine learning models, etc.\n", + "and optimizes their performance.\n", "\n", - "---\n", - "sidebar_label: trial\n", - "title: onlineml.trial\n", - "---\n", + "* FLAML enables building next-gen GPT-X applications based on multi-agent conversations with minimal effort. It simplifies the orchestration, automation and optimization of a complex GPT-X workflow. It maximizes the performance of GPT-X models and augments their weakness.\n", + "* For common machine learning tasks like classification and regression, it quickly finds quality models for user-provided data with low computational resources. It is easy to customize or extend. Users can find their desired customizability from a smooth range.\n", + "* It supports fast and economical automatic tuning (e.g., inference hyperparameters for foundation models, configurations in MLOps/LMOps workflows, pipelines, mathematical/statistical models, algorithms, computing experiments, software configurations), capable of handling large search space with heterogeneous evaluation cost and complex constraints/guidance/early stopping.\n", "\n", - "#### get\\_ns\\_feature\\_dim\\_from\\_vw\\_example\n", + "FLAML is powered by a series of [research studies](https://microsoft.github.io/FLAML/docs/Research/) from Microsoft Research and collaborators such as Penn State University, Stevens Institute of Technology, University of Washington, and University of Waterloo.\n", "\n", - "```python\n", - "def get_ns_feature_dim_from_vw_example(vw_example) -> dict\n", - "```\n", + "FLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source, cross-platform machine learning framework for .NET.\n", "\n", - "Get a dictionary of feature dimensionality for each namespace singleton.\n", + "## Installation\n", "\n", - "## OnlineResult Objects\n", + "FLAML requires **Python version >= 3.8**. It can be installed from pip:\n", "\n", - "```python\n", - "class OnlineResult()\n", + "```bash\n", + "pip install flaml\n", "```\n", "\n", - "Class for managing the result statistics of a trial.\n", - "\n", - "#### CB\\_COEF\n", - "\n", - "0.001 for mse\n", - "\n", - "#### \\_\\_init\\_\\_\n", - "\n", - "```python\n", - "def __init__(result_type_name: str,\n", - " cb_coef: Optional[float] = None,\n", - " init_loss: Optional[float] = 0.0,\n", - " init_cb: Optional[float] = 100.0,\n", - " mode: Optional[str] = \"min\",\n", - " sliding_window_size: Optional[int] = 100)\n", + "Minimal dependencies are installed without extra options. You can install extra options based on the feature you need. For example, use the following to install the dependencies needed by the [`autogen`](https://microsoft.github.io/autogen/) package.\n", + "```bash\n", + "pip install \"flaml[autogen]\"\n", "```\n", "\n", - "Constructor.\n", + "Find more options in [Installation](https://microsoft.github.io/FLAML/docs/Installation).\n", + "Each of the [`notebook examples`](https://github.com/microsoft/FLAML/tree/main/notebook) may require a specific option to be installed.\n", "\n", - "**Arguments**:\n", - "\n", - "- `result_type_name` - A String to specify the name of the result type.\n", - "- `cb_coef` - a string to specify the coefficient on the confidence bound.\n", - "- `init_loss` - a float to specify the initial loss.\n", - "- `init_cb` - a float to specify the intial confidence bound.\n", - "- `mode` - A string in ['min', 'max'] to specify the objective as\n", - " minimization or maximization.\n", - "- `sliding_window_size` - An int to specify the size of the sliding window\n", - " (for experimental purpose).\n", - "\n", - "#### update\\_result\n", + "## Quickstart\n", "\n", + "* (New) The [autogen](https://microsoft.github.io/autogen/) package enables the next-gen GPT-X applications with a generic multi-agent conversation framework.\n", + "It offers customizable and conversable agents which integrate LLMs, tools and human.\n", + "By automating chat among multiple capable agents, one can easily make them collectively perform tasks autonomously or with human feedback, including tasks that require using tools via code. For example,\n", "```python\n", - "def update_result(new_loss,\n", - " new_resource_used,\n", - " data_dimension,\n", - " bound_of_range=1.0,\n", - " new_observation_count=1.0)\n", + "from flaml import autogen\n", + "assistant = autogen.AssistantAgent(\"assistant\")\n", + "user_proxy = autogen.UserProxyAgent(\"user_proxy\")\n", + "user_proxy.initiate_chat(assistant, message=\"Show me the YTD gain of 10 largest technology companies as of today.\")\n", + "# This initiates an automated chat between the two agents to solve the task\n", "```\n", "\n", - "Update result statistics.\n", - "\n", - "## BaseOnlineTrial Objects\n", - "\n", + "Autogen also helps maximize the utility out of the expensive LLMs such as ChatGPT and GPT-4. It offers a drop-in replacement of `openai.Completion` or `openai.ChatCompletion` with powerful functionalites like tuning, caching, templating, filtering. For example, you can optimize generations by LLM with your own tuning data, success metrics and budgets.\n", "```python\n", - "class BaseOnlineTrial(Trial)\n", + "# perform tuning\n", + "config, analysis = autogen.Completion.tune(\n", + " data=tune_data,\n", + " metric=\"success\",\n", + " mode=\"max\",\n", + " eval_func=eval_func,\n", + " inference_budget=0.05,\n", + " optimization_budget=3,\n", + " num_samples=-1,\n", + ")\n", + "# perform inference for a test instance\n", + "response = autogen.Completion.create(context=test_instance, **config)\n", "```\n", - "\n", - "Class for the online trial.\n", - "\n", - "#### \\_\\_init\\_\\_\n", + "* With three lines of code, you can start using this economical and fast\n", + "AutoML engine as a [scikit-learn style estimator](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML).\n", "\n", "```python\n", - "def __init__(config: dict,\n", - " min_resource_lease: float,\n", - " is_champion: Optional[bool] = False,\n", - " is_checked_under_current_champion: Optional[bool] = True,\n", - " custom_trial_name: Optional[str] = \"mae\",\n", - " trial_id: Optional[str] = None)\n", + "from flaml import AutoML\n", + "automl = AutoML()\n", + "automl.fit(X_train, y_train, task=\"classification\")\n", "```\n", "\n", - "Constructor.\n", - "\n", - "**Arguments**:\n", - "\n", - "- `config` - The configuration dictionary.\n", - "- `min_resource_lease` - A float specifying the minimum resource lease.\n", - "- `is_champion` - A bool variable indicating whether the trial is champion.\n", - "- `is_checked_under_current_champion` - A bool indicating whether the trial\n", - " has been used under the current champion.\n", - "- `custom_trial_name` - A string of a custom trial name.\n", - "- `trial_id` - A string for the trial id.\n", - "\n", - "#### set\\_resource\\_lease\n", + "* You can restrict the learners and use FLAML as a fast hyperparameter tuning\n", + "tool for XGBoost, LightGBM, Random Forest etc. or a [customized learner](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML#estimator-and-search-space).\n", "\n", "```python\n", - "def set_resource_lease(resource: float)\n", + "automl.fit(X_train, y_train, task=\"classification\", estimator_list=[\"lgbm\"])\n", "```\n", "\n", - "Sets the resource lease accordingly.\n", - "\n", - "#### set\\_status\n", + "* You can also run generic hyperparameter tuning for a [custom function](https://microsoft.github.io/FLAML/docs/Use-Cases/Tune-User-Defined-Function).\n", "\n", "```python\n", - "def set_status(status)\n", + "from flaml import tune\n", + "tune.run(evaluation_function, config={…}, low_cost_partial_config={…}, time_budget_s=3600)\n", "```\n", "\n", - "Sets the status of the trial and record the start time.\n", - "\n", - "## VowpalWabbitTrial Objects\n", + "* [Zero-shot AutoML](https://microsoft.github.io/FLAML/docs/Use-Cases/Zero-Shot-AutoML) allows using the existing training API from lightgbm, xgboost etc. while getting the benefit of AutoML in choosing high-performance hyperparameter configurations per task.\n", "\n", "```python\n", - "class VowpalWabbitTrial(BaseOnlineTrial)\n", + "from flaml.default import LGBMRegressor\n", + "\n", + "# Use LGBMRegressor in the same way as you use lightgbm.LGBMRegressor.\n", + "estimator = LGBMRegressor()\n", + "# The hyperparameters are automatically set according to the training data.\n", + "estimator.fit(X_train, y_train)\n", "```\n", "\n", - "The class for Vowpal Wabbit online trials.\n", + "## Documentation\n", "\n", - "#### \\_\\_init\\_\\_\n", + "You can find a detailed documentation about FLAML [here](https://microsoft.github.io/FLAML/).\n", "\n", - "```python\n", - "def __init__(config: dict,\n", - " min_resource_lease: float,\n", - " metric: str = \"mae\",\n", - " is_champion: Optional[bool] = False,\n", - " is_checked_under_current_champion: Optional[bool] = True,\n", - " custom_trial_name: Optional[str] = \"vw_mae_clipped\",\n", - " trial_id: Optional[str] = None,\n", - " cb_coef: Optional[float] = None)\n", - "```\n", + "In addition, you can find:\n", "\n", - "Constructor.\n", + "- [Research](https://microsoft.github.io/FLAML/docs/Research) and [blogposts](https://microsoft.github.io/FLAML/blog) around FLAML.\n", "\n", - "**Arguments**:\n", + "- [Discord](https://discord.gg/Cppx2vSPVP).\n", "\n", - "- `config` _dict_ - the config of the trial (note that the config is a set\n", - " because the hyperparameters are).\n", - "- `min_resource_lease` _float_ - the minimum resource lease.\n", - "- `metric` _str_ - the loss metric.\n", - "- `is_champion` _bool_ - indicates whether the trial is the current champion or not.\n", - "- `is_checked_under_current_champion` _bool_ - indicates whether this trials has\n", - " been paused under the current champion.\n", - "- `trial_id` _str_ - id of the trial (if None, it will be generated in the constructor).\n", + "- [Contributing guide](https://microsoft.github.io/FLAML/docs/Contribute).\n", "\n", - "#### train\\_eval\\_model\\_online\n", + "- ML.NET documentation and tutorials for [Model Builder](https://learn.microsoft.com/dotnet/machine-learning/tutorials/predict-prices-with-model-builder), [ML.NET CLI](https://learn.microsoft.com/dotnet/machine-learning/tutorials/sentiment-analysis-cli), and [AutoML API](https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/how-to-use-the-automl-api).\n", "\n", - "```python\n", - "def train_eval_model_online(data_sample, y_pred)\n", - "```\n", + "## Contributing\n", "\n", - "Train and evaluate model online.\n", + "This project welcomes contributions and suggestions. Most contributions require you to agree to a\n", + "Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\n", + "the rights to use your contribution. For details, visit .\n", "\n", - "#### predict\n", + "If you are new to GitHub [here](https://help.github.com/categories/collaborating-with-issues-and-pull-requests/) is a detailed help source on getting involved with development on GitHub.\n", "\n", - "```python\n", - "def predict(x)\n", - "```\n", - "\n", - "Predict using the model.\n", + "When you submit a pull request, a CLA bot will automatically determine whether you need to provide\n", + "a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\n", + "provided by the bot. You will only need to do this once across all repos using our CLA.\n", "\n", + "This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n", + "For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\n", + "contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n", "\n", "\n", "\n", @@ -1355,10 +1032,20 @@ "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to ragproxyagent):\n", "\n", - "The author of FLAML is Microsoft Corporation.\n", + "The author of FLAML is Chi Wang, along with other collaborators including Qingyun Wu, Markus Weimer, Erkang Zhu, Silu Huang, Amin Saied, Susan Xueqing Liu, John Langford, Paul Mineiro, Marco Rossi, Moe Kayali, Shaokun Zhang, Feiran Jia, Yiran Wu, Hangyu Li, Yue Wang, Yin Tat Lee, Richard Peng, and Ahmed H. Awadallah, as indicated in the provided references for FLAML's research publications.\n", "\n", "--------------------------------------------------------------------------------\n" ] + }, + { + "data": { + "text/plain": [ + "ChatResult(chat_id=None, chat_history=[{'content': 'You\\'re a retrieve augmented coding assistant. You answer user\\'s questions based on your own knowledge and the\\ncontext provided by the user.\\nIf you can\\'t answer the question with or without the current context, you should reply exactly `UPDATE CONTEXT`.\\nFor code generation, you must obey the following rules:\\nRule 1. You MUST NOT install any packages because all the packages needed are already installed.\\nRule 2. You must follow the formats below to write your code:\\n```language\\n# your code\\n```\\n\\nUser\\'s question is: Who is the author of FLAML?\\n\\nContext is: # Research\\n\\nFor technical details, please check our research publications.\\n\\n* [FLAML: A Fast and Lightweight AutoML Library](https://www.microsoft.com/en-us/research/publication/flaml-a-fast-and-lightweight-automl-library/). Chi Wang, Qingyun Wu, Markus Weimer, Erkang Zhu. MLSys 2021.\\n\\n```bibtex\\n@inproceedings{wang2021flaml,\\n title={FLAML: A Fast and Lightweight AutoML Library},\\n author={Chi Wang and Qingyun Wu and Markus Weimer and Erkang Zhu},\\n year={2021},\\n booktitle={MLSys},\\n}\\n```\\n\\n* [Frugal Optimization for Cost-related Hyperparameters](https://arxiv.org/abs/2005.01571). Qingyun Wu, Chi Wang, Silu Huang. AAAI 2021.\\n\\n```bibtex\\n@inproceedings{wu2021cfo,\\n title={Frugal Optimization for Cost-related Hyperparameters},\\n author={Qingyun Wu and Chi Wang and Silu Huang},\\n year={2021},\\n booktitle={AAAI},\\n}\\n```\\n\\n* [Economical Hyperparameter Optimization With Blended Search Strategy](https://www.microsoft.com/en-us/research/publication/economical-hyperparameter-optimization-with-blended-search-strategy/). Chi Wang, Qingyun Wu, Silu Huang, Amin Saied. ICLR 2021.\\n\\n```bibtex\\n@inproceedings{wang2021blendsearch,\\n title={Economical Hyperparameter Optimization With Blended Search Strategy},\\n author={Chi Wang and Qingyun Wu and Silu Huang and Amin Saied},\\n year={2021},\\n booktitle={ICLR},\\n}\\n```\\n\\n* [An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models](https://aclanthology.org/2021.acl-long.178.pdf). Susan Xueqing Liu, Chi Wang. ACL 2021.\\n\\n```bibtex\\n@inproceedings{liuwang2021hpolm,\\n title={An Empirical Study on Hyperparameter Optimization for Fine-Tuning Pre-trained Language Models},\\n author={Susan Xueqing Liu and Chi Wang},\\n year={2021},\\n booktitle={ACL},\\n}\\n```\\n\\n* [ChaCha for Online AutoML](https://www.microsoft.com/en-us/research/publication/chacha-for-online-automl/). Qingyun Wu, Chi Wang, John Langford, Paul Mineiro and Marco Rossi. ICML 2021.\\n\\n```bibtex\\n@inproceedings{wu2021chacha,\\n title={ChaCha for Online AutoML},\\n author={Qingyun Wu and Chi Wang and John Langford and Paul Mineiro and Marco Rossi},\\n year={2021},\\n booktitle={ICML},\\n}\\n```\\n\\n* [Fair AutoML](https://arxiv.org/abs/2111.06495). Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2111.06495 (2021).\\n\\n```bibtex\\n@inproceedings{wuwang2021fairautoml,\\n title={Fair AutoML},\\n author={Qingyun Wu and Chi Wang},\\n year={2021},\\n booktitle={ArXiv preprint arXiv:2111.06495},\\n}\\n```\\n\\n* [Mining Robust Default Configurations for Resource-constrained AutoML](https://arxiv.org/abs/2202.09927). Moe Kayali, Chi Wang. ArXiv preprint arXiv:2202.09927 (2022).\\n\\n```bibtex\\n@inproceedings{kayaliwang2022default,\\n title={Mining Robust Default Configurations for Resource-constrained AutoML},\\n author={Moe Kayali and Chi Wang},\\n year={2022},\\n booktitle={ArXiv preprint arXiv:2202.09927},\\n}\\n```\\n\\n* [Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives](https://openreview.net/forum?id=0Ij9_q567Ma). Shaokun Zhang, Feiran Jia, Chi Wang, Qingyun Wu. ICLR 2023 (notable-top-5%).\\n\\n```bibtex\\n@inproceedings{zhang2023targeted,\\n title={Targeted Hyperparameter Optimization with Lexicographic Preferences Over Multiple Objectives},\\n author={Shaokun Zhang and Feiran Jia and Chi Wang and Qingyun Wu},\\n booktitle={International Conference on Learning Representations},\\n year={2023},\\n url={https://openreview.net/forum?id=0Ij9_q567Ma},\\n}\\n```\\n\\n* [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673). Chi Wang, Susan Xueqing Liu, Ahmed H. Awadallah. ArXiv preprint arXiv:2303.04673 (2023).\\n\\n```bibtex\\n@inproceedings{wang2023EcoOptiGen,\\n title={Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference},\\n author={Chi Wang and Susan Xueqing Liu and Ahmed H. Awadallah},\\n year={2023},\\n booktitle={ArXiv preprint arXiv:2303.04673},\\n}\\n```\\n\\n* [An Empirical Study on Challenging Math Problem Solving with GPT-4](https://arxiv.org/abs/2306.01337). Yiran Wu, Feiran Jia, Shaokun Zhang, Hangyu Li, Erkang Zhu, Yue Wang, Yin Tat Lee, Richard Peng, Qingyun Wu, Chi Wang. ArXiv preprint arXiv:2306.01337 (2023).\\n\\n```bibtex\\n@inproceedings{wu2023empirical,\\n title={An Empirical Study on Challenging Math Problem Solving with GPT-4},\\n author={Yiran Wu and Feiran Jia and Shaokun Zhang and Hangyu Li and Erkang Zhu and Yue Wang and Yin Tat Lee and Richard Peng and Qingyun Wu and Chi Wang},\\n year={2023},\\n booktitle={ArXiv preprint arXiv:2306.01337},\\n}\\n```\\n\\n[![PyPI version](https://badge.fury.io/py/FLAML.svg)](https://badge.fury.io/py/FLAML)\\n![Conda version](https://img.shields.io/conda/vn/conda-forge/flaml)\\n[![Build](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml/badge.svg)](https://github.com/microsoft/FLAML/actions/workflows/python-package.yml)\\n![Python Version](https://img.shields.io/badge/3.8%20%7C%203.9%20%7C%203.10-blue)\\n[![Downloads](https://pepy.tech/badge/flaml)](https://pepy.tech/project/flaml)\\n[![](https://img.shields.io/discord/1025786666260111483?logo=discord&style=flat)](https://discord.gg/Cppx2vSPVP)\\n\\n\\n\\n# A Fast Library for Automated Machine Learning & Tuning\\n\\n

\\n \\n
\\n

\\n\\n:fire: Heads-up: We have migrated [AutoGen](https://microsoft.github.io/autogen/) into a dedicated [github repository](https://github.com/microsoft/autogen). Alongside this move, we have also launched a dedicated [Discord](https://discord.gg/pAbnFJrkgZ) server and a [website](https://microsoft.github.io/autogen/) for comprehensive documentation.\\n\\n:fire: The automated multi-agent chat framework in [AutoGen](https://microsoft.github.io/autogen/) is in preview from v2.0.0.\\n\\n:fire: FLAML is highlighted in OpenAI\\'s [cookbook](https://github.com/openai/openai-cookbook#related-resources-from-around-the-web).\\n\\n:fire: [autogen](https://microsoft.github.io/autogen/) is released with support for ChatGPT and GPT-4, based on [Cost-Effective Hyperparameter Optimization for Large Language Model Generation Inference](https://arxiv.org/abs/2303.04673).\\n\\n:fire: FLAML supports Code-First AutoML & Tuning – Private Preview in [Microsoft Fabric Data Science](https://learn.microsoft.com/en-us/fabric/data-science/).\\n\\n\\n## What is FLAML\\nFLAML is a lightweight Python library for efficient automation of machine\\nlearning and AI operations. It automates workflow based on large language models, machine learning models, etc.\\nand optimizes their performance.\\n\\n* FLAML enables building next-gen GPT-X applications based on multi-agent conversations with minimal effort. It simplifies the orchestration, automation and optimization of a complex GPT-X workflow. It maximizes the performance of GPT-X models and augments their weakness.\\n* For common machine learning tasks like classification and regression, it quickly finds quality models for user-provided data with low computational resources. It is easy to customize or extend. Users can find their desired customizability from a smooth range.\\n* It supports fast and economical automatic tuning (e.g., inference hyperparameters for foundation models, configurations in MLOps/LMOps workflows, pipelines, mathematical/statistical models, algorithms, computing experiments, software configurations), capable of handling large search space with heterogeneous evaluation cost and complex constraints/guidance/early stopping.\\n\\nFLAML is powered by a series of [research studies](https://microsoft.github.io/FLAML/docs/Research/) from Microsoft Research and collaborators such as Penn State University, Stevens Institute of Technology, University of Washington, and University of Waterloo.\\n\\nFLAML has a .NET implementation in [ML.NET](http://dot.net/ml), an open-source, cross-platform machine learning framework for .NET.\\n\\n## Installation\\n\\nFLAML requires **Python version >= 3.8**. It can be installed from pip:\\n\\n```bash\\npip install flaml\\n```\\n\\nMinimal dependencies are installed without extra options. You can install extra options based on the feature you need. For example, use the following to install the dependencies needed by the [`autogen`](https://microsoft.github.io/autogen/) package.\\n```bash\\npip install \"flaml[autogen]\"\\n```\\n\\nFind more options in [Installation](https://microsoft.github.io/FLAML/docs/Installation).\\nEach of the [`notebook examples`](https://github.com/microsoft/FLAML/tree/main/notebook) may require a specific option to be installed.\\n\\n## Quickstart\\n\\n* (New) The [autogen](https://microsoft.github.io/autogen/) package enables the next-gen GPT-X applications with a generic multi-agent conversation framework.\\nIt offers customizable and conversable agents which integrate LLMs, tools and human.\\nBy automating chat among multiple capable agents, one can easily make them collectively perform tasks autonomously or with human feedback, including tasks that require using tools via code. For example,\\n```python\\nfrom flaml import autogen\\nassistant = autogen.AssistantAgent(\"assistant\")\\nuser_proxy = autogen.UserProxyAgent(\"user_proxy\")\\nuser_proxy.initiate_chat(assistant, message=\"Show me the YTD gain of 10 largest technology companies as of today.\")\\n# This initiates an automated chat between the two agents to solve the task\\n```\\n\\nAutogen also helps maximize the utility out of the expensive LLMs such as ChatGPT and GPT-4. It offers a drop-in replacement of `openai.Completion` or `openai.ChatCompletion` with powerful functionalites like tuning, caching, templating, filtering. For example, you can optimize generations by LLM with your own tuning data, success metrics and budgets.\\n```python\\n# perform tuning\\nconfig, analysis = autogen.Completion.tune(\\n data=tune_data,\\n metric=\"success\",\\n mode=\"max\",\\n eval_func=eval_func,\\n inference_budget=0.05,\\n optimization_budget=3,\\n num_samples=-1,\\n)\\n# perform inference for a test instance\\nresponse = autogen.Completion.create(context=test_instance, **config)\\n```\\n* With three lines of code, you can start using this economical and fast\\nAutoML engine as a [scikit-learn style estimator](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML).\\n\\n```python\\nfrom flaml import AutoML\\nautoml = AutoML()\\nautoml.fit(X_train, y_train, task=\"classification\")\\n```\\n\\n* You can restrict the learners and use FLAML as a fast hyperparameter tuning\\ntool for XGBoost, LightGBM, Random Forest etc. or a [customized learner](https://microsoft.github.io/FLAML/docs/Use-Cases/Task-Oriented-AutoML#estimator-and-search-space).\\n\\n```python\\nautoml.fit(X_train, y_train, task=\"classification\", estimator_list=[\"lgbm\"])\\n```\\n\\n* You can also run generic hyperparameter tuning for a [custom function](https://microsoft.github.io/FLAML/docs/Use-Cases/Tune-User-Defined-Function).\\n\\n```python\\nfrom flaml import tune\\ntune.run(evaluation_function, config={…}, low_cost_partial_config={…}, time_budget_s=3600)\\n```\\n\\n* [Zero-shot AutoML](https://microsoft.github.io/FLAML/docs/Use-Cases/Zero-Shot-AutoML) allows using the existing training API from lightgbm, xgboost etc. while getting the benefit of AutoML in choosing high-performance hyperparameter configurations per task.\\n\\n```python\\nfrom flaml.default import LGBMRegressor\\n\\n# Use LGBMRegressor in the same way as you use lightgbm.LGBMRegressor.\\nestimator = LGBMRegressor()\\n# The hyperparameters are automatically set according to the training data.\\nestimator.fit(X_train, y_train)\\n```\\n\\n## Documentation\\n\\nYou can find a detailed documentation about FLAML [here](https://microsoft.github.io/FLAML/).\\n\\nIn addition, you can find:\\n\\n- [Research](https://microsoft.github.io/FLAML/docs/Research) and [blogposts](https://microsoft.github.io/FLAML/blog) around FLAML.\\n\\n- [Discord](https://discord.gg/Cppx2vSPVP).\\n\\n- [Contributing guide](https://microsoft.github.io/FLAML/docs/Contribute).\\n\\n- ML.NET documentation and tutorials for [Model Builder](https://learn.microsoft.com/dotnet/machine-learning/tutorials/predict-prices-with-model-builder), [ML.NET CLI](https://learn.microsoft.com/dotnet/machine-learning/tutorials/sentiment-analysis-cli), and [AutoML API](https://learn.microsoft.com/dotnet/machine-learning/how-to-guides/how-to-use-the-automl-api).\\n\\n## Contributing\\n\\nThis project welcomes contributions and suggestions. Most contributions require you to agree to a\\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\\nthe rights to use your contribution. For details, visit .\\n\\nIf you are new to GitHub [here](https://help.github.com/categories/collaborating-with-issues-and-pull-requests/) is a detailed help source on getting involved with development on GitHub.\\n\\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\\nprovided by the bot. You will only need to do this once across all repos using our CLA.\\n\\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\\n\\n\\n', 'role': 'assistant'}, {'content': \"The author of FLAML is Chi Wang, along with other collaborators including Qingyun Wu, Markus Weimer, Erkang Zhu, Silu Huang, Amin Saied, Susan Xueqing Liu, John Langford, Paul Mineiro, Marco Rossi, Moe Kayali, Shaokun Zhang, Feiran Jia, Yiran Wu, Hangyu Li, Yue Wang, Yin Tat Lee, Richard Peng, and Ahmed H. Awadallah, as indicated in the provided references for FLAML's research publications.\", 'role': 'user'}], summary=\"The author of FLAML is Chi Wang, along with other collaborators including Qingyun Wu, Markus Weimer, Erkang Zhu, Silu Huang, Amin Saied, Susan Xueqing Liu, John Langford, Paul Mineiro, Marco Rossi, Moe Kayali, Shaokun Zhang, Feiran Jia, Yiran Wu, Hangyu Li, Yue Wang, Yin Tat Lee, Richard Peng, and Ahmed H. Awadallah, as indicated in the provided references for FLAML's research publications.\", cost=({'total_cost': 0.11538, 'gpt-4': {'cost': 0.11538, 'prompt_tokens': 3632, 'completion_tokens': 107, 'total_tokens': 3739}}, {'total_cost': 0}), human_input=[])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -1366,7 +1053,7 @@ "assistant.reset()\n", "\n", "qa_problem = \"Who is the author of FLAML?\"\n", - "ragproxyagent.initiate_chat(assistant, problem=qa_problem)" + "ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=qa_problem)" ] } ], @@ -1386,7 +1073,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/notebook/agentchats_sequential_chats.ipynb b/notebook/agentchats_sequential_chats.ipynb index 9b590405068c..c10e72ad8850 100644 --- a/notebook/agentchats_sequential_chats.ipynb +++ b/notebook/agentchats_sequential_chats.ipynb @@ -9,7 +9,7 @@ } }, "source": [ - "# Solving Multiple Tasks in a Sequence of Chats\n", + "# Solving Multiple Tasks in a Sequence of Chats with Different Conversable Agent Pairs\n", "\n", "This notebook showcases how to use the new chat interface `autogen.initiate_chats` to solve a set of tasks with a sequence of chats. \n", "\n", @@ -75,16 +75,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Solve tasks with a series of chats\n", + "### Example 1: Solve tasks with a series of chats\n", "\n", "The `autogen.initiate_chats` interface can take a list of dictionaries as inputs. Each dictionary preserves the following fields: \n", "- `sender`: a conversable agent as the sender;\n", "- `recipient`: a conversable agent as the recipient;\n", - "- `message`: is a string of text (typically a message containing the task);\n", - "- `summary_method`: A string specifying the method to get a summary from the chat. Currently supported choices include `last_msg`, which takes the last message from the chat history as the summary, and `reflection_with_llm`, which uses an LLM call to reflect on the chat history and summarize a takeaway;\n", - "- `summary_prompt`: A string specifying how to instruct an LLM-backed agent (either the recipient or the sender in the chat) to reflect on the chat history and derive a summary. If not otherwise specified, a default prompt will be used when `summary_method` is `reflection_with_llm`.\n", - "\"Summarize the takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out.\"\n", - "- `carryover`: A string or a list of string to specify additional context to be used in the chat. With `initiate_chats`, summary from previous chats will be added as carryover. They will be appended after the carryover provided by the user." + "- `message`: is a string of text (typically a message containing the task) or a callable;\n", + "- `summary_method`: A string or a callable to get a summary from the chat. Currently supported choices include `last_msg`, which takes the last message from the chat history as the summary, and `reflection_with_llm`, which uses an LLM call to reflect on the chat history and summarize a takeaway;" ] }, { @@ -818,6 +815,312 @@ " ), f\"The chat history should contain at most 4 messages because max_turns is set to 2 in the {i}-th chat.\"\n", " print(\"\\n\\n\")" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Solve a Sequence of Tasks involving User Defined Message\n", + "\n", + "In this example, say I have two tasks. One resarch task and a one writing task. The writing task needs data from research task. In this example, we direct read data from a file as part of the message." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "research_task = \"\"\"What are daily stock prices of NVDA and TESLA in the past month. Save the results in a .md file named 'stock_prices.md'.\"\"\"\n", + "\n", + "\n", + "def my_writing_task(sender, recipient, context):\n", + " carryover = context.get(\"carryover\", \"\")\n", + " if isinstance(carryover, list):\n", + " carryover = carryover[-1]\n", + "\n", + " try:\n", + " filename = context.get(\"work_dir\", \"\") + \"/stock_prices.md\"\n", + " with open(filename, \"r\") as file:\n", + " data = file.read()\n", + " except Exception as e:\n", + " data = f\"An error occurred while reading the file: {e}\"\n", + "\n", + " return (\n", + " \"\"\"Develop an engaging blog post using any information provided. \"\"\"\n", + " + \"\\nContext:\\n\"\n", + " + carryover\n", + " + \"\\nData:\"\n", + " + data\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStarting a new chat....\n", + "\n", + "Message:\n", + "What are daily stock prices of NVDA and TESLA in the past month. Save the results in a .md file named 'stock_prices.md'.\n", + "\n", + "Carryover: \n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mUser_Proxy_Auto\u001b[0m (to Financial_researcher):\n", + "\n", + "What are daily stock prices of NVDA and TESLA in the past month. Save the results in a .md file named 'stock_prices.md'.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mFinancial_researcher\u001b[0m (to User_Proxy_Auto):\n", + "\n", + "To accomplish this task, I will write a Python script that utilizes an API to fetch the daily stock prices of NVIDIA (NVDA) and Tesla (TSLA) over the past month. I will then format the data into Markdown and save it to a file named 'stock_prices.md'.\n", + "\n", + "Step 1: Fetch the stock price data.\n", + "Step 2: Format the data into Markdown.\n", + "Step 3: Save the data to 'stock_prices.md'.\n", + "\n", + "We'll use the `yfinance` library to fetch the stock data. It's a popular choice for financial data extraction. If you don't have `yfinance` installed, you would need to install it via pip (`!pip install yfinance` in Jupyter notebooks or `pip install yfinance` in the command line). \n", + "\n", + "Let's start with step 1 by writing a script to collect the stock price data of NVDA and TSLA.\n", + "\n", + "```python\n", + "# filename: fetch_stock_data.py\n", + "\n", + "import yfinance as yf\n", + "from datetime import datetime, timedelta\n", + "\n", + "# Function to fetch the stock data\n", + "def fetch_stock_data(ticker):\n", + " end_date = datetime.now()\n", + " start_date = end_date - timedelta(days=30)\n", + " \n", + " # Fetching the historical data\n", + " stock_data = yf.download(ticker, start=start_date, end=end_date)\n", + " \n", + " # Formatting the date to a more readable form\n", + " stock_data.index = stock_data.index.strftime('%Y-%m-%d')\n", + " \n", + " return stock_data\n", + "\n", + "# Fetching data for NVDA and TSLA\n", + "nvda_data = fetch_stock_data('NVDA')\n", + "tesla_data = fetch_stock_data('TSLA')\n", + "\n", + "# Formatting the data into Markdown\n", + "with open('stock_prices.md', 'w') as md_file:\n", + " md_file.write('# Stock Prices for NVDA and TSLA in the Past Month\\n\\n')\n", + " md_file.write('## NVDA\\n\\n')\n", + " md_file.write(nvda_data['Close'].to_markdown() + '\\n\\n')\n", + " md_file.write('## TSLA\\n\\n')\n", + " md_file.write(tesla_data['Close'].to_markdown() + '\\n\\n')\n", + "\n", + "print(\"Stock prices fetched and saved to 'stock_prices.md'.\")\n", + "```\n", + "\n", + "Execute this script after ensuring that `yfinance` is installed. It will fetch the data and save it to 'stock_prices.md'.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", + "\u001b[33mUser_Proxy_Auto\u001b[0m (to Financial_researcher):\n", + "\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "Stock prices fetched and saved to 'stock_prices.md'.\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mFinancial_researcher\u001b[0m (to User_Proxy_Auto):\n", + "\n", + "The script execution was successful, and it has fetched the stock prices for NVDA and TSLA for the past month and saved the data in a file named 'stock_prices.md'.\n", + "\n", + "You should now have a Markdown file with the stock prices. If you need further assistance or additional tasks, feel free to ask.\n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStarting a new chat....\n", + "\n", + "Message:\n", + "Callable: my_writing_task\n", + "\n", + "Carryover: \n", + "The script execution was successful, and it has fetched the stock prices for NVDA and TSLA for the past month and saved the data in a file named 'stock_prices.md'.\n", + "\n", + "You should now have a Markdown file with the stock prices. If you need further assistance or additional tasks, feel free to ask.\n", + "\n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mUser_Proxy_Auto\u001b[0m (to Writer):\n", + "\n", + "Develop an engaging blog post using any information provided. \n", + "Context:\n", + "The script execution was successful, and it has fetched the stock prices for NVDA and TSLA for the past month and saved the data in a file named 'stock_prices.md'.\n", + "\n", + "You should now have a Markdown file with the stock prices. If you need further assistance or additional tasks, feel free to ask.\n", + "\n", + "\n", + "Data:# Stock Prices for NVDA and TSLA in the Past Month\n", + "\n", + "## NVDA\n", + "\n", + "| Date | Close |\n", + "|:-----------|--------:|\n", + "| 2024-02-02 | 661.6 |\n", + "| 2024-02-05 | 693.32 |\n", + "| 2024-02-06 | 682.23 |\n", + "| 2024-02-07 | 700.99 |\n", + "| 2024-02-08 | 696.41 |\n", + "| 2024-02-09 | 721.33 |\n", + "| 2024-02-12 | 722.48 |\n", + "| 2024-02-13 | 721.28 |\n", + "| 2024-02-14 | 739 |\n", + "| 2024-02-15 | 726.58 |\n", + "| 2024-02-16 | 726.13 |\n", + "| 2024-02-20 | 694.52 |\n", + "| 2024-02-21 | 674.72 |\n", + "| 2024-02-22 | 785.38 |\n", + "| 2024-02-23 | 788.17 |\n", + "| 2024-02-26 | 790.92 |\n", + "| 2024-02-27 | 787.01 |\n", + "| 2024-02-28 | 776.63 |\n", + "| 2024-02-29 | 791.12 |\n", + "| 2024-03-01 | 822.79 |\n", + "\n", + "## TSLA\n", + "\n", + "| Date | Close |\n", + "|:-----------|--------:|\n", + "| 2024-02-02 | 187.91 |\n", + "| 2024-02-05 | 181.06 |\n", + "| 2024-02-06 | 185.1 |\n", + "| 2024-02-07 | 187.58 |\n", + "| 2024-02-08 | 189.56 |\n", + "| 2024-02-09 | 193.57 |\n", + "| 2024-02-12 | 188.13 |\n", + "| 2024-02-13 | 184.02 |\n", + "| 2024-02-14 | 188.71 |\n", + "| 2024-02-15 | 200.45 |\n", + "| 2024-02-16 | 199.95 |\n", + "| 2024-02-20 | 193.76 |\n", + "| 2024-02-21 | 194.77 |\n", + "| 2024-02-22 | 197.41 |\n", + "| 2024-02-23 | 191.97 |\n", + "| 2024-02-26 | 199.4 |\n", + "| 2024-02-27 | 199.73 |\n", + "| 2024-02-28 | 202.04 |\n", + "| 2024-02-29 | 201.88 |\n", + "| 2024-03-01 | 202.64 |\n", + "\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mWriter\u001b[0m (to User_Proxy_Auto):\n", + "\n", + "# A Rollercoaster Month: The Tale of NVDA and TSLA Stock Prices\n", + "\n", + "Welcome to our latest analysis where we bring the ups and downs of the stock market to life. If you've been keeping an eye on the tech industry, you've probably noticed the electrifying performance of two market giants: NVIDIA (NVDA) and Tesla (TSLA). Over the past month, these stocks have taken investors on quite the ride, and we're here to break down the twists and turns.\n", + "\n", + "## NVIDIA: A Journey Through the Digital Realm\n", + "\n", + "NVIDIA, the powerhouse behind some of the most advanced graphics processing technologies, began February modestly at $661.60. However, it didn't take long for this tech titan to start climbing. By February 5, we saw NVDA accelerating to a cool $693.32, hinting at the potential for a thrilling month ahead. The momentum wasn't constant, though, with minor dips, like the one on February 6th to $682.23, reminding shareholders that the journey wouldn't be without its bumps.\n", + "\n", + "Midway through the month, there came a slight calm before the storm, as the stock hovered around $720, suggesting that something big was on the horizon. And true to form, on February 22nd, NVIDIA took everyone by surprise as it surged to an impressive $785.38.\n", + "\n", + "The climax of this exhilarating ride came as February gave way to March, with NVIDIA stock skyrocketing to an apex of $822.79. Investors buckled in tight surely enjoyed the view from this peak.\n", + "\n", + "## Tesla: Charging Ahead with Volatility\n", + "\n", + "Tesla, well-loved and -debated for its visionary approach to automotive technology, also delivered its fair share of market excitement. Starting on a lower key at $187.91, TSLA had a more modest opening than NVDA but held promise for an interesting chapter.\n", + "\n", + "The stock dropped to a monthly low of $181.06 on February 5, causing a bit of heart-in-mouth for supporters. Yet, in classic Tesla fashion, the company steered back onto the track, recovering to $185.10 the next day. Over the next few days, Tesla continued to inch higher, reaching $189.56 by February 8th.\n", + "\n", + "Despite a temporary retreat mid-month, where the stock dipped to $188.13, the electric automaker charged back with vigor, breaking the $200 barrier and hitting a high of $200.45 on February 15th - giving investors a much-needed adrenaline boost.\n", + "\n", + "As February ended and March began, Tesla maintained a price over $200, somewhat steadying the wheel for a close at $202.64, a reassuring end to a month of market fluctuations.\n", + "\n", + "## What's Behind the Movements?\n", + "\n", + "While the numbers tell one story, they don't explain the 'why' behind these market rides. For NVIDIA, a series of positive earnings reports, strategic partnerships, and strong demand for gaming and server GPUs contributed to its stock gains. For Tesla, market sentiment often hinged on production milestones, regulatory news, and the ever-present Twitter activity of its CEO, Elon Musk.\n", + "\n", + "It's important to remember that the stock market is influenced by a complex web of factors, including investor sentiment, industry trends, geopolitical events, and economic indicators. As such, companies like NVIDIA and Tesla don't just operate in a vacuum—every shift in the market can be an echo of larger global narratives.\n", + "\n", + "## An Investor's Perspective\n", + "\n", + "The past month's performance of NVDA and TSLA provides valuable lessons for investors. It showcases the importance of staying informed and understanding that volatility is part and parcel of the investing experience, especially in sectors like technology where innovation is rapid and competition fierce.\n", + "\n", + "While these price swings may cause some to balk, seasoned stock market enthusiasts know that these fluctuations can present opportunities. Whether leveraging these movements for short-term gains or buckling in for the long haul, understanding the story behind the numbers is crucial.\n", + "\n", + "In conclusion, the tale of NVDA and TSLA over the past month has been nothing short of a nail-biter. As we keep our eyes glued to the screens for the next series of movements, always remember: investing is not just about the figures; it's about seeing the whole picture and appreciating every twist and turn of the story that unfolds.\n", + "\n", + "Stay tuned, fasten your seatbelts, and until next time, may your portfolio's story be equally thrilling and rewarding.\n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "researcher = autogen.AssistantAgent(\n", + " name=\"Financial_researcher\",\n", + " llm_config=llm_config,\n", + ")\n", + "writer = autogen.AssistantAgent(\n", + " name=\"Writer\",\n", + " llm_config=llm_config,\n", + " system_message=\"\"\"\n", + " You are a professional writer, known for\n", + " your insightful and engaging articles.\n", + " You transform complex concepts into compelling narratives.\n", + " Reply \"TERMINATE\" in the end when everything is done.\n", + " \"\"\",\n", + ")\n", + "\n", + "user_proxy_auto = autogen.UserProxyAgent(\n", + " name=\"User_Proxy_Auto\",\n", + " human_input_mode=\"NEVER\",\n", + " is_termination_msg=lambda x: x.get(\"content\", \"\") and x.get(\"content\", \"\").rstrip().endswith(\"TERMINATE\"),\n", + " code_execution_config={\n", + " \"last_n_messages\": 1,\n", + " \"work_dir\": \"tasks\",\n", + " \"use_docker\": False,\n", + " }, # 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", + "\n", + "chat_results = autogen.initiate_chats(\n", + " [\n", + " {\n", + " \"sender\": user_proxy_auto,\n", + " \"recipient\": researcher,\n", + " \"message\": research_task,\n", + " \"clear_history\": True,\n", + " \"silent\": False,\n", + " \"summary_method\": \"last_msg\",\n", + " },\n", + " {\n", + " \"sender\": user_proxy_auto,\n", + " \"recipient\": writer,\n", + " \"message\": my_writing_task,\n", + " \"max_turns\": 2, # max number of turns for the conversation (added for demo purposes, generally not necessarily needed)\n", + " \"summary_method\": \"reflection_with_llm\",\n", + " \"work_dir\": \"tasks\",\n", + " },\n", + " ]\n", + ")" + ] } ], "metadata": { diff --git a/test/agentchat/test_assistant_agent.py b/test/agentchat/test_assistant_agent.py index c79780b3069e..b99ed5766fb9 100755 --- a/test/agentchat/test_assistant_agent.py +++ b/test/agentchat/test_assistant_agent.py @@ -173,32 +173,36 @@ def test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10): "Can we add a new point to the graph? It's distance should be randomly between 0 - 5 to each of the existing points.", ] - class TSPUserProxyAgent(UserProxyAgent): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - with open(f"{here}/tsp_prompt.txt", "r") as f: - self._prompt = f.read() - - def generate_init_message(self, question) -> str: - return self._prompt.format(question=question) + def tsp_message(sender, recipient, context): + filename = context.get("prompt_filename", "") + with open(filename, "r") as f: + prompt = f.read() + question = context.get("question", "") + return prompt.format(question=question) # autogen.ChatCompletion.start_logging() assistant = AssistantAgent("assistant", llm_config={"temperature": 0, "config_list": config_list}) - user = TSPUserProxyAgent( + user = UserProxyAgent( "user", - code_execution_config={"work_dir": here}, + code_execution_config={ + "work_dir": here, + }, human_input_mode=human_input_mode, max_consecutive_auto_reply=max_consecutive_auto_reply, ) - user.initiate_chat(assistant, question=hard_questions[2]) + chat_res = user.initiate_chat( + assistant, message=tsp_message, question=hard_questions[2], prompt_filename=f"{here}/tsp_prompt.txt" + ) # print(autogen.ChatCompletion.logged_history) # autogen.ChatCompletion.stop_logging() + # print(chat_res.summary) + print(chat_res.cost) if __name__ == "__main__": - test_gpt35() + # test_gpt35() # test_create_execute_script(human_input_mode="TERMINATE") # when GPT-4, i.e., the DEFAULT_MODEL, is used, conversation in the following test # should terminate in 2-3 rounds of interactions (because is_termination_msg should be true after 2-3 rounds) # although the max_consecutive_auto_reply is set to 10. - # test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) + test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) diff --git a/test/agentchat/test_chats.py b/test/agentchat/test_chats.py index 07e279910d7f..7f4a42cac6f3 100755 --- a/test/agentchat/test_chats.py +++ b/test/agentchat/test_chats.py @@ -491,10 +491,93 @@ def currency_calculator( print(res.summary, res.cost, res.chat_history) +@pytest.mark.skipif(skip_openai, reason="requested to skip openai tests") +def test_udf_message_in_chats(): + import autogen + + config_list = autogen.config_list_from_json(env_or_file="OAI_CONFIG_LIST") + llm_config = {"config_list": config_list} + + research_task = """ + ## NVDA (NVIDIA Corporation) + - Current Stock Price: $822.79 + - Performance over the past month: 24.36% + + ## TSLA (Tesla, Inc.) + - Current Stock Price: $202.64 + - Performance over the past month: 7.84% + + Save them to a file named stock_prices.md. + """ + + def my_writing_task(sender, recipient, context): + carryover = context.get("carryover", "") + if isinstance(carryover, list): + carryover = carryover[-1] + + try: + filename = context.get("work_dir", "") + "/stock_prices.md" + with open(filename, "r") as file: + data = file.read() + except Exception as e: + data = f"An error occurred while reading the file: {e}" + + return """Make a joke. """ + "\nContext:\n" + carryover + "\nData:" + data + + researcher = autogen.AssistantAgent( + name="Financial_researcher", + llm_config=llm_config, + ) + writer = autogen.AssistantAgent( + name="Writer", + llm_config=llm_config, + system_message=""" + You are a professional writer, known for + your insightful and engaging articles. + You transform complex concepts into compelling narratives. + Reply "TERMINATE" in the end when everything is done. + """, + ) + + user_proxy_auto = autogen.UserProxyAgent( + name="User_Proxy_Auto", + human_input_mode="NEVER", + is_termination_msg=lambda x: x.get("content", "") and x.get("content", "").rstrip().endswith("TERMINATE"), + code_execution_config={ + "last_n_messages": 1, + "work_dir": "tasks", + "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. + ) + + chat_results = autogen.initiate_chats( + [ + { + "sender": user_proxy_auto, + "recipient": researcher, + "message": research_task, + "clear_history": True, + "silent": False, + }, + { + "sender": user_proxy_auto, + "recipient": writer, + "message": my_writing_task, + "max_turns": 2, # max number of turns for the conversation (added for demo purposes, generally not necessarily needed) + "summary_method": "reflection_with_llm", + "work_dir": "tasks", + }, + ] + ) + print(chat_results[0].summary, chat_results[0].cost) + print(chat_results[1].summary, chat_results[1].cost) + + if __name__ == "__main__": - test_chats() - test_chats_general() + # test_chats() + # test_chats_general() # test_chats_exceptions() # test_chats_group() # test_chats_w_func() # test_chat_messages_for_summary() + test_udf_message_in_chats() diff --git a/test/agentchat/test_math_user_proxy_agent.py b/test/agentchat/test_math_user_proxy_agent.py index 6e7a3b5f7ca8..be93b9cadff9 100755 --- a/test/agentchat/test_math_user_proxy_agent.py +++ b/test/agentchat/test_math_user_proxy_agent.py @@ -53,11 +53,7 @@ def test_math_user_proxy_agent(): assistant.reset() math_problem = "$x^3=125$. What is x?" - # assistant.receive( - # message=mathproxyagent.generate_init_message(math_problem), - # sender=mathproxyagent, - # ) - res = mathproxyagent.initiate_chat(assistant, problem=math_problem) + res = mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem) print(conversations) print("Chat summary:", res.summary) print("Chat history:", res.chat_history) @@ -121,13 +117,13 @@ def test_execute_one_wolfram_query(): def test_generate_prompt(): mathproxyagent = MathUserProxyAgent(name="MathChatAgent", human_input_mode="NEVER") - assert "customized" in mathproxyagent.generate_init_message( - problem="2x=4", prompt_type="python", customized_prompt="customized" + assert "customized" in mathproxyagent.message_generator( + mathproxyagent, None, {"problem": "2x=4", "prompt_type": "python", "customized_prompt": "customized"} ) if __name__ == "__main__": # test_add_remove_print() # test_execute_one_python_code() - # test_generate_prompt() + test_generate_prompt() test_math_user_proxy_agent() diff --git a/website/blog/2023-10-18-RetrieveChat/index.mdx b/website/blog/2023-10-18-RetrieveChat/index.mdx index 65b3ccc7181a..e5c205b336bc 100644 --- a/website/blog/2023-10-18-RetrieveChat/index.mdx +++ b/website/blog/2023-10-18-RetrieveChat/index.mdx @@ -353,7 +353,8 @@ def retrieve_content(message, n_results=3): boss_aid.problem = message if not hasattr(boss_aid, "problem") else boss_aid.problem _, ret_msg = boss_aid._generate_retrieve_user_reply(message) else: - ret_msg = boss_aid.generate_init_message(message, n_results=n_results) + _context = {"problem": message, "n_results": n_results} + ret_msg = boss_aid.message_generator(boss_aid, None, _context) return ret_msg if ret_msg else message for agent in [boss, coder, pm, reviewer]: From 7fed080c06094a1c4ac5ede888c93ea9ea0f4cea Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 15:53:47 -0500 Subject: [PATCH 02/26] send --- autogen/agentchat/conversable_agent.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 2082b685dc7e..6767ea1d4d9d 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -556,11 +556,10 @@ def _process_message_before_send( def send( self, - message: Union[Dict, str, Callable], + message: Union[Dict, str], recipient: Agent, request_reply: Optional[bool] = None, silent: Optional[bool] = False, - **context, ) -> ChatResult: """Send a message to another agent. @@ -590,8 +589,7 @@ def send( recipient (Agent): the recipient of the message. request_reply (bool or None): whether to request a reply from the recipient. silent (bool or None): (Experimental) whether to print the message sent. - **context: the context of the message, which will be passed to [OpenAIWrapper.create](../oai/client#create). - #TODO: shall we also support callable message and context in "send" + Raises: ValueError: if the message can't be converted into a valid ChatCompletion message. From dab2b6959fc06a2a50632a3fca0d63a163e7101c Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 16:26:42 -0500 Subject: [PATCH 03/26] message func doc str --- autogen/agentchat/conversable_agent.py | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 6767ea1d4d9d..bf59310f2aae 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -885,15 +885,39 @@ def my_summary_method( Default is DEFAULT_summary_prompt, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." message (str or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. - If a string is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. - - If a callable is provided, it will be called to get the initial message. E.g, + - If a callable is provided, it will be called to get the initial message in the form of a string or a dict. If the returned value is a dict, it should contain the following reserved fields: + If the type is dict, it may contain the following reserved fields (either content or function_call need to be provided). + 1. "content": content of the message, can be None. + 2. "function_call": a dictionary containing the function name and arguments. (deprecated in favor of "tool_calls") + 3. "tool_calls": a list of dictionaries containing the function name and arguments. + 4. "role": role of the message, can be "assistant", "user", "function". + This field is only needed to distinguish between "function" or "assistant"/"user". + 5. "name": In most cases, this field is not needed. When the role is "function", this field is needed to indicate the function name. + 6. "context" (dict): the context of the message, which will be passed to + [OpenAIWrapper.create](../oai/client#create). + + Example of a callable message (returning a string): ```python - def my_message(sender, receiver, context): + def my_message(sender: ConversableAgent, recipient: ConversableAgent, context: dict) -> Union[str, Dict]: carryover = context.get("carryover", "") if isinstance(message, list): carryover = carryover[-1] final_msg = "Write a blogpost." + "\nContext: \n" + carryover return final_msg ``` + + Example of a callable message (returning a dict): + ```python + def my_message(sender: ConversableAgent, recipient: ConversableAgent, context: dict) -> Union[str, Dict]: + final_msg = {} + carryover = context.get("carryover", "") + carryover = context.get("carryover", "") + if isinstance(message, list): + carryover = carryover[-1] + final_msg["content"] = "Write a blogpost." + "\nContext: \n" + carryover + final_msg["context"] = {"prefix": "Today I feel"} + return final_msg + ``` **context: any context information. It has the following reserved fields: "carryover": a string or a list of string to specify the carryover information to be passed to this chat. If provided, we will combine this carryover (by attaching a "context: "string and the carryover content after the message content) with the "message" content when generating the initial chat From 9953e8db83ff55b43735120e95b295dbdd428bdb Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 16:42:27 -0500 Subject: [PATCH 04/26] test dict message --- test/agentchat/test_assistant_agent.py | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/agentchat/test_assistant_agent.py b/test/agentchat/test_assistant_agent.py index b99ed5766fb9..fbe8a82ece26 100755 --- a/test/agentchat/test_assistant_agent.py +++ b/test/agentchat/test_assistant_agent.py @@ -199,6 +199,43 @@ def tsp_message(sender, recipient, context): print(chat_res.cost) +@pytest.mark.skipif(skip, reason="openai not installed OR requested to skip") +def test_message(human_input_mode="NEVER", max_consecutive_auto_reply=10): + config_list = autogen.config_list_from_json( + OAI_CONFIG_LIST, + file_location=KEY_LOC, + ) + + def my_message(sender, recipient, context): + final_msg = {} + final_msg["content"] = "Make a joke about AI." + final_msg["context"] = {"prefix": "Today I feel"} + return final_msg + + # autogen.ChatCompletion.start_logging() + assistant = AssistantAgent("assistant", llm_config={"config_list": config_list}) + user = UserProxyAgent( + "user", + code_execution_config={ + "work_dir": here, + # "use_docker": False, + }, + human_input_mode=human_input_mode, + max_consecutive_auto_reply=max_consecutive_auto_reply, + ) + chat_res = user.initiate_chat( + assistant, + message=my_message, + max_turns=1, + ) + print(chat_res.summary) + print(chat_res.chat_history) + # print(autogen.ChatCompletion.logged_history) + # autogen.ChatCompletion.stop_logging() + # print(chat_res.summary) + print(chat_res.cost) + + if __name__ == "__main__": # test_gpt35() # test_create_execute_script(human_input_mode="TERMINATE") @@ -206,3 +243,4 @@ def tsp_message(sender, recipient, context): # should terminate in 2-3 rounds of interactions (because is_termination_msg should be true after 2-3 rounds) # although the max_consecutive_auto_reply is set to 10. test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) + test_message(human_input_mode="NEVER", max_consecutive_auto_reply=10) From 0635c0b52b77fafaa979485c0bbff1c6a0032aca Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 16:47:11 -0500 Subject: [PATCH 05/26] retiring soon --- autogen/agentchat/contrib/compressible_agent.py | 2 +- notebook/agentchat_compression.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index a652ff0e90bb..92adf74b02bc 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -19,7 +19,7 @@ def colored(x, *args, **kwargs): class CompressibleAgent(ConversableAgent): - """(Experimental) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, + """(Attention: CompressibleAgent Retiring Soon!) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, it also provides the added feature of compression when activated through the `compress_config` setting. `compress_config` is set to False by default, making this agent equivalent to the `AssistantAgent`. diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index d6a7181f0195..5de4d32166c4 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Experimental)\n", + "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Attention: CompressibleAgent Retiring Soon.)\n", "\n", "AutoGen offers conversable agents powered by LLM, tools, or humans, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participance through multi-agent conversation. Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n", "\n", From d55addb1d1a52b4e46ddffdd4c750dfa9fbccfc7 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 16:56:21 -0500 Subject: [PATCH 06/26] generate_init_message docstr --- autogen/agentchat/conversable_agent.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index bf59310f2aae..5c773a543350 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -2155,19 +2155,18 @@ async def a_execute_function(self, func_call): "content": str(content), } - def generate_init_message(self, message, **context) -> Union[str, Dict]: + def generate_init_message(self, message: Union[str, None], **context) -> Union[str, Dict]: """Generate the initial message for the agent. - TODO: offer a way to customize initial message without overriding this function. - - Override this function to customize the initial message based on user's request. - If not overridden, "message" needs to be provided in the context, or input() will be called to get the initial message. + If message is None, input() will be called to get the initial message. Args: - message (str): the message to be processed. + message (str or None): the message to be processed. **context: any context information. It has the following reserved fields: "carryover": a string or a list of string to specify the carryover information to be passed to this chat. It can be a string or a list of string. If provided, we will combine this carryover with the "message" content when generating the initial chat message. + Returns: + str or dict: the processed message. """ if message is None: message = self.get_human_input(">") @@ -2187,15 +2186,15 @@ def _process_carryover(self, message, context): "Carryover should be a string or a list of strings. Not adding carryover to the message." ) - async def a_generate_init_message(self, message, **context) -> Union[str, Dict]: + async def a_generate_init_message(self, message: Union[str, None], **context) -> Union[str, Dict]: """Generate the initial message for the agent. - TODO: offer a way to customize initial message without overriding this function. - - Override this function to customize the initial message based on user's request. - If not overridden, "message" needs to be provided in the context, or input() will be called to get the initial message. + If message is None, input() will be called to get the initial message. Args: Please refer to `generate_init_message` for the description of the arguments. + + Returns: + str or dict: the processed message. """ if message is None: message = await self.a_get_human_input(">") From 63915b5b739d7df269058eda95a350071f533d02 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 16:57:49 -0500 Subject: [PATCH 07/26] remove todo --- autogen/agentchat/conversable_agent.py | 1 - 1 file changed, 1 deletion(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 5c773a543350..4b61304057f9 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -929,7 +929,6 @@ def my_message(sender: ConversableAgent, recipient: ConversableAgent, context: d Returns: ChatResult: an ChatResult object. """ - # TODO: check _chat_info = context.copy() _chat_info["recipient"] = recipient _chat_info["sender"] = self From 7e7ad16dd124271ee13969adf685722c82ed0526 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 17:23:36 -0500 Subject: [PATCH 08/26] update notebook --- ...at_auto_feedback_from_code_execution.ipynb | 180 +++++++++--------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/notebook/agentchat_auto_feedback_from_code_execution.ipynb b/notebook/agentchat_auto_feedback_from_code_execution.ipynb index 87d7afec1460..83a612168bca 100644 --- a/notebook/agentchat_auto_feedback_from_code_execution.ipynb +++ b/notebook/agentchat_auto_feedback_from_code_execution.ipynb @@ -29,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -821,7 +821,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -840,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -849,43 +849,46 @@ "text": [ "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", "\n", - "Plot a chart of META and TESLA stock price gain YTDI have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.If you suggest code, the code will be executed in IPython.\n", + "Plot a chart of META and TESLA stock price gain YTD. Use data from the following csv file if it exists: coding/stock_price_ytd.csv. Use csv to read the file. Otherwise, figure out how to get the data.If you suggest code, the code will be executed in IPython.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", "\n", - "To plot a chart of META and TESLA stock price gain YTD using the data from `stock_price_ytd.csv`, we will follow these steps:\n", + "First, we need to check if the file `coding/stock_price_ytd.csv` exists and if it contains the necessary data for META and TESLA stock price gains YTD (Year-To-Date). We will attempt to read the file using Python's `csv` module. If the file does not exist or we cannot obtain the necessary data from it, we will then look for an alternative way to get the data.\n", "\n", - "1. Read the CSV file into a DataFrame using pandas.\n", - "2. Plot the data using matplotlib.\n", - "\n", - "Here is the Python code to execute in IPython:\n", + "Let's start by checking if the file exists and reading its contents.\n", "\n", "```python\n", - "# filename: plot_stock_price_ytd.py\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "# Read the CSV file into a DataFrame\n", - "df = pd.read_csv('coding/stock_price_ytd.csv')\n", - "\n", - "# Check if the necessary columns 'META' and 'TESLA' are present\n", - "if 'META' in df.columns and 'TESLA' in df.columns:\n", - " # Plot the stock price gain YTD for META and TESLA\n", - " plt.figure(figsize=(10, 5))\n", - " plt.plot(df['META'], label='META')\n", - " plt.plot(df['TESLA'], label='TESLA')\n", - " plt.title('META and TESLA Stock Price Gain YTD')\n", - " plt.xlabel('Date')\n", - " plt.ylabel('Stock Price Gain')\n", - " plt.legend()\n", - " plt.grid(True)\n", - " plt.show()\n", + "# filename: check_csv_file.py\n", + "\n", + "import csv\n", + "import os\n", + "\n", + "# Define the path to the CSV file\n", + "file_path = 'coding/stock_price_ytd.csv'\n", + "\n", + "# Check if the file exists\n", + "if os.path.exists(file_path):\n", + " try:\n", + " # Attempt to read the file and print its contents\n", + " with open(file_path, mode='r') as file:\n", + " csv_reader = csv.DictReader(file)\n", + " meta_data = []\n", + " tesla_data = []\n", + " for row in csv_reader:\n", + " if 'META' in row:\n", + " meta_data.append(row)\n", + " if 'TESLA' in row:\n", + " tesla_data.append(row)\n", + " print(\"META data:\", meta_data)\n", + " print(\"TESLA data:\", tesla_data)\n", + " except Exception as e:\n", + " print(f\"An error occurred while reading the file: {e}\")\n", "else:\n", - " print(\"The required columns 'META' and 'TESLA' are not present in the CSV file.\")\n", + " print(\"The file does not exist.\")\n", "```\n", "\n", - "Please execute this code in IPython to generate the plot. If there are any issues with the code, let me know, and I will provide the necessary corrections.\n", + "Please execute the above code to check for the file and read its contents. If the file exists and contains the required data, we will proceed to plot the chart. If not, we will explore alternative ways to obtain the data.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[31m\n", @@ -894,74 +897,65 @@ "\n", "exitcode: 0 (execution succeeded)\n", "Code output: \n", - "The required columns 'META' and 'TESLA' are not present in the CSV file.\n", + "META data: [{'Date': '2024-01-02', 'META': '346.2900085449219', 'TSLA': '248.4199981689453'}, {'Date': '2024-01-03', 'META': '344.4700012207031', 'TSLA': '238.4499969482422'}, {'Date': '2024-01-04', 'META': '347.1199951171875', 'TSLA': '237.92999267578125'}, {'Date': '2024-01-05', 'META': '351.95001220703125', 'TSLA': '237.49000549316406'}, {'Date': '2024-01-08', 'META': '358.6600036621094', 'TSLA': '240.4499969482422'}, {'Date': '2024-01-09', 'META': '357.42999267578125', 'TSLA': '234.9600067138672'}, {'Date': '2024-01-10', 'META': '370.4700012207031', 'TSLA': '233.94000244140625'}, {'Date': '2024-01-11', 'META': '369.6700134277344', 'TSLA': '227.22000122070312'}, {'Date': '2024-01-12', 'META': '374.489990234375', 'TSLA': '218.88999938964844'}, {'Date': '2024-01-16', 'META': '367.4599914550781', 'TSLA': '219.91000366210938'}, {'Date': '2024-01-17', 'META': '368.3699951171875', 'TSLA': '215.5500030517578'}, {'Date': '2024-01-18', 'META': '376.1300048828125', 'TSLA': '211.8800048828125'}, {'Date': '2024-01-19', 'META': '383.45001220703125', 'TSLA': '212.19000244140625'}, {'Date': '2024-01-22', 'META': '381.7799987792969', 'TSLA': '208.8000030517578'}, {'Date': '2024-01-23', 'META': '385.20001220703125', 'TSLA': '209.13999938964844'}, {'Date': '2024-01-24', 'META': '390.70001220703125', 'TSLA': '207.8300018310547'}, {'Date': '2024-01-25', 'META': '393.17999267578125', 'TSLA': '182.6300048828125'}, {'Date': '2024-01-26', 'META': '394.1400146484375', 'TSLA': '183.25'}, {'Date': '2024-01-29', 'META': '401.0199890136719', 'TSLA': '190.92999267578125'}, {'Date': '2024-01-30', 'META': '400.05999755859375', 'TSLA': '191.58999633789062'}, {'Date': '2024-01-31', 'META': '390.1400146484375', 'TSLA': '187.2899932861328'}, {'Date': '2024-02-01', 'META': '394.7799987792969', 'TSLA': '188.86000061035156'}, {'Date': '2024-02-02', 'META': '474.989990234375', 'TSLA': '187.91000366210938'}, {'Date': '2024-02-05', 'META': '459.4100036621094', 'TSLA': '181.05999755859375'}, {'Date': '2024-02-06', 'META': '454.7200012207031', 'TSLA': '185.10000610351562'}, {'Date': '2024-02-07', 'META': '469.5899963378906', 'TSLA': '187.5800018310547'}, {'Date': '2024-02-08', 'META': '470.0', 'TSLA': '189.55999755859375'}, {'Date': '2024-02-09', 'META': '468.1099853515625', 'TSLA': '193.57000732421875'}, {'Date': '2024-02-12', 'META': '468.8999938964844', 'TSLA': '188.1300048828125'}, {'Date': '2024-02-13', 'META': '460.1199951171875', 'TSLA': '184.02000427246094'}, {'Date': '2024-02-14', 'META': '473.2799987792969', 'TSLA': '188.7100067138672'}, {'Date': '2024-02-15', 'META': '484.0299987792969', 'TSLA': '200.4499969482422'}, {'Date': '2024-02-16', 'META': '473.32000732421875', 'TSLA': '199.9499969482422'}, {'Date': '2024-02-20', 'META': '471.75', 'TSLA': '193.75999450683594'}, {'Date': '2024-02-21', 'META': '468.0299987792969', 'TSLA': '194.77000427246094'}, {'Date': '2024-02-22', 'META': '486.1300048828125', 'TSLA': '197.41000366210938'}, {'Date': '2024-02-23', 'META': '484.0299987792969', 'TSLA': '191.97000122070312'}, {'Date': '2024-02-26', 'META': '481.739990234375', 'TSLA': '199.39999389648438'}, {'Date': '2024-02-27', 'META': '487.04998779296875', 'TSLA': '199.72999572753906'}, {'Date': '2024-02-28', 'META': '484.0199890136719', 'TSLA': '202.0399932861328'}, {'Date': '2024-02-29', 'META': '490.1300048828125', 'TSLA': '201.8800048828125'}, {'Date': '2024-03-01', 'META': '502.29998779296875', 'TSLA': '202.63999938964844'}]\n", + "TESLA data: []\n", "\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", "\n", - "It seems that the expected column names 'META' and 'TESLA' are not present in the CSV file. To resolve this, we need to first inspect the CSV file to understand its structure and identify the correct column names. Let's write a Python code snippet to print the first few rows of the CSV file. This will help us determine the correct column names to use for plotting.\n", + "The output indicates that the file `coding/stock_price_ytd.csv` exists and contains the necessary data for META stock price gains YTD. However, it seems that the TESLA data is not separated correctly, as the TESLA data appears to be within the same rows as the META data, but the script is looking for a separate 'TESLA' entry in the rows.\n", "\n", - "Please execute the following code:\n", + "We need to adjust the script to correctly parse the TESLA data from the same rows as the META data. Let's modify the script to extract both META and TESLA stock prices from the same rows and then plot the chart.\n", "\n", "```python\n", - "# filename: inspect_csv_columns.py\n", - "import pandas as pd\n", - "\n", - "# Read the CSV file into a DataFrame\n", - "df = pd.read_csv('coding/stock_price_ytd.csv')\n", - "\n", - "# Print the first few rows of the DataFrame to inspect the column names\n", - "print(df.head())\n", - "```\n", - "\n", - "After executing this code, please provide the output so that I can suggest the next steps based on the actual column names in the CSV file.\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[31m\n", - ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n", - "\u001b[33mipython_user_proxy\u001b[0m (to assistant):\n", + "# filename: plot_stock_prices.py\n", "\n", - "exitcode: 0 (execution succeeded)\n", - "Code output: \n", - " Date META TSLA\n", - "0 2024-01-02 346.290009 248.419998\n", - "1 2024-01-03 344.470001 238.449997\n", - "2 2024-01-04 347.119995 237.929993\n", - "3 2024-01-05 351.950012 237.490005\n", - "4 2024-01-08 358.660004 240.449997\n", - "\n", - "\n", - "--------------------------------------------------------------------------------\n", - "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", - "\n", - "Thank you for providing the output. It appears that the column names for the stock prices are 'META' and 'TSLA', not 'TESLA'. With this information, we can now plot the chart correctly using the actual column names.\n", - "\n", - "Here is the updated Python code to plot the chart:\n", - "\n", - "```python\n", - "# filename: plot_stock_price_ytd_corrected.py\n", - "import pandas as pd\n", + "import csv\n", + "import os\n", "import matplotlib.pyplot as plt\n", "\n", - "# Read the CSV file into a DataFrame\n", - "df = pd.read_csv('coding/stock_price_ytd.csv')\n", - "\n", - "# Plot the stock price gain YTD for META and TSLA\n", - "plt.figure(figsize=(10, 5))\n", - "plt.plot(df['Date'], df['META'], label='META')\n", - "plt.plot(df['Date'], df['TSLA'], label='TSLA')\n", - "plt.title('META and TSLA Stock Price Gain YTD')\n", - "plt.xlabel('Date')\n", - "plt.ylabel('Stock Price Gain')\n", - "plt.xticks(rotation=45) # Rotate the x-axis labels for better readability\n", - "plt.legend()\n", - "plt.grid(True)\n", - "plt.tight_layout() # Adjust layout to prevent clipping of labels\n", - "plt.show()\n", + "# Define the path to the CSV file\n", + "file_path = 'coding/stock_price_ytd.csv'\n", + "\n", + "# Initialize lists to hold the parsed data\n", + "dates = []\n", + "meta_prices = []\n", + "tesla_prices = []\n", + "\n", + "# Check if the file exists\n", + "if os.path.exists(file_path):\n", + " try:\n", + " # Read the file and parse the data\n", + " with open(file_path, mode='r') as file:\n", + " csv_reader = csv.DictReader(file)\n", + " for row in csv_reader:\n", + " dates.append(row['Date'])\n", + " meta_prices.append(float(row['META']))\n", + " tesla_prices.append(float(row['TSLA']))\n", + " \n", + " # Plot the data\n", + " plt.figure(figsize=(14, 7))\n", + " plt.plot(dates, meta_prices, label='META', marker='o')\n", + " plt.plot(dates, tesla_prices, label='TESLA', marker='x')\n", + " \n", + " # Formatting the plot\n", + " plt.title('META vs TESLA Stock Price Gain YTD')\n", + " plt.xlabel('Date')\n", + " plt.ylabel('Stock Price')\n", + " plt.xticks(rotation=45)\n", + " plt.legend()\n", + " plt.tight_layout()\n", + " \n", + " # Show the plot\n", + " plt.show()\n", + " except Exception as e:\n", + " print(f\"An error occurred while processing the file: {e}\")\n", + "else:\n", + " print(\"The file does not exist.\")\n", "```\n", "\n", - "Please execute this code in IPython to generate the plot. If there are any further issues, let me know, and I will address them.\n", + "Please execute the above code to plot the chart of META and TESLA stock price gains YTD.\n", "\n", "--------------------------------------------------------------------------------\n", "\u001b[31m\n", @@ -970,9 +964,9 @@ }, { "data": { - "image/png": "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Xf5w//33V9i2rO6DBw8aFovFmD59ern1W7duNby8vCosP112drYBGKNHj66wLjMz0zh+/LjrcfrP/W//DRiGYXz22WcGYPz4448VPpMDBw64lsXHx1fYLj093fDx8TEeeuihM9Z6up9//tkwm81GeHi4ERoaaqSmpp5x298zvNwwDGPjxo0GYDz44INVqk1E5EKlM90iIg3I9ddfT2FhIfPmzSM3N5d58+adc2j51VdfzaJFiyo8Bg0aVO33nzlzJqNGjSIoKAiA1q1b06NHjyoPMffz83M9z8zMJDs7m379+vHLL79U2HbIkCG0bNnS9bpLly4EBwezf/9+AOx2OwsWLGD06NE0a9bMtV379u0ZPnx4tY/tbIYOHcrq1au56qqr2Lx5My+++CLDhw+nSZMmfPPNN+eVOXv2bMxmc7kztTfddBPz588nMzPznPtPmzaNWbNm0a1bNxYsWMDf//53evToQffu3dm5c+c591+/fj3p6encc889+Pr6upaPGjWKdu3auUYfHD9+nB9//JHbb7+93OcMVHoDvW3btjFgwAASEhJYvHgxYWFh56ylzJAhQ2jUqBFxcXHceOONBAYGMnfuXJo0aVJuu4kTJ54z66uvvsJkMvHkk09WWFdW95w5c3A4HFx//fXlRoHExMTQunVrli5desb8nJwcAAIDAyusGzhwII0aNXI93nzzTde60/8NlI1C6dOnD0Cl/w5+q0OHDvTr18/1ulGjRrRt29b17+Jcevfuzd13301GRgbPPffceY1AqKqyzyY3N9dt7yEiUh/o7uUiIg1Io0aNGDJkCLNmzaKgoAC73c5111131n2aNm1a5Ttin83OnTvZuHEj48aNIykpybV84MCBvPnmm+Tk5JzzLsXz5s3jmWeeYdOmTRQXF7uWV9a8/bbBAwgLC3M1pMePH6ewsJDWrVtX2K5t27YkJiZW+diqolevXsyZM4eSkhI2b97M3LlzeeWVV7juuuvYtGkTHTp0qFbep59+Su/evTl58qRryHy3bt0oKSnhiy++4K677jpnxk033cRNN91ETk4OP//8MzNmzGDWrFlceeWVbNu2rVwz/VuHDh0CnJ/Vb7Vr1841PV1ZM9epU6cqHdeVV15JdHQ0CxYsqLQhPZs333yTNm3a4OXlRXR0NG3btsVsLn/+wMvLi6ZNm54za9++fTRu3Jjw8PAzbrN3714Mw6j0ZwjAarWecd+yPzzl5eVVWPfuu++Sm5tLWloat9xyS7l1GRkZTJs2jdmzZ5Oenl5uXXZ29hnfr8y5/l1URdl1/j179qzyPuej7LMp+6xERBoqNd0iIg3M2LFjufPOO0lNTWXEiBGV3tzLHT799FPAeV3ogw8+WGH9V199xYQJE864/4oVK7jqqqvo378/b731FrGxsVitVj766CNmzZpVYXuLxVJpjlGF617dydvbm169etGrVy/atGnDhAkT+OKLLyo9o3ome/fuZd26dQCVNnwzZ86sUtNdJjg4mKFDhzJ06FCsVisff/wxP//8MwMGDKhyRk0ZM2YMH3/8MTNnzuRPf/pTtfbt3bv3ORtBHx+fCo34+XI4HJhMJubPn1/pz9vZ/mgQEhJCbGxspdf5l13jffpN0Mpcf/31rFq1iocffpiuXbsSGBiIw+HgiiuuqNKUZ/X130Vlyj6bVq1a1XElIiLupaZbRKSBueaaa/jTn/7EmjVr+Pzzz2vlPQ3DYNasWQwaNIh77rmnwvqnn36amTNnnrXp/uqrr/D19WXBggXl5h7+6KOPzqumsrto7927t8K63bt3n1dmdZU1iCkpKdXab+bMmVitVj755JMKTdRPP/3E66+/TnJycqVnNatS08cff+yq6UxzqJfNG717927XXcLL7N6927W+RYsWAFW+idw//vEPvLy8uOeeewgKCqrVO+ufrmXLlixYsICMjIwznu1u2bIlhmHQvHlz2rRpU+33GDVqFP/+979Zu3YtvXv3Puf2mZmZLFmyhGnTpvHEE0+4llf2M9wQfPLJJ5hMJoYOHVrXpYiIuJWu6RYRaWACAwN5++23mTp1KldeeWWtvOfKlSs5ePAgEyZM4LrrrqvwuOGGG1i6dCnHjh07Y4bFYsFkMmG3213LDh48yNdff31eNVksFoYPH87XX39NcnKya/nOnTtZsGDBeWWeydKlSys9k1g2hL2yIdpnM3PmTPr168cNN9xQ4bN8+OGHAcrdpf63CgoKWL16daXr5s+fX66mgIAAgAp34u7ZsydRUVG888475Yb6z58/n507dzJq1CjA+ceN/v378+GHH5b7nKHys6smk4n33nuP6667jvHjx5/3Ne+/15gxYzAMg2nTplVYV1b3tddei8ViYdq0aRWOxTCMCtN4/dZf//pX/P39uf322yudEu23mWV/YPnt8ldfffWcx3Ohef7551m4cCE33HDDGYfvi4g0FDrTLSLSAI0fP77K2+7Zs8c1NPx00dHRVT4DNXPmTCwWi6sR+62rrrqKv//978yePZvJkydXus2oUaN4+eWXueKKKxg7dizp6em8+eabtGrVii1btlT5eE43bdo0vv/+e/r168c999xDaWkpb7zxBh07djzvzMrcd999FBQUcM0119CuXTtKSkpYtWoVn3/+OQkJCRXO8CclJfHMM89UyOnWrRuRkZEkJSVx7733VvpeTZo0oXv37sycOZNHHnmk0m0KCgq45JJL6NOnD1dccQVxcXFkZWXx9ddfs2LFCkaPHk23bt0A59nc0NBQ3nnnHYKCgggICODiiy+mefPmvPDCC0yYMIEBAwZw0003kZaWxmuvvUZCQkK5Swhef/11LrvsMrp3785dd91F8+bNOXjwIN999x2bNm2qUJ/ZbObTTz9l9OjRXH/99SQmJlY4m+5ugwYN4tZbb+X1119n7969ruHbK1asYNCgQdx77720bNmSZ555hilTpnDw4EFGjx5NUFAQBw4cYO7cudx111385S9/OeN7tG7dmlmzZnHTTTfRtm1bbr75Zi666CIMw+DAgQPMmjULs9nsugY9ODiY/v378+KLL2Kz2WjSpAkLFy50zUN+ISotLXX996WoqIhDhw7xzTffsGXLFgYNGsR7771XxxWKiNSCurhluoiI1JzTpww7m+pOGXam6bF+O3VWSUmJERERYfTr1++s79+8eXOjW7duZ93mgw8+MFq3bm34+PgY7dq1Mz766CPXVFO/rXvSpEmVHuP48ePLLVu+fLnRo0cPw9vb22jRooXxzjvvVJp5LmebMmz+/PnG7bffbrRr184IDAw0vL29jVatWhn33XefkZaWVqHGM33md9xxh3HfffcZgLFv374z1jJ16lQDMDZv3lzpepvNZrz//vvG6NGjjfj4eMPHx8fw9/c3unXrZvzjH/8wiouLy23/v//9z+jQoYPh5eVVYfqwzz//3OjWrZvh4+NjhIeHGzfffLNriq7Tbdu2zbjmmmuM0NBQw9fX12jbtq3x+OOPu9afPmVYmYKCAmPAgAFGYGCgsWbNmjMeb1V/xs82RdVvpwwzDMMoLS01/vGPfxjt2rUzvL29jUaNGhkjRoyoMLXaV199ZVx22WVGQECAERAQYLRr186YNGmSsXv37rPWUyYpKcmYOHGi0apVK8PX19fw8/Mz2rVrZ9x9993Gpk2bym175MgR1+cYEhJi/PGPfzSOHTtWYWquM00ZVtl0dAMGDDjjz25lqvp5V2XKsNN/vv39/Y2EhARjzJgxxpdffmnY7fYq1yQiciEzGUY9vLOGiIiIiIiISAOga7pFRERERERE3ERNt4iIiIiIiIibqOkWERERERERcRM13SIiIiIiIiJuoqZbRERERERExE3UdIuIiIiIiIi4iVddF1AfOBwOjh07RlBQECaTqa7LERERERERkXrOMAxyc3Np3LgxZvOZz2er6QaOHTtGXFxcXZchIiIiIiIiF5jDhw/TtGnTM65X0w0EBQUBzg8rODi4jqupnM1mY+HChQwbNgyr1Vrv8tyR6Wl57sis73nuyPS0PHdk1vc8d2R6Wp47Mj0tzx2Z9T3PHZmelueOTE/Lc0dmfc9zR6an5blLTk4OcXFxrn7yTNR0g2tIeXBwcL1uuv39/QkODq6xH+SazHNHpqfluSOzvue5I9PT8tyRWd/z3JHpaXnuyPS0PHdk1vc8d2R6Wp47Mj0tzx2Z9T3PHZmeludu57pEWTdSExEREREREXETNd0iIiIiIiIibqKmW0RERERERMRNdE13Fdntdmw2W529v81mw8vLi6KiIux2e73Lq26m1WrFYrHUyPuKiIiIiIjUV2q6z8EwDFJTU8nKyqrzOmJiYjh8+HCNzCVe03nnkxkaGkpMTIzmRhcRERERkQZLTfc5lDXcUVFR+Pv711mD6HA4yMvLIzAw8KwTr9dVXnUyDcOgoKCA9PR0AGJjY2vk/UVEREREROobNd1nYbfbXQ13REREndbicDgoKSnB19e3xprumsyrbqafnx8A6enpREVFaai5iIiIiIg0SLqR2lmUXcPt7+9fx5U0TGWfa11eKy8iIiIiIuJOarqrQNccu4c+VxERERERaejUdIuIiIiIiIi4iZpuERERERERETep06Z76tSpmEymco927dq51hcVFTFp0iQiIiIIDAxkzJgxpKWllctITk5m1KhR+Pv7ExUVxcMPP0xpaWltH0q9c9ttt2Eymbj77rsrrJs0aRIWi4V77rmn3La/fVxxxRUsW7as0nWnP5YtWwbAkSNHiIqKokuXLrV5qCIiIiIi0sA4jLqu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zy0iIuIJ1HSLiIhIrdH13CIi4mnUdIuIiEityC60sf2YrucWERHPoqZbREREasXaAxk4DGgRGUB0sG9dlyMiIlIr1HSLiIhIrSgbWt5HZ7lFRMSDqOkWERGRWqGbqImIiCdS0y0iIiJul5lfws6UHAD6qOkWEREPoqZbRERE3O7nA86z3K2jAmkU5FPH1YiIiNQeNd0iIiLidpoqTEREPJWabhEREXE7Xc8tIiKeSk23iIiIuNWJvGL2pOUBcLGabhER8TBqukVERMSt1pw6y90uJojwAO86rkZERKR2qekWERERt9L13CIi4snUdIuIiIhb6XpuERHxZGq6RURExG3ScorYfzwfkwkubq6mW0REPI+abhEREXGbsuu5OzYOJsTfWsfViIiI1D413SIiIuI2ruu5NbRcREQ8lJpuERERcRvX9dy6iZqIiHgoNd0iIiLiFseyCjl0sgCL2USvhPC6LkdERKROqOkWERERtygbWt6pSQhBvrqeW0REPJOabhEREXELTRUmIiKipltERETcxHUTNV3PLSIiHkxNt4iIiNS4wxkFHM0qxMtsomd8WF2XIyIiUmfUdIuIiEiNKzvLfVFcKAE+XnVcjYiISN1R0y0iIiI1Ttdzi4iIOKnpFhERkRplGIau5xYRETlFTbeIiIjUqIMnC0jNKcLbYqaHrucWEREPp6ZbREREalTZWe6uzULxtVrquBoREZG6paZbREREapSu5xYREfmVmm4RERGpMbqeW0REpDw13SIiIlJj9h3P50ReMT5eZro1C63rckREROqcmm4RERGpMT8fyACgR3wYPl66nltERERNt4iIiNSYNQcyAV3PLSIiUqbeNN3PP/88JpOJBx54wLWsqKiISZMmERERQWBgIGPGjCEtLa3cfsnJyYwaNQp/f3+ioqJ4+OGHKS0treXqRURExGH8eqZb13OLiIg41Yume926dbz77rt06dKl3PIHH3yQb7/9li+++ILly5dz7Ngxrr32Wtd6u93OqFGjKCkpYdWqVXz88cfMmDGDJ554orYPQURExOOlFkBmgQ0/q4UuTUPruhwREZF6wauuC8jLy+Pmm2/m/fff55lnnnEtz87O5oMPPmDWrFlcfvnlAHz00Ue0b9+eNWvW0KdPHxYuXMiOHTtYvHgx0dHRdO3alaeffppHHnmEqVOn4u3tXVeHJSIi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MJtNRAT6EBHoAwQBzjsCJmZtZ2T3JhoKLiIiIiIi4iZVarodDkelz0VERERERETkzM58O+tK2Gw2Bg8ezN69e91Vj4iIiIiIiEiDUa2m22q1smXLFnfVIiIiIiIiItKgVKvpBrjlllvOetdxEREREREREXGq9pRhpaWlfPjhhyxevJgePXoQEBBQbv3LL79cY8WJiIiIiIiIXMiq3XRv27aN7t27A7Bnz55y6842h7OIiIiIiIiIp6l207106VJ31CEiIiIiIiLS4FSr6f7888/55ptvKCkpYfDgwdx9993uqktERERERETkglflpvvtt99m0qRJtG7dGj8/P+bMmcO+ffv4xz/+4c76RERERERERC5YVb57+b/+9S+efPJJdu/ezaZNm/j4449566233FmbiIiIiIiIyAWtyk33/v37GT9+vOv12LFjKS0tJSUlxS2FiYiIiIiIiFzoqtx0FxcXl5sezGw24+3tTWFhoVsKExEREREREbnQVetGao8//jj+/v6u1yUlJUyfPp2QkBDXMs3TLSIiIiIiIuJU5aa7f//+7N69u9yySy65hP3797tea55uERERERERkV9VueletmyZG8sQERERERERaXiqfE23iIiIiIiIiFSPmm4RERERERERN1HTLSIiIiIiIuImarpFRERERERE3KTaTbfNZjvjuhMnTvyuYkREREREREQakmo33TfeeCOGYVRYnpaWxsCBA2uiJhEREREREZEGodpNd3JyMv/3f/9XbllqaioDBw6kXbt2NVaYiIiIiIiIyIWu2k13YmIiq1atYvLkyQAcO3aMAQMG0LlzZ/773//WeIEiIiIiIiIiFyqv6u7QqFEjFi5cyGWXXQbAvHnz6N69OzNnzsRs1n3ZRERERERERMpUu+kGiIuLY9GiRfTr14+hQ4fyySefYDKZaro2ERERERERkQtalZrusLCwSpvqgoICvv32WyIiIlzLMjIyaq46ERERERERkQtYlZruV1991c1liIiIiIiIiDQ8VWq6x48f7+46RERERERERBqc87p7+YIFCyosX7hwIfPnz6+RokREREREREQagmo33Y8++ih2u73CcofDwaOPPlojRYmIiIiIiIg0BNVuuvfu3UuHDh0qLG/Xrh1JSUk1UpSIiIiIiIhIQ1DtpjskJIT9+/dXWJ6UlERAQECNFCUiIiIiIiLSEFS76b766qt54IEH2Ldvn2tZUlISDz30EFdddVWNFiciIiIiIiJyIat20/3iiy8SEBBAu3btaN68Oc2bN6d9+/ZERETw0ksvuaNGERERERERkQtSlaYMO11ISAirVq1i0aJFbN68GT8/P7p06UL//v3dUZ+IiIiIiIjIBavaTTeAyWRi2LBhDBs2rKbrEREREREREWkwqj28HGD58uVceeWVtGrVilatWnHVVVexYsWKmq5NRERERERE5IJW7ab7008/ZciQIfj7+3P//fdz//334+fnx+DBg5k1a1a1st5++226dOlCcHAwwcHB9O3bl/nz57vWFxUVMWnSJCIiIggMDGTMmDGkpaWVy0hOTmbUqFH4+/sTFRXFww8/TGlpaXUPS0RERERERKTGVXt4+fTp03nxxRd58MEHXcvuv/9+Xn75ZZ5++mnGjh1b5aymTZvy/PPP07p1awzD4OOPP+bqq69m48aNdOzYkQcffJDvvvuOL774gpCQEO69916uvfZaVq5cCYDdbmfUqFHExMSwatUqUlJSGDduHFarlWeffba6hyYiIiIiIiJSo6p9pnv//v1ceeWVFZZfddVVHDhwoFpZV155JSNHjqR169a0adOG6dOnExgYyJo1a8jOzuaDDz7g5Zdf5vLLL6dHjx589NFHrFq1ijVr1gCwcOFCduzYwaeffkrXrl0ZMWIETz/9NG+++SYlJSXVPTQRERERERGRGlXtpjsuLo4lS5ZUWL548WLi4uLOuxC73c7s2bPJz8+nb9++bNiwAZvNxpAhQ1zbtGvXjmbNmrF69WoAVq9eTefOnYmOjnZtM3z4cHJycti+fft51yIiIiIiIiJSE6o9vPyhhx7i/vvvZ9OmTVxyySUArFy5khkzZvDaa69Vu4CtW7fSt29fioqKCAwMZO7cuXTo0IFNmzbh7e1NaGhoue2jo6NJTU0FIDU1tVzDXba+bN2ZFBcXU1xc7Hqdk5MDgM1mw2azVfsYakNZXTVVX03nuSPT0/LckVnf89yR6Wl57sis73nuyPS0PHdkelqeOzLre547Mj0tzx2Znpbnjsz6nueOTE/Lc5eq1mcyDMOobvjcuXP55z//yc6dOwFo3749Dz/8MFdffXV1oygpKSE5OZns7Gy+/PJL/v3vf7N8+XI2bdrEhAkTyjXHAL1792bQoEG88MIL3HXXXRw6dIgFCxa41hcUFBAQEEBiYiIjRoyo9D2nTp3KtGnTKiyfNWsW/v7+1T4GERERERER8SwFBQWMHTuW7OxsgoODz7jdec3Tfc0113DNNdecd3Gn8/b2plWrVgD06NGDdevW8dprr3HDDTdQUlJCVlZWubPdaWlpxMTEABATE8PatWvL5ZXd3bxsm8pMmTKFyZMnu17n5OQQFxfHsGHDzvph1SWbzcaiRYsYOnQoVqu13uW5I9PT8tyRWd/z3JHpaXnuyKzvee7I9LQ8d2R6Wp47Mut7njsyPS3PHZmelueOzPqe545MT8tzl7IR0+dS7aa7RYsWrFu3joiIiHLLs7Ky6N69O/v3769uZDkOh4Pi4mJ69OiB1WplyZIljBkzBoDdu3eTnJxM3759Aejbty/Tp08nPT2dqKgoABYtWkRwcDAdOnQ443v4+Pjg4+NTYbnVaq3X31So+Rrdccz1vcb6nueOzPqe545MT8tzR2Z9z3NHpqfluSPT0/LckVnf89yR6Wl57sj0tDx3ZNb3PHdkelpeTatqbdVuug8ePIjdbq+wvLi4mKNHj1Yra8qUKYwYMYJmzZqRm5vLrFmzWLZsGQsWLCAkJIQ77riDyZMnEx4eTnBwMPfddx99+/alT58+AAwbNowOHTpw66238uKLL5Kamspjjz3GpEmTKm2qRURERERERGpTlZvub775xvW8rCkuY7fbWbJkCQkJCdV68/T0dMaNG0dKSgohISF06dKFBQsWMHToUABeeeUVzGYzY8aMobi4mOHDh/PWW2+59rdYLMybN4+JEyfSt29fAgICGD9+PE899VS16hARERERERFxhyo33aNHjwbAZDIxfvz4cuusVisJCQn885//rNabf/DBB2dd7+vry5tvvsmbb755xm3i4+NJTEys1vuKiIiIiIiI1IYqN90OhwOA5s2bs27dOiIjI91WlIiIiIiIiEhDUO1rug8cOOCOOkREREREREQaHHNVN1y9ejXz5s0rt+w///kPzZs3JyoqirvuuqvCnNoiIiIiIiIinqzKTfdTTz3F9u3bXa+3bt3KHXfcwZAhQ3j00Uf59ttvee6559xSpIiIiIiIiMiFqMpN96ZNmxg8eLDr9ezZs7n44ot5//33mTx5Mq+//jr//e9/3VKkiIiIiIiIyIWoyk13ZmYm0dHRrtfLly9nxIgRrte9evXi8OHDNVudiIiIiIiIyAWsyk13dHS06yZqJSUl/PLLL/Tp08e1Pjc3F6vVWvMVioiIiIiIiFygqtx0jxw5kkcffZQVK1YwZcoU/P396devn2v9li1baNmypVuKFBEREREREbkQVXnKsKeffpprr72WAQMGEBgYyMcff4y3t7dr/YcffsiwYcPcUqSIiIiIiIjIhajKTXdkZCQ//vgj2dnZBAYGYrFYyq3/4osvCAwMrPECRURERERERC5UVW66y4SEhFS6PDw8/HcXIyIiIiIiItKQVPmabhERERERERGpHjXdIiIiIiIiIm6ipltERERERETETdR0i4iIiIiIiLiJmm4RERERERERN1HTLSIiIiIiIuImarpFRERERERE3ERNt4iIiIiIiIibqOkWERERERERcRM13SIiIiIiIiJuoqZbRERERERExE3UdIuIiIiIiIi4iZpuERERERERETdR0y0iIiIiIiLiJmq6RURERERERNxETbeIiIiIiIiIm6jpFhEREREREXETNd0iIiIiIiIibqKmW0RERERERMRN1HSLiIiIiIiIuImabhERERERERE3UdMtIiIiIiIi4iZqukVERERERETcRE23iIiIiIiIiJuo6RYRERERERFxEzXdIiIiIiIiIm6ipltERERERETETdR0i4iIiIiIiLhJnTbdzz33HL169SIoKIioqChGjx7N7t27y20zcOBATCZTucfdd99dbpvk5GRGjRqFv78/UVFRPPzww5SWltbmoYiIiIiIiIhU4FWXb758+XImTZpEr169KC0t5W9/+xvDhg1jx44dBAQEuLa78847eeqpp1yv/f39Xc/tdjujRo0iJiaGVatWkZKSwrhx47BarTz77LO1ejwiIiIiIiIip6vTpvv7778v93rGjBlERUWxYcMG+vfv71ru7+9PTExMpRkLFy5kx44dLF68mOjoaLp27crTTz/NI488wtSpU/H29nbrMYiIiIiIiIicSb26pjs7OxuA8PDwcstnzpxJZGQknTp1YsqUKRQUFLjWrV69ms6dOxMdHe1aNnz4cHJycti+fXvtFC4iIiIiIiJSiTo90306h8PBAw88wKWXXkqnTp1cy8eOHUt8fDyNGzdmy5YtPPLII+zevZs5c+YAkJqaWq7hBlyvU1NTK32v4uJiiouLXa9zcnIAsNls2Gy2Gj2u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//79HY4m7WybrT+XLVtGXbp0sY0f4YxvzJgxtjYTyUVUt27d6OLFi3YZu3Xr5nBnriOftc2XLl2iuLg423eYSP6u9OvXj95//30iune5Pnv2LL344ov37DC6G1f6L2ecrvRfJfksFku5tm8c+YofkXe1/3Lks67nzp8/X67+68SJEzRw4EBq164dtWvXjt59913ba7du3Ure3t40bdo0u/FwHnvsMZo8ebLDcVLu9r333nuUk5Nj9xpX+gdnfETO9w2uOIszY8YMeuqppxwePChtHiYkJLjcPziTz5W+wVG+4judjhw54lLfUJbzypUrFB8f71L/oMR6paJw0V3FSUlJoUaNGtELL7xAZ86coS1bttDEiRNJr9fbTl+1LqiHDh2izp0708KFC8nb29t2qoWrvuLk5eXRkiVLqE6dOnYDaDjjsw5O8uWXX1L37t3vyVPS3sbSnNYBKBxlrF+/vsM9cM60OTU1lbp06UIDBgygMWPG0FNPPUW1atWy6yDK8ul0Ovriiy/uef2VK1doyZIlVLt2bYcbAhcuXKDQ0FCqWbMmPfroo7aCt3hBdubMGRozZgy1bt2agoODqV+/fhQYGOgwX2lOR1g7rzlz5lBgYKDDz9kVX2JiIkVERNC0adNKXA5La7N1pX316lV66aWXKCQkhPz9/alTp05Uv359h+/tSj4i+UhPaGgoPf/88yUePXDGmZOTQxMnTqTu3btTz549acKECVS7dm2Hn0tpbXaU4ezZs7R48WIKDAy0rWiKc+rUKapTpw4tXLiQ9u7dS6+99hp16dLFdolE8RWYM32Dq06isvuHsnxERF999RX16NHDqf6hJN+3337rsD1l9Q3O+C5cuEDjx4+nRo0aUd26dal3794UFBRU4jLmasay+oeTJ09S7dq16aWXXqLExETavHkztWzZ0m6j4ujRoxQREUGdOnWidu3a0ciRI8nf3/+e085L8xVvj3V5tH4XZ8+eTbVq1XL4GV++fJnq169PCxYsoIMHD9J7771HgwcPppo1a9p2vhbfuUckD5YYFBRkVwC66rRubG3dupX+8pe/0PLly8nb29vhxlZJvoCAAPrf//5n12Zr3qVLl1JwcLDDz6Q0359//klE8sjCnTt3ptatW1PLli1p2LBhVLNmzRL7bFfmI5G8USxJEn399dcu+QICAmy+ixcvUu3atWnEiBE0b948mjVrFgUGBjpcbkrzFR/Mz8rNmzdpyZIlVK9ePYenMVtvDenv70/PPfdcqSPGO9t/ueIkKrv/Ks1n7Qtd6b/Km6+k/qskX/H1qKv918mTJ6lOnTo0b9482rJlC02fPp3CwsLsLunavXs3BQUFUe/eval///40adIkql69OsXFxTntc1T8OtM/OOOzfjbO9A3OOO9e7xmNRlq6dCnVq1fP4WUOzszDEydOUJcuXahVq1Zl9g+uzEOisvsGZ3zJyclO9w0lOdu0aVPipYBl9Q9KrFfcARfdVZzo6Ghq166d3WlA+fn59Pzzz5OXlxf99NNPtukHDhwgSZKoVq1aJXYuzvrMZjNt3bqVnnjiCWrUqFGJHXRpPr1eb7sWufiesbJO/XUl45YtW2jSpEnUoEGDcmX08vKyHe1MSEigZ555hoYNG0azZs0q8ZoxV/JdvHiRli5dSg0bNnSYz3pd5tixY2n9+vU0YMAAGjFihMPCOz09nY4fP06vv/46bdiwgc6dO+cwX1lOR/znP/8hSZLKlfFuzp49S5IkUUBAQLnbbO1M8/PzKTU1lTZs2EA7d+50eMp7edpLRPTFF1843NBy1ln8GvZ33nmHJk2aRIsXL3a4ceRqxrS0NFq5ciU1atTI4Uo4OzubRo0aRTNnzrSbPnjwYIejVjvTN7jq3LZtW6n9g7M+o9F4zyUp7shXVt/gjM/6GWdkZFBcXBy9+eab9OWXX5Z4LaSrGcvqHzIzM+nBBx+85/S7AQMG0BdffEHbtm2zFYWJiYn0ww8/0Pz58+nvf/+7w+WwLN+PP/5oO7Jhbfv69etL7BuI5A3wyMhIu8uezp8/T5MmTaJq1arZ/s/qe/vtt0v1ueIkkpdDSZKodu3aJRZjzvosFgtt3ryZRo8eXeIOvrJ8vr6+to3X5ORk2r59Oy1atIj+8Y9/2B0ZLG9Gk8lk+44sXLiwxPVUWRmtR4qPHTtGQ4YMoT59+tCkSZNK7BPL8hX/nOPj42nRokVUt25dh/MwNzeXJk+eTJMmTaIVK1ZQREQEzZ07t8Qi1Jn+y1VnWf2XMz7r+tmZ/svVfGX1X2X5iq9Hne2/MjIy6KGHHqI5c+bYTe/UqZPteu3i43GsW7eOpkyZQi+88ILDQscZ392U1j+46nOmb3DV+fXXX9PEiRMpODi43BmLH1iIiooqtX8ozzwkKrlvcOUztt7Joqy+wdWMJ06cKLV/IFJmveIOuOiu4uzZs4ckSbpnQ8hisdAzzzxD/v7+to2uy5cvU/fu3Uvdy+OK7+rVq/Tuu++Wuje2NN+cOXPsfEq1efXq1SUWoM74atSoYeucrKcYlnYNlyv5CgoKKDo62m6Qj7v56quv6OOPPyYiou+++85hEerqYF9lOR1R2vVdrvjS0tJo3LhxDo/OuuJzpc2u5HP2NmHOOO++9q60zK5kNBqNlJycbDegTnGs90i17tSyHvVbv349jRgxgojsN/6Sk5PL7BtcdV65cqXU/sFVX1mUp82rVq0qsW9wxlf89jNKZMzPz6fjx4+X2D/k5OTQhg0b7E6lfe2110ij0VB4eDh16tSJdDpdiUc8yuPT6/V2G6jp6emlDrj03XffkVarvecynKtXr9LYsWOpefPmdrfQO378eJnrBFec586do5CQEIdH2crjS0xMpPnz55d63aIzPlduielqRiulraec8VnXYdZLlEobEMqVfDk5OfTLL7+UOA+sg1Fu3LiRiIg++OCDUotQZ/ovV51XrlyhdevWldh/ueorq58oT5tL67+c9bmyHo2Pj6exY8faboVl3R5atGgRTZ061dbOu50lvYczvrs9x44dK7F/cNV39uxZCg4OLrVvcNWZmJhIL730Uok70Jydh67cqs2VfFZvSX2Ds/ms2zTWU91L6xtczZiTk0M7d+4stY9UYr3iDrjoruKYTCZ68MEHacKECbY9PtYO7sqVK/Tggw/Sq6++anf9nTt8dw/cUhHfihUrnHKVN2NZKxVnfWaz2al2O9tmVwtlK998842tILMe5SwsLLRdtyqCsySf9TpTV0dUv1/5lJiHJRXGSmcsfv2udXndsGED9evXz26a9ToyZ0ZYddZpPbJT1jLurK+sa+fKm6+sHSzO+rKzs53K54qz+AA1pVH8+r7//ve/VLt2bfrhhx8oIyOD0tPTacSIETRgwADKz893qs9xty8lJYW6du1KixcvvudzPHDgAHXu3NnhZTfudJa1bLvqK2u5ccXn7HrA3c7y+Epb77n7c777ntvvvfeerWi07uQxGAy2jW5n+i9nndbxC8qaj874ioqK7hnN2135yloOnfU5m89isdhdBmOdP2vWrKHx48fbvbaksWRE8Vn77LtHu66I07rcl/a5uOIr6RJLUXwljT1x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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAKyCAYAAACuWPzHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAD4XElEQVR4nOzdd3hUZfrG8Xsy6W0gPUAIIdRQpBcbvSgiq1hQse3qKrJrXde6ApbVdVdX14L+XDsgNiywgoIFFVB6DVJDS4WETHqbOb8/QkbGJGQCSWaSfD/XNReZ97TnDAnizXue12QYhiEAAAAAAAAAgEfwcncBAAAAAAAAAIBfEdoCAAAAAAAAgAchtAUAAAAAAAAAD0JoCwAAAAAAAAAehNAWAAAAAAAAADwIoS0AAAAAAAAAeBBCWwAAAAAAAADwIIS2AAAAAAAAAOBBCG0BAAAAAAAAwIMQ2gIAAABucsMNNyg4ONht1x85cqRGjhzptus3tNmzZ8tkMrm7DAAAgDNGaAsAAFq0t956SyaTSSaTST/++GO17YZhKC4uTiaTSRdddJHTtqrjanrdeuut+u677065z8mvk11xxRUymUy67777GvXeXTVy5EiX7mH27NmSpE6dOtW6z8SJE53O/eOPP+qCCy5Q+/bt5e/vr44dO2ry5MlasGCB034mk0l/+tOfXK755Zdflslk0tChQ+t1r3a7Xe+8846GDh2qsLAwhYSEqFu3brruuuv0008/OfZLTk7W7NmzdeDAgXqdvylVBZRVr8DAQCUlJenhhx9WXl6eu8ur09atW3XjjTcqISFB/v7+Cg4OVr9+/fTXv/5V+/fvd3d5euqpp2QymfTll1/WuP3CCy+UxWLR+PHjXfr5ueGGGyQ5/7x5eXkpNDRU3bt317XXXqvly5c34R0CAABP5u3uAgAAAJqCv7+/FixYoHPPPddpfOXKlTpy5Ij8/PxqPG7cuHG67rrrqo1369ZN8fHxevfdd53GH3jgAQUHB+uhhx6q8Xx5eXlavHixOnXqpPfee88RDLnTQw89pJtuusnxft26dfrPf/6jBx98UD179nSM9+3b1/F1v379dM8991Q7V7t27Rxff/jhh7ryyivVr18/3XHHHWrbtq1SUlL0/fff67XXXtPVV1992jXPnz9fnTp10tq1a7V371516dLFpeNuv/12vfTSS5oyZYquueYaeXt7a9euXVq6dKk6d+6sYcOGSaoMbefMmaORI0eqU6dOp11nU5g7d66Cg4NVUFCgr776Sk888YS++eYbrVq1qs7vra+++qqJqnT22muvacaMGYqIiNA111yjHj16qKKiQtu3b9c777yj5557TsXFxTKbzfU678MPP6z777+/QWq85557tGDBAt12223avn27AgICHNs+/PBDLV26VC+99JL69+/v9GdESkqKHnnkEf3xj3/Ueeed5xhPTEx0fN2hQwc9+eSTkqTCwkLt3btXixYt0rx583TFFVdo3rx58vHxaZD7AAAAzZQBAADQgr355puGJOPSSy81IiIijPLycqftN998szFw4EAjPj7emDRpktM2ScbMmTPrdb1evXoZI0aMqHX7G2+8Yfj4+BjffPONIcn47rvv6nX+pvDhhx8akoxvv/22xu01fVY1SUpKMnr16mWUlpZW25aZmen0vj6f9f79+w1JxqJFi4zIyEhj9uzZLh2XkZFhmEwm4+abb662zW63O9VU12fQUK6//nojKCjotI6dNWuWIck4evSo0/ill15qSDJWr15d67GFhYWndc2GsGrVKsNsNhvnn3++kZeXV217cXGx8fDDDxsVFRVuqM7ZmjVrDC8vL+OBBx5wjOXl5Rnt2rUzhg0bZthstmrHrFu3zpBkvPnmmzWec8SIEUavXr2qjVdUVBi33XabIcn461//2mD3AAAAmifaIwAAgFbhqquuUnZ2ttPjx2VlZfroo4/OaMZnfc2fP1/jxo3TqFGj1LNnT82fP7/OY8rLyxUWFqYbb7yx2ra8vDz5+/vrL3/5i2PshRdeUK9evRQYGKi2bdtq0KBB1doRNIV9+/Zp8ODB8vX1rbYtKirqtM87f/58tW3bVpMmTdJll13m0mcoVc6ANAxD55xzTrVtJpPJUdNbb72lyy+/XJI0atQox6Ps3333nWP/l19+Wb169ZKfn5/atWunmTNnKjc3t9p5f/75Z1144YVq27atgoKC1LdvXz3//POnrHPz5s2KjIzUyJEjVVBQ4NK9nWz06NGO+5UqH8fv3bu3NmzYoPPPP1+BgYF68MEHHdt+29O2pKREs2fPVrdu3eTv76/Y2Fhdeuml2rdvn2Mfu92u5557Tr169ZK/v7+io6N1yy236Pjx43XWN2fOHJlMJs2fP18hISHVtvv7++uxxx5zmmX7ww8/6PLLL1fHjh3l5+enuLg43XXXXSouLnY6tqaetlWtNz799FP17t1bfn5+6tWrl5YtW1ZnrcOGDdOtt96qf/3rX0pOTpZUOZs3KytL//d//ycvr4b73ymz2az//Oc/SkpK0osvviir1dpg5wYAAM0PoS0AAGgVOnXqpOHDh+u9995zjC1dulRWq1XTpk2r9biSkhIdO3as2qusrKzeNaSlpenbb7/VVVddJakySP7oo4/qPJePj48uueQSffrpp9X2/fTTT1VaWuq4h9dee0233367kpKS9Nxzz2nOnDnq16+ffv7553rXeyrl5eU1fi4nh2jx8fH6+uuvdeTIkQa99vz583XppZfK19dXV111lfbs2aN169bVeVx8fLykykfbi4qKat3v/PPP1+233y5JevDBB/Xuu+/q3XffdbSKmD17tmbOnKl27drpmWee0dSpU/Xqq69q/PjxKi8vd5xn+fLlOv/885WcnKw77rhDzzzzjEaNGqUlS5bUeu1169Zp9OjR6t+/v5YuXXpai5RVhavh4eGOsezsbF1wwQXq16+fnnvuOY0aNarGY202my666CLNmTNHAwcO1DPPPKM77rhDVqtV27dvd+x3yy236N5779U555yj559/XjfeeKPmz5+vCRMmOH0Gv1VUVKRvvvlGI0eOVIcOHVy+p6rfsxkzZuiFF17QhAkT9MILL9TYuqQmP/74o2677TZNmzZNTz/9tEpKSjR16lRlZ2fXeeyTTz6pyMhI3XLLLdqwYYNeeukl/eUvf1GfPn1crt9VZrNZV111lYqKimrswQ0AAFoRd0/1BQAAaExV7RHWrVtnvPjii0ZISIhRVFRkGIZhXH755caoUaMMw6j5kX9Jtb7ee++9Gq93qvYI//rXv4yAgADHI+G7d+82JBmffPJJnffx5ZdfGpKMxYsXO41feOGFRufOnR3vp0yZUuOj1/XhSnuE2j6XJ5980rHf66+/bkgyfH19jVGjRhl/+9vfjB9++KHGR8rlYnuE9evXG5KM5cuXG4ZR2dagQ4cOxh133OHSvV133XWGJKNt27bGJZdcYvzrX/8ydu7c6fJnkJWVZfj6+hrjx493uo8XX3zRkGS88cYbhmFUPuqekJBgxMfHG8ePH3c6h91ud3x9cnuEH3/80QgNDTUmTZpklJSU1HkvVe0Rdu3aZRw9etRISUkxXn31VcPPz8+Ijo52tEAYMWKEIcl45ZVXqp1jxIgRTt+vb7zxhiHJePbZZ6vtW1X3Dz/8YEgy5s+f77R92bJlNY6fbMuWLYYk484776y2LTs72zh69KjjdXJbjaqf2ZM9+eSThslkMg4ePFjtMzlZ1ffg3r17q9Xxwgsv1FrryT766CNDkhEWFmZ07ty5xnqqnG57hCqffPKJIcl4/vnnXaoNAAC0TMy0BQAArcYVV1yh4uJiLVmyRPn5+VqyZEmdrRGmTJmi5cuXV3vVNlPxVObPn69JkyY5Hgnv2rWrBg4c6NLj/aNHj1ZERITef/99x9jx48e1fPlyXXnllY6xNm3a6MiRIy7NPD0TQ4cOrfFzqZpFLEm///3vtWzZMo0cOVI//vijHnvsMZ133nnq2rWrVq9efVrXnT9/vqKjox2fv8lk0pVXXqmFCxfKZrPVefybb76pF198UQkJCfrkk0/0l7/8RT179tSYMWOUmppa5/ErVqxQWVmZ7rzzTqdH42+++WaFhobqf//7nyRp06ZNSklJ0Z133qk2bdo4naOmxcG+/fZbTZgwQWPGjNGiRYtqXRivJt27d1dkZKQSEhJ0yy23qEuXLvrf//6nwMBAxz5+fn41ttf4rY8//lgRERH685//XG1bVd0ffvihLBaLxo0b5zTLeuDAgQoODta3335b6/nz8vIkqcYZxJ07d1ZkZKTj9fnnnzu2nbwIWGFhoY4dO6azzz5bhmFo06ZNdd7X2LFjnRYC69u3r0JDQ7V///46j5WkqVOn6sILL1ROTo5eeuklp3oaWtVnk5+f32jXAAAAns/b3QUAAAA0lcjISI0dO1YLFixQUVGRbDabLrvsslMe06FDB40dO/aMr71z505t2rRJ1113nfbu3esYHzlypF566SXl5eUpNDS01uO9vb01depULViwQKWlpfLz89OiRYtUXl7uFNred999WrFihYYMGaIuXbpo/Pjxuvrqq2vs43omIiIiXPpcJkyYoAkTJqioqEgbNmzQ+++/r1deeUUXXXSRfvnll3r1trXZbFq4cKFGjRrl6NcqVQbIzzzzjL7++muNHz/+lOfw8vLSzJkzNXPmTGVnZ2vVqlV65ZVXtHTpUk2bNk0//PDDKY8/ePCgpMqg9GS+vr7q3LmzY3tVi4LevXvXeV8lJSWaNGmSBg4cqA8++EDe3vX7K/rHH3+s0NBQ+fj4qEOHDk7hZJX27dvX2Fv4t/bt26fu3bufsoY9e/bIarXW+nuXlZVV67FV/2BRU6/ezz77TOXl5dqyZYtTj2ZJOnTokB555BF9/vnn1frmutL7tWPHjtXG2rZt61IP3iqDBw/WF198oUGDBrl8zOmo+mxq6vcLAABaD0JbAADQqlx99dW6+eablZGRoQsuuKDaLMjGMm/ePEnSXXfdpbvuuqva9o8//rjOmZDTpk3Tq6++qqVLl+p3v/udPvjgA/Xo0UNnnXWWY5+ePXtq165dWrJkiZYtW6aPP/5YL7/8sh555BHNmTOnYW+qHgIDA3XeeefpvPPOU0REhObMmaOlS5fq+uuvd/kc33zzjdLT07Vw4UItXLiw2vb58+fXGdqeLDw8XBdffLEuvvhijRw5UitXrtTBgwcdvW+bip+fny688EJ99tlnWrZsmS666KJ6HX/++ecrIiLilPs05MxQu92uqKioWmeIR0ZG1npsly5d5O3t7dQft8qIESMkqVpgbLPZNG7cOOXk5Oi+++5Tjx49FBQUpNTUVN1www2y2+111nzyomYnMwyjzmObWtVn06VLFzdXAgAA3InQFgAAtCqXXHKJbrnlFv30009OrQYak2EYWrBggUaNGqXbbrut2vbHHntM8+fPrzO0Pf/88xUbG6v3339f5557rr755hs99NBD1fYLCgrSlVdeqSuvvFJlZWW69NJL9cQTT+iBBx6Qv79/g93X6aqaqZienl6v4+bPn6+oqCi99NJL1bYtWrRIn3zyiV555ZXTCigHDRqklStXKj09XfHx8TW2MJB+Xcxs165d6ty5s2O8rKxMKSkpjtnHVbNdt2/fXueMZJPJpPnz52vKlCm6/PLLtXTpUo0cObLe99AQEhMT9fPPP6u8vFw+Pj617rNixQqdc8459f6sg4KCHAF5amqq2rdvX+cx27Zt0+7du/X22287LTy2fPnyel27ObDZbFqwYIECAwN17rnnurscAADgRvS0BQAArUpwcLDmzp2r2bNna/LkyU1yzVWrVunAgQO68cYbddlll1V7XXnllfr222+VlpZ2yvN4eXnpsssu0+LFi/Xuu++qoqLCqTWCJGVnZzu99/X1VVJSkgzDUHl5eYPf26l8/fXXNY5/8cUXkqq3GDiV4uJiLVq0SBdddFGNn+Gf/vQn5efnO/VB/a2MjAwlJydXGy8rK9PXX38tLy8vx+zGoKAgSVJubq7TvmPHjpWvr6/+85//OM3SfP3112W1WjVp0iRJ0oABA5SQkKDnnnuu2jlqmt3p6+urRYsWafDgwZo8ebLWrl3r0ufS0KZOnapjx47pxRdfrLatqu4rrrhCNptNjz32WLV9Kioqqt3vbz3yyCOy2WyaPn16jW0Sfvv5VM2SPXncMAw9//zzdd5Pc2Kz2XT77bdr586duv3220/ZLgUAALR8zLQFAACtTn0eyd+9e7ejtcHJoqOjNW7cOJfOMX/+fJnNZkeg91sXX3yxHnroIS1cuFB33333Kc915ZVX6oUXXtCsWbPUp08f9ezZ02n7+PHjFRMTo3POOUfR0dHauXOnXnzxRacF0BpCampqjZ9LcHCwfve730mqXMQtISFBkydPVmJiogoLC7VixQotXrzYEU6ebP369Xr88cernXPkyJFKTU1Vfn6+Lr744hrrGTZsmCIjIzV//vxqQXaVI0eOaMiQIRo9erTGjBmjmJgYZWVl6b333tOWLVt05513OtoM9OvXT2azWf/4xz9ktVrl5+en0aNHKyoqSg888IDmzJmjiRMn6uKLL9auXbv08ssva/DgwZo+fbqkyoB97ty5mjx5svr166cbb7xRsbGx+uWXX7Rjxw59+eWX1eoLCAjQkiVLNHr0aF1wwQVauXKlSz1xG9J1112nd955R3fffbfWrl2r8847z/H7dtttt2nKlCkaMWKEbrnlFj355JPavHmzxo8fLx8fH+3Zs0cffvihnn/++VP2ij7vvPP04osv6s9//rO6du2qa665Rj169FBZWZl2796t+fPny9fXVzExMZKkHj16KDExUX/5y1+Umpqq0NBQffzxx/XqR+tprFar4+enqKhIe/fu1aJFi7Rv3z5NmzatxkAcAAC0MgYAAEAL9uabbxqSjHXr1p1yv/j4eGPSpElOY5JqfY0YMaLG8/Tq1ctpW1lZmREeHm6cd955p7x+QkKC0b9//zrvx263G3FxcYYk4/HHH6+2/dVXXzXOP/98Izw83PDz8zMSExONe++917BarXWeu8qHH35oSDK+/fbbGrfHx8fX+rnEx8c79nvvvfeMadOmGYmJiUZAQIDh7+9vJCUlGQ899JCRl5fndM5TfdaPPfaYMXnyZMPf398o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z9a///1vBQUFNWC1AAAAAAAAaCzZBaWaszhZn29JkyQlRATpqUv7aGjncDdX1jIR2tZXeZH093and+z3/6x81fa+Lg+mSb4NG3SeffbZ8vLychorKKhc5e/ZZ5/VZZddppiYGPXq1Utnn322pkyZogsuuKBe13j99dc1bdo0mc1m9e7dW507d9aHH36oG264oaFuAwAAAAAAAGfAZje0NiVHWfkligrx15CEMJm9TDIMQ4u3pmv25zuUU1gmL5N08/mdddfYbvL3Mbu77BaL0LaVe//999WzZ88atyUlJWn79u3asGGDVq1ape+//16TJ0/WDTfcoP/+978unT83N1eLFi3Sjz/+6BibPn26Xn/9dUJbAAAAAAAAD7Bse7rmLE5WurXEMRZr8dftY7rq651ZWrEzU5LUIyZET1/WV307tHFTpa0HoW19+QRWznitr6qWCGbfyjYJ599b2SqhvtduYHFxcerSpUut2728vDR48GANHjxYd955p+bNm6drr71WDz30kBISEuo8/4IFC1RSUuLUw9YwDNntdu3evVvdunVrkPsAAAAAAABA/S3bnq4Z8zbK+M14urVEDyzaJknyMZv0p1FdNWNkony9vaqfBA2O0La+TKb6tyhY+XRlYDvqocrFyKoWITP7Oi9O1gwkJSVJkgoLXevD+/rrr+uee+6pNqv2tttu0xtvvKGnnnqqoUsEAAAAAACAC2x2Q3MWJ1cLbE/mYzbps5nnKqldaJPVBULbxlcV0FYFttKvv377hPN7N8jOzlZGRobTWJs2beTv76/LLrtM55xzjs4++2zFxMQoJSVFDzzwgLp166YePXo49rdardq8ebPTOcLDw5Wdna2NGzdq/vz5TvtL0lVXXaVHH31Ujz/+uLy9+TYEAAAAAABoamtTcpxaItSk3GbIWlzeRBWhCmlZY7PbnAPbKlXv7bamr+kkY8eOrTb23nvvadq0aZowYYLee+89Pfnkk7JarYqJidHo0aM1e/Zsp6D1u+++U//+/Z3O8Yc//EEBAQFKSkqqFthK0iWXXKI//elP+uKLL3TxxRc3/I0BAAAAAAA0gNoW6GruDMPQlsO5Lu2blX/qYBcNz2QYxqlmQLcKeXl5slgsslqtCg39dap3SUmJUlJSlJCQIH9/fzdW2LrwuQMAAAAAAE9Q2wJdsyYnaWLvWDdWdnoMw1By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3r3bWrZsaQ8//LBt2bLFNm7caMOHD7cuXbrYHXfcEdQsv2g51v/yyy9Wq1Yte/TRR23v3r22fv1683g89sUXX4Qs041jvRvHebPoOda7sb+EAkVbFNvhw4dtxIgRNnz4cMvIyAgUt/7xj39Yq1at7MiRI0HfKQ4dOmS333673XHHHZaVlRU4+V599dX25JNP2h/+8IeQvvn517/+ZRMnTrQFCxbYxRdfbP3797f58+fbxIkTbcOGDUHLOXDggPXt29cSExMD86666irr2LGjeTweO//88+2FF14IWp7f4sWLrUWLFrZq1arAvFtvvdWuvPJKGzp0qN1+++2B1zmYb3xmz55t7du3ty1btgTmrVy50s444wyrUaNGvg/0wbJv3z5r166dffTRR4F5WVlZtnDhQmvTpo117do1MD8Y27HP57PXXnvNBgwYYAsWLLARI0ZY165d8xVug/1mMiMjw0aNGmXXX3+9HTx4MPDaff7551avXj3btWtXUPPyGjFiRKD4361bNzv//POtV69eduaZZ9qZZ55p69atM7PgrPOWLVusYcOG+VpdzZkzx+rXr29vvPGGmYWmBd+6deusZcuWNm/evMC8kSNHWvfu3e2iiy6ySy+91DZt2mRmwS3UpKSkWJs2bWz16tWBedu3b7c2bdpY3bp1rWPHjkHLMuNYXxaP9WZm11xzjT3//PNmZta9e3c777zzrFmzZtasWTPr3Llz4MN1sF5bN47zZmabNm2y5s2b2w8//BCYd/DgQfvvf/9rrVq1squuuiqoednZ2fbcc8/ZZZddZkuWLLGLL77YOnbsGNLCbUZGho0fP96uvvpqW758ubVq1cratWuX78NJsDMPHDhg1113XeCLT//j/+EPf7ALLrjAzIJ/Ttu7d6916dLFGjduHJh3/fXXW+fOnS0uLs4uv/zyfOf0YBowYIBNmTLFzMx69eplvXr1smrVqlnbtm3tiiuuCHreypUrzev12tixYwPz/vGPf1h8fLwtW7bMzELzBcCsWbOsdevW+fbT3/72t3bPPffYyJEjbcKECUHP/Oyzz6xLly62d+/ewDrNnTvXatasaU2bNg168YtzGue0YElJSbGmTZsG9kkz5zj1pz/9yTp06GAPPPBA0LLMoudYn5aWZkOHDrW7777bfD5f4PGvuOIK+/vf/24vv/yyzZo1K6iZfqV5rHfrOG8WHcd6N/aXUKFPWxSbmSkrK0vnnHOO4uPjFRMTI0mqXbu29u7dq4yMjKBnxsTE6IorrtBdd92l2NhYeb1ePfXUU/rPf/6j1atX69tvv9Wzzz6rBx54IOjZkpSYmKhp06apc+fOGj16tBITE3XllVdq3LhxKleunKTgDDgSGxur2267TXXr1tVll12mAQMGKDs7W48++qh+/PFHnXHGGZoyZUrQOyuvWLGiMjIy9O677yozM1NPPvmk/u///k+tWrVSvXr1NGfOHJ177rmSgttnZ0ZGhvbv369t27YF5h09elQ1a9bUY489pg0bNuj9998PWp4kJSUlyePx6Ntvvw3Mi42NVadOnfTaa6/pwIEDGj16tKTgDKDg8Xh0+eWX684771Tnzp3117/+VZ07d9aHH36oV155RYcOHQp6f6Rmpvj4ePXp00cVK1YM7KPVq1fX4cOHQ7KP5rV48WL961//UvXq1TVt2jT973//008//aQGDRpo0KBBkoKzHcXFxenVV1/Vs88+G5jXq1cvXXrppXr66aeVnp4eWPdgSkpKUnZ2tl555RWlpqbq8ccf1z//+U9ddtlluvTSS7V//37169dPGRkZQR+E48CBA0pJSQlcT0tLk9fr1QsvvKD9+/fney5KimN92TvWS86gdYsXL9Zrr72mpKQkffzxx/r55581ZcoUZWRk6IorrpAUvAFk3DjOS85r6fP59OOPPwbmVaxYUQMHDtS4ceO0fv16vfrqq0HLi4mJUZ8+fXTjjTeqffv2+vzzz1W7dm1NnDhR06ZNCxwPgnms93q96tatm4YMGaLWrVsrOTlZWVlZuvXWW7Vo0SL5fL6gH4N8Pp/S09PVtWtXeb3ewOM3atRIW7duVVZWVlDzJGc9r7jiClWvXl133XWX+vbtq/3792vEiBGaNm2a9u3bp+eff14zZswIenZ2draSk5M1ceJEVapUSR9++KFSUlL06KOPasOGDRo5cmRQ82JiYvTiiy9qwoQJgW1l+PDh6tq1qyZMmKDMzMyQDO4UGxurw4cP64svvpAkTZw4Ue+99568Xq92796tDz74QNdcc01QM9PS0rRv3z4dPXo0sE45OTlq3ry5hgwZop9++klz5swJWp7P5+OcxjktKCpVqqSsrKzAGApmpqpVq+qOO+7Q4MGDNXv2bH3++edBy/N6verevXtUHOsvvvhi3XHHHfJ4PPJ6vXr66ac1bdo0TZs2Ta+88ooefPBBvfDCC0HPLs1jvVvHeSk6jvVuvDcKGZeKxSgj/D/DNcttyTZv3jw7++yz833rtmLFiqBlHjlyJPB3SkqKVapUKV//nGPHjrVOnTrZjh07gpbp/2Zx9erV1q1bt8D8/v37W4UKFax79+42c+bMoOWZOS0B/vvf/1rTpk2tZ8+e+Z7r/fv323nnnWfXXnttUDP3799vo0ePtoYNG1q/fv0sLi7O/vvf/wZunzVrltWpUyfoPxH59ddfrUmTJnb99dfblClTbObMmZaUlBT45rFnz55B7RfL/63ak08+aT179rRPP/003+3Z2dn20EMPWd++fQMto0IhKyurwBa3b775Zokf27+Ou3fvDszz75ObNm2yFi1a2J49ewK3/fTTTyXOzJs7e/ZsO+ecc6xnz56Bn7z48+fPn28NGjSwn3/+ucR5BX277583a9Ysa9q0qf373/8+6X1LIisry/72t79Zo0aNbMCAAVahQgV7//33A7dv3LjRqlatmm9eMOzZs8cuvPBCu+KKK2zSpEn26aefWpUqVezBBx80M7Nrr73Wbr755qBmRtOx3szK9LHev4++88471q9fP+vfv789+uij+e7z4YcfWqtWrQIt4oNh06ZNduaZZ5bacd7v8OHDdvPNN1v//v1tyZIl+W47cuSIXXnllTZkyJCgZh7/E+CMjIx8LW79t3/yySdBy8y7v5iZHT16NF+rEjPntQ/m9rt58+bA3/7jwscff2ydO3fOd7+8LXlKas+ePfanP/3JzjjjDLvgggvy7f87d+60s846y+69996g5fmPCePHj7fLLrvMrrjiinz9/GdmZtqzzz5rPXv2tH379gUt92TGjx9vLVu2DDynwT6v7dixw6655hpr3Lix9e/f32JiYvIdd9966y0766yzLCUlJWiZK1eutAoVKtj9999vP/zwg82bN88SExMDLb2aNGlizzzzTFCy/Me/vNtNqM9p/sy87ydL65zmVxrnNP96Hj161D766KNSO6f5X6/SPqeZOb/au+CCC2zQoEEn/HotLS3NOnToYCNGjAhq5tGjR0+4XhaP9YcPHw78/dNPP1nlypVt6tSplp2dbVlZWTZ06FDr37//Cc9HcfnX68knnyyVY/2pWniG+jhv5u6x/r777gv5sd7PjfdGoUDRFkWya9cuW7Roka1cuTJfwSfvweSnn36yhg0b2sGDB83MeRPSr1+/Yh/g8mbu3bvXzJydy3+w2759e75leOONN6x169YlOqDmzcz7OD6fz84//3zbuHGj3XDDDVavXj177bXXbNCgQdalS5cSFTMLem4PHDhgn3/+uX355ZeBk4n/8v7777fzzjuvRAfygjL3799vGzZssB9++MHOPvvsfJ2wL1q0yJo1a2bz588vdubxuf6i4vz5861Dhw525plnWsOGDfMNInXdddcF+qkqrrwfov3bzsaNG61Xr1528cUX27fffpvv/u+99541a9YsX9GzJJnH87+OmZmZgcLtM888Y3fccYfFxMTYxo0bS5SZ9+dE/ut+a9eutTp16tjWrVvNzGzcuHHWrl27Yne4X9B67tmzx2655RaLi4uzvn375rtt2bJl1qpVqxK9ESjsz9z8r28wFLQNZWdn2759+2zFihXWunXrwOvm8/lszZo11rp16xO2reLm5n1Nf/nlFxs8eLC1bNnSWrRoke8Dyr333lvivjrT0tJs/fr1tnXr1sCx3Cy0x/q8mXn7eg7lsT5vZt4PCD6fzy644IKQHOsLWs+DBw+G9Fhf0Hr++uuv1qdPn8CAInnNnj3bWrRoUaKfzebN9A8K4z/ON23aNCTHeTPng8isWbNsxowZgW1m2bJlVrduXRs0aFC+bkXMzF588UXr0KFDvu28JJn+gkveY4SZ80HBX7j94IMP7M4777S6desGjsNFtW/fPlu7dq2lpqbm62LH5/MFBn88cuRI4MPJ3LlzbcSIEda+fftiH+v9mTt37sz3fOXtfmbq1KnWokWLwPUxY8bYddddF1jG4mampqYGMlNTU+3111+3r7766oTn+frrr7eBAwcWK6ugTP9yr1ixItAv3vFfLnz66afWsmVL27lzZ9By/fupf73867l//36rXbu2jRo1qkRZBWX6n99t27bZokWL7OOPP7YOHTrkG2xoxowZduaZZ9rKlSuDkuk/Nnz11VdWs2bNQHcpeZ/jiy++uMSFeP8+cfx7o1Ce0woahDWvUJzTTpUZqnNa3kz/dnrkyJGQntMKWs/Nmzdb3759Q3ZOM3O6udi1a5cdPHgwsAzz5s2z+Ph4u+eee04YAG3s2LHWp0+f024Lhc08vquvUB3rC1pPs/z7S7CP9adaz6NHj9qvv/5qZrnb0bPPPmsdOnTI994tGJkrV64M6bE+b6b/+Tz+p/rBPs4fn+v/XLFjx46QHusLyvz666+tVq1aITvWu/HeqDRQtEWhLV261Jo3b25Nmza1Bg0aWOfOnW3u3Lkn3O+HH36wKlWq2OHDh+3xxx+32NjYYhf5CpN5/En/vvvusyFDhhT7IH6qzMzMTDv//POtTp061rBhQ1u8eLGZOW82hw4dGjihBCNzzpw5ZuZ8M1/QyX7o0KF2zz33FLsvluMzO3XqFMg0czru7ty5c75vnh577DFr3759iVoBFJTr72tw165dtnnz5nwniKysLLvkkkvsqaeeMrPi9T2zcuVKu+6662zhwoWBx/CfnFesWBEYkMbf/2lmZqY98MADdv755xf7g/zxmQXxL4O/xW1CQoIlJiYWeyThwmSaOQWMChUq2M6dO238+PEWHx9f7H20oEz/Prl582YbMmSIlStXzm677Tbz+Xy2e/due/LJJ61Dhw7FftNTlOf2m2++sbp16+b79jgYmXmLiWbOm4TOnTvnG/DiiSeesFatWpWo5UFBuf7jQXp6uqWnp+c77vh8Phs8eHCJWiympKRYr169rEWLFnbmmWfa/fffX+BrFcxjfWEyj9/3S3qsP1VmRkZGSI71BWX6P7iH6lhfUKa/5dOaNWusS5cuVrVq1cCAIkeOHLHHH3/cevbsGfiiNBiZ/uJkampqSI7zZs5IyGeffba1bt3aGjdubL/5zW8CuQsXLrTKlSvbVVddZd98803gf+644w4bOHBgsX9VcXzmJZdccsK2639dMzIy7JJLLrG4uDirWLHiaY/VJ7N06VJr166dnXXWWXbmmWfakCFD7JdffjGz3OOv/wPS0aNHrW3bthYbG2vly5cPaaaZ03q4QYMGZmb26KOPWkxMTL5+v0uauXz5cjNzjn/Hv2Y5OTl2xRVX2Lhx44qVd7JMf9+CS5cuterVq1uDBg3yHevHjBljffv2LXDE+pLkHv/8+i8fffRR69q1a6DP9GBlDh48OPD8mjnFqO7du9v69esD88aMGWPdu3cv9hfaBWX6n99ff/3VkpOTA8dbM+d41KdPH3v55ZfNrHjHhtWrV9vo0aNP+MLmeME8pxUmM9jntJNl+ny+kH1+KSgzb8viUJzTCsr0v+dbu3ZtSM5pZs575379+lmbNm2sTZs29uKLLwYeb+rUqZaQkGDDhw/PNy7I9ddfb9ddd12xx1U4PvOll16y9PT0fPcJ9rG+MJlmwT3WFzYzrzvuuMNuueWWYg9ud6rXc/ny5SE51hdmPYN9nC8o98UXX8z35dD8+fODfqw/VeaWLVssJSUl6Md6N94blRaKtiiU7du3W6NGjeyRRx6xVatW2ccff2xDhw61uLi4wE9//TvXzz//bF26dLGHHnrIEhISAk3PQ5GZ16FDh2zs2LFWs2bNfB3CByvTP0DA+++/bz169DhhvYr7zeKpMv0doR+fM3bsWKtTp06xv/0qzHO7c+dO69q1q1144YU2ePBgu+WWW6xatWr5DrDByo2NjbV33333hPtv2bLFxo4dazVq1Djtm+2TWbdunTVs2NCqVKliV111VaAgmrf4tWrVKhs8eLC1aNHC6tevb3369LGqVasWe11PllkQ/0nkrrvusqpVqxZ72y1K5oYNG6xjx442fPjwEu2jp3pu/W9Qt27dar///e+tQYMGlpiYaJ06dbI6deoUuzBdlPU0c1oMNWzY0B5++OFit+ooTGZ6eroNHTrUevToYb169bJrr73WatSoUaL95VTPb0Hrsnr1ahszZoxVrVo18CalqH755RerWbOmPfTQQ/bDDz/YU089ZV27dg10k5L3TVSwjvVFyTQLzrH+dJlmZh988IH17NkzaMf6k2X+5z//KfD+wTjWFyZz3bp1ds0111ijRo2sVq1adt5551n16tWLvY8WdT2DcZw3c76Aq1Gjhv3+97+3DRs22JQpU6xZs2b53ogvWLDAOnbsaJ06dbI2bdrY5ZdfbomJiSd0m1DSzLzPnX9f9R8TR44cadWqVSv2trt582arU6eOPfjgg/bTTz/ZSy+9ZAMGDLAqVaoEvmDO+4WgmTMwZPXq1fMV5IKd6f8gNHXqVDv33HPt8ccft4SEhGJ/EDpZZlJSkv34449mlr9YnJ2dbePGjbP69esXezs6Veb3339vZs5I3126dLEWLVpYs2bN7NJLL7UqVaqU6FhflNfUzPmA7fF47F//+lfQM5OSkgKZ69evtxo1athll11m9957r40YMcKqVq1a7P3lVJl5Bwr027Nnj40dO9Zq165d7J+1r1271mrVqmWJiYn2wAMP2Nq1a09632Cd04qSaRacc9qpMv3n0GCf04q7niU5p50sM+/7zmCf08yc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", "text/plain": [ - "
" + "
" ] }, "metadata": {}, @@ -991,9 +985,9 @@ "--------------------------------------------------------------------------------\n", "\u001b[33massistant\u001b[0m (to ipython_user_proxy):\n", "\n", - "It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\n", + "It appears that the code executed successfully and the chart should have been displayed on your screen. Since I cannot view the chart, I will assume that you were able to see the META and TESLA stock price gains YTD plotted correctly.\n", "\n", - "If you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\n", + "If you have any further requests or need assistance with another task, feel free to ask. Otherwise, if everything is done, please let me know.\n", "\n", "TERMINATE\n", "\n", @@ -1003,10 +997,10 @@ { "data": { "text/plain": [ - "ChatResult(chat_id=None, chat_history=[{'content': 'Plot a chart of META and TESLA stock price gain YTDI have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.If you suggest code, the code will be executed in IPython.', 'role': 'assistant'}, {'content': 'To plot a chart of META and TESLA stock price gain YTD using the data from `stock_price_ytd.csv`, we will follow these steps:\\n\\n1. Read the CSV file into a DataFrame using pandas.\\n2. Plot the data using matplotlib.\\n\\nHere is the Python code to execute in IPython:\\n\\n```python\\n# filename: plot_stock_price_ytd.py\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv(\\'coding/stock_price_ytd.csv\\')\\n\\n# Check if the necessary columns \\'META\\' and \\'TESLA\\' are present\\nif \\'META\\' in df.columns and \\'TESLA\\' in df.columns:\\n # Plot the stock price gain YTD for META and TESLA\\n plt.figure(figsize=(10, 5))\\n plt.plot(df[\\'META\\'], label=\\'META\\')\\n plt.plot(df[\\'TESLA\\'], label=\\'TESLA\\')\\n plt.title(\\'META and TESLA Stock Price Gain YTD\\')\\n plt.xlabel(\\'Date\\')\\n plt.ylabel(\\'Stock Price Gain\\')\\n plt.legend()\\n plt.grid(True)\\n plt.show()\\nelse:\\n print(\"The required columns \\'META\\' and \\'TESLA\\' are not present in the CSV file.\")\\n```\\n\\nPlease execute this code in IPython to generate the plot. If there are any issues with the code, let me know, and I will provide the necessary corrections.', 'role': 'user'}, {'content': \"exitcode: 0 (execution succeeded)\\nCode output: \\nThe required columns 'META' and 'TESLA' are not present in the CSV file.\\n\", 'role': 'assistant'}, {'content': \"It seems that the expected column names 'META' and 'TESLA' are not present in the CSV file. To resolve this, we need to first inspect the CSV file to understand its structure and identify the correct column names. Let's write a Python code snippet to print the first few rows of the CSV file. This will help us determine the correct column names to use for plotting.\\n\\nPlease execute the following code:\\n\\n```python\\n# filename: inspect_csv_columns.py\\nimport pandas as pd\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv('coding/stock_price_ytd.csv')\\n\\n# Print the first few rows of the DataFrame to inspect the column names\\nprint(df.head())\\n```\\n\\nAfter executing this code, please provide the output so that I can suggest the next steps based on the actual column names in the CSV file.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n Date META TSLA\\n0 2024-01-02 346.290009 248.419998\\n1 2024-01-03 344.470001 238.449997\\n2 2024-01-04 347.119995 237.929993\\n3 2024-01-05 351.950012 237.490005\\n4 2024-01-08 358.660004 240.449997\\n', 'role': 'assistant'}, {'content': \"Thank you for providing the output. It appears that the column names for the stock prices are 'META' and 'TSLA', not 'TESLA'. With this information, we can now plot the chart correctly using the actual column names.\\n\\nHere is the updated Python code to plot the chart:\\n\\n```python\\n# filename: plot_stock_price_ytd_corrected.py\\nimport pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Read the CSV file into a DataFrame\\ndf = pd.read_csv('coding/stock_price_ytd.csv')\\n\\n# Plot the stock price gain YTD for META and TSLA\\nplt.figure(figsize=(10, 5))\\nplt.plot(df['Date'], df['META'], label='META')\\nplt.plot(df['Date'], df['TSLA'], label='TSLA')\\nplt.title('META and TSLA Stock Price Gain YTD')\\nplt.xlabel('Date')\\nplt.ylabel('Stock Price Gain')\\nplt.xticks(rotation=45) # Rotate the x-axis labels for better readability\\nplt.legend()\\nplt.grid(True)\\nplt.tight_layout() # Adjust layout to prevent clipping of labels\\nplt.show()\\n```\\n\\nPlease execute this code in IPython to generate the plot. If there are any further issues, let me know, and I will address them.\", 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n', 'role': 'assistant'}, {'content': 'It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\\n\\nIf you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\\n\\nTERMINATE', 'role': 'user'}], summary='It seems the code executed successfully and the plot was generated without any errors. If you can see the chart displaying the stock price gain YTD for META and TSLA, then the task has been completed successfully.\\n\\nIf you have any more requests or need further assistance, feel free to ask. Otherwise, if everything is done, we can conclude this task.\\n\\n', cost=({'total_cost': 1.0460099999999999, 'gpt-4': {'cost': 1.0460099999999999, 'prompt_tokens': 21049, 'completion_tokens': 6909, 'total_tokens': 27958}}, {'total_cost': 0.6627899999999999, 'gpt-4': {'cost': 0.6627899999999999, 'prompt_tokens': 13543, 'completion_tokens': 4275, 'total_tokens': 17818}}), human_input=[])" + "ChatResult(chat_id=None, chat_history=[{'content': 'Plot a chart of META and TESLA stock price gain YTD. Use data from the following csv file if it exists: coding/stock_price_ytd.csv. Use csv to read the file. Otherwise, figure out how to get the data.If you suggest code, the code will be executed in IPython.', 'role': 'assistant'}, {'content': 'First, we need to check if the file `coding/stock_price_ytd.csv` exists and if it contains the necessary data for META and TESLA stock price gains YTD (Year-To-Date). We will attempt to read the file using Python\\'s `csv` module. If the file does not exist or we cannot obtain the necessary data from it, we will then look for an alternative way to get the data.\\n\\nLet\\'s start by checking if the file exists and reading its contents.\\n\\n```python\\n# filename: check_csv_file.py\\n\\nimport csv\\nimport os\\n\\n# Define the path to the CSV file\\nfile_path = \\'coding/stock_price_ytd.csv\\'\\n\\n# Check if the file exists\\nif os.path.exists(file_path):\\n try:\\n # Attempt to read the file and print its contents\\n with open(file_path, mode=\\'r\\') as file:\\n csv_reader = csv.DictReader(file)\\n meta_data = []\\n tesla_data = []\\n for row in csv_reader:\\n if \\'META\\' in row:\\n meta_data.append(row)\\n if \\'TESLA\\' in row:\\n tesla_data.append(row)\\n print(\"META data:\", meta_data)\\n print(\"TESLA data:\", tesla_data)\\n except Exception as e:\\n print(f\"An error occurred while reading the file: {e}\")\\nelse:\\n print(\"The file does not exist.\")\\n```\\n\\nPlease execute the above code to check for the file and read its contents. If the file exists and contains the required data, we will proceed to plot the chart. If not, we will explore alternative ways to obtain the data.', 'role': 'user'}, {'content': \"exitcode: 0 (execution succeeded)\\nCode output: \\nMETA data: [{'Date': '2024-01-02', 'META': '346.2900085449219', 'TSLA': '248.4199981689453'}, {'Date': '2024-01-03', 'META': '344.4700012207031', 'TSLA': '238.4499969482422'}, {'Date': '2024-01-04', 'META': '347.1199951171875', 'TSLA': '237.92999267578125'}, {'Date': '2024-01-05', 'META': '351.95001220703125', 'TSLA': '237.49000549316406'}, {'Date': '2024-01-08', 'META': '358.6600036621094', 'TSLA': '240.4499969482422'}, {'Date': '2024-01-09', 'META': '357.42999267578125', 'TSLA': '234.9600067138672'}, {'Date': '2024-01-10', 'META': '370.4700012207031', 'TSLA': '233.94000244140625'}, {'Date': '2024-01-11', 'META': '369.6700134277344', 'TSLA': '227.22000122070312'}, {'Date': '2024-01-12', 'META': '374.489990234375', 'TSLA': '218.88999938964844'}, {'Date': '2024-01-16', 'META': '367.4599914550781', 'TSLA': '219.91000366210938'}, {'Date': '2024-01-17', 'META': '368.3699951171875', 'TSLA': '215.5500030517578'}, {'Date': '2024-01-18', 'META': '376.1300048828125', 'TSLA': '211.8800048828125'}, {'Date': '2024-01-19', 'META': '383.45001220703125', 'TSLA': '212.19000244140625'}, {'Date': '2024-01-22', 'META': '381.7799987792969', 'TSLA': '208.8000030517578'}, {'Date': '2024-01-23', 'META': '385.20001220703125', 'TSLA': '209.13999938964844'}, {'Date': '2024-01-24', 'META': '390.70001220703125', 'TSLA': '207.8300018310547'}, {'Date': '2024-01-25', 'META': '393.17999267578125', 'TSLA': '182.6300048828125'}, {'Date': '2024-01-26', 'META': '394.1400146484375', 'TSLA': '183.25'}, {'Date': '2024-01-29', 'META': '401.0199890136719', 'TSLA': '190.92999267578125'}, {'Date': '2024-01-30', 'META': '400.05999755859375', 'TSLA': '191.58999633789062'}, {'Date': '2024-01-31', 'META': '390.1400146484375', 'TSLA': '187.2899932861328'}, {'Date': '2024-02-01', 'META': '394.7799987792969', 'TSLA': '188.86000061035156'}, {'Date': '2024-02-02', 'META': '474.989990234375', 'TSLA': '187.91000366210938'}, {'Date': '2024-02-05', 'META': '459.4100036621094', 'TSLA': '181.05999755859375'}, {'Date': '2024-02-06', 'META': '454.7200012207031', 'TSLA': '185.10000610351562'}, {'Date': '2024-02-07', 'META': '469.5899963378906', 'TSLA': '187.5800018310547'}, {'Date': '2024-02-08', 'META': '470.0', 'TSLA': '189.55999755859375'}, {'Date': '2024-02-09', 'META': '468.1099853515625', 'TSLA': '193.57000732421875'}, {'Date': '2024-02-12', 'META': '468.8999938964844', 'TSLA': '188.1300048828125'}, {'Date': '2024-02-13', 'META': '460.1199951171875', 'TSLA': '184.02000427246094'}, {'Date': '2024-02-14', 'META': '473.2799987792969', 'TSLA': '188.7100067138672'}, {'Date': '2024-02-15', 'META': '484.0299987792969', 'TSLA': '200.4499969482422'}, {'Date': '2024-02-16', 'META': '473.32000732421875', 'TSLA': '199.9499969482422'}, {'Date': '2024-02-20', 'META': '471.75', 'TSLA': '193.75999450683594'}, {'Date': '2024-02-21', 'META': '468.0299987792969', 'TSLA': '194.77000427246094'}, {'Date': '2024-02-22', 'META': '486.1300048828125', 'TSLA': '197.41000366210938'}, {'Date': '2024-02-23', 'META': '484.0299987792969', 'TSLA': '191.97000122070312'}, {'Date': '2024-02-26', 'META': '481.739990234375', 'TSLA': '199.39999389648438'}, {'Date': '2024-02-27', 'META': '487.04998779296875', 'TSLA': '199.72999572753906'}, {'Date': '2024-02-28', 'META': '484.0199890136719', 'TSLA': '202.0399932861328'}, {'Date': '2024-02-29', 'META': '490.1300048828125', 'TSLA': '201.8800048828125'}, {'Date': '2024-03-01', 'META': '502.29998779296875', 'TSLA': '202.63999938964844'}]\\nTESLA data: []\\n\", 'role': 'assistant'}, {'content': 'The output indicates that the file `coding/stock_price_ytd.csv` exists and contains the necessary data for META stock price gains YTD. However, it seems that the TESLA data is not separated correctly, as the TESLA data appears to be within the same rows as the META data, but the script is looking for a separate \\'TESLA\\' entry in the rows.\\n\\nWe need to adjust the script to correctly parse the TESLA data from the same rows as the META data. Let\\'s modify the script to extract both META and TESLA stock prices from the same rows and then plot the chart.\\n\\n```python\\n# filename: plot_stock_prices.py\\n\\nimport csv\\nimport os\\nimport matplotlib.pyplot as plt\\n\\n# Define the path to the CSV file\\nfile_path = \\'coding/stock_price_ytd.csv\\'\\n\\n# Initialize lists to hold the parsed data\\ndates = []\\nmeta_prices = []\\ntesla_prices = []\\n\\n# Check if the file exists\\nif os.path.exists(file_path):\\n try:\\n # Read the file and parse the data\\n with open(file_path, mode=\\'r\\') as file:\\n csv_reader = csv.DictReader(file)\\n for row in csv_reader:\\n dates.append(row[\\'Date\\'])\\n meta_prices.append(float(row[\\'META\\']))\\n tesla_prices.append(float(row[\\'TSLA\\']))\\n \\n # Plot the data\\n plt.figure(figsize=(14, 7))\\n plt.plot(dates, meta_prices, label=\\'META\\', marker=\\'o\\')\\n plt.plot(dates, tesla_prices, label=\\'TESLA\\', marker=\\'x\\')\\n \\n # Formatting the plot\\n plt.title(\\'META vs TESLA Stock Price Gain YTD\\')\\n plt.xlabel(\\'Date\\')\\n plt.ylabel(\\'Stock Price\\')\\n plt.xticks(rotation=45)\\n plt.legend()\\n plt.tight_layout()\\n \\n # Show the plot\\n plt.show()\\n except Exception as e:\\n print(f\"An error occurred while processing the file: {e}\")\\nelse:\\n print(\"The file does not exist.\")\\n```\\n\\nPlease execute the above code to plot the chart of META and TESLA stock price gains YTD.', 'role': 'user'}, {'content': 'exitcode: 0 (execution succeeded)\\nCode output: \\n', 'role': 'assistant'}, {'content': 'It appears that the code executed successfully and the chart should have been displayed on your screen. Since I cannot view the chart, I will assume that you were able to see the META and TESLA stock price gains YTD plotted correctly.\\n\\nIf you have any further requests or need assistance with another task, feel free to ask. Otherwise, if everything is done, please let me know.\\n\\nTERMINATE', 'role': 'user'}], summary='It appears that the code executed successfully and the chart should have been displayed on your screen. Since I cannot view the chart, I will assume that you were able to see the META and TESLA stock price gains YTD plotted correctly.\\n\\nIf you have any further requests or need assistance with another task, feel free to ask. Otherwise, if everything is done, please let me know.\\n\\n', cost=({'total_cost': 2.1070799999999994, 'gpt-4': {'cost': 2.1070799999999994, 'prompt_tokens': 45338, 'completion_tokens': 12449, 'total_tokens': 57787}}, {'total_cost': 1.7238599999999995, 'gpt-4': {'cost': 1.7238599999999995, 'prompt_tokens': 37832, 'completion_tokens': 9815, 'total_tokens': 47647}}), human_input=[])" ] }, - "execution_count": 18, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1026,8 +1020,8 @@ "ipy_user.initiate_chat(\n", " assistant,\n", " message=my_ipy_message_generator,\n", - " raw_message=\"\"\"Plot a chart of META and TESLA stock price gain YTD\"\"\",\n", - " carryover=\"I have collected and saved their stock price change YTD to coding/stock_price_ytd.csv.\",\n", + " raw_message=\"\"\"Plot a chart of META and TESLA stock price gain YTD. \"\"\",\n", + " carryover=\"Use data from the following csv file if it exists: coding/stock_price_ytd.csv. Use csv to read the file. Otherwise, figure out how to get the data.\",\n", ")" ] } From d717a1a1901374493948b481955ee1c58888077b Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 20:38:32 -0500 Subject: [PATCH 09/26] CompressibleAgent --- autogen/agentchat/contrib/compressible_agent.py | 2 +- notebook/agentchat_compression.ipynb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index 92adf74b02bc..7945fc90acee 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -19,7 +19,7 @@ def colored(x, *args, **kwargs): class CompressibleAgent(ConversableAgent): - """(Attention: CompressibleAgent Retiring Soon!) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, + """(Attention: CompressibleAgent Retiring Soon! Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, it also provides the added feature of compression when activated through the `compress_config` setting. `compress_config` is set to False by default, making this agent equivalent to the `AssistantAgent`. diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index 5de4d32166c4..cb968eaee90c 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -11,7 +11,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Attention: CompressibleAgent Retiring Soon.)\n", + "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Retiring Soon.)\n", + "\n", + "**Attention: CompressibleAgent Retiring Soon.** \n", + "\n", + "Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.\n", "\n", "AutoGen offers conversable agents powered by LLM, tools, or humans, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participance through multi-agent conversation. Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n", "\n", From f750ff23597f89cbc7fe5cf3eb97a1688b3cab87 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 21:59:55 -0500 Subject: [PATCH 10/26] update notebook --- ...at_auto_feedback_from_code_execution.ipynb | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/notebook/agentchat_auto_feedback_from_code_execution.ipynb b/notebook/agentchat_auto_feedback_from_code_execution.ipynb index 83a612168bca..e639a6475e1b 100644 --- a/notebook/agentchat_auto_feedback_from_code_execution.ipynb +++ b/notebook/agentchat_auto_feedback_from_code_execution.ipynb @@ -421,7 +421,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -477,14 +477,16 @@ "source": [ "# Path to your CSV file\n", "file_path = \"coding/stock_price_ytd.csv\"\n", - "\n", - "with open(file_path, mode=\"r\", encoding=\"utf-8\") as file:\n", - " # Read each line in the file\n", - " for line in file:\n", - " # Split the line into a list using the comma as a separator\n", - " row = line.strip().split(\",\")\n", - " # Print the list representing the current row\n", - " print(row)" + "try:\n", + " with open(file_path, mode=\"r\", encoding=\"utf-8\") as file:\n", + " # Read each line in the file\n", + " for line in file:\n", + " # Split the line into a list using the comma as a separator\n", + " row = line.strip().split(\",\")\n", + " # Print the list representing the current row\n", + " print(row)\n", + "except FileNotFoundError:\n", + " print(\"File not found. Please check the file name and modify if necessary.\")" ] }, { @@ -683,11 +685,13 @@ "def my_message_generator(sender, recipient, context):\n", " # your CSV file\n", " file_name = context.get(\"file_name\")\n", - " with open(file_name, mode=\"r\", encoding=\"utf-8\") as file:\n", - " file_content = file.read()\n", + " try:\n", + " with open(file_name, mode=\"r\", encoding=\"utf-8\") as file:\n", + " file_content = file.read()\n", + " except FileNotFoundError:\n", + " file_content = \"No data found.\"\n", " return \"Analyze the data and write a brief but engaging blog post. \\n Data: \\n\" + file_content\n", "\n", - "\n", "# followup of the previous question\n", "chat_res = user_proxy.initiate_chat(\n", " recipient=assistant,\n", From ec6e6be6c0c9df7cbb3d847da62ad9720ae0ecc8 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 22:34:40 -0500 Subject: [PATCH 11/26] add test --- test/agentchat/test_assistant_agent.py | 46 ++++++++++++++++---------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/test/agentchat/test_assistant_agent.py b/test/agentchat/test_assistant_agent.py index fbe8a82ece26..dff12ee3ed23 100755 --- a/test/agentchat/test_assistant_agent.py +++ b/test/agentchat/test_assistant_agent.py @@ -200,40 +200,52 @@ def tsp_message(sender, recipient, context): @pytest.mark.skipif(skip, reason="openai not installed OR requested to skip") -def test_message(human_input_mode="NEVER", max_consecutive_auto_reply=10): +def test_message_func(human_input_mode="NEVER", max_consecutive_auto_reply=10): + import random + + class Function: + call_count = 0 + + def get_random_number(self): + self.call_count += 1 + return random.randint(0, 100) + config_list = autogen.config_list_from_json( OAI_CONFIG_LIST, file_location=KEY_LOC, ) - def my_message(sender, recipient, context): + def my_message_play(sender, recipient, context): final_msg = {} - final_msg["content"] = "Make a joke about AI." - final_msg["context"] = {"prefix": "Today I feel"} + final_msg["content"] = "Let's play a game." + final_msg["function_call"] = {"name": "get_random_number", "arguments": "{}"} return final_msg + func = Function() # autogen.ChatCompletion.start_logging() - assistant = AssistantAgent("assistant", llm_config={"config_list": config_list}) user = UserProxyAgent( "user", code_execution_config={ "work_dir": here, - # "use_docker": False, + "use_docker": False, }, human_input_mode=human_input_mode, max_consecutive_auto_reply=max_consecutive_auto_reply, ) - chat_res = user.initiate_chat( - assistant, - message=my_message, + player = autogen.AssistantAgent( + name="Player", + system_message="You will use function `get_random_number` to get a random number. Stop only when you get at least 1 even number and 1 odd number. Reply TERMINATE to stop.", + description="A player that makes function_calls.", + llm_config={"config_list": config_list}, + function_map={"get_random_number": func.get_random_number}, + ) + + chat_res_play = user.initiate_chat( + player, + message=my_message_play, max_turns=1, ) - print(chat_res.summary) - print(chat_res.chat_history) - # print(autogen.ChatCompletion.logged_history) - # autogen.ChatCompletion.stop_logging() - # print(chat_res.summary) - print(chat_res.cost) + print(chat_res_play.summary) if __name__ == "__main__": @@ -242,5 +254,5 @@ def my_message(sender, recipient, context): # when GPT-4, i.e., the DEFAULT_MODEL, is used, conversation in the following test # should terminate in 2-3 rounds of interactions (because is_termination_msg should be true after 2-3 rounds) # although the max_consecutive_auto_reply is set to 10. - test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) - test_message(human_input_mode="NEVER", max_consecutive_auto_reply=10) + # test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) + test_message_func(human_input_mode="NEVER", max_consecutive_auto_reply=10) From 39be02b524f010b1ac6f821358acbf3633dcf780 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 22:41:29 -0500 Subject: [PATCH 12/26] retrieve agent --- test/agentchat/contrib/test_retrievechat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/agentchat/contrib/test_retrievechat.py b/test/agentchat/contrib/test_retrievechat.py index cd6a8c04856a..bed407a13198 100755 --- a/test/agentchat/contrib/test_retrievechat.py +++ b/test/agentchat/contrib/test_retrievechat.py @@ -68,7 +68,9 @@ def test_retrievechat(): assistant.reset() code_problem = "How can I use FLAML to perform a classification task, set use_spark=True, train 30 seconds and force cancel jobs if time limit is reached." - ragproxyagent.initiate_chat(assistant, problem=code_problem, search_string="spark", silent=True) + ragproxyagent.initiate_chat( + assistant, message=ragproxyagent.message_generator, problem=code_problem, search_string="spark", silent=True + ) print(conversations) From 25f5ea4676b416c72eabd3452037f13fda236700 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 22:49:04 -0500 Subject: [PATCH 13/26] update test --- test/agentchat/contrib/test_qdrant_retrievechat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/agentchat/contrib/test_qdrant_retrievechat.py b/test/agentchat/contrib/test_qdrant_retrievechat.py index 63a50e9243f3..5d30ad8c2dca 100755 --- a/test/agentchat/contrib/test_qdrant_retrievechat.py +++ b/test/agentchat/contrib/test_qdrant_retrievechat.py @@ -73,7 +73,7 @@ def test_retrievechat(): assistant.reset() code_problem = "How can I use FLAML to perform a classification task, set use_spark=True, train 30 seconds and force cancel jobs if time limit is reached." - ragproxyagent.initiate_chat(assistant, problem=code_problem, silent=True) + ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=code_problem, silent=True) print(conversations) From 4b6727bd07f8ddbda4c10187874223a1ee42c092 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 23:26:00 -0500 Subject: [PATCH 14/26] summary_method args --- autogen/agentchat/conversable_agent.py | 1 + test/agentchat/test_async_chats.py | 2 +- test/agentchat/test_chats.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 1fa47284578a..3479a6dcc3a2 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1072,6 +1072,7 @@ def _summarize_chat( def my_summary_method( sender: ConversableAgent, recipient: ConversableAgent, + summary_args: dict, ): return recipient.last_message(sender)["content"] ``` diff --git a/test/agentchat/test_async_chats.py b/test/agentchat/test_async_chats.py index 63b3753f41f4..8ad0216a7686 100755 --- a/test/agentchat/test_async_chats.py +++ b/test/agentchat/test_async_chats.py @@ -59,7 +59,7 @@ async def test_async_chats(): }, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly. ) - def my_summary_method(recipient, sender): + def my_summary_method(recipient, sender, summary_args): return recipient.chat_messages[sender][0].get("content", "") chat_res = await user.a_initiate_chats( diff --git a/test/agentchat/test_chats.py b/test/agentchat/test_chats.py index 7f4a42cac6f3..0f3cd8cbd781 100755 --- a/test/agentchat/test_chats.py +++ b/test/agentchat/test_chats.py @@ -192,7 +192,7 @@ def test_chats(): }, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly. ) - def my_summary_method(recipient, sender): + def my_summary_method(recipient, sender, summary_args): return recipient.chat_messages[sender][0].get("content", "") chat_res = user.initiate_chats( @@ -297,7 +297,7 @@ def test_chats_general(): }, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly. ) - def my_summary_method(recipient, sender): + def my_summary_method(recipient, sender, summary_args): return recipient.chat_messages[sender][0].get("content", "") chat_res = initiate_chats( From 2bd56a32b8d4ae625a9fc6283c6949f07689533b Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Mon, 4 Mar 2024 23:46:22 -0500 Subject: [PATCH 15/26] summary --- test/agentchat/test_assistant_agent.py | 52 +--------- test/agentchat/test_conversable_agent.py | 123 ++++++++++++++++++++++- 2 files changed, 122 insertions(+), 53 deletions(-) diff --git a/test/agentchat/test_assistant_agent.py b/test/agentchat/test_assistant_agent.py index dff12ee3ed23..b99ed5766fb9 100755 --- a/test/agentchat/test_assistant_agent.py +++ b/test/agentchat/test_assistant_agent.py @@ -199,60 +199,10 @@ def tsp_message(sender, recipient, context): print(chat_res.cost) -@pytest.mark.skipif(skip, reason="openai not installed OR requested to skip") -def test_message_func(human_input_mode="NEVER", max_consecutive_auto_reply=10): - import random - - class Function: - call_count = 0 - - def get_random_number(self): - self.call_count += 1 - return random.randint(0, 100) - - config_list = autogen.config_list_from_json( - OAI_CONFIG_LIST, - file_location=KEY_LOC, - ) - - def my_message_play(sender, recipient, context): - final_msg = {} - final_msg["content"] = "Let's play a game." - final_msg["function_call"] = {"name": "get_random_number", "arguments": "{}"} - return final_msg - - func = Function() - # autogen.ChatCompletion.start_logging() - user = UserProxyAgent( - "user", - code_execution_config={ - "work_dir": here, - "use_docker": False, - }, - human_input_mode=human_input_mode, - max_consecutive_auto_reply=max_consecutive_auto_reply, - ) - player = autogen.AssistantAgent( - name="Player", - system_message="You will use function `get_random_number` to get a random number. Stop only when you get at least 1 even number and 1 odd number. Reply TERMINATE to stop.", - description="A player that makes function_calls.", - llm_config={"config_list": config_list}, - function_map={"get_random_number": func.get_random_number}, - ) - - chat_res_play = user.initiate_chat( - player, - message=my_message_play, - max_turns=1, - ) - print(chat_res_play.summary) - - if __name__ == "__main__": # test_gpt35() # test_create_execute_script(human_input_mode="TERMINATE") # when GPT-4, i.e., the DEFAULT_MODEL, is used, conversation in the following test # should terminate in 2-3 rounds of interactions (because is_termination_msg should be true after 2-3 rounds) # although the max_consecutive_auto_reply is set to 10. - # test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) - test_message_func(human_input_mode="NEVER", max_consecutive_auto_reply=10) + test_tsp(human_input_mode="NEVER", max_consecutive_auto_reply=10) diff --git a/test/agentchat/test_conversable_agent.py b/test/agentchat/test_conversable_agent.py index e55757d14c52..078ad0f0ab30 100755 --- a/test/agentchat/test_conversable_agent.py +++ b/test/agentchat/test_conversable_agent.py @@ -14,7 +14,7 @@ from pydantic import BaseModel, Field from typing_extensions import Annotated import autogen - +import os from autogen.agentchat import ConversableAgent, UserProxyAgent from autogen.agentchat.conversable_agent import register_function from test_assistant_agent import KEY_LOC, OAI_CONFIG_LIST @@ -27,6 +27,8 @@ else: skip = False or skip_openai +here = os.path.abspath(os.path.dirname(__file__)) + @pytest.fixture def conversable_agent(): @@ -1086,6 +1088,121 @@ def test_max_turn(): assert len(res.chat_history) <= 6 +@pytest.mark.skipif(skip, reason="openai not installed OR requested to skip") +def test_message_func(): + import random + + class Function: + call_count = 0 + + def get_random_number(self): + self.call_count += 1 + return random.randint(0, 100) + + config_list = autogen.config_list_from_json( + OAI_CONFIG_LIST, + file_location=KEY_LOC, + ) + + def my_message_play(sender, recipient, context): + final_msg = {} + final_msg["content"] = "Let's play a game." + final_msg["function_call"] = {"name": "get_random_number", "arguments": "{}"} + return final_msg + + func = Function() + # autogen.ChatCompletion.start_logging() + user = UserProxyAgent( + "user", + code_execution_config={ + "work_dir": here, + "use_docker": False, + }, + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + ) + player = autogen.AssistantAgent( + name="Player", + system_message="You will use function `get_random_number` to get a random number. Stop only when you get at least 1 even number and 1 odd number. Reply TERMINATE to stop.", + description="A player that makes function_calls.", + llm_config={"config_list": config_list}, + function_map={"get_random_number": func.get_random_number}, + ) + + chat_res_play = user.initiate_chat( + player, + message=my_message_play, + max_turns=1, + ) + print(chat_res_play.summary) + + +@pytest.mark.skipif(skip, reason="openai not installed OR requested to skip") +def test_summary(): + import random + + class Function: + call_count = 0 + + def get_random_number(self): + self.call_count += 1 + return random.randint(0, 100) + + config_list = autogen.config_list_from_json( + OAI_CONFIG_LIST, + file_location=KEY_LOC, + ) + + def my_message_play(sender, recipient, context): + final_msg = {} + final_msg["content"] = "Let's play a game." + final_msg["function_call"] = {"name": "get_random_number", "arguments": "{}"} + return final_msg + + def my_summary(sender, recipient, summary_args): + prefix = summary_args.get("prefix", "Summary:") + return prefix + recipient.chat_messages[sender][-1].get("content", "") + + func = Function() + # autogen.ChatCompletion.start_logging() + user = UserProxyAgent( + "user", + code_execution_config={ + "work_dir": here, + "use_docker": False, + }, + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + ) + player = autogen.AssistantAgent( + name="Player", + system_message="You will use function `get_random_number` to get a random number. Stop only when you get at least 1 even number and 1 odd number. Reply TERMINATE to stop.", + description="A player that makes function_calls.", + llm_config={"config_list": config_list}, + function_map={"get_random_number": func.get_random_number}, + ) + + chat_res_play = user.initiate_chat( + player, + message=my_message_play, + # message="Make a joke about AI", + max_turns=1, + summary_method="reflection_with_llm", + summary_args={"summary_prompt": "Summarize the conversation into less than five words."}, + ) + print(chat_res_play.summary) + + chat_res_play = user.initiate_chat( + player, + # message=my_message_play, + message="Make a joke about AI", + max_turns=1, + summary_method=my_summary, + summary_args={"prefix": "This is the last message:"}, + ) + print(chat_res_play.summary) + + def test_process_before_send(): print_mock = unittest.mock.MagicMock() @@ -1114,4 +1231,6 @@ def send_to_frontend(sender, message, recipient, silent): # test_conversable_agent() # test_no_llm_config() # test_max_turn() - test_process_before_send() + # test_process_before_send() + # test_message_func() + test_summary() From 5c252da96330bfdcf5337595177b73abf22ce7ab Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Tue, 5 Mar 2024 09:46:04 -0500 Subject: [PATCH 16/26] carryover --- autogen/agentchat/conversable_agent.py | 4 +- test/agentchat/test_chats.py | 56 ++++++++++++++++++-------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 3479a6dcc3a2..d8aedbec54bc 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -2185,8 +2185,7 @@ def generate_init_message(self, message: Union[str, None], **context) -> Union[s """ if message is None: message = self.get_human_input(">") - self._process_carryover(message, context) - return message + return self._process_carryover(message, context) def _process_carryover(self, message, context): carryover = context.get("carryover") @@ -2200,6 +2199,7 @@ def _process_carryover(self, message, context): raise warnings.warn( "Carryover should be a string or a list of strings. Not adding carryover to the message." ) + return message async def a_generate_init_message(self, message: Union[str, None], **context) -> Union[str, Dict]: """Generate the initial message for the agent. diff --git a/test/agentchat/test_chats.py b/test/agentchat/test_chats.py index 0f3cd8cbd781..2862220dfd1f 100755 --- a/test/agentchat/test_chats.py +++ b/test/agentchat/test_chats.py @@ -16,14 +16,16 @@ def test_chat_messages_for_summary(): - assistant = UserProxyAgent(name="assistant", human_input_mode="NEVER") - user = UserProxyAgent(name="user", human_input_mode="NEVER") + assistant = UserProxyAgent(name="assistant", human_input_mode="NEVER", code_execution_config={"use_docker": False}) + user = UserProxyAgent(name="user", human_input_mode="NEVER", code_execution_config={"use_docker": False}) user.send("What is the capital of France?", assistant) messages = assistant.chat_messages_for_summary(user) assert len(messages) == 1 groupchat = GroupChat(agents=[user, assistant], messages=[], max_round=2) - manager = GroupChatManager(groupchat=groupchat, name="manager", llm_config=False) + manager = GroupChatManager( + groupchat=groupchat, name="manager", llm_config=False, code_execution_config={"use_docker": False} + ) user.initiate_chat(manager, message="What is the capital of France?") messages = manager.chat_messages_for_summary(user) assert len(messages) == 2 @@ -42,10 +44,10 @@ def test_chats_group(): ) financial_tasks = [ """What are the full names of NVDA and TESLA.""", - """Pros and cons of the companies I'm interested in. Keep it short.""", + """Give lucky numbers for them.""", ] - writing_tasks = ["""Develop a short but engaging blog post using any information provided."""] + writing_tasks = ["""Make a joke."""] user_proxy = UserProxyAgent( name="User_proxy", @@ -126,13 +128,15 @@ def test_chats_group(): "recipient": financial_assistant, "message": financial_tasks[0], "summary_method": "last_msg", + "max_turns": 1, }, { "recipient": manager_1, "message": financial_tasks[1], "summary_method": "reflection_with_llm", + "max_turns": 1, }, - {"recipient": manager_2, "message": writing_tasks[0]}, + {"recipient": manager_2, "message": writing_tasks[0], "max_turns": 1}, ] ) @@ -148,26 +152,44 @@ def test_chats_group(): @pytest.mark.skipif(skip_openai, reason="requested to skip openai tests") def test_chats(): + import random + + class Function: + call_count = 0 + + def get_random_number(self): + self.call_count += 1 + return random.randint(0, 100) + config_list = autogen.config_list_from_json( OAI_CONFIG_LIST, file_location=KEY_LOC, ) + def luck_number_message(sender, recipient, context): + final_msg = {} + final_msg["content"] = "Give lucky numbers for them." + final_msg["function_call"] = {"name": "get_random_number", "arguments": "{}"} + return final_msg + financial_tasks = [ """What are the full names of NVDA and TESLA.""", - """Get their stock price.""", - """Analyze pros and cons. Keep it short.""", + luck_number_message, + luck_number_message, ] - writing_tasks = ["""Develop a short but engaging blog post using any information provided."""] + writing_tasks = ["""Make a joke."""] + func = Function() financial_assistant_1 = AssistantAgent( name="Financial_assistant_1", llm_config={"config_list": config_list}, + function_map={"get_random_number": func.get_random_number}, ) financial_assistant_2 = AssistantAgent( name="Financial_assistant_2", llm_config={"config_list": config_list}, + function_map={"get_random_number": func.get_random_number}, ) writer = AssistantAgent( name="Writer", @@ -215,12 +237,14 @@ def my_summary_method(recipient, sender, summary_args): "message": financial_tasks[2], "summary_method": "last_msg", "clear_history": False, + "max_turns": 1, }, { "recipient": writer, "message": writing_tasks[0], - "carryover": "I want to include a figure or a table of data in the blogpost.", + "carryover": "Make the numbers relevant.", "summary_method": "last_msg", + "max_turns": 1, }, ] ) @@ -248,8 +272,8 @@ def test_chats_general(): financial_tasks = [ """What are the full names of NVDA and TESLA.""", - """Get their stock price.""", - """Analyze pros and cons. Keep it short.""", + """Give lucky numbers for them.""", + """Give lucky words for them.""", ] writing_tasks = ["""Develop a short but engaging blog post using any information provided."""] @@ -354,8 +378,8 @@ def test_chats_exceptions(): financial_tasks = [ """What are the full names of NVDA and TESLA.""", - """Get their stock price.""", - """Analyze pros and cons. Keep it short.""", + """Give lucky numbers for them.""", + """Give lucky words for them.""", ] financial_assistant_1 = AssistantAgent( @@ -574,10 +598,10 @@ def my_writing_task(sender, recipient, context): if __name__ == "__main__": - # test_chats() + test_chats() # test_chats_general() # test_chats_exceptions() # test_chats_group() # test_chats_w_func() - # test_chat_messages_for_summary() + test_chat_messages_for_summary() test_udf_message_in_chats() From 256e9744f1dedce2f8298e48b18e96e574be3a34 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Tue, 5 Mar 2024 10:18:35 -0500 Subject: [PATCH 17/26] dict message --- autogen/agentchat/chat.py | 3 +++ autogen/agentchat/conversable_agent.py | 32 ++++++++++++++++-------- test/agentchat/test_chats.py | 23 +++++++++++++++-- test/agentchat/test_conversable_agent.py | 18 ++++++++++++- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/autogen/agentchat/chat.py b/autogen/agentchat/chat.py index 8e466d066651..0aa2ef380dca 100644 --- a/autogen/agentchat/chat.py +++ b/autogen/agentchat/chat.py @@ -116,6 +116,8 @@ def __post_carryover_processing(chat_info: Dict[str, Any]): print_message = message elif callable(message): print_message = "Callable: " + message.__name__ + elif isinstance(message, dict): + print_message = "Dict: " + str(message) elif message is None: print_message = "None" print(colored("\n" + "*" * 80, "blue"), flush=True, sep="") @@ -166,6 +168,7 @@ def initiate_chats(chat_queue: List[Dict[str, Any]]) -> List[ChatResult]: chat_info["carryover"] = _chat_carryover + [r.summary for r in finished_chats] __post_carryover_processing(chat_info) sender = chat_info["sender"] + print("chat_infochat_infochat_info", chat_info) chat_res = sender.initiate_chat(**chat_info) finished_chats.append(chat_res) return finished_chats diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index d8aedbec54bc..0070cc2e02e5 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -862,7 +862,7 @@ def initiate_chat( max_turns: Optional[int] = None, summary_method: Optional[Union[str, Callable]] = DEFAULT_summary_method, summary_args: Optional[dict] = {}, - message: Optional[Union[str, Callable]] = None, + message: Optional[Union[Dict, str, Callable]] = None, **context, ) -> ChatResult: """Initiate a chat with the recipient agent. @@ -898,10 +898,9 @@ def my_summary_method( E.g., a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflext on the conversation and extract a summary when summary_method is "reflection_with_llm". Default is DEFAULT_summary_prompt, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." - message (str or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. - - If a string is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. - - If a callable is provided, it will be called to get the initial message in the form of a string or a dict. If the returned value is a dict, it should contain the following reserved fields: - If the type is dict, it may contain the following reserved fields (either content or function_call need to be provided). + message (str, dict or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. + - If a string or a dict is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. + If dict, it may contain the following reserved fields (either content or function_call need to be provided). 1. "content": content of the message, can be None. 2. "function_call": a dictionary containing the function name and arguments. (deprecated in favor of "tool_calls") 3. "tool_calls": a list of dictionaries containing the function name and arguments. @@ -910,6 +909,8 @@ def my_summary_method( 5. "name": In most cases, this field is not needed. When the role is "function", this field is needed to indicate the function name. 6. "context" (dict): the context of the message, which will be passed to [OpenAIWrapper.create](../oai/client#create). + - If a callable is provided, it will be called to get the initial message in the form of a string or a dict. If the returned value is a dict, it should contain the following reserved fields: + If the returned type is dict, it may contain the reserved fields mentioned above. Example of a callable message (returning a string): ```python @@ -2170,7 +2171,7 @@ async def a_execute_function(self, func_call): "content": str(content), } - def generate_init_message(self, message: Union[str, None], **context) -> Union[str, Dict]: + def generate_init_message(self, message: Union[Dict, str, None], **context) -> Union[str, Dict]: """Generate the initial message for the agent. If message is None, input() will be called to get the initial message. @@ -2185,9 +2186,14 @@ def generate_init_message(self, message: Union[str, None], **context) -> Union[s """ if message is None: message = self.get_human_input(">") - return self._process_carryover(message, context) + if isinstance(message, str): + return self._process_carryover(message, context) + elif isinstance(message, dict): + message = message.copy() + message["content"] = self._process_carryover(message["content"], context) + return message - def _process_carryover(self, message, context): + def _process_carryover(self, message: str, context: dict) -> str: carryover = context.get("carryover") if carryover: # if carryover is string @@ -2201,7 +2207,7 @@ def _process_carryover(self, message, context): ) return message - async def a_generate_init_message(self, message: Union[str, None], **context) -> Union[str, Dict]: + async def a_generate_init_message(self, message: Union[Dict, str, None], **context) -> Union[str, Dict]: """Generate the initial message for the agent. If message is None, input() will be called to get the initial message. @@ -2213,8 +2219,12 @@ async def a_generate_init_message(self, message: Union[str, None], **context) -> """ if message is None: message = await self.a_get_human_input(">") - self._process_carryover(message, context) - return message + if isinstance(message, str): + return self._process_carryover(message, context) + elif isinstance(message, dict): + message = message.copy() + message["content"] = self._process_carryover(message["content"], context) + return message def register_function(self, function_map: Dict[str, Union[Callable, None]]): """Register functions to the agent. diff --git a/test/agentchat/test_chats.py b/test/agentchat/test_chats.py index 2862220dfd1f..4aa0ce76d595 100755 --- a/test/agentchat/test_chats.py +++ b/test/agentchat/test_chats.py @@ -217,6 +217,15 @@ def luck_number_message(sender, recipient, context): def my_summary_method(recipient, sender, summary_args): return recipient.chat_messages[sender][0].get("content", "") + # chat_res_play = user.initiate_chat( + # player, + # message= {"content": "Let's play a game.", "function_call": {"name": "get_random_number", "arguments": "{}"}}, + # max_turns=1, + # summary_method=my_summary, + # summary_args={"prefix": "This is the last message:"}, + # ) + # print(chat_res_play.summary) + chat_res = user.initiate_chats( [ { @@ -239,6 +248,16 @@ def my_summary_method(recipient, sender, summary_args): "clear_history": False, "max_turns": 1, }, + { + "recipient": financial_assistant_1, + "message": { + "content": "Let's play a game.", + "function_call": {"name": "get_random_number", "arguments": "{}"}, + }, + "carryover": "I like even number.", + "summary_method": "last_msg", + "max_turns": 1, + }, { "recipient": writer, "message": writing_tasks[0], @@ -603,5 +622,5 @@ def my_writing_task(sender, recipient, context): # test_chats_exceptions() # test_chats_group() # test_chats_w_func() - test_chat_messages_for_summary() - test_udf_message_in_chats() + # test_chat_messages_for_summary() + # test_udf_message_in_chats() diff --git a/test/agentchat/test_conversable_agent.py b/test/agentchat/test_conversable_agent.py index 078ad0f0ab30..7e9a55cd073c 100755 --- a/test/agentchat/test_conversable_agent.py +++ b/test/agentchat/test_conversable_agent.py @@ -1129,6 +1129,13 @@ def my_message_play(sender, recipient, context): function_map={"get_random_number": func.get_random_number}, ) + chat_res_play = user.initiate_chat( + player, + message={"content": "Let's play a game.", "function_call": {"name": "get_random_number", "arguments": "{}"}}, + max_turns=1, + ) + print(chat_res_play.summary) + chat_res_play = user.initiate_chat( player, message=my_message_play, @@ -1202,6 +1209,15 @@ def my_summary(sender, recipient, summary_args): ) print(chat_res_play.summary) + chat_res_play = user.initiate_chat( + player, + message={"content": "Let's play a game.", "function_call": {"name": "get_random_number", "arguments": "{}"}}, + max_turns=1, + summary_method=my_summary, + summary_args={"prefix": "This is the last message:"}, + ) + print(chat_res_play.summary) + def test_process_before_send(): print_mock = unittest.mock.MagicMock() @@ -1232,5 +1248,5 @@ def send_to_frontend(sender, message, recipient, silent): # test_no_llm_config() # test_max_turn() # test_process_before_send() - # test_message_func() + test_message_func() test_summary() From b45865a84bf35304afe0be545c08ed979e395bdf Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Tue, 5 Mar 2024 11:08:48 -0500 Subject: [PATCH 18/26] update nested doc --- autogen/agentchat/conversable_agent.py | 2 ++ notebook/agentchat_nestedchat.ipynb | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index 0070cc2e02e5..a52af4474a23 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -2190,6 +2190,8 @@ def generate_init_message(self, message: Union[Dict, str, None], **context) -> U return self._process_carryover(message, context) elif isinstance(message, dict): message = message.copy() + if message.get("content") is None: + message["content"] = self.get_human_input(">") message["content"] = self._process_carryover(message["content"], context) return message diff --git a/notebook/agentchat_nestedchat.ipynb b/notebook/agentchat_nestedchat.ipynb index 1e902a7f6213..3a9093007589 100644 --- a/notebook/agentchat_nestedchat.ipynb +++ b/notebook/agentchat_nestedchat.ipynb @@ -11,7 +11,7 @@ "source": [ "# Solving Complex Tasks with Nested Chats\n", "\n", - "This notebook shows how you can leverage \"nested chats\" to solve complex task with AutoGen. Nested chats allow AutoGen agents to use other agents as their inner monologue to accomplish tasks. This abstraction is powerful as it allows you to compose agents in rich ways. This notebook shows how you can nest a pretty complex sequence of chats among _inner_ agents inside an _outer_ agent.\n", + "This notebook shows how you can leverage **nested chats** to solve complex task with AutoGen. Nested chats is a sequence of chats created by a receiver agent after receiving a message from a sender agent and finished before the receiver agent replies to this message. Nested chats allow AutoGen agents to use other agents as their inner monologue to accomplish tasks. This abstraction is powerful as it allows you to compose agents in rich ways. This notebook shows how you can nest a pretty complex sequence of chats among _inner_ agents inside an _outer_ agent.\n", "\n", "\\:\\:\\:info Requirements\n", "\n", @@ -809,8 +809,10 @@ ], "metadata": { "front_matter": { - "tags": ["nested chat"], - "description": "Solve complex tasks with one or more sequence chats nested as inner monologue." + "description": "Solve complex tasks with one or more sequence chats nested as inner monologue.", + "tags": [ + "nested chat" + ] }, "kernelspec": { "display_name": "Python 3", From a745d01cc9a4ba2b24503f57723039a8b549bdc0 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Tue, 5 Mar 2024 12:42:11 -0500 Subject: [PATCH 19/26] generate_init_message --- autogen/agentchat/conversable_agent.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index a52af4474a23..dd3ff868c159 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -2190,9 +2190,10 @@ def generate_init_message(self, message: Union[Dict, str, None], **context) -> U return self._process_carryover(message, context) elif isinstance(message, dict): message = message.copy() - if message.get("content") is None: - message["content"] = self.get_human_input(">") - message["content"] = self._process_carryover(message["content"], context) + # TODO: Do we need to do the following? + # if message.get("content") is None: + # message["content"] = self.get_human_input(">") + message["content"] = self._process_carryover(message.get("content", ""), context) return message def _process_carryover(self, message: str, context: dict) -> str: From db32d31c8f1b3b3b82cc60ceb1855c74d28c5ff6 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Wed, 6 Mar 2024 09:42:08 -0500 Subject: [PATCH 20/26] fix typo --- autogen/agentchat/chat.py | 1 - autogen/agentchat/contrib/compressible_agent.py | 2 +- autogen/agentchat/conversable_agent.py | 4 ++-- notebook/agentchat_compression.ipynb | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/autogen/agentchat/chat.py b/autogen/agentchat/chat.py index 0aa2ef380dca..6d0704863735 100644 --- a/autogen/agentchat/chat.py +++ b/autogen/agentchat/chat.py @@ -168,7 +168,6 @@ def initiate_chats(chat_queue: List[Dict[str, Any]]) -> List[ChatResult]: chat_info["carryover"] = _chat_carryover + [r.summary for r in finished_chats] __post_carryover_processing(chat_info) sender = chat_info["sender"] - print("chat_infochat_infochat_info", chat_info) chat_res = sender.initiate_chat(**chat_info) finished_chats.append(chat_res) return finished_chats diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index 7945fc90acee..7201e203b29a 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -19,7 +19,7 @@ def colored(x, *args, **kwargs): class CompressibleAgent(ConversableAgent): - """(Attention: CompressibleAgent Retiring Soon! Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, + """(CompressibleAgent will be deprecated. Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.) CompressibleAgent agent. While this agent retains all the default functionalities of the `AssistantAgent`, it also provides the added feature of compression when activated through the `compress_config` setting. `compress_config` is set to False by default, making this agent equivalent to the `AssistantAgent`. diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index dd3ff868c159..adcd674a90e2 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -1108,7 +1108,7 @@ def _last_msg_as_summary(sender, recipient, summary_args) -> str: try: summary = recipient.last_message(sender)["content"].replace("TERMINATE", "") except (IndexError, AttributeError) as e: - warnings.warn(f"Cannot extract summary using last_msg: {e}. Using an empty as summary.", UserWarning) + warnings.warn(f"Cannot extract summary using last_msg: {e}. Using an empty str as summary.", UserWarning) summary = "" return summary @@ -1124,7 +1124,7 @@ def _relfection_with_llm_as_summary(sender, recipient, summary_args): summary = sender._reflection_with_llm(prompt, msg_list, llm_agent=agent, cache=summary_args.get("cache")) except BadRequestError as e: warnings.warn( - f"Cannot extract summary using reflection_with_llm: {e}. Using an empty as summary.", UserWarning + f"Cannot extract summary using reflection_with_llm: {e}. Using an empty str as summary.", UserWarning ) summary = "" return summary diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index cb968eaee90c..596be227a926 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -13,7 +13,7 @@ "source": [ "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Retiring Soon.)\n", "\n", - "**Attention: CompressibleAgent Retiring Soon.** \n", + "**CompressibleAgent will be deprecated.** \n", "\n", "Refer to https://github.com/microsoft/autogen/blob/main/notebook/agentchat_capability_long_context_handling.ipynb for long context handling capability.\n", "\n", From 53b45b2aa1554376b2ab759c476464bebd847580 Mon Sep 17 00:00:00 2001 From: kevin666aa Date: Wed, 6 Mar 2024 15:51:17 -0500 Subject: [PATCH 21/26] update docs for mathchat --- .../contrib/math_user_proxy_agent.py | 29 ++++++----- notebook/agentchat_MathChat.ipynb | 49 ++++++++----------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/autogen/agentchat/contrib/math_user_proxy_agent.py b/autogen/agentchat/contrib/math_user_proxy_agent.py index 4a5d01fc2924..70f365ef9fef 100644 --- a/autogen/agentchat/contrib/math_user_proxy_agent.py +++ b/autogen/agentchat/contrib/math_user_proxy_agent.py @@ -182,19 +182,22 @@ def message_generator(sender, recipient, context): """Generate a prompt for the assistant agent with the given problem and prompt. Args: - problem (str): the problem to be solved. - prompt_type (str): the type of the prompt. Possible values are "default", "python", "wolfram". - (1) "default": the prompt that allows the agent to choose between 3 ways to solve a problem: - 1. write a python program to solve it directly. - 2. solve it directly without python. - 3. solve it step by step with python. - (2) "python": - a simplified prompt from the third way of the "default" prompt, that asks the assistant - to solve the problem step by step with python. - (3) "two_tools": - a simplified prompt similar to the "python" prompt, but allows the model to choose between - Python and Wolfram Alpha to solve the problem. - customized_prompt (str): a customized prompt to be used. If it is not None, the prompt_type will be ignored. + sender (Agent): the sender of the message. + recipient (Agent): the recipient of the message. + context (dict): a dictionary with the following fields: + problem (str): the problem to be solved. + prompt_type (str, Optional): the type of the prompt. Possible values are "default", "python", "wolfram". + (1) "default": the prompt that allows the agent to choose between 3 ways to solve a problem: + 1. write a python program to solve it directly. + 2. solve it directly without python. + 3. solve it step by step with python. + (2) "python": + a simplified prompt from the third way of the "default" prompt, that asks the assistant + to solve the problem step by step with python. + (3) "two_tools": + a simplified prompt similar to the "python" prompt, but allows the model to choose between + Python and Wolfram Alpha to solve the problem. + customized_prompt (str, Optional): a customized prompt to be used. If it is not None, the prompt_type will be ignored. Returns: str: the generated prompt ready to be sent to the assistant agent. diff --git a/notebook/agentchat_MathChat.ipynb b/notebook/agentchat_MathChat.ipynb index 42c81c763a57..8a234ede013a 100644 --- a/notebook/agentchat_MathChat.ipynb +++ b/notebook/agentchat_MathChat.ipynb @@ -19,21 +19,17 @@ "\n", "MathChat is an experimental conversational framework for math problem solving. In this notebook, we demonstrate how to use MathChat to solve math problems. MathChat uses the `AssistantAgent` and `MathUserProxyAgent`, which is similar to the usage of `AssistantAgent` and `UserProxyAgent` in other notebooks (e.g., [Automated Task Solving with Code Generation, Execution & Debugging](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_auto_feedback_from_code_execution.ipynb)). Essentially, `MathUserProxyAgent` implements a different auto reply mechanism corresponding to the MathChat prompts. You can find more details in the paper [An Empirical Study on Challenging Math Problem Solving with GPT-4](https://arxiv.org/abs/2306.01337) or the [blogpost](https://microsoft.github.io/autogen/blog/2023/06/28/MathChat).\n", "\n", - "## Requirements\n", + "````{=mdx}\n", + ":::info Requirements\n", + "Some extra dependencies are needed for this notebook, which can be installed via pip:\n", "\n", - "AutoGen requires `Python>=3.8`. To run this notebook example, please install the [mathchat] option.\n", "```bash\n", - "pip install \"pyautogen[mathchat]\"\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# %pip install \"pyautogen[mathchat]~=0.1.1\"" + "pip install pyautogen[mathchat]\n", + "```\n", + "\n", + "For more information, please refer to the [installation guide](/docs/installation/).\n", + ":::\n", + "````" ] }, { @@ -59,21 +55,13 @@ "\n", "config_list = autogen.config_list_from_json(\n", " \"OAI_CONFIG_LIST\",\n", - " # filter_dict={\n", - " # \"model\": {\n", - " # \"gpt-4\",\n", - " # \"gpt4\",\n", - " # \"gpt-4-32k\",\n", - " # \"gpt-4-32k-0314\",\n", - " # \"gpt-4-32k-v0314\",\n", - " # \"gpt-3.5-turbo\",\n", - " # \"gpt-3.5-turbo-16k\",\n", - " # \"gpt-3.5-turbo-0301\",\n", - " # \"chatgpt-35-turbo-0301\",\n", - " # \"gpt-35-turbo-v0301\",\n", - " # \"gpt\",\n", - " # }\n", - " # },\n", + " filter_dict={\n", + " \"model\": {\n", + " \"gpt-4-1106-preview\",\n", + " \"gpt-3.5-turbo\",\n", + " \"gpt-35-turbo\",\n", + " }\n", + " },\n", ")" ] }, @@ -415,6 +403,9 @@ "math_problem = (\n", " \"Find all $x$ that satisfy the inequality $(2x+10)(x+3)<(3x+9)(x+8)$. Express your answer in interval notation.\"\n", ")\n", + "\n", + "# We call `initiate_chat` to start the conversation.\n", + "# When setting `message=mathproxyagent.message_generator`, you need to pass in the problem through the `problem` parameter.\n", "mathproxyagent.initiate_chat(assistant, message=mathproxyagent.message_generator, problem=math_problem)" ] }, @@ -1025,7 +1016,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.9.18" }, "orig_nbformat": 4 }, From 4daf82a12c6dfea6dc53fbf0c51970a666883af3 Mon Sep 17 00:00:00 2001 From: Li Jiang Date: Thu, 7 Mar 2024 04:43:03 +0000 Subject: [PATCH 22/26] Fix missing message --- notebook/agentchat_groupchat_RAG.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/notebook/agentchat_groupchat_RAG.ipynb b/notebook/agentchat_groupchat_RAG.ipynb index 26cdadc4963b..27b5d2abbe21 100644 --- a/notebook/agentchat_groupchat_RAG.ipynb +++ b/notebook/agentchat_groupchat_RAG.ipynb @@ -162,6 +162,7 @@ " # Start chatting with boss_aid as this is the user proxy agent.\n", " boss_aid.initiate_chat(\n", " manager,\n", + " message=boss_aid.message_generator,\n", " problem=PROBLEM,\n", " n_results=3,\n", " )\n", From 16e07402dee1d7ababe41594b84055a18d16eb8a Mon Sep 17 00:00:00 2001 From: Li Jiang Date: Thu, 7 Mar 2024 04:52:44 +0000 Subject: [PATCH 23/26] Add docstrings --- .../agentchat/contrib/retrieve_user_proxy_agent.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/autogen/agentchat/contrib/retrieve_user_proxy_agent.py b/autogen/agentchat/contrib/retrieve_user_proxy_agent.py index dd640673de59..5d9657d81431 100644 --- a/autogen/agentchat/contrib/retrieve_user_proxy_agent.py +++ b/autogen/agentchat/contrib/retrieve_user_proxy_agent.py @@ -410,6 +410,18 @@ def retrieve_docs(self, problem: str, n_results: int = 20, search_string: str = @staticmethod def message_generator(sender, recipient, context): + """ + Generate an initial message with the given context for the RetrieveUserProxyAgent. + Args: + sender (Agent): the sender agent. It should be the instance of RetrieveUserProxyAgent. + recipient (Agent): the recipient agent. Usually it's the assistant agent. + context (dict): the context for the message generation. It should contain the following keys: + - problem (str): the problem to be solved. + - n_results (int): the number of results to be retrieved. Default is 20. + - search_string (str): only docs that contain an exact match of this string will be retrieved. Default is "". + Returns: + str: the generated message ready to be sent to the recipient agent. + """ sender._reset() problem = context.get("problem", "") From f288e88111546e55418deedb4d9679286f4b7b22 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Thu, 7 Mar 2024 13:47:45 -0500 Subject: [PATCH 24/26] model --- .../agentchat/contrib/compressible_agent.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/autogen/agentchat/contrib/compressible_agent.py b/autogen/agentchat/contrib/compressible_agent.py index df1fa819a21e..0264d265654e 100644 --- a/autogen/agentchat/contrib/compressible_agent.py +++ b/autogen/agentchat/contrib/compressible_agent.py @@ -159,10 +159,9 @@ def _set_compress_config(self, compress_config: Optional[Dict] = False): if not (isinstance(trigger_count, int) or isinstance(trigger_count, float)) or trigger_count <= 0: raise ValueError("trigger_count must be a positive number.") if isinstance(trigger_count, float) and 0 < trigger_count <= 1: - # _model = self.llm_config["model"] - # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. - _model = "gpt-3.5-turbo" - self.compress_config["trigger_count"] = int(trigger_count * get_max_token_limit(_model)) + self.compress_config["trigger_count"] = int( + trigger_count * get_max_token_limit(self.llm_config["model"]) + ) trigger_count = self.compress_config["trigger_count"] init_count = self._compute_init_token_count() if trigger_count < init_count: @@ -221,13 +220,10 @@ def _compute_init_token_count(self): return 0 func_count = 0 - # _model = self.llm_config["model"] - # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. - _model = "gpt-3.5-turbo" if "functions" in self.llm_config: - func_count = num_tokens_from_functions(self.llm_config["functions"], _model) + func_count = num_tokens_from_functions(self.llm_config["functions"], self.llm_config["model"]) - return func_count + count_token(self._oai_system_message, _model) + return func_count + count_token(self._oai_system_message, self.llm_config["model"]) def _manage_history_on_token_limit(self, messages, token_used, max_token_allowed, model): """Manage the message history with different modes when token limit is reached. @@ -302,14 +298,13 @@ def on_oai_token_limit( TODO: async compress TODO: maintain a list for old oai messages (messages before compression) """ + llm_config = self.llm_config if config is None else config if self.compress_config is False: return False, None if messages is None: messages = self._oai_messages[sender] - # model = llm_config["model"] - # TODO: ERROR: self.llm_config does not have "model". Using gpt-3.5-turbo as a temporary fix. - model = "gpt-3.5-turbo" + model = llm_config["model"] init_token_count = self._compute_init_token_count() token_used = init_token_count + count_token(messages, model) final, compressed_messages = self._manage_history_on_token_limit( From 9e48802a09ceb698ae96d3ec9d5d19ecbdee8de4 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Thu, 7 Mar 2024 13:50:07 -0500 Subject: [PATCH 25/26] notebook --- notebook/agentchat_compression.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/agentchat_compression.ipynb b/notebook/agentchat_compression.ipynb index cd819b5bdf45..a6f2605f9d39 100644 --- a/notebook/agentchat_compression.ipynb +++ b/notebook/agentchat_compression.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled (Retiring Soon.)\n", + "# Auto Generated Agent Chat: Conversations with Chat History Compression Enabled\n", "\n", "**CompressibleAgent will be deprecated.** \n", "\n", From 704fbed5ec773a6b029443a3e5f987c211e6bee5 Mon Sep 17 00:00:00 2001 From: Qingyun Wu Date: Sat, 9 Mar 2024 14:31:12 -0500 Subject: [PATCH 26/26] default naming --- autogen/agentchat/conversable_agent.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index da5e4f9b7a36..07e1b2234c1c 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -66,8 +66,8 @@ class ConversableAgent(LLMAgent): DEFAULT_CONFIG = {} # An empty configuration MAX_CONSECUTIVE_AUTO_REPLY = 100 # maximum number of consecutive auto replies (subject to future change) - DEFAULT_summary_prompt = "Summarize the takeaway from the conversation. Do not add any introductory phrases." - DEFAULT_summary_method = "last_msg" + DEFAULT_SUMMARY_PROMPT = "Summarize the takeaway from the conversation. Do not add any introductory phrases." + DEFAULT_SUMMARY_METHOD = "last_msg" llm_config: Union[Dict, Literal[False]] def __init__( @@ -860,7 +860,7 @@ def initiate_chat( silent: Optional[bool] = False, cache: Optional[Cache] = None, max_turns: Optional[int] = None, - summary_method: Optional[Union[str, Callable]] = DEFAULT_summary_method, + summary_method: Optional[Union[str, Callable]] = DEFAULT_SUMMARY_METHOD, summary_args: Optional[dict] = {}, message: Optional[Union[Dict, str, Callable]] = None, **context, @@ -879,7 +879,7 @@ def initiate_chat( max_turns (int or None): the maximum number of turns for the chat between the two agents. One turn means one conversation round trip. Note that this is different from [max_consecutive_auto_reply](#max_consecutive_auto_reply) which is the maximum number of consecutive auto replies; and it is also different from [max_rounds in GroupChat](./groupchat#groupchat-objects) which is the maximum number of rounds in a group chat session. If max_turns is set to None, the chat will continue until a termination condition is met. Default is None. - summary_method (string or callable) : a method to get a summary from the chat. Default is DEFAULT_summary_method, i.e., "last_msg". + summary_method (string or callable) : a method to get a summary from the chat. Default is DEFAULT_SUMMARY_METHOD, i.e., "last_msg". - Supported string are "last_msg" and "reflection_with_llm": when set "last_msg", it returns the last message of the dialog as the summary. when set "reflection_with_llm", it returns a summary extracted using an llm client. @@ -897,7 +897,7 @@ def my_summary_method( summary_args (dict): a dictionary of arguments to be passed to the summary_method. E.g., a string of text used to prompt a LLM-based agent (the sender or receiver agent) to reflext on the conversation and extract a summary when summary_method is "reflection_with_llm". - Default is DEFAULT_summary_prompt, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." + Default is DEFAULT_SUMMARY_PROMPT, i.e., "Summarize takeaway from the conversation. Do not add any introductory phrases. If the intended request is NOT properly addressed, please point it out." message (str, dict or Callable): the initial message to be sent to the recipient. Needs to be provided. Otherwise, input() will be called to get the initial message. - If a string or a dict is provided, it will be used as the initial message. `generate_init_message` is called to generate the initial message for the agent based on this string and the context. If dict, it may contain the following reserved fields (either content or function_call need to be provided). @@ -997,7 +997,7 @@ async def a_initiate_chat( silent: Optional[bool] = False, cache: Optional[Cache] = None, max_turns: Optional[int] = None, - summary_method: Optional[Union[str, Callable]] = DEFAULT_summary_method, + summary_method: Optional[Union[str, Callable]] = DEFAULT_SUMMARY_METHOD, summary_args: Optional[dict] = {}, message: Optional[Union[str, Callable]] = None, **context, @@ -1115,7 +1115,7 @@ def _last_msg_as_summary(sender, recipient, summary_args) -> str: @staticmethod def _relfection_with_llm_as_summary(sender, recipient, summary_args): prompt = summary_args.get("summary_prompt") - prompt = ConversableAgent.DEFAULT_summary_prompt if prompt is None else prompt + prompt = ConversableAgent.DEFAULT_SUMMARY_PROMPT if prompt is None else prompt if not isinstance(prompt, str): raise ValueError("The summary_prompt must be a string.") msg_list = recipient.chat_messages_for_summary(sender)