autogen icon indicating copy to clipboard operation
autogen copied to clipboard

[Issue]: need some help on multimodal multi agent, function call

Open tomorrmato opened this issue 1 year ago • 10 comments

Describe the issue

Hi guys, I am currently using autogen multi-agent, multimodal, and function call for my project, my goal is to 1. convert code to image 2. get gpt-4 vision image feedback 3. improve the code based on image feedback.

I am currently using autogen.GroupChatManager for my task, my project has several agents: coder: trigger the python code executor and convert python code to image bytes or extract error message (this agent uses function call so it uses gpt-4 text model) image_critic: given image feedback (this agent uses gpt-4 vision)

current workflow: step 1. coder triggers the function call to get image step 2. coder sends the image to image_critic for feedback step 3. image_critic send feedback to gpt-4 vision and request code update
continue step 1-3 until max iteration or all feedback has been addressed.

My local env: docker container with pip installing following version

autogen==0.2.15
jupyter_client==8.6.0

here is my self contained code

import autogen
import jupyter_client
import base64
from base64 import b64decode

from autogen.agentchat.contrib.multimodal_conversable_agent import MultimodalConversableAgent
from autogen.agentchat.contrib.img_utils import gpt4v_formatter 
from autogen import Agent, AssistantAgent
from typing import List, Dict, Union, Optional

config_list = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": [
                "gpt-4-vision-preview", 
                  # "gpt-4-1106-preview"
        ],
    },
)

config_list_4v = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": ["gpt-4-vision-preview", #"gpt-4-1106-preview"
                 ],
    },
)

config_list_gpt4 = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": ["gpt-4-vision-preview", "gpt-4-1106-preview"],
    },
)

# gpt4_llm_config = {"config_list": config_list_gpt4, "cache_seed": 42}


criteria_prompt = """Evaluate the image against the provided criteria. For each criterion, assign a score on a scale from 1 to 5, where 1 indicates the worst performance and 5 indicates the best.
Criteria:
Q0. How helpful is the image?
Q1. How easy is it to follow the image?

use the following format in your output:
Q 1 | Q 2 score | your reason 1
Q 2 | Q 2 score | your reason 2
...
"""

# sample python code for testing, use this to save image 
py_code = """
import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \\cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()
"""


# jupyter kernel to execute python code and output a tuple of image bytes and error message 
def call_python(code: str):
    # Create a new kernel manager
    km = jupyter_client.KernelManager()
    km.start_kernel()

    # Create a client for the kernel
    kc = km.client()
    kc.start_channels()

    image_data, error_message = None, None 
    try:
        # Send code to be executed
        kc.execute(code)

        # Wait for and process the execution result
        while True:
            try:
                msg = kc.get_iopub_msg(timeout=90)
                if msg['header']['msg_type'] in ['execute_result', 'display_data']:
                    data = msg['content']['data']

                    # change the type 
                    if 'image/png' in data:
                        # Decode the image data
                        image_data = b64decode(data['image/png'])
                        break

                if msg['header']['msg_type'] == 'error':
                    error_content = msg['content']
                    error_message = f"Error: {error_content['ename']}: {error_content['evalue']}\n"
                    error_message += "\n".join(error_content['traceback'])
                    break

                if msg['header']['msg_type'] == 'status':
                    if msg['content']['execution_state'] == 'idle':
                        break
            except Exception as e:
                error_message = f"Exception while waiting for execute result: {e}"
                break
    finally:
        # Stop the channels and kernel
        kc.stop_channels()
        km.shutdown_kernel()

    if error_message:
        return None, error_message

    return image_data, None

def code_to_image_bytes_or_error(code: str):
    image_data, error = call_python(code)
    if error:
        return error
    else:
        image_bytes = base64.b64encode(image_data).decode('utf-8')
        msg = f"""<img data:image/jpeg;base64,{image_bytes}>"""
        return msg
        

# # Modified MultimodalConversableAgent to support passing image bytes 
import copy 
from autogen.code_utils import content_str

class ImageAgent(MultimodalConversableAgent):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    # modified this to support image bytes in str message """<img data:image/jpeg;base64,{image_bytes}>"""
    @staticmethod
    def _message_to_dict(message: Union[Dict, List, str]) -> Dict:
        """Convert a message to a dictionary. This implementation
        handles the GPT-4V formatting for easier prompts.

        The message can be a string, a dictionary, or a list of dictionaries:
            - If it's a string, it will be cast into a list and placed in the 'content' field.
            - If it's a list, it will be directly placed in the 'content' field.
            - If it's a dictionary, it is already in message dict format. The 'content' field of this dictionary
            will be processed using the gpt4v_formatter.
        """
        if isinstance(message, str):
            # important: changed from pil to url 
            return {"content": gpt4v_formatter(message, img_format="url")}            
        if isinstance(message, list):
            return {"content": message}
        if isinstance(message, dict):
            assert "content" in message, "The message dict must have a `content` field"
            if isinstance(message["content"], str):
                message = copy.deepcopy(message)
                message["content"] = gpt4v_formatter(message["content"], img_format="pil")
            try:
                content_str(message["content"])
            except (TypeError, ValueError) as e:
                print("The `content` field should be compatible with the content_str function!")
                raise e
            return message
        raise ValueError(f"Unsupported message type: {type(message)}")


