Merge branch 'mgx_ops' into swe_editer

This commit is contained in:
seeker-jie 2024-08-29 18:03:47 +08:00
commit b394fed52b
58 changed files with 2003 additions and 1372 deletions

View file

@ -80,6 +80,9 @@ exp_pool:
enable_write: false
persist_path: .chroma_exp_data # The directory.
retrieval_type: bm25 # Default is `bm25`, can be set to `chroma` for vector storage, which requires setting up embedding.
use_llm_ranker: true # Default is `true`, it will use LLM Reranker to get better result.
collection_name: experience_pool # When `retrieval_type` is `chroma`, `collection_name` is the collection name in chromadb.
azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY"
azure_tts_region: "eastus"

View file

@ -46,8 +46,8 @@ async def add_exp(req: str, resp: str, tag: str, metric: Metric = None):
metric=metric or Metric(score=Score(val=10, reason="Manual")),
)
exp_manager = get_exp_manager()
exp_manager.config.exp_pool.enabled = True
exp_manager.config.exp_pool.enable_write = True
exp_manager.is_writable = True
exp_manager.create_exp(exp)
logger.info(f"New experience created for the request `{req[:10]}`.")

View file

@ -0,0 +1,85 @@
"""Load and save experiences from the log file."""
import json
from pathlib import Path
from metagpt.exp_pool import get_exp_manager
from metagpt.exp_pool.schema import LOG_NEW_EXPERIENCE_PREFIX, Experience
from metagpt.logs import logger
def load_exps(log_file_path: str) -> list[Experience]:
"""Loads experiences from a log file.
Args:
log_file_path (str): The path to the log file.
Returns:
list[Experience]: A list of Experience objects loaded from the log file.
"""
if not Path(log_file_path).exists():
logger.warning(f"`load_exps` called with a non-existent log file path: {log_file_path}")
return
exps = []
with open(log_file_path, "r") as log_file:
for line in log_file:
if LOG_NEW_EXPERIENCE_PREFIX in line:
json_str = line.split(LOG_NEW_EXPERIENCE_PREFIX, 1)[1].strip()
exp_data = json.loads(json_str)
exp = Experience(**exp_data)
exps.append(exp)
logger.info(f"Loaded {len(exps)} experiences from log file: {log_file_path}")
return exps
def save_exps(exps: list[Experience]):
"""Saves a list of experiences to the experience pool.
Args:
exps (list[Experience]): The list of experiences to save.
"""
if not exps:
logger.warning("`save_exps` called with an empty list of experiences.")
return
manager = get_exp_manager()
manager.is_writable = True
manager.create_exps(exps)
logger.info(f"Saved {len(exps)} experiences.")
def get_log_file_path() -> str:
"""Retrieves the path to the log file.
Returns:
str: The path to the log file.
Raises:
ValueError: If the log file path cannot be found.
"""
handlers = logger._core.handlers
for handler in handlers.values():
if "log" in handler._name:
return handler._name[1:-1]
raise ValueError("Log file not found")
def main():
log_file_path = get_log_file_path()
exps = load_exps(log_file_path)
save_exps(exps)
if __name__ == "__main__":
main()

View file

@ -231,7 +231,7 @@ class WriteDesign(Action):
async def _save_mermaid_file(self, data: str, pathname: Path):
pathname.parent.mkdir(parents=True, exist_ok=True)
await mermaid_to_file(self.config.mermaid.engine, data, pathname)
image_path = pathname.parent / f"{pathname.name}.png"
image_path = pathname.parent / f"{pathname.name}.svg"
if image_path.exists():
await GalleryReporter().async_report(image_path, "path")

View file

@ -1,141 +0,0 @@
from __future__ import annotations
import asyncio
import re
from enum import Enum
from typing import Tuple
from pydantic import BaseModel
from metagpt.actions import Action
from metagpt.schema import Message
class SOPItemDef(BaseModel):
"""
Represents an item in a Standard Operating Procedure (SOP).
Attributes:
name (str): name of the SOP item.
description (str): The description or title of the SOP.
sop (List[str]): The steps or details of the SOP.
"""
name: str
description: str
sop: list[str] = []
class SOPItem(Enum):
SOFTWARE_DEVELOPMENT = SOPItemDef(
name="software development",
description="Software development intention including developing or building software, games, app, websites, etc. EXCLUDING bug fixes, report any issues, environment setup, terminal operations, and pip install.",
sop=[
"Writes a PRD based on software requirements.",
"Writes a system design to the project repository, based on the PRD of the project. Write high-level system design instead of the actual code.",
"Writes a project plan to the project repository, based on the design of the project.",
"Writes code to implement designed features according to the project plan and adds them to the project repository.",
"Stage and commit changes for the project repository using Git.",
],
)
# FIX_BUGS = SOPItemDef(
# name="fix bugs",
# description="Fix bugs in a given project.",
# sop=[
# "Fix bugs in the project repository.",
# "Stage and commit changes for the project repository using Git.",
# ],
# )
# FORMAT_REPO = SOPItemDef(
# name="format repo",
# description="download repository from git and format the project to MetaGPT project",
# sop=[
# "Imports a project from a Git website and formats it to MetaGPT project format to enable incremental appending requirements.",
# "Stage and commit changes for the project repository using Git.",
# ],
# )
WEB_OPERATION = SOPItemDef(
name="web operation",
description="web browsing, scraping, imitation and other interaction with the web",
)
OTHER = SOPItemDef(
name="other",
description="Other intentions that do not fall into the above categories, including data science, data analysis, machine learning, deep learning and text-to-image etc.",
)
@property
def type_name(self):
return self.value.name
@classmethod
def get_type(cls, type_name):
for member in cls:
if member.type_name == type_name:
return member.value
return None
DETECT_PROMPT = """
# User Requirement
{user_requirement}
# Intentions
{intentions}
# Task
Classify user requirement into one type of the above intentions, output the index of the intention directly.
Intention index:
"""
REQ_WITH_SOP = """
{user_requirement}
## Knowledge
To meet user requirements, the following standard operating procedure(SOP) must be used:
{sop}
"""
class DetectIntent(Action):
async def run(self, with_message: Message, **kwargs) -> Tuple[str, str]:
user_requirement = with_message.content
mappings = {i + 1: si for i, si in enumerate(SOPItem)}
intentions = "\n".join([f"{i + 1}. {si.type_name}: {si.value.description}" for i, si in enumerate(SOPItem)])
prompt = DETECT_PROMPT.format(user_requirement=user_requirement, intentions=intentions)
rsp = await self._aask(prompt)
match = re.search(r"\d+", rsp)
index = len(SOPItem) + 1 # 1-based
if match:
index = int(match.group()) # 1-based
sop = mappings[index].value.sop if index in mappings else None
sop_type = mappings[index].type_name if index in mappings else SOPItem.OTHER.type_name
req_with_sop = (
REQ_WITH_SOP.format(
user_requirement=user_requirement, sop="\n".join([f"{i + 1}. {v}" for i, v in enumerate(sop)])
)
if sop
else user_requirement
)
return req_with_sop, sop_type
async def main():
# Example usage of the DetectIntent action
user_requirements = [
"Develop a 2048 game.",
"Run data analysis on sklearn wine dataset",
"帮我把pip的源设置成https://pypi.tuna.tsinghua.edu.cn/simple",
"This is a website url does not require login: https://demosc.chinaz.net/Files/DownLoad//moban/202404/moban7767 please write a similar web page,developed in vue language, The package.json dependency must be generated",
"I would like to imitate the website available at https://demosc.chinaz.net/Files/DownLoad//moban/202404/moban7767. Could you please browse through it?",
]
detect_intent = DetectIntent()
for user_requirement in user_requirements:
req_with_sop, sop_type = await detect_intent.run(Message(role="user", content=user_requirement))
print(req_with_sop)
print(f"Detected SOP Type: {sop_type}")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -278,7 +278,7 @@ class WritePRD(Action):
pathname = output_filename or self.repo.workdir / COMPETITIVE_ANALYSIS_FILE_REPO / Path(prd_doc.filename).stem
pathname.parent.mkdir(parents=True, exist_ok=True)
await mermaid_to_file(self.config.mermaid.engine, quadrant_chart, pathname)
image_path = pathname.parent / f"{pathname.name}.png"
image_path = pathname.parent / f"{pathname.name}.svg"
if image_path.exists():
await GalleryReporter().async_report(image_path, "path")

8
metagpt/base/__init__.py Normal file
View file

@ -0,0 +1,8 @@
from metagpt.base.base_env import BaseEnvironment
from metagpt.base.base_role import BaseRole
__all__ = [
"BaseEnvironment",
"BaseRole",
]

38
metagpt/base/base_env.py Normal file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc : base environment
from abc import abstractmethod
from typing import Any, Optional
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.schema import Message
class BaseEnvironment:
"""Base environment"""
@abstractmethod
def reset(
self,
*,
seed: Optional[int] = None,
options: Optional[dict[str, Any]] = None,
) -> tuple[dict[str, Any], dict[str, Any]]:
"""Implement this to get init observation"""
@abstractmethod
def observe(self, obs_params: Optional[BaseEnvObsParams] = None) -> Any:
"""Implement this if you want to get partial observation from the env"""
@abstractmethod
def step(self, action: BaseEnvAction) -> tuple[dict[str, Any], float, bool, bool, dict[str, Any]]:
"""Implement this to feed a action and then get new observation from the env"""
@abstractmethod
def publish_message(self, message: Message, peekable: bool = True) -> bool:
"""Distribute the message to the recipients."""
@abstractmethod
async def run(self, k=1):
"""Process all task at once"""

36
metagpt/base/base_role.py Normal file
View file

@ -0,0 +1,36 @@
from abc import abstractmethod
from typing import Optional, Union
from metagpt.schema import Message
class BaseRole:
"""Abstract base class for all roles."""
name: str
@property
def is_idle(self) -> bool:
raise NotImplementedError
@abstractmethod
def think(self):
"""Consider what to do and decide on the next course of action."""
raise NotImplementedError
@abstractmethod
def act(self):
"""Perform the current action."""
raise NotImplementedError
@abstractmethod
async def react(self) -> Message:
"""Entry to one of three strategies by which Role reacts to the observed Message."""
@abstractmethod
async def run(self, with_message: Optional[Union[str, Message, list[str]]] = None) -> Optional[Message]:
"""Observe, and think and act based on the results of the observation."""
@abstractmethod
def get_memories(self, k: int = 0) -> list[Message]:
"""Return the most recent k memories of this role."""

View file

@ -110,8 +110,7 @@ class Config(CLIParams, YamlModel):
CONFIG_ROOT / "config2.yaml",
)
if reload or default_config_paths not in _CONFIG_CACHE:
dicts = [kwargs, dict(os.environ)]
dicts += [Config.read_yaml(path) for path in default_config_paths]
dicts = [dict(os.environ), *(Config.read_yaml(path) for path in default_config_paths), kwargs]
final = merge_dict(dicts)
_CONFIG_CACHE[default_config_paths] = Config(**final)
return _CONFIG_CACHE[default_config_paths]

View file

@ -21,3 +21,5 @@ class ExperiencePoolConfig(YamlModel):
retrieval_type: ExperiencePoolRetrievalType = Field(
default=ExperiencePoolRetrievalType.BM25, description="The retrieval type for experience pool."
)
use_llm_ranker: bool = Field(default=True, description="Use LLM Reranker to get better result.")
collection_name: str = Field(default="experience_pool", description="The collection name in chromadb")

View file

@ -3,3 +3,4 @@ from metagpt.utils.yaml_model import YamlModel
class OmniParseConfig(YamlModel):
url: str = ""
timeout: int = 600

View file

@ -8,9 +8,9 @@ from typing import Any, Optional
from pydantic import Field
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.environment.android.const import ADB_EXEC_FAIL
from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable
from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams
class AndroidExtEnv(ExtEnv):

View file

@ -5,28 +5,26 @@
import asyncio
from abc import abstractmethod
from enum import Enum
from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Set, Union
from typing import Any, Dict, Iterable, Optional, Set, Union
from gymnasium import spaces
from gymnasium.core import ActType, ObsType
from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator
from metagpt.base import BaseEnvironment, BaseRole
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.context import Context
from metagpt.environment.api.env_api import (
EnvAPIAbstract,
ReadAPIRegistry,
WriteAPIRegistry,
)
from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.logs import logger
from metagpt.memory import Memory
from metagpt.schema import Message
from metagpt.utils.common import get_function_schema, is_coroutine_func, is_send_to
from metagpt.utils.git_repository import GitRepository
if TYPE_CHECKING:
from metagpt.roles.role import Role # noqa: F401
class EnvType(Enum):
ANDROID = "Android"
@ -52,7 +50,7 @@ def mark_as_writeable(func):
return func
class ExtEnv(BaseModel):
class ExtEnv(BaseEnvironment, BaseModel):
"""External Env to integrate actual game environment"""
model_config = ConfigDict(arbitrary_types_allowed=True)
@ -131,8 +129,8 @@ class Environment(ExtEnv):
model_config = ConfigDict(arbitrary_types_allowed=True)
desc: str = Field(default="") # 环境描述
roles: dict[str, SerializeAsAny["Role"]] = Field(default_factory=dict, validate_default=True)
member_addrs: Dict["Role", Set] = Field(default_factory=dict, exclude=True)
roles: dict[str, SerializeAsAny[BaseRole]] = Field(default_factory=dict, validate_default=True)
member_addrs: Dict[BaseRole, Set] = Field(default_factory=dict, exclude=True)
history: Memory = Field(default_factory=Memory) # For debug
context: Context = Field(default_factory=Context, exclude=True)
@ -155,7 +153,7 @@ class Environment(ExtEnv):
self.add_roles(self.roles.values())
return self
def add_role(self, role: "Role"):
def add_role(self, role: BaseRole):
"""增加一个在当前环境的角色
Add a role in the current environment
"""
@ -163,7 +161,7 @@ class Environment(ExtEnv):
role.set_env(self)
role.context = self.context
def add_roles(self, roles: Iterable["Role"]):
def add_roles(self, roles: Iterable[BaseRole]):
"""增加一批在当前环境的角色
Add a batch of characters in the current environment
"""
@ -212,13 +210,13 @@ class Environment(ExtEnv):
await asyncio.gather(*futures)
logger.debug(f"is idle: {self.is_idle}")
def get_roles(self) -> dict[str, "Role"]:
def get_roles(self) -> dict[str, BaseRole]:
"""获得环境内的所有角色
Process all Role runs at once
"""
return self.roles
def get_role(self, name: str) -> "Role":
def get_role(self, name: str) -> BaseRole:
"""获得环境内的指定角色
get all the environment roles
"""
@ -247,12 +245,3 @@ class Environment(ExtEnv):
if auto_archive and self.context.kwargs.get("project_path"):
git_repo = GitRepository(self.context.kwargs.project_path)
git_repo.archive()
@classmethod
def model_rebuild(cls, **kwargs):
from metagpt.roles.role import Role # noqa: F401
super().model_rebuild(**kwargs)
Environment.model_rebuild()

View file

