diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index 35f624867..6ae21ebd9 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -25,11 +25,7 @@ from metagpt.actions.design_api_an import ( REFINED_DESIGN_NODE, REFINED_PROGRAM_CALL_FLOW, ) -from metagpt.const import ( - DATA_API_DESIGN_FILE_REPO, - DEFAULT_WORKSPACE_ROOT, - SEQ_FLOW_FILE_REPO, -) +from metagpt.const import DATA_API_DESIGN_FILE_REPO, SEQ_FLOW_FILE_REPO from metagpt.logs import logger from metagpt.schema import AIMessage, Document, Documents, Message from metagpt.tools.tool_registry import register_tool @@ -268,7 +264,7 @@ class WriteDesign(Action): if not output_pathname: output_pathname = Path(output_pathname) / "docs" / "sytem_design.json" elif not Path(output_pathname).is_absolute(): - output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname + output_pathname = self.config.workspace.path / output_pathname output_pathname = Path(output_pathname) await awrite(filename=output_pathname, data=design.content) output_filename = output_pathname.parent / f"{output_pathname.stem}-class-diagram" diff --git a/metagpt/actions/di/execute_nb_code.py b/metagpt/actions/di/execute_nb_code.py index b2769b839..01019b493 100644 --- a/metagpt/actions/di/execute_nb_code.py +++ b/metagpt/actions/di/execute_nb_code.py @@ -25,7 +25,6 @@ from rich.panel import Panel from rich.syntax import Syntax from metagpt.actions import Action -from metagpt.const import DEFAULT_WORKSPACE_ROOT from metagpt.logs import logger from metagpt.utils.report import NotebookReporter @@ -96,7 +95,7 @@ class ExecuteNbCode(Action): self.nb_client = RealtimeOutputNotebookClient( self.nb, timeout=self.timeout, - resources={"metadata": {"path": DEFAULT_WORKSPACE_ROOT}}, + resources={"metadata": {"path": self.config.workspace.path}}, notebook_reporter=self.reporter, coalesce_streams=True, ) @@ -276,7 +275,7 @@ class ExecuteNbCode(Action): else: raise ValueError(f"Only support for language: python, markdown, but got {language}, ") - file_path = DEFAULT_WORKSPACE_ROOT / "code.ipynb" + file_path = self.config.workspace.path / "code.ipynb" nbformat.write(self.nb, file_path) await self.reporter.async_report(file_path, "path") diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index e5b85738f..b9d3bc3ba 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -19,7 +19,7 @@ from pydantic import BaseModel, Field from metagpt.actions.action import Action from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE -from metagpt.const import DEFAULT_WORKSPACE_ROOT, PACKAGE_REQUIREMENTS_FILENAME +from metagpt.const import PACKAGE_REQUIREMENTS_FILENAME from metagpt.logs import logger from metagpt.schema import AIMessage, Document, Documents, Message from metagpt.tools.tool_registry import register_tool @@ -190,7 +190,7 @@ class WriteTasks(Action): if not output_pathname: output_pathname = Path(output_pathname) / "docs" / "project_schedule.json" elif not Path(output_pathname).is_absolute(): - output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname + output_pathname = self.config.workspace.path / output_pathname output_pathname = Path(output_pathname) await awrite(filename=output_pathname, data=file_content) md_output_filename = output_pathname.with_suffix(".md") diff --git a/metagpt/actions/requirement_analysis/framework/__init__.py b/metagpt/actions/requirement_analysis/framework/__init__.py index 3f7d31b99..5e0653088 100644 --- a/metagpt/actions/requirement_analysis/framework/__init__.py +++ b/metagpt/actions/requirement_analysis/framework/__init__.py @@ -16,7 +16,7 @@ from pydantic import BaseModel from metagpt.actions.requirement_analysis.framework.evaluate_framework import EvaluateFramework from metagpt.actions.requirement_analysis.framework.write_framework import WriteFramework -from metagpt.const import DEFAULT_WORKSPACE_ROOT +from metagpt.config2 import Config from metagpt.utils.common import awrite @@ -54,7 +54,7 @@ async def save_framework( output_dir = ( Path(output_dir) if output_dir - else DEFAULT_WORKSPACE_ROOT / (datetime.now().strftime("%Y%m%d%H%M%ST") + uuid.uuid4().hex[0:8]) + else Config.default().workspace.path / (datetime.now().strftime("%Y%m%d%H%M%ST") + uuid.uuid4().hex[0:8]) ) output_dir.mkdir(parents=True, exist_ok=True) diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index 462cc4748..e10e7333a 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -34,7 +34,6 @@ from metagpt.actions.write_prd_an import ( from metagpt.const import ( BUGFIX_FILENAME, COMPETITIVE_ANALYSIS_FILE_REPO, - DEFAULT_WORKSPACE_ROOT, REQUIREMENT_FILENAME, ) from metagpt.logs import logger @@ -312,9 +311,9 @@ class WritePRD(Action): new_prd = await self._merge(req=req, related_doc=old_prd) if not output_pathname: - output_pathname = DEFAULT_WORKSPACE_ROOT / "docs" / "prd.json" + output_pathname = self.config.workspace.path / "docs" / "prd.json" elif not Path(output_pathname).is_absolute(): - output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname + output_pathname = self.config.workspace.path / output_pathname output_pathname = Path(output_pathname) await awrite(filename=output_pathname, data=new_prd.content) competitive_analysis_filename = output_pathname.parent / f"{output_pathname.stem}-competitive-analysis" diff --git a/metagpt/config2.py b/metagpt/config2.py index 8ed9d3f6b..61c93b492 100644 --- a/metagpt/config2.py +++ b/metagpt/config2.py @@ -9,7 +9,7 @@ import os from pathlib import Path from typing import Dict, Iterable, List, Literal, Optional -from pydantic import BaseModel, model_validator +from pydantic import BaseModel, Field, model_validator from metagpt.configs.browser_config import BrowserConfig from metagpt.configs.embedding_config import EmbeddingConfig @@ -68,12 +68,12 @@ class Config(CLIParams, YamlModel): # Misc Parameters repair_llm_output: bool = False prompt_schema: Literal["json", "markdown", "raw"] = "json" - workspace: WorkspaceConfig = WorkspaceConfig() + workspace: WorkspaceConfig = Field(default_factory=WorkspaceConfig) enable_longterm_memory: bool = False code_review_k_times: int = 2 # Experience Pool Parameters - exp_pool: ExperiencePoolConfig = ExperiencePoolConfig() + exp_pool: ExperiencePoolConfig = Field(default_factory=ExperiencePoolConfig) # Will be removed in the future metagpt_tti_url: str = "" @@ -97,7 +97,7 @@ class Config(CLIParams, YamlModel): return Config.from_yaml_file(pathname) @classmethod - def default(cls, reload: bool = False): + def default(cls, reload: bool = False, **kwargs): """Load default config - Priority: env < default_config_paths - Inside default_config_paths, the latter one overwrites the former one @@ -107,7 +107,7 @@ class Config(CLIParams, YamlModel): CONFIG_ROOT / "config2.yaml", ) if reload or default_config_paths not in _CONFIG_CACHE: - dicts = [dict(os.environ)] + dicts = [kwargs, dict(os.environ)] dicts += [Config.read_yaml(path) for path in default_config_paths] final = merge_dict(dicts) _CONFIG_CACHE[default_config_paths] = Config(**final) diff --git a/metagpt/ext/cr/actions/modify_code.py b/metagpt/ext/cr/actions/modify_code.py index 2c54cdbae..820bdae4a 100644 --- a/metagpt/ext/cr/actions/modify_code.py +++ b/metagpt/ext/cr/actions/modify_code.py @@ -7,7 +7,6 @@ from typing import Optional from unidiff import PatchSet from metagpt.actions.action import Action -from metagpt.const import DEFAULT_WORKSPACE_ROOT from metagpt.ext.cr.utils.cleaner import ( add_line_num_on_patch, get_code_block_from_patch, @@ -97,7 +96,7 @@ class ModifyCode(Action): output_dir = ( Path(output_dir) if output_dir - else DEFAULT_WORKSPACE_ROOT / "modify_code" / str(datetime.date.today()) / self.pr + else self.config.workspace.path / "modify_code" / str(datetime.date.today()) / self.pr ) patch_file = output_dir / f"{patch_target_file_name}.patch" patch_file.parent.mkdir(exist_ok=True, parents=True) diff --git a/metagpt/tools/libs/gpt_v_generator.py b/metagpt/tools/libs/gpt_v_generator.py index 66c023766..62c36b2f8 100644 --- a/metagpt/tools/libs/gpt_v_generator.py +++ b/metagpt/tools/libs/gpt_v_generator.py @@ -10,7 +10,6 @@ from pathlib import Path from typing import Optional from metagpt.config2 import Config -from metagpt.const import DEFAULT_WORKSPACE_ROOT from metagpt.logs import logger from metagpt.tools.tool_registry import register_tool from metagpt.utils.common import CodeParser, encode_image @@ -86,7 +85,7 @@ class GPTvGenerator: Path: The path of the saved webpages. """ # Create a folder called webpages in the workspace directory to store HTML, CSS, and JavaScript files - webpages_path = DEFAULT_WORKSPACE_ROOT / "webpages" / save_folder_name + webpages_path = Config.default().workspace.path / "webpages" / save_folder_name logger.info(f"code will be saved at {webpages_path}") webpages_path.mkdir(parents=True, exist_ok=True) diff --git a/metagpt/tools/libs/software_development.py b/metagpt/tools/libs/software_development.py index 1a20bf087..0955faa7a 100644 --- a/metagpt/tools/libs/software_development.py +++ b/metagpt/tools/libs/software_development.py @@ -18,7 +18,7 @@ from metagpt.actions.requirement_analysis.trd import ( EvaluateTRD, WriteTRD, ) -from metagpt.const import ASSISTANT_ALIAS, DEFAULT_WORKSPACE_ROOT, TEST_DATA_PATH +from metagpt.const import ASSISTANT_ALIAS, TEST_DATA_PATH from metagpt.context import Context from metagpt.logs import ToolLogItem, log_tool_output, logger from metagpt.utils.common import aread @@ -202,7 +202,7 @@ async def write_framework( output_dir = ( Path(output_dir) if output_dir - else DEFAULT_WORKSPACE_ROOT / (datetime.now().strftime("%Y%m%d%H%M%ST") + uuid.uuid4().hex[0:8]) + else context.config.workspace.path / (datetime.now().strftime("%Y%m%d%H%M%ST") + uuid.uuid4().hex[0:8]) ) file_list = [] while not is_pass and (context.cost_manager.total_cost < context.cost_manager.max_budget): diff --git a/metagpt/tools/libs/terminal.py b/metagpt/tools/libs/terminal.py index 36fa5da15..24daa184d 100644 --- a/metagpt/tools/libs/terminal.py +++ b/metagpt/tools/libs/terminal.py @@ -4,7 +4,8 @@ from asyncio import Queue from asyncio.subprocess import PIPE, STDOUT from typing import Optional -from metagpt.const import DEFAULT_WORKSPACE_ROOT, SWE_SETUP_PATH +from metagpt.config2 import Config +from metagpt.const import SWE_SETUP_PATH from metagpt.logs import logger from metagpt.tools.tool_registry import register_tool from metagpt.utils.report import END_MARKER_VALUE, TerminalReporter @@ -151,12 +152,12 @@ class Bash(Terminal): def __init__(self): """init""" - os.environ["SWE_CMD_WORK_DIR"] = str(DEFAULT_WORKSPACE_ROOT) + os.environ["SWE_CMD_WORK_DIR"] = str(Config.default().workspace.path) super().__init__() self.start_flag = False async def start(self): - await self.run_command(f"cd {DEFAULT_WORKSPACE_ROOT}") + await self.run_command(f"cd {Config.default().workspace.path}") await self.run_command(f"source {SWE_SETUP_PATH}") async def run(self, cmd) -> str: