@@ -125,6 +125,51 @@ def defaults_to_dict(self) -> dict:
125
125
return result
126
126
127
127
128
+ @dataclass
129
+ class SandboxConfig (metaclass = Singleton ):
130
+ """
131
+ Configuration for the sandbox.
132
+
133
+ Attributes:
134
+ box_type: The type of sandbox to use. Options are: ssh, e2b, local.
135
+ container_image: The container image to use for the sandbox.
136
+ user_id: The user ID for the sandbox.
137
+ timeout: The timeout for the sandbox.
138
+
139
+ """
140
+
141
+ box_type : str = 'ssh'
142
+ container_image : str = 'ghcr.io/opendevin/sandbox' + (
143
+ f':{ os .getenv ("OPEN_DEVIN_BUILD_VERSION" )} '
144
+ if os .getenv ('OPEN_DEVIN_BUILD_VERSION' )
145
+ else ':main'
146
+ )
147
+ user_id : int = os .getuid () if hasattr (os , 'getuid' ) else 1000
148
+ timeout : int = 120
149
+
150
+ def defaults_to_dict (self ) -> dict :
151
+ """
152
+ Serialize fields to a dict for the frontend, including type hints, defaults, and whether it's optional.
153
+ """
154
+ dict = {}
155
+ for f in fields (self ):
156
+ dict [f .name ] = get_field_info (f )
157
+ return dict
158
+
159
+ def __str__ (self ):
160
+ attr_str = []
161
+ for f in fields (self ):
162
+ attr_name = f .name
163
+ attr_value = getattr (self , f .name )
164
+
165
+ attr_str .append (f'{ attr_name } ={ repr (attr_value )} ' )
166
+
167
+ return f"SandboxConfig({ ', ' .join (attr_str )} )"
168
+
169
+ def __repr__ (self ):
170
+ return self .__str__ ()
171
+
172
+
128
173
class UndefinedString (str , Enum ):
129
174
UNDEFINED = 'UNDEFINED'
130
175
@@ -137,6 +182,7 @@ class AppConfig(metaclass=Singleton):
137
182
Attributes:
138
183
llm: The LLM configuration.
139
184
agent: The agent configuration.
185
+ sandbox: The sandbox configuration.
140
186
runtime: The runtime environment.
141
187
file_store: The file store to use.
142
188
file_store_path: The path to the file store.
@@ -145,17 +191,14 @@ class AppConfig(metaclass=Singleton):
145
191
workspace_mount_path_in_sandbox: The path to mount the workspace in the sandbox. Defaults to /workspace.
146
192
workspace_mount_rewrite: The path to rewrite the workspace mount path to.
147
193
cache_dir: The path to the cache directory. Defaults to /tmp/cache.
148
- sandbox_container_image: The container image to use for the sandbox.
149
194
run_as_devin: Whether to run as devin.
150
195
max_iterations: The maximum number of iterations.
151
196
max_budget_per_task: The maximum budget allowed per task, beyond which the agent will stop.
152
197
e2b_api_key: The E2B API key.
153
- sandbox_type: The type of sandbox to use. Options are: ssh, exec, e2b, local.
154
198
use_host_network: Whether to use the host network.
155
199
ssh_hostname: The SSH hostname.
156
200
disable_color: Whether to disable color. For terminals that don't support color.
157
- sandbox_user_id: The user ID for the sandbox.
158
- sandbox_timeout: The timeout for the sandbox.
201
+ initialize_plugins: Whether to initialize plugins.
159
202
debug: Whether to enable debugging.
160
203
enable_auto_lint: Whether to enable auto linting. This is False by default, for regular runs of the app. For evaluation, please set this to True.
161
204
enable_cli_session: Whether to enable saving and restoring the session when run from CLI.
@@ -166,6 +209,7 @@ class AppConfig(metaclass=Singleton):
166
209
167
210
llm : LLMConfig = field (default_factory = LLMConfig )
168
211
agent : AgentConfig = field (default_factory = AgentConfig )
212
+ sandbox : SandboxConfig = field (default_factory = SandboxConfig )
169
213
runtime : str = 'server'
170
214
file_store : str = 'memory'
171
215
file_store_path : str = '/tmp/file_store'
@@ -176,21 +220,13 @@ class AppConfig(metaclass=Singleton):
176
220
workspace_mount_path_in_sandbox : str = '/workspace'
177
221
workspace_mount_rewrite : str | None = None
178
222
cache_dir : str = '/tmp/cache'
179
- sandbox_container_image : str = 'ghcr.io/opendevin/sandbox' + (
180
- f':{ os .getenv ("OPEN_DEVIN_BUILD_VERSION" )} '
181
- if os .getenv ('OPEN_DEVIN_BUILD_VERSION' )
182
- else ':main'
183
- )
184
223
run_as_devin : bool = True
185
224
max_iterations : int = 100
186
225
max_budget_per_task : float | None = None
187
226
e2b_api_key : str = ''
188
- sandbox_type : str = 'ssh' # Can be 'ssh', 'exec', or 'e2b'
189
227
use_host_network : bool = False
190
228
ssh_hostname : str = 'localhost'
191
229
disable_color : bool = False
192
- sandbox_user_id : int = os .getuid () if hasattr (os , 'getuid' ) else 1000
193
- sandbox_timeout : int = 120
194
230
initialize_plugins : bool = True
195
231
persist_sandbox : bool = False
196
232
ssh_port : int = 63710
@@ -287,7 +323,7 @@ def get_field_info(f):
287
323
288
324
def load_from_env (cfg : AppConfig , env_or_toml_dict : dict | MutableMapping [str , str ]):
289
325
"""Reads the env-style vars and sets config attributes based on env vars or a config.toml dict.
290
- Compatibility with vars like LLM_BASE_URL, AGENT_MEMORY_ENABLED and others.
326
+ Compatibility with vars like LLM_BASE_URL, AGENT_MEMORY_ENABLED, SANDBOX_TIMEOUT and others.
291
327
292
328
Args:
293
329
cfg: The AppConfig object to set attributes on.
@@ -335,6 +371,9 @@ def set_attr_from_env(sub_config: Any, prefix=''):
335
371
f'Error setting env var { env_var_name } ={ value } : check that the value is of the right type'
336
372
)
337
373
374
+ if 'SANDBOX_TYPE' in env_or_toml_dict :
375
+ logger .error ('SANDBOX_TYPE is deprecated. Please use SANDBOX_BOX_TYPE instead.' )
376
+ env_or_toml_dict ['SANDBOX_BOX_TYPE' ] = env_or_toml_dict .pop ('SANDBOX_TYPE' )
338
377
# Start processing from the root of the config object
339
378
set_attr_from_env (cfg )
340
379
@@ -380,8 +419,32 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
380
419
if 'agent' in toml_config :
381
420
agent_config = AgentConfig (** toml_config ['agent' ])
382
421
422
+ # set sandbox config from the toml file
423
+ sandbox_config = config .sandbox
424
+
425
+ # migrate old sandbox configs from [core] section to sandbox config
426
+ keys_to_migrate = [key for key in core_config if key .startswith ('sandbox_' )]
427
+ for key in keys_to_migrate :
428
+ new_key = key .replace ('sandbox_' , '' )
429
+ if new_key == 'type' :
430
+ new_key = 'box_type'
431
+ if new_key in sandbox_config .__annotations__ :
432
+ # read the key in sandbox and remove it from core
433
+ setattr (sandbox_config , new_key , core_config .pop (key ))
434
+ else :
435
+ logger .warning (f'Unknown sandbox config: { key } ' )
436
+
437
+ # the new style values override the old style values
438
+ if 'sandbox' in toml_config :
439
+ sandbox_config = SandboxConfig (** toml_config ['sandbox' ])
440
+
383
441
# update the config object with the new values
384
- AppConfig (llm = llm_config , agent = agent_config , ** core_config )
442
+ AppConfig (
443
+ llm = llm_config ,
444
+ agent = agent_config ,
445
+ sandbox = sandbox_config ,
446
+ ** core_config ,
447
+ )
385
448
except (TypeError , KeyError ) as e :
386
449
logger .warning (
387
450
f'Cannot parse config from toml, toml values have not been applied.\n Error: { e } ' ,
@@ -400,7 +463,7 @@ def finalize_config(cfg: AppConfig):
400
463
cfg .workspace_base = os .path .abspath (cfg .workspace_base )
401
464
402
465
# In local there is no sandbox, the workspace will have the same pwd as the host
403
- if cfg .sandbox_type == 'local' :
466
+ if cfg .sandbox . box_type == 'local' :
404
467
cfg .workspace_mount_path_in_sandbox = cfg .workspace_mount_path
405
468
406
469
if cfg .workspace_mount_rewrite : # and not config.workspace_mount_path:
0 commit comments