# verify image bytes work fine in ImageAgent
test_image_agent = MultimodalConversableAgent(
    name="image-explainer",
    max_consecutive_auto_reply=10,
    llm_config={"config_list": config_list_4v, "temperature": 0, "max_tokens": 4096},
)

test_user_proxy = ImageAgent(
    name="User_proxy",
    system_message="A human admin.",
    human_input_mode="NEVER",  # Try between ALWAYS or NEVER
    max_consecutive_auto_reply=0,
    code_execution_config={
        "use_docker": False
    },  
    llm_config={"config_list": config_list_4v, "temperature": 0, "max_tokens": 4096},
)

image_tag = code_to_image_bytes_or_error(py_code)
test_user_proxy.initiate_chat(
    test_image_agent,
    message=f""" what is this image about? {image_tag}""",
)



# MultimodalConversableAgent or ImageAgent
image_critic = MultimodalConversableAgent(
    name="image-explainer",
    system_message=criteria_prompt,
    max_consecutive_auto_reply=10,
    llm_config={"config_list": config_list_4v, "temperature": 0, "max_tokens": 4096},
)

# MultimodalConversableAgent or ImageAgent or autogen.UserProxyAgent?
user_proxy = autogen.UserProxyAgent(
    name="User_proxy",
    system_message="A human admin.",
    human_input_mode="TERMINATE",
    code_execution_config={
        "use_docker": False
    },  
    llm_config={"config_list": config_list_gpt4, #config_list_4v, 
                "temperature": 0, "max_tokens": 4096,
               "functions": [
                    {
                        "name": "code_to_image_bytes_or_error",
                        "description": "python code executor",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "code": {
                                    "type": "string",
                                    "description": "python code str",
                                }
                            },
                            "required": ["code"],
                        },
                    },
                ]        
               },
    function_map={"code_to_image_bytes_or_error": code_to_image_bytes_or_error}
)

# MultimodalConversableAgent or ImageAgent
coder = ImageAgent(
    name="Coder",
    system_message="your goal is to trigger the function call for code",
    llm_config={"config_list": config_list_gpt4, "temperature": 0, "max_tokens": 4096,
               "functions": [
                    {
                        "name": "code_to_image_bytes_or_error",
                        "description": "python code executor",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "code": {
                                    "type": "string",
                                    "description": "python code str",
                                }
                            },
                            "required": ["code"],
                        },
                    },
                ]           
        },
    function_map={"code_to_image_bytes_or_error": code_to_image_bytes_or_error}
)

groupchat = autogen.GroupChat(agents=[user_proxy, image_critic, coder], messages=[], max_round=12)
manager = autogen.GroupChatManager(
    groupchat=groupchat, 
    llm_config={"config_list": config_list_gpt4, "temperature": 0, "max_tokens": 4096}
)
user_proxy.initiate_chat(
    manager, 
    message=f""" let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start the the initial python code
{py_code}
""" 
)

changes I have made: ImageAgent._message_to_dict has been modified to support image bytes, verified to work in test_user_proxy.initiate_chat, check out the notebook cell

Errors I got in last block:

User_proxy (to chat_manager):

 let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start the the initial python code

import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

