Merge branch 'mgx_ops' into automated-get-image

This commit is contained in:
黄伟韬 2024-09-12 13:54:33 +08:00
commit a939efcd7a
8 changed files with 59 additions and 26 deletions

View file

@ -19,8 +19,8 @@ LANGUAGE = ActionNode(
PROGRAMMING_LANGUAGE = ActionNode(
key="Programming Language",
expected_type=str,
instruction="Python/JavaScript or other mainstream programming language.",
example="Python",
instruction="Mainstream programming language. If not specified in the requirements, use HTML, CSS, and Pure JavaScript.",
example="HTML, CSS, and Pure JavaScript",
)
ORIGINAL_REQUIREMENTS = ActionNode(

View file

@ -46,6 +46,7 @@ Special Command: Use {{"command_name": "end"}} to do nothing or indicate complet
# Instruction
{instruction}
"""
CMD_EXPERIENCE_MASK = f"""
@ -64,8 +65,8 @@ CMD_PROMPT = (
# Current Task
{current_task}
# Restrictions
{requirements_constraints}
# Response Language
you must respond in {respond_language}.
Pay close attention to the Example provided, you can reuse the example for your current situation if it fits.
You may use any of the available commands to create a plan or update the plan. You may output mutiple commands, they will be executed sequentially.
@ -89,7 +90,6 @@ Output should adhere to the following format.
Notice: your output JSON data section must start with **```json [**
"""
)
THOUGHT_GUIDANCE = """
First, describe the actions you have taken recently.
Second, describe the messages you have received recently, with a particular emphasis on messages from users. If necessary, develop a plan to address the new user requirements.
@ -236,7 +236,7 @@ reply : The herb names have been successfully extracted. A total of 8 herb names
Carefully review the history and respond to the user in the expected language to meet their requirements.
If you have any deliverables that are helpful in explaining the results (such as files, metrics, quantitative results, etc.), provide brief descriptions of them.
Your reply must be concise.
{lanaguge_restruction}
You must respond in {respond_language}
Directly output your reply content. Do not add any output format.
"""
SUMMARY_PROMPT = """
@ -244,3 +244,11 @@ Summarize what you have accomplished lately. Be concise.
If you produce any deliverables, include their short descriptions and file paths. If there are any metrics or quantitative results, include them, too.
If the deliverable is code, only output the file path.
"""
DETECT_LANGUAGE_PROMPT = """
The requirement is:
{requirement}
Which Natural Language must you respond in?
Output only the language type.
"""

View file

@ -32,8 +32,10 @@ Note:
9. Do not use the 'end' command when the current task remains unfinished; instead, use the 'finish_current_task' command to indicate completion before switching to the next task.
10. Do not use escape characters in json data, particularly within file paths.
11. Analyze the capabilities of team members and assign tasks to them based on user Requirements. If the requirements ask to ignore certain tasks, follow the requirements.
12. Add default web technologies: HTML (*.html), CSS (*.css), and JavaScript (*.js) to your requirements.If no specific programming language is required, include these technologies in the project requirements. Using instruction to forward this information to your team members.
13. If the the user message is a question. use 'reply to human' to respond to the question, and then end.
12. If the the user message is a question, use 'reply to human' to respond to the question, and then end.
13. Instructions and reply must be in the same language.
14. Default technology stack is HTML (.html), CSS (.css), and Pure JavaScript (.js). Web app is the default option when developing software.
15. You are the only one who decides the programming language for the software, so the instruction must contain the programming language.
"""
TL_THOUGHT_GUIDANCE = (
THOUGHT_GUIDANCE

View file

@ -10,7 +10,6 @@ from typing import Annotated, Callable, Dict, List, Literal, Optional, Tuple
from pydantic import Field, model_validator
from metagpt.actions import Action, UserRequirement
from metagpt.actions.analyze_requirements import AnalyzeRequirementsRestrictions
from metagpt.actions.di.run_command import RunCommand
from metagpt.actions.search_enhanced_qa import SearchEnhancedQA
from metagpt.const import IMAGES
@ -21,6 +20,7 @@ from metagpt.logs import logger
from metagpt.prompts.di.role_zero import (
ASK_HUMAN_COMMAND,
CMD_PROMPT,
DETECT_LANGUAGE_PROMPT,
JSON_REPAIR_PROMPT,
QUICK_RESPONSE_SYSTEM_PROMPT,
QUICK_THINK_EXAMPLES,
@ -79,6 +79,7 @@ class RoleZero(Role):
"Editor.edit_file_by_replace",
"Editor.insert_content_at_line",
"Editor.append_file",
"Editor.open_file",
]
# Equipped with three basic tools by default for optional use
editor: Editor = Editor(enable_auto_lint=True)
@ -92,7 +93,7 @@ class RoleZero(Role):
commands: list[dict] = [] # commands to be executed
memory_k: int = 200 # number of memories (messages) to use as historical context
use_fixed_sop: bool = False
requirements_constraints: str = "" # the constraints in user requirements
respond_language: str = "" # Language for responding humans and publishing messages.
use_summary: bool = True # whether to summarize at the end
@model_validator(mode="after")
@ -179,8 +180,8 @@ class RoleZero(Role):
if not self.planner.plan.goal:
self.planner.plan.goal = self.get_memories()[-1].content
self.requirements_constraints = await AnalyzeRequirementsRestrictions().run(self.planner.plan.goal)
detect_language_prompt = DETECT_LANGUAGE_PROMPT.format(requirement=self.planner.plan.goal)
self.respond_language = await self.llm.aask(detect_language_prompt)
### 1. Experience ###
example = self._retrieve_experience()
@ -206,7 +207,7 @@ class RoleZero(Role):
current_state=self.cmd_prompt_current_state,
plan_status=plan_status,
current_task=current_task,
requirements_constraints=self.requirements_constraints,
respond_language=self.respond_language,
)
### Recent Observation ###
@ -549,9 +550,7 @@ class RoleZero(Role):
# Ensure reply to the human before the "end" command is executed. Hard code k=5 for checking.
if not any(["reply_to_human" in memory.content for memory in self.get_memories(k=5)]):
logger.info("manually reply to human")
pattern = r"\[Language Restrictions\](.*?)\n"
match = re.search(pattern, self.requirements_constraints, re.DOTALL)
reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format(lanaguge_restruction=match.group(0) if match else "")
reply_to_human_prompt = REPORT_TO_HUMAN_PROMPT.format(respond_language=self.respond_language)
async with ThoughtReporter(enable_llm_stream=True) as reporter:
await reporter.async_report({"type": "quick"})
reply_content = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(reply_to_human_prompt)]))

View file

@ -470,7 +470,7 @@ class TRDExpRetriever(ExpRetriever):
TL_EXAMPLE = """
## example 1
User Requirement: Create a cli snake game using Python.
User Requirement: Create a cli snake game.
Explanation: The requirement is about software development. Assign each tasks to a different team member based on their expertise. When publishing message to Product Manager, we copy original user requirement directly to ensure no information loss.
```json
[
@ -479,7 +479,7 @@ Explanation: The requirement is about software development. Assign each tasks to
"args": {
"task_id": "1",
"dependent_task_ids": [],
"instruction": "Create a product requirement document (PRD) outlining the features, user interface, and user experience of the CLI python snake game. Using Python as the programming language.",
"instruction": "Use HTML, CSS, Pure JavaScrip as the programming language. And create a product requirement document (PRD) outlining the features, user interface. ",
"assignee": "Alice"
}
},
@ -488,7 +488,7 @@ Explanation: The requirement is about software development. Assign each tasks to
"args": {
"task_id": "2",
"dependent_task_ids": ["1"],
"instruction": "Design the software architecture for the CLI snake game, including the choice of programming language, libraries, and data flow. Using Python as the programming language.",
"instruction": "Use HTML, CSS, Pure JavaScrip as the programming language. Design the software architecture for the CLI snake game, including the data flow.",
"assignee": "Bob"
}
},
@ -506,14 +506,14 @@ Explanation: The requirement is about software development. Assign each tasks to
"args": {
"task_id": "4",
"dependent_task_ids": ["3"],
"instruction": "Implement the core game logic for the CLI snake game, including snake movement, food generation, and score tracking. Using Python as the programming language.",
"instruction": "Use HTML, CSS, Pure JavaScrip as the programming language. Implement the core game logic for the CLI snake game, including snake movement, food generation, and score tracking.",
"assignee": "Alex"
}
},
{
"command_name": "TeamLeader.publish_message",
"args": {
"content": "Create a cli snake game. Using Python as the programming language.",
"content": "Use HTML, CSS, Pure JavaScrip as the programming language. Create a cli snake game.",
"send_to": "Alice"
}
},
@ -848,7 +848,7 @@ Here's the plan:
[Optional] 0. **Task 0**: Obtain images before coding.
1. **Task 1**: Create `index.html` - This file will contain the HTML structure necessary for the game's UI.
2. **Task 2**: Create `style.css` - This file will define the CSS styles to make the game visually appealing and responsive.
3. **Task 3**: Create `script.js` - This file will contain the JavaScript code for the game logic and UI interactions.
3. **Task 3**: Create `script.js` - This file will contain the Pure JavaScript code for the game logic and UI interactions.
Let's start by appending the first task to the plan.
@ -877,7 +877,7 @@ Let's start by appending the first task to the plan.
"args": {
"task_id": "3",
"dependent_task_ids": ["1", "2"],
"instruction": "Create the script.js file containing the JavaScript logic for the 2048 game.",
"instruction": "Create the script.js file containing the Pure JavaScript logic for the 2048 game.",
"assignee": "Alex"
}
}

