include more plan status in thinking, add row num for editor

This commit is contained in:
garylin2099 2024-05-15 21:26:13 +08:00
parent 819c00a55a
commit 27157e33a0
3 changed files with 58 additions and 49 deletions

View file

@ -22,7 +22,8 @@ If plan is created, you should track the progress and update the plan accordingl
Pay close attention to new user message, review the conversation history, use reply_to_human to respond to new user requirement.
Note:
1. If you keeping encountering errors, unexpected situation, or you are not sure of proceeding, use ask_human to ask for help.
2. Each time you finish a task, use reply_to_human to report your progress.
2. Carefully review your progress at the current task, if your actions so far has not fulfilled the task instruction, you should continue with current task. Otherwise, finish current task.
3. Each time you finish a task, use reply_to_human to report your progress.
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.

View file

@ -73,9 +73,9 @@ class DataAnalyst(DataInterpreter):
# TODO: implement experience retrieval in multi-round setting
plan_status = self.planner.plan.model_dump(include=["goal", "tasks"])
for task in plan_status["tasks"]:
task.pop("code")
task.pop("result")
# for task in plan_status["tasks"]:
# task.pop("code")
# task.pop("result")
prompt = CMD_PROMPT.format(
plan_status=plan_status,
example=example,

View file

@ -2,7 +2,7 @@ import os
import shutil
import subprocess
from pydantic import BaseModel, Field
from pydantic import BaseModel
from metagpt.tools.tool_registry import register_tool
from metagpt.utils.report import EditorReporter
@ -13,12 +13,6 @@ class FileBlock(BaseModel):
file_path: str
block_content: str
block_start_line: int
block_end_line: int
symbol: str = Field(default="", description="The symbol of interest in the block, empty if not applicable.")
symbol_start_line: int = Field(
default=-1, description="The line number of the symbol in the file, -1 if not applicable"
)
@register_tool()
@ -34,13 +28,19 @@ class Editor:
f.write(content)
self.resource.report(path, "path")
def read(self, path: str) -> str:
def read(self, path: str) -> FileBlock:
"""Read the whole content of a file."""
with open(path, "r") as f:
self.resource.report(path, "path")
return f.read()
lines = f.readlines()
lines_with_num = [f"{i + 1:03}|{line}" for i, line in enumerate(lines)]
result = FileBlock(
file_path=path,
block_content="".join(lines_with_num),
)
return result
def search_content(self, symbol: str, root_path: str = ".", window: int = 20) -> FileBlock:
def search_content(self, symbol: str, root_path: str = ".", window: int = 50) -> FileBlock:
"""
Search symbol in all files under root_path, return the context of symbol with window size
Useful for locating class or function in a large codebase. Example symbol can be "def some_function", "class SomeClass", etc.
@ -48,7 +48,7 @@ class Editor:
Args:
symbol (str): The symbol to search.
root_path (str, optional): The root path to search in. If not provided, search in the current directory. Defaults to ".".
root_path (str, optional): The root path to search in, the path can be a folder or a file. If not provided, search in the current directory. Defaults to ".".
window (int, optional): The window size to return. Defaults to 20.
Returns:
@ -56,42 +56,50 @@ class Editor:
class FileBlock(BaseModel):
file_path: str
block_content: str
block_start_line: int
block_end_line: int
symbol: str = Field(default="", description="The symbol of interest in the block, empty if not applicable.")
symbol_start_line: int = Field(default=-1, description="The line number of the symbol in the file, -1 if not applicable")
"""
if not os.path.exists(root_path):
print(f"Currently at {os.getcwd()}. Path {root_path} does not exist.")
return None
not_found_msg = (
"symbol not found, you may try searching another one, or break down your search term to search a part of it"
)
if os.path.isfile(root_path):
result = self._search_content_in_file(symbol, root_path, window)
if not result:
print(not_found_msg)
return result
for root, _, files in os.walk(root_path or "."):
for file in files:
file_path = os.path.join(root, file)
if not file.endswith(".py"):
continue
with open(file_path, "r", encoding="utf-8") as f:
try:
lines = f.readlines()
except Exception:
continue
for i, line in enumerate(lines):
if symbol in line:
start = max(i - window, 0)
end = min(i + window, len(lines) - 1)
block_content = "".join(lines[start : end + 1])
result = FileBlock(
file_path=file_path,
block_content=block_content,
block_start_line=start + 1,
block_end_line=end + 1,
symbol=symbol,
symbol_start_line=i + 1,
)
self.resource.report(result.file_path, "path")
return result
print(
"symbol not found, you may try searching another one, or break down your search term to search a part of it"
)
result = self._search_content_in_file(symbol, file_path, window)
if result:
# FIXME: This returns the first found result, not all results.
return result
print(not_found_msg)
return None
def _search_content_in_file(self, symbol: str, file_path: str, window: int = 50) -> FileBlock:
print("search in", file_path)
if not file_path.endswith(".py"):
return None
with open(file_path, "r", encoding="utf-8") as f:
try:
lines = f.readlines()
except Exception:
return None
for i, line in enumerate(lines):
if symbol in line:
start = max(i - window, 0)
end = min(i + window, len(lines) - 1)
for row_num in range(start, end + 1):
lines[row_num] = f"{(row_num + 1):03}|{lines[row_num]}"
block_content = "".join(lines[start : end + 1])
result = FileBlock(
file_path=file_path,
block_content=block_content,
)
self.resource.report(result.file_path, "path")
return result
return None
def write_content(self, file_path: str, start_line: int, end_line: int, new_block_content: str = "") -> str:
@ -100,12 +108,13 @@ class Editor:
1. If the new block content is empty, the original block will be deleted.
2. If the new block content is not empty and end_line < start_line (e.g. set end_line = -1) the new block content will be inserted at start_line.
3. If the new block content is not empty and end_line >= start_line, the original block from start_line to end_line (both inclusively) will be replaced by the new block content.
This function can sometimes be used given a FileBlock upstream. Think carefully if you want to use block_start_line or symbol_start_line in the FileBlock as your start_line input. Your new_block_content will be placed at the start_line.
This function can sometimes be used given a FileBlock upstream. You should carefully review its row number. Determine the start_line and end_line based on the row number of the FileBlock.
The file content from start_line to end_line will be replaced by your new_block_content. DON'T replace more than you intend to.
Args:
file_path (str): The file path to write the new block content.
start_line (int): start line of the original block to be updated.
end_line (int): end line of the original block to be updated.
start_line (int): start line of the original block to be updated (inclusive).
end_line (int): end line of the original block to be updated (inclusive).
new_block_content (str): The new block content to write.
Returns:
@ -130,8 +139,6 @@ class Editor:
new_file_block = FileBlock(
file_path=file_path,
block_content=new_block_content,
block_start_line=start_line,
block_end_line=-1 if end_line < start_line else start_line + new_block_content.count("\n"),
)
self.resource.report(new_file_block.file_path, "path")
@ -143,6 +150,7 @@ class Editor:
os.remove(temp_file_path)
def _write_content(self, file_path: str, start_line: int, end_line: int, new_block_content: str = ""):
"""start_line and end_line are both 1-based indices and inclusive."""
with open(file_path, "r") as file:
lines = file.readlines()