***** Suggested function Call: code_to_image_bytes_or_error *****
Arguments: 
{"code":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# Create a figure with high dpi\nplt.figure(dpi=400)\n\n# Define the x values\nx = np.linspace(0.1, 2*np.pi, 400)\nx = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)\n\n# Calculate the functions\nu = 1/np.tan(x)  # g(x) = cot(x)\ny = np.sin(u)  # f(g(x)) = sin(cot(x))\ng_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)\nf_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))\ndy_dx = f_prime_of_g * g_prime  # The derivative\n\n# Plotting\nplt.subplot(2, 3, 1)\nplt.plot(x, u, label='$g(x) = cot(x)$')\nplt.title('Inner Function $g(x)$')\nplt.xlabel('$x$')\nplt.ylabel('$g(x)$')\nplt.legend()\n\nplt.subplot(2, 3, 2)\nplt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')\nplt.title('Composite Function $f(g(x))$')\nplt.xlabel('$x$')\nplt.ylabel('$f(g(x))$')\nplt.legend()\n\nplt.subplot(2, 3, 3)\nplt.plot(x, g_prime, label=\"$g'(x) = -csc^2(x)$\")\nplt.title(\"Derivative of Inner Function $g'(x)$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$g'(x)$\")\nplt.legend()\n\nplt.subplot(2, 3, 4)\nplt.plot(x, f_prime_of_g, label=\"$f'(g(x)) = cos(cot(x))$\")\nplt.title(\"Derivative of Outer Function $f'(u)$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$f'(g(x))$\")\nplt.legend()\n\nplt.subplot(2, 3, 5)\nplt.plot(x, dy_dx, label=\"$dy/dx = -cos(cot(x)) \\cdot csc^2(x)$\")\nplt.title(\"Derivative of Composite Function $dy/dx$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$dy/dx$\")\nplt.legend()\n\n# Adjust layout to prevent overlap\nplt.tight_layout()\n\n# Show the plot\nplt.show()\n"}
*****************************************************************

--------------------------------------------------------------------------------
---------------------------------------------------------------------------
BadRequestError                           Traceback (most recent call last)
Cell In[6], line 69
     64 groupchat = autogen.GroupChat(agents=[user_proxy, image_critic, coder], messages=[], max_round=12)
     65 manager = autogen.GroupChatManager(
     66     groupchat=groupchat, 
     67     llm_config={"config_list": config_list_gpt4, "temperature": 0, "max_tokens": 4096}
     68 )
---> 69 user_proxy.initiate_chat(
     70     manager, 
     71     message=f""" let's improve the code by using the following flow: 
     72 step 1. trigger the function call for given code (I will execute the code and provide image to you)
     73 step 2. send the image to critics for feedback 
     74 step 3. use the critics feedback to update the code 
     75 continue step 1-3 until all feedback has been addressed.
     76 
     77 let's start the the initial python code
     78 {py_code}
     79 """ 
     80 )

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:838, in ConversableAgent.initiate_chat(self, recipient, clear_history, silent, cache, max_turns, **context)
    836 else:
    837     self._prepare_chat(recipient, clear_history)
--> 838     self.send(self.generate_init_message(**context), recipient, silent=silent)
    839 summary = self._summarize_chat(
    840     context.get("summary_method", ConversableAgent.DEFAULT_summary_method),
    841     recipient,
    842     prompt=context.get("summary_prompt"),
    843     cache=cache,
    844 )
    845 for agent in [self, recipient]:

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:530, in ConversableAgent.send(self, message, recipient, request_reply, silent)
    528 valid = self._append_oai_message(message, "assistant", recipient)
    529 if valid:
--> 530     recipient.receive(message, self, request_reply, silent)
    531 else:
    532     raise ValueError(
    533         "Message can't be converted into a valid ChatCompletion message. Either content or function_call must be provided."
    534     )

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:692, in ConversableAgent.receive(self, message, sender, request_reply, silent)
    690 if request_reply is False or request_reply is None and self.reply_at_receive[sender] is False:
    691     return
--> 692 reply = self.generate_reply(messages=self.chat_messages[sender], sender=sender)
    693 if reply is not None:
    694     self.send(reply, sender, silent=silent)

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:1665, in ConversableAgent.generate_reply(self, messages, sender, **kwargs)
   1663     continue
   1664 if self._match_trigger(reply_func_tuple["trigger"], sender):
-> 1665     final, reply = reply_func(self, messages=messages, sender=sender, config=reply_func_tuple["config"])
   1666     if final:
   1667         return reply

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/groupchat.py:575, in GroupChatManager.run_chat(self, messages, sender, config)
    572     break
    573 try:
    574     # select the next speaker
--> 575     speaker = groupchat.select_speaker(speaker, self)
    576     # let the speaker speak
    577     reply = speaker.generate_reply(sender=self)

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/groupchat.py:395, in GroupChat.select_speaker(self, last_speaker, selector)
    393 # auto speaker selection
    394 selector.update_system_message(self.select_speaker_msg(agents))
--> 395 final, name = selector.generate_oai_reply(messages)
    396 return self._finalize_speaker(last_speaker, final, name, agents)

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:1081, in ConversableAgent.generate_oai_reply(self, messages, sender, config)
   1079 if messages is None:
   1080     messages = self._oai_messages[sender]
-> 1081 extracted_response = self._generate_oai_reply_from_client(
   1082     client, self._oai_system_message + messages, self.client_cache
   1083 )
   1084 return (False, None) if extracted_response is None else (True, extracted_response)

File /usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py:1100, in ConversableAgent._generate_oai_reply_from_client(self, llm_client, messages, cache)
   1097         all_messages.append(message)
   1099 # TODO: #1143 handle token limit exceeded error
-> 1100 response = llm_client.create(
   1101     context=messages[-1].pop("context", None),
   1102     messages=all_messages,
   1103     cache=cache,
   1104 )
   1105 extracted_response = llm_client.extract_text_or_completion_object(response)[0]
   1107 if extracted_response is None:

File /usr/local/lib/python3.11/site-packages/autogen/oai/client.py:624, in OpenAIWrapper.create(self, **config)
    622 try:
    623     request_ts = get_current_ts()
--> 624     response = client.create(params)
    625 except APITimeoutError as err:
    626     logger.debug(f"config {i} timed out", exc_info=True)

File /usr/local/lib/python3.11/site-packages/autogen/oai/client.py:278, in OpenAIClient.create(self, params)
    276     params = params.copy()
    277     params["stream"] = False
--> 278     response = completions.create(**params)
    280 return response

File /usr/local/lib/python3.11/site-packages/openai/_utils/_utils.py:270, in required_args.<locals>.inner.<locals>.wrapper(*args, **kwargs)
    268             msg = f"Missing required argument: {quote(missing[0])}"
    269     raise TypeError(msg)
--> 270 return func(*args, **kwargs)

File /usr/local/lib/python3.11/site-packages/openai/resources/chat/completions.py:645, in Completions.create(self, messages, model, frequency_penalty, function_call, functions, logit_bias, logprobs, max_tokens, n, presence_penalty, response_format, seed, stop, stream, temperature, tool_choice, tools, top_logprobs, top_p, user, extra_headers, extra_query, extra_body, timeout)
    596 @required_args(["messages", "model"], ["messages", "model", "stream"])
    597 def create(
    598     self,
   (...)
    643     timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    644 ) -> ChatCompletion | Stream[ChatCompletionChunk]:
--> 645     return self._post(
    646         "/chat/completions",
    647         body=maybe_transform(
    648             {
    649                 "messages": messages,
    650                 "model": model,
    651                 "frequency_penalty": frequency_penalty,
    652                 "function_call": function_call,
    653                 "functions": functions,
    654                 "logit_bias": logit_bias,
    655                 "logprobs": logprobs,
    656                 "max_tokens": max_tokens,
    657                 "n": n,
    658                 "presence_penalty": presence_penalty,
    659                 "response_format": response_format,
    660                 "seed": seed,
    661                 "stop": stop,
    662                 "stream": stream,
    663                 "temperature": temperature,
    664                 "tool_choice": tool_choice,
    665                 "tools": tools,
    666                 "top_logprobs": top_logprobs,
    667                 "top_p": top_p,
    668                 "user": user,
    669             },
    670             completion_create_params.CompletionCreateParams,
    671         ),
    672         options=make_request_options(
    673             extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
    674         ),
    675         cast_to=ChatCompletion,
    676         stream=stream or False,
    677         stream_cls=Stream[ChatCompletionChunk],
    678     )

File /usr/local/lib/python3.11/site-packages/openai/_base_client.py:1088, in SyncAPIClient.post(self, path, cast_to, body, options, files, stream, stream_cls)
   1074 def post(
   1075     self,
   1076     path: str,
   (...)
   1083     stream_cls: type[_StreamT] | None = None,
   1084 ) -> ResponseT | _StreamT:
   1085     opts = FinalRequestOptions.construct(
   1086         method="post", url=path, json_data=body, files=to_httpx_files(files), **options
   1087     )
-> 1088     return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

File /usr/local/lib/python3.11/site-packages/openai/_base_client.py:853, in SyncAPIClient.request(self, cast_to, options, remaining_retries, stream, stream_cls)
    844 def request(
    845     self,
    846     cast_to: Type[ResponseT],
   (...)
    851     stream_cls: type[_StreamT] | None = None,
    852 ) -> ResponseT | _StreamT:
--> 853     return self._request(
    854         cast_to=cast_to,
    855         options=options,
    856         stream=stream,
    857         stream_cls=stream_cls,
    858         remaining_retries=remaining_retries,
    859     )

File /usr/local/lib/python3.11/site-packages/openai/_base_client.py:930, in SyncAPIClient._request(self, cast_to, options, remaining_retries, stream, stream_cls)
    927     if not err.response.is_closed:
    928         err.response.read()
--> 930     raise self._make_status_error_from_response(err.response) from None
    932 return self._process_response(
    933     cast_to=cast_to,
    934     options=options,
   (...)
    937     stream_cls=stream_cls,
    938 )

BadRequestError: Error code: 400 - {'error': {'message': "None is not of type 'object' - 'messages.2.function_call'", 'type': 'invalid_request_error', 'param': None, 'code': None}}

I am not sure if I used autogen incorrectly or this feature is not yet supported, and need some help on my code.

A few questions during learning autogen: In multi-agent setup, I saw some examples using self.register_reply like https://github.com/microsoft/autogen/blob/f0d5808fefe3f7a248ed0123e4a30da51534db90/notebook/agentchat_dalle_and_gpt4v.ipynb while some does not like https://github.com/microsoft/autogen/blob/main/notebook/agentchat_groupchat_vis.ipynb ? how do each agent interact with other other? for example, in 3 agents A/B/C env, when agent A output X, does llm choose the next agent to respond or I need to implement the logic myself? what if I want the coder to debug if the generated code does not work? how should I do that?

Appreciate any hints/code/guidance!

Steps to reproduce

use notebook to replicate the error.

Screenshots and logs

No response

Additional Information

No response

tomorrmato avatar Mar 02 '24 00:03 tomorrmato

@BeibinLi @skzhang1 fyi

sonichi avatar Mar 02 '24 06:03 sonichi

@tomorrmato, thank you for the detailed bug report!

As you already mentioned, for instance at the end of the LMM Notebook, my implementation with three agents (Commander, Critics, and Coder) to perform the same plotting task uses a predefined dataflow rather than a non-deterministic GroupChat.

Therefore, using "self.register_reply" is preferred when you can simply define a deterministic data flow, because this can save API calls and provide better data control; otherwise, if it is too hard to manually define how the agents should interact with each other, use GroupChat and let the manager (an LLM) decide which agent to call next.

I understand that you wanted to integrate these agents into GroupChat, and I will test your implementation and get back to you soon.

BeibinLi avatar Mar 02 '24 07:03 BeibinLi

@tomorrmato Please see the PR #1926

BeibinLi avatar Mar 13 '24 21:03 BeibinLi

@BeibinLi does the following code in the PR notebook fix my issue?

vision_capability = VisionCapability(lmm_config={"config_list": config_list_4v, "temperature": 0.5, "max_tokens": 300})
group_chat_manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=gpt4_llm_config)
vision_capability.add_to_agent(group_chat_manager)

