diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 3358b3aad..386c36c22 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -31,6 +31,7 @@ from metagpt.provider.base_llm import BaseLLM from metagpt.provider.constant import GENERAL_FUNCTION_SCHEMA from metagpt.provider.llm_provider_registry import register_provider from metagpt.schema import Message +from metagpt.utils.common import CodeParser from metagpt.utils.cost_manager import Costs from metagpt.utils.exceptions import handle_exception from metagpt.utils.token_counter import ( @@ -247,6 +248,12 @@ class OpenAILLM(BaseLLM): ) return self._parse_arguments(message.tool_calls[0].function.arguments) elif message.tool_calls is None and message.content is not None: + # reponse is code, fix openai tools_call respond bug, + # The response content is `code``, but it appears in the content instead of the arguments. + code_formats = ("```", '"""', "'''") + if message.content.startswith(code_formats) and message.content.endswith(code_formats): + code = CodeParser.parse_code(None, message.content) + return {"language": "python", "code": code} # reponse is message return {"language": "markdown", "code": self.get_choice_text(rsp)} else: diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index b20b4acd2..ed73cb061 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -8,6 +8,7 @@ Add generic class-to-string and object-to-string conversion functionality. @Modified By: mashenquan, 2023/11/27. Bug fix: `parse_recipient` failed to parse the recipient in certain GPT-3.5 responses. +@Modified By: liubangbang, 2024/01/23. Update: support [```, ''', \"\"\" ] codes in CodeParser.parse_code. """ from __future__ import annotations @@ -264,10 +265,10 @@ class CodeParser: return block_dict @classmethod - def parse_code(cls, block: str, text: str, lang: str = "") -> str: + def parse_code(cls, block: str, text: str, lang: str = "", start_ends: str = r'["\'`]{3}') -> str: if block: text = cls.parse_block(block, text) - pattern = rf"```{lang}.*?\s+(.*?)```" + pattern = rf"{start_ends}{lang}.*?\s+(.*?){start_ends}" match = re.search(pattern, text, re.DOTALL) if match: code = match.group(1) diff --git a/tests/metagpt/provider/test_openai.py b/tests/metagpt/provider/test_openai.py index 2e5799475..77820a5f8 100644 --- a/tests/metagpt/provider/test_openai.py +++ b/tests/metagpt/provider/test_openai.py @@ -87,6 +87,20 @@ class TestOpenAI: messages.append( ChatCompletionMessage(content="Completed a python code for hello world!", role="assistant", tool_calls=None) ) + # 添加 openai tool calls respond bug, code 出现在ChatCompletionMessage.content中 + messages.extend( + [ + ChatCompletionMessage(content="```python\nprint('hello world')```", role="assistant", tool_calls=None), + ChatCompletionMessage(content="'''python\nprint('hello world')'''", role="assistant", tool_calls=None), + ChatCompletionMessage( + content='"""python\nprint(\'hello world\')"""', role="assistant", tool_calls=None + ), + ChatCompletionMessage( + content="'''python\nprint(\"hello world\")'''", role="assistant", tool_calls=None + ), + ChatCompletionMessage(content="```python\nprint('hello world')```", role="assistant", tool_calls=None), + ] + ) choices = [ Choice(finish_reason="tool_calls", logprobs=None, index=i, message=msg) for i, msg in enumerate(messages) ] @@ -129,6 +143,7 @@ class TestOpenAI: assert "code" in code assert "language" in code assert "hello world" in code["code"] + logger.info(f'code is : {code["code"]}') if "Completed a python code for hello world!" == code["code"]: code["language"] == "markdown"