diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index f563c3804..3f46b1e31 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -80,6 +80,7 @@ class Action(ABC): pattern = r"\[CONTENT\](\s*\{.*?\}\s*)\[/CONTENT\]" matches = re.findall(pattern, content, re.DOTALL) + extracted_content = None for match in matches: if match: extracted_content = match diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index 7b531b4d3..526915b30 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -5,7 +5,7 @@ @Author : alexanderwu @File : project_management.py """ -from typing import List, Tuple +from typing import List from metagpt.actions.action import Action from metagpt.const import WORKSPACE_ROOT @@ -35,65 +35,42 @@ Attention: Use '##' to split sections, not '#', and '## ' SHOULD W ## Anything UNCLEAR: Provide as Plain text. Make clear here. For example, don't forget a main entry. don't forget to init 3rd party libs. +output a properly formatted JSON, wrapped inside [CONTENT][/CONTENT] like format example, +and only output the json inside this tag, nothing else """ FORMAT_EXAMPLE = ''' ---- -## Required Python third-party packages -```python -""" -flask==1.1.2 -bcrypt==3.2.0 -""" -``` - -## Required Other language third-party packages -```python -""" -No third-party ... -""" -``` - -## Full API spec -```python -""" -openapi: 3.0.0 -... -description: A JSON object ... -""" -``` - -## Logic Analysis -```python -[ - ("game.py", "Contains ..."), -] -``` - -## Task list -```python -[ - "game.py", -] -``` - -## Shared Knowledge -```python -""" -'game.py' contains ... -""" -``` - -## Anything UNCLEAR -We need ... how to start. ---- +{ + "Required Python third-party packages": [ + "flask==1.1.2", + "bcrypt==3.2.0" + ], + "Required Other language third-party packages": [ + "No third-party ..." + ], + "Full API spec": """ + openapi: 3.0.0 + ... + description: A JSON object ... + """, + "Logic Analysis": [ + ["game.py","Contains..."] + ], + "Task list": [ + "game.py" + ], + "Shared Knowledge": """ + 'game.py' contains ... + """, + "Anything UNCLEAR": "We need ... how to start." +} ''' OUTPUT_MAPPING = { - "Required Python third-party packages": (str, ...), - "Required Other language third-party packages": (str, ...), + "Required Python third-party packages": (List[str], ...), + "Required Other language third-party packages": (List[str], ...), "Full API spec": (str, ...), - "Logic Analysis": (List[Tuple[str, str]], ...), + "Logic Analysis": (List[List[str]], ...), "Task list": (List[str], ...), "Shared Knowledge": (str, ...), "Anything UNCLEAR": (str, ...), @@ -113,13 +90,11 @@ class WriteTasks(Action): # Write requirements.txt requirements_path = WORKSPACE_ROOT / ws_name / "requirements.txt" - requirements_path.write_text( - rsp.instruct_content.dict().get("Required Python third-party packages").strip('"\n') - ) + requirements_path.write_text("\n".join(rsp.instruct_content.dict().get("Required Python third-party packages"))) async def run(self, context): prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE) - rsp = await self._aask_v1(prompt, "task", OUTPUT_MAPPING) + rsp = await self._aask_json_v1(prompt, "task", OUTPUT_MAPPING) self._save(context, rsp) return rsp diff --git a/tests/metagpt/utils/test_custom_decoder.py b/tests/metagpt/utils/test_custom_decoder.py index c7b14ad59..072d96152 100644 --- a/tests/metagpt/utils/test_custom_decoder.py +++ b/tests/metagpt/utils/test_custom_decoder.py @@ -70,3 +70,99 @@ def test_parse_triple_single_quote(): parsed_data = decoder.decode(input_data) assert parsed_data["a"] == "b" + + +# def test_parse_complex(): +# # Create a custom JSON decoder +# decoder = CustomDecoder(strict=False) +# # Your provided input with single-quoted strings and line breaks +# input_data = '''{ +# "Required Python third-party packages": [ +# "flask==1.1.2", +# "bcrypt==3.2.0" +# ], +# "Required Other language third-party packages": [ +# "No third-party packages are required." +# ], +# "Full API spec": """ +# openapi: 3.0.0 +# +# description: A JSON object representing the game state. +# +# paths: +# /game: +# get: +# summary: Get the current game state. +# responses: +# '200': +# description: The current game state. +# +# /game/{direction}: +# post: +# summary: Move the snake in the specified direction. +# parameters: +# - name: direction +# in: path +# description: The direction to move the snake (one of UP, DOWN, LEFT, or RIGHT). +# responses: +# '200': +# description: The updated game state. +# +# /game/{food}: +# get: +# summary: Get the current food position. +# responses: +# '200': +# description: The current food position. +# +# /game/{snake}: +# get: +# summary: Get the current snake position. +# responses: +# '200': +# description: The current snake position. +# +# /game/{snake}/{direction}: +# post: +# summary: Set the snake direction. +# parameters: +# - name: direction +# in: path +# description: The direction to move the snake (one of UP, DOWN, LEFT, or RIGHT). +# responses: +# '200': +# description: The updated game state. +# +# /game/end: +# post: +# summary: End the game. +# responses: +# '200': +# description: The final game state. +# " +# ], +# "Logic Analysis": [ +# ["game.py","Contains the game logic and snake movement."], +# ["snake.py","Contains the snake class and methods."], +# ["game_objects.py","Contains the game objects and their properties."], +# ["game_logic.py","Contains the game logic and rules."], +# ["ui.py","Contains the user interface and display logic."] +# ], +# "Task list": [ +# "game.py", +# "snake.py", +# "game_objects.py", +# "game_logic.py", +# "ui.py" +# ], +# "Shared Knowledge": """ +# The game state is represented by a JSON object. +# The snake movement is based on the direction parameter. +# The game ends when the snake collides with the wall or eats the food. +# """, +# "Anything UNCLEAR": "The requirement is clear to me." +# } +# ''' +# # Parse the JSON using the custom decoder +# +# parsed_data = decoder.decode(input_data)