diff --git a/metagpt/actions/run_code.py b/metagpt/actions/run_code.py index 1e7010e52..fa13a0980 100644 --- a/metagpt/actions/run_code.py +++ b/metagpt/actions/run_code.py @@ -12,13 +12,15 @@ RunCodeResult to standardize and unify parameter passing between WriteCode, RunCode, and DebugError. 4. According to section 2.2.3.5.7 of RFC 135, change the method of transferring file content (code files, unit test files, log files) from using the message to using the file name. + 5. Merged the `Config` class of send18:dev branch to take over the set/get operations of the Environment + class. """ -import os import subprocess import traceback from typing import Tuple from metagpt.actions.action import Action +from metagpt.config import CONFIG from metagpt.logs import logger from metagpt.schema import RunCodeResult @@ -92,7 +94,7 @@ class RunCode(Action): additional_python_paths = [str(path) for path in additional_python_paths] # Copy the current environment variables - env = os.environ.copy() + env = CONFIG.new_environ() # Modify the PYTHONPATH environment variable additional_python_paths = [working_directory] + additional_python_paths diff --git a/metagpt/config.py b/metagpt/config.py index a20f58ec1..1b70d5fa6 100644 --- a/metagpt/config.py +++ b/metagpt/config.py @@ -7,11 +7,13 @@ Provide configuration, singleton 2. Add the parameter `src_workspace` for the old version project path. """ import os +from copy import deepcopy +from typing import Any import openai import yaml -from metagpt.const import PROJECT_ROOT +from metagpt.const import OPTIONS, PROJECT_ROOT from metagpt.logs import logger from metagpt.tools import SearchEngineType, WebBrowserEngineType from metagpt.utils.singleton import Singleton @@ -42,9 +44,11 @@ class Config(metaclass=Singleton): default_yaml_file = PROJECT_ROOT / "config/config.yaml" def __init__(self, yaml_file=default_yaml_file): - self._configs = {} - self._init_with_config_files_and_env(self._configs, yaml_file) + self._init_with_config_files_and_env(yaml_file) logger.info("Config loading done.") + self._update() + + def _update(self): self.global_proxy = self._get("GLOBAL_PROXY") self.openai_api_key = self._get("OPENAI_API_KEY") self.anthropic_api_key = self._get("Anthropic_API_KEY") @@ -96,12 +100,10 @@ class Config(metaclass=Singleton): self.pyppeteer_executable_path = self._get("PYPPETEER_EXECUTABLE_PATH", "") self.prompt_format = self._get("PROMPT_FORMAT", "markdown") - self.git_repo = None - self.src_workspace = None - def _init_with_config_files_and_env(self, configs: dict, yaml_file): + def _init_with_config_files_and_env(self, yaml_file): """Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority""" - configs.update(os.environ) + configs = dict(os.environ) for _yaml_file in [yaml_file, self.key_yaml_file]: if not _yaml_file.exists(): @@ -112,11 +114,13 @@ class Config(metaclass=Singleton): yaml_data = yaml.safe_load(file) if not yaml_data: continue - os.environ.update({k: v for k, v in yaml_data.items() if isinstance(v, str)}) configs.update(yaml_data) + OPTIONS.set(configs) - def _get(self, *args, **kwargs): - return self._configs.get(*args, **kwargs) + @staticmethod + def _get(*args, **kwargs): + m = OPTIONS.get() + return m.get(*args, **kwargs) def get(self, key, *args, **kwargs): """Search for a value in config/key.yaml, config/config.yaml, and env; raise an error if not found""" @@ -125,5 +129,33 @@ class Config(metaclass=Singleton): raise ValueError(f"Key '{key}' not found in environment variables or in the YAML file") return value + def __setattr__(self, name: str, value: Any) -> None: + OPTIONS.get()[name] = value + + def __getattr__(self, name: str) -> Any: + m = OPTIONS.get() + return m.get(name) + + def set_context(self, options: dict): + """Update current config""" + if not options: + return + opts = deepcopy(OPTIONS.get()) + opts.update(options) + OPTIONS.set(opts) + self._update() + + @property + def options(self): + """Return all key-values""" + return OPTIONS.get() + + def new_environ(self): + """Return a new os.environ object""" + env = os.environ.copy() + m = self.options + env.update({k: v for k, v in m.items() if isinstance(v, str)}) + return env + CONFIG = Config() diff --git a/metagpt/const.py b/metagpt/const.py index ce06655f1..9278a5d0e 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -8,6 +8,7 @@ common properties in the Message. @Modified By: mashenquan, 2023-11-27. Defines file repository paths according to Section 2.2.3.4 of RFC 135. """ +import contextvars from pathlib import Path @@ -27,6 +28,8 @@ def get_project_root(): current_path = parent_path +OPTIONS = contextvars.ContextVar("OPTIONS") + PROJECT_ROOT = get_project_root() DATA_PATH = PROJECT_ROOT / "data" WORKSPACE_ROOT = PROJECT_ROOT / "workspace"