@ -10,8 +10,8 @@ from typing import Any, Optional
import requests
from pydantic import ConfigDict, Field, model_validator
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.environment.base_env import ExtEnv, mark_as_writeable
from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.environment.minecraft.const import (
MC_CKPT_DIR,
MC_CORE_INVENTORY_ITEMS,

View file

@ -9,7 +9,7 @@ import numpy.typing as npt
from gymnasium import spaces
from pydantic import ConfigDict, Field, field_validator
from metagpt.environment.base_env_space import (
from metagpt.base.base_env_space import (
BaseEnvAction,
BaseEnvActionType,
BaseEnvObsParams,

View file

@ -9,8 +9,8 @@ from typing import Any, Callable, Optional
from pydantic import ConfigDict, Field
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable
from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.logs import logger

View file

@ -10,7 +10,13 @@ from metagpt.config2 import Config
from metagpt.exp_pool.context_builders import BaseContextBuilder, SimpleContextBuilder
from metagpt.exp_pool.manager import ExperienceManager, get_exp_manager
from metagpt.exp_pool.perfect_judges import BasePerfectJudge, SimplePerfectJudge
from metagpt.exp_pool.schema import Experience, Metric, QueryType, Score
from metagpt.exp_pool.schema import (
LOG_NEW_EXPERIENCE_PREFIX,
Experience,
Metric,
QueryType,
Score,
)
from metagpt.exp_pool.scorers import BaseScorer, SimpleScorer
from metagpt.exp_pool.serializers import BaseSerializer, SimpleSerializer
from metagpt.logs import logger
@ -173,6 +179,7 @@ class ExpCacheHandler(BaseModel):
exp = Experience(req=self._req, resp=self._resp, tag=self.tag, metric=Metric(score=self._score))
self.exp_manager.create_exp(exp)
self._log_exp(exp)
@staticmethod
def choose_wrapper(func, wrapped_func):
@ -215,3 +222,8 @@ class ExpCacheHandler(BaseModel):
return await self.func(*self.args, **self.kwargs)
return self.func(*self.args, **self.kwargs)
def _log_exp(self, exp: Experience):
log_entry = exp.model_dump_json(include={"uuid", "req", "resp", "tag"})
logger.debug(f"{LOG_NEW_EXPERIENCE_PREFIX}{log_entry}")

View file

@ -7,12 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field
from metagpt.config2 import Config
from metagpt.configs.exp_pool_config import ExperiencePoolRetrievalType
from metagpt.exp_pool.schema import (
DEFAULT_COLLECTION_NAME,
DEFAULT_SIMILARITY_TOP_K,
Experience,
QueryType,
)
from metagpt.exp_pool.schema import DEFAULT_SIMILARITY_TOP_K, Experience, QueryType
from metagpt.logs import logger
from metagpt.utils.exceptions import handle_exception
@ -36,7 +31,7 @@ class ExperienceManager(BaseModel):
_storage: Any = None
@property
def storage(self):
def storage(self) -> "SimpleEngine":
if self._storage is None:
logger.info(f"exp_pool config: {self.config.exp_pool}")
@ -44,6 +39,34 @@ class ExperienceManager(BaseModel):
return self._storage
@storage.setter
def storage(self, value):
self._storage = value
@property
def is_readable(self) -> bool:
return self.config.exp_pool.enabled and self.config.exp_pool.enable_read
@is_readable.setter
def is_readable(self, value: bool):
self.config.exp_pool.enable_read = value
# If set to True, ensure that enabled is also True.
if value:
self.config.exp_pool.enabled = True
@property
def is_writable(self) -> bool:
return self.config.exp_pool.enabled and self.config.exp_pool.enable_write
@is_writable.setter
def is_writable(self, value: bool):
self.config.exp_pool.enable_write = value
# If set to True, ensure that enabled is also True.
if value:
self.config.exp_pool.enabled = True
@handle_exception
def create_exp(self, exp: Experience):
"""Adds an experience to the storage if writing is enabled.
@ -52,10 +75,19 @@ class ExperienceManager(BaseModel):
exp (Experience): The experience to add.
"""
if not self.config.exp_pool.enabled or not self.config.exp_pool.enable_write:
self.create_exps([exp])
@handle_exception
def create_exps(self, exps: list[Experience]):
"""Adds multiple experiences to the storage if writing is enabled.
Args:
exps (list[Experience]): A list of experiences to add.
"""
if not self.is_writable:
return
self.storage.add_objs([exp])
self.storage.add_objs(exps)
self.storage.persist(self.config.exp_pool.persist_path)
@handle_exception(default_return=[])
@ -71,7 +103,7 @@ class ExperienceManager(BaseModel):
list[Experience]: A list of experiences that match the args.
"""
if not self.config.exp_pool.enabled or not self.config.exp_pool.enable_read:
if not self.is_readable:
return []
nodes = await self.storage.aretrieve(req)
@ -86,6 +118,15 @@ class ExperienceManager(BaseModel):
return exps
@handle_exception
def delete_all_exps(self):
"""Delete the all experiences."""
if not self.is_writable:
return
self.storage.clear(persist_dir=self.config.exp_pool.persist_path)
def get_exps_count(self) -> int:
"""Get the total number of experiences."""
@ -117,18 +158,14 @@ class ExperienceManager(BaseModel):
try:
from metagpt.rag.engines import SimpleEngine
from metagpt.rag.schema import (
BM25IndexConfig,
BM25RetrieverConfig,
LLMRankerConfig,
)
from metagpt.rag.schema import BM25IndexConfig, BM25RetrieverConfig
except ImportError:
raise ImportError("To use the experience pool, you need to install the rag module.")
persist_path = Path(self.config.exp_pool.persist_path)
docstore_path = persist_path / "docstore.json"
ranker_configs = [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)]
ranker_configs = self._get_ranker_configs()
if not docstore_path.exists():
logger.debug(f"Path `{docstore_path}` not exists, try to create a new bm25 storage.")
@ -163,28 +200,42 @@ class ExperienceManager(BaseModel):
try:
from metagpt.rag.engines import SimpleEngine
from metagpt.rag.schema import ChromaRetrieverConfig, LLMRankerConfig
from metagpt.rag.schema import ChromaRetrieverConfig
except ImportError:
raise ImportError("To use the experience pool, you need to install the rag module.")
retriever_configs = [
ChromaRetrieverConfig(
persist_path=self.config.exp_pool.persist_path,
collection_name=DEFAULT_COLLECTION_NAME,
collection_name=self.config.exp_pool.collection_name,
similarity_top_k=DEFAULT_SIMILARITY_TOP_K,
)
]
ranker_configs = [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)]
ranker_configs = self._get_ranker_configs()
storage = SimpleEngine.from_objs(retriever_configs=retriever_configs, ranker_configs=ranker_configs)
return storage
def _get_ranker_configs(self):
"""Returns ranker configurations based on the configuration.
If `use_llm_ranker` is True, returns a list with one `LLMRankerConfig`
instance. Otherwise, returns an empty list.
Returns:
list: A list of `LLMRankerConfig` instances or an empty list.
"""
from metagpt.rag.schema import LLMRankerConfig
return [LLMRankerConfig(top_n=DEFAULT_SIMILARITY_TOP_K)] if self.config.exp_pool.use_llm_ranker else []
_exp_manager = None
def get_exp_manager():
def get_exp_manager() -> ExperienceManager:
global _exp_manager
if _exp_manager is None:
_exp_manager = ExperienceManager()

View file

@ -2,14 +2,16 @@
import time
from enum import Enum
from typing import Optional
from uuid import UUID, uuid4
from pydantic import BaseModel, Field
MAX_SCORE = 10
DEFAULT_COLLECTION_NAME = "experience_pool"
DEFAULT_SIMILARITY_TOP_K = 2
LOG_NEW_EXPERIENCE_PREFIX = "New experience: "
class QueryType(str, Enum):
"""Type of query experiences."""
@ -68,6 +70,7 @@ class Experience(BaseModel):
tag: str = Field(default="", description="Tagging experience.")
traj: Optional[Trajectory] = Field(default=None, description="Trajectory.")
timestamp: Optional[float] = Field(default_factory=time.time)
uuid: Optional[UUID] = Field(default_factory=uuid4)
def rag_key(self):
return self.req

View file

@ -4,7 +4,9 @@
import json
import re
from pathlib import Path
import aiofiles
from unidiff import PatchSet
from metagpt.actions.action import Action
@ -16,6 +18,7 @@ from metagpt.ext.cr.utils.cleaner import (
from metagpt.ext.cr.utils.schema import Point
from metagpt.logs import logger
from metagpt.utils.common import parse_json_code_block
from metagpt.utils.report import EditorReporter
CODE_REVIEW_PROMPT_TEMPLATE = """
NOTICE
@ -193,16 +196,26 @@ class CodeReview(Action):
patched_file_path = patched_file.path
for c in comments_batch:
c["commented_file"] = patched_file_path
comments += comments_batch
comments.extend(comments_batch)
return comments
async def run(self, patch: PatchSet, points: list[Point]):
async def run(self, patch: PatchSet, points: list[Point], output_file: str):
patch: PatchSet = rm_patch_useless_part(patch)
patch: PatchSet = add_line_num_on_patch(patch)
result = []
comments = await self.cr_by_points(patch=patch, points=points)
async with EditorReporter(enable_llm_stream=True) as reporter:
log_cr_output_path = Path(output_file).with_suffix(".log")
await reporter.async_report(
{"src_path": str(log_cr_output_path), "filename": log_cr_output_path.name}, "meta"
)
comments = await self.cr_by_points(patch=patch, points=points)
log_cr_output_path.parent.mkdir(exist_ok=True, parents=True)
async with aiofiles.open(log_cr_output_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(comments, ensure_ascii=False, indent=2))
await reporter.async_report(log_cr_output_path)
if len(comments) != 0:
comments = self.format_comments(comments, points, patch)
comments = await self.confirm_comments(patch=patch, comments=comments, points=points)
@ -210,4 +223,14 @@ class CodeReview(Action):
if comment["code"]:
if not (comment["code"].isspace()):
result.append(comment)
async with EditorReporter() as reporter:
src_path = output_file
cr_output_path = Path(output_file)
await reporter.async_report(
{"type": "CodeReview", "src_path": src_path, "filename": cr_output_path.name}, "meta"
)
async with aiofiles.open(cr_output_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(comments, ensure_ascii=False, indent=2))
await reporter.async_report(cr_output_path)
return result

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,656 @@
[
{
"id": 1,
"text": "避免未使用的临时变量",
"language": "Java",
"detail": "缺陷类型避免未使用的临时变量对应FixerUnusedLocalVariableFixer修复方案删除未使用的临时变量",
"yes_example": "### 被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic String initCreationForm(Map<String, Object> model) {\n\t\tOwner owner = new Owner();\n\t\tmodel.put(\"owner\", owner);\n\t\tint unusedVar = 10;\n\t\treturn VIEWS_OWNER_CREATE_OR_UPDATE_FORM;\n\t}\n上述代码中unusedVar变量未被使用所以这个被判定为\"避免未使用的临时变量\"\n</例子1>\n<例子2>\nint unusedVariable = 10;\nSystem.out.println(\"Hello, World!\");\n这段代码的变量\"unusedVariable\"未被使用或者引用,所以这个不能判定为\"避免未使用的临时变量\"\n</例子2>",
"no_example": "### 不能被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map<String, Object> transientVariables) {\nthrow new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}\n这段代码的\"transientVariables\"是函数参数而不是临时变量虽然transientVariables没有被使用或者引用但是这个也不能判定为\"避免未使用的临时变量\"\n</例子1>\n\n<例子2>\npublic class TriggerCmd extends NeedsActiveExecutionCmd<Object> {\n protected Map<String, Object> transientVariables;\n public TriggerCmd(Map<String, Object> transientVariables) {\n this.transientVariables = transientVariables;\n }\n}\n上述代码中transientVariables不属于临时变量它是类属性且它在构造函数中被使用所以这个不能被判定为\"避免未使用的临时变量\"\n</例子2>"
},
{
"id": 2,
"text": "不要使用 System.out.println 去打印",
"language": "Java",
"detail": "缺陷类型:不要使用 System.out.println 去打印对应FixerSystemPrintlnFixer修复方案注释System.out.println代码",
"yes_example": "### 被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nSystem.out.println(\"Initializing new owner form.\");\n上述代码使用了\"System.out.println\"进行打印,所以这个被判定为\"不要使用 System.out.println 去打印\"\n</例子1>",
"no_example": "### 不能被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nthrow new IllegalStateException(\"There is no authenticated user, we need a user authenticated to find tasks\");\n上述代码是抛出异常的代码没有使用\"System.out.print\",所以这个不能被判定为\"不要使用 System.out.println 去打印\"\n</例子1>"
},
{
"id": 3,
"text": "避免函数中未使用的形参",
"language": "Java",
"detail": "缺陷类型:避免函数中未使用的形参;修复方案:忽略",
"yes_example": "### 被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map<String, Object> transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}这段代码中的形参\"transientVariables\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n</例子1>\n\n<例子2>\nprotected void modifyFetchPersistencePackageRequest(PersistencePackageRequest ppr, Map<String, String> pathVars) {}\n这段代码中的形参\"ppr\"和\"pathVars\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n</例子2>",
"no_example": "### 不能被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic String processFindForm(@RequestParam(value = \"pageNo\", defaultValue = \"1\") int pageNo) {\n\tlastName = owner.getLastName();\n\treturn addPaginationModel(pageNo, paginationModel, lastName, ownersResults);\n}这段代码中的形参\"pageNo\"在当前函数'processFindForm'内被'return addPaginationModel(pageNo, paginationModel, lastName, ownersResults);'这一句被使用虽然pageNo没有被用于逻辑计算但作为了函数调用其他函数的参数使用了所以这个不能被判定为\"避免函数中未使用的形参\"\n</例子1>\n<例子2>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的形参date在System.out.println(\"Formatted date: \" + sdf.format(date))这一句中被引用到,所以这个不能被判定为\"避免函数中未使用的形参\"\n</例子2>"
},
{
"id": 4,
"text": "if语句块不能为空",
"language": "Java",
"detail": "缺陷类型if 语句块不能为空对应FixerEmptyIfStmtFixer修复方案删除if语句块 或 适当的逻辑处理 或 注释说明为何为空",
"yes_example": "### 被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void emptyIfStatement() {\n\tif (getSpecialties().isEmpty()) {\n\t}\n}这段代码中的if语句块内容是空的所以这个被判定为\"if语句块不能为空\"\n</例子1>\n\n<例子2>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\t// judge persion if not null\n\t}\n}\n这段代码中的if语句块虽然有内容但是\"// judge persion if not null\"只是代码注释if语句块内并没有实际的逻辑代码所以这个被判定为\"if语句块不能为空\"\n</例子2>",
"no_example": "### 不能被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\treturn 0;\n\t}\n}这段代码中的if语句块里有内容且里面有非注释代码的逻辑代码\"return 0;\",所以这个不能被判定为\"if语句块不能为空\"\n</例子1>"
},
{
"id": 5,
"text": "循环体不能为空",
"language": "Java",
"detail": "缺陷类型循环体不能为空对应FixerEmptyStatementNotInLoopFixer修复方案删除对应while、for、foreach 循环体 或 添加适当的逻辑处理或者注释说明为何为空",
"yes_example": "### 被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t}\n}这段代码中的for循环体的内容是空的所以这个被判定为\"循环体不能为空\"\n</例子1>\n\n<例子2>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t// this is a code example\n\t}\n}这段代码中的while循环体的内容虽然不是空的但内容只是代码注释无逻辑内容所以这个被判定为\"循环体不能为空\"\n</例子2>\n\n<例子3>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t\n\t}\n}这段代码中的while循环体内容是空的所以这个被判定为\"循环体不能为空\"\n</例子3>",
"no_example": "### 不能被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t\ta = 1;\n\t\tif (a == 1) {\n\t\t\tretrun a;\n\t\t}\n\t}\n}上述代码的for循环体的内容不为空且内容不全是代码注释所以这个不能被判定为\"循环体不能为空\"\n</例子1>"
},
{
"id": 6,
"text": "避免使用 printStackTrace(),应该使用日志的方式去记录",
"language": "Java",
"detail": "缺陷类型:避免使用 printStackTrace(),应该使 用日志的方式去记录;修复方案:用日志的方式去记录",
"yes_example": "### 被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\te.printStackTrace();\n\t}\n}这段代码中的catch语句中使用了printStackTrace(),所以这个被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n</例子1>",
"no_example": "### 不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\tlogging.info(\"info\");\n\t}\n}这段代码的catch语句中使用的是日志记录的方式所以这个不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n</例子1>"
},
{
"id": 7,
"text": "catch 语句块不能为空",
"language": "Java",
"detail": "缺陷类型catch 语句块不能为空对应FixerEmptyCatchBlockFixer修复方案在catch里面添加注释",
"yes_example": "### 被判定为\"catch语句块不能为空\"的例子\n<例子1>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n \n}\n这段代码中的catch语句中没有内容所以这个被判定为\"catch语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n String str = null;\n str.length();\n} catch (NullPointerException e) {\n \n}这段代码中的catch语句中没有内容所以这个被判定为\"catch语句块不能为空\"\n</例子2>\n\n<例子3>\npublic class EmptyCatchExample {\n public static void main(String[] args) {\n try {\n // 尝试除以零引发异常\n int result = 10 / 0;\n } catch (ArithmeticException e) {\n \n }\n }\n}这段代码中的catch语句中没有内容所以这个被判定为\"catch语句块不能为空\"\n</例子3>\n<例子4>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n \n}这段代码中的catch语句中没有内容所以这个被判定为\"catch语句块不能为空\"\n</例子4>\n<例子5>\ntry {\n Object obj = \"string\";\n Integer num = (Integer) obj;\n} catch (ClassCastException e) {\n\t\n}这段代码中的catch语句中没有内容所以这个被判定为\"catch语句块不能为空\"\n</例子5>",
"no_example": "### 不能被判定为\"catch语句块不能为空\"的例子\n<例子1>\npersionNum = 1\ntry {\n\treturn True;\n} catch (Exception e) {\n\t// 如果人数为1则返回false\n\tif (persionNum == 1){\n\t\treturn False;\n\t}\n}这段代码的catch语句中不为空所以不能把这个被判定为\"catch语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n\tthrow new Exception(\"Fake exception\");\n} catch (Exception e) {\n\te.printStackTrace();\n}这段代码的catch语句中虽然只有\"e.printStackTrace();\"但确实不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n</例子2>"
},
{
"id": 8,
"text": "避免不必要的永真/永假判断",
"language": "Java",
"detail": "缺陷类型:避免不必要的永真/永假判断对应FixerUnconditionalIfStatement Fixer修复方案删除永真/永假判断逻辑",
"yes_example": "### 被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\npublic void someMethod() {\n\twhile (true) {\n\t}\n}这段代码中的\"while (true)\"是一个使用true做判断条件但是没有循环结束标记所以这个被判定为\"避免不必要的永真/永假判断\"\n</例子1>\n\n<例子2>\nif (true) {\n\tSystem.out.println(\"This is always true\");\n}这段代码中的\"if (true)\"是一个使用true条件做条件但是没有循环结束标记所以这个被判定为\"避免不必要的永真/永假判断\"\n</例子2>\n\n<例子3>\na = 1;\nwhile(a > 0){\n\ta = a + 1\n}这段代码初始化a=1是大于0的while循环体的逻辑是每次加1那么判断条件a > 0会永远是真的不会退出循环所以这个被判定为\"避免不必要的永真/永假判断\"\n<例子3>",
"no_example": "### 不能被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\na = 0;\nwhile (a < 5) {\n\ta = a + 1;\n}这段代码中的a<5是一个判断当执行了5次while语句中的逻辑a=a+1之后a会满足a < 5就会退出循环所以这个能被判定为\"避免不必要的永真/永假判断\"\n</例子1>"
},
{
"id": 9,
"text": "switch 中 default 必须放在最后",
"language": "Java",
"detail": "缺陷类型switch 中 default 必须放在最后对应FixerDefaultLabelNotLastInSwitchStmtFixer修复方案switch 中 default 放在最后",
"yes_example": "### 被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tcase 2:\n\t\tSystem.out.println(\"Number two\");\n\t\tbreak;\n}这段代码是一个switch语句但是里面的default没有放在最后所以这个被判定为\"switch 中 default 必须放在最后\"\n</例子1>",
"no_example": "### 不能被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\ncase 3:\n\tSystem.out.println(\"Number one\");\n\tbreak;\ncase 4:\n\tSystem.out.println(\"Number two\");\n\tbreak;\ndefault:\n\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\tbreak;\n}这段代码是一个switch语句且里面的default放在了最后所以这个不能被判定为\"switch 中 default 必须放在最后\"\n</例子1>"
},
{
"id": 10,
"text": "未使用equals()函数对 String 作比较",
"language": "Java",
"detail": "缺陷类型未使用equals()函数对 String 作比较对应FixerUnSynStaticDateFormatter Fixer修复方案使用equals()函数对 String 作比较",
"yes_example": "### 被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (existingPet != null && existingPet.getName() == petName) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中所涉及的existingPet.getName()和petName均是字符串但是在if语句里做比较的时候使用了==而没有使用equals()对string做比较所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子1>\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\" == isOk) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串但在if判断中与\"ok\"比较的时候使用的是==未使用equals()对string做比较应该使用\"ok\".equals(isOk),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子2>\n\n<例子3>\nString str1 = \"Hello\";\nString str2 = \"Hello\";\nif (str1 == str2) {\n\tSystem.out.println(\"str1 和 str2 引用相同\");\n} else {\n\tSystem.out.println(\"str1 和 str2 引用不同\");\n}\n这段代码中的if (str1 == str2) 使用了==进行str1和str2的比较未使用equals()对string做比较应该使用str1.equals(str2),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子3>\n\n<例子4>\nString str = \"This is string\";\nif (str == \"This is not str\") {\n\treturn str;\n}这段代码中的if (str == \"This is not str\")使用了==进行字符串比较未使用equals()对string做比较\"This is not str\".equals(str),所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子4>",
"no_example": "### 不能被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (PROPERTY_VALUE_YES.equalsIgnoreCase(readWriteReqNode))\n formProperty.setRequired(true);\n这段代码中的PROPERTY_VALUE_YES和readWriteReqNode均是字符串在if语句里比较PROPERTY_VALUE_YES和readWriteReqNode的使用的是equalsIgnoreCase字符串比较忽略大小写所以equalsIgnoreCase也是符合使用equals()函数对 String 作比较的,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n</例子1>\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\".equals(isOk)) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串在if判断中与\"ok\"比较的时候使用的是equals()对string做比较所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n</例子2>"
},
{
"id": 11,
"text": "禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象",
"language": "Java",
"detail": "缺陷类型:禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象 输出异常对应FixerConcatExceptionFixer修复方案使用占位符传递异常对象",
"yes_example": "### 被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\ntry {\n listenersNode = objectMapper.readTree(listenersNode.asText());\n} catch (Exception e) {\n LOGGER.info(\"Listeners node can not be read\", e);\n}这段代码中日志输出内容内容是直接使用字符串\"Listeners node can not be read\"拼接,日志输出异常时,应使用占位符输出异常信息,而不是直接使用字符串拼接,所以这个被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n</例子1>",
"no_example": "### 不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\nPersion persion = persionService.getPersion(1);\nif (persion == null){\n\tLOGGER.error(PERSION_NOT_EXIT);\n}这段代码中的PERSION_NOT_EXIT是一个用户自定义的异常常量代表persion不存在没有直接使用字符串\"persion not exit\"拼接,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n<例子1>\n\n<例子2>\ntry {\n a = a + 1;\n} catch (Exception e) {\n Persion persion = persionService.getPersion(1);\n LOGGER.info(persion);\n}这段代码中输出日志没有直接使用字符串拼接而是使用的Persion对象输出所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n</例子2>"
},
{
"id": 12,
"text": "finally 语句块不能为空",
"language": "Java",
"detail": "缺陷类型finally 语句块不能为空对应FixerEmptyFinallyBlockFixer修复方案删除空 finally 语句块",
"yes_example": "### 被判定为\"finally 语句块不能为空\"的例子\n<例子1>\ntry {\n\tPersion persion = persionService.getPersion(1);\n\treturn persion;\n} finally {\n\t\n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n\tSystem.out.println(\"Inside try block\");\n} finally {\n\t// 空的finally块没有任何语句这是一个缺陷\n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子2>\n\n<例子3>\ntry {\n int result = 10 / 0;\n} catch (ArithmeticException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子3>\n\n<例子4>\ntry {\n String str = null;\n System.out.println(str.length());\n} catch (NullPointerException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子4>\n\n<例子5>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n e.printStackTrace();\n} finally {\n // 只有注释的 finally 语句块\n // 这是一个空的 finally 块\n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子5>\n\n<例子6>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n e.printStackTrace();\n} finally {\n // 只有空行的 finally 语句块\n \n}这段代码中的finally语句块内没有内容所以这个被判定为\"finally 语句块不能为空\"\n</例子6>",
"no_example": "### 不能被判定为\"finally 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"finally 语句块不能为空\"\n</例子1>"
},
{
"id": 13,
"text": "try 语句块不能为空",
"language": "Java",
"detail": "缺陷类型try 语句块不能为空对应FixerEmptyTryBlockFixer修复方案删除整个 try 语句",
"yes_example": "### 被判定为\"try 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\n\t}\n\treturn null;\n}这段代码中的try语句块内没有内容所以这个被判定为\"try 语句块不能为空\"\n</例子1>\n\n<例子2>\npublic void demoFinallyBlock() {\n\ttry {\n\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的try语句块内没有内容所以这个被判定为\"try 语句块不能为空\"\n</例子2>\n\n<例子3>\ntry {\n \n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内没有内容所以这个被判定为\"try 语句块不能为空\"\n</例子3>\n\n<例子4>\ntry {\n // 只有注释的 try 语句块\n\t\n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内只有注释和空行也可以认定为这种情况是try语句块内没有内容所以这个被判定为\"try 语句块不能为空\"\n</例子4>",
"no_example": "### 不能被判定为\"try 语句块不能为空\"的例子\n<例子1>\ntry {\n\ta = a + 1;\n} catch (Exception e) {\n\te.printStackTrace();\n}\n这段代码中的try语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"try 语句块不能为空\"\n</例子1>"
},
{
"id": 14,
"text": "避免对象进行不必要的 NULL或者null 检查",
"language": "Java",
"detail": "缺陷类型:避免对象进行不必要的 NULL或者null 检查对应FixerLogicalOpNpeFixer修复方案删除对对象不必要的 NULL 检查的逻辑",
"yes_example": "### 被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\na = \"dog\";\nif (a != null){\n\treturn a;\n}这段代码中的对象a已经是确定的值\"dog\"所以if条件句的判断\"a != null\"是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子1>\n\n<例子2>\nif (authenticatedUserId != null && !authenticatedUserId.isEmpty() && userGroupManager!=null){\n\treturn authenticatedUserId;\n}这段代码中的\"authenticatedUserId != null\"和\"!authenticatedUserId.isEmpty()\"都是对\"authenticatedUserId\"的空判断,重复了,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子2>\n\n<例子3>\nList<Integer> list = new ArrayList<>();\nif (list != null) {\n list.add(1);\n}这段代码中的list已经被初始化不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子3>\n\n<例子4>\nif (this.type != null && this.type.getName() != null) {\n\tSystem.out.println(\"Type name is not null\");\n}这段代码中的对象type已经检查过非null再次检查getName()是否为null是不必要的所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n</例子4>\n\n<例子5>\nif (\"dog\".equals(null)){\n\treturn a;\n}这段代码中的\"dog\"是个确定的字符串不需要进行null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子5>\n\n<例子6>\nInteger num = 10;\nif (num != null) {\n System.out.println(num);\n}这段代码中的num 已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子6>",
"no_example": "### 不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\nCat cat = catService.get(1);\nif (cat != null){\n\tretrun cat;\n}这段代码中的对象\"cat\"是通过service获取到的不确定是否为空所以if条件句的判断的\"cat != null\"是必要的,所以这个不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子1>"
},
{
"id": 15,
"text": "避免 finally 块中出现 return",
"language": "Java",
"detail": "缺陷类型:避免 finally 块中出现 return修复方案无需修复",
"yes_example": "### 被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块内容包含\"return\",所以这个被判定为\"避免 finally 块中出现 return\"\n</例子1>",
"no_example": "### 不能被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\tLOGGER.info(PERSION_NOT_EXIT);\n\t}\n}这段代码中的finally语句块中内容不包含\"return\",所以这个不能被判定为\"避免 finally 块中出现 return\"\n</例子1>"
},
{
"id": 16,
"text": "避免空的 static 初始化",
"language": "Java",
"detail": "缺陷类型:避免空的 static 初始化对应FixerEmptyInitializerFixer修复方案删除整个空初始化块",
"yes_example": "### 被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class PetValidator implements Validator {\n\tstatic {\n\n\t}\n}这段代码中的static语句块没有内容是空的所以这个被判定为\"避免空的 static 初始化\"\n</例子1>\n\n<例子2>\npublic class Persion {\n\tstatic {\n\t\t// 初始化的静态块\n\t}\n}这段代码中的static语句块是有内容的不是空的但是static初始化语句块中只有注释代码没有实际的逻辑所以这个被判定为\"避免空的 static 初始化\"\n</例子2>",
"no_example": "### 不能被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class Cat {\n\tstatic {\n\t\t// 初始化的静态块\n\t\tcat = null;\n\t}\n}这段代码中的static语句块是有内容的不是空的且static初始化语句块中有非注释代码有实际的逻辑所以这个不能被判定为\"避免空的 static 初始化\"\n</例子1>"
},
{
"id": 17,
"text": "避免日历类用法不当风险",
"language": "Java",
"detail": "缺陷类型避免日历类用法不当风险修复方案使用Java 8 及以上版本中的 java.time 包的LocalDate",
"yes_example": "### 被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final Calendar calendar = new GregorianCalendar(2020, Calendar.JANUARY, 1);\n这段代码中的Calendar和GregorianCalendar是线程不安全的所以这个被判定为\"避免日历类用法不当风险\"\n</例子1>",
"no_example": "### 不能被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final LocalDate calendar = LocalDate.of(2020, 1, 1);\n这段代码中的LocalDate使用的是Java 8 及以上版本中的 java.time 包LocalDate 是不可变的并且是线程安全的,不会有线程安全和性能方面的问题,所以这个不能被判定为\"避免日历类用法不当风险\"\n</例子1>"
},
{
"id": 18,
"text": "使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()",
"language": "Java",
"detail": "缺陷类型使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()对应FixerClassCastExpWithToArrayF ixer修复方案使用集合的toArray(T[]array),且传入的是类型完全一样的数组",
"yes_example": "### 被判定为\"使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()\"的例子\n<例子1>\nList<String> stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nObject[] objectArray = stringList.toArray(new Object[5]);\n这段代码使用集合转数组的方法的时候使用了toArray(new Object[5]),但是传入的数组类型不一致,所以这个被判定为\"使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()\"\n</例子1>",
"no_example": "### 不能被判定为\"使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()\"的例子\n<例子1>\nList<String> stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nString[] stringArray = stringList.toArray(new String[stringList.size()]);\n这段代码使用集合转数组的方法的时候使用了toArray(new String[stringList.size()]),传入的是类型完全一样的数组,所以这个不能被判定为\"使用集合转数组的方法必须使用集合的toArray(T[]array)传入的是类型完全一样的数组大小就是list.size()\"\n</例子1>"
},
{
"id": 19,
"text": "禁止在 equals()中使用 NULL或者null 做比较",
"language": "Java",
"detail": "缺陷类型:禁止在 equals()中使用 NULL或者null 做比较对应FixerEqualsNullFixer修复方案使用Object的判空函数 做比较",
"yes_example": "### 被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\nif (\"test\".equals(null)) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码\"test\".equals(null)使用equals()函数与null进行了比较所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子1>\n\n<例子2>\nif (!rangeValues[1].equals(\"null\")) {\n\tmaxValue = new BigDecimal(rangeValues[1]);\n}这段代码中if条件中的代码!rangeValues[1].equals(\"null\")使用equals()函数与Nnull进行了比较所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子2>\n\n<例子3>\nString str1 = \"example\";\nif (str1.equals(\"null\")) {\n System.out.println(\"str1 is null\");\n}这段代码中if条件中的代码str1.equals(null)使用equals()函数与null进行了比较所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子3>\n\n<例子4>\nString str3 = \"example\";\nif (str3 != null && str3.equals(\"null\")) {\n System.out.println(\"str3 is null\");\n}这段代码中if条件中的代码str3.equals(\"null\")使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子4>\n\n<例子5>\nInteger num1 = 10;\nif (num1.equals(null)) {\n System.out.println(\"num1 is null\");\n}这段代码中if条件中的代码num1.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子5>\n\n<例子6>\nObject obj = new Object();\nif (obj.equals(null)) {\n System.out.println(\"obj is null\");\n}这段代码中if条件中的代码obj.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子6>",
"no_example": "### 不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\na = \"test\";\nif (a.equals(\"test\")) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码a.equals(\"test\")使用equals()函数与\"test\"进行了比较,所以这个不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子1>"
},
{
"id": 20,
"text": "switch 语句块不能为空",
"language": "Java",
"detail": "缺陷类型switch 语句块不能为空对应FixerEmptySwitchStatementsFix修复方案删除整个空 switch 语句块",
"yes_example": "### 被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\t\n}这段代码是一个switch语句块但是里面没有内容所以这个被判定为\"switch 语句块不能为空\"\n</例子1>\n\n<例子2>\nswitch (number) {\n\t// 这是一个switch语句块\n}这段代码是一个switch语句块里面虽然有内容但是内容仅仅是注释内容没有实际的逻辑所以这个被判定为\"switch 语句块不能为空\"\n</例子2>",
"no_example": "### 不能被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n}这段代码是一个switch语句块里面有内容而且内容里有非注释的代码有实际的逻辑所以这个不能被判定为\"switch 语句块不能为空\"\n</例子1>"
},
{
"id": 21,
"text": "在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开",
"detail": "缺陷类型:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开;修复方案:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。",
"language": "Java",
"yes_example": "### 被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int) 3.0;\n</例子1>\n<例子2>\nint b = (int) 4.0;\n</例子2>\n<例子3>\nlong a = (long) 5;\n</例子3>\n<例子4>\nstring a = (string) 3.5;\n</例子4>\n<例子5>\nPersion a = (Persion) \"zhangsan\";\n</例子5>",
"no_example": "### 不能被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int)3.0;\n</例子1>"
},
{
"id": 22,
"text": "方法参数在定义和传入时,多个参数逗号后面必须加空格",
"detail": "缺陷类型:方法参数在定义和传入时,多个参数逗号后面必须加空格;修复方案:方法参数在定义和传入时,多个参数逗号后面必须加空格。",
"language": "Java",
"yes_example": "### 被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a,int b,int c) {}\n</例子1>",
"no_example": "### 不能被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a, int b, int c) {}\n</例子1>"
},
{
"id": 23,
"text": "禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象",
"detail": "缺陷类型:禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象;修复方案:推荐使用 BigDecimal 的 valueOf 方法。",
"language": "Java",
"yes_example": "### 被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = new BigDecimal(0.1);\n</例子1>",
"no_example": "### 不能被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = BigDecimal.valueOf(0.1);\n</例子1>"
},
{
"id": 24,
"text": "不能有多余的分号",
"detail": "缺陷类型:多余的分号;修复方案:删除多余的分号",
"yes_example": "### 被判定为\"不能有多余的分号\"的例子\n<例子1>\npublic void trigger(String executionId, Map<String, Object> processVariables) {\n commandExecutor.execute(new TriggerCmd(executionId, processVariables));\n}\n;\na = 1;\nb = 2;\nsum = a + b;\n这段代码中包含一个多余的分号\";\",所以这个被判定为\"不能有多余的分号\"\n</例子1>",
"no_example": "### 不能被判定为\"不能有多余的分号\"的例子\n<例子1>\nwhile (True) {\n\ta = a + 1;\n\tbreak;\n}这段代码每个分号都是必须要的,所以这个能被判定为\"不能有多余的分号\"\n</例子1>"
},
{
"id": 25,
"text": "非线程安全的 SimpleDateFormat 使用必须在函数或代码块级别使用synchronized",
"detail": "缺陷类型:非线程安全的 SimpleDateFormat 使用修复方案在函数或代码块级别加上synchronized修饰 或 使用其他线程安全的方式",
"yes_example": "### 被判定为\"非线程安全的 SimpleDateFormat 使用必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的函数formatDate在未使用synchronized同步修饰的情况下使用了SimpleDateFormat这是线程不安全的所以这个被判定为\"非线程安全的 SimpleDateFormat 使用必须在函数或代码块级别使用synchronized\"\n</例子1>",
"no_example": "### 不能被判定为\"非线程安全的 SimpleDateFormat 使用必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic synchronized void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码是在synchronized同步块对函数'formatDate'进行保护,保证了线程安全,所以这个不能被判定为\"非线程安全的 SimpleDateFormat 使用必须在函数或代码块级别使用synchronized\"\n</例子1>"
},
{
"id": 26,
"text": "未按驼峰命名规范进行命名类名使用驼峰式UpperCamelCase风格, 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格",
"detail": "缺陷类型未按驼峰命名规范进行命名修复方案类名使用UpperCamelCase风格方法名、参数名、成员变量、局部变量使用lowerCamelCase风格。",
"language": "Java",
"yes_example": "### 被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class myClass {\n private int MyVariable;\n public void MyMethod() {}\n}\n这段代码中的类名、成员变量和方法名没有遵循驼峰命名法所以被判定为命名规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"未按驼峰命名规范进行命名\"的例子\n<例子1>\npublic class MyClass {\n private int myVariable;\n public void myMethod() {}\n}\n这段代码中的类名、成员变量和方法名都遵循了驼峰命名法所以不能被判定为命名规范问题。\n</例子1>"
},
{
"id": 27,
"text": "抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾",
"detail": "缺陷类型:命名规范;修复方案:抽象类命名使用 Abstract 或 Base 开头,异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾。",
"language": "Java",
"yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class MyAbstractClass {}\npublic class MyExceptionClass {}\npublic class TestMyClass {}\n这段代码中的抽象类、异常类和测试类的命名不符合规范所以被判定为命名规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic abstract class AbstractMyClass {}\npublic class MyCustomException extends Exception {}\npublic class MyClassTest {}\n这段代码中的抽象类、异常类和测试类的命名都符合规范所以不能被判定为命名规范问题。\n</例子1>"
},
{
"id": 28,
"text": "POJO 类中的任何布尔类型的变量,避免加\"is\" 前缀",
"detail": "缺陷类型命名规范修复方案POJO 类中的布尔类型变量不要加 is 前缀。",
"language": "Java",
"yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean isActive;\n}\n这段代码中的布尔类型变量加了 is 前缀,所以被判定为命名规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class User {\n private boolean active;\n}\n这段代码中的布尔类型变量没有加 is 前缀,所以不能被判定为命名规范问题。\n</例子1>"
},
{
"id": 29,
"text": "杜绝完全不规范的英文缩写,避免望文不知义。",
"detail": "缺陷类型:命名规范;修复方案:避免使用不规范的英文缩写,确保代码可读性。",
"language": "Java",
"yes_example": "### 被判定为\"命名规范\"的例子\n<例子1>\npublic class CfgMgr {\n private int cnt;\n}\n这段代码中的类名和变量名使用了不规范的英文缩写所以被判定为命名规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"命名规范\"的例子\n<例子1>\npublic class ConfigManager {\n private int count;\n}\n这段代码中的类名和变量名没有使用不规范的英文缩写所以不能被判定为命名规范问题。\n</例子1>"
},
{
"id": 30,
"text": "避免出现魔法字符和数字,应声明为常量",
"detail": "缺陷类型:避免出现魔法字符和数字,应声明为常量;修复方案:将魔法值定义为常量。",
"language": "Java",
"yes_example": "### 被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n public void calculate() {\n int result = 42 * 2;\n }\n}\n这段代码中直接使用了魔法值 42所以被判定为代码规范问题。\n</例子1>\n<例子2>\npublic class MagicNumberExample {\n public void calculate() {\n String result = \"This is a result\";\n }\n}\n这段代码中直接使用了魔法值 \"This is a result\",所以被判定为代码规范问题。\n</例子2>",
"no_example": "### 不能被判定为\"避免出现魔法字符和数字,应声明为常量\"的例子\n<例子1>\npublic class MagicNumberExample {\n private static final int MULTIPLIER = 42;\n public void calculate() {\n int result = MULTIPLIER * 2;\n }\n}\n这段代码中将魔法值定义为了常量所以不能被判定为代码规范问题。\n</例子1>"
},
{
"id": 31,
"text": "long 或 Long 赋值时,数值后使用大写 L不能是小写 l浮点数类型的数值后缀统一为大写的 D 或 F",
"detail": "缺陷类型代码规范修复方案long 或 Long 赋值时使用大写 L浮点数类型的数值后缀使用大写的 D 或 F。",
"language": "Java",
"yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000l;\n private double pi = 3.14d;\n}\n这段代码中使用了小写的 l 和 d所以被判定为代码规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000L;\n private double pi = 3.14D;\n}\n这段代码中使用了大写的 L 和 D所以不能被判定为代码规范问题。\n</例子1>"
},
{
"id": 32,
"text": "如果大括号内为空,简洁地写成{}即可大括号中间无需换行和空格如果是非空代码块1左大括号前不换行。2左大括号后换行。3右大括号前换行。4右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。",
"detail": "缺陷类型:代码格式;修复方案:遵循大括号的使用规范。",
"language": "Java",
"yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample{public void method(){\n if (true) {\n }}\n}\n这段代码中的大括号使用不符合规范所以被判定为代码格式问题。\n</例子1>",
"no_example": "### 不能被判定为\"代码格式\"的例子\n<例子1>\npublic class BracketExample {\n public void method() {\n if (true) {\n // do something\n }\n }\n}\n这段代码中的大括号使用符合规范所以不能被判定为代码格式问题。\n</例子1>"
},
{
"id": 33,
"text": "左小括号和右边相邻字符之间不需要空格;右小括号和左边相邻字符之间也不需要空格;而左大括号前需要加空格。",
"detail": "缺陷类型:代码格式;修复方案:遵循括号和空格的使用规范。",
"language": "Java",
"yes_example": "### 被判定为\"代码格式\"的例子\n<例子1>\npublic class SpaceExample {\n public void method (){\n }\n}\n这段代码中的括号和空格使用不符合规范所以被判定为代码格式问题。\n</例子1>",
"no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class SpaceExample {\n public void method() {}\n}\n这段代码中的括号和空格使用符合规范所以不能被判定为代码格式问题。\n</例子1>"
},
{
"id": 34,
"text": "if / for / while / switch / do 等保留字与左右括号之间都必须加空格。",
"detail": "缺陷类型:代码格式;修复方案:保留字与左右括号之间加空格。",
"language": "Java",
"yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if(true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间没有空格,所以被判定为代码格式问题。\n</例子1>",
"no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if (true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间有空格,所以不能被判定为代码格式问题。\n</例子1>"
},
{
"id": 35,
"text": "所有整型包装类对象之间值的比较,全部使用 equals 方法比较",
"detail": "缺陷类型:代码规范;修复方案:整型包装类对象之间的值比较使用 equals 方法。",
"language": "Java",
"yes_example": "### 被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a == b) {\n }\n }\n}\n这段代码中使用了 == 比较整型包装类对象,所以被判定为代码规范问题。\n</例子1>",
"no_example": "### 不能被判定为\"代码规范\"的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a.equals(b)) {\n }\n }\n}\n这段代码中使用了 equals 方法比较整型包装类对象,所以不能被判定为代码规范问题。\n</例子1>"
},
{
"id": 36,
"text": "BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法。",
"detail": "缺陷类型BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法;修复方案:使用 compareTo() 方法进行比较。",
"language": "Java",
"yes_example": "### 被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.equals(b)) {\n // 这段代码会返回 false因为 equals() 方法会比较精度\n}\n</例子1>",
"no_example": "### 不能被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.compareTo(b) == 0) {\n // 这段代码会返回 true因为 compareTo() 方法只比较数值\n}\n</例子1>"
},
{
"id": 37,
"text": "禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法。",
"detail": "缺陷类型POJO 类中存在重复的 getter 方法;修复方案:确保只存在一个 getter 方法。",
"language": "Java",
"yes_example": "### 被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private boolean active;\n public boolean isActive() {\n return active;\n }\n public boolean getActive() {\n return active;\n }\n}\n</例子1>",
"no_example": "### 不能被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private int age;\n public int getAge() {\n return age;\n }\n}\n</例子1>"
},
{
"id": 38,
"text": "日期格式化时,传入 pattern 中表示年份统一使用小写的 y。",
"detail": "缺陷类型:日期格式化错误;修复方案:使用小写的 y 表示年份。",
"language": "Java",
"yes_example": "### 被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"YYYY-MM-dd\");\n</例子1>",
"no_example": "### 不能被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n</例子1>"
},
{
"id": 39,
"text": "禁止在程序任何地方中使用1java.sql.Date 2java.sql.Time 3java.sql.Timestamp。",
"detail": "缺陷类型:使用了 java.sql 包中的日期类;修复方案:使用 java.time 包中的日期类。",
"language": "Java",
"yes_example": "### 被判定为\"禁止在程序任何地方中使用1java.sql.Date 2java.sql.Time 3java.sql.Timestamp\"的例子\n<例子1>\njava.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());\n</例子1>",
"no_example": "### 不能被判定为\"禁止在程序任何地方中使用1java.sql.Date 2java.sql.Time 3java.sql.Timestamp\"的例子\n<例子1>\njava.time.LocalDate localDate = java.time.LocalDate.now();\n</例子1>"
},
{
"id": 40,
"text": "判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。",
"detail": "缺陷类型:集合判空方式错误;修复方案:使用 isEmpty() 方法。",
"language": "Java",
"yes_example": "### 被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList<String> list = new ArrayList<>();\nif (list.size() == 0) {\n // 判空逻辑\n}\n</例子1>",
"no_example": "### 不能被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList<String> list = new ArrayList<>();\nif (list.isEmpty()) {\n // 判空逻辑\n}\n</例子1>"
},
{
"id": 41,
"text": "只要重写 equals就必须重写 hashCode。",
"detail": "缺陷类型:未重写 hashCode 方法;修复方案:同时重写 equals 和 hashCode 方法。",
"language": "Java",
"yes_example": "### 被判定为\"只要重写 equals就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n}\n</例子1>",
"no_example": "### 不能被判定为\"只要重写 equals就必须重写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n @Override\n public int hashCode() {\n return Objects.hash(name);\n }\n}\n</例子1>"
},
{
"id": 42,
"text": "使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。",
"detail": "缺陷类型:对 Map 的 keySet() / values() / entrySet() 返回的集合进行添加操作;修复方案:避免对这些集合进行添加操作。",
"language": "Java",
"yes_example": "### 被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap<String, String> map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet<String> keys = map.keySet();\nkeys.add(\"key2\");\n</例子1>",
"no_example": "### 不能被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap<String, String> map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet<String> keys = map.keySet();\n// 不进行添加操作\n</例子1>"
},
{
"id": 43,
"text": "不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator",
"detail": "缺陷类型:在 foreach 循环中进行元素的 remove / add 操作;修复方案:使用 iterator 进行元素的 remove 操作。",
"language": "Java",
"yes_example": "### 被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList<String> list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nfor (String s : list) {\n if (s.equals(\"a\")) {\n list.remove(s);\n }\n}\n</例子1>",
"no_example": "### 不能被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList<String> list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n String s = iterator.next();\n if (s.equals(\"a\")) {\n iterator.remove();\n }\n}\n</例子1>"
},
{
"id": 44,
"text": "类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式。",
"detail": "缺陷类型:注释不符合 Javadoc 规范;修复方案:使用 Javadoc 规范的注释格式。",
"language": "Java",
"yes_example": "### 被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\npublic class Example {\n // 这是一个类注释\n private String name;\n // 这是一个属性注释\n public String getName() {\n return name;\n }\n // 这是一个方法注释\n}\n</例子1>",
"no_example": "### 不能被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\n/**\n * 这是一个类注释\n */\npublic class Example {\n /**\n * 这是一个属性注释\n */\n private String name;\n /**\n * 这是一个方法注释\n */\n public String getName() {\n return name;\n }\n}\n</例子1>"
},
{
"id": 45,
"text": "所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释",
"detail": "缺陷类型:所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释;修复方案:为所有的抽象方法(包括接口中的方法)添加 Javadoc 注释,除了返回值、参数异常说明外,还必须指出该方法做什么事情,实现什么功能。",
"language": "Java",
"yes_example": "### 被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\npublic interface MyInterface {\n void doSomething();\n}\n这段代码中的接口方法 doSomething() 没有 Javadoc 注释,所以被判定为缺少 Javadoc 注释。\n</例子1>",
"no_example": "### 不能被判定为\"所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释\"的例子\n<例子1>\n/**\n * 执行某个操作\n * @param param 参数说明\n * @return 返回值说明\n * @throws Exception 异常说明\n */\npublic interface MyInterface {\n void doSomething(String param) throws Exception;\n}\n这段代码中的接口方法 doSomething() 有完整的 Javadoc 注释,所以不能被判定为缺少 Javadoc 注释。\n</例子1>"
},
{
"id": 46,
"text": "方法内部单行注释和多行注释的使用规范",
"detail": "缺陷类型:注释使用不规范;修复方案:方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。",
"language": "Java",
"yes_example": "### 被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n int a = 1; // 初始化变量a\n int b = 2; /* 初始化变量b */\n}\n这段代码中的单行注释和多行注释没有按照规范使用所以被判定为注释使用不规范。\n</例子1>",
"no_example": "### 不能被判定为\"注释使用不规范\"的例子\n<例子1>\npublic void exampleMethod() {\n // 初始化变量a\n int a = 1;\n /*\n * 初始化变量b\n */\n int b = 2;\n}\n这段代码中的单行注释和多行注释按照规范使用所以不能被判定为注释使用不规范。\n</例子1>"
},
{
"id": 47,
"text": "所有的枚举类型字段必须要有注释",
"detail": "缺陷类型:枚举类型字段缺少注释;修复方案:为所有的枚举类型字段添加注释,说明每个数据项的用途。",
"language": "Java",
"yes_example": "### 被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n ACTIVE,\n INACTIVE\n}\n这段代码中的枚举类型字段没有注释所以被判定为枚举类型字段缺少注释。\n</例子1>",
"no_example": "### 不能被判定为\"枚举类型字段缺少注释\"的例子\n<例子1>\npublic enum Status {\n /**\n * 活跃状态\n */\n ACTIVE,\n /**\n * 非活跃状态\n */\n INACTIVE\n}\n这段代码中的枚举类型字段有注释所以不能被判定为枚举类型字段缺少注释。\n</例子1>"
},
{
"id": 48,
"text": "finally 块必须对资源对象、流对象进行关闭",
"detail": "缺陷类型:资源对象、流对象未在 finally 块中关闭;修复方案:在 finally 块中对资源对象、流对象进行关闭,有异常也要做 try-catch。",
"language": "Java",
"yes_example": "### 被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n }\n}\n这段代码中的 FileInputStream 对象没有在 finally 块中关闭,所以被判定为资源对象、流对象未在 finally 块中关闭。\n</例子1>",
"no_example": "### 不能被判定为\"资源对象、流对象未在 finally 块中关闭\"的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n if (fis != null) {\n try {\n fis.close();\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n }\n}\n这段代码中的 FileInputStream 对象在 finally 块中关闭,所以不能被判定为资源对象、流对象未在 finally 块中关闭。\n</例子1>"
},
{
"id": 49,
"text": "常量命名应该全部大写,单词间用下划线隔开",
"detail": "缺陷类型:常量命名不规范;修复方案:常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。",
"language": "Java",
"yes_example": "### 被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int maxCount = 100;\n</例子1>",
"no_example": "### 不能被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int MAX_COUNT = 100;\n</例子1>"
},
{
"id": 50,
"text": "任何二目、三目运算符的左右两边都需要加一个空格",
"detail": "缺陷类型:运算符两边缺少空格;修复方案:任何二目、三目运算符的左右两边都需要加一个空格。",
"language": "Java",
"yes_example": "### 被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a=b+c;\n</例子1>",
"no_example": "### 不能被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a = b + c;\n</例子1>"
},
{
"id": 51,
"text": "避免使用from <module> import *",
"detail": "缺陷类型避免使用from <module> import *,导入所有内容会造成命名冲突;修复方案:每个使用到的子依赖需分别导入。",
"language": "Python",
"yes_example": "### 被判定为\"避免使用from <module> import *\"的例子\n<例子1>from math import * \n</例子1>",
"no_example": "### 不能被判定为\"避免使用from <module> import *\"的例子\n<例子1>from math import sqrt, pi \n</例子1>"
},
{
"id": 52,
"text": "避免使用__import__()函数动态导入模块",
"detail": "缺陷类型避免使用__import__()函数动态导入模块修复方案使用标准的import语句。",
"language": "Python",
"yes_example": "### 被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>module = __import__('math') \n</例子1>",
"no_example": "### 不能被判定为\"使用__import__()函数动态导入模块\"的例子\n<例子1>import math \n</例子1>"
},
{
"id": 53,
"text": "导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组",
"detail": "缺陷类型:导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组;修复方案:按顺序分组导入语句。",
"language": "Python",
"yes_example": "### 被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>\nimport numpy as np\nimport os\nimport sys\nfrom my_local_module import my_function\n在这个样例中先导入了第三方库然后导入了标准库。\n</例子1>\n<例子2>\nfrom my_project import my_local_function\nimport datetime\nimport requests\n在这个样例中先导入了本地模块然后导入了标准库。\n</例子2>\n<例子3>\nimport os\nfrom my_project.local_module import some_function\nimport pandas as pd\nimport sys\nfrom another_local_module import another_function\nimport math\n在这个样例中导入语句完全混乱没有遵循任何顺序。\n</例子3>\n<例子4>\nimport os\nimport requests\nimport sys\nimport numpy as np\nfrom local_package import local_module\n在这个样例中导入标准库和第三方库交替进行。\n</例子4>",
"no_example": "### 不能被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>import os \n\n import requests \n\n import mymodule \n</例子1>"
},
{
"id": 54,
"text": "避免未使用的函数形参",
"detail": "缺陷类型:避免未使用的函数形参;修复方案:移除未使用的函数形参。",
"language": "Python",
"yes_example": "### 被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a, b): \n return a</例子1>\n<例子2>def start_game(unused_param): \npuzzle = Puzzle() \npuzzle.solve()</例子2>\n<例子3>def make_move(self, board):\npass \n</例子3>\n<例子4>def move(self, direction):\npass \n</例子4>",
"no_example": "### 不能被判定为'避免未使用的函数形参'的例子\n<例子1>def func(a): \n return a</例子1>"
},
{
"id": 55,
"text": "使用is not None来检查一个变量是否不是None",
"detail": "缺陷类型未使用is not None来检查一个变量是否不是None修复方案使用is not None来检查。",
"language": "Python",
"yes_example": "### 被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable != None:\n pass</例子1>",
"no_example": "### 不能被判定为'未使用is not None来检查一个变量是否不是None'的例子\n<例子1>if variable is not None:\n pass</例子1>"
},
{
"id": 56,
"text": "避免使用==或!=来比较对象实例的等价性",
"detail": "缺陷类型:使用==或!=来比较对象实例的等价性修复方案应使用equals比较。",
"language": "Python",
"yes_example": "### 被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1 == obj2: \n pass\n</例子1>",
"no_example": "### 不能被判定为'使用==或!=来比较对象实例的等价性'的例子\n<例子1>obj1 = MyClass() \n obj2 = MyClass() if obj1.equals(obj2): \n pass\n</例子1>\n<例子2>obj1 = 21 \n obj2 = 22 \n if obj1.equals(obj2):\n pass</例子2>"
},
{
"id": 57,
"text": "避免使用单字母变量名,使用描述性变量名",
"detail": "缺陷类型:避免使用单字母变量名,使用描述性变量名;修复方案:使用描述性变量名。",
"language": "Python",
"yes_example": "### 被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>x = 10 \n</例子1>\n<例子2>y = 10 \n</例子2>",
"no_example": "### 不能被判定为'避免使用单字母变量名,使用描述性变量名'的例子\n<例子1>count = 10 \n</例子1>"
},
{
"id": 58,
"text": "常量命名使用全大写字母,并用下划线分隔",
"detail": "缺陷类型:常量命名未使用全大写字母或未用下划线分隔;修复方案:常量命名使用全大写字母,并用下划线分隔。",
"language": "Python",
"yes_example": "### 被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>pi = 3.14159</例子1>",
"no_example": "### 不能被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>PI = 3.14159</例子1>\n<例子2>max_size = 1 \n max_size += 1</例子2>"
},
{
"id": 59,
"text": "类名应使用驼峰式命名CamelCase",
"detail": "缺陷类型:类名未使用驼峰式命名;修复方案:类名使用驼峰式命名。",
"language": "Python",
"yes_example": "### 被判定为'类名未使用驼峰式命名CamelCase'的例子\n<例子1>class my_class: \n pass</例子1>\n<例子2>class my_class: \n def solve(self):\n pass</例子2>",
"no_example": "### 不能被判定为'类名未使用驼峰式命名CamelCase'的例子\n<例子1>class MyClass: \n pass</例子1>"
},
{
"id": 60,
"text": "尽量使用with语句来管理资源",
"detail": "缺陷类型未使用with语句来管理资源修复方案使用with语句来管理资源。",
"language": "Python",
"yes_example": "### 被判定为'未使用with语句来管理资源'的例子\n<例子1>file = open('file.txt', 'r') \n content = file.read() \n file.close()</例子1>",
"no_example": "### 不能被判定为'未使用with语句来管理资源'的例子\n<例子1>with open('file.txt', 'r') as file: \n content = file.read()</例子1>"
},
{
"id": 61,
"text": "避免使用except 或 通用的Exception来捕获所有异常应该指定异常类型",
"detail": "缺陷类型:捕获所有异常;修复方案:指定具体的异常类型。",
"language": "Python",
"yes_example": "### 被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except: \n handle_error()</例子1>\n### 被判定为'抛出通用的Exception异常'的例子\n<例子2>\n try:\n process_data(data) \n except: \n raise Exception('An error occurred') \n </例子2>",
"no_example": "### 不能被判定为'使用except:来捕获所有异常'的例子\n<例子1>try: \n # some code \n except ValueError: \n handle_value_error()</例子1>"
},
{
"id": 62,
"text": "尽量避免手动拼接字符串",
"detail": "缺陷类型手动拼接字符串修复方案使用格式化字符串或join方法。",
"language": "Python",
"yes_example": "### 被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = 'Hello, ' + name + '!' \n</例子1> \n <例子2>greeting = '2048' + 'game' \n</例子2> \n <例子3>pygame.display.set_caption('贪吃蛇' + '游戏')</例子3>",
"no_example": "### 不能被判定为'手动拼接字符串'的例子\n<例子1>\n name = 'John' \n greeting = f'Hello, {name}!' \n</例子1>"
},
{
"id": 63,
"text": "避免出现魔法字符和数字,应声明为常量",
"detail": "缺陷类型:使用魔法字符和数字;修复方案:将其声明为常量。",
"language": "Python",
"yes_example": "### 被判定为'出现魔法字符和数字'的例子\n<例子1>\n if status == 1: \n print('Active')' \n</例子1>\n<例子2>\n self.board = [[0] * 4 for _ in range(4)] \n self.score = 0</例子2>\n<例子3>\ndef __init__(self, width=10, height=10, mines=15):\n</例子3>\n<例子4>\nx, y = event.x // 20, event.y // 20\n</例子4>\n<例子5>\nraise ValueError(\"余额不足\")\n</例子5>\n<例子6>\ntransfer(bank, \"123\", \"456\", 200)\n</例子6>\n<例子7>\nbank.add_account(Account(\"123\", 1000))\n</例子7>",
"no_example": "### 不能被判定为'出现魔法字符和数字'的例子\n<例子1>\n ACTIVE_STATUS = 1 \n if status == ACTIVE_STATUS:\n print(ACTIVE_STATUS)' \n</例子1>"
},
{
"id": 64,
"text": "boolean变量判断无需显式比较",
"detail": "缺陷类型显式比较boolean变量修复方案直接使用boolean变量进行判断。",
"language": "Python",
"yes_example": "### 被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag == True: \n print('Flag is true')</例子1>\n<例子2>if self.game.is_game_over() == True: \n return</例子2><例子3>if self.canvas.drawings ==True:</例子3>",
"no_example": "### 不能被判定为'显式比较boolean变量'的例子\n<例子1>flag = True \n if flag: \n print('Flag is true') \n</例子1>"
},
{
"id": 65,
"text": "避免使用type()检查对象类型",
"detail": "缺陷类型避免使用type()检查对象类型修复方案使用isinstance()函数。",
"language": "Python",
"yes_example": "### 被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if type(obj) == list: \n print('obj is a list')</例子1>",
"no_example": "### 不能被判定为'避免使用type()检查对象类型'的例子\n<例子1>\n if isinstance(obj, list): \n print('obj is a list') \n</例子1>"
},
{
"id": 66,
"text": "避免使用os.system()来调用外部命令",
"detail": "缺陷类型使用os.system()调用外部命令修复方案使用subprocess模块。",
"language": "Python",
"yes_example": "### 被判定为'使用os.system()来调用外部命令'的例子\n<例子1>os.system('ls -l')</例子1>\n<例子2>os.system('ls -l')</例子2>",
"no_example": "### 不能被判定为'使用os.system()来调用外部命令'的例子\n<例子1>import subprocess \n subprocess.run(['ls', '-l'])</例子1>"
},
{
"id": 67,
"text": "只使用@property装饰器创建只读属性而非修改属性",
"detail": "缺陷类型:使用@property装饰器创建可修改属性修复方案只使用@property装饰器创建只读属性。",
"language": "Python",
"yes_example": "### 被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self, new_value): \n self._value = new_value</例子1>\n<例子2>@property \n def game_over(self): \n return self._is_game_over() \n def _is_game_over(self): \n pass</例子2>",
"no_example": "### 不能被判定为'使用@property装饰器来创建可修改属性'的例子\n<例子1>@property \n def value(self): \n return self._value</例子1>\n<例子2>@property \n def __str__(self): \n return 'Maze Game State'</例子2>"
},
{
"id": 68,
"text": "在使用索引或切片时,不要在方括号或冒号内加空格",
"detail": "缺陷类型:在索引或切片的方括号或冒号内加空格;修复方案:去掉方括号或冒号内的空格。",
"language": "Python",
"yes_example": "### 被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[ 1 : 3 ]</例子1>\n<例子2>start_point = self.canvas.drawings[ -1] </例子2>\n<例子3>if head[ 0] < 0 or head[ 0] >= GRID_WIDTH or head[ 1] < 0 or head[ 1] >= GRID_HEIGHT:</例子3>\n<例子4>for segment in self.snake[ 1:]:</例子4>",
"no_example": "### 不能被判定为'在使用索引或切片时,在方括号或冒号内加空格'的例子\n<例子1>list = [1, 2, 3, 4] \n sublist = list[1:3]</例子1>"
},
{
"id": 69,
"text": "在逗号、分号或冒号前不要加空格,但在它们之后要加空格",
"detail": "缺陷类型:在逗号、分号或冒号前加空格或在它们之后不加空格;修复方案:在逗号、分号或冒号前不要加空格,但在它们之后要加空格。",
"language": "Python",
"yes_example": "### 被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4 : \n print(x , y)</例子1>\n<例子2>if event.keysym == 'Up' or event.keysym == 'Down' or event.keysym == 'Left' or event.keysym == 'Right' :</例子2>\n<例子3>x ,y = 1 ,2</例子3>\n<例子4>def on_key_press(self , event) :</例子4>\n<例子5>elif event.keysym == 'Down' ; </例子5>\n<例子6>def update_status(self ,message: str) : \n pass </例子6>",
"no_example": "### 不能被判定为'在逗号、分号或冒号前加空格,或没在它们之后加空格'的例子\n<例子1>if x == 4: \n print(x, y)</例子1>"
},
{
"id": 70,
"text": "对于二元操作符,两边都应有空格",
"detail": "缺陷类型:二元操作符两边没有空格;修复方案:在二元操作符两边加空格",
"language": "Python",
"yes_example": "### 被判定为'二元操作符两边没有空格'的例子\n<例子1>a=b+1</例子1>",
"no_example": "### 不能被判定为'二元操作符两边没有空格'的例子\n<例子1>a = b + 1</例子1>\n<例子2>label = tk.Label(self.root, text=str(cell), bg='white')</例子2>\n<例子3>label.grid(row=i, column=j)</例子3>"
},
{
"id": 71,
"text": "避免使用Python关键字作为变量名或函数名",
"detail": "缺陷类型使用Python关键字作为变量名或函数名修复方案使用非关键字的名称。",
"language": "Python",
"yes_example": "### 被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def class(): \n pass</例子1>\n<例子2>for = 5</例子2>\n<例子3>def if(self): </例子3>",
"no_example": "### 不能被判定为'使用Python关键字作为变量名或函数名'的例子\n<例子1>def my_function(): \n pass</例子1>\n<例子2>number = 5</例子2>"
},
{
"id": 72,
"text": "避免使用特殊字符作为变量名/方法名/类名,例如$或@",
"detail": "缺陷类型:使用特殊字符作为变量名/方法名/类名;修复方案:使用合法的变量名。",
"language": "Python",
"yes_example": "### 被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my$var = 10</例子1>\n<例子2>@var = 20</例子2>\n<例子3>def add_score@(self, points): \n self.score += points</例子3>\n<例子4>class @MyClass: \n pass</例子4>\n<例子5>def mine@(self):</例子5>",
"no_example": "### 不能被判定为'使用特殊字符作为变量名/方法名/类名,例如$或@'的例子\n<例子1>my_var = 10</例子1>\n<例子2>var_20 = 20</例子2>"
},
{
"id": 73,
"text": "避免使用raise来重新抛出当前的异常这会丢失原始的栈跟踪",
"detail": "缺陷类型使用raise重新抛出当前异常修复方案使用raise ... from ...语法。",
"language": "Python",
"yes_example": "### 被判定为'避免使用raise来重新抛出当前的异常这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n raise SomeException('新的异常信息')\n</例子1>\n<例子2>\ntry:\n db.get_data()\nexcept ValueError as e:\n raise ValueError(\"Something went wrong!\")\n</例子2>\n<例子3>\ntry:\n\traise Exception(\"形状添加失败\")\nexcept Exception as e:\n\tpass\n</例子3>",
"no_example": "### 不能被判定为'避免使用raise来重新抛出当前的异常这会丢失原始的栈跟踪'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n raise RuntimeError('Error occurred') from e \n</例子1>\n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n\tlogger.error(e)\n raise \n</例子2>"
},
{
"id": 74,
"text": "避免在except块中使用pass这会捕获并忽略异常",
"detail": "缺陷类型在except块中使用pass修复方案处理异常或记录日志。",
"language": "Python",
"yes_example": "### 被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n </例子1>\n<例子2>\n try: \n 1 / 0 \n except ZeroDivisionError: \n pass \n</例子2>",
"no_example": "### 不能被判定为'在except块中使用pass'的例子\n<例子1>\n try: \n 1 / 0 \n except ZeroDivisionError as e: \n logging.error('Error occurred: %s', e) \n</例子1>"
},
{
"id": 75,
"text": "避免使用assert语句来执行重要的运行时检查",
"detail": "缺陷类型使用assert语句执行重要的运行时检查修复方案使用显式的条件检查和异常处理。",
"language": "Python",
"yes_example": "### 被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n assert b != 0 \n return a / b \n</例子1>",
"no_example": "### 不能被判定为'使用assert语句来执行重要的运行时检查'的例子\n<例子1>\n def divide(a, b): \n if b == 0: \n raise ValueError('b cannot be zero') \n return a / b \n</例子1>"
},
{
"id": 76,
"text": "避免使用eval()和exec(),这些函数可能会带来安全风险",
"detail": "缺陷类型使用eval()和exec()函数;修复方案:使用安全的替代方案。",
"language": "Python",
"yes_example": "### 被判定为'使用eval()和exec()'的例子\n<例子1>\n eval('print(1)') \n</例子1>\n<例子2> \n exec('a = 1') \n</例子2>",
"no_example": "### 不能被判定为'使用eval()和exec()'的例子\n<例子1>\n compiled_code = compile('print(1)', '<string>', 'exec') \n exec(compiled_code) \n</例子1>"
},
{
"id": 77,
"text": "避免使用sys.exit(),应使用异常来控制程序的退出",
"detail": "缺陷类型避免使用sys.exit(),应使用异常来控制程序的退出;修复方案:使用异常来控制程序的退出。",
"language": "Python",
"yes_example": "### 被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n import sys\nsys.exit(1)\n</例子1>\n<例子2>\n import sys \n sys.exit()\n</例子2>\n<例子3>\nif event.type == pygame.QUIT:\n\tpygame.quit()\n\texit()\n</例子3>\n<例子4>\n import sys \n sys.exit('退出程序'))\n</例子4>",
"no_example": "### 不能被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\n raise SystemExit(1)\n</例子1>"
},
{
"id": 78,
"text": "避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件",
"detail": "缺陷类型使用time.sleep()进行线程同步;修复方案:使用同步原语。",
"language": "Python",
"yes_example": "### 被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import time \n\n def worker(): \n time.sleep(1) \n</例子1>\n<例子2>\n import time \n\n time.sleep(1) \n</例子2>",
"no_example": "### 不能被判定为'使用time.sleep()进行线程同步'的例子\n<例子1>\n import threading \n\n event = threading.Event() \n\n def worker(): \n event.wait()\n</例子1>"
},
{
"id": 79,
"text": "每行代码避免超过79个字符",
"detail": "缺陷类型每行代码避免超过79个字符修复方案将长行代码格式化为多行。",
"language": "Python",
"yes_example": "### 被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit........') \n</例子1>",
"no_example": "### 不能被判定为'每行代码避免超过79个字符'的例子\n<例子1>\n print('This is a very long line of code that exceeds the 79 characters limit' + \n ' but it is split into two lines')\n</例子1>"
},
{
"id": 80,
"text": "模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔",
"detail": "缺陷类型:模块级别的函数和类定义之间没有用两个空行分隔,类内部的方法定义之间没有用一个空行分隔;修复方案:按照规范添加空行。",
"language": "Python",
"yes_example": "### 被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n def func2(): \n pass \n</例子1>\n<例子2>\n class MyClass: \n def method1(self): \n pass \n def method2(self): \n pass \n</例子2>",
"no_example": "### 不能被判定为'模块级别的函数和类定义之间没用两个空行分隔,类内部的方法定义之间没用一个空行分隔'的例子\n<例子1>\n def func1(): \n pass \n\n\n def func2(): \n pass \n</例子1>\n<例子2>\n class MyClass: \n def method1(self): \n pass \n\n def method2(self): \n pass \n</例子2>"
},
{
"id": 81,
"text": "使用小写字母和下划线分隔的方式命名变量和函数名",
"detail": "缺陷类型:变量和函数命名不符合小写字母和下划线分隔的方式;修复方案:使用小写字母和下划线分隔的方式命名。",
"language": "Python",
"yes_example": "### 被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def myFunction(): \n pass \n</例子1>\n<例子2>\n myVariable = 10 \n</例子2>\n<例子3>\n def Calculatesquareroot(self, x): \n return 1 \n</例子3>",
"no_example": "### 不能被判定为'未使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\n def my_function(): \n pass \n</例子1>\n<例子2>\n my_variable = 10 \n</例子2>"
},
{
"id": 82,
"text": "不允许使用print()函数来记录日志使用logging模块等来记录日志",
"detail": "缺陷类型使用print()函数记录日志修复方案使用logging模块记录日志。",
"language": "Python",
"yes_example": "### 被判定为'使用print()函数来记录日志'的例子\n<例子1>\n print('Error occurred') \n</例子1>\n<例子2>\n print('打印的日志字符串内容') \n</例子2>\n<例子3>\n task = 'xxx' \n print(task) \n</例子3>\n<例子4>\n print(1)\n</例子4>",
"no_example": "### 不能被判定为'使用print()函数来记录日志'的例子\n<例子1>\n import logging \n logging.error('Error occurred') \n</例子1>"
}
]

View file

@ -16,7 +16,7 @@ import time
from datetime import datetime, timedelta
from operator import itemgetter
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from typing import Optional
from pydantic import ConfigDict, Field, field_validator, model_validator
@ -27,6 +27,7 @@ from metagpt.environment.stanford_town.env_space import (
EnvObsParams,
EnvObsType,
)
from metagpt.environment.stanford_town.stanford_town_env import StanfordTownEnv
from metagpt.ext.stanford_town.actions.dummy_action import DummyAction, DummyMessage
from metagpt.ext.stanford_town.actions.inner_voice_action import (
AgentWhisperThoughtAction,
@ -49,28 +50,15 @@ from metagpt.roles.role import Role, RoleContext
from metagpt.schema import Message
from metagpt.utils.common import any_to_str
if TYPE_CHECKING:
from metagpt.environment.stanford_town.stanford_town_env import ( # noqa: F401
StanfordTownEnv,
)
class STRoleContext(RoleContext):
model_config = ConfigDict(arbitrary_types_allowed=True)
env: "StanfordTownEnv" = Field(default=None, exclude=True)
env: StanfordTownEnv = Field(default=None, exclude=True)
memory: AgentMemory = Field(default_factory=AgentMemory)
scratch: Scratch = Field(default_factory=Scratch)
spatial_memory: MemoryTree = Field(default_factory=MemoryTree)
@classmethod
def model_rebuild(cls, **kwargs):
from metagpt.environment.stanford_town.stanford_town_env import ( # noqa: F401
StanfordTownEnv,
)
super(RoleContext, cls).model_rebuild(**kwargs)
class STRole(Role):
# add a role's property structure to store role's age and so on like GA's Scratch.
@ -635,6 +623,3 @@ class STRole(Role):
time.sleep(0.5)
return DummyMessage()
STRoleContext.model_rebuild()

View file

@ -1,6 +1,6 @@
from metagpt.prompts.di.role_zero import ROLE_INSTRUCTION
EXTRA_INSTRUCTION = """
EXTRA_INSTRUCTION_DEPRECATED = """
4. Each time you write a code in your response, write with the Editor directly without preparing a repetitive code block beforehand.
5. Take on ONE task and write ONE code file in each response. DON'T attempt all tasks in one response.
6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path.
@ -10,7 +10,7 @@ EXTRA_INSTRUCTION = """
10. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.
11. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.
12. To modify code in a file, read the entire file, make changes, and update the file with the complete code, ensuring that no line numbers are included in the final write.
13. When a system design or project schedule is provided, at the end of the plan, add a CodeRview Task for each file; for example, if there are three files, add three CodeRview Tasks. For each CodeRview Task, just call ValidateAndRewriteCode.run.
13. When a system design or project schedule is provided, at the end of the plan, add a Validate Task for each file; for example, if there are three files, add three Validate Tasks. For each Validate Task, just call ValidateAndRewriteCode.run.
14. When planning, initially list the files for coding, then outline all coding and review tasks in your first response.
15. Note 'Task for {file_name} completed.' signifies the {file_name} coding task is done.
16. Avoid re-reviewing or re-coding the same code. When you decide to take a write or review action, include the command 'finish current task' in the same response.
@ -18,5 +18,39 @@ EXTRA_INSTRUCTION = """
18. If you plan to read a file, do not include other plans in the same response.
"""
EXTRA_INSTRUCTION = """
6. When not specified, you should write files in a folder named "src". If you know the project path, then write in a "src" folder under the project path.
7. When provided system design or project schedule, you MUST read them first before making a plan, then adhere to them in your implementation, especially in the programming language, package, or framework. You MUST implement all code files prescribed in the system design or project schedule. You can create a plan first with each task corresponding to implementing one code file.
8. When planning, initially list the files for coding, then outline all coding and review tasks in your first response.
9. If you plan to read a file, do not include other plans in the same response.
10. Use Engineer2.write_new_code to create or modify a file. Write only one code file each time.
11. When the requirement is simple, you don't need to create a plan, just do it right away.
"""
ENGINEER2_INSTRUCTION = ROLE_INSTRUCTION + EXTRA_INSTRUCTION.strip()
WRITE_CODE_SYSTEM_PROMPT = """
You are a world-class engineer, your goal is to write google-style, elegant, modular, readable, maintainable, fully functional, and ready-for-production code.
Pay attention to the conversation history and the following constraints:
1. When provided system design, YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.
2. When modifying a code, rewrite the full code instead of updating or inserting a snippet.
3. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.
"""
WRITE_CODE_PROMPT = """
# User Requirement
{user_requirement}
# Plan Status
{plan_status}
# Further Instruction
{instruction}
# Output
While some concise thoughts are helpful, code is absolutely required. Always output one and only one code block in your response. Output code in the following format:
```
your code
```
"""

View file

@ -218,3 +218,27 @@ QUICK_RESPONSE_SYSTEM_PROMPT = """
{role_info}
However, you MUST respond to the user message by yourself directly, DON'T ask your team members.
"""
REPORT_TO_HUMAN_PROMPT = """
## Examlpe
example 1:
User requirement: create a 2048 game
reply: The development of the 2048 game has been completed. All files (index.html, style.css, and script.js) have been created and reviewed.
example 2:
User requirement: Crawl and extract all the herb names from the website, Tell me the number of herbs.
reply : The herb names have been successfully extracted. A total of 8 herb names were extracted.
------------
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}
Directly output your reply content. Do not add any output format.
"""
SUMMARY_PROMPT = """
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.
"""

View file

@ -16,7 +16,7 @@ Note:
1. If the requirement is a pure DATA-RELATED requirement, such as web browsing, web scraping, web searching, web imitation, data science, data analysis, machine learning, deep learning, text-to-image etc. DON'T decompose it, assign a single task with the original user requirement as instruction directly to Data Analyst.
2. If the requirement is developing a software, game, app, or website, excluding the above data-related tasks, you should decompose the requirement into multiple tasks and assign them to different team members based on their expertise. The software default development process has four steps: creating a Product Requirement Document (PRD) by the Product Manager -> writing a System Design by the Architect -> creating tasks by the Project Manager -> and coding by the Engineer. You may choose to execute any of these steps. When publishing message to Product Manager, you should directly copy the full original user requirement.
2.1. If the requirement contains both DATA-RELATED part mentioned in 1 and software development part mentioned in 2, you should decompose the software development part and assign them to different team members based on their expertise, and assign the DATA-RELATED part to Data Analyst David directly.
3.1 If the task involves code review or code checking, you should assign it to Engineer.
3.1 If the task involves code review (CR) or code checking, you should assign it to Engineer.
3.2. If the requirement is to fix a bug or issue, you should assign it to Issue Solver. However, if the code is written by Engineer, Engineer must maintain the code.
4. If the requirement is a common-sense, logical, or math problem, you should respond directly without assigning any task to team members.
5. If you think the requirement is not clear or ambiguous, you should ask the user for clarification immediately. Assign tasks only after all info is clear.

View file

@ -300,7 +300,6 @@ class BaseLLM(ABC):
if compress_type == CompressType.NO_COMPRESS:
return messages
current_token_count = 0
max_token = TOKEN_MAX.get(self.config.model, max_token)
keep_token = int(max_token * threshold)
compressed = []
@ -318,7 +317,7 @@ class BaseLLM(ABC):
# system_msgs = [msg for msg in messages if msg["role"] == system_msg_val]
# user_assistant_msgs = [msg for msg in messages if msg["role"] != system_msg_val]
compressed.extend(system_msgs)
current_token_count += self.count_tokens(system_msgs)
current_token_count = self.count_tokens(system_msgs)
if compress_type in [CompressType.POST_CUT_BY_TOKEN, CompressType.POST_CUT_BY_MSG]:
# Under keep_token constraint, keep as many latest messages as possible

View file

@ -38,6 +38,7 @@ from metagpt.rag.factories import (
)
from metagpt.rag.interface import NoEmbedding, RAGObject
from metagpt.rag.retrievers.base import (
DeletableRAGRetriever,
ModifiableRAGRetriever,
PersistableRAGRetriever,
QueryableRAGRetriever,
@ -218,7 +219,13 @@ class SimpleEngine(RetrieverQueryEngine):
"""Count."""
self._ensure_retriever_queryable()
return self._retriever.query_total_count()
return self.retriever.query_total_count()
def clear(self, **kwargs):
"""Clear."""
self._ensure_retriever_deletable()
return self.retriever.clear(**kwargs)
@staticmethod
def get_obj_nodes(objs: Optional[list[RAGObject]] = None) -> list[ObjectNode]:
@ -277,6 +284,9 @@ class SimpleEngine(RetrieverQueryEngine):
def _ensure_retriever_queryable(self):
self._ensure_retriever_of_type(QueryableRAGRetriever)
def _ensure_retriever_deletable(self):
self._ensure_retriever_of_type(DeletableRAGRetriever)
def _ensure_retriever_of_type(self, required_type: BaseRetriever):
"""Ensure that self.retriever is required_type, or at least one of its components, if it's a SimpleHybridRetriever.

View file

@ -38,6 +38,7 @@ class RankerFactory(ConfigBasedFactory):
def _create_llm_ranker(self, config: LLMRankerConfig, **kwargs) -> LLMRerank:
config.llm = self._extract_llm(config, **kwargs)
return LLMRerank(**config.model_dump())
def _create_colbert_ranker(self, config: ColbertRerankConfig, **kwargs) -> LLMRerank:

View file

View file

@ -0,0 +1,34 @@
"""Set of default prompts."""
from llama_index.core.prompts.base import PromptTemplate
from llama_index.core.prompts.prompt_type import PromptType
DEFAULT_CHOICE_SELECT_PROMPT_TMPL = """
You are a highly efficient assistant, tasked with evaluating a list of documents to a given question.
I will provide you with a question with a list of documents. Your task is to respond with the numbers of the documents you should consult to answer the question, in order of relevance, as well as the relevance score.
## Question
{query_str}
## Documents
{context_str}
## Format Example
Doc: 9, Relevance: 7
## Instructions
- Understand the question.
- Evaluate the relevance between the question and the documents.
- The relevance score is a number from 1-10 based on how relevant you think the document is to the question.
- Do not include any documents that are not relevant to the question.
## Constraint
Format: Just print the result in format like **Format Example**.
## Action
Follow instructions, generate output and make sure it follows the **Constraint**.
"""
DEFAULT_CHOICE_SELECT_PROMPT = PromptTemplate(DEFAULT_CHOICE_SELECT_PROMPT_TMPL, prompt_type=PromptType.CHOICE_SELECT)

View file

@ -58,4 +58,18 @@ class QueryableRAGRetriever(RAGRetriever):
@abstractmethod
def query_total_count(self) -> int:
"""To support querying total count, must implement this func"""
"""To support querying total count, must implement this func."""
class DeletableRAGRetriever(RAGRetriever):
"""Support deleting all nodes."""
@classmethod
def __subclasshook__(cls, C):
if cls is DeletableRAGRetriever:
return check_methods(C, "clear")
return NotImplemented
@abstractmethod
def clear(self, **kwargs) -> int:
"""To support deleting all nodes, must implement this func."""

View file

@ -1,4 +1,5 @@
"""BM25 retriever."""
from pathlib import Path
from typing import Callable, Optional
from llama_index.core import VectorStoreIndex
@ -36,6 +37,7 @@ class DynamicBM25Retriever(BM25Retriever):
def add_nodes(self, nodes: list[BaseNode], **kwargs) -> None:
"""Support add nodes."""
self._nodes.extend(nodes)
self._corpus = [self._tokenizer(node.get_content()) for node in self._nodes]
self.bm25 = BM25Okapi(self._corpus)
@ -45,6 +47,7 @@ class DynamicBM25Retriever(BM25Retriever):
def persist(self, persist_dir: str, **kwargs) -> None:
"""Support persist."""
if self._index:
self._index.storage_context.persist(persist_dir)
@ -52,3 +55,19 @@ class DynamicBM25Retriever(BM25Retriever):
"""Support query total count."""
return len(self._nodes)
def clear(self, **kwargs) -> None:
"""Support deleting all nodes."""
self._delete_json_files(kwargs.get("persist_dir"))
self._nodes = []
@staticmethod
def _delete_json_files(directory: str):
"""Delete all JSON files in the specified directory."""
if not directory:
return
for file in Path(directory).glob("*.json"):
file.unlink()

View file

@ -8,6 +8,10 @@ from llama_index.vector_stores.chroma import ChromaVectorStore
class ChromaRetriever(VectorIndexRetriever):
"""Chroma retriever."""
@property
def vector_store(self) -> ChromaVectorStore:
return self._vector_store
def add_nodes(self, nodes: list[BaseNode], **kwargs) -> None:
"""Support add nodes."""
self._index.insert_nodes(nodes, **kwargs)
@ -20,6 +24,11 @@ class ChromaRetriever(VectorIndexRetriever):
def query_total_count(self) -> int:
"""Support query total count."""
vector_store: ChromaVectorStore = self._vector_store
return self.vector_store._collection.count()
return vector_store._collection.count()
def clear(self, **kwargs) -> None:
"""Support deleting all nodes."""
ids = self.vector_store._collection.get()["ids"]
if ids:
self.vector_store._collection.delete(ids=ids)

View file

@ -6,6 +6,7 @@ from typing import Any, ClassVar, List, Literal, Optional, Union
from chromadb.api.types import CollectionMetadata
from llama_index.core.embeddings import BaseEmbedding
from llama_index.core.indices.base import BaseIndex
from llama_index.core.prompts import BasePromptTemplate
from llama_index.core.schema import TextNode
from llama_index.core.vector_stores.types import VectorStoreQueryMode
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, model_validator
@ -14,6 +15,7 @@ from metagpt.config2 import Config
from metagpt.configs.embedding_config import EmbeddingType
from metagpt.logs import logger
from metagpt.rag.interface import RAGObject
from metagpt.rag.prompts.default_prompts import DEFAULT_CHOICE_SELECT_PROMPT
class BaseRetrieverConfig(BaseModel):
@ -124,6 +126,9 @@ class LLMRankerConfig(BaseRankerConfig):
default=None,
description="The LLM to rerank with. using Any instead of LLM, as llama_index.core.llms.LLM is pydantic.v1.",
)
choice_select_prompt: Optional[BasePromptTemplate] = Field(
default=DEFAULT_CHOICE_SELECT_PROMPT, description="Choice select prompt."
)
class ColbertRerankConfig(BaseRankerConfig):

View file

@ -26,7 +26,7 @@ from metagpt.tools.tool_registry import register_tool
class DataAnalyst(RoleZero):
name: str = "David"
profile: str = "DataAnalyst"
goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, web deployment, terminal operation, document QA & analysis, etc."
goal: str = "Take on any data-related tasks, such as data analysis, machine learning, deep learning, web browsing, web scraping, web searching, terminal operation, document QA & analysis, etc."
instruction: str = ROLE_INSTRUCTION + EXTRA_INSTRUCTION
task_type_desc: str = TASK_TYPE_DESC

View file

@ -1,14 +1,25 @@
from __future__ import annotations
from pathlib import Path
from pydantic import Field
from metagpt.actions.write_code_review import ValidateAndRewriteCode
from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION
# from metagpt.actions.write_code_review import ValidateAndRewriteCode
from metagpt.prompts.di.engineer2 import (
ENGINEER2_INSTRUCTION,
WRITE_CODE_PROMPT,
WRITE_CODE_SYSTEM_PROMPT,
)
from metagpt.roles.di.role_zero import RoleZero
from metagpt.schema import UserMessage
from metagpt.strategy.experience_retriever import ENGINEER_EXAMPLE
from metagpt.tools.libs.terminal import Terminal
from metagpt.tools.tool_registry import register_tool
from metagpt.utils.common import CodeParser, awrite
from metagpt.utils.report import EditorReporter
@register_tool(include_functions=["write_new_code"])
class Engineer2(RoleZero):
name: str = "Alex"
profile: str = "Engineer"
@ -17,16 +28,16 @@ class Engineer2(RoleZero):
terminal: Terminal = Field(default_factory=Terminal, exclude=True)
tools: list[str] = ["Plan", "Editor:write,read", "RoleZero", "Terminal:run_command", "ValidateAndRewriteCode"]
tools: list[str] = ["Plan", "Editor:read", "RoleZero", "Terminal:run_command", "Engineer2"]
def _update_tool_execution(self):
validate = ValidateAndRewriteCode()
# validate = ValidateAndRewriteCode()
self.tool_execution_map.update(
{
"Terminal.run_command": self.terminal.run_command,
"ValidateAndRewriteCode.run": validate.run,
"ValidateAndRewriteCode": validate.run,
"Engineer2.write_new_code": self.write_new_code,
# "ValidateAndRewriteCode.run": validate.run,
# "ValidateAndRewriteCode": validate.run,
}
)
@ -42,3 +53,28 @@ class Engineer2(RoleZero):
command_output += "All tasks are finished.\n"
command_output += await super()._run_special_command(cmd)
return command_output
async def write_new_code(self, path: str, instruction: str = "") -> str:
"""Write a new code file.
Args:
path (str): The absolute path of the file to be created.
instruction (optional, str): Further hints or notice other than the current task instruction, must be very concise and can be empty. Defaults to "".
"""
plan_status, _ = self._get_plan_status()
prompt = WRITE_CODE_PROMPT.format(
user_requirement=self.planner.plan.goal,
plan_status=plan_status,
instruction=instruction,
)
context = self.llm.format_msg(self.rc.memory.get(self.memory_k) + [UserMessage(content=prompt)])
async with EditorReporter(enable_llm_stream=True) as reporter:
await reporter.async_report({"type": "code", "filename": Path(path).name, "src_path": path}, "meta")
rsp = await self.llm.aask(context, system_msgs=[WRITE_CODE_SYSTEM_PROMPT])
code = CodeParser.parse_code(text=rsp)
await awrite(path, code)
await reporter.async_report(path, "path")
# TODO: Consider adding line no to be ready for editing.
return f"The file {path} has been successfully created, with content:\n{code}"

View file

@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
# @Author : stellahong (stellahong@fuzhi.ai)
# @Desc :
import asyncio
from typing import Dict
from metagpt.actions.di.detect_intent import DetectIntent, SOPItem
from metagpt.logs import logger
from metagpt.roles.di.data_interpreter import DataInterpreter
from metagpt.schema import Message
from metagpt.tools.tool_recommend import BM25ToolRecommender
class MGX(DataInterpreter):
use_intent: bool = True
intents: Dict = {}
async def _detect_intent(self, user_msg: Message) -> str:
todo = DetectIntent(context=self.context)
request_with_sop, sop_type = await todo.run(user_msg)
logger.info(f"{sop_type} {request_with_sop}")
if sop_type == SOPItem.SOFTWARE_DEVELOPMENT.type_name:
self.tool_recommender = BM25ToolRecommender(tools=["software development"])
else:
self.tool_recommender = BM25ToolRecommender(tools=["<all>"])
return request_with_sop
async def _plan_and_act(self) -> Message:
"""first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ... Use llm to come up with the plan dynamically."""
# create initial plan and update it until confirmation
goal = self.rc.memory.get()[-1].content # retrieve latest user requirement
if self.use_intent: # add mode
user_message = Message(content=goal, role="user")
goal = await self._detect_intent(user_message)
logger.info(f"Goal is {goal}")
await self.planner.update_plan(goal=goal)
# take on tasks until all finished
while self.planner.current_task:
task = self.planner.current_task
logger.info(f"ready to take on task {task}")
# take on current task
task_result = await self._act_on_task(task)
# process the result, such as reviewing, confirming, plan updating
await self.planner.process_task_result(task_result)
rsp = self.planner.get_useful_memories()[0] # return the completed plan as a response
self.rc.memory.add(rsp) # add to persistent memory
return rsp
if __name__ == "__main__":
test_requirements = [
"design a game using Gym (an open source Python library), including a graphical interface and interactive gameplay"
# "Run data analysis on sklearn Wine recognition dataset, include a plot, and train a model to predict wine class (20% as validation), and show validation accuracy"
# "获取https://www.stats.gov.cn/sj/sjjd/202307/t20230718_1941322.html的内容并返回上半年cpi的增长或下降幅度"
]
for requirement in test_requirements:
mgx = MGX()
rsp = asyncio.run(mgx.run(requirement))

View file

@ -4,6 +4,7 @@ import inspect
import json
import re
import traceback
from datetime import datetime
from typing import Annotated, Callable, Dict, List, Literal, Optional, Tuple
from pydantic import Field, model_validator
@ -26,7 +27,9 @@ from metagpt.prompts.di.role_zero import (
QUICK_THINK_PROMPT,
QUICK_THINK_SYSTEM_PROMPT,
REGENERATE_PROMPT,
REPORT_TO_HUMAN_PROMPT,
ROLE_INSTRUCTION,
SUMMARY_PROMPT,
SYSTEM_PROMPT,
THOUGHT_GUIDANCE,
)
@ -65,7 +68,7 @@ class RoleZero(Role):
# React Mode
react_mode: Literal["react"] = "react"
max_react_loop: int = 20 # used for react mode
max_react_loop: int = 50 # used for react mode
# Tools
tools: list[str] = [] # Use special symbol ["<all>"] to indicate use of all registered tools
@ -85,6 +88,7 @@ class RoleZero(Role):
memory_k: int = 20 # number of memories (messages) to use as historical context
use_fixed_sop: bool = False
requirements_constraints: str = "" # the constraints in user requirements
use_summary: bool = True # whether to summarize at the end
@model_validator(mode="after")
def set_plan_and_tool(self) -> "RoleZero":
@ -203,13 +207,13 @@ class RoleZero(Role):
memory = self.parse_images(memory)
req = self.llm.format_msg(memory + [UserMessage(content=prompt)])
state_data = dict(
plan_status=plan_status,
current_task=current_task,
instruction=instruction,
)
async with ThoughtReporter(enable_llm_stream=True) as reporter:
await reporter.async_report({"type": "react"})
state_data = dict(
plan_status=plan_status,
current_task=current_task,
instruction=instruction,
)
self.command_rsp = await self.llm_cached_aask(req=req, system_msgs=[system_prompt], state_data=state_data)
self.command_rsp = await self._check_duplicates(req, self.command_rsp)
@ -246,13 +250,18 @@ class RoleZero(Role):
msg.add_metadata(IMAGES, images)
return memory
def _get_prefix(self) -> str:
time_info = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return super()._get_prefix() + f" The current time is {time_info}."
async def _act(self) -> Message:
if self.use_fixed_sop:
return await super()._act()
commands, ok = await self._parse_commands()
commands, ok = await self._parse_commands(self.command_rsp)
if not ok:
error_msg = commands
self.rc.memory.add(UserMessage(content=error_msg))
return error_msg
logger.info(f"Commands: \n{commands}")
outputs = await self._run_commands(commands)
@ -288,6 +297,14 @@ class RoleZero(Role):
logger.debug(f"{self._setting}: {self.rc.state=}, will do {self.rc.todo}")
rsp = await self._act()
actions_taken += 1
# post-check
if self.rc.max_react_loop >= 10 and actions_taken >= self.rc.max_react_loop:
# If max_react_loop is a small value (e.g. < 10), it is intended to be reached and make the agent stop
logger.warning(f"reached max_react_loop: {actions_taken}")
rsp = await self.ask_human("I have reached my max action rounds, do you want me to continue? Yes or no")
if "yes" in rsp.lower():
actions_taken = 0
return rsp # return output from the last action
def format_quick_system_prompt(self) -> str:
@ -315,10 +332,13 @@ class RoleZero(Role):
self.llm.format_msg(memory),
system_msgs=[QUICK_RESPONSE_SYSTEM_PROMPT.format(role_info=self._get_prefix())],
)
if "command_name" in answer:
# an actual TASK intent misclassified as QUICK, correct it here, FIXME: a better way is to classify it correctly in the first place
answer = ""
intent_result = "TASK"
# If the answer contains the substring '[Message] from A to B:', remove it.
pattern = r"\[Message\] from .+? to .+?:\s*"
answer = re.sub(pattern, "", answer, count=1)
if "command_name" in answer:
# an actual TASK intent misclassified as QUICK, correct it here, FIXME: a better way is to classify it correctly in the first place
answer = ""
intent_result = "TASK"
elif "SEARCH" in intent_result:
query = "\n".join(str(msg) for msg in memory)
answer = await SearchEnhancedQA().run(query)
@ -352,7 +372,7 @@ class RoleZero(Role):
command_rsp = await self.llm.aask(regenerate_req)
return command_rsp
async def _parse_commands(self) -> Tuple[List[Dict], bool]:
async def _parse_commands(self, command_rsp) -> Tuple[List[Dict], bool]:
"""Retrieves commands from the Large Language Model (LLM).
This function attempts to retrieve a list of commands from the LLM by
@ -364,20 +384,20 @@ class RoleZero(Role):
- A boolean flag indicating success (True) or failure (False).
"""
try:
commands = CodeParser.parse_code(block=None, lang="json", text=self.command_rsp)
commands = CodeParser.parse_code(block=None, lang="json", text=command_rsp)
if commands.endswith("]") and not commands.startswith("["):
commands = "[" + commands
commands = json.loads(repair_llm_raw_output(output=commands, req_keys=[None], repair_type=RepairType.JSON))
except json.JSONDecodeError as e:
logger.warning(f"Failed to parse JSON for: {self.command_rsp}. Trying to repair...")
logger.warning(f"Failed to parse JSON for: {command_rsp}. Trying to repair...")
commands = await self.llm.aask(
msg=JSON_REPAIR_PROMPT.format(json_data=self.command_rsp, json_decode_error=str(e))
msg=JSON_REPAIR_PROMPT.format(json_data=command_rsp, json_decode_error=str(e))
)
try:
commands = json.loads(CodeParser.parse_code(block=None, lang="json", text=commands))
except json.JSONDecodeError:
# repair escape error of code and math
commands = CodeParser.parse_code(block=None, lang="json", text=self.command_rsp)
commands = CodeParser.parse_code(block=None, lang="json", text=command_rsp)
new_command = repair_escape_error(commands)
commands = json.loads(
repair_llm_raw_output(output=new_command, req_keys=[None], repair_type=RepairType.JSON)
@ -385,8 +405,7 @@ class RoleZero(Role):
except Exception as e:
tb = traceback.format_exc()
print(tb)
error_msg = UserMessage(content=str(e))
self.rc.memory.add(error_msg)
error_msg = str(e)
return error_msg, False
# 为了对LLM不按格式生成进行容错
@ -439,8 +458,7 @@ class RoleZero(Role):
command_output = "Current task is finished. If all tasks are finished, use 'end' to stop."
elif cmd["command_name"] == "end":
self._set_state(-1)
command_output = ""
command_output = await self._end()
# output from bash.run may be empty, add decorations to the output to ensure visibility.
elif cmd["command_name"] == "Bash.run":
@ -499,3 +517,24 @@ class RoleZero(Role):
if not isinstance(self.rc.env, MGXEnv):
return "Not in MGXEnv, command will not be executed."
return await self.rc.env.reply_to_human(content, sent_from=self)
async def _end(self):
self._set_state(-1)
memory = self.rc.memory.get(self.memory_k)
# 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 "")
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)]))
await self.reply_to_human(content=reply_content)
self.rc.memory.add(AIMessage(content=reply_content, cause_by=RunCommand))
outputs = ""
# Summary of the Completed Task and Deliverables
if self.use_summary:
logger.info("end current run and summarize")
outputs = await self.llm.aask(self.llm.format_msg(memory + [UserMessage(SUMMARY_PROMPT)]))
return outputs

