mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-05 14:55:18 +02:00
feat: + __init__.py
feat: +import_git_repo
This commit is contained in:
parent
05a4967e0b
commit
1a88b3fc19
7 changed files with 103 additions and 7 deletions
|
|
@ -1,5 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
|
||||
This script defines an action to import a Git repository into the MetaGPT project format, enabling incremental
|
||||
appending of requirements.
|
||||
The MetaGPT project format encompasses a structured representation of project data compatible with MetaGPT's
|
||||
capabilities, facilitating the integration of Git repositories into MetaGPT workflows while allowing for the gradual
|
||||
addition of requirements.
|
||||
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
|
@ -30,11 +39,30 @@ from metagpt.utils.project_repo import ProjectRepo
|
|||
|
||||
|
||||
class ImportRepo(Action):
|
||||
repo_path: str
|
||||
graph_db: Optional[GraphRepository] = None
|
||||
rid: str = ""
|
||||
"""
|
||||
An action to import a Git repository into a graph database and create related artifacts.
|
||||
|
||||
Attributes:
|
||||
repo_path (str): The URL of the Git repository to import.
|
||||
graph_db (Optional[GraphRepository]): The output graph database of the Git repository.
|
||||
rid (str): The output requirement ID.
|
||||
"""
|
||||
|
||||
repo_path: str # input, git repo url.
|
||||
graph_db: Optional[GraphRepository] = None # output. graph db of the git repository
|
||||
rid: str = "" # output, requirement ID.
|
||||
|
||||
async def run(self, with_messages: List[Message] = None, **kwargs) -> Message:
|
||||
"""
|
||||
Runs the import process for the Git repository.
|
||||
|
||||
Args:
|
||||
with_messages (List[Message], optional): Additional messages to include.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the completion of the import process.
|
||||
"""
|
||||
await self._create_repo()
|
||||
await self._create_prd()
|
||||
await self._create_system_design()
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ class Engineer(Role):
|
|||
return self.rc.todo
|
||||
return None
|
||||
|
||||
async def _new_coding_context(self, filename, dependency) -> CodingContext:
|
||||
async def _new_coding_context(self, filename, dependency) -> Optional[CodingContext]:
|
||||
old_code_doc = await self.project_repo.srcs.get(filename)
|
||||
if not old_code_doc:
|
||||
old_code_doc = Document(root_path=str(self.project_repo.src_relative_path), filename=filename, content="")
|
||||
|
|
@ -283,6 +283,8 @@ class Engineer(Role):
|
|||
elif str(i.parent) == CODE_PLAN_AND_CHANGE_FILE_REPO:
|
||||
code_plan_and_change_doc = await self.project_repo.docs.code_plan_and_change.get(i.name)
|
||||
if not task_doc or not design_doc:
|
||||
if filename == "__init__.py": # `__init__.py` created by `init_python_folder`
|
||||
return None
|
||||
logger.error(f'Detected source code "{filename}" from an unknown origin.')
|
||||
raise ValueError(f'Detected source code "{filename}" from an unknown origin.')
|
||||
context = CodingContext(
|
||||
|
|
@ -294,8 +296,10 @@ class Engineer(Role):
|
|||
)
|
||||
return context
|
||||
|
||||
async def _new_coding_doc(self, filename, dependency):
|
||||
async def _new_coding_doc(self, filename, dependency) -> Optional[Document]:
|
||||
context = await self._new_coding_context(filename, dependency)
|
||||
if not context:
|
||||
return None # `__init__.py` created by `init_python_folder`
|
||||
coding_doc = Document(
|
||||
root_path=str(self.project_repo.src_relative_path), filename=filename, content=context.model_dump_json()
|
||||
)
|
||||
|
|
@ -352,6 +356,8 @@ class Engineer(Role):
|
|||
if filename in changed_files.docs:
|
||||
continue
|
||||
coding_doc = await self._new_coding_doc(filename=filename, dependency=dependency)
|
||||
if not coding_doc:
|
||||
continue # `__init__.py` created by `init_python_folder`
|
||||
changed_files.docs[filename] = coding_doc
|
||||
self.code_todos.append(WriteCode(i_context=coding_doc, context=self.context, llm=self.llm))
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from metagpt.const import MESSAGE_ROUTE_TO_NONE
|
|||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.schema import Document, Message, RunCodeContext, TestingContext
|
||||
from metagpt.utils.common import any_to_str_set, parse_recipient
|
||||
from metagpt.utils.common import any_to_str_set, init_python_folder, parse_recipient
|
||||
|
||||
|
||||
class QaEngineer(Role):
|
||||
|
|
@ -141,6 +141,7 @@ class QaEngineer(Role):
|
|||
)
|
||||
|
||||
async def _act(self) -> Message:
|
||||
await init_python_folder(self.project_repo.tests.workdir)
|
||||
if self.test_round > self.test_round_allowed:
|
||||
result_msg = Message(
|
||||
content=f"Exceeding {self.test_round_allowed} rounds of tests, skip (writing code counts as a round, too)",
|
||||
|
|
|
|||
|
|
@ -270,3 +270,32 @@ async def git_archive(project_path: str | Path) -> str:
|
|||
ctx.set_repo_dir(project_path)
|
||||
ctx.git_repo.archive()
|
||||
return ctx.git_repo.log()
|
||||
|
||||
|
||||
@register_tool(tags=["software development", "import git repo"])
|
||||
async def import_git_repo(url: str) -> Path:
|
||||
"""
|
||||
Imports a project from a Git website and formats it to MetaGPT project format to enable incremental appending requirements.
|
||||
|
||||
Args:
|
||||
url (str): The Git project URL, such as "https://github.com/geekan/MetaGPT.git".
|
||||
|
||||
Returns:
|
||||
Path: The path of the formatted project.
|
||||
|
||||
Example:
|
||||
# The Git project URL to input
|
||||
>>> git_url = "https://github.com/geekan/MetaGPT.git"
|
||||
|
||||
# Import the Git repository and get the formatted project path
|
||||
>>> formatted_project_path = await import_git_repo(git_url)
|
||||
>>> print("Formatted project path:", formatted_project_path)
|
||||
/PATH/TO/THE/FORMMATTED/PROJECT
|
||||
"""
|
||||
from metagpt.actions.import_repo import ImportRepo
|
||||
from metagpt.context import Context
|
||||
|
||||
ctx = Context()
|
||||
action = ImportRepo(repo_path=url, context=ctx)
|
||||
await action.run()
|
||||
return ctx.repo.workdir
|
||||
|
|
|
|||
|
|
@ -251,6 +251,8 @@ class GitRepository:
|
|||
if not directory_path.exists():
|
||||
return []
|
||||
for file_path in directory_path.iterdir():
|
||||
if not file_path.is_relative_to(root_relative_path):
|
||||
continue
|
||||
if file_path.is_file():
|
||||
rpath = file_path.relative_to(root_relative_path)
|
||||
files.append(str(rpath))
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -12,6 +12,7 @@ from metagpt.tools.libs import (
|
|||
write_prd,
|
||||
write_project_plan,
|
||||
)
|
||||
from metagpt.tools.libs.software_development import import_git_repo
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -53,5 +54,12 @@ NameError: name 'x' is not defined
|
|||
assert git_log
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_repo():
|
||||
url = "https://github.com/spec-first/connexion.git"
|
||||
path = await import_git_repo(url)
|
||||
assert path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-s"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue