mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-15 11:02:36 +02:00
Merge branch 'del-_parse_arguments-in-openai_api.py' into code_intepreter
This commit is contained in:
commit
ca6749b5f1
3 changed files with 33 additions and 63 deletions
|
|
@ -8,7 +8,6 @@
|
|||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import AsyncIterator, Optional, Union
|
||||
|
||||
from openai import APIConnectionError, AsyncOpenAI, AsyncStream
|
||||
|
|
@ -195,31 +194,7 @@ class OpenAILLM(BaseLLM):
|
|||
rsp = await self._achat_completion_function(messages, **kwargs)
|
||||
return self.get_choice_function_arguments(rsp)
|
||||
|
||||
def _parse_arguments(self, arguments: str) -> dict:
|
||||
"""parse arguments in openai function call"""
|
||||
if "langugae" not in arguments and "code" not in arguments:
|
||||
logger.warning(f"Not found `code`, `language`, We assume it is pure code:\n {arguments}\n. ")
|
||||
return {"language": "python", "code": arguments}
|
||||
|
||||
# 匹配language
|
||||
language_pattern = re.compile(r'[\"\']?language[\"\']?\s*:\s*["\']([^"\']+?)["\']', re.DOTALL)
|
||||
language_match = language_pattern.search(arguments)
|
||||
language_value = language_match.group(1) if language_match else "python"
|
||||
|
||||
# 匹配code
|
||||
code_pattern = r'(["\'`]{3}|["\'`])([\s\S]*?)\1'
|
||||
try:
|
||||
code_value = re.findall(code_pattern, arguments)[-1][-1]
|
||||
except Exception as e:
|
||||
logger.error(f"{e}, when re.findall({code_pattern}, {arguments})")
|
||||
code_value = None
|
||||
|
||||
if code_value is None:
|
||||
raise ValueError(f"Parse code error for {arguments}")
|
||||
# arguments只有code的情况
|
||||
return {"language": language_value, "code": code_value}
|
||||
|
||||
@handle_exception
|
||||
# @handle_exception
|
||||
def get_choice_function_arguments(self, rsp: ChatCompletion) -> dict:
|
||||
"""Required to provide the first function arguments of choice.
|
||||
|
||||
|
|
@ -237,19 +212,15 @@ class OpenAILLM(BaseLLM):
|
|||
try:
|
||||
return json.loads(message.tool_calls[0].function.arguments, strict=False)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logger.warning(
|
||||
"\n".join(
|
||||
[
|
||||
(f"Got JSONDecodeError for \n{'--'*40} \n{message.tool_calls[0].function.arguments}"),
|
||||
(f"{'--'*40}\nwe will use RegExp to parse code. JSONDecodeError is: {e}"),
|
||||
]
|
||||
)
|
||||
error_msg = (
|
||||
f"Got JSONDecodeError for \n{'--'*40} \n{message.tool_calls[0].function.arguments}, {str(e)}"
|
||||
)
|
||||
return self._parse_arguments(message.tool_calls[0].function.arguments)
|
||||
logger.error(error_msg)
|
||||
raise json.decoder.JSONDecodeError(error_msg, e.doc, e.pos)
|
||||
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 = ("```", '"""', "'''")
|
||||
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}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
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
|
||||
|
||||
|
|
@ -273,19 +272,16 @@ class CodeParser:
|
|||
def parse_code(cls, block: str, text: str, lang: str = "") -> str:
|
||||
if block:
|
||||
text = cls.parse_block(block, text)
|
||||
start_ends = ["```", "'''", '"""']
|
||||
patterns = []
|
||||
for start_end in start_ends:
|
||||
pattern = rf"{start_end}{lang}.*?\s+(.*?){start_end}"
|
||||
match = re.search(pattern, text, re.DOTALL)
|
||||
if match:
|
||||
code = match.group(1)
|
||||
return code
|
||||
patterns.append(pattern)
|
||||
logger.error(f"{patterns} not match following text:")
|
||||
logger.error(text)
|
||||
# raise Exception
|
||||
return text # just assume original text is code
|
||||
pattern = rf"```{lang}.*?\s+(.*?)```"
|
||||
match = re.search(pattern, text, re.DOTALL)
|
||||
if match:
|
||||
code = match.group(1)
|
||||
else:
|
||||
logger.error(f"{pattern} not match following text:")
|
||||
logger.error(text)
|
||||
# raise Exception
|
||||
return text # just assume original text is code
|
||||
return code
|
||||
|
||||
@classmethod
|
||||
def parse_str(cls, block: str, text: str, lang: str = ""):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import json
|
||||
|
||||
import pytest
|
||||
from openai.types.chat import (
|
||||
ChatCompletion,
|
||||
|
|
@ -41,16 +43,6 @@ async def test_speech_to_text():
|
|||
def tool_calls_rsp():
|
||||
function_rsps = [
|
||||
Function(arguments='{\n"language": "python",\n"code": "print(\'hello world\')"}', name="execute"),
|
||||
Function(arguments='{\n"language": "python",\n"code": \'print("hello world")\'}', name="execute"),
|
||||
Function(arguments='{\n"language": \'python\',\n"code": "print(\'hello world\')"}', name="execute"),
|
||||
Function(arguments='{\n"language": "python",\n"code": "print(\'hello world\')"}', name="execute"),
|
||||
Function(arguments='{\n"language": "python",\n"code": ```print("hello world")```}', name="execute"),
|
||||
Function(arguments='{\n"language": "python",\n"code": """print("hello world")"""}', name="execute"),
|
||||
Function(arguments='\nprint("hello world")\\n', name="execute"),
|
||||
# only `{` in arguments
|
||||
Function(arguments='{\n"language": "python",\n"code": "print(\'hello world\')"', name="execute"),
|
||||
# no `{`, `}` in arguments
|
||||
Function(arguments='\n"language": "python",\n"code": "print(\'hello world\')"', name="execute"),
|
||||
]
|
||||
tool_calls = [
|
||||
ChatCompletionMessageToolCall(type="function", id=f"call_{i}", function=f) for i, f in enumerate(function_rsps)
|
||||
|
|
@ -64,10 +56,6 @@ def tool_calls_rsp():
|
|||
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 = [
|
||||
|
|
@ -79,6 +67,15 @@ def tool_calls_rsp():
|
|||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def json_decode_error():
|
||||
function_rsp = Function(arguments='{\n"language": \'python\',\n"code": "print(\'hello world\')"}', name="execute")
|
||||
tool_calls = [ChatCompletionMessageToolCall(type="function", id=f"call_{0}", function=function_rsp)]
|
||||
message = ChatCompletionMessage(content=None, role="assistant", tool_calls=tool_calls)
|
||||
choices = [Choice(finish_reason="tool_calls", logprobs=None, index=0, message=message)]
|
||||
return ChatCompletion(id="0", choices=choices, created=0, model="gpt-4", object="chat.completion")
|
||||
|
||||
|
||||
class TestOpenAI:
|
||||
def test_make_client_kwargs_without_proxy(self):
|
||||
instance = OpenAILLM(mock_llm_config)
|
||||
|
|
@ -107,6 +104,12 @@ class TestOpenAI:
|
|||
else:
|
||||
code["language"] == "python"
|
||||
|
||||
def test_aask_code_json_decode_error(self, json_decode_error):
|
||||
instance = OpenAILLM(mock_llm_config)
|
||||
with pytest.raises(json.decoder.JSONDecodeError) as e:
|
||||
instance.get_choice_function_arguments(json_decode_error)
|
||||
assert "JSONDecodeError" in str(e)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_gen_image():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue