config.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import os
  2. import argparse
  3. import toml
  4. import pathlib
  5. import platform
  6. from dotenv import load_dotenv
  7. from opendevin.schema import ConfigType
  8. import logging
  9. logger = logging.getLogger(__name__)
  10. DEFAULT_CONTAINER_IMAGE = 'ghcr.io/opendevin/sandbox'
  11. if os.getenv('OPEN_DEVIN_BUILD_VERSION'):
  12. DEFAULT_CONTAINER_IMAGE += ':' + (os.getenv('OPEN_DEVIN_BUILD_VERSION') or '')
  13. else:
  14. DEFAULT_CONTAINER_IMAGE += ':main'
  15. load_dotenv()
  16. DEFAULT_CONFIG: dict = {
  17. ConfigType.LLM_API_KEY: None,
  18. ConfigType.LLM_BASE_URL: None,
  19. ConfigType.WORKSPACE_BASE: os.getcwd(),
  20. ConfigType.WORKSPACE_MOUNT_PATH: None,
  21. ConfigType.WORKSPACE_MOUNT_PATH_IN_SANDBOX: '/workspace',
  22. ConfigType.WORKSPACE_MOUNT_REWRITE: None,
  23. ConfigType.CACHE_DIR: os.path.join(os.path.dirname(os.path.abspath(__file__)), '.cache'),
  24. ConfigType.LLM_MODEL: 'gpt-3.5-turbo-1106',
  25. ConfigType.SANDBOX_CONTAINER_IMAGE: DEFAULT_CONTAINER_IMAGE,
  26. ConfigType.RUN_AS_DEVIN: 'true',
  27. ConfigType.LLM_EMBEDDING_MODEL: 'local',
  28. ConfigType.LLM_EMBEDDING_DEPLOYMENT_NAME: None,
  29. ConfigType.LLM_API_VERSION: None,
  30. ConfigType.LLM_NUM_RETRIES: 5,
  31. ConfigType.LLM_RETRY_MIN_WAIT: 3,
  32. ConfigType.LLM_RETRY_MAX_WAIT: 60,
  33. ConfigType.MAX_ITERATIONS: 100,
  34. # GPT-4 pricing is $10 per 1M input tokens. Since tokenization happens on LLM side,
  35. # we cannot easily count number of tokens, but we can count characters.
  36. # Assuming 5 characters per token, 5 million is a reasonable default limit.
  37. ConfigType.MAX_CHARS: 5_000_000,
  38. ConfigType.AGENT: 'MonologueAgent',
  39. ConfigType.E2B_API_KEY: '',
  40. ConfigType.SANDBOX_TYPE: 'ssh', # Can be 'ssh', 'exec', or 'e2b'
  41. ConfigType.USE_HOST_NETWORK: 'false',
  42. ConfigType.SSH_HOSTNAME: 'localhost',
  43. ConfigType.DISABLE_COLOR: 'false',
  44. }
  45. config_str = ''
  46. if os.path.exists('config.toml'):
  47. with open('config.toml', 'rb') as f:
  48. config_str = f.read().decode('utf-8')
  49. def int_value(value, default, config_key):
  50. # FIXME use a library
  51. try:
  52. return int(value)
  53. except ValueError:
  54. logger.warning(f'Invalid value for {config_key}: {value} not applied. Using default value {default}')
  55. return default
  56. tomlConfig = toml.loads(config_str)
  57. config = DEFAULT_CONFIG.copy()
  58. for k, v in config.items():
  59. if k in os.environ:
  60. config[k] = os.environ[k]
  61. elif k in tomlConfig:
  62. config[k] = tomlConfig[k]
  63. if k in [ConfigType.LLM_NUM_RETRIES, ConfigType.LLM_RETRY_MIN_WAIT, ConfigType.LLM_RETRY_MAX_WAIT]:
  64. config[k] = int_value(config[k], v, config_key=k)
  65. def get_parser():
  66. parser = argparse.ArgumentParser(
  67. description='Run an agent with a specific task')
  68. parser.add_argument(
  69. '-d',
  70. '--directory',
  71. type=str,
  72. help='The working directory for the agent',
  73. )
  74. parser.add_argument(
  75. '-t', '--task', type=str, default='', help='The task for the agent to perform'
  76. )
  77. parser.add_argument(
  78. '-f',
  79. '--file',
  80. type=str,
  81. help='Path to a file containing the task. Overrides -t if both are provided.',
  82. )
  83. parser.add_argument(
  84. '-c',
  85. '--agent-cls',
  86. default='MonologueAgent',
  87. type=str,
  88. help='The agent class to use',
  89. )
  90. parser.add_argument(
  91. '-m',
  92. '--model-name',
  93. default=config.get(ConfigType.LLM_MODEL),
  94. type=str,
  95. help='The (litellm) model name to use',
  96. )
  97. parser.add_argument(
  98. '-i',
  99. '--max-iterations',
  100. default=config.get(ConfigType.MAX_ITERATIONS),
  101. type=int,
  102. help='The maximum number of iterations to run the agent',
  103. )
  104. parser.add_argument(
  105. '-n',
  106. '--max-chars',
  107. default=config.get(ConfigType.MAX_CHARS),
  108. type=int,
  109. help='The maximum number of characters to send to and receive from LLM per task',
  110. )
  111. return parser
  112. def parse_arguments():
  113. parser = get_parser()
  114. args, _ = parser.parse_known_args()
  115. if args.directory:
  116. config[ConfigType.WORKSPACE_BASE] = os.path.abspath(args.directory)
  117. print(f'Setting workspace base to {config[ConfigType.WORKSPACE_BASE]}')
  118. return args
  119. args = parse_arguments()
  120. def finalize_config():
  121. if config.get(ConfigType.WORKSPACE_MOUNT_REWRITE) and not config.get(ConfigType.WORKSPACE_MOUNT_PATH):
  122. base = config.get(ConfigType.WORKSPACE_BASE) or os.getcwd()
  123. parts = config[ConfigType.WORKSPACE_MOUNT_REWRITE].split(':')
  124. config[ConfigType.WORKSPACE_MOUNT_PATH] = base.replace(parts[0], parts[1])
  125. USE_HOST_NETWORK = config[ConfigType.USE_HOST_NETWORK].lower() != 'false'
  126. if USE_HOST_NETWORK and platform.system() == 'Darwin':
  127. logger.warning(
  128. 'Please upgrade to Docker Desktop 4.29.0 or later to use host network mode on macOS. '
  129. 'See https://github.com/docker/roadmap/issues/238#issuecomment-2044688144 for more information.'
  130. )
  131. config[ConfigType.USE_HOST_NETWORK] = USE_HOST_NETWORK
  132. if config.get(ConfigType.WORKSPACE_MOUNT_PATH) is None:
  133. config[ConfigType.WORKSPACE_MOUNT_PATH] = config.get(ConfigType.WORKSPACE_BASE)
  134. finalize_config()
  135. def get(key: str, required: bool = False):
  136. """
  137. Get a key from the environment variables or config.toml or default configs.
  138. """
  139. value = config.get(key)
  140. if not value and required:
  141. raise KeyError(f"Please set '{key}' in `config.toml` or `.env`.")
  142. return value
  143. _cache_dir = config.get('CACHE_DIR')
  144. if _cache_dir:
  145. pathlib.Path(_cache_dir).mkdir(parents=True, exist_ok=True)