|
|
@@ -3,126 +3,323 @@ import logging
|
|
|
import os
|
|
|
import pathlib
|
|
|
import platform
|
|
|
+from dataclasses import dataclass, field, fields, is_dataclass
|
|
|
+from types import UnionType
|
|
|
+from typing import Any, ClassVar, get_args, get_origin
|
|
|
|
|
|
import toml
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
-from opendevin.core.schema import ConfigType
|
|
|
+from opendevin.core.utils import Singleton
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
-DEFAULT_CONTAINER_IMAGE = 'ghcr.io/opendevin/sandbox'
|
|
|
-if os.getenv('OPEN_DEVIN_BUILD_VERSION'):
|
|
|
- DEFAULT_CONTAINER_IMAGE += ':' + (os.getenv('OPEN_DEVIN_BUILD_VERSION') or '')
|
|
|
-else:
|
|
|
- DEFAULT_CONTAINER_IMAGE += ':main'
|
|
|
-
|
|
|
load_dotenv()
|
|
|
|
|
|
-DEFAULT_CONFIG: dict = {
|
|
|
- ConfigType.LLM_API_KEY: None,
|
|
|
- ConfigType.LLM_BASE_URL: None,
|
|
|
- ConfigType.LLM_CUSTOM_LLM_PROVIDER: None,
|
|
|
- ConfigType.AWS_ACCESS_KEY_ID: None,
|
|
|
- ConfigType.AWS_SECRET_ACCESS_KEY: None,
|
|
|
- ConfigType.AWS_REGION_NAME: None,
|
|
|
- ConfigType.WORKSPACE_BASE: os.getcwd(),
|
|
|
- ConfigType.WORKSPACE_MOUNT_PATH: None,
|
|
|
- ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX: '/workspace',
|
|
|
- ConfigType.WORKSPACE_MOUNT_REWRITE: None,
|
|
|
- ConfigType.CACHE_DIR: '/tmp/cache', # '/tmp/cache' is the default cache directory
|
|
|
- ConfigType.LLM_MODEL: 'gpt-3.5-turbo-1106',
|
|
|
- ConfigType.SANDBOX_CONTAINER_IMAGE: DEFAULT_CONTAINER_IMAGE,
|
|
|
- ConfigType.RUN_AS_DEVIN: True,
|
|
|
- ConfigType.LLM_EMBEDDING_MODEL: 'local',
|
|
|
- ConfigType.LLM_EMBEDDING_BASE_URL: None,
|
|
|
- ConfigType.LLM_EMBEDDING_DEPLOYMENT_NAME: None,
|
|
|
- ConfigType.LLM_API_VERSION: None,
|
|
|
- ConfigType.LLM_NUM_RETRIES: 5,
|
|
|
- ConfigType.LLM_RETRY_MIN_WAIT: 3,
|
|
|
- ConfigType.LLM_RETRY_MAX_WAIT: 60,
|
|
|
- ConfigType.MAX_ITERATIONS: 100,
|
|
|
- ConfigType.LLM_MAX_INPUT_TOKENS: None,
|
|
|
- ConfigType.LLM_MAX_OUTPUT_TOKENS: None,
|
|
|
- ConfigType.AGENT_MEMORY_MAX_THREADS: 2,
|
|
|
- ConfigType.AGENT_MEMORY_ENABLED: False,
|
|
|
- ConfigType.LLM_TIMEOUT: None,
|
|
|
- ConfigType.LLM_TEMPERATURE: None,
|
|
|
- ConfigType.LLM_TOP_P: None,
|
|
|
- # GPT-4 pricing is $10 per 1M input tokens. Since tokenization happens on LLM side,
|
|
|
- # we cannot easily count number of tokens, but we can count characters.
|
|
|
- # Assuming 5 characters per token, 5 million is a reasonable default limit.
|
|
|
- ConfigType.MAX_CHARS: 5_000_000,
|
|
|
- ConfigType.AGENT: 'CodeActAgent',
|
|
|
- ConfigType.E2B_API_KEY: '',
|
|
|
- ConfigType.SANDBOX_TYPE: 'ssh', # Can be 'ssh', 'exec', or 'e2b'
|
|
|
- ConfigType.USE_HOST_NETWORK: False,
|
|
|
- ConfigType.SSH_HOSTNAME: 'localhost',
|
|
|
- ConfigType.DISABLE_COLOR: False,
|
|
|
- ConfigType.SANDBOX_USER_ID: os.getuid() if hasattr(os, 'getuid') else None,
|
|
|
- ConfigType.SANDBOX_TIMEOUT: 120,
|
|
|
- ConfigType.GITHUB_TOKEN: None,
|
|
|
- ConfigType.SANDBOX_USER_ID: None,
|
|
|
- ConfigType.DEBUG: False,
|
|
|
-}
|
|
|
-
|
|
|
-config_str = ''
|
|
|
-if os.path.exists('config.toml'):
|
|
|
- with open('config.toml', 'rb') as f:
|
|
|
- config_str = f.read().decode('utf-8')
|
|
|
-
|
|
|
-
|
|
|
-def str_to_bool(value):
|
|
|
- if isinstance(value, bool):
|
|
|
- return value
|
|
|
- return value.lower() in [
|
|
|
- 'true',
|
|
|
- '1',
|
|
|
- ]
|
|
|
-
|
|
|
-
|
|
|
-def int_value(value, default, config_key):
|
|
|
- # FIXME use a library
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class LLMConfig(metaclass=Singleton):
|
|
|
+ model: str = 'gpt-3.5-turbo-1106'
|
|
|
+ api_key: str | None = None
|
|
|
+ base_url: str | None = None
|
|
|
+ api_version: str | None = None
|
|
|
+ embedding_model: str = 'local'
|
|
|
+ embedding_base_url: str | None = None
|
|
|
+ embedding_deployment_name: str | None = None
|
|
|
+ aws_access_key_id: str | None = None
|
|
|
+ aws_secret_access_key: str | None = None
|
|
|
+ aws_region_name: str | None = None
|
|
|
+ num_retries: int = 5
|
|
|
+ retry_min_wait: int = 3
|
|
|
+ retry_max_wait: int = 60
|
|
|
+ timeout: int | None = None
|
|
|
+ max_chars: int = 5_000_000 # fallback for token counting
|
|
|
+ temperature: float = 0
|
|
|
+ top_p: float = 0.5
|
|
|
+ custom_llm_provider: str | None = None
|
|
|
+ max_input_tokens: int | None = None
|
|
|
+ max_output_tokens: int | None = None
|
|
|
+
|
|
|
+ def defaults_to_dict(self) -> dict:
|
|
|
+ """
|
|
|
+ Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
|
|
|
+ """
|
|
|
+ dict = {}
|
|
|
+ for f in fields(self):
|
|
|
+ dict[f.name] = get_field_info(f)
|
|
|
+ return dict
|
|
|
+
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class AgentConfig(metaclass=Singleton):
|
|
|
+ name: str = 'CodeActAgent'
|
|
|
+ memory_enabled: bool = False
|
|
|
+ memory_max_threads: int = 2
|
|
|
+
|
|
|
+ def defaults_to_dict(self) -> dict:
|
|
|
+ """
|
|
|
+ Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
|
|
|
+ """
|
|
|
+ dict = {}
|
|
|
+ for f in fields(self):
|
|
|
+ dict[f.name] = get_field_info(f)
|
|
|
+ return dict
|
|
|
+
|
|
|
+
|
|
|
+@dataclass
|
|
|
+class AppConfig(metaclass=Singleton):
|
|
|
+ llm: LLMConfig = field(default_factory=LLMConfig)
|
|
|
+ agent: AgentConfig = field(default_factory=AgentConfig)
|
|
|
+ workspace_base: str = os.getcwd()
|
|
|
+ workspace_mount_path: str = os.getcwd()
|
|
|
+ workspace_mount_path_in_sandbox: str = '/workspace'
|
|
|
+ workspace_mount_rewrite: str | None = None
|
|
|
+ cache_dir: str = '/tmp/cache'
|
|
|
+ sandbox_container_image: str = 'ghcr.io/opendevin/sandbox' + (
|
|
|
+ f':{os.getenv("OPEN_DEVIN_BUILD_VERSION")}'
|
|
|
+ if os.getenv('OPEN_DEVIN_BUILD_VERSION')
|
|
|
+ else ':main'
|
|
|
+ )
|
|
|
+ run_as_devin: bool = True
|
|
|
+ max_iterations: int = 100
|
|
|
+ e2b_api_key: str = ''
|
|
|
+ sandbox_type: str = 'ssh' # Can be 'ssh', 'exec', or 'e2b'
|
|
|
+ use_host_network: bool = False
|
|
|
+ ssh_hostname: str = 'localhost'
|
|
|
+ disable_color: bool = False
|
|
|
+ sandbox_user_id: int = os.getuid() if hasattr(os, 'getuid') else 1000
|
|
|
+ sandbox_timeout: int = 120
|
|
|
+ github_token: str | None = None
|
|
|
+ debug: bool = False
|
|
|
+
|
|
|
+ defaults_dict: ClassVar[dict] = {}
|
|
|
+
|
|
|
+ def __post_init__(self):
|
|
|
+ """
|
|
|
+ Post-initialization hook, called when the instance is created with only default values.
|
|
|
+ """
|
|
|
+ AppConfig.defaults_dict = self.defaults_to_dict()
|
|
|
+
|
|
|
+ def defaults_to_dict(self) -> dict:
|
|
|
+ """
|
|
|
+ Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
|
|
|
+ """
|
|
|
+ dict = {}
|
|
|
+ for f in fields(self):
|
|
|
+ field_value = getattr(self, f.name)
|
|
|
+
|
|
|
+ # dataclasses compute their defaults themselves
|
|
|
+ if is_dataclass(type(field_value)):
|
|
|
+ dict[f.name] = field_value.defaults_to_dict()
|
|
|
+ else:
|
|
|
+ dict[f.name] = get_field_info(f)
|
|
|
+ return dict
|
|
|
+
|
|
|
+
|
|
|
+def get_field_info(field):
|
|
|
+ """
|
|
|
+ Extract information about a dataclass field: type, optional, and default.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ field: The field to extract information from.
|
|
|
+
|
|
|
+ Returns: A dict with the field's type, whether it's optional, and its default value.
|
|
|
+ """
|
|
|
+ field_type = field.type
|
|
|
+ optional = False
|
|
|
+
|
|
|
+ # for types like str | None, find the non-None type and set optional to True
|
|
|
+ # this is useful for the frontend to know if a field is optional
|
|
|
+ # and to show the correct type in the UI
|
|
|
+ # Note: this only works for UnionTypes with None as one of the types
|
|
|
+ if get_origin(field_type) is UnionType:
|
|
|
+ types = get_args(field_type)
|
|
|
+ non_none_arg = next((t for t in types if t is not type(None)), None)
|
|
|
+ if non_none_arg is not None:
|
|
|
+ field_type = non_none_arg
|
|
|
+ optional = True
|
|
|
+
|
|
|
+ # type name in a pretty format
|
|
|
+ type_name = (
|
|
|
+ field_type.__name__ if hasattr(field_type, '__name__') else str(field_type)
|
|
|
+ )
|
|
|
+
|
|
|
+ # default is always present
|
|
|
+ default = field.default
|
|
|
+
|
|
|
+ # return a schema with the useful info for frontend
|
|
|
+ return {'type': type_name.lower(), 'optional': optional, 'default': default}
|
|
|
+
|
|
|
+
|
|
|
+def load_from_env(config: AppConfig, env_or_toml_dict: dict | os._Environ):
|
|
|
+ """Reads the env-style vars and sets config attributes based on env vars or a config.toml dict.
|
|
|
+ Compatibility with vars like LLM_BASE_URL, AGENT_MEMORY_ENABLED and others.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ config: The AppConfig object to set attributes on.
|
|
|
+ env_or_toml_dict: The environment variables or a config.toml dict.
|
|
|
+ """
|
|
|
+
|
|
|
+ def get_optional_type(union_type: UnionType) -> Any:
|
|
|
+ """Returns the non-None type from an Union."""
|
|
|
+ types = get_args(union_type)
|
|
|
+ return next((t for t in types if t is not type(None)), None)
|
|
|
+
|
|
|
+ # helper function to set attributes based on env vars
|
|
|
+ def set_attr_from_env(sub_config: Any, prefix=''):
|
|
|
+ """Set attributes of a config dataclass based on environment variables."""
|
|
|
+ for field_name, field_type in sub_config.__annotations__.items():
|
|
|
+ # compute the expected env var name from the prefix and field name
|
|
|
+ # e.g. LLM_BASE_URL
|
|
|
+ env_var_name = (prefix + field_name).upper()
|
|
|
+
|
|
|
+ if is_dataclass(field_type):
|
|
|
+ # nested dataclass
|
|
|
+ nested_sub_config = getattr(sub_config, field_name)
|
|
|
+
|
|
|
+ # the agent field: the env var for agent.name is just 'AGENT'
|
|
|
+ if field_name == 'agent' and 'AGENT' in env_or_toml_dict:
|
|
|
+ setattr(nested_sub_config, 'name', env_or_toml_dict[env_var_name])
|
|
|
+
|
|
|
+ set_attr_from_env(nested_sub_config, prefix=field_name + '_')
|
|
|
+ elif env_var_name in env_or_toml_dict:
|
|
|
+ # convert the env var to the correct type and set it
|
|
|
+ value = env_or_toml_dict[env_var_name]
|
|
|
+ try:
|
|
|
+ # if it's an optional type, get the non-None type
|
|
|
+ if get_origin(field_type) is UnionType:
|
|
|
+ field_type = get_optional_type(field_type)
|
|
|
+
|
|
|
+ # Attempt to cast the env var to type hinted in the dataclass
|
|
|
+ cast_value = field_type(value)
|
|
|
+ setattr(sub_config, field_name, cast_value)
|
|
|
+ except (ValueError, TypeError):
|
|
|
+ logger.error(
|
|
|
+ f'Error setting env var {env_var_name}={value}: check that the value is of the right type'
|
|
|
+ )
|
|
|
+
|
|
|
+ # Start processing from the root of the config object
|
|
|
+ set_attr_from_env(config)
|
|
|
+
|
|
|
+
|
|
|
+def load_from_toml(config: AppConfig, toml_file: str = 'config.toml'):
|
|
|
+ """Load the config from the toml file. Supports both styles of config vars.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ config: The AppConfig object to update attributes of.
|
|
|
+ """
|
|
|
+
|
|
|
+ # try to read the config.toml file into the config object
|
|
|
+ toml_config = {}
|
|
|
+
|
|
|
try:
|
|
|
- return int(value)
|
|
|
- except ValueError:
|
|
|
+ with open(toml_file, 'r', encoding='utf-8') as toml_contents:
|
|
|
+ toml_config = toml.load(toml_contents)
|
|
|
+ except FileNotFoundError:
|
|
|
+ # the file is optional, we don't need to do anything
|
|
|
+ return
|
|
|
+ except toml.TomlDecodeError:
|
|
|
logger.warning(
|
|
|
- f'Invalid value for {config_key}: {value} not applied. Using default value {default}'
|
|
|
+ 'Cannot parse config from toml, toml values have not been applied.',
|
|
|
+ exc_info=False,
|
|
|
+ )
|
|
|
+ return
|
|
|
+
|
|
|
+ # if there was an exception or core is not in the toml, try to use the old-style toml
|
|
|
+ if 'core' not in toml_config:
|
|
|
+ # re-use the env loader to set the config from env-style vars
|
|
|
+ load_from_env(config, toml_config)
|
|
|
+ return
|
|
|
+
|
|
|
+ core_config = toml_config['core']
|
|
|
+
|
|
|
+ try:
|
|
|
+ # set llm config from the toml file
|
|
|
+ llm_config = config.llm
|
|
|
+ if 'llm' in toml_config:
|
|
|
+ llm_config = LLMConfig(**toml_config['llm'])
|
|
|
+
|
|
|
+ # set agent config from the toml file
|
|
|
+ agent_config = config.agent
|
|
|
+ if 'agent' in toml_config:
|
|
|
+ agent_config = AgentConfig(**toml_config['agent'])
|
|
|
+
|
|
|
+ # update the config object with the new values
|
|
|
+ config = AppConfig(llm=llm_config, agent=agent_config, **core_config)
|
|
|
+ except (TypeError, KeyError):
|
|
|
+ logger.warning(
|
|
|
+ 'Cannot parse config from toml, toml values have not been applied.',
|
|
|
+ exc_info=False,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def finalize_config(config: AppConfig):
|
|
|
+ """
|
|
|
+ More tweaks to the config after it's been loaded.
|
|
|
+ """
|
|
|
+
|
|
|
+ # In local there is no sandbox, the workspace will have the same pwd as the host
|
|
|
+ if config.sandbox_type == 'local':
|
|
|
+ config.workspace_mount_path_in_sandbox = config.workspace_mount_path
|
|
|
+
|
|
|
+ if config.workspace_mount_rewrite: # and not config.workspace_mount_path:
|
|
|
+ # TODO why do we need to check if workspace_mount_path is None?
|
|
|
+ base = config.workspace_base or os.getcwd()
|
|
|
+ parts = config.workspace_mount_rewrite.split(':')
|
|
|
+ config.workspace_mount_path = base.replace(parts[0], parts[1])
|
|
|
+
|
|
|
+ if config.llm.embedding_base_url is None:
|
|
|
+ config.llm.embedding_base_url = config.llm.base_url
|
|
|
+
|
|
|
+ if config.use_host_network and platform.system() == 'Darwin':
|
|
|
+ logger.warning(
|
|
|
+ 'Please upgrade to Docker Desktop 4.29.0 or later to use host network mode on macOS. '
|
|
|
+ 'See https://github.com/docker/roadmap/issues/238#issuecomment-2044688144 for more information.'
|
|
|
)
|
|
|
- return default
|
|
|
-
|
|
|
-
|
|
|
-tomlConfig = toml.loads(config_str)
|
|
|
-config = DEFAULT_CONFIG.copy()
|
|
|
-for k, v in config.items():
|
|
|
- if k in os.environ:
|
|
|
- config[k] = os.environ[k]
|
|
|
- elif k in tomlConfig:
|
|
|
- config[k] = tomlConfig[k]
|
|
|
-
|
|
|
- if k in [
|
|
|
- ConfigType.LLM_NUM_RETRIES,
|
|
|
- ConfigType.LLM_RETRY_MIN_WAIT,
|
|
|
- ConfigType.LLM_RETRY_MAX_WAIT,
|
|
|
- ]:
|
|
|
- config[k] = int_value(config[k], v, config_key=k)
|
|
|
-
|
|
|
- if k in [
|
|
|
- ConfigType.RUN_AS_DEVIN,
|
|
|
- ConfigType.USE_HOST_NETWORK,
|
|
|
- ConfigType.AGENT_MEMORY_ENABLED,
|
|
|
- ConfigType.DISABLE_COLOR,
|
|
|
- ConfigType.DEBUG,
|
|
|
- ]:
|
|
|
- config[k] = str_to_bool(config[k])
|
|
|
-# In local there is no sandbox, the workspace will have the same pwd as the host
|
|
|
-if config[ConfigType.SANDBOX_TYPE] == 'local':
|
|
|
- config[ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX] = config[
|
|
|
- ConfigType.WORKSPACE_MOUNT_PATH
|
|
|
- ]
|
|
|
|
|
|
+ # make sure cache dir exists
|
|
|
+ if config.cache_dir:
|
|
|
+ pathlib.Path(config.cache_dir).mkdir(parents=True, exist_ok=True)
|
|
|
+
|
|
|
+
|
|
|
+config = AppConfig()
|
|
|
+load_from_toml(config)
|
|
|
+load_from_env(config, os.environ)
|
|
|
+finalize_config(config)
|
|
|
|
|
|
+
|
|
|
+# Utility function for command line --group argument
|
|
|
+def get_llm_config_arg(llm_config_arg: str):
|
|
|
+ """
|
|
|
+ Get a group of llm settings from the config file.
|
|
|
+ """
|
|
|
+
|
|
|
+ # keep only the name, just in case
|
|
|
+ llm_config_arg = llm_config_arg.strip('[]')
|
|
|
+ logger.info(f'Loading llm config from {llm_config_arg}')
|
|
|
+
|
|
|
+ # load the toml file
|
|
|
+ try:
|
|
|
+ with open('config.toml', 'r', encoding='utf-8') as toml_file:
|
|
|
+ toml_config = toml.load(toml_file)
|
|
|
+ except FileNotFoundError:
|
|
|
+ return None
|
|
|
+ except toml.TomlDecodeError as e:
|
|
|
+ logger.error(f'Cannot parse llm group from {llm_config_arg}. Exception: {e}')
|
|
|
+ return None
|
|
|
+
|
|
|
+ # update the llm config with the specified section
|
|
|
+ if llm_config_arg in toml_config:
|
|
|
+ return LLMConfig(**toml_config[llm_config_arg])
|
|
|
+ logger.debug(f'Loading from toml failed for {llm_config_arg}')
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+# Command line arguments
|
|
|
def get_parser():
|
|
|
+ """
|
|
|
+ Get the parser for the command line arguments.
|
|
|
+ """
|
|
|
parser = argparse.ArgumentParser(description='Run an agent with a specific task')
|
|
|
parser.add_argument(
|
|
|
'-d',
|
|
|
@@ -142,89 +339,51 @@ def get_parser():
|
|
|
parser.add_argument(
|
|
|
'-c',
|
|
|
'--agent-cls',
|
|
|
- default=config.get(ConfigType.AGENT),
|
|
|
+ default=config.agent.name,
|
|
|
type=str,
|
|
|
help='The agent class to use',
|
|
|
)
|
|
|
parser.add_argument(
|
|
|
'-m',
|
|
|
'--model-name',
|
|
|
- default=config.get(ConfigType.LLM_MODEL),
|
|
|
+ default=config.llm.model,
|
|
|
type=str,
|
|
|
help='The (litellm) model name to use',
|
|
|
)
|
|
|
parser.add_argument(
|
|
|
'-i',
|
|
|
'--max-iterations',
|
|
|
- default=config.get(ConfigType.MAX_ITERATIONS),
|
|
|
+ default=config.max_iterations,
|
|
|
type=int,
|
|
|
help='The maximum number of iterations to run the agent',
|
|
|
)
|
|
|
parser.add_argument(
|
|
|
'-n',
|
|
|
'--max-chars',
|
|
|
- default=config.get(ConfigType.MAX_CHARS),
|
|
|
+ default=config.llm.max_chars,
|
|
|
type=int,
|
|
|
help='The maximum number of characters to send to and receive from LLM per task',
|
|
|
)
|
|
|
+ parser.add_argument(
|
|
|
+ '-l',
|
|
|
+ '--llm-config',
|
|
|
+ default=None,
|
|
|
+ type=str,
|
|
|
+ help='The group of llm settings, e.g. a [llama3] section in the toml file. Overrides model if both are provided.',
|
|
|
+ )
|
|
|
return parser
|
|
|
|
|
|
|
|
|
def parse_arguments():
|
|
|
+ """
|
|
|
+ Parse the command line arguments.
|
|
|
+ """
|
|
|
parser = get_parser()
|
|
|
args, _ = parser.parse_known_args()
|
|
|
if args.directory:
|
|
|
- config[ConfigType.WORKSPACE_BASE] = os.path.abspath(args.directory)
|
|
|
- print(f'Setting workspace base to {config[ConfigType.WORKSPACE_BASE]}')
|
|
|
+ config.workspace_base = os.path.abspath(args.directory)
|
|
|
+ print(f'Setting workspace base to {config.workspace_base}')
|
|
|
return args
|
|
|
|
|
|
|
|
|
args = parse_arguments()
|
|
|
-
|
|
|
-
|
|
|
-def finalize_config():
|
|
|
- if config.get(ConfigType.WORKSPACE_MOUNT_REWRITE) and not config.get(
|
|
|
- ConfigType.WORKSPACE_MOUNT_PATH
|
|
|
- ):
|
|
|
- base = config.get(ConfigType.WORKSPACE_BASE) or os.getcwd()
|
|
|
- parts = config[ConfigType.WORKSPACE_MOUNT_REWRITE].split(':')
|
|
|
- config[ConfigType.WORKSPACE_MOUNT_PATH] = base.replace(parts[0], parts[1])
|
|
|
-
|
|
|
- if config.get(ConfigType.WORKSPACE_MOUNT_PATH) is None:
|
|
|
- config[ConfigType.WORKSPACE_MOUNT_PATH] = os.path.abspath(
|
|
|
- config[ConfigType.WORKSPACE_BASE]
|
|
|
- )
|
|
|
-
|
|
|
- if config.get(ConfigType.LLM_EMBEDDING_BASE_URL) is None:
|
|
|
- config[ConfigType.LLM_EMBEDDING_BASE_URL] = config.get(ConfigType.LLM_BASE_URL)
|
|
|
-
|
|
|
- USE_HOST_NETWORK = config[ConfigType.USE_HOST_NETWORK]
|
|
|
- if USE_HOST_NETWORK and platform.system() == 'Darwin':
|
|
|
- logger.warning(
|
|
|
- 'Please upgrade to Docker Desktop 4.29.0 or later to use host network mode on macOS. '
|
|
|
- 'See https://github.com/docker/roadmap/issues/238#issuecomment-2044688144 for more information.'
|
|
|
- )
|
|
|
- config[ConfigType.USE_HOST_NETWORK] = USE_HOST_NETWORK
|
|
|
-
|
|
|
- if config.get(ConfigType.WORKSPACE_MOUNT_PATH) is None:
|
|
|
- config[ConfigType.WORKSPACE_MOUNT_PATH] = config.get(ConfigType.WORKSPACE_BASE)
|
|
|
-
|
|
|
-
|
|
|
-finalize_config()
|
|
|
-
|
|
|
-
|
|
|
-def get(key: ConfigType, required: bool = False):
|
|
|
- """
|
|
|
- Get a key from the environment variables or config.toml or default configs.
|
|
|
- """
|
|
|
- if not isinstance(key, ConfigType):
|
|
|
- raise ValueError(f"key '{key}' must be an instance of ConfigType Enum")
|
|
|
- value = config.get(key)
|
|
|
- if not value and required:
|
|
|
- raise KeyError(f"Please set '{key}' in `config.toml` or `.env`.")
|
|
|
- return value
|
|
|
-
|
|
|
-
|
|
|
-_cache_dir = config.get(ConfigType.CACHE_DIR)
|
|
|
-if _cache_dir:
|
|
|
- pathlib.Path(_cache_dir).mkdir(parents=True, exist_ok=True)
|