View file

@ -13,7 +13,6 @@ from typing import List, Optional, Union
from pydantic import BaseModel, ConfigDict
from metagpt.const import DEFAULT_WORKSPACE_ROOT
from metagpt.logs import logger
from metagpt.tools.libs.index_repo import IndexRepo
from metagpt.tools.libs.linter import Linter
from metagpt.tools.tool_registry import register_tool
@ -717,8 +716,6 @@ class Editor(BaseModel):
If you need to use it multiple times, wait for the next turn.
"""
# FIXME: support replacing *all* occurrences
if to_replace.strip() == "":
raise ValueError("`to_replace` must not be empty.")
if to_replace == new_content:
raise ValueError("`to_replace` and `new_content` must be different.")
@ -730,6 +727,11 @@ class Editor(BaseModel):
with file_name.open("r") as file:
file_content = file.read()
if to_replace.strip() == "":
if file_content.strip() == "":
raise ValueError(f"The file '{file_name}' is empty. Please use the append method to add content.")
raise ValueError("`to_replace` must not be empty.")
if file_content.count(to_replace) > 1:
raise ValueError(
"`to_replace` appears more than once, please include enough lines to make code in `to_replace` unique."

View file

@ -32,6 +32,7 @@ class Linter:
self.languages = dict(
python=self.py_lint,
sql=self.fake_lint, # base_lint lacks support for full SQL syntax. Use fake_lint to bypass the validation.
)
self.all_lint_cmd = None
@ -112,6 +113,9 @@ class Linter:
error = basic_lint(rel_fname, code)
return error
def fake_lint(self, fname, rel_fname, code):
return None
def lint_python_compile(fname, code):
try:

View file

@ -45,6 +45,15 @@ def temp_py_file(tmp_path):
temp_file_path.unlink()
@pytest.fixture
def empty_file(tmp_path):
assert tmp_path is not None
temp_file_path = tmp_path / "test_script_empty_file_for_editor.py"
temp_file_path.write_text("")
yield temp_file_path
temp_file_path.unlink()
EXPECTED_CONTENT_AFTER_REPLACE = """
# this is line one
def test_function_for_fm():
@ -530,6 +539,15 @@ def test_edit_file_by_replace(temp_py_file):
assert new_content.strip() == EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip()
def test_edit_file_by_replace_to_palce_empty(empty_file):
editor = Editor()
with pytest.raises(ValueError) as exc_info:
editor.edit_file_by_replace(
file_name=str(empty_file), to_replace="", new_content=EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip()
)
assert "is empty. Use the append method to add content." in str(exc_info.value)
def test_append_file(temp_file_path):
editor = Editor()
# 写入初始内容