View file

@ -9,6 +9,7 @@ from metagpt.prompts.di.swe_agent import (
NEXT_STEP_TEMPLATE,
)
from metagpt.roles.di.role_zero import RoleZero
from metagpt.schema import Message
from metagpt.tools.libs.git import git_create_pull
from metagpt.tools.libs.terminal import Bash
@ -33,8 +34,6 @@ class SWEAgent(RoleZero):
async def _think(self) -> bool:
await self._format_instruction()
res = await super()._think()
if self.run_eval:
await self._parse_commands_for_eval()
return res
def _update_tool_execution(self):
@ -56,6 +55,12 @@ class SWEAgent(RoleZero):
bash_state = json.loads(state_output)
self.cmd_prompt_current_state = CURRENT_BASH_STATE.format(**bash_state).strip()
async def _act(self) -> Message:
message = await super()._act()
if self.run_eval:
self._parse_commands_for_eval()
return message
async def _parse_commands_for_eval(self):
"""
Handles actions based on parsed commands.
@ -65,24 +70,19 @@ class SWEAgent(RoleZero):
This function is specifically added for SWE bench evaluation.
"""
# only import when evaluation is needed
from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch
# If todo switches to None, it indicates that this is the final round of reactions, and the Swe-Agent will stop. Use git diff to store any changes made.
if not self.rc.todo:
from metagpt.tools.swe_agent_commands.swe_agent_utils import extract_patch
commands, ok = await self._parse_commands()
if not ok:
return
for cmd in commands:
if "end" != cmd.get("command_name", ""):
return
try:
diff_output = await self.terminal.run("git diff --cached")
clear_diff = extract_patch(diff_output)
logger.info(f"Diff output: \n{clear_diff}")
if clear_diff:
self.output_diff = clear_diff
try:
diff_output = await self.terminal.run("git diff --cached")
clear_diff = extract_patch(diff_output)
logger.info(f"Diff output: \n{clear_diff}")
if clear_diff:
self.output_diff = clear_diff
except Exception as e:
logger.error(f"Error during submission: {e}")
except Exception as e:
logger.error(f"Error during submission: {e}")
def _retrieve_experience(self) -> str:
return MINIMAL_EXAMPLE