If so, I can update my autogen version and give it a try.

tomorrmato avatar Mar 14 '24 05:03 tomorrmato

@tomorrmato Yes, it should resolve your issue. The PR is not merged into the main branch yet, but you can checkout to the working branch to test your code. Or, you can wait the PR to be merged (hopefully soon).

BeibinLi avatar Mar 14 '24 16:03 BeibinLi

Hey @BeibinLi, thank you very much for your PR, I just gave it a try and had some errors, not sure how to fix the errors.

here is my latest script based on your PR branch

import autogen
import jupyter_client
import base64
from base64 import b64decode

from autogen.agentchat.contrib.multimodal_conversable_agent import MultimodalConversableAgent
from autogen.agentchat.contrib.capabilities.vision_capability import VisionCapability
from autogen import Agent, AssistantAgent
from typing import List, Dict, Union, Optional

config_list = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": [
                "gpt-4-vision-preview", 
                "gpt-4-1106-preview"
        ],
    },
)

config_list_4v = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": ["gpt-4-vision-preview", #"gpt-4-1106-preview"
                 ],
    },
)

config_list_gpt4 = autogen.config_list_from_json(
    "env.json",
    filter_dict={
        "model": ["gpt-4-vision-preview", "gpt-4-1106-preview"],
    },
)

