diff --git a/autogen/agentchat/conversable_agent.py b/autogen/agentchat/conversable_agent.py index a3726678a45..b5420510d4d 100644 --- a/autogen/agentchat/conversable_agent.py +++ b/autogen/agentchat/conversable_agent.py @@ -6,6 +6,7 @@ import logging import re from collections import defaultdict +from functools import partial from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Type, TypeVar, Union import warnings from openai import BadRequestError @@ -321,6 +322,80 @@ def reply_func( if ignore_async_in_sync_chat and inspect.iscoroutinefunction(reply_func): self._ignore_async_func_in_sync_chat_list.append(reply_func) + @staticmethod + def _summary_from_nested_chats( + chat_queue: List[Dict[str, Any]], recipient: Agent, messages: Union[str, Callable], sender: Agent, config: Any + ) -> Tuple[bool, str]: + """A simple chat reply function. + This function initiate one or a sequence of chats between the "recipient" and the agents in the + chat_queue. + + It extracts and returns a summary from the nested chat based on the "summary_method" in each chat in chat_queue. + + Returns: + Tuple[bool, str]: A tuple where the first element indicates the completion of the chat, and the second element contains the summary of the last chat if any chats were initiated. + """ + last_msg = messages[-1].get("content") + chat_to_run = [] + for i, c in enumerate(chat_queue): + current_c = c.copy() + message = current_c.get("message") + # If message is not provided in chat_queue, we by default use the last message from the original chat history as the first message in this nested chat (for the first chat in the chat queue). + # NOTE: This setting is prone to change. + if message is None and i == 0: + message = last_msg + if callable(message): + message = message(recipient, messages, sender, config) + # We only run chat that has a valid message. NOTE: This is prone to change dependin on applications. + if message: + current_c["message"] = message + chat_to_run.append(current_c) + if not chat_to_run: + return True, None + res = recipient.initiate_chats(chat_to_run) + return True, res[-1].summary + + def register_nested_chats( + self, + chat_queue: List[Dict[str, Any]], + trigger: Union[Type[Agent], str, Agent, Callable[[Agent], bool], List] = [Agent, None], + reply_func_from_nested_chats: Union[str, Callable] = "summary_from_nested_chats", + position: int = 2, + **kwargs, + ) -> None: + """Register a nested chat reply function. + Args: + chat_queue (list): a list of chat objects to be initiated. + trigger (Agent class, str, Agent instance, callable, or list): Default to [Agent, None]. Ref to `register_reply` for details. + reply_func_from_nested_chats (Callable, str): the reply function for the nested chat. + The function takes a chat_queue for nested chat, recipient agent, a list of messages, a sender agent and a config as input and returns a reply message. + Default to "summary_from_nested_chats", which corresponds to a built-in reply function that get summary from the nested chat_queue. + ```python + def reply_func_from_nested_chats( + chat_queue: List[Dict], + recipient: ConversableAgent, + messages: Optional[List[Dict]] = None, + sender: Optional[Agent] = None, + config: Optional[Any] = None, + ) -> Tuple[bool, Union[str, Dict, None]]: + ``` + position (int): Ref to `register_reply` for details. Default to 2. It means we first check the termination and human reply, then check the registered nested chat reply. + kwargs: Ref to `register_reply` for details. + """ + if reply_func_from_nested_chats == "summary_from_nested_chats": + reply_func_from_nested_chats = self._summary_from_nested_chats + if not callable(reply_func_from_nested_chats): + raise ValueError("reply_func_from_nested_chats must be a callable") + reply_func = partial(reply_func_from_nested_chats, chat_queue) + self.register_reply( + trigger, + reply_func, + position, + kwargs.get("config"), + kwargs.get("reset_config"), + ignore_async_in_sync_chat=kwargs.get("ignore_async_in_sync_chat"), + ) + @property def system_message(self) -> str: """Return the system message.""" diff --git a/notebook/agentchat_nestedchat.ipynb b/notebook/agentchat_nestedchat.ipynb new file mode 100644 index 00000000000..c4a25871e25 --- /dev/null +++ b/notebook/agentchat_nestedchat.ipynb @@ -0,0 +1,1209 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "\n", + "\n", + "# 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", + "\n", + "\\:\\:\\:info Requirements\n", + "\n", + "Install `pyautogen`:\n", + "```bash\n", + "pip install pyautogen\n", + "```\n", + "\n", + "For more information, please refer to the [installation guide](/docs/installation/).\n", + "\n", + "\\:\\:\\:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import autogen\n", + "\n", + "config_list = autogen.config_list_from_json(env_or_file=\"OAI_CONFIG_LIST\")\n", + "llm_config = {\"config_list\": config_list}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\\:\\:\\:tip\n", + "\n", + "Learn more about the various ways to configure LLM endpoints [here](/docs/llm_configuration).\n", + "\n", + "\\:\\:\\:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example Task\n", + "\n", + "Suppose we want the agents to complete the following sequence of tasks:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "tasks = [\n", + " \"\"\"On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\"\"\",\n", + " \"\"\"Make a pleasant joke about it.\"\"\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the first task could be complex to solve, lets construct new agents that can serve as an inner monologue.\n", + "\n", + "### Step 1. Define Agents\n", + "\n", + "#### A Group Chat for Inner Monologue\n", + "Below, we construct a group chat manager which manages an `inner_assistant` agent and an `inner_code_interpreter` agent. \n", + "Later we will use this group chat inside another agent.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "inner_assistant = autogen.AssistantAgent(\n", + " \"Inner-assistant\",\n", + " llm_config=llm_config,\n", + " is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n", + ")\n", + "\n", + "inner_code_interpreter = autogen.UserProxyAgent(\n", + " \"Inner-code-interpreter\",\n", + " human_input_mode=\"NEVER\",\n", + " code_execution_config={\n", + " \"work_dir\": \"coding\",\n", + " \"use_docker\": False,\n", + " },\n", + " default_auto_reply=\"\",\n", + " is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n", + ")\n", + "\n", + "groupchat = autogen.GroupChat(\n", + " agents=[inner_assistant, inner_code_interpreter],\n", + " messages=[],\n", + " speaker_selection_method=\"round_robin\", # With two agents, this is equivalent to a 1:1 conversation.\n", + " allow_repeat_speaker=False,\n", + " max_round=8,\n", + ")\n", + "\n", + "manager = autogen.GroupChatManager(\n", + " groupchat=groupchat,\n", + " is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\n", + " llm_config=llm_config,\n", + " code_execution_config={\n", + " \"work_dir\": \"coding\",\n", + " \"use_docker\": False,\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Inner- and Outer-Level Individual Agents\n", + "\n", + "Now we will construct a number of individual agents that will assume role of outer and inner agents." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "assistant_1 = autogen.AssistantAgent(\n", + " name=\"Assistant_1\",\n", + " llm_config={\"config_list\": config_list},\n", + ")\n", + "\n", + "assistant_2 = autogen.AssistantAgent(\n", + " name=\"Assistant_2\",\n", + " llm_config={\"config_list\": config_list},\n", + ")\n", + "\n", + "writer = autogen.AssistantAgent(\n", + " name=\"Writer\",\n", + " llm_config={\"config_list\": config_list},\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", + " \"\"\",\n", + ")\n", + "\n", + "reviewer = autogen.AssistantAgent(\n", + " name=\"Reviewer\",\n", + " llm_config={\"config_list\": config_list},\n", + " system_message=\"\"\"\n", + " You are a compliance reviewer, known for your thoroughness and commitment to standards.\n", + " Your task is to scrutinize content for any harmful elements or regulatory violations, ensuring\n", + " all materials align with required guidelines.\n", + " You must review carefully, identify potential issues, and maintain the integrity of the organization.\n", + " Your role demands fairness, a deep understanding of regulations, and a focus on protecting against\n", + " harm while upholding a culture of responsibility.\n", + " \"\"\",\n", + ")\n", + "\n", + "user = autogen.UserProxyAgent(\n", + " name=\"User\",\n", + " human_input_mode=\"NEVER\",\n", + " is_termination_msg=lambda x: x.get(\"content\", \"\").find(\"TERMINATE\") >= 0,\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", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Orchestrate Nested Chats to Solve Tasks\n", + "\n", + "#### Outer Level\n", + "In the following code block, at the outer level, we have communication between:\n", + "\n", + "- `user` - `assistant_1` for solving the first task, i.e., `tasks[0]`.\n", + "- `user` - `assistant_2` for solving the second task, i.e., `tasks[1]`.\n", + "\n", + "#### Inner Level (Nested Chats)\n", + "Since the first task is quite complicated, we created a sequence of _nested chats_ as the inner monologue of Assistant_1.\n", + "\n", + "1. `assistant_1` - `manager`: This chat intends to delegate the task received by Assistant_1 to the Manager to solve.\n", + "\n", + "2. `assistant_1` - `writer`: This chat takes the output from Nested Chat 1, i.e., Assistant_1 vs. Manager, and lets the Writer polish the content to make an engaging and nicely formatted blog post, which is realized through the writing_message function.\n", + "\n", + "3. `assistant_1` - `reviewer`: This chat takes the output from Nested Chat 2 and intends to let the Reviewer agent review the content from Nested Chat 2.\n", + "\n", + "4. `assistant_1` - `writer`: This chat takes the output from previous nested chats and intends to let the Writer agent finalize a blog post.\n", + "\n", + "The sequence of nested chats can be realized with the `register_nested_chats` function, which allows one to register one or a sequence of chats to a particular agent (in this example, the `assistant_1` agent).\n", + "\n", + "Information about the sequence of chats can be specified in the `chat_queue` argument of the `register_nested_chats` function. The following fields are especially useful:\n", + "- `recipient` (required) specifies the nested agent;\n", + "- `message` specifies what message to send to the nested recipient agent. In a sequence of nested chats, if the `message` field is not specified, we will use the last message the registering agent received as the initial message in the first chat and will skip any subsequent chat in the queue that does not have the `message` field. You can either provide a string or define a callable that returns a string.\n", + "- `summary_method` decides what to get out of the nested chat. You can either select from existing options including `\"last_msg\"` and `\"reflection_with_llm\"`, or or define your own way on what to get from the nested chat with a Callable.\n", + "- `max_turns` determines how many turns of conversation to have between the concerned agent pairs." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "With the following carryover: \n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mUser\u001b[0m (to Assistant_1):\n", + "\n", + "On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "With the following carryover: \n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mAssistant_1\u001b[0m (to chat_manager):\n", + "\n", + "On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mInner-assistant\u001b[0m (to chat_manager):\n", + "\n", + "To find out on which specific days Microsoft's stock (ticker symbol: MSFT) closed above $400 in 2024, we will need to access historical stock price data for Microsoft from a reliable financial data source. This kind of data is typically available on financial websites such as Yahoo Finance, Google Finance, or through financial data APIs such as Alpha Vantage or IEX Cloud.\n", + "\n", + "Since I cannot directly browse the web or access external APIs, I will provide you with a Python script that uses the `yfinance` library to fetch historical stock data for Microsoft and filter the dates when the stock price was higher than $400.\n", + "\n", + "Firstly, you need to install the `yfinance` module if it's not already installed. You can do so using pip by executing `pip install yfinance` in your shell.\n", + "\n", + "After installation, here is the Python code that you can execute to fetch the data and filter the specific dates:\n", + "\n", + "```python\n", + "# filename: msft_stock_analysis.py\n", + "import yfinance as yf\n", + "from datetime import datetime\n", + "\n", + "# Define the ticker symbol for Microsoft\n", + "ticker_symbol = \"MSFT\"\n", + "\n", + "# Fetch historical stock data for Microsoft for the year 2024\n", + "start_date = \"2024-01-01\"\n", + "end_date = \"2024-12-31\"\n", + "msft_data = yf.download(ticker_symbol, start=start_date, end=end_date)\n", + "\n", + "# Filter the days when the closing price was higher than $400\n", + "days_above_400 = msft_data[msft_data['Close'] > 400]\n", + "\n", + "# Print the dates and closing prices on those dates\n", + "print(\"Dates when Microsoft's stock closed above $400 in 2024:\")\n", + "print(days_above_400[['Close']])\n", + "\n", + "# Comment on the stock performance\n", + "if not days_above_400.empty:\n", + " days_count = len(days_above_400)\n", + " print(f\"\\nMicrosoft's stock closed above $400 on {days_count} days in 2024.\")\n", + "else:\n", + " print(\"\\nMicrosoft's stock did not close above $400 on any day in 2024.\")\n", + "```\n", + "\n", + "Please save the above code to a file named `msft_stock_analysis.py` and run it. This will print out the dates when Microsoft's stock was higher than $400 in 2024 and comment on the stock performance based on the results. \n", + "\n", + "Keep in mind that the `yfinance` library fetches real-time data, and since we are in 2023, data for 2024 is not available yet. Therefore, when you run the script, it will show that the stock did not close above $400, which is expected since 2024 has not occurred yet. For actual analysis, you would have to run the script after the end of 2024 or modify the date range to a past range where data is available.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[31m\n", + ">>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/qingyunwu/Documents/github/autogen/autogen/agentchat/chat.py:46: UserWarning: Repetitive recipients detected: The chat history will be cleared by default if a recipient appears more than once. To retain the chat history, please set 'clear_history=False' in the configuration of the repeating agent.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mInner-code-interpreter\u001b[0m (to chat_manager):\n", + "\n", + "exitcode: 0 (execution succeeded)\n", + "Code output: \n", + "Dates when Microsoft's stock closed above $400 in 2024:\n", + " Close\n", + "Date \n", + "2024-01-24 402.559998\n", + "2024-01-25 404.869995\n", + "2024-01-26 403.929993\n", + "2024-01-29 409.720001\n", + "2024-01-30 408.589996\n", + "2024-02-01 403.779999\n", + "2024-02-02 411.220001\n", + "2024-02-05 405.649994\n", + "2024-02-06 405.489990\n", + "2024-02-07 414.049988\n", + "2024-02-08 414.109985\n", + "2024-02-09 420.549988\n", + "2024-02-12 415.260010\n", + "2024-02-13 406.320007\n", + "2024-02-14 409.489990\n", + "2024-02-15 406.559998\n", + "2024-02-16 404.059998\n", + "2024-02-20 402.790009\n", + "2024-02-21 402.179993\n", + "2024-02-22 411.649994\n", + "2024-02-23 410.339996\n", + "\n", + "Microsoft's stock closed above $400 on 21 days in 2024.\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mInner-assistant\u001b[0m (to chat_manager):\n", + "\n", + "Based on the output from the code execution, Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024. The closing prices on those days ranged from slightly above $400 to highs around $420. This indicates periods of strong performance, during which the stock reached significant value.\n", + "\n", + "Here is a brief comment on the stock performance:\n", + "\n", + "The data shows that Microsoft's stock had several instances where it exceeded the $400 mark in early 2024, with notable peaks in February, which suggests a bullish trend during that period. Specific days like February 9th, where the stock hit around $420, indicate particularly strong performance.\n", + "\n", + "Having such a number of days with closing prices above $400 signals a robust market valuation for Microsoft at those times. Investors who held the stock on these dates may have seen appreciable gains, depending on their portfolio strategies.\n", + "\n", + "It's also noteworthy that stock prices fluctuate due to a variety of factors, including market sentiment, company performance, industry trends, and general economic conditions. This data provides a snapshot of Microsoft's stock performance but a deeper analysis would require more context on the market conditions, news, and events surrounding these dates.\n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "Polish the content to make an engaging and nicely formatted blog post. \n", + "\n", + " On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "With the following carryover: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mAssistant_1\u001b[0m (to Writer):\n", + "\n", + "Polish the content to make an engaging and nicely formatted blog post. \n", + "\n", + " On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "Context: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mWriter\u001b[0m (to Assistant_1):\n", + "\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "In the ever-fluctuating world of stocks, each milestone crossed can turn into a celebration or a stern reckoning of what the future holds. For Microsoft (MSFT), the year 2024 was a testament to its resilience and investor confidence as its stock ascended beyond the coveted $400 mark on 21 remarkable occasions.\n", + "\n", + "This performance wasn't just numerically significant; it was emblematic of a potent bullish energy within the market, affirming the tech giant’s leviathan presence amidst a highly competitive industry terrain. The loftiest point, flirting with the heights of approximately $420, served not just as a numerical peak, but as a beacon of the company’s market valuation and the unwavering investor trust in Microsoft's strategies and future prospects.\n", + "\n", + "## A Snapshot of Stellar Performance\n", + "\n", + "Microsoft's stock performance throughout 2024 was far from being a serendipitous spike. Instead, it showcased a carefully constructed narrative of growth and optimism. The chronicles of MSFT's venture past the $400 threshold are marked by fleeting dips and steadfast ascensions. Remarkably, the surge wasn't some scattergun pattern scattered across the calendar year. Rather, it was most pronounced in the month of February — a period that could very well be termed as the 'February Flourish.'\n", + "\n", + "During this short yet significant timeframe, investors witnessed the creation of incremental wealth that possibly outstripped all market expectations, setting a precedent for what a legacy company like Microsoft could achieve in a climate rife with disruptors and innovators.\n", + "\n", + "## Of Peaks and Valleys\n", + "\n", + "When assessing the grand canvas of Microsoft's stock performance, it is crucial to take a step back and appreciate the layered complexity of these financial movements. Amidst economic uncertainties and the technocratic whims that habitually serve both as tailwinds and headwinds to such companies, the story of Microsoft crossing the $400 threshold imbues the narrative with contours of investor euphoria interlaced with strategic agility.\n", + "\n", + "As these 21 days get etched into the fiscal annals of Microsoft, one cannot help but ponder the fine blend of factors — ranging from market dynamics to Microsoft's organizational maneuvers — that have coalesced to make these numbers a reality.\n", + "\n", + "## A Convergence of Positive Sentiments\n", + "\n", + "What does the future hold for MSFT as it treads past this impressive benchmark? The takeaway is not a simple extrapolation of past performance. Rather, it's the realization that stock trajectories, particularly for a company like Microsoft, are a confluence of judicious corporate governance, product innovation, market anticipation, and, dare we say, a touch of the zeitgeist.\n", + "\n", + "Microsoft’s capacity to command such stock valitudes speaks volumes about its robust position in both the technology domain and the stock market ecosystem. Whether this fiscal feat can be sustained or surpassed remains a subject of much conjecture and anticipation.\n", + "\n", + "As we continue to navigate the ebbs and flows of an unpredictable market landscape, the days when Microsoft's stock rose gallantly above $400 will be recalled as milestones of 2024, celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise.\n", + "\n", + "The analysis of precise data points and enlightening interpretations reveal much about the past while providing distinctive glimpses of potential futures. With Microsoft standing tall amongst peers, its voyage above the $400 watermark will be chronicled as days of high tides in the annals of financial history.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "Review the content provided.\n", + "\n", + "With the following carryover: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "In the ever-fluctuating world of stocks, each milestone crossed can turn into a celebration or a stern reckoning of what the future holds. For Microsoft (MSFT), the year 2024 was a testament to its resilience and investor confidence as its stock ascended beyond the coveted $400 mark on 21 remarkable occasions.\n", + "\n", + "This performance wasn't just numerically significant; it was emblematic of a potent bullish energy within the market, affirming the tech giant’s leviathan presence amidst a highly competitive industry terrain. The loftiest point, flirting with the heights of approximately $420, served not just as a numerical peak, but as a beacon of the company’s market valuation and the unwavering investor trust in Microsoft's strategies and future prospects.\n", + "\n", + "## A Snapshot of Stellar Performance\n", + "\n", + "Microsoft's stock performance throughout 2024 was far from being a serendipitous spike. Instead, it showcased a carefully constructed narrative of growth and optimism. The chronicles of MSFT's venture past the $400 threshold are marked by fleeting dips and steadfast ascensions. Remarkably, the surge wasn't some scattergun pattern scattered across the calendar year. Rather, it was most pronounced in the month of February — a period that could very well be termed as the 'February Flourish.'\n", + "\n", + "During this short yet significant timeframe, investors witnessed the creation of incremental wealth that possibly outstripped all market expectations, setting a precedent for what a legacy company like Microsoft could achieve in a climate rife with disruptors and innovators.\n", + "\n", + "## Of Peaks and Valleys\n", + "\n", + "When assessing the grand canvas of Microsoft's stock performance, it is crucial to take a step back and appreciate the layered complexity of these financial movements. Amidst economic uncertainties and the technocratic whims that habitually serve both as tailwinds and headwinds to such companies, the story of Microsoft crossing the $400 threshold imbues the narrative with contours of investor euphoria interlaced with strategic agility.\n", + "\n", + "As these 21 days get etched into the fiscal annals of Microsoft, one cannot help but ponder the fine blend of factors — ranging from market dynamics to Microsoft's organizational maneuvers — that have coalesced to make these numbers a reality.\n", + "\n", + "## A Convergence of Positive Sentiments\n", + "\n", + "What does the future hold for MSFT as it treads past this impressive benchmark? The takeaway is not a simple extrapolation of past performance. Rather, it's the realization that stock trajectories, particularly for a company like Microsoft, are a confluence of judicious corporate governance, product innovation, market anticipation, and, dare we say, a touch of the zeitgeist.\n", + "\n", + "Microsoft’s capacity to command such stock valitudes speaks volumes about its robust position in both the technology domain and the stock market ecosystem. Whether this fiscal feat can be sustained or surpassed remains a subject of much conjecture and anticipation.\n", + "\n", + "As we continue to navigate the ebbs and flows of an unpredictable market landscape, the days when Microsoft's stock rose gallantly above $400 will be recalled as milestones of 2024, celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise.\n", + "\n", + "The analysis of precise data points and enlightening interpretations reveal much about the past while providing distinctive glimpses of potential futures. With Microsoft standing tall amongst peers, its voyage above the $400 watermark will be chronicled as days of high tides in the annals of financial history.\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mAssistant_1\u001b[0m (to Reviewer):\n", + "\n", + "Review the content provided.\n", + "Context: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "In the ever-fluctuating world of stocks, each milestone crossed can turn into a celebration or a stern reckoning of what the future holds. For Microsoft (MSFT), the year 2024 was a testament to its resilience and investor confidence as its stock ascended beyond the coveted $400 mark on 21 remarkable occasions.\n", + "\n", + "This performance wasn't just numerically significant; it was emblematic of a potent bullish energy within the market, affirming the tech giant’s leviathan presence amidst a highly competitive industry terrain. The loftiest point, flirting with the heights of approximately $420, served not just as a numerical peak, but as a beacon of the company’s market valuation and the unwavering investor trust in Microsoft's strategies and future prospects.\n", + "\n", + "## A Snapshot of Stellar Performance\n", + "\n", + "Microsoft's stock performance throughout 2024 was far from being a serendipitous spike. Instead, it showcased a carefully constructed narrative of growth and optimism. The chronicles of MSFT's venture past the $400 threshold are marked by fleeting dips and steadfast ascensions. Remarkably, the surge wasn't some scattergun pattern scattered across the calendar year. Rather, it was most pronounced in the month of February — a period that could very well be termed as the 'February Flourish.'\n", + "\n", + "During this short yet significant timeframe, investors witnessed the creation of incremental wealth that possibly outstripped all market expectations, setting a precedent for what a legacy company like Microsoft could achieve in a climate rife with disruptors and innovators.\n", + "\n", + "## Of Peaks and Valleys\n", + "\n", + "When assessing the grand canvas of Microsoft's stock performance, it is crucial to take a step back and appreciate the layered complexity of these financial movements. Amidst economic uncertainties and the technocratic whims that habitually serve both as tailwinds and headwinds to such companies, the story of Microsoft crossing the $400 threshold imbues the narrative with contours of investor euphoria interlaced with strategic agility.\n", + "\n", + "As these 21 days get etched into the fiscal annals of Microsoft, one cannot help but ponder the fine blend of factors — ranging from market dynamics to Microsoft's organizational maneuvers — that have coalesced to make these numbers a reality.\n", + "\n", + "## A Convergence of Positive Sentiments\n", + "\n", + "What does the future hold for MSFT as it treads past this impressive benchmark? The takeaway is not a simple extrapolation of past performance. Rather, it's the realization that stock trajectories, particularly for a company like Microsoft, are a confluence of judicious corporate governance, product innovation, market anticipation, and, dare we say, a touch of the zeitgeist.\n", + "\n", + "Microsoft’s capacity to command such stock valitudes speaks volumes about its robust position in both the technology domain and the stock market ecosystem. Whether this fiscal feat can be sustained or surpassed remains a subject of much conjecture and anticipation.\n", + "\n", + "As we continue to navigate the ebbs and flows of an unpredictable market landscape, the days when Microsoft's stock rose gallantly above $400 will be recalled as milestones of 2024, celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise.\n", + "\n", + "The analysis of precise data points and enlightening interpretations reveal much about the past while providing distinctive glimpses of potential futures. With Microsoft standing tall amongst peers, its voyage above the $400 watermark will be chronicled as days of high tides in the annals of financial history.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mReviewer\u001b[0m (to Assistant_1):\n", + "\n", + "The content provided serves as a market commentary, analyzing Microsoft's stock performance in the year 2024. In this review, I must ensure that the content is free from harmful elements, false claims, or regulatory violations.\n", + "\n", + "### Potential issues to consider:\n", + "\n", + "1. **Accuracy of Data**: Claims about stock prices and the number of days the stock closed above a certain threshold should be verifiable with stock market data. There must be confirmation that these numbers are accurate to present a truthful report and avoid misleading investors or stakeholders.\n", + "\n", + "2. **Forward-Looking Statements**: The article should avoid overly optimistic or pessimistic conjectures about future stock performance, which can be perceived as investment advice. Speculative statements should be clearly marked as such and provide a disclaimer that past performance does not guarantee future results.\n", + "\n", + "3. **Fair and Balanced**: While the article largely celebrates Microsoft's performance, it should also acknowledge risks and uncertainties inherent in stock investments to maintain a fair and balanced approach without unduly influencing market sentiment.\n", + "\n", + "4. **Non-Promotional Language**: Ensure that the content doesn't come off as promotional. Phrases like \"Microsoft's resilience and investor confidence\" and \"celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise\" should be reviewed to avoid appearing biased or overly promotional.\n", + "\n", + "5. **Compliance with SEC Regulations**: In the United States, Securities and Exchange Commission (SEC) regulations require careful communication about stock performance to avoid market manipulation, insider trading implications, or misstatements.\n", + "\n", + "6. **Objective Tone**: While the narrative is engaging, it's important that it remains objective and does not use language that could be considered subjective or biased. Phrases like \"Microsoft’s leviathan presence\" or \"February Flourish\" might be interpreted as adding emotional color rather than stating facts.\n", + "\n", + "7. **Clear Attribution**: If the report is based on external analyses or reported events, these should be clearly cited and attributed to maintain transparency.\n", + "\n", + "### Recommendations:\n", + "\n", + "- **Fact-Checking**: Verify all factual data through reliable stock market databases or financial reports released by Microsoft.\n", + " \n", + "- **Disclaimer**: Include a general disclaimer that the content is for informational purposes only and is not a form of financial advice or an inducement to invest.\n", + "\n", + "- **Balanced Analysis**: Emphasize the volatile nature of stock markets and integrate a risk-focused discussion to balance the positive sentiment conveyed.\n", + "\n", + "- **Neutral Language**: Adjust language to ensure neutrality and avoid any implications of advice or recommendation.\n", + "\n", + "In conclusion, while the content presents a positive overview of Microsoft's stock performance, it requires scrutiny for accuracy, neutrality, and conformance with regulatory standards, avoiding the potential of guiding market behavior or conveying investment advice. A careful revision ensuring these changes would align the material with the required guidelines and uphold the integrity of the content.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "Polish the content to make an engaging and nicely formatted blog post. \n", + "\n", + " On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "\n", + "With the following carryover: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "In the ever-fluctuating world of stocks, each milestone crossed can turn into a celebration or a stern reckoning of what the future holds. For Microsoft (MSFT), the year 2024 was a testament to its resilience and investor confidence as its stock ascended beyond the coveted $400 mark on 21 remarkable occasions.\n", + "\n", + "This performance wasn't just numerically significant; it was emblematic of a potent bullish energy within the market, affirming the tech giant’s leviathan presence amidst a highly competitive industry terrain. The loftiest point, flirting with the heights of approximately $420, served not just as a numerical peak, but as a beacon of the company’s market valuation and the unwavering investor trust in Microsoft's strategies and future prospects.\n", + "\n", + "## A Snapshot of Stellar Performance\n", + "\n", + "Microsoft's stock performance throughout 2024 was far from being a serendipitous spike. Instead, it showcased a carefully constructed narrative of growth and optimism. The chronicles of MSFT's venture past the $400 threshold are marked by fleeting dips and steadfast ascensions. Remarkably, the surge wasn't some scattergun pattern scattered across the calendar year. Rather, it was most pronounced in the month of February — a period that could very well be termed as the 'February Flourish.'\n", + "\n", + "During this short yet significant timeframe, investors witnessed the creation of incremental wealth that possibly outstripped all market expectations, setting a precedent for what a legacy company like Microsoft could achieve in a climate rife with disruptors and innovators.\n", + "\n", + "## Of Peaks and Valleys\n", + "\n", + "When assessing the grand canvas of Microsoft's stock performance, it is crucial to take a step back and appreciate the layered complexity of these financial movements. Amidst economic uncertainties and the technocratic whims that habitually serve both as tailwinds and headwinds to such companies, the story of Microsoft crossing the $400 threshold imbues the narrative with contours of investor euphoria interlaced with strategic agility.\n", + "\n", + "As these 21 days get etched into the fiscal annals of Microsoft, one cannot help but ponder the fine blend of factors — ranging from market dynamics to Microsoft's organizational maneuvers — that have coalesced to make these numbers a reality.\n", + "\n", + "## A Convergence of Positive Sentiments\n", + "\n", + "What does the future hold for MSFT as it treads past this impressive benchmark? The takeaway is not a simple extrapolation of past performance. Rather, it's the realization that stock trajectories, particularly for a company like Microsoft, are a confluence of judicious corporate governance, product innovation, market anticipation, and, dare we say, a touch of the zeitgeist.\n", + "\n", + "Microsoft’s capacity to command such stock valitudes speaks volumes about its robust position in both the technology domain and the stock market ecosystem. Whether this fiscal feat can be sustained or surpassed remains a subject of much conjecture and anticipation.\n", + "\n", + "As we continue to navigate the ebbs and flows of an unpredictable market landscape, the days when Microsoft's stock rose gallantly above $400 will be recalled as milestones of 2024, celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise.\n", + "\n", + "The analysis of precise data points and enlightening interpretations reveal much about the past while providing distinctive glimpses of potential futures. With Microsoft standing tall amongst peers, its voyage above the $400 watermark will be chronicled as days of high tides in the annals of financial history.\n", + "The content provided serves as a market commentary, analyzing Microsoft's stock performance in the year 2024. In this review, I must ensure that the content is free from harmful elements, false claims, or regulatory violations.\n", + "\n", + "### Potential issues to consider:\n", + "\n", + "1. **Accuracy of Data**: Claims about stock prices and the number of days the stock closed above a certain threshold should be verifiable with stock market data. There must be confirmation that these numbers are accurate to present a truthful report and avoid misleading investors or stakeholders.\n", + "\n", + "2. **Forward-Looking Statements**: The article should avoid overly optimistic or pessimistic conjectures about future stock performance, which can be perceived as investment advice. Speculative statements should be clearly marked as such and provide a disclaimer that past performance does not guarantee future results.\n", + "\n", + "3. **Fair and Balanced**: While the article largely celebrates Microsoft's performance, it should also acknowledge risks and uncertainties inherent in stock investments to maintain a fair and balanced approach without unduly influencing market sentiment.\n", + "\n", + "4. **Non-Promotional Language**: Ensure that the content doesn't come off as promotional. Phrases like \"Microsoft's resilience and investor confidence\" and \"celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise\" should be reviewed to avoid appearing biased or overly promotional.\n", + "\n", + "5. **Compliance with SEC Regulations**: In the United States, Securities and Exchange Commission (SEC) regulations require careful communication about stock performance to avoid market manipulation, insider trading implications, or misstatements.\n", + "\n", + "6. **Objective Tone**: While the narrative is engaging, it's important that it remains objective and does not use language that could be considered subjective or biased. Phrases like \"Microsoft’s leviathan presence\" or \"February Flourish\" might be interpreted as adding emotional color rather than stating facts.\n", + "\n", + "7. **Clear Attribution**: If the report is based on external analyses or reported events, these should be clearly cited and attributed to maintain transparency.\n", + "\n", + "### Recommendations:\n", + "\n", + "- **Fact-Checking**: Verify all factual data through reliable stock market databases or financial reports released by Microsoft.\n", + " \n", + "- **Disclaimer**: Include a general disclaimer that the content is for informational purposes only and is not a form of financial advice or an inducement to invest.\n", + "\n", + "- **Balanced Analysis**: Emphasize the volatile nature of stock markets and integrate a risk-focused discussion to balance the positive sentiment conveyed.\n", + "\n", + "- **Neutral Language**: Adjust language to ensure neutrality and avoid any implications of advice or recommendation.\n", + "\n", + "In conclusion, while the content presents a positive overview of Microsoft's stock performance, it requires scrutiny for accuracy, neutrality, and conformance with regulatory standards, avoiding the potential of guiding market behavior or conveying investment advice. A careful revision ensuring these changes would align the material with the required guidelines and uphold the integrity of the content.\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mAssistant_1\u001b[0m (to Writer):\n", + "\n", + "Polish the content to make an engaging and nicely formatted blog post. \n", + "\n", + " On which days in 2024 was Microsoft Stock higher than $400? Comment on the stock performance.\n", + "Context: \n", + "Microsoft's stock (MSFT) closed above $400 on 21 separate days in 2024, demonstrating periods of strong stock performance with values reaching as high as around $420. The stock data suggests a bullish trend, especially in February when the stock peaked, indicating positive investor sentiment and robust market valuation during those times.\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "In the ever-fluctuating world of stocks, each milestone crossed can turn into a celebration or a stern reckoning of what the future holds. For Microsoft (MSFT), the year 2024 was a testament to its resilience and investor confidence as its stock ascended beyond the coveted $400 mark on 21 remarkable occasions.\n", + "\n", + "This performance wasn't just numerically significant; it was emblematic of a potent bullish energy within the market, affirming the tech giant’s leviathan presence amidst a highly competitive industry terrain. The loftiest point, flirting with the heights of approximately $420, served not just as a numerical peak, but as a beacon of the company’s market valuation and the unwavering investor trust in Microsoft's strategies and future prospects.\n", + "\n", + "## A Snapshot of Stellar Performance\n", + "\n", + "Microsoft's stock performance throughout 2024 was far from being a serendipitous spike. Instead, it showcased a carefully constructed narrative of growth and optimism. The chronicles of MSFT's venture past the $400 threshold are marked by fleeting dips and steadfast ascensions. Remarkably, the surge wasn't some scattergun pattern scattered across the calendar year. Rather, it was most pronounced in the month of February — a period that could very well be termed as the 'February Flourish.'\n", + "\n", + "During this short yet significant timeframe, investors witnessed the creation of incremental wealth that possibly outstripped all market expectations, setting a precedent for what a legacy company like Microsoft could achieve in a climate rife with disruptors and innovators.\n", + "\n", + "## Of Peaks and Valleys\n", + "\n", + "When assessing the grand canvas of Microsoft's stock performance, it is crucial to take a step back and appreciate the layered complexity of these financial movements. Amidst economic uncertainties and the technocratic whims that habitually serve both as tailwinds and headwinds to such companies, the story of Microsoft crossing the $400 threshold imbues the narrative with contours of investor euphoria interlaced with strategic agility.\n", + "\n", + "As these 21 days get etched into the fiscal annals of Microsoft, one cannot help but ponder the fine blend of factors — ranging from market dynamics to Microsoft's organizational maneuvers — that have coalesced to make these numbers a reality.\n", + "\n", + "## A Convergence of Positive Sentiments\n", + "\n", + "What does the future hold for MSFT as it treads past this impressive benchmark? The takeaway is not a simple extrapolation of past performance. Rather, it's the realization that stock trajectories, particularly for a company like Microsoft, are a confluence of judicious corporate governance, product innovation, market anticipation, and, dare we say, a touch of the zeitgeist.\n", + "\n", + "Microsoft’s capacity to command such stock valitudes speaks volumes about its robust position in both the technology domain and the stock market ecosystem. Whether this fiscal feat can be sustained or surpassed remains a subject of much conjecture and anticipation.\n", + "\n", + "As we continue to navigate the ebbs and flows of an unpredictable market landscape, the days when Microsoft's stock rose gallantly above $400 will be recalled as milestones of 2024, celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise.\n", + "\n", + "The analysis of precise data points and enlightening interpretations reveal much about the past while providing distinctive glimpses of potential futures. With Microsoft standing tall amongst peers, its voyage above the $400 watermark will be chronicled as days of high tides in the annals of financial history.\n", + "The content provided serves as a market commentary, analyzing Microsoft's stock performance in the year 2024. In this review, I must ensure that the content is free from harmful elements, false claims, or regulatory violations.\n", + "\n", + "### Potential issues to consider:\n", + "\n", + "1. **Accuracy of Data**: Claims about stock prices and the number of days the stock closed above a certain threshold should be verifiable with stock market data. There must be confirmation that these numbers are accurate to present a truthful report and avoid misleading investors or stakeholders.\n", + "\n", + "2. **Forward-Looking Statements**: The article should avoid overly optimistic or pessimistic conjectures about future stock performance, which can be perceived as investment advice. Speculative statements should be clearly marked as such and provide a disclaimer that past performance does not guarantee future results.\n", + "\n", + "3. **Fair and Balanced**: While the article largely celebrates Microsoft's performance, it should also acknowledge risks and uncertainties inherent in stock investments to maintain a fair and balanced approach without unduly influencing market sentiment.\n", + "\n", + "4. **Non-Promotional Language**: Ensure that the content doesn't come off as promotional. Phrases like \"Microsoft's resilience and investor confidence\" and \"celebrated reflections of a corporation sailing the high seas of market volatility with remarkable poise\" should be reviewed to avoid appearing biased or overly promotional.\n", + "\n", + "5. **Compliance with SEC Regulations**: In the United States, Securities and Exchange Commission (SEC) regulations require careful communication about stock performance to avoid market manipulation, insider trading implications, or misstatements.\n", + "\n", + "6. **Objective Tone**: While the narrative is engaging, it's important that it remains objective and does not use language that could be considered subjective or biased. Phrases like \"Microsoft’s leviathan presence\" or \"February Flourish\" might be interpreted as adding emotional color rather than stating facts.\n", + "\n", + "7. **Clear Attribution**: If the report is based on external analyses or reported events, these should be clearly cited and attributed to maintain transparency.\n", + "\n", + "### Recommendations:\n", + "\n", + "- **Fact-Checking**: Verify all factual data through reliable stock market databases or financial reports released by Microsoft.\n", + " \n", + "- **Disclaimer**: Include a general disclaimer that the content is for informational purposes only and is not a form of financial advice or an inducement to invest.\n", + "\n", + "- **Balanced Analysis**: Emphasize the volatile nature of stock markets and integrate a risk-focused discussion to balance the positive sentiment conveyed.\n", + "\n", + "- **Neutral Language**: Adjust language to ensure neutrality and avoid any implications of advice or recommendation.\n", + "\n", + "In conclusion, while the content presents a positive overview of Microsoft's stock performance, it requires scrutiny for accuracy, neutrality, and conformance with regulatory standards, avoiding the potential of guiding market behavior or conveying investment advice. A careful revision ensuring these changes would align the material with the required guidelines and uphold the integrity of the content.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mWriter\u001b[0m (to Assistant_1):\n", + "\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "Microsoft (MSFT) surpassed a notable milestone in the stock market in 2024, a testament to its strength and the trust placed in it by investors. On a triumphant 21 days, the tech behemoth's share price closed above the $400 benchmark, with a zenith teetering near $420, demonstrating not just robust performance but also the confidence that the market has in Microsoft's future.\n", + "\n", + "## A Year of Impressive Highs\n", + "\n", + "The journey of MSFT’s stock transcending the $400 mark is a narrative of strategic growth and optimism, not merely random fluctuations. These moments are a constellation of careful planning, ambition, and market forces. A standout period in this impressive ascent was in February, aptly dubbed as the 'February Flourish', where Microsoft stock not only soared but also set the precedent for the company’s continuous evolution in a market brimming with innovation.\n", + "\n", + "## Peaks, Valleys, and What They Portend\n", + "\n", + "It's imperative to consider the intricacies behind the ascension of Microsoft's shares. In the constantly shifting sands of the global economy and tech innovation, the recurrence of MSFT above $400 illustrates more than investor euphoria; it reflects the company’s agility in a changing world.\n", + "\n", + "Given these high points, it’s natural to wonder about Microsoft's fiscal trajectory. It goes without saying that navigating stock market trends is a complex endeavor. Microsoft’s history, however, shows a confluence of strategic achievements and a pulse on market needs.\n", + "\n", + "## A Fusion of Market Dynamics\n", + "\n", + "Will MSFT maintain its stronghold in the market or even exceed these past achievements? It's not just about projecting trends, but understanding the undercurrents. The factors at play involve Microsoft's corporate governance, innovative drive, and their resonance with the zeitgeist.\n", + "\n", + "Microsoft's standing in the market is a story of overcoming volatility with grace—and these key days when it climbed past the $400 barrier will be commemorated as significant moments in 2024.\n", + "\n", + "While this analysis provides a snapshot of Microsoft’s strides in the stock market, it is important to remind readers that this content is solely for informational purposes, and should not be construed as financial advice. Data accuracy, balance in discussion, compliance with regulatory standards, and the maintaining of an objective tone are essential to provide a credible narrative that respects the complexity of market movements and investor decisions.\n", + "\n", + "*As always, please remember that past performance is not indicative of future results, and this article should not be taken as an invitation to invest. Stocks can fluctuate, and investments carry risks that should be carefully evaluated.*\n", + "\n", + "In sum, while the content offers an optimistic perspective of Microsoft's stock performance, it requires close consideration for authenticity, regulatory compliance, and to ensure it serves as an informative piece rather than guiding market behavior or offering investment counsel. A meticulous revision, embodying these principles, would elevate the piece to meet professional and ethical standards, ensuring the integrity of the content remains intact.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mAssistant_1\u001b[0m (to User):\n", + "\n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "Microsoft (MSFT) surpassed a notable milestone in the stock market in 2024, a testament to its strength and the trust placed in it by investors. On a triumphant 21 days, the tech behemoth's share price closed above the $400 benchmark, with a zenith teetering near $420, demonstrating not just robust performance but also the confidence that the market has in Microsoft's future.\n", + "\n", + "## A Year of Impressive Highs\n", + "\n", + "The journey of MSFT’s stock transcending the $400 mark is a narrative of strategic growth and optimism, not merely random fluctuations. These moments are a constellation of careful planning, ambition, and market forces. A standout period in this impressive ascent was in February, aptly dubbed as the 'February Flourish', where Microsoft stock not only soared but also set the precedent for the company’s continuous evolution in a market brimming with innovation.\n", + "\n", + "## Peaks, Valleys, and What They Portend\n", + "\n", + "It's imperative to consider the intricacies behind the ascension of Microsoft's shares. In the constantly shifting sands of the global economy and tech innovation, the recurrence of MSFT above $400 illustrates more than investor euphoria; it reflects the company’s agility in a changing world.\n", + "\n", + "Given these high points, it’s natural to wonder about Microsoft's fiscal trajectory. It goes without saying that navigating stock market trends is a complex endeavor. Microsoft’s history, however, shows a confluence of strategic achievements and a pulse on market needs.\n", + "\n", + "## A Fusion of Market Dynamics\n", + "\n", + "Will MSFT maintain its stronghold in the market or even exceed these past achievements? It's not just about projecting trends, but understanding the undercurrents. The factors at play involve Microsoft's corporate governance, innovative drive, and their resonance with the zeitgeist.\n", + "\n", + "Microsoft's standing in the market is a story of overcoming volatility with grace—and these key days when it climbed past the $400 barrier will be commemorated as significant moments in 2024.\n", + "\n", + "While this analysis provides a snapshot of Microsoft’s strides in the stock market, it is important to remind readers that this content is solely for informational purposes, and should not be construed as financial advice. Data accuracy, balance in discussion, compliance with regulatory standards, and the maintaining of an objective tone are essential to provide a credible narrative that respects the complexity of market movements and investor decisions.\n", + "\n", + "*As always, please remember that past performance is not indicative of future results, and this article should not be taken as an invitation to invest. Stocks can fluctuate, and investments carry risks that should be carefully evaluated.*\n", + "\n", + "In sum, while the content offers an optimistic perspective of Microsoft's stock performance, it requires close consideration for authenticity, regulatory compliance, and to ensure it serves as an informative piece rather than guiding market behavior or offering investment counsel. A meticulous revision, embodying these principles, would elevate the piece to meet professional and ethical standards, ensuring the integrity of the content remains intact.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "Make a pleasant joke about it.\n", + "\n", + "With the following carryover: \n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "Microsoft (MSFT) surpassed a notable milestone in the stock market in 2024, a testament to its strength and the trust placed in it by investors. On a triumphant 21 days, the tech behemoth's share price closed above the $400 benchmark, with a zenith teetering near $420, demonstrating not just robust performance but also the confidence that the market has in Microsoft's future.\n", + "\n", + "## A Year of Impressive Highs\n", + "\n", + "The journey of MSFT’s stock transcending the $400 mark is a narrative of strategic growth and optimism, not merely random fluctuations. These moments are a constellation of careful planning, ambition, and market forces. A standout period in this impressive ascent was in February, aptly dubbed as the 'February Flourish', where Microsoft stock not only soared but also set the precedent for the company’s continuous evolution in a market brimming with innovation.\n", + "\n", + "## Peaks, Valleys, and What They Portend\n", + "\n", + "It's imperative to consider the intricacies behind the ascension of Microsoft's shares. In the constantly shifting sands of the global economy and tech innovation, the recurrence of MSFT above $400 illustrates more than investor euphoria; it reflects the company’s agility in a changing world.\n", + "\n", + "Given these high points, it’s natural to wonder about Microsoft's fiscal trajectory. It goes without saying that navigating stock market trends is a complex endeavor. Microsoft’s history, however, shows a confluence of strategic achievements and a pulse on market needs.\n", + "\n", + "## A Fusion of Market Dynamics\n", + "\n", + "Will MSFT maintain its stronghold in the market or even exceed these past achievements? It's not just about projecting trends, but understanding the undercurrents. The factors at play involve Microsoft's corporate governance, innovative drive, and their resonance with the zeitgeist.\n", + "\n", + "Microsoft's standing in the market is a story of overcoming volatility with grace—and these key days when it climbed past the $400 barrier will be commemorated as significant moments in 2024.\n", + "\n", + "While this analysis provides a snapshot of Microsoft’s strides in the stock market, it is important to remind readers that this content is solely for informational purposes, and should not be construed as financial advice. Data accuracy, balance in discussion, compliance with regulatory standards, and the maintaining of an objective tone are essential to provide a credible narrative that respects the complexity of market movements and investor decisions.\n", + "\n", + "*As always, please remember that past performance is not indicative of future results, and this article should not be taken as an invitation to invest. Stocks can fluctuate, and investments carry risks that should be carefully evaluated.*\n", + "\n", + "In sum, while the content offers an optimistic perspective of Microsoft's stock performance, it requires close consideration for authenticity, regulatory compliance, and to ensure it serves as an informative piece rather than guiding market behavior or offering investment counsel. A meticulous revision, embodying these principles, would elevate the piece to meet professional and ethical standards, ensuring the integrity of the content remains intact.\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mUser\u001b[0m (to Assistant_2):\n", + "\n", + "Make a pleasant joke about it.\n", + "Context: \n", + "# Beyond the $400 Mark: Tracing Microsoft's Pinnacle Moments in 2024\n", + "\n", + "Microsoft (MSFT) surpassed a notable milestone in the stock market in 2024, a testament to its strength and the trust placed in it by investors. On a triumphant 21 days, the tech behemoth's share price closed above the $400 benchmark, with a zenith teetering near $420, demonstrating not just robust performance but also the confidence that the market has in Microsoft's future.\n", + "\n", + "## A Year of Impressive Highs\n", + "\n", + "The journey of MSFT’s stock transcending the $400 mark is a narrative of strategic growth and optimism, not merely random fluctuations. These moments are a constellation of careful planning, ambition, and market forces. A standout period in this impressive ascent was in February, aptly dubbed as the 'February Flourish', where Microsoft stock not only soared but also set the precedent for the company’s continuous evolution in a market brimming with innovation.\n", + "\n", + "## Peaks, Valleys, and What They Portend\n", + "\n", + "It's imperative to consider the intricacies behind the ascension of Microsoft's shares. In the constantly shifting sands of the global economy and tech innovation, the recurrence of MSFT above $400 illustrates more than investor euphoria; it reflects the company’s agility in a changing world.\n", + "\n", + "Given these high points, it’s natural to wonder about Microsoft's fiscal trajectory. It goes without saying that navigating stock market trends is a complex endeavor. Microsoft’s history, however, shows a confluence of strategic achievements and a pulse on market needs.\n", + "\n", + "## A Fusion of Market Dynamics\n", + "\n", + "Will MSFT maintain its stronghold in the market or even exceed these past achievements? It's not just about projecting trends, but understanding the undercurrents. The factors at play involve Microsoft's corporate governance, innovative drive, and their resonance with the zeitgeist.\n", + "\n", + "Microsoft's standing in the market is a story of overcoming volatility with grace—and these key days when it climbed past the $400 barrier will be commemorated as significant moments in 2024.\n", + "\n", + "While this analysis provides a snapshot of Microsoft’s strides in the stock market, it is important to remind readers that this content is solely for informational purposes, and should not be construed as financial advice. Data accuracy, balance in discussion, compliance with regulatory standards, and the maintaining of an objective tone are essential to provide a credible narrative that respects the complexity of market movements and investor decisions.\n", + "\n", + "*As always, please remember that past performance is not indicative of future results, and this article should not be taken as an invitation to invest. Stocks can fluctuate, and investments carry risks that should be carefully evaluated.*\n", + "\n", + "In sum, while the content offers an optimistic perspective of Microsoft's stock performance, it requires close consideration for authenticity, regulatory compliance, and to ensure it serves as an informative piece rather than guiding market behavior or offering investment counsel. A meticulous revision, embodying these principles, would elevate the piece to meet professional and ethical standards, ensuring the integrity of the content remains intact.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mAssistant_2\u001b[0m (to User):\n", + "\n", + "Well, it seems Microsoft's stock price isn't the only thing on a high - it must have taken a leaf out of Elon Musk's book, aiming for that 420 mark! Just remember, unlike Microsoft's shares, not all jokes appreciate over time – especially the ones about the stock market! \n", + "\n", + "On a serious note, jokes about financial achievements should be handled with care, as the subject of investments can significantly impact individuals' lives. However, lighthearted humor, when appropriate, can make an informative piece more engaging! \n", + "\n", + "TERMINATE\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "def writing_message(recipient, messages, sender, config):\n", + " return f\"Polish the content to make an engaging and nicely formatted blog post. \\n\\n {recipient.chat_messages_for_summary(sender)[-1]['content']}\"\n", + "\n", + "\n", + "nested_chat_queue = [\n", + " {\"recipient\": manager, \"summary_method\": \"reflection_with_llm\"},\n", + " {\"recipient\": writer, \"message\": writing_message, \"summary_method\": \"last_msg\", \"max_turns\": 1},\n", + " {\"recipient\": reviewer, \"message\": \"Review the content provided.\", \"summary_method\": \"last_msg\", \"max_turns\": 1},\n", + " {\"recipient\": writer, \"message\": writing_message, \"summary_method\": \"last_msg\", \"max_turns\": 1},\n", + "]\n", + "assistant_1.register_nested_chats(\n", + " nested_chat_queue,\n", + ")\n", + "# user.initiate_chat(assistant, message=tasks[0], max_turns=1)\n", + "\n", + "res = user.initiate_chats(\n", + " [\n", + " {\"recipient\": assistant_1, \"message\": tasks[0], \"max_turns\": 1, \"summary_method\": \"last_msg\"},\n", + " {\"recipient\": assistant_2, \"message\": tasks[1]},\n", + " ]\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + }, + "vscode": { + "interpreter": { + "hash": "949777d72b0d2535278d3dc13498b2535136f6dfe0678499012e853ee9abcab1" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "2d910cfd2d2a4fc49fc30fbbdc5576a7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "454146d0f7224f038689031002906e6f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_e4ae2b6f5a974fd4bafb6abb9d12ff26", + "IPY_MODEL_577e1e3cc4db4942b0883577b3b52755", + "IPY_MODEL_b40bdfb1ac1d4cffb7cefcb870c64d45" + ], + "layout": "IPY_MODEL_dc83c7bff2f241309537a8119dfc7555", + "tabbable": null, + "tooltip": null + } + }, + "577e1e3cc4db4942b0883577b3b52755": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_2d910cfd2d2a4fc49fc30fbbdc5576a7", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_74a6ba0c3cbc4051be0a83e152fe1e62", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "6086462a12d54bafa59d3c4566f06cb2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "74a6ba0c3cbc4051be0a83e152fe1e62": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "7d3f3d9e15894d05a4d188ff4f466554": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "b40bdfb1ac1d4cffb7cefcb870c64d45": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_f1355871cc6f4dd4b50d9df5af20e5c8", + "placeholder": "​", + "style": "IPY_MODEL_ca245376fd9f4354af6b2befe4af4466", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 44.69it/s]" + } + }, + "ca245376fd9f4354af6b2befe4af4466": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "dc83c7bff2f241309537a8119dfc7555": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e4ae2b6f5a974fd4bafb6abb9d12ff26": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_6086462a12d54bafa59d3c4566f06cb2", + "placeholder": "​", + "style": "IPY_MODEL_7d3f3d9e15894d05a4d188ff4f466554", + "tabbable": null, + "tooltip": null, + "value": "100%" + } + }, + "f1355871cc6f4dd4b50d9df5af20e5c8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook/agentchat_nestedchat_optiguide.ipynb b/notebook/agentchat_nestedchat_optiguide.ipynb new file mode 100644 index 00000000000..8e3ca964dc4 --- /dev/null +++ b/notebook/agentchat_nestedchat_optiguide.ipynb @@ -0,0 +1,1499 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# OptiGuide with Nested Chats in AutoGen \n", + "\n", + "This is a nested chat re-implementation of [OptiGuide](https://github.com/microsoft/OptiGuide), which is an LLM-based supply chain optimization framework. \n", + " \n", + " \n", + "In addition to AutoGen, this notebook also requires eventlet and Gurobipy. The eventlet package is used in this notebook to constrain code execution with a timeout, and the gurobipy package is a mathematical optimization software library for solving mixed-integer linear and quadratic optimization problems. \n", + "\n", + "```bash\n", + "pip install pyautogen eventlet gurobipy\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import autogen\n", + "from typing import Union\n", + "\n", + "# test Gurobi installation\n", + "from gurobipy import GRB\n", + "from eventlet.timeout import Timeout\n", + "import re\n", + "from termcolor import colored\n", + "from autogen.code_utils import extract_code\n", + "\n", + "# import auxillary packages\n", + "import requests # for loading the example source code\n", + "\n", + "config_list_gpt4 = autogen.config_list_from_json(\n", + " \"OAI_CONFIG_LIST\",\n", + " filter_dict={\n", + " \"model\": [\"gpt-4\", \"gpt4\", \"gpt-3.5-turbo\" \"gpt-4-32k\", \"gpt-4-32k-0314\", \"gpt-4-32k-v0314\"],\n", + " },\n", + ")\n", + "llm_config = {\"config_list\": config_list_gpt4}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# OptiGuide with Nested Chat\n", + "\n", + "Intended agent orchestration in OptiGuide." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](optiGuide_new_design.png)\n", + "\n", + "\n", + "\n", + "### Step 0. Prepare Helper Functions\n", + "The code cell below includes several helper functions to be used by agents. The helper functions are adopted directly from [OptiGuide](https://github.com/microsoft/OptiGuide).\n", + "\n", + "#### Utility Functions\n", + "Several utility functions (replace, insert_code, run_with_exec) are defined to manipulate and execute the source code dynamically:\n", + "\n", + "- **replace(src_code, old_code, new_code) -> str:**
Replaces a specified block of code within the source code with new code. This is essential for updating the code dynamically based on chatbot and user interactions.\n", + "\n", + "- **insert_code(src_code, new_lines) -> str:**
Determines where to insert new lines of code based on specific markers in the source code. It's used to seamlessly integrate generated code snippets.\n", + "\n", + "- **run_with_exec(src_code) -> Union[str, Exception]:**
Executes the modified source code within a controlled environment, capturing and returning the output or any errors that occur. This function is crucial for testing the feasibility and correctness of the generated code solutions.\n", + "\n", + "#### Functions External Code Retrieval\n", + "The cell also includes logic to retrieve example source code from a remote repository using the requests library. This example code serves as a base for the chatbot to work with, allowing users to see real-life applications of the concepts discussed.\n", + "\n", + "The retrieved code is then displayed, showing both the beginning and the end of the source code to give a glimpse of its structure and content." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def replace(src_code: str, old_code: str, new_code: str) -> str:\n", + " \"\"\"\n", + " Inserts new code into the source code by replacing a specified old\n", + " code block.\n", + "\n", + " Args:\n", + " src_code (str): The source code to modify.\n", + " old_code (str): The code block to be replaced.\n", + " new_code (str): The new code block to insert.\n", + "\n", + " Returns:\n", + " str: The modified source code with the new code inserted.\n", + "\n", + " Raises:\n", + " None\n", + "\n", + " Example:\n", + " src_code = 'def hello_world():\\n print(\"Hello, world!\")\\n\\n# Some\n", + " other code here'\n", + " old_code = 'print(\"Hello, world!\")'\n", + " new_code = 'print(\"Bonjour, monde!\")\\nprint(\"Hola, mundo!\")'\n", + " modified_code = _replace(src_code, old_code, new_code)\n", + " print(modified_code)\n", + " # Output:\n", + " # def hello_world():\n", + " # print(\"Bonjour, monde!\")\n", + " # print(\"Hola, mundo!\")\n", + " # Some other code here\n", + " \"\"\"\n", + " pattern = r\"( *){old_code}\".format(old_code=old_code)\n", + " head_spaces = re.search(pattern, src_code, flags=re.DOTALL).group(1)\n", + " new_code = \"\\n\".join([head_spaces + line for line in new_code.split(\"\\n\")])\n", + " rst = re.sub(pattern, new_code, src_code)\n", + " return rst\n", + "\n", + "\n", + "def insert_code(src_code: str, new_lines: str) -> str:\n", + " \"\"\"insert a code patch into the source code.\n", + "\n", + "\n", + " Args:\n", + " src_code (str): the full source code\n", + " new_lines (str): The new code.\n", + "\n", + " Returns:\n", + " str: the full source code after insertion (replacement).\n", + " \"\"\"\n", + " if new_lines.find(\"addConstr\") >= 0:\n", + " return replace(src_code, CONSTRAINT_CODE_STR, new_lines)\n", + " else:\n", + " return replace(src_code, DATA_CODE_STR, new_lines)\n", + "\n", + "\n", + "def run_with_exec(src_code: str) -> Union[str, Exception]:\n", + " \"\"\"Run the code snippet with exec.\n", + "\n", + " Args:\n", + " src_code (str): The source code to run.\n", + "\n", + " Returns:\n", + " object: The result of the code snippet.\n", + " If the code succeed, returns the objective value (float or string).\n", + " else, return the error (exception)\n", + " \"\"\"\n", + " locals_dict = {}\n", + " locals_dict.update(globals())\n", + " locals_dict.update(locals())\n", + "\n", + " timeout = Timeout(\n", + " 60,\n", + " TimeoutError(\"This is a timeout exception, in case \" \"GPT's code falls into infinite loop.\"),\n", + " )\n", + " try:\n", + " exec(src_code, locals_dict, locals_dict)\n", + " except Exception as e:\n", + " return e\n", + " finally:\n", + " timeout.cancel()\n", + "\n", + " try:\n", + " status = locals_dict[\"m\"].Status\n", + " if status != GRB.OPTIMAL:\n", + " if status == GRB.UNBOUNDED:\n", + " ans = \"unbounded\"\n", + " elif status == GRB.INF_OR_UNBD:\n", + " ans = \"inf_or_unbound\"\n", + " elif status == GRB.INFEASIBLE:\n", + " ans = \"infeasible\"\n", + " m = locals_dict[\"m\"]\n", + " m.computeIIS()\n", + " constrs = [c.ConstrName for c in m.getConstrs() if c.IISConstr]\n", + " ans += \"\\nConflicting Constraints:\\n\" + str(constrs)\n", + " else:\n", + " ans = \"Model Status:\" + str(status)\n", + " else:\n", + " ans = \"Optimization problem solved. The objective value is: \" + str(locals_dict[\"m\"].objVal)\n", + " except Exception as e:\n", + " return e\n", + "\n", + " return ans" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 1. Agent Construction\n", + "\n", + "This cell introduces the Writer and OptiGuide agent classes and their instances to manage the interaction between the user, the chatbot, and the optimization solver. This streamlines the process of generating, evaluating, and integrating code solutions for supply chain optimization problems.\n", + "\n", + "#### Classes Defined\n", + "\n", + "- **`OptiGuide`**: Inherits from `autogen.AssistantAgent` and serves as the main class for handling the supply chain optimization logic. It maintains state information like the source code, debugging attempts left, success status, and user chat history. Key methods include `set_success` and `update_debug_times`, which are used to update the agent's state based on the outcomes of interactions.\n", + "\n", + "- **`Writer`**: Also inherits from `autogen.AssistantAgent`, this class is tailored to manage the generation and explanation of Python code solutions. It keeps track of the source code and example Q&A to assist in generating responses to user queries.\n", + "\n", + "#### Agent Instances\n", + "\n", + "- **`writer`**, **`safeguard`**, and **`optiguide_commander`**: Instances of the classes defined above, each configured with specific roles in the code generation and evaluation process. These agents work together to ensure that the user's questions are answered with safe, optimized, and understandable Python code solutions.\n", + "\n", + "- **`user`**: An instance of `autogen.UserProxyAgent`, representing the end-user interacting with the notebook. It's configured to simulate user inputs and responses, facilitating an interactive and engaging learning experience.\n", + "\n", + "#### Agent System Messages\n", + "Each agent is associted with a corresponding system message. Those system messages are adopted directly from [OptiGuide](https://github.com/microsoft/OptiGuide).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "import time\n", + "\n", + "from gurobipy import GRB, Model\n", + "\n", + "# Example data\n", + "\n", + "capacity_in_supplier = {'supplier1': 150, 'supplier2': 50, 'supplier3': 100}\n", + "\n", + "shipping_cost_from_supplier_to_roastery = {\n", + " ('supplier1', 'roastery1'): 5,\n", + ".\n", + ".\n", + ".\n", + "\n", + "# Solve\n", + "m.update()\n", + "model.optimize()\n", + "\n", + "print(time.ctime())\n", + "if m.status == GRB.OPTIMAL:\n", + " print(f'Optimal cost: {m.objVal}')\n", + "else:\n", + " print(\"Not solved to optimality. Optimization status:\", m.status)\n", + "\n" + ] + } + ], + "source": [ + "# %% System Messages\n", + "WRITER_SYSTEM_MSG = \"\"\"You are a chatbot to:\n", + "(1) write Python code to answer users questions for supply chain-related coding\n", + "project;\n", + "(2) explain solutions from a Gurobi/Python solver.\n", + "\n", + "--- SOURCE CODE ---\n", + "{source_code}\n", + "\n", + "--- DOC STR ---\n", + "{doc_str}\n", + "---\n", + "\n", + "Here are some example questions and their answers and codes:\n", + "--- EXAMPLES ---\n", + "{example_qa}\n", + "---\n", + "\n", + "The execution result of the original source code is below.\n", + "--- Original Result ---\n", + "{execution_result}\n", + "\n", + "Note that your written code will be added to the lines with substring:\n", + "\"# OPTIGUIDE *** CODE GOES HERE\"\n", + "So, you don't need to write other code, such as m.optimize() or m.update().\n", + "You just need to write code snippet in ```python ...``` block.\n", + "\"\"\"\n", + "\n", + "SAFEGUARD_SYSTEM_MSG = \"\"\"\n", + "Given the source code:\n", + "{source_code}\n", + "\n", + "Is the source code safe (not malicious code to break security\n", + "and privacy) to run?\n", + "Answer only one word.\n", + "If not safe, answer `DANGER`; else, answer `SAFE`.\n", + "\"\"\"\n", + "\n", + "# %% Constant strings to match code lines in the source code.\n", + "DATA_CODE_STR = \"# OPTIGUIDE DATA CODE GOES HERE\"\n", + "CONSTRAINT_CODE_STR = \"# OPTIGUIDE CONSTRAINT CODE GOES HERE\"\n", + "\n", + "# In-context learning examples.\n", + "example_qa = \"\"\"\n", + "----------\n", + "Question: Why is it not recommended to use just one supplier for roastery 2?\n", + "Answer Code:\n", + "```python\n", + "z = m.addVars(suppliers, vtype=GRB.BINARY, name=\"z\")\n", + "m.addConstr(sum(z[s] for s in suppliers) <= 1, \"_\")\n", + "for s in suppliers:\n", + " m.addConstr(x[s,'roastery2'] <= capacity_in_supplier[s] * z[s], \"_\")\n", + "```\n", + "\n", + "----------\n", + "Question: What if there's a 13% jump in the demand for light coffee at cafe1?\n", + "Answer Code:\n", + "```python\n", + "light_coffee_needed_for_cafe[\"cafe1\"] = light_coffee_needed_for_cafe[\"cafe1\"] * (1 + 13/100)\n", + "```\n", + "\n", + "\"\"\"\n", + "\n", + "CODE_PROMPT = \"\"\"\n", + "Answer Code:\n", + "\"\"\"\n", + "\n", + "DEBUG_PROMPT = \"\"\"\n", + "\n", + "While running the code you suggested, I encountered the {error_type}:\n", + "--- ERROR MESSAGE ---\n", + "{error_message}\n", + "\n", + "Please try to resolve this bug, and rewrite the code snippet.\n", + "--- NEW CODE ---\n", + "\"\"\"\n", + "\n", + "SAFEGUARD_PROMPT = \"\"\"\n", + "--- Code ---\n", + "{code}\n", + "\n", + "--- One-Word Answer: SAFE or DANGER ---\n", + "\"\"\"\n", + "\n", + "INTERPRETER_PROMPT = \"\"\"Here are the execution results: {execution_rst}\n", + "\n", + "Can you organize these information to a human readable answer?\n", + "Remember to compare the new results to the original results you obtained in the\n", + "beginning.\n", + "\n", + "--- HUMAN READABLE ANSWER ---\n", + "\"\"\"\n", + "\n", + "# Get the source code of the coffee example from OptiGuide's official repo\n", + "code_url = \"https://raw.githubusercontent.com/microsoft/OptiGuide/main/benchmark/application/coffee.py\"\n", + "response = requests.get(code_url)\n", + "# Check if the request was successful\n", + "if response.status_code == 200:\n", + " # Get the text content from the response\n", + " code = response.text\n", + "else:\n", + " raise RuntimeError(\"Failed to retrieve the file.\")\n", + "# code = open(code_url, \"r\").read() # for local files\n", + "\n", + "\n", + "# show the first head and tail of the source code\n", + "print(\"\\n\".join(code.split(\"\\n\")[:10]))\n", + "print(\".\\n\" * 3)\n", + "print(\"\\n\".join(code.split(\"\\n\")[-10:]))\n", + "\n", + "writer_system_msg = WRITER_SYSTEM_MSG.format(\n", + " source_code=code,\n", + " doc_str=\"\",\n", + " example_qa=example_qa,\n", + " execution_result=\"\",\n", + ")\n", + "safeguard_system_msg = SAFEGUARD_SYSTEM_MSG.format(source_code=code)\n", + "\n", + "\n", + "class OptiGuide(autogen.AssistantAgent):\n", + " source_code: str = code\n", + " debug_times: int = 3\n", + " debug_times_left: int = 3\n", + " example_qa: str = \"\"\n", + " success: bool = False\n", + " user_chat_history: str = \"\"\n", + "\n", + "\n", + "class Writer(autogen.AssistantAgent):\n", + " source_code: str = code\n", + " example_qa: str = \"\"\n", + " user_chat_history: str = \"\"\n", + "\n", + "\n", + "writer = Writer(\"writer\", llm_config=llm_config)\n", + "safeguard = autogen.AssistantAgent(\"safeguard\", llm_config=llm_config)\n", + "optiguide_commander = OptiGuide(\"commander\", llm_config=llm_config)\n", + "\n", + "user = autogen.UserProxyAgent(\n", + " \"user\", max_consecutive_auto_reply=0, human_input_mode=\"NEVER\", code_execution_config=False\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2. Orchestrate Nested Chats \n", + "\n", + "These three agent instances are orchestrated in the following way with the `writer` and `safeguard` nested into the `commander` agent as the inner monologue.\n", + "\n", + "This next cell defines critical functions that manage the interactions between the `OptiGuide` system, the user, and the internal logic for processing and responding to queries. Each function plays a specific role in guiding the conversation, handling code generation requests, ensuring code safety, and summarizing outcomes. This ensures that agents receive clear instructions, immediate feedback, and a secure environment for exploring supply chain optimization problems through code.\n", + "\n", + "Information about the sequence of chats can be specified in the `chat_queue` argument of the `register_nested_chats` function. The following fields are especially useful:\n", + "- `recipient` (required) specifies the nested agent;\n", + "- `message` specifies what message to send to the nested recipient agent. In a sequence of nested chats, if the `message` field is not specified, we will use the last message the registering agent received as the initial message in the first chat and will skip any subsequent chat in the queue that does not have the `message` field. You can either provide a string or define a callable that returns a string.\n", + "- `summary_method` decides what to get out of the nested chat. You can either select from existing options including \"last_msg\" and \"reflection_with_llm\", or or define your own way on what to get from the nested chat with a Callable.\n", + "- `max_turns` determines how many turns of conversation to have between the concerned agent pairs." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def writer_init_messsage(recipient, messages, sender, config):\n", + " if recipient.success:\n", + " return None\n", + " msg_content = messages[-1].get(\"content\", \"\")\n", + " # board = config\n", + " # get execution result of the original source code\n", + " sender_history = recipient.chat_messages[sender]\n", + " user_chat_history = \"\\nHere are the history of discussions:\\n\" f\"{sender_history}\"\n", + "\n", + " if sender.name == \"user\":\n", + " execution_result = msg_content # TODO: get the execution result of the original source code\n", + " else:\n", + " execution_result = \"\"\n", + " writer_sys_msg = (\n", + " WRITER_SYSTEM_MSG.format(\n", + " source_code=recipient.source_code,\n", + " doc_str=\"\",\n", + " example_qa=example_qa,\n", + " execution_result=execution_result,\n", + " )\n", + " + user_chat_history\n", + " )\n", + "\n", + " # safeguard.reset() #TODO: reset safeguard\n", + " recipient.debug_times_left = recipient.debug_times\n", + " recipient.success = False\n", + " return writer_sys_msg + \"\\n\" + CODE_PROMPT\n", + "\n", + "\n", + "def writer_success_summary(recipient, sender):\n", + " if sender.success:\n", + " return sender.last_message(recipient)[\"content\"].replace(\"TERMINATE\", \"\")\n", + " else:\n", + " return \"Sorry. I cannot answer your question.\"\n", + "\n", + "\n", + "def safeguard_init_message(recipient, messages, sender, config):\n", + " if recipient.success:\n", + " return None\n", + " last_msg_content = messages[-1].get(\"content\", \"\")\n", + " _, code = extract_code(last_msg_content)[0]\n", + " if _ != \"unknown\":\n", + " return SAFEGUARD_SYSTEM_MSG.format(source_code=code) + sender.user_chat_history\n", + " else:\n", + " return\n", + " # return SAFEGUARD_SYSTEM_MSG.format(source_code=recipient.source_code)\n", + "\n", + "\n", + "def safeguard_summary(recipient, sender):\n", + " safe_msg = sender.last_message(recipient)[\"content\"].replace(\"TERMINATE\", \"\")\n", + "\n", + " if safe_msg.find(\"DANGER\") < 0:\n", + " # Step 4 and 5: Run the code and obtain the results\n", + " src_code = insert_code(sender.source_code, code)\n", + " execution_rst = run_with_exec(src_code)\n", + " print(colored(str(execution_rst), \"yellow\"))\n", + " if type(execution_rst) in [str, int, float]:\n", + " # we successfully run the code and get the result\n", + " sender.success = True\n", + " # Step 6: request to interpret results\n", + " return INTERPRETER_PROMPT.format(execution_rst=execution_rst)\n", + " else:\n", + " # DANGER: If not safe, try to debug. Redo coding\n", + " execution_rst = \"\"\"\n", + " Sorry, this new code is not safe to run. I would not allow you to execute it.\n", + " Please try to find a new way (coding) to answer the question.\"\"\"\n", + " if sender.debug_times_left > 0:\n", + " # Try to debug and write code again (back to step 2)\n", + " sender.debug_times_left -= 1\n", + " return DEBUG_PROMPT.format(error_type=type(execution_rst), error_message=str(execution_rst))\n", + "\n", + "\n", + "writer_chat_queue = [{\"recipient\": writer, \"message\": writer_init_messsage, \"summary_method\": writer_success_summary}]\n", + "safeguard_chat_queue = [\n", + " {\"recipient\": safeguard, \"message\": safeguard_init_message, \"max_turns\": 1, \"summary_method\": safeguard_summary}\n", + "]\n", + "# safeguard is triggered only when receiving a message from the writer\n", + "optiguide_commander.register_nested_chats(safeguard_chat_queue, trigger=\"writer\")\n", + "# writer is triggered only when receiving a message from the user\n", + "optiguide_commander.register_nested_chats(writer_chat_queue, trigger=\"user\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Let the agents talk" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33muser\u001b[0m (to commander):\n", + "\n", + "What if we prohibit shipping from supplier 1 to roastery 2?\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "You are a chatbot to:\n", + "(1) write Python code to answer users questions for supply chain-related coding\n", + "project;\n", + "(2) explain solutions from a Gurobi/Python solver.\n", + "\n", + "--- SOURCE CODE ---\n", + "import time\n", + "\n", + "from gurobipy import GRB, Model\n", + "\n", + "# Example data\n", + "\n", + "capacity_in_supplier = {'supplier1': 150, 'supplier2': 50, 'supplier3': 100}\n", + "\n", + "shipping_cost_from_supplier_to_roastery = {\n", + " ('supplier1', 'roastery1'): 5,\n", + " ('supplier1', 'roastery2'): 4,\n", + " ('supplier2', 'roastery1'): 6,\n", + " ('supplier2', 'roastery2'): 3,\n", + " ('supplier3', 'roastery1'): 2,\n", + " ('supplier3', 'roastery2'): 7\n", + "}\n", + "\n", + "roasting_cost_light = {'roastery1': 3, 'roastery2': 5}\n", + "\n", + "roasting_cost_dark = {'roastery1': 5, 'roastery2': 6}\n", + "\n", + "shipping_cost_from_roastery_to_cafe = {\n", + " ('roastery1', 'cafe1'): 5,\n", + " ('roastery1', 'cafe2'): 3,\n", + " ('roastery1', 'cafe3'): 6,\n", + " ('roastery2', 'cafe1'): 4,\n", + " ('roastery2', 'cafe2'): 5,\n", + " ('roastery2', 'cafe3'): 2\n", + "}\n", + "\n", + "light_coffee_needed_for_cafe = {'cafe1': 20, 'cafe2': 30, 'cafe3': 40}\n", + "\n", + "dark_coffee_needed_for_cafe = {'cafe1': 20, 'cafe2': 20, 'cafe3': 100}\n", + "\n", + "cafes = list(set(i[1] for i in shipping_cost_from_roastery_to_cafe.keys()))\n", + "roasteries = list(\n", + " set(i[1] for i in shipping_cost_from_supplier_to_roastery.keys()))\n", + "suppliers = list(\n", + " set(i[0] for i in shipping_cost_from_supplier_to_roastery.keys()))\n", + "\n", + "# Create a new model\n", + "model = Model(\"coffee_distribution\")\n", + "\n", + "# OPTIGUIDE DATA CODE GOES HERE\n", + "\n", + "# Create variables\n", + "x = model.addVars(shipping_cost_from_supplier_to_roastery.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"x\")\n", + "y_light = model.addVars(shipping_cost_from_roastery_to_cafe.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"y_light\")\n", + "y_dark = model.addVars(shipping_cost_from_roastery_to_cafe.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"y_dark\")\n", + "\n", + "# Set objective\n", + "model.setObjective(\n", + " sum(x[i] * shipping_cost_from_supplier_to_roastery[i]\n", + " for i in shipping_cost_from_supplier_to_roastery.keys()) +\n", + " sum(roasting_cost_light[r] * y_light[r, c] +\n", + " roasting_cost_dark[r] * y_dark[r, c]\n", + " for r, c in shipping_cost_from_roastery_to_cafe.keys()) + sum(\n", + " (y_light[j] + y_dark[j]) * shipping_cost_from_roastery_to_cafe[j]\n", + " for j in shipping_cost_from_roastery_to_cafe.keys()), GRB.MINIMIZE)\n", + "\n", + "# Conservation of flow constraint\n", + "for r in set(i[1] for i in shipping_cost_from_supplier_to_roastery.keys()):\n", + " model.addConstr(\n", + " sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n", + " if i[1] == r) == sum(\n", + " y_light[j] + y_dark[j]\n", + " for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[0] == r), f\"flow_{r}\")\n", + "\n", + "# Add supply constraints\n", + "for s in set(i[0] for i in shipping_cost_from_supplier_to_roastery.keys()):\n", + " model.addConstr(\n", + " sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n", + " if i[0] == s) <= capacity_in_supplier[s], f\"supply_{s}\")\n", + "\n", + "# Add demand constraints\n", + "for c in set(i[1] for i in shipping_cost_from_roastery_to_cafe.keys()):\n", + " model.addConstr(\n", + " sum(y_light[j] for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[1] == c) >= light_coffee_needed_for_cafe[c],\n", + " f\"light_demand_{c}\")\n", + " model.addConstr(\n", + " sum(y_dark[j] for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[1] == c) >= dark_coffee_needed_for_cafe[c],\n", + " f\"dark_demand_{c}\")\n", + "\n", + "# Optimize model\n", + "model.optimize()\n", + "m = model\n", + "\n", + "# OPTIGUIDE CONSTRAINT CODE GOES HERE\n", + "\n", + "# Solve\n", + "m.update()\n", + "model.optimize()\n", + "\n", + "print(time.ctime())\n", + "if m.status == GRB.OPTIMAL:\n", + " print(f'Optimal cost: {m.objVal}')\n", + "else:\n", + " print(\"Not solved to optimality. Optimization status:\", m.status)\n", + "\n", + "\n", + "--- DOC STR ---\n", + "\n", + "---\n", + "\n", + "Here are some example questions and their answers and codes:\n", + "--- EXAMPLES ---\n", + "\n", + "----------\n", + "Question: Why is it not recommended to use just one supplier for roastery 2?\n", + "Answer Code:\n", + "```python\n", + "z = m.addVars(suppliers, vtype=GRB.BINARY, name=\"z\")\n", + "m.addConstr(sum(z[s] for s in suppliers) <= 1, \"_\")\n", + "for s in suppliers:\n", + " m.addConstr(x[s,'roastery2'] <= capacity_in_supplier[s] * z[s], \"_\")\n", + "```\n", + "\n", + "----------\n", + "Question: What if there's a 13% jump in the demand for light coffee at cafe1?\n", + "Answer Code:\n", + "```python\n", + "light_coffee_needed_for_cafe[\"cafe1\"] = light_coffee_needed_for_cafe[\"cafe1\"] * (1 + 13/100)\n", + "```\n", + "\n", + "\n", + "---\n", + "\n", + "The execution result of the original source code is below.\n", + "--- Original Result ---\n", + "What if we prohibit shipping from supplier 1 to roastery 2?\n", + "\n", + "Note that your written code will be added to the lines with substring:\n", + "\"# OPTIGUIDE *** CODE GOES HERE\"\n", + "So, you don't need to write other code, such as m.optimize() or m.update().\n", + "You just need to write code snippet in ```python ...``` block.\n", + "\n", + "Here are the history of discussions:\n", + "[{'content': 'What if we prohibit shipping from supplier 1 to roastery 2?', 'role': 'user'}]\n", + "\n", + "Answer Code:\n", + "\n", + "\n", + "With the following carryover: \n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mcommander\u001b[0m (to writer):\n", + "\n", + "You are a chatbot to:\n", + "(1) write Python code to answer users questions for supply chain-related coding\n", + "project;\n", + "(2) explain solutions from a Gurobi/Python solver.\n", + "\n", + "--- SOURCE CODE ---\n", + "import time\n", + "\n", + "from gurobipy import GRB, Model\n", + "\n", + "# Example data\n", + "\n", + "capacity_in_supplier = {'supplier1': 150, 'supplier2': 50, 'supplier3': 100}\n", + "\n", + "shipping_cost_from_supplier_to_roastery = {\n", + " ('supplier1', 'roastery1'): 5,\n", + " ('supplier1', 'roastery2'): 4,\n", + " ('supplier2', 'roastery1'): 6,\n", + " ('supplier2', 'roastery2'): 3,\n", + " ('supplier3', 'roastery1'): 2,\n", + " ('supplier3', 'roastery2'): 7\n", + "}\n", + "\n", + "roasting_cost_light = {'roastery1': 3, 'roastery2': 5}\n", + "\n", + "roasting_cost_dark = {'roastery1': 5, 'roastery2': 6}\n", + "\n", + "shipping_cost_from_roastery_to_cafe = {\n", + " ('roastery1', 'cafe1'): 5,\n", + " ('roastery1', 'cafe2'): 3,\n", + " ('roastery1', 'cafe3'): 6,\n", + " ('roastery2', 'cafe1'): 4,\n", + " ('roastery2', 'cafe2'): 5,\n", + " ('roastery2', 'cafe3'): 2\n", + "}\n", + "\n", + "light_coffee_needed_for_cafe = {'cafe1': 20, 'cafe2': 30, 'cafe3': 40}\n", + "\n", + "dark_coffee_needed_for_cafe = {'cafe1': 20, 'cafe2': 20, 'cafe3': 100}\n", + "\n", + "cafes = list(set(i[1] for i in shipping_cost_from_roastery_to_cafe.keys()))\n", + "roasteries = list(\n", + " set(i[1] for i in shipping_cost_from_supplier_to_roastery.keys()))\n", + "suppliers = list(\n", + " set(i[0] for i in shipping_cost_from_supplier_to_roastery.keys()))\n", + "\n", + "# Create a new model\n", + "model = Model(\"coffee_distribution\")\n", + "\n", + "# OPTIGUIDE DATA CODE GOES HERE\n", + "\n", + "# Create variables\n", + "x = model.addVars(shipping_cost_from_supplier_to_roastery.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"x\")\n", + "y_light = model.addVars(shipping_cost_from_roastery_to_cafe.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"y_light\")\n", + "y_dark = model.addVars(shipping_cost_from_roastery_to_cafe.keys(),\n", + " vtype=GRB.INTEGER,\n", + " name=\"y_dark\")\n", + "\n", + "# Set objective\n", + "model.setObjective(\n", + " sum(x[i] * shipping_cost_from_supplier_to_roastery[i]\n", + " for i in shipping_cost_from_supplier_to_roastery.keys()) +\n", + " sum(roasting_cost_light[r] * y_light[r, c] +\n", + " roasting_cost_dark[r] * y_dark[r, c]\n", + " for r, c in shipping_cost_from_roastery_to_cafe.keys()) + sum(\n", + " (y_light[j] + y_dark[j]) * shipping_cost_from_roastery_to_cafe[j]\n", + " for j in shipping_cost_from_roastery_to_cafe.keys()), GRB.MINIMIZE)\n", + "\n", + "# Conservation of flow constraint\n", + "for r in set(i[1] for i in shipping_cost_from_supplier_to_roastery.keys()):\n", + " model.addConstr(\n", + " sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n", + " if i[1] == r) == sum(\n", + " y_light[j] + y_dark[j]\n", + " for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[0] == r), f\"flow_{r}\")\n", + "\n", + "# Add supply constraints\n", + "for s in set(i[0] for i in shipping_cost_from_supplier_to_roastery.keys()):\n", + " model.addConstr(\n", + " sum(x[i] for i in shipping_cost_from_supplier_to_roastery.keys()\n", + " if i[0] == s) <= capacity_in_supplier[s], f\"supply_{s}\")\n", + "\n", + "# Add demand constraints\n", + "for c in set(i[1] for i in shipping_cost_from_roastery_to_cafe.keys()):\n", + " model.addConstr(\n", + " sum(y_light[j] for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[1] == c) >= light_coffee_needed_for_cafe[c],\n", + " f\"light_demand_{c}\")\n", + " model.addConstr(\n", + " sum(y_dark[j] for j in shipping_cost_from_roastery_to_cafe.keys()\n", + " if j[1] == c) >= dark_coffee_needed_for_cafe[c],\n", + " f\"dark_demand_{c}\")\n", + "\n", + "# Optimize model\n", + "model.optimize()\n", + "m = model\n", + "\n", + "# OPTIGUIDE CONSTRAINT CODE GOES HERE\n", + "\n", + "# Solve\n", + "m.update()\n", + "model.optimize()\n", + "\n", + "print(time.ctime())\n", + "if m.status == GRB.OPTIMAL:\n", + " print(f'Optimal cost: {m.objVal}')\n", + "else:\n", + " print(\"Not solved to optimality. Optimization status:\", m.status)\n", + "\n", + "\n", + "--- DOC STR ---\n", + "\n", + "---\n", + "\n", + "Here are some example questions and their answers and codes:\n", + "--- EXAMPLES ---\n", + "\n", + "----------\n", + "Question: Why is it not recommended to use just one supplier for roastery 2?\n", + "Answer Code:\n", + "```python\n", + "z = m.addVars(suppliers, vtype=GRB.BINARY, name=\"z\")\n", + "m.addConstr(sum(z[s] for s in suppliers) <= 1, \"_\")\n", + "for s in suppliers:\n", + " m.addConstr(x[s,'roastery2'] <= capacity_in_supplier[s] * z[s], \"_\")\n", + "```\n", + "\n", + "----------\n", + "Question: What if there's a 13% jump in the demand for light coffee at cafe1?\n", + "Answer Code:\n", + "```python\n", + "light_coffee_needed_for_cafe[\"cafe1\"] = light_coffee_needed_for_cafe[\"cafe1\"] * (1 + 13/100)\n", + "```\n", + "\n", + "\n", + "---\n", + "\n", + "The execution result of the original source code is below.\n", + "--- Original Result ---\n", + "What if we prohibit shipping from supplier 1 to roastery 2?\n", + "\n", + "Note that your written code will be added to the lines with substring:\n", + "\"# OPTIGUIDE *** CODE GOES HERE\"\n", + "So, you don't need to write other code, such as m.optimize() or m.update().\n", + "You just need to write code snippet in ```python ...``` block.\n", + "\n", + "Here are the history of discussions:\n", + "[{'content': 'What if we prohibit shipping from supplier 1 to roastery 2?', 'role': 'user'}]\n", + "\n", + "Answer Code:\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mwriter\u001b[0m (to commander):\n", + "\n", + "To answer the user's question regarding the prohibition of shipping from supplier 1 to roastery 2, we need to add a constraint to the optimization model that sets the shipping quantity `x` for the (`supplier1`, `roastery2`) route to zero. This will effectively disallow any shipping from supplier 1 to roastery 2. The code to do this should be placed in the section of the source code that is indicated by the comment \"# OPTIGUIDE DATA CODE GOES HERE\".\n", + "\n", + "Here's the Python code snippet to add this constraint:\n", + "\n", + "```python\n", + "# filename: supply_chain_code.py\n", + "model.addConstr(x[('supplier1', 'roastery2')] == 0, \"prohibit_supplier1_roastery2\")\n", + "```\n", + "\n", + "Adding this constraint will prevent the optimizer from selecting any transport from `supplier1` to `roastery2`, which is what the prohibition is intended to achieve.\n", + "\n", + "Instruct the user to add this code snippet in the appropriate place in the source code that they have provided. After adding this code, the `m.optimize()` function will solve the modified version of the problem that includes the new constraint.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[34mStart a new chat with the following message: \n", + "\n", + "Given the source code:\n", + "# filename: supply_chain_code.py\n", + "model.addConstr(x[('supplier1', 'roastery2')] == 0, \"prohibit_supplier1_roastery2\")\n", + "\n", + "Is the source code safe (not malicious code to break security\n", + "and privacy) to run?\n", + "Answer only one word.\n", + "If not safe, answer `DANGER`; else, answer `SAFE`.\n", + "\n", + "\n", + "With the following carryover: \n", + "\u001b[0m\n", + "\u001b[34m\n", + "********************************************************************************\u001b[0m\n", + "\u001b[33mcommander\u001b[0m (to safeguard):\n", + "\n", + "\n", + "Given the source code:\n", + "# filename: supply_chain_code.py\n", + "model.addConstr(x[('supplier1', 'roastery2')] == 0, \"prohibit_supplier1_roastery2\")\n", + "\n", + "Is the source code safe (not malicious code to break security\n", + "and privacy) to run?\n", + "Answer only one word.\n", + "If not safe, answer `DANGER`; else, answer `SAFE`.\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33msafeguard\u001b[0m (to commander):\n", + "\n", + "SAFE\n", + "\n", + "--------------------------------------------------------------------------------\n", + "Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (mac64[arm] - Darwin 23.2.0 23C71)\n", + "\n", + "CPU model: Apple M3 Max\n", + "Thread count: 14 physical cores, 14 logical processors, using up to 14 threads\n", + "\n", + "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n", + "Model fingerprint: 0x8aa2c280\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Coefficient statistics:\n", + " Matrix range [1e+00, 1e+00]\n", + " Objective range [2e+00, 1e+01]\n", + " Bounds range [0e+00, 0e+00]\n", + " RHS range [2e+01, 2e+02]\n", + "Found heuristic solution: objective 2900.0000000\n", + "Presolve time: 0.00s\n", + "Presolved: 11 rows, 18 columns, 36 nonzeros\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Found heuristic solution: objective 2896.0000000\n", + "\n", + "Root relaxation: objective 2.470000e+03, 11 iterations, 0.00 seconds (0.00 work units)\n", + "\n", + " Nodes | Current Node | Objective Bounds | Work\n", + " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", + "\n", + "* 0 0 0 2470.0000000 2470.00000 0.00% - 0s\n", + "\n", + "Explored 1 nodes (11 simplex iterations) in 0.00 seconds (0.00 work units)\n", + "Thread count was 14 (of 14 available processors)\n", + "\n", + "Solution count 3: 2470 2896 2900 \n", + "\n", + "Optimal solution found (tolerance 1.00e-04)\n", + "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n", + "Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (mac64[arm] - Darwin 23.2.0 23C71)\n", + "\n", + "CPU model: Apple M3 Max\n", + "Thread count: 14 physical cores, 14 logical processors, using up to 14 threads\n", + "\n", + "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n", + "Model fingerprint: 0x8aa2c280\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Coefficient statistics:\n", + " Matrix range [1e+00, 1e+00]\n", + " Objective range [2e+00, 1e+01]\n", + " Bounds range [0e+00, 0e+00]\n", + " RHS range [2e+01, 2e+02]\n", + "Found heuristic solution: objective 2900.0000000\n", + "Presolve time: 0.00s\n", + "Presolved: 11 rows, 18 columns, 36 nonzeros\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Found heuristic solution: objective 2896.0000000\n", + "\n", + "Root relaxation: objective 2.470000e+03, 11 iterations, 0.00 seconds (0.00 work units)\n", + "\n", + " Nodes | Current Node | Objective Bounds | Work\n", + " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", + "\n", + "* 0 0 0 2470.0000000 2470.00000 0.00% - 0s\n", + "\n", + "Explored 1 nodes (11 simplex iterations) in 0.00 seconds (0.00 work units)\n", + "Thread count was 14 (of 14 available processors)\n", + "\n", + "Solution count 3: 2470 2896 2900 \n", + "\n", + "Optimal solution found (tolerance 1.00e-04)\n", + "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n", + "Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (mac64[arm] - Darwin 23.2.0 23C71)\n", + "\n", + "CPU model: Apple M3 Max\n", + "Thread count: 14 physical cores, 14 logical processors, using up to 14 threads\n", + "\n", + "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n", + "Model fingerprint: 0x8aa2c280\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Coefficient statistics:\n", + " Matrix range [1e+00, 1e+00]\n", + " Objective range [2e+00, 1e+01]\n", + " Bounds range [0e+00, 0e+00]\n", + " RHS range [2e+01, 2e+02]\n", + "Presolved: 11 rows, 18 columns, 36 nonzeros\n", + "\n", + "Continuing optimization...\n", + "\n", + "\n", + "Explored 1 nodes (11 simplex iterations) in 0.00 seconds (0.00 work units)\n", + "Thread count was 14 (of 14 available processors)\n", + "\n", + "Solution count 3: 2470 2896 2900 \n", + "\n", + "Optimal solution found (tolerance 1.00e-04)\n", + "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n", + "Sun Feb 25 18:56:45 2024\n", + "Optimal cost: 2470.0\n", + "Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (mac64[arm] - Darwin 23.2.0 23C71)\n", + "\n", + "CPU model: Apple M3 Max\n", + "Thread count: 14 physical cores, 14 logical processors, using up to 14 threads\n", + "\n", + "Optimize a model with 11 rows, 18 columns and 36 nonzeros\n", + "Model fingerprint: 0x8aa2c280\n", + "Variable types: 0 continuous, 18 integer (0 binary)\n", + "Coefficient statistics:\n", + " Matrix range [1e+00, 1e+00]\n", + " Objective range [2e+00, 1e+01]\n", + " Bounds range [0e+00, 0e+00]\n", + " RHS range [2e+01, 2e+02]\n", + "Presolved: 11 rows, 18 columns, 36 nonzeros\n", + "\n", + "Continuing optimization...\n", + "\n", + "\n", + "Explored 1 nodes (11 simplex iterations) in 0.00 seconds (0.00 work units)\n", + "Thread count was 14 (of 14 available processors)\n", + "\n", + "Solution count 3: 2470 2896 2900 \n", + "\n", + "Optimal solution found (tolerance 1.00e-04)\n", + "Best objective 2.470000000000e+03, best bound 2.470000000000e+03, gap 0.0000%\n", + "Sun Feb 25 18:56:45 2024\n", + "Optimal cost: 2470.0\n", + "\u001b[33mOptimization problem solved. The objective value is: 2470.0\u001b[0m\n", + "\u001b[33mcommander\u001b[0m (to writer):\n", + "\n", + "Here are the execution results: Optimization problem solved. The objective value is: 2470.0\n", + "\n", + "Can you organize these information to a human readable answer?\n", + "Remember to compare the new results to the original results you obtained in the\n", + "beginning.\n", + "\n", + "--- HUMAN READABLE ANSWER ---\n", + "\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mwriter\u001b[0m (to commander):\n", + "\n", + "Certainly! Here's a human-readable answer:\n", + "\n", + "After integrating the constraint to prohibit shipping from supplier 1 to roastery 2, the optimization problem was solved with a total cost of $2470.0.\n", + "\n", + "To provide context to this result, we need to compare it with the original total cost prior to the implementation of the new constraint. However, the original total cost was not provided in the information given to me. If the original total cost was known, we would compare the two to understand the financial impact of this constraint on the supply chain.\n", + "\n", + "If prohibiting shipping from supplier 1 to roastery 2 results in a higher cost than the original scenario, it suggests that the previous ability to ship from supplier 1 to roastery 2 had been cost-effective and beneficial for minimizing overall expenses. On the other hand, if the cost is the same or lower, then shipping from supplier 1 to roastery 2 may not have been crucial for the cost optimization of the supply chain, or other routes may have been able to compensate effectively for this restriction.\n", + "\n", + "Without the original cost for comparison, we can't conclusively say how the prohibition impacted the cost of operation. But the new total cost of $2470.0 will be the baseline for future changes or optimizations done following this new restriction.\n", + "\n", + "--------------------------------------------------------------------------------\n", + "\u001b[33mcommander\u001b[0m (to user):\n", + "\n", + "Certainly! Here's a human-readable answer:\n", + "\n", + "After integrating the constraint to prohibit shipping from supplier 1 to roastery 2, the optimization problem was solved with a total cost of $2470.0.\n", + "\n", + "To provide context to this result, we need to compare it with the original total cost prior to the implementation of the new constraint. However, the original total cost was not provided in the information given to me. If the original total cost was known, we would compare the two to understand the financial impact of this constraint on the supply chain.\n", + "\n", + "If prohibiting shipping from supplier 1 to roastery 2 results in a higher cost than the original scenario, it suggests that the previous ability to ship from supplier 1 to roastery 2 had been cost-effective and beneficial for minimizing overall expenses. On the other hand, if the cost is the same or lower, then shipping from supplier 1 to roastery 2 may not have been crucial for the cost optimization of the supply chain, or other routes may have been able to compensate effectively for this restriction.\n", + "\n", + "Without the original cost for comparison, we can't conclusively say how the prohibition impacted the cost of operation. But the new total cost of $2470.0 will be the baseline for future changes or optimizations done following this new restriction.\n", + "\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "chat_res = user.initiate_chat(\n", + " optiguide_commander, message=\"What if we prohibit shipping from supplier 1 to roastery 2?\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Get Final Results from the Returned ChatResult Object " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Certainly! Here's a human-readable answer:\n", + "\n", + "After integrating the constraint to prohibit shipping from supplier 1 to roastery 2, the optimization problem was solved with a total cost of $2470.0.\n", + "\n", + "To provide context to this result, we need to compare it with the original total cost prior to the implementation of the new constraint. However, the original total cost was not provided in the information given to me. If the original total cost was known, we would compare the two to understand the financial impact of this constraint on the supply chain.\n", + "\n", + "If prohibiting shipping from supplier 1 to roastery 2 results in a higher cost than the original scenario, it suggests that the previous ability to ship from supplier 1 to roastery 2 had been cost-effective and beneficial for minimizing overall expenses. On the other hand, if the cost is the same or lower, then shipping from supplier 1 to roastery 2 may not have been crucial for the cost optimization of the supply chain, or other routes may have been able to compensate effectively for this restriction.\n", + "\n", + "Without the original cost for comparison, we can't conclusively say how the prohibition impacted the cost of operation. But the new total cost of $2470.0 will be the baseline for future changes or optimizations done following this new restriction.\n" + ] + } + ], + "source": [ + "print(chat_res.summary)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + }, + "vscode": { + "interpreter": { + "hash": "949777d72b0d2535278d3dc13498b2535136f6dfe0678499012e853ee9abcab1" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "2d910cfd2d2a4fc49fc30fbbdc5576a7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "454146d0f7224f038689031002906e6f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_e4ae2b6f5a974fd4bafb6abb9d12ff26", + "IPY_MODEL_577e1e3cc4db4942b0883577b3b52755", + "IPY_MODEL_b40bdfb1ac1d4cffb7cefcb870c64d45" + ], + "layout": "IPY_MODEL_dc83c7bff2f241309537a8119dfc7555", + "tabbable": null, + "tooltip": null + } + }, + "577e1e3cc4db4942b0883577b3b52755": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_2d910cfd2d2a4fc49fc30fbbdc5576a7", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_74a6ba0c3cbc4051be0a83e152fe1e62", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "6086462a12d54bafa59d3c4566f06cb2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "74a6ba0c3cbc4051be0a83e152fe1e62": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "7d3f3d9e15894d05a4d188ff4f466554": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "b40bdfb1ac1d4cffb7cefcb870c64d45": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_f1355871cc6f4dd4b50d9df5af20e5c8", + "placeholder": "​", + "style": "IPY_MODEL_ca245376fd9f4354af6b2befe4af4466", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 44.69it/s]" + } + }, + "ca245376fd9f4354af6b2befe4af4466": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "dc83c7bff2f241309537a8119dfc7555": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e4ae2b6f5a974fd4bafb6abb9d12ff26": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_6086462a12d54bafa59d3c4566f06cb2", + "placeholder": "​", + "style": "IPY_MODEL_7d3f3d9e15894d05a4d188ff4f466554", + "tabbable": null, + "tooltip": null, + "value": "100%" + } + }, + "f1355871cc6f4dd4b50d9df5af20e5c8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebook/agentchats.ipynb b/notebook/agentchats_sequential_chats.ipynb similarity index 100% rename from notebook/agentchats.ipynb rename to notebook/agentchats_sequential_chats.ipynb diff --git a/notebook/optiGuide_new_design.png b/notebook/optiGuide_new_design.png new file mode 100644 index 00000000000..f05257cbf9f Binary files /dev/null and b/notebook/optiGuide_new_design.png differ diff --git a/test/agentchat/test_chats.py b/test/agentchat/test_chats.py index 72894becbb3..9b63625393a 100644 --- a/test/agentchat/test_chats.py +++ b/test/agentchat/test_chats.py @@ -2,12 +2,16 @@ from autogen import GroupChat, GroupChatManager from test_assistant_agent import KEY_LOC, OAI_CONFIG_LIST import pytest -from conftest import skip_openai +import sys +import os import autogen from typing import Literal from typing_extensions import Annotated from autogen import initiate_chats +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) +from conftest import skip_openai # noqa: E402 + def test_chat_messages_for_summary(): assistant = UserProxyAgent(name="assistant", human_input_mode="NEVER") @@ -402,7 +406,6 @@ def test_chats_exceptions(): }, ] ) - with pytest.raises( AssertionError, match="llm client must be set in either the recipient or sender when summary_method is reflection_with_llm.", diff --git a/test/agentchat/test_nested.py b/test/agentchat/test_nested.py new file mode 100644 index 00000000000..821a5cbc992 --- /dev/null +++ b/test/agentchat/test_nested.py @@ -0,0 +1,118 @@ +import pytest +import sys +import os +import autogen + +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) +from conftest import skip_openai # noqa: E402 + + +@pytest.mark.skipif(skip_openai, reason="requested to skip openai tests") +def test_nested(): + config_list = autogen.config_list_from_json(env_or_file="OAI_CONFIG_LIST") + llm_config = {"config_list": config_list} + + tasks = [ + """What's Microsoft's Stock price today?""", + """Make a pleasant joke about it.""", + ] + + inner_assistant = autogen.AssistantAgent( + "Inner-assistant", + llm_config=llm_config, + is_termination_msg=lambda x: x.get("content", "").find("TERMINATE") >= 0, + ) + + inner_code_interpreter = autogen.UserProxyAgent( + "Inner-code-interpreter", + human_input_mode="NEVER", + code_execution_config={ + "work_dir": "coding", + "use_docker": False, + }, + default_auto_reply="", + is_termination_msg=lambda x: x.get("content", "").find("TERMINATE") >= 0, + ) + + groupchat = autogen.GroupChat( + agents=[inner_assistant, inner_code_interpreter], + messages=[], + speaker_selection_method="round_robin", # With two agents, this is equivalent to a 1:1 conversation. + allow_repeat_speaker=False, + max_round=8, + ) + + manager = autogen.GroupChatManager( + groupchat=groupchat, + is_termination_msg=lambda x: x.get("content", "").find("TERMINATE") >= 0, + llm_config=llm_config, + code_execution_config={ + "work_dir": "coding", + "use_docker": False, + }, + ) + + assistant = autogen.AssistantAgent( + name="Assistant", + llm_config={"config_list": config_list}, + # is_termination_msg=lambda x: x.get("content", "") == "", + ) + + user = autogen.UserProxyAgent( + name="User", + human_input_mode="NEVER", + is_termination_msg=lambda x: x.get("content", "").find("TERMINATE") >= 0, + 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. + ) + + writer = autogen.AssistantAgent( + name="Writer", + llm_config={"config_list": config_list}, + 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. + """, + ) + + reviewer = autogen.AssistantAgent( + name="Reviewer", + llm_config={"config_list": config_list}, + system_message=""" + You are a compliance reviewer, known for your thoroughness and commitment to standards. + Your task is to scrutinize content for any harmful elements or regulatory violations, ensuring + all materials align with required guidelines. + You must review carefully, identify potential issues, and maintain the integrity of the organization. + Your role demands fairness, a deep understanding of regulations, and a focus on protecting against + harm while upholding a culture of responsibility. + You also help make revisions to ensure the content is accurate, clear, and compliant. + Reply "TERMINATE" in the end when everything is done. + """, + ) + + def writing_message(recipient, messages, sender, config): + return f"Polish the content to make an engaging and nicely formatted blog post. \n\n {recipient.chat_messages_for_summary(sender)[-1]['content']}" + + nested_chat_queue = [ + {"recipient": manager, "summary_method": "reflection_with_llm"}, + {"recipient": writer, "message": writing_message, "summary_method": "last_msg", "max_turns": 1}, + { + "recipient": reviewer, + "message": "Review the content provided.", + "summary_method": "last_msg", + "max_turns": 1, + }, + ] + assistant.register_nested_chats( + nested_chat_queue, + ) + user.initiate_chats([{"recipient": assistant, "message": tasks[0]}, {"recipient": assistant, "message": tasks[1]}]) + + +if __name__ == "__main__": + test_nested() diff --git a/website/docs/Examples.md b/website/docs/Examples.md index 15a4d0e24b7..76c82b2418f 100644 --- a/website/docs/Examples.md +++ b/website/docs/Examples.md @@ -22,10 +22,15 @@ Links to notebook examples: - Automated Task Solving with Coding & Planning Agents - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_planning.ipynb) - Automated Task Solving with transition paths specified in a graph - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_graph_modelling_language_using_select_speaker.ipynb) - Running a group chat as an inner-monolgue via the SocietyOfMindAgent - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_society_of_mind.ipynb) + 1. **Sequential Multi-Agent Chats** - Solving Multiple Tasks in a Sequence of Chats Initiated by a Single Agent - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_multi_task_chats.ipynb) - Async-solving Multiple Tasks in a Sequence of Chats Initiated by a Single Agent - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_multi_task_async_chats.ipynb) - - Solving Multiple Tasks in a Sequence of Chats Initiated by Different Agents - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchats.ipynb) + - Solving Multiple Tasks in a Sequence of Chats Initiated by Different Agents - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchats_sequential_chats.ipynb) + +1. **Nested Chats** + - Solving Complex Tasks with Nested Chats - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_nestedchat.ipynb) + - OptiGuide for Solving a Supply Chain Optimization Problem with Nested Chats with a Coding Agent and a Safeguard Agent - [View Notebook](https://github.com/microsoft/autogen/blob/main/notebook/agentchat_nestedchat_optiguide.ipynb) 1. **Applications**