From f221e31db11e46dc57e63eea4d6709a084f002c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Sat, 7 Oct 2023 16:22:44 +0800 Subject: [PATCH 1/7] fix: litellm Function calling error. --- metagpt/tools/code_interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/tools/code_interpreter.py b/metagpt/tools/code_interpreter.py index 97398ccfd..231904c1a 100644 --- a/metagpt/tools/code_interpreter.py +++ b/metagpt/tools/code_interpreter.py @@ -41,7 +41,7 @@ class OpenCodeInterpreter(object): interpreter.auto_run = auto_run interpreter.model = CONFIG.openai_api_model or "gpt-3.5-turbo" interpreter.api_key = CONFIG.openai_api_key - interpreter.api_base = CONFIG.openai_api_base + # interpreter.api_base = CONFIG.openai_api_base self.interpreter = interpreter def chat(self, query: str, reset: bool = True): From e6e2aa48952fb388fc4b7c4927bfe430e95d7038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Sat, 7 Oct 2023 16:23:13 +0800 Subject: [PATCH 2/7] add wrapt. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index de861ded9..f49bce24e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,4 +42,4 @@ pytest-mock==3.11.1 open-interpreter==0.1.4; python_version>"3.9" ta==0.10.2 semantic-kernel==0.3.10.dev0 - +wrapt==1.15.0 From 524be9d53e8721285370653777f7515d79803be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Sun, 8 Oct 2023 19:04:12 +0800 Subject: [PATCH 3/7] feat: upgrade open-interpreter to 0.1.7 --- metagpt/tools/code_interpreter.py | 68 ++++++++++++++++++++++++++----- requirements.txt | 8 ++-- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/metagpt/tools/code_interpreter.py b/metagpt/tools/code_interpreter.py index 231904c1a..138341f1a 100644 --- a/metagpt/tools/code_interpreter.py +++ b/metagpt/tools/code_interpreter.py @@ -1,11 +1,11 @@ import re -from typing import List, Callable +from typing import List, Callable, Dict from pathlib import Path import wrapt import textwrap import inspect -from interpreter.interpreter import Interpreter +from interpreter.core.core import Interpreter from metagpt.logs import logger from metagpt.config import CONFIG @@ -47,7 +47,8 @@ class OpenCodeInterpreter(object): def chat(self, query: str, reset: bool = True): if reset: self.interpreter.reset() - return self.interpreter.chat(query, return_messages=True) + # return self.interpreter.chat(query, return_messages=True) + return self.interpreter.chat(query) @staticmethod def extract_function(query_respond: List, function_name: str, *, language: str = 'python', @@ -61,11 +62,19 @@ class OpenCodeInterpreter(object): assert language == 'python', f"Expect python language for default function_format, but got {language}." function_format = """def {function_name}():\n{code}""" # Extract the code module in the open-interpreter respond message. - code = [item['function_call']['parsed_arguments']['code'] for item in query_respond - if "function_call" in item - and "parsed_arguments" in item["function_call"] - and 'language' in item["function_call"]['parsed_arguments'] - and item["function_call"]['parsed_arguments']['language'] == language] + if "function_call" in query_respond[1]: + code = [item['function_call']['parsed_arguments']['code'] for item in query_respond + if "function_call" in item + and "parsed_arguments" in item["function_call"] + and 'language' in item["function_call"]['parsed_arguments'] + and item["function_call"]['parsed_arguments']['language'] == language] + elif "code" in query_respond[1]: + code = [item['code'] for item in query_respond + if "code" in item + and 'language' in item + and item['language'] == language] + else: + raise ValueError(f"Unexpect message format in query_respond: {query_respond[1].keys()}") # add indent. indented_code_str = textwrap.indent("\n".join(code), ' ' * 4) # Return the code after deduplication. @@ -94,13 +103,49 @@ class OpenInterpreterDecorator(object): self.code_file_path = code_file_path self.clear_code = clear_code + def _have_code(self, rsp: List[Dict]): + # Is there any code generated? + return 'code' in rsp[1] and rsp[1]['code'] not in ("", None) + + def _is_faild_plan(self, rsp: List[Dict]): + # is faild plan? + func_code = OpenCodeInterpreter.extract_function(rsp, 'function') + # If there is no more than 1 '\n', the plan execution fails. + if isinstance(func_code, str) and func_code.count('\n') <= 1: + return True + return False + + def _check_respond(self, query: str, interpreter: OpenCodeInterpreter, respond: List[Dict], max_try: int = 3): + for _ in range(max_try): + # TODO: If no code or faild plan is generated, execute chat again, repeating no more than max_try times. + if self._have_code(respond) and not self._is_faild_plan(respond): + break + elif not self._have_code(respond): + logger.warning(f"llm did not return executable code, resend the query: \n{query}") + respond = interpreter.chat(query) + elif self._is_faild_plan(respond): + logger.warning(f"llm did not generate successful plan, resend the query: \n{query}") + respond = interpreter.chat(query) + + # Post-processing of respond + if not self._have_code(respond): + error_msg = f"OpenCodeInterpreter do not generate code for query: \n{query}" + logger.error(error_msg) + raise ValueError(error_msg) + + if self._is_faild_plan(respond): + error_msg = f"OpenCodeInterpreter do not generate code for query: \n{query}" + logger.error(error_msg) + raise ValueError(error_msg) + return respond + def __call__(self, wrapped): @wrapt.decorator async def wrapper(wrapped: Callable, instance, args, kwargs): # Get the decorated function name. func_name = wrapped.__name__ # If the script exists locally and clearcode is not required, execute the function from the script. - if Path(self.code_file_path).is_file() and not self.clear_code: + if self.code_file_path and Path(self.code_file_path).is_file() and not self.clear_code: return run_function_script(self.code_file_path, func_name, *args, **kwargs) # Auto run generate code by using open-interpreter. @@ -108,6 +153,8 @@ class OpenInterpreterDecorator(object): query = gen_query(wrapped, args, kwargs) logger.info(f"query for OpenCodeInterpreter: \n {query}") respond = interpreter.chat(query) + # Make sure the response is as expected. + respond = self._check_respond(query, interpreter, respond, 3) # Assemble the code blocks generated by open-interpreter into a function without parameters. func_code = interpreter.extract_function(respond, func_name) # Clone the `func_code` into wrapped, that is, @@ -121,9 +168,10 @@ class OpenInterpreterDecorator(object): # execute this function. try: res = run_function_code(code, func_name, *args, **kwargs) - if self.save_code: + if self.save_code and self.code_file_path: cf._save(self.code_file_path, code) except Exception as e: + logger.error(f"Could not evaluate Python code \n{logger_code}: \nError: {e}") raise Exception("Could not evaluate Python code", e) return res return wrapper(wrapped) diff --git a/requirements.txt b/requirements.txt index f49bce24e..8ad024f15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ langchain==0.0.231 loguru==0.6.0 meilisearch==0.21.0 numpy==1.24.3 -openai==0.27.8 +openai openpyxl beautifulsoup4==4.12.2 pandas==2.0.3 @@ -23,7 +23,7 @@ pydantic==1.10.8 #pymilvus==2.2.8 pytest==7.2.2 python_docx==0.8.11 -PyYAML==6.0 +PyYAML # sentence_transformers==2.2.2 setuptools==65.6.3 tenacity==8.2.2 @@ -39,7 +39,7 @@ typing_extensions==4.5.0 libcst==1.0.1 qdrant-client==1.4.0 pytest-mock==3.11.1 -open-interpreter==0.1.4; python_version>"3.9" +open-interpreter==0.1.7; python_version>"3.9" ta==0.10.2 -semantic-kernel==0.3.10.dev0 +semantic-kernel wrapt==1.15.0 From 6bfd0b89b6eadb14094bece114c45daecf12fd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Mon, 9 Oct 2023 11:19:18 +0800 Subject: [PATCH 4/7] chore: remove unused code and add example of the open-interpreter query respond. --- metagpt/tools/code_interpreter.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/metagpt/tools/code_interpreter.py b/metagpt/tools/code_interpreter.py index 138341f1a..8429db10f 100644 --- a/metagpt/tools/code_interpreter.py +++ b/metagpt/tools/code_interpreter.py @@ -47,7 +47,6 @@ class OpenCodeInterpreter(object): def chat(self, query: str, reset: bool = True): if reset: self.interpreter.reset() - # return self.interpreter.chat(query, return_messages=True) return self.interpreter.chat(query) @staticmethod @@ -62,12 +61,23 @@ class OpenCodeInterpreter(object): assert language == 'python', f"Expect python language for default function_format, but got {language}." function_format = """def {function_name}():\n{code}""" # Extract the code module in the open-interpreter respond message. + # The respond of open-interpreter before v0.1.4 is: + # [{'role': 'user', 'content': your query string}, + # {'role': 'assistant', 'content': plan from llm, 'function_call': { + # "name": "run_code", "arguments": "{"language": "python", "code": code of first plan}, + # "parsed_arguments": {"language": "python", "code": code of first plan} + # ...] if "function_call" in query_respond[1]: code = [item['function_call']['parsed_arguments']['code'] for item in query_respond if "function_call" in item and "parsed_arguments" in item["function_call"] and 'language' in item["function_call"]['parsed_arguments'] and item["function_call"]['parsed_arguments']['language'] == language] + # The respond of open-interpreter v0.1.7 is: + # [{'role': 'user', 'message': your query string}, + # {'role': 'assistant', 'message': plan from llm, 'language': 'python', + # 'code': code of first plan, 'output': output of first plan code}, + # ...] elif "code" in query_respond[1]: code = [item['code'] for item in query_respond if "code" in item From 1e645ddc6a3651b87672d9a96660c900926f799f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Mon, 9 Oct 2023 11:23:56 +0800 Subject: [PATCH 5/7] chore: change comment respond to query_respond --- metagpt/tools/code_interpreter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/tools/code_interpreter.py b/metagpt/tools/code_interpreter.py index 8429db10f..e41eaab72 100644 --- a/metagpt/tools/code_interpreter.py +++ b/metagpt/tools/code_interpreter.py @@ -61,7 +61,7 @@ class OpenCodeInterpreter(object): assert language == 'python', f"Expect python language for default function_format, but got {language}." function_format = """def {function_name}():\n{code}""" # Extract the code module in the open-interpreter respond message. - # The respond of open-interpreter before v0.1.4 is: + # The query_respond of open-interpreter before v0.1.4 is: # [{'role': 'user', 'content': your query string}, # {'role': 'assistant', 'content': plan from llm, 'function_call': { # "name": "run_code", "arguments": "{"language": "python", "code": code of first plan}, @@ -73,7 +73,7 @@ class OpenCodeInterpreter(object): and "parsed_arguments" in item["function_call"] and 'language' in item["function_call"]['parsed_arguments'] and item["function_call"]['parsed_arguments']['language'] == language] - # The respond of open-interpreter v0.1.7 is: + # The query_respond of open-interpreter v0.1.7 is: # [{'role': 'user', 'message': your query string}, # {'role': 'assistant', 'message': plan from llm, 'language': 'python', # 'code': code of first plan, 'output': output of first plan code}, From c90be914f27c970463790026cc112a4148de6ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Mon, 23 Oct 2023 10:13:19 +0800 Subject: [PATCH 6/7] chore: keep the pinged version of PyYAML. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8ad024f15..67cbee01a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ pydantic==1.10.8 #pymilvus==2.2.8 pytest==7.2.2 python_docx==0.8.11 -PyYAML +PyYAML==6.0.1 # sentence_transformers==2.2.2 setuptools==65.6.3 tenacity==8.2.2 From 5ab0bdf3b64324a19d8ebc43943e74cb8aafccf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=A3=92=E6=A3=92?= Date: Mon, 23 Oct 2023 17:09:17 +0800 Subject: [PATCH 7/7] chore --- requirements.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6155badb3..093298775 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,10 +41,6 @@ qdrant-client==1.4.0 pytest-mock==3.11.1 open-interpreter==0.1.7; python_version>"3.9" ta==0.10.2 -semantic-kernel +semantic-kernel==0.3.13.dev0 wrapt==1.15.0 websocket-client==0.58.0 -aiofiles~=23.2.1 -pygments~=2.16.1 -requests~=2.31.0 -yaml~=0.2.5