gpt4_llm_config = {"config_list": config_list_gpt4, "cache_seed": 42}


criteria_prompt = """Evaluate the image against the provided criteria. For each criterion, assign a score on a scale from 1 to 5, where 1 indicates the worst performance and 5 indicates the best.
Criteria:
Q0. How helpful is the image?
Q1. How easy is it to follow the image?

use the following format in your output:
Q 1 | Q 2 score | your reason 1
Q 2 | Q 2 score | your reason 2
...
"""

# sample python code for testing, use this to save image 
py_code = """
import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \\cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()
"""


# jupyter kernel to execute python code and output a tuple of image bytes and error message 
def call_python(code: str):
    # Create a new kernel manager
    km = jupyter_client.KernelManager()
    km.start_kernel()

    # Create a client for the kernel
    kc = km.client()
    kc.start_channels()

    image_data, error_message = None, None 
    try:
        # Send code to be executed
        kc.execute(code)

        # Wait for and process the execution result
        while True:
            try:
                msg = kc.get_iopub_msg(timeout=90)
                if msg['header']['msg_type'] in ['execute_result', 'display_data']:
                    data = msg['content']['data']

                    # change the type 
                    if 'image/png' in data:
                        # Decode the image data
                        image_data = b64decode(data['image/png'])
                        break

                if msg['header']['msg_type'] == 'error':
                    error_content = msg['content']
                    error_message = f"Error: {error_content['ename']}: {error_content['evalue']}\n"
                    error_message += "\n".join(error_content['traceback'])
                    break

                if msg['header']['msg_type'] == 'status':
                    if msg['content']['execution_state'] == 'idle':
                        break
            except Exception as e:
                error_message = f"Exception while waiting for execute result: {e}"
                break
    finally:
        # Stop the channels and kernel
        kc.stop_channels()
        km.shutdown_kernel()

    if error_message:
        return None, error_message

    return image_data, None

