diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index 4692abfea..a2192c4dc 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -13,6 +13,7 @@ from metagpt.actions import Action, ActionOutput from metagpt.const import WORKSPACE_ROOT from metagpt.logs import logger from metagpt.utils.common import CodeParser +from metagpt.utils.json_to_markdown import json_to_markdown from metagpt.utils.mermaid import mermaid_to_file PROMPT_TEMPLATE = """ @@ -97,8 +98,9 @@ class WriteDesign(Action): quadrant_chart = context[-1].instruct_content.dict()["Competitive Quadrant Chart"] mermaid_to_file(quadrant_chart, resources_path / "competitive_analysis") - logger.info(f"Saving PRD to {prd_file}") - prd_file.write_text(context[-1].content) + if context[-1].instruct_content: + logger.info(f"Saving PRD to {prd_file}") + prd_file.write_text(json_to_markdown(context[-1].instruct_content.dict())) def _save_system_design(self, docs_path, resources_path, system_design): data_api_design = system_design.instruct_content.dict()[ @@ -111,7 +113,7 @@ class WriteDesign(Action): mermaid_to_file(seq_flow, resources_path / "seq_flow") system_design_file = docs_path / "system_design.md" logger.info(f"Saving System Designs to {system_design_file}") - system_design_file.write_text(system_design.content) + system_design_file.write_text((json_to_markdown(system_design.instruct_content.dict()))) def _save(self, context, system_design): if isinstance(system_design, ActionOutput): @@ -128,10 +130,7 @@ class WriteDesign(Action): self._save_system_design(docs_path, resources_path, system_design) async def run(self, context): - if isinstance(context, ActionOutput): - prompt = PROMPT_TEMPLATE.format(context=context.content, format_example=FORMAT_EXAMPLE) - else: - prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE) + prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE) # system_design = await self._aask(prompt) system_design = await self._aask_json_v1(prompt, "system_design", OUTPUT_MAPPING) self._save(context, system_design) diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index 526915b30..cc62425c0 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -9,6 +9,7 @@ from typing import List from metagpt.actions.action import Action from metagpt.const import WORKSPACE_ROOT +from metagpt.utils.json_to_markdown import json_to_markdown PROMPT_TEMPLATE = """ # Context @@ -86,7 +87,7 @@ class WriteTasks(Action): "Python package name" ] # CodeParser.parse_str(block="Python package name", text=context[-1].content) file_path = WORKSPACE_ROOT / ws_name / "docs/api_spec_and_tasks.md" - file_path.write_text(rsp.content) + file_path.write_text(json_to_markdown(rsp.instruct_content.dict())) # Write requirements.txt requirements_path = WORKSPACE_ROOT / ws_name / "requirements.txt" diff --git a/metagpt/utils/json_to_markdown.py b/metagpt/utils/json_to_markdown.py new file mode 100644 index 000000000..d9b40c6f6 --- /dev/null +++ b/metagpt/utils/json_to_markdown.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/11 11:50 +@Author : femto Zheng +@File : json_to_markdown.py +""" + + +# since we original write docs/*.md in markdown format, so I convert json back to markdown +def json_to_markdown(data, depth=2): + """ + Convert a JSON object to Markdown with headings for keys and lists for arrays, supporting nested objects. + + Args: + data: JSON object (dictionary) or value. + depth (int): Current depth level for Markdown headings. + + Returns: + str: Markdown representation of the JSON data. + """ + markdown = "" + + if isinstance(data, dict): + for key, value in data.items(): + if isinstance(value, list): + # Handle JSON arrays + markdown += "#" * depth + f" {key}\n\n" + items = [str(item) for item in value] + markdown += "- " + "\n- ".join(items) + "\n\n" + elif isinstance(value, dict): + # Handle nested JSON objects + markdown += "#" * depth + f" {key}\n\n" + markdown += json_to_markdown(value, depth + 1) + else: + # Handle other values + markdown += "#" * depth + f" {key}\n\n{value}\n\n" + else: + # Handle non-dictionary JSON data + markdown = str(data) + + return markdown diff --git a/tests/metagpt/actions/test_design_api.py b/tests/metagpt/actions/test_design_api.py index 46ef59c29..0add8fb74 100644 --- a/tests/metagpt/actions/test_design_api.py +++ b/tests/metagpt/actions/test_design_api.py @@ -7,9 +7,9 @@ """ import pytest -from metagpt.actions import ActionOutput from metagpt.actions.design_api import WriteDesign from metagpt.logs import logger +from metagpt.schema import Message from tests.metagpt.actions.mock import PRD_SAMPLE @@ -19,7 +19,7 @@ async def test_design_api(): design_api = WriteDesign("design_api") - result = await design_api.run([ActionOutput(content=prd, instruct_content=None)]) + result = await design_api.run([Message(content=prd, instruct_content=None)]) logger.info(result) assert result @@ -30,7 +30,7 @@ async def test_design_api_calculator(): prd = PRD_SAMPLE design_api = WriteDesign("design_api") - result = await design_api.run([ActionOutput(content=prd, instruct_content=None)]) + result = await design_api.run([Message(content=prd, instruct_content=None)]) logger.info(result) assert result diff --git a/tests/metagpt/utils/test_json_to_markdown.py b/tests/metagpt/utils/test_json_to_markdown.py new file mode 100644 index 000000000..53e410398 --- /dev/null +++ b/tests/metagpt/utils/test_json_to_markdown.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/11 11:53 +@Author : femto Zheng +@File : test_json_to_markdown.py +""" + +from metagpt.utils.json_to_markdown import json_to_markdown + + +def test_json_to_markdown(): + # Example nested JSON data + json_data = { + "title": "Sample JSON to Markdown Conversion", + "description": "Convert JSON to Markdown with headings and lists.", + "tags": ["json", "markdown", "conversion"], + "content": { + "section1": {"subsection1": "This is a subsection.", "subsection2": "Another subsection."}, + "section2": "This is the second section content.", + }, + } + + # Convert JSON to Markdown with nested sections + markdown_output = json_to_markdown(json_data) + + expected = """## title + +Sample JSON to Markdown Conversion + +## description + +Convert JSON to Markdown with headings and lists. + +## tags + +- json +- markdown +- conversion + +## content + +### section1 + +#### subsection1 + +This is a subsection. + +#### subsection2 + +Another subsection. + +### section2 + +This is the second section content. + +""" + # Print or use the generated Markdown + # print(markdown_output) + assert expected == markdown_output