View file

@ -30,6 +30,8 @@ class TeamLeader(RoleZero):
experience_retriever: Annotated[ExpRetriever, Field(exclude=True)] = SimpleExpRetriever()
use_summary: bool = False
def _update_tool_execution(self):
self.tool_execution_map.update(
{

View file

@ -23,13 +23,14 @@
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING, Iterable, Optional, Set, Type, Union
from typing import Iterable, Optional, Set, Type, Union
from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator
from metagpt.actions import Action, ActionOutput
from metagpt.actions.action_node import ActionNode
from metagpt.actions.add_requirement import UserRequirement
from metagpt.base import BaseEnvironment, BaseRole
from metagpt.const import MESSAGE_ROUTE_TO_SELF
from metagpt.context_mixin import ContextMixin
from metagpt.logs import logger
@ -47,9 +48,6 @@ from metagpt.strategy.planner import Planner
from metagpt.utils.common import any_to_name, any_to_str, role_raise_decorator
from metagpt.utils.repair_llm_raw_output import extract_state_value_from_output
if TYPE_CHECKING:
from metagpt.environment import Environment # noqa: F401
PREFIX_TEMPLATE = """You are a {profile}, named {name}, your goal is {goal}. """
CONSTRAINT_TEMPLATE = "the constraint is {constraints}. "
@ -97,7 +95,7 @@ class RoleContext(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
# # env exclude=True to avoid `RecursionError: maximum recursion depth exceeded in comparison`
env: "Environment" = Field(default=None, exclude=True) # # avoid circular import
env: BaseEnvironment = Field(default=None, exclude=True) # # avoid circular import
# TODO judge if ser&deser
msg_buffer: MessageQueue = Field(
default_factory=MessageQueue, exclude=True
@ -123,14 +121,8 @@ class RoleContext(BaseModel):
def history(self) -> list[Message]:
return self.memory.get()
@classmethod
def model_rebuild(cls, **kwargs):
from metagpt.environment.base_env import Environment # noqa: F401
super().model_rebuild(**kwargs)
class Role(SerializationMixin, ContextMixin, BaseModel):
class Role(BaseRole, SerializationMixin, ContextMixin, BaseModel):
"""Role/Agent"""
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
@ -310,7 +302,7 @@ class Role(SerializationMixin, ContextMixin, BaseModel):
logger.debug(f"actions={self.actions}, state={state}")
self.set_todo(self.actions[self.rc.state] if state >= 0 else None)
def set_env(self, env: "Environment"):
def set_env(self, env: BaseEnvironment):
"""Set the environment in which the role works. The role can talk to the environment and can also receive
messages by observing."""
self.rc.env = env
@ -590,6 +582,3 @@ class Role(SerializationMixin, ContextMixin, BaseModel):
if self.actions:
return any_to_name(self.actions[0])
return ""
RoleContext.model_rebuild()

View file

@ -147,8 +147,8 @@ class SerializationMixin(BaseModel, extra="forbid"):
serialized_data = self.model_dump()
write_json_file(file_path, serialized_data)
logger.info(f"{self.__class__.__qualname__} serialization successful. File saved at: {file_path}")
write_json_file(file_path, serialized_data, use_fallback=True)
logger.debug(f"{self.__class__.__qualname__} serialization successful. File saved at: {file_path}")
return file_path
@ -171,7 +171,7 @@ class SerializationMixin(BaseModel, extra="forbid"):
data: dict = read_json_file(file_path)
model = cls(**data)
logger.info(f"{cls.__qualname__} deserialization successful. Instance created from file: {file_path}")
logger.debug(f"{cls.__qualname__} deserialization successful. Instance created from file: {file_path}")
return model

View file

@ -849,7 +849,6 @@ Here's the plan:
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.
4. **Code Review Tasks**: Review each file to ensure they meet the project requirements and adhere to the system design.
Let's start by appending the first task to the plan.
@ -881,33 +880,6 @@ Let's start by appending the first task to the plan.
"instruction": "Create the script.js file containing the JavaScript logic for the 2048 game.",
"assignee": "Alex"
}
},
{
"command_name": "Plan.append_task",
"args": {
"task_id": "4",
"dependent_task_ids": ["1"],
"instruction": "Use ValidateAndRewriteCode to review the code in index.html to ensure it meets the design specifications.",
"assignee": "Alex"
}
},
{
"command_name": "Plan.append_task",
"args": {
"task_id": "5",
"dependent_task_ids": ["2"],
"instruction": "Use ValidateAndRewriteCode to review the code in style.css to ensure it meets the design specifications.",
"assignee": "Alex"
}
},
{
"command_name": "Plan.append_task",
"args": {
"task_id": "6",
"dependent_task_ids": ["3"],
"instruction": "Use ValidateAndRewriteCode to review the code in script.js to ensure it meets the design specifications. ",
"assignee": "Alex"
}
}
]
```
@ -918,32 +890,9 @@ Explanation: Take on one task, such as writing a file. Upon completion, finish c
```json
[
{
"command_name": "Editor.write",
"command_name": "Engineer2.write_new_code",
"args": {
"path": "/Users/gary/Files/temp/workspace/snake_game/src/index.html",
"content": "the code ..."
}
},
{
"command_name": "Plan.finish_current_task",
"args": {{}}
}
]
```
## example 4
I will now review the code in `script.js`.
Explanation: to review the code, call ValidateAndRewriteCode.run.
```json
[
{
"command_name": "ValidateAndRewriteCode.run",
"args": {
"code_path": "/tmp/src/script.js",
"system_design_input": "/tmp/docs/system_design.json",
"project_schedule_input": "/tmp/docs/project_schedule.json",
"code_validate_k_times": 2
"path": "src/index.html"
}
},
{

View file

@ -1,3 +1,4 @@
import difflib
import json
from pathlib import Path
from typing import Optional
@ -22,35 +23,33 @@ class CodeReview:
async def review(
self,
patch_path: str,
cr_output_file: str,
cr_point_file: Optional[str] = None,
output_file: str,
point_file: Optional[str] = None,
) -> str:
"""Review a PR and save code review comments.
Notes:
If the user does not specify an output path, saved it using a relative path in the current working directory.
Args:
patch_path: The local path of the patch file or the url of the pull request. Example: "/data/xxx-pr-1.patch", "https://github.com/xx/XX/pull/1362"
cr_output_file: Output file path where code review comments will be saved. Example: "cr/xxx-pr-1.json"
cr_point_file: File path for specifying code review points. If not specified, this parameter is not passed..
patch_path: The local path of the patch file or the URL of the pull request.
output_file: Output file path where code review comments will be saved.
point_file: File path for specifying code review points. If not specified, this parameter does not need to be passed.
Examples:
>>> cr = CodeReview()
>>> await cr.review(patch_path="https://github.com/geekan/MetaGPT/pull/136", output_file="cr/MetaGPT_136.json")
>>> await cr.review(patch_path="/data/uploads/dev-master.diff", output_file="cr/dev-master.json")
>>> await cr.review(patch_path="/data/uploads/main.py", output_file="cr/main.json")
"""
patch = await self._get_patch_content(patch_path)
cr_point_file = cr_point_file if cr_point_file else Path(metagpt.ext.cr.__file__).parent / "points.json"
async with aiofiles.open(cr_point_file, "rb") as f:
point_file = point_file if point_file else Path(metagpt.ext.cr.__file__).parent / "points.json"
async with aiofiles.open(point_file, "rb") as f:
cr_point_content = await f.read()
cr_points = [Point(**i) for i in json.loads(cr_point_content)]
async with FileIOOperatorReporter(enable_llm_stream=True) as reporter:
src_path = cr_output_file
cr_output_path = Path(cr_output_file)
await reporter.async_report(
{"type": "CodeReview", "src_path": src_path, "filename": cr_output_path.name}, "meta"
)
comments = await CodeReview_().run(patch, cr_points)
cr_output_path.parent.mkdir(exist_ok=True, parents=True)
async with aiofiles.open(cr_output_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(comments, ensure_ascii=False))
await reporter.async_report(cr_output_path)
return f"The number of defects: {len(comments)} and the comments are stored in {cr_output_file}"
comments = await CodeReview_().run(patch, cr_points, output_file)
return f"The number of defects: {len(comments)} and the comments are stored in {output_file}"
async def fix(
self,
@ -88,6 +87,12 @@ class CodeReview:
async with aiofiles.open(patch_path, encoding="utf-8") as f:
patch_file_content = await f.read()
await FileIOOperatorReporter().async_report(patch_path)
if not patch_path.endswith((".diff", ".patch")):
name = Path(patch_path).name
patch_file_content = "".join(
difflib.unified_diff([], patch_file_content.splitlines(keepends=True), "/dev/null", f"b/{name}"),
)
patch_file_content = f"diff --git a/{name} b/{name}\n{patch_file_content}"
patch: PatchSet = PatchSet(patch_file_content)
return patch

View file

@ -1,7 +1,7 @@
import base64
import os
from pathlib import Path
from typing import List, Optional, Union
from typing import List, Optional, Tuple, Union
from pydantic import BaseModel, ConfigDict
@ -104,17 +104,19 @@ class FileOperator(BaseModel):
from metagpt.tools.libs import get_env_default
from metagpt.utils.omniparse_client import OmniParseClient
base_url = await get_env_default(key="base_url", app_name="OmniParse", default_value="")
if not base_url:
base_url = await FileOperator._read_omniparse_config()
env_base_url = await get_env_default(key="base_url", app_name="OmniParse", default_value="")
env_timeout = await get_env_default(key="timeout", app_name="OmniParse", default_value="")
conf_base_url, conf_timeout = await FileOperator._read_omniparse_config()
base_url = env_base_url or conf_base_url
if not base_url:
return None
api_key = await get_env_default(key="api_key", app_name="OmniParse", default_value="")
v = await get_env_default(key="timeout", app_name="OmniParse", default_value="120")
timeout = env_timeout or conf_timeout or 600
try:
timeout = int(v) or 120
timeout = int(timeout)
except ValueError:
timeout = 120
timeout = 600
try:
if not await check_http_endpoint(url=base_url):
@ -140,8 +142,8 @@ class FileOperator(BaseModel):
return result
@staticmethod
async def _read_omniparse_config() -> str:
async def _read_omniparse_config() -> Tuple[str, int]:
config = Config.default()
if config.omniparse and config.omniparse.url:
return config.omniparse.url
return ""
return config.omniparse.url, config.omniparse.timeout
return "", 0

View file

@ -7,6 +7,7 @@
"""
from __future__ import annotations
import contextlib
import inspect
import os
from collections import defaultdict
@ -99,7 +100,9 @@ def register_tool(tags: list[str] = None, schema_path: str = "", **kwargs):
if "metagpt" in file_path:
# split to handle ../metagpt/metagpt/tools/... where only metapgt/tools/... is needed
file_path = "metagpt" + file_path.split("metagpt")[-1]
source_code = inspect.getsource(cls)
source_code = ""
with contextlib.suppress(OSError):
source_code = inspect.getsource(cls)
TOOL_REGISTRY.register_tool(
tool_name=cls.__name__,

View file

@ -581,20 +581,20 @@ def read_json_file(json_file: str, encoding: str = "utf-8") -> list[Any]:
def handle_unknown_serialization(x: Any) -> str:
"""For `to_jsonable_python` debug, unknown values will be logged instead of raising an exception."""
"""For `to_jsonable_python` debug, get more detail about the x."""
if inspect.ismethod(x):
logger.error(f"Method: {x.__self__.__class__.__name__}.{x.__func__.__name__}")
tip = f"Cannot serialize method '{x.__func__.__name__}' of class '{x.__self__.__class__.__name__}'"
elif inspect.isfunction(x):
logger.error(f"Function: {x.__name__}")
tip = f"Cannot serialize function '{x.__name__}'"
elif hasattr(x, "__class__"):
logger.error(f"Instance of: {x.__class__.__name__}")
tip = f"Cannot serialize instance of '{x.__class__.__name__}'"
elif hasattr(x, "__name__"):
logger.error(f"Class or module: {x.__name__}")
tip = f"Cannot serialize class or module '{x.__name__}'"
else:
logger.error(f"Unknown type: {type(x)}")
tip = f"Cannot serialize object of type '{type(x).__name__}'"
return f"<Unserializable {type(x).__name__} object>"
raise TypeError(tip)
def write_json_file(json_file: str, data: Any, encoding: str = "utf-8", indent: int = 4, use_fallback: bool = False):

View file

@ -38,7 +38,7 @@ async def mermaid_to_file(
Returns:
int: 0 if the conversion is successful, -1 if the conversion fails.
"""
suffixes = suffixes or ["png"]
suffixes = suffixes or ["svg"]
# Write the Mermaid code to a temporary file
config = config if config else Config.default()
dir_name = os.path.dirname(output_file_without_suffix)

View file

@ -189,7 +189,7 @@ class OmniParseClient:
# Do not verify if only byte data is provided
return
file_ext = Path(verify_file_path).suffix
file_ext = Path(verify_file_path).suffix.lower()
if file_ext not in allowed_file_extensions:
raise ValueError(f"Not allowed {file_ext} File extension must be one of {allowed_file_extensions}")

View file

@ -68,28 +68,29 @@ extras_require["pyppeteer"] = [
extras_require["dev"] = (["pylint~=3.0.3", "black~=23.3.0", "isort~=5.12.0", "pre-commit~=3.6.0"],)
setup(
name="metagpt",
version="0.8.0",
description="The Multi-Agent Framework",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/geekan/MetaGPT",
author="Alexander Wu",
author_email="alexanderwu@deepwisdom.ai",
license="MIT",
keywords="metagpt multi-agent multi-role programming gpt llm metaprogramming",
packages=find_packages(exclude=["contrib", "docs", "examples", "tests*"]),
python_requires=">=3.9",
install_requires=requirements,
extras_require=extras_require,
cmdclass={
"install_mermaid": InstallMermaidCLI,
},
entry_points={
"console_scripts": [
"metagpt=metagpt.software_company:app",
],
},
include_package_data=True,
)
if __name__ == "__main__":
setup(
name="metagpt",
version="0.8.0",
description="The Multi-Agent Framework",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/geekan/MetaGPT",
author="Alexander Wu",
author_email="alexanderwu@deepwisdom.ai",
license="MIT",
keywords="metagpt multi-agent multi-role programming gpt llm metaprogramming",
packages=find_packages(exclude=["contrib", "docs", "examples", "tests*"]),
python_requires=">=3.9",
install_requires=requirements,
extras_require=extras_require,
cmdclass={
"install_mermaid": InstallMermaidCLI,
},
entry_points={
"console_scripts": [
"metagpt=metagpt.software_company:app",
],
},
include_package_data=True,
)

View file

@ -1,56 +0,0 @@
import pytest
from metagpt.actions.di.detect_intent import DetectIntent
from metagpt.schema import Message
SOFTWARE_DEV_REQ1 = """
I'd like to create a personalized website that features the 'Game of Life' simulation.
"""
SOFTWARE_DEV_REQ2 = """
Create a website widget for TODO list management.
"""
SOFTWARE_DEV_REQ3 = """
Create an official website with a top bar, banner, About Us section, and footer.
"""
DI_REQ1 = """
can you finetune a 78 Llama model using https://github.com/huggingface/peft should be instructions in the Readme.
"""
DI_REQ2 = """
I came across a blog post on the website Mafengwo (https://www.mafengwo.cn/i/17171539.html) that discusses the possibility of generating images with hidden text. The post refers to a script that can be used for this purpose. Could you help me set up this script and use it to generate some images? I would like the images to have the hidden text 'MAX' and also some with 'MetaGPT' as the hidden text.
"""
DI_REQ3 = """
Extract all of the blog posts from `https://stripe.com/blog/page/1` and return a CSV with the columns `date`, `article_text`, `author` and `summary`. Generate a summary for each article yourself.
"""
FIX_BUG_REQ = """
Fix this error from the 2048 game repo: TypeError: __init__() takes 1 positional argument but 2 were given"
"""
FORMAT_REPO_REQ = """
git clone 'https://github.com/spec-first/connexion' and format to MetaGPT project
"""
@pytest.mark.asyncio
@pytest.mark.parametrize(
"requirement, expected_intent_type",
[
(SOFTWARE_DEV_REQ1, "software development"),
(SOFTWARE_DEV_REQ2, "software development"),
(SOFTWARE_DEV_REQ3, "software development"),
(DI_REQ1, "other"),
(DI_REQ2, "other"),
(DI_REQ3, "other"),
(FIX_BUG_REQ, "fix bugs"),
(FORMAT_REPO_REQ, "format repo"),
],
)
async def test_detect_intent(requirement, expected_intent_type):
di = DetectIntent()
_, intent_type = await di.run(Message(role="user", content=requirement))
assert intent_type == expected_intent_type

View file

@ -6,6 +6,7 @@ from typing import Any, Optional
import pytest
from metagpt.base.base_env_space import BaseEnvAction, BaseEnvObsParams
from metagpt.environment.api.env_api import EnvAPIAbstract
from metagpt.environment.base_env import (
Environment,
@ -14,7 +15,6 @@ from metagpt.environment.base_env import (
mark_as_readable,
mark_as_writeable,
)
from metagpt.environment.base_env_space import BaseEnvAction, BaseEnvObsParams
class ForTestEnv(Environment):

View file

@ -7,7 +7,7 @@ from metagpt.configs.exp_pool_config import (
)
from metagpt.configs.llm_config import LLMConfig
from metagpt.exp_pool.manager import Experience, ExperienceManager
from metagpt.exp_pool.schema import QueryType
from metagpt.exp_pool.schema import DEFAULT_SIMILARITY_TOP_K, QueryType
class TestExperienceManager:
@ -129,3 +129,16 @@ class TestExperienceManager:
manager = ExperienceManager(config=mock_config)
storage = manager._create_chroma_storage()
assert storage is not None
def test_get_ranker_configs_use_llm_ranker_true(self, mock_config):
mock_config.exp_pool.use_llm_ranker = True
manager = ExperienceManager(config=mock_config)
ranker_configs = manager._get_ranker_configs()
assert len(ranker_configs) == 1
assert ranker_configs[0].top_n == DEFAULT_SIMILARITY_TOP_K
def test_get_ranker_configs_use_llm_ranker_false(self, mock_config):
mock_config.exp_pool.use_llm_ranker = False
manager = ExperienceManager(config=mock_config)
ranker_configs = manager._get_ranker_configs()
assert len(ranker_configs) == 0

View file

@ -28,10 +28,6 @@ async def test_ltm_search(mocker):
)
role_id = "UTUserLtm(Product Manager)"
from metagpt.environment import Environment
Environment
RoleContext.model_rebuild()
rc = RoleContext(watch={"metagpt.actions.add_requirement.UserRequirement"})
ltm = LongTermMemory()
ltm.recover_memory(role_id, rc)

View file

@ -1,179 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import List
import pytest
from metagpt.roles.di.mgx import MGX
from metagpt.schema import Message
DEMO_CONTENT = [
{
"role": "user",
"content": "Can you build TextToSummarize which is a SMS number that I can text and it will scrape a website "
"and summarize it with ChatGPT",
},
{
"role": "assistant",
"content": "Absolutely, I can build a service like TextToSummarize for you. The process will involve setting up"
" an SMS service that can receive your texts, scraping the website content you send, and then using"
" an AI to summarize the content.\nTo get started, I'll need to set up a number for receiving SMS"
", then I'll work on the script to scrape the website content from the URLs you text, and finally."
" I'll integrate it with an AI service to Generalte the summaries.\n I'll keep you updated on my"
" progress!",
},
{"role": "user", "content": "What exactly do we need the web app for?"},
{
"role": "assistant",
"content": "The web app will service as the interface between the SMS service and the AI summarization service"
". When you send a text with a URL to the SMS number, teh web app will process that, scrape the"
" content from the URL, and then use the AI to summarize it. The summary will then be sent back to"
" you. It's the central hub that ties all the components of the service togather.",
},
]
DEMO1_CONTENT = [
{
"role": "user",
"content": "Extract all of the blog posts from `https://stripe.com/blog/page/1` and return a CSV with the"
" columns `date`, `article_text`, `author` and `summary`. Generate a summary for each article"
" yourself.",
}
]
_DEMO2_CONTENT_ASSISTANT = """
user:
## User Requirement
### User Requirement Detail
```text
2048 game
````
### Knowledge
To meet user requirements, the following standard operating procedure(SOP) must be used. SOP descriptions cannot be modified; user requirements can only be appended to the end of corresponding steps.
- Writes a PRD based on software requirements.
- Writes a design to the project repository, based on the PRD of the project.
- Writes a project plan to the project repository, based on the design of the project.
- Writes code to implement designed features according to the project plan and adds them to the project repository.
- Stage and commit changes for the project repository using Git.
## Context
## Current Plan
[
{
"task_id": "1",
"dependent_task_ids": [],
"instruction": "Write a PRD based on the software requirements.",
"task_type": "other",
"code": "from metagpt.tools.libs.software_development import write_prd\n\nasync def create_2048_game_prd():\n idea = \"Create a 2048 game\"\n prd_path = await write_prd(idea)\n return prd_path\n\nawait create_2048_game_prd()\n",
"result": ",,,,,[,CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a, 2048 game\",\n \"Project Name\": \"game_2048\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n , \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \",As a player, I want to be able to choose difficulty levels\",\n \"As a player,, I want to see my score after each game\",\n \"As a player, I want, to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n , ],\n \"Competitive Analysis\": [\n \"2048 Game, A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my, best score shown, but many ads\"\n ],\n \"Competitive Quadrant, Chart\": \"quadrantChart\\n title, \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\",High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant,-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be, improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.,23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n , \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign, F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6,]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n, \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n, ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR,\": \"\"\n}\n[/CONTENT]\n,,,,,,,,,,,,{'output': '[{\"type_\": \"str\", \"name\": \"PRD File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd/20240401194850.json\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Competitive Analysis\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/competitive_analysis/20240401194850.svg\"}]', 'tool_name': 'write_prd'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd')",
"is_success": true,
"is_finished": true
},
{
"task_id": "2",
"dependent_task_ids": [
"1"
],
"instruction": "Write a design to the project repository, based on the PRD of the project.",
"task_type": "other",
"code": "from metagpt.tools.libs.software_development import write_design\n\nasync def create_2048_game_design(prd_path):\n system_design_path = await write_design(prd_path)\n return system_design_path\n\nawait create_2048_game_design('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/prd/20240401194850.json')\n",
"result": ",[CONTENT]\n{\n \"Implementation approach\":, \"We will use the Py,game library to create the game interface and handle user input. The game logic will be implemented using Python classes and data structures to manage the game, board and tiles.\",\n \"File list\": [\n \"main.py,\",\n \"game.py\"\n, ],\n \"Data structures and interfaces\": \"\\nclassDiagram\\n class Game2048 {\\n -int[][] board,\\n -int score\\n -bool game_over\\n +__init__()\\n +reset()\\n +,move(direction: str)\\n +get_board() int[][]\\n +get_score() int\\n +is_game_over() bool\\n }\\n , class UI {\\n +display_board(board: int[][], score: int)\\n, +show_game_over()\\n }\\n Game,2048 --> UI\\n\",\n \"Program call flow\": \"\\nsequenceDiagram,\\n participant M as Main\\n participant G as Game2048,\\n participant UI as UI\\n M->>G: reset,()\\n G-->>UI: display_board(board, score)\\,n M->>G: move(direction)\\n G-->>,G: update board and score\\n G-->>UI:, display_board(board, score)\\n G->>G: is_game_over,()\\n G-->>UI: show_game_over()\\n\",\n,,,,, \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n,,,,,,,,,,,,,,,{'output': '[{\"type_\": \"str\", \"name\": \"Intermedia Design File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design/20240401194850.json\"}, {\"type_\": \"str\", \"name\": \"Design File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/system_design/20240401194850.md\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Class Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/data_api_design/20240401194850.svg\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.mmd\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.pdf\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.png\"}, {\"type_\": \"str\", \"name\": \"Sequence Diagram File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/resources/seq_flow/20240401194850.svg\"}]', 'tool_name': 'write_design'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design')",
"is_success": true,
"is_finished": true
},
{
"task_id": "3",
"dependent_task_ids": [
"2"
],
"instruction": "Write a project plan to the project repository, based on the design of the project.",
"task_type": "other",
"code": "from metagpt.tools.libs.software_development import write_project_plan\n\nasync def create_2048_game_project_plan(system_design_path):\n task_path = await write_project_plan(system_design_path)\n return task_path\n\nawait create_2048_game_project_plan('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/system_design/20240401194850.json')\n",
"result": ",\n[CONTENT,]\n{\n \"Required Python packages\": [\n \"pygame==2.0.1\"\n ],\n \"Required, Other language third-party packages\": [\n \"No third-party dependencies required,\"\n ],\n \"Logic Analysis\": [\n , [\n \"game.py\",\n \"Contains Game2048, class and functions to manage the game board and tiles\"\n, ],\n [\n \"main.py\",\n \",Contains the main function, and imports Game2048, from game.py\"\n ]\n ],\n \"Task list,\": [\n \"game.py\",\n \"main.py\"\n, ],\n \"Full API spec\":, \"\",\n \"Shared Knowledge\": \"The Pygame library will be used, to create the game interface and handle user input. The game, logic will be implemented using Python classes and data structures to manage the game, board and tiles.\",\n \"Anything UNCLEAR\": \"No unclear aspects,,,,, mentioned.\"\n}\n[/CONTENT]\n{'output': '[{\"type_\": \"str\", \"name\": \"Project Plan\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task/20240401194850.json\"}]', 'tool_name': 'write_project_plan'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task')",
"is_success": true,
"is_finished": true
},
{
"task_id": "4",
"dependent_task_ids": [
"3"
],
"instruction": "Write code to implement designed features according to the project plan and add them to the project repository.",
"task_type": "other",
"code": "from metagpt.tools.libs.software_development import write_codes\n\nasync def implement_2048_game_features(task_path):\n src_path = await write_codes(task_path)\n return src_path\n\nawait implement_2048_game_features('/Users/iorishinier/github/MetaGPT/workspace/game_2048/docs/task/20240401194850.json')\n",
"result": ",```python\n,## game.py\n\nclass Game2048,:\n def __init__(self):\n, self.board = [[0 for _, in range(4)] for _ in, range(4)]\n self.score =, 0\n self.game_over =, False\n\n def reset(self):\n , self.board = [[0 for _ in, range(4)] for _, in range(4)]\n self.score = 0\n self,.game_over = False\n\n , def move(self, direction: str):\n, # Implement the logic to move the, tiles in the specified direction\n, pass\n\n def get_board(self) ->, list:\n return self.board\n\n , def get_score(self) -> int:\n, return self.score\n\n def is,_game_over(self) -> bool:\n ,,,,, return self.game_over\n```\n,```,python\n## main.py\n\nimport pygame\nfrom game import Game2048\n\ndef main():\n # Initialize the game\n game = Game2048()\n game.reset()\n\n # Initialize Pygame\n, pygame.init()\n screen = pygame.display.set_mode((400, 400))\n pygame.display.set_caption('2048 Game,')\n\n running = True\n while running:\n for event in pygame.event.get():\n if, event.type == pygame.QUIT:\n running = False\n elif event.type == pygame.KEYDOWN:\n if event.key ==, pygame.K_UP:\n game.move('up')\n elif event.key == pygame.K_DOWN:\n , game.move('down')\n elif event.key == pygame.K_LEFT:\n game.move('left')\n elif, event.key == pygame.K_RIGHT,:\n game.move('right')\n\n # Display the game board and score\n board = game.get_board()\n score = game.get_score()\n display_board(screen, board, score)\n\n # Check, if the game is over\n if game.is_game_over():\n show_game_over(screen)\n\n pygame.display.update()\n\n pygame.quit()\n\n,def display_board(screen, board, score):\n # Write,,,, code to display the game board and score on the Pygame screen\n pass\n\ndef show_game_over(screen):\n # Write code to display the game over message on the Pygame screen\n pass\n\nif __name__ == \"__main__\":\n main()\n```\n,,{'output': '[{\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/__init__.py\"}, {\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/game.py\"}, {\"type_\": \"str\", \"name\": \"Source File\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048/main.py\"}]', 'tool_name': 'write_codes'},PosixPath('/Users/iorishinier/github/MetaGPT/workspace/game_2048/game_2048')",
"is_success": true,
"is_finished": true
},
{
"task_id": "5",
"dependent_task_ids": [
"4"
],
"instruction": "Stage and commit changes for the project repository using Git.",
"task_type": "other",
"code": "from metagpt.tools.libs.software_development import git_archive\n\nproject_path = '/Users/iorishinier/github/MetaGPT/workspace/game_2048'\ngit_log = await git_archive(project_path)\nprint(git_log)\n",
"result": ",{'output': '[{\"type_\": \"str\", \"name\": \"Git Commit\", \"value\": \"/Users/iorishinier/github/MetaGPT/workspace/game_2048\"}]', 'tool_name': 'git_archive'}commit 41c731341d0da8b630709bca50d4841dcea98d43\nAuthor: 莘权 马 <mashenquan@fuzhi.ai>\nDate: Mon Apr 1 19:50:53 2024 +0800\n\n Archive\n\ncommit a61daa93f24e74bf934ea5a3ba217038f167ab1f\nAuthor: 莘权 马 <mashenquan@fuzhi.ai>\nDate: Mon Apr 1 19:48:40 2024 +0800\n\n Add .gitignore\n",
"is_success": true,
"is_finished": true
}
]
## Current Task
{}
"""
DEMO2_CONTENT = [
{"role": "user", "content": 'Create a "2048 game"'},
{"role": "assistant", "content": _DEMO2_CONTENT_ASSISTANT},
{"role": "user", "content": "TypeError: __init__() takes 1 positional argument but 2 were given"},
]
DEMO3_CONTENT = [
{"role": "user", "content": "git clone 'https://github.com/spec-first/connexion' and format to MetaGPT project"}
]
@pytest.mark.asyncio
@pytest.mark.parametrize(
"user_messages",
[
[Message.model_validate(i) for i in DEMO2_CONTENT if i["role"] == "user"],
[Message.model_validate(i) for i in DEMO_CONTENT if i["role"] == "user"],
[Message.model_validate(i) for i in DEMO1_CONTENT if i["role"] == "user"],
],
)
@pytest.mark.skip
async def test_mgx(user_messages: List[Message], context):
mgx = MGX(context=context, tools=["<all>"])
for i, msg in enumerate(user_messages):
await mgx.run(msg)
data = mgx.model_dump_json()
await context.repo.test_outputs.save(filename=f"{i}.json", content=data)
@pytest.mark.asyncio
@pytest.mark.parametrize(
("user_message", "history_messages"),
[(Message.model_validate(DEMO2_CONTENT[2]), [Message.model_validate(i) for i in DEMO2_CONTENT[0:2]])],
)
@pytest.mark.skip
async def test_mgx_fixbug(user_message: Message, history_messages: List[Message], context):
mgx = MGX(context=context, tools=["<all>"])
mgx.rc.memory.add_batch(history_messages)
await mgx.run(user_message)
data = mgx.model_dump_json()
await context.repo.test_outputs.save(filename="test_mgx_fixbug.json", content=data)
@pytest.mark.asyncio
@pytest.mark.parametrize("user_message", [Message.model_validate(i) for i in DEMO3_CONTENT if i["role"] == "user"])
@pytest.mark.skip
async def test_git_import(user_message, context):
mgx = MGX(context=context, tools=["<all>"])
await mgx.run(user_message)
if __name__ == "__main__":
pytest.main([__file__, "-s"])