def code_to_image_bytes_or_error(code: str):
    image_data, error = call_python(code)
    if error:
        return error
    else:
        image_bytes = base64.b64encode(image_data).decode('utf-8')
        msg = f"""<img data:image/jpeg;base64,{image_bytes}>"""
        return msg
        

image_critic = MultimodalConversableAgent(
    name="image-explainer",
    system_message=criteria_prompt,
    max_consecutive_auto_reply=10,
    llm_config={"config_list": config_list_4v, "temperature": 0, "max_tokens": 4096},
)

user_proxy = autogen.UserProxyAgent(
    name="User_proxy",
    system_message="A human admin.",
    human_input_mode="TERMINATE",
    code_execution_config={
        "use_docker": False
    },  
    llm_config={"config_list": config_list_gpt4, #config_list_4v, 
                "temperature": 0, "max_tokens": 4096,
            #    "functions": [
            #         {
            #             "name": "code_to_image_bytes_or_error",
            #             "description": "python code executor",
            #             "parameters": {
            #                 "type": "object",
            #                 "properties": {
            #                     "code": {
            #                         "type": "string",
            #                         "description": "python code str",
            #                     }
            #                 },
            #                 "required": ["code"],
            #             },
            #         },
            #     ]        
               },
    # function_map={"code_to_image_bytes_or_error": code_to_image_bytes_or_error}
)

coder = MultimodalConversableAgent(
    name="Coder",
    system_message="your goal is to trigger the function call for code",
    llm_config={"config_list": config_list_gpt4, "temperature": 0, "max_tokens": 4096,
               "functions": [
                    {
                        "name": "code_to_image_bytes_or_error",
                        "description": "python code executor",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "code": {
                                    "type": "string",
                                    "description": "python code str",
                                }
                            },
                            "required": ["code"],
                        },
                    },
                ]           
        },
    function_map={"code_to_image_bytes_or_error": code_to_image_bytes_or_error}
)

groupchat = autogen.GroupChat(agents=[user_proxy, image_critic, coder], messages=[], max_round=12)

vision_capability = VisionCapability(lmm_config={"config_list": config_list_4v, "temperature": 0.5, "max_tokens": 300})
group_chat_manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=gpt4_llm_config)
vision_capability.add_to_agent(group_chat_manager)

user_proxy.initiate_chat(
    group_chat_manager, 
    message=f""" let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code with no function call in `user_proxy` agent,  
{py_code}
""" 
)

It does not trigger function call and User_proxy agent seems to get stuck

User_proxy (to chat_manager):

 let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code

import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

As an AI language model, I'm unable to execute code or provide images. However, I can guide you through the process of improving the code based on hypothetical feedback from critics. 

Let's assume the critics have provided the following feedback:

1. The plots are too small and difficult to read.
2. The colors of the plots are not distinct enough for people with color vision deficiency.
3. The legend overlaps with the plot in some cases.
4. The x-axis should include the value of pi for better understanding of the function behavior around that point.
5. The title of the fourth subplot is incorrect; it should be "Derivative of Outer Function $f'(g(x))$".

Based on this feedback, here are the improvements we can make:

1. Increase the size of the figure to make the plots larger and more readable.
2. Use a color palette that is friendly for color vision deficiency.
3. Move the legend to a location where it does not overlap with the plot.
4. Include the value of pi in the x-axis.
5. Correct the title of the fourth subplot.

Here's the updated code:

python
import matplotlib.pyplot as plt
import numpy as np

# Create a larger figure with high dpi
plt.figure(figsize=(12, 8), dpi=400)

# Define the x values
x = np.linspace(0, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Define a colorblind-friendly color palette
colors = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple']

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$', color=colors[0])
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend(loc='upper right')

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$', color=colors[1])
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend(loc='upper right')

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$", color=colors[2])
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend(loc='upper right')

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$", color=colors[3])
plt.title("Derivative of Outer Function $f'(g(x))$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend(loc='upper right')

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$", color=colors[4])
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend(loc='upper right')

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()


This updated code addresses the feedback provided by the critics. If you have the ability to execute the code and generate the image, you can then send the updated image to the critics for further feedback. Repeat the process until all feedback has been addressed satisfactorily.

--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

As an AI language model, I'm unable to execute code or provide images. However, I can guide you through the process of improving the code based on hypothetical feedback from critics. 

Let's assume the critics have provided the following feedback:

1. The plots are too small and difficult to read.
2. The colors of the plots are not distinct enough for people with color vision deficiency.
3. The legend overlaps with the plot in some cases.
4. The x-axis should include the value of pi for better understanding of the function behavior around that point.
5. The title of the fourth subplot is incorrect; it should be "Derivative of Outer Function $f'(g(x))$".

Based on this feedback, here are the improvements we can make:

1. Increase the size of the figure to make the plots larger and more readable.
2. Use a color palette that is friendly for color vision deficiency.
3. Move the legend to a location where it does not overlap with the plot.
4. Include the value of pi in the x-axis.
5. Correct the title of the fourth subplot.

Here's the updated code:

python
import matplotlib.pyplot as plt
import numpy as np

# Create a larger figure with high dpi
plt.figure(figsize=(12, 8), dpi=400)

# Define the x values
x = np.linspace(0, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Define a colorblind-friendly color palette
colors = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple']

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$', color=colors[0])
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend(loc='upper right')

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$', color=colors[1])
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend(loc='upper right')

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$", color=colors[2])
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend(loc='upper right')

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$", color=colors[3])
plt.title("Derivative of Outer Function $f'(g(x))$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend(loc='upper right')

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$", color=colors[4])
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend(loc='upper right')

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()


This updated code addresses the feedback provided by the critics. If you have the ability to execute the code and generate the image, you can then send the updated image to the critics for further feedback. Repeat the process until all feedback has been addressed satisfactorily.

--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

I apologize for the confusion, but as an AI developed by OpenAI, I don't have the capability to execute code or generate images. However, I can help you refine the code based on the hypothetical feedback you might receive from critics. If you have specific feedback or areas of improvement you'd like to address, please let me know, and I can assist you in updating the code accordingly.

--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

I apologize for the confusion, but as an AI developed by OpenAI, I don't have the capability to execute code or generate images. However, I can help you refine the code based on the hypothetical feedback you might receive from critics. If you have specific feedback or areas of improvement you'd like to address, please let me know, and I can assist you in updating the code accordingly.

...

I also tried to uncomment out the function call parameters in user_proxy agent, and it raised the following error

User_proxy (to chat_manager):

 let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code

import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):

***** Suggested function Call: code_to_image_bytes_or_error *****
Arguments: 
{"code":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# Create a figure with high dpi\nplt.figure(dpi=400)\n\n# Define the x values\nx = np.linspace(0.1, 2*np.pi, 400)\nx = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)\n\n# Calculate the functions\nu = 1/np.tan(x)  # g(x) = cot(x)\ny = np.sin(u)  # f(g(x)) = sin(cot(x))\ng_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)\nf_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))\ndy_dx = f_prime_of_g * g_prime  # The derivative\n\n# Plotting\nplt.subplot(2, 3, 1)\nplt.plot(x, u, label='$g(x) = cot(x)$')\nplt.title('Inner Function $g(x)$')\nplt.xlabel('$x$')\nplt.ylabel('$g(x)$')\nplt.legend()\n\nplt.subplot(2, 3, 2)\nplt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')\nplt.title('Composite Function $f(g(x))$')\nplt.xlabel('$x$')\nplt.ylabel('$f(g(x))$')\nplt.legend()\n\nplt.subplot(2, 3, 3)\nplt.plot(x, g_prime, label=\"$g'(x) = -csc^2(x)$\")\nplt.title(\"Derivative of Inner Function $g'(x)$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$g'(x)$\")\nplt.legend()\n\nplt.subplot(2, 3, 4)\nplt.plot(x, f_prime_of_g, label=\"$f'(g(x)) = cos(cot(x))$\")\nplt.title(\"Derivative of Outer Function $f'(u)$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$f'(g(x))$\")\nplt.legend()\n\nplt.subplot(2, 3, 5)\nplt.plot(x, dy_dx, label=\"$dy/dx = -cos(cot(x)) \\cdot csc^2(x)$\")\nplt.title(\"Derivative of Composite Function $dy/dx$\")\nplt.xlabel('$x$')\nplt.ylabel(\"$dy/dx$\")\nplt.legend()\n\n# Adjust layout to prevent overlap\nplt.tight_layout()\n\n# Show the plot\nplt.show()\n"}
*****************************************************************

--------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/app/temp/autogen/notebook/test_multimodal_ex.py", line 241, in <module>
    user_proxy.initiate_chat(
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 977, in initiate_chat
    self.send(msg2send, recipient, silent=silent)
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 624, in send
    recipient.receive(message, self, request_reply, silent)
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 783, in receive
    reply = self.generate_reply(messages=self.chat_messages[sender], sender=sender)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 1866, in generate_reply
    final, reply = reply_func(self, messages=messages, sender=sender, config=reply_func_tuple["config"])
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/groupchat.py", line 614, in run_chat
    speaker = groupchat.select_speaker(speaker, self)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/groupchat.py", line 430, in select_speaker
    final, name = selector.generate_oai_reply(messages)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 1265, in generate_oai_reply
    extracted_response = self._generate_oai_reply_from_client(
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/agentchat/conversable_agent.py", line 1284, in _generate_oai_reply_from_client
    response = llm_client.create(
               ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/oai/client.py", line 625, in create
    response = client.create(params)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/autogen/oai/client.py", line 278, in create
    response = completions.create(**params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/_utils/_utils.py", line 270, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/resources/chat/completions.py", line 645, in create
    return self._post(
           ^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/_base_client.py", line 1088, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/_base_client.py", line 853, in request
    return self._request(
           ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/_base_client.py", line 930, in _request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "None is not of type 'object' - 'messages.2.function_call'", 'type': 'invalid_request_error', 'param': None, 'code': None}}

based on this post, it seems to be that function call array is not supported, do you know how to fix this issue? Once again, thank you very much for your help!

tomorrmato avatar Mar 15 '24 21:03 tomorrmato

@tomorrmato

  1. You should NOT provide a "llm_config" into user_proxy; otherwise, it will not run code for you.
  2. You need to wrap your code in markdown if you want the UserProxyAgent to run code.

So, the last line of your code should be


user_proxy.initiate_chat(
    group_chat_manager, 
    message=f""" let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code with no function call in `user_proxy` agent,  

```python
{py_code}

""" )

BeibinLi avatar Mar 15 '24 23:03 BeibinLi

Because you have a manually-defined workflow, you don't need a groupchat manager.

You can either use sequential chat or the FigureCreator directly from here.

I can create an example later with plotting improvements.

BeibinLi avatar Mar 15 '24 23:03 BeibinLi

@tomorrmato

  1. You should NOT provide a "llm_config" into user_proxy; otherwise, it will not run code for you.
  2. You need to wrap your code in markdown if you want the UserProxyAgent to run code.

So, the last line of your code should be

user_proxy.initiate_chat(
    group_chat_manager, 
    message=f""" let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code with no function call in `user_proxy` agent,  

```python
{py_code}

""" )

This is not True: "You should NOT provide a "llm_config" into user_proxy; otherwise, it will not run code for you." code execution based reply proceeds llm-based reply.

sonichi avatar Mar 16 '24 20:03 sonichi

@sonichi @BeibinLi I tried to

  1. comment out llm_config in user_proxy and
  2. update markdown format by using the following
```python
{py_code}
```

here is the terminal output 
```
User_proxy (to chat_manager):

 let's improve the code by using the following flow: 
step 1. trigger the function call for given code (I will execute the code and provide image to you)
step 2. send the image to critics for feedback 
step 3. use the critics feedback to update the code 
continue step 1-3 until all feedback has been addressed.

let's start with the the initial python code
```python

import matplotlib.pyplot as plt
import numpy as np

# Create a figure with high dpi
plt.figure(dpi=400)

# Define the x values
x = np.linspace(0.1, 2*np.pi, 400)
x = x[x != np.pi]  # Remove pi to avoid division by zero in cot(x) and csc(x)

# Calculate the functions
u = 1/np.tan(x)  # g(x) = cot(x)
y = np.sin(u)  # f(g(x)) = sin(cot(x))
g_prime = -1/(np.sin(x)**2)  # g'(x) = -csc^2(x)
f_prime_of_g = np.cos(u)  # f'(g(x)) = cos(cot(x))
dy_dx = f_prime_of_g * g_prime  # The derivative

# Plotting
plt.subplot(2, 3, 1)
plt.plot(x, u, label='$g(x) = cot(x)$')
plt.title('Inner Function $g(x)$')
plt.xlabel('$x$')
plt.ylabel('$g(x)$')
plt.legend()

plt.subplot(2, 3, 2)
plt.plot(x, y, label='$f(g(x)) = sin(cot(x))$')
plt.title('Composite Function $f(g(x))$')
plt.xlabel('$x$')
plt.ylabel('$f(g(x))$')
plt.legend()

plt.subplot(2, 3, 3)
plt.plot(x, g_prime, label="$g'(x) = -csc^2(x)$")
plt.title("Derivative of Inner Function $g'(x)$")
plt.xlabel('$x$')
plt.ylabel("$g'(x)$")
plt.legend()

plt.subplot(2, 3, 4)
plt.plot(x, f_prime_of_g, label="$f'(g(x)) = cos(cot(x))$")
plt.title("Derivative of Outer Function $f'(u)$")
plt.xlabel('$x$')
plt.ylabel("$f'(g(x))$")
plt.legend()

plt.subplot(2, 3, 5)
plt.plot(x, dy_dx, label="$dy/dx = -cos(cot(x)) \cdot csc^2(x)$")
plt.title("Derivative of Composite Function $dy/dx$")
plt.xlabel('$x$')
plt.ylabel("$dy/dx$")
plt.legend()

# Adjust layout to prevent overlap
plt.tight_layout()

# Show the plot
plt.show()

```


--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):



--------------------------------------------------------------------------------

>>>>>>>> USING AUTO REPLY...
User_proxy (to chat_manager):
```
it does not seem to do anything. I also tried to comment out `function_map` of `user_proxy`, the terminal output is similar, I am not sure if there is anything I may miss, please let me know, thank you! 

numtqi avatar Mar 18 '24 16:03 numtqi