wukaixingxp commited on
Commit
f3e3629
·
verified ·
1 Parent(s): 534ba48

Upload folder using huggingface_hub

Browse files
Dockerfile CHANGED
@@ -1,26 +1,78 @@
1
- # Base image
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  FROM python:3.11-slim
3
 
4
- # Set working directory
5
- WORKDIR /app/env
 
 
 
6
 
7
  # Install system dependencies
8
  RUN apt-get update && apt-get install -y \
9
  git \
 
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
- # Copy environment files
13
- COPY . .
14
 
15
- # Install Python dependencies
16
- RUN pip install --no-cache-dir -e .
17
 
18
- # Expose port
19
- EXPOSE 8000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  # Set environment variables
22
  ENV PYTHONUNBUFFERED=1
23
  ENV ENABLE_WEB_INTERFACE=true
24
 
25
- # Run the server
 
 
 
 
 
 
 
 
 
26
  CMD ["python", "-m", "uvicorn", "coding_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # Self-contained Dockerfile for coding_env
8
+ # Works with the flattened temp directory structure created by `openenv build`:
9
+ # temp/coding_env/
10
+ # ├── openenv/ # The openenv package (copied from src/openenv)
11
+ # ├── pyproject.toml # Updated to reference local openenv
12
+ # ├── server/
13
+ # └── ...
14
+
15
  FROM python:3.11-slim
16
 
17
+ # Build arguments
18
+ ARG BUILD_MODE=in-repo
19
+ ARG ENV_NAME=coding
20
+
21
+ WORKDIR /app
22
 
23
  # Install system dependencies
24
  RUN apt-get update && apt-get install -y \
25
  git \
26
+ curl \
27
  && rm -rf /var/lib/apt/lists/*
28
 
29
+ # Install uv for fast dependency management
30
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
31
 
32
+ # Copy entire build context (the prepared coding_env directory)
33
+ COPY . /app/env
34
 
35
+ WORKDIR /app/env
36
+
37
+ # Install dependencies using uv
38
+ # The pyproject.toml has been updated to reference openenv @ file:///app/env/openenv
39
+ RUN --mount=type=cache,target=/root/.cache/uv \
40
+ uv sync --no-install-project --no-editable || \
41
+ uv pip install --system -e .
42
+
43
+ # Install the project itself (openenv-core from repo root)
44
+ RUN --mount=type=cache,target=/root/.cache/uv \
45
+ uv sync --no-editable || \
46
+ uv pip install --system -e .
47
+
48
+ # Install the coding_env package from envs/coding_env/
49
+ # This is needed when building from repo root (not via openenv build)
50
+ RUN --mount=type=cache,target=/root/.cache/uv \
51
+ if [ -d "/app/env/envs/coding_env" ]; then \
52
+ cd /app/env/envs/coding_env && \
53
+ uv pip install -e . ; \
54
+ fi
55
+
56
+ # Set PATH to include uv's venv if created
57
+ ENV PATH="/app/env/.venv/bin:$PATH"
58
+
59
+ # Set PYTHONPATH so imports work correctly
60
+ # When building from repo root, coding_env is under /app/env/envs/coding_env
61
+ # When building via openenv build (flattened), it's directly under /app/env
62
+ ENV PYTHONPATH="/app/env:/app/env/envs/coding_env"
63
 
64
  # Set environment variables
65
  ENV PYTHONUNBUFFERED=1
66
  ENV ENABLE_WEB_INTERFACE=true
67
 
68
+ # Expose port
69
+ EXPOSE 8000
70
+
71
+ # Health check
72
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
73
+ CMD curl -f http://localhost:8000/health || exit 1
74
+
75
+ # Run the FastAPI server
76
+ # Use the installed package path (coding_env.server.app) instead of the raw directory path (server.app)
77
+ # This ensures relative imports like "from ..models" work correctly
78
  CMD ["python", "-m", "uvicorn", "coding_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
build/lib/coding_env/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """Coding Environment - A Python code execution environment."""
8
+
9
+ from .client import CodingEnv
10
+ from .models import CodeAction, CodeObservation, CodeState
11
+
12
+ __all__ = ["CodingEnv", "CodeAction", "CodeObservation", "CodeState"]
build/lib/coding_env/client.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ CodingEnv
3
+ ---------
4
+ Client-side wrapper for the Coding environment server.
5
+
6
+ This client maintains a persistent WebSocket connection to the environment
7
+ server, enabling efficient multi-step interactions with lower latency.
8
+
9
+ - users instantiate CodingEnv with a base_url provided by the higher-level
10
+ vector/orchestration layer.
11
+ - Environment authors ship the Docker image that serves the API.
12
+
13
+ (Seeds, episode IDs, request IDs, capabilities can be added later in the payloads.)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from openenv.core.client_types import StepResult
19
+ from openenv.core.env_client import EnvClient
20
+
21
+ from .models import CodeAction, CodeObservation, CodeState
22
+
23
+
24
+ class CodingEnv(EnvClient[CodeAction, CodeObservation, CodeState]):
25
+ # --- HTTPEnvClient abstract hooks ---
26
+
27
+ def _step_payload(self, action: CodeAction) -> dict:
28
+ # Shape expected by the server's /step endpoint under "action"
29
+ return {
30
+ "code": action.code,
31
+ }
32
+
33
+ def _parse_result(self, payload: dict) -> StepResult[CodeObservation]:
34
+ # Expecting: { "observation": {...}, "reward": <float|null>, "done": <bool>, "info": {...} }
35
+ obs = CodeObservation(**payload["observation"])
36
+ return StepResult(
37
+ observation=obs,
38
+ reward=payload.get("reward"),
39
+ done=bool(payload.get("done", False)),
40
+ )
41
+
42
+ def _parse_state(self, payload: dict) -> CodeState:
43
+ """
44
+ Parse server response into CodeState object.
45
+
46
+ Args:
47
+ payload: JSON response from /state endpoint
48
+
49
+ Returns:
50
+ CodeState object with episode_id, step_count, and last_exit_code
51
+ """
52
+ return CodeState(
53
+ episode_id=payload.get("episode_id"),
54
+ step_count=payload.get("step_count", 0),
55
+ last_exit_code=payload.get("last_exit_code", 0),
56
+ )
build/lib/coding_env/models.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ envs/coding_env/models.py
3
+ --------------------------------
4
+ Action/Observation types for the Coding environment.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from openenv.core.env_server.types import Action, Observation, State
10
+
11
+
12
+ class CodeAction(Action):
13
+ """
14
+ Represents a single code execution request.
15
+ """
16
+
17
+ code: str
18
+ # Optional: future fields like 'lint': bool, 'timeout_s': float, etc.
19
+
20
+
21
+ class CodeObservation(Observation):
22
+ """
23
+ Result of executing code in the environment.
24
+ """
25
+
26
+ stdout: str = ""
27
+ stderr: str = ""
28
+ exit_code: int = 0
29
+
30
+
31
+ class CodeState(State):
32
+ """State for CodeAct environment with persistent execution context."""
33
+
34
+ last_exit_code: int = 0
build/lib/coding_env/openenv.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ name: coding_env
2
+ version: "0.1.0"
3
+ description: "Coding environment for OpenEnv"
4
+ action: CodeAction
5
+ observation: CodeObservation
build/lib/coding_env/server/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """Coding environment server components."""
8
+
9
+ from .python_codeact_env import PythonCodeActEnv
10
+
11
+ __all__ = ["PythonCodeActEnv"]
build/lib/coding_env/server/app.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """
8
+ FastAPI application for the Coding Environment.
9
+
10
+ This module creates an HTTP server that exposes the PythonCodeActEnv
11
+ over HTTP and WebSocket endpoints, compatible with EnvClient.
12
+
13
+ Usage:
14
+ # Development (with auto-reload):
15
+ uvicorn envs.coding_env.server.app:app --reload --host 0.0.0.0 --port 8000
16
+
17
+ # Production:
18
+ uvicorn envs.coding_env.server.app:app --host 0.0.0.0 --port 8000 --workers 4
19
+
20
+ # Or run directly:
21
+ python -m envs.coding_env.server.app
22
+ """
23
+
24
+ from openenv.core.env_server import create_app
25
+
26
+ from coding_env.models import CodeAction, CodeObservation
27
+ from coding_env.server.python_codeact_env import PythonCodeActEnv
28
+
29
+ # Create the app with web interface and README integration
30
+ # Pass the class (factory) instead of an instance for WebSocket session support
31
+ app = create_app(PythonCodeActEnv, CodeAction, CodeObservation, env_name="coding_env")
32
+
33
+
34
+ if __name__ == "__main__":
35
+ import uvicorn
36
+
37
+ uvicorn.run(app, host="0.0.0.0", port=8000)
38
+
39
+
40
+ def main():
41
+ """Main entry point for running the server."""
42
+ import uvicorn
43
+
44
+ uvicorn.run(app, host="0.0.0.0", port=8000)
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
build/lib/coding_env/server/python_codeact_env.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """
8
+ Python Code Action Environment.
9
+
10
+ This module provides a server-side environment implementation for executing
11
+ Python code actions using PyExecutor.
12
+ """
13
+
14
+ import uuid
15
+
16
+ from openenv.core.env_server.interfaces import Action, Environment, Observation
17
+ from .python_executor import PyExecutor
18
+
19
+ from ..models import CodeAction, CodeObservation, CodeState
20
+ from .transforms import create_safe_coding_transform
21
+
22
+
23
+ class PythonCodeActEnv(Environment):
24
+ """
25
+ Python Code Action Environment for executing code and tracking state.
26
+
27
+ This environment executes Python code submitted as CodeAction during step,
28
+ maintains the last exit code in its state, and returns results wrapped
29
+ in CodeObservation.
30
+
31
+ Args:
32
+ transform: Optional transform to apply to observations
33
+ additional_imports: List of additional module imports to authorize
34
+ (e.g., ["numpy", "pandas", "matplotlib"])
35
+
36
+ Example:
37
+ >>> env = PythonCodeActEnv()
38
+ >>> obs = env.reset()
39
+ >>> action = CodeAction(code="print('Hello, World!')")
40
+ >>> obs = env.step(action)
41
+ >>> print(obs.stdout) # "Hello, World!\n"
42
+ >>> print(obs.exit_code) # 0
43
+ >>> print(env.state.last_exit_code) # 0
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ ):
49
+ self.transform = create_safe_coding_transform()
50
+ self._executor = PyExecutor()
51
+ self._state = CodeState()
52
+
53
+ def reset(self) -> Observation:
54
+ """
55
+ Reset environment and start fresh execution session.
56
+
57
+ Returns:
58
+ Initial observation with empty stdout/stderr and exit_code=0
59
+ """
60
+ # Initialize fresh state
61
+ self._state = CodeState(episode_id=str(uuid.uuid4()), step_count=0)
62
+ # Add last_exit_code to state
63
+ self._state.last_exit_code = 0
64
+
65
+ # Reset executor to clear any previously defined variables/functions
66
+ self._executor = PyExecutor()
67
+
68
+ # Reset transform to clear any accumulated state
69
+ self.transform = create_safe_coding_transform()
70
+
71
+ # Return initial observation
72
+ observation = CodeObservation(
73
+ stdout="",
74
+ stderr="",
75
+ exit_code=0,
76
+ )
77
+
78
+ return self._apply_transform(observation)
79
+
80
+ def step(self, action: Action) -> Observation:
81
+ """
82
+ Execute code action and return observation.
83
+
84
+ Args:
85
+ action: CodeAction containing the code to execute
86
+
87
+ Returns:
88
+ CodeObservation with execution results (stdout, stderr, exit_code)
89
+
90
+ Raises:
91
+ ValueError: If action is not a CodeAction instance
92
+ """
93
+ if not isinstance(action, CodeAction):
94
+ raise ValueError(f"Expected CodeAction, got {type(action)}")
95
+
96
+ # Execute the code using PyExecutor
97
+ result = self._executor.run(action.code)
98
+
99
+ # Update state
100
+ self._state.step_count += 1
101
+ self._state.last_exit_code = result.exit_code
102
+
103
+ # Create observation from execution result
104
+ observation = CodeObservation(
105
+ stdout=result.stdout,
106
+ stderr=result.stderr,
107
+ exit_code=result.exit_code,
108
+ )
109
+
110
+ return self._apply_transform(observation)
111
+
112
+ @property
113
+ def state(self) -> CodeState:
114
+ """Get current environment state including last exit code."""
115
+ return self._state
build/lib/coding_env/server/python_executor.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """Local Python Executor (enhanced).
8
+
9
+ This module provides a safer wrapper around smolagents.LocalPythonExecutor
10
+ with improved exception handling and a few helpful tools registered with
11
+ the executor to make debugging executed code easier.
12
+
13
+ Key improvements:
14
+ - Register a few helper utilities via send_tools so user code can use
15
+ them for reporting (e.g. `format_exc`).
16
+ - More robust extraction of stdout/stderr/exit codes from the executor
17
+ result object, tolerant to different versions of smolagents.
18
+ - Detailed stderr on unexpected exceptions including full traceback.
19
+ - Structured logging for operational visibility.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import logging
26
+ import traceback
27
+
28
+ from smolagents import LocalPythonExecutor
29
+
30
+ from openenv.core.env_server.types import CodeExecResult
31
+
32
+ logger = logging.getLogger(__name__)
33
+ logger.addHandler(logging.NullHandler())
34
+
35
+
36
+ class PyExecutor:
37
+ """Wrapper around smolagents LocalPythonExecutor.
38
+
39
+ The wrapper registers a few non-privileged helper tools to the
40
+ LocalPythonExecutor that can be used by the executed code to
41
+ format exceptions and to safely stringify results for improved
42
+ error reporting.
43
+ """
44
+
45
+ def __init__(self, additional_imports: list[str] | None = None):
46
+ if additional_imports is None:
47
+ additional_imports = []
48
+
49
+ self._executor = LocalPythonExecutor(additional_authorized_imports=additional_imports)
50
+
51
+ # Register helpful utilities exposed to the execution environment.
52
+ # These are intentionally small, read-only helpers.
53
+ tools = {
54
+ # Provide a small helper to format the current exception in the
55
+ # executed context. This is a *string formatting* helper only.
56
+ "format_exc": traceback.format_exc,
57
+ # Safe JSON dumps with a fallback for non-serializable objects.
58
+ "safe_json_dumps": lambda obj: json.dumps(obj, default=lambda o: repr(o)),
59
+ }
60
+
61
+ # `send_tools` is the public API on LocalPythonExecutor to make
62
+ # helper callables available to the sandboxed runtime. We don't
63
+ # provide any builtins that could change the environment.
64
+ try:
65
+ self._executor.send_tools(tools)
66
+ except Exception:
67
+ # If the LocalPythonExecutor implementation doesn't support
68
+ # send_tools or fails, log and continue — the executor is still usable.
69
+ logger.debug("LocalPythonExecutor.send_tools failed; continuing without extra tools", exc_info=True)
70
+
71
+ def run(self, code: str) -> CodeExecResult:
72
+ """Execute Python code and return a CodeExecResult.
73
+
74
+ This method is intentionally defensive: it attempts to extract
75
+ meaningful stdout/stderr/exit_code information from a variety of
76
+ possible return shapes that different versions of smolagents
77
+ may provide.
78
+ """
79
+ try:
80
+ exec_result = self._executor(code)
81
+
82
+ # Default values
83
+ stdout_parts: list[str] = []
84
+ stderr_parts: list[str] = []
85
+ exit_code = 0
86
+
87
+ # Extract logs/prints
88
+ try:
89
+ logs = getattr(exec_result, "logs", None)
90
+ if logs:
91
+ stdout_parts.append(str(logs))
92
+ except Exception:
93
+ logger.debug("Failed to read exec_result.logs", exc_info=True)
94
+
95
+ # Extract the result / output value
96
+ try:
97
+ if hasattr(exec_result, "output"):
98
+ out_val = exec_result.output
99
+ # If the output is not None, stringify it in a safe way
100
+ if out_val is not None:
101
+ # Prefer JSON if possible, otherwise repr
102
+ try:
103
+ stdout_parts.append(json.dumps(out_val))
104
+ except Exception:
105
+ stdout_parts.append(repr(out_val))
106
+ except Exception:
107
+ logger.debug("Failed to read exec_result.output", exc_info=True)
108
+
109
+ # Some runtime implementations may put errors on `error` or `exception`
110
+ try:
111
+ err = getattr(exec_result, "error", None)
112
+ if err:
113
+ stderr_parts.append(str(err))
114
+ except Exception:
115
+ logger.debug("Failed to read exec_result.error", exc_info=True)
116
+
117
+ try:
118
+ ex = getattr(exec_result, "exception", None)
119
+ if ex:
120
+ stderr_parts.append(str(ex))
121
+ except Exception:
122
+ logger.debug("Failed to read exec_result.exception", exc_info=True)
123
+
124
+ # Determine exit code if provided
125
+ try:
126
+ if hasattr(exec_result, "exit_code"):
127
+ exit_code = int(exec_result.exit_code) if exec_result.exit_code is not None else 0
128
+ elif hasattr(exec_result, "success"):
129
+ # Some versions use `success` boolean
130
+ exit_code = 0 if exec_result.success else 1
131
+ else:
132
+ # Fallback: if there were any stderr parts, treat as non-zero
133
+ exit_code = 1 if stderr_parts else 0
134
+ except Exception:
135
+ logger.debug("Failed to determine exec_result exit code", exc_info=True)
136
+ exit_code = 1 if stderr_parts else 0
137
+
138
+ # Compose the final stdout/stderr strings
139
+ stdout = "\n".join(part for part in stdout_parts if part is not None)
140
+ stderr = "\n".join(part for part in stderr_parts if part is not None)
141
+
142
+ return CodeExecResult(stdout=stdout, stderr=stderr, exit_code=exit_code)
143
+
144
+ except Exception as e:
145
+ # Any unexpected exception from the LocalPythonExecutor is
146
+ # returned with a full traceback to make debugging easier.
147
+ tb = traceback.format_exc()
148
+ logger.exception("LocalPythonExecutor raised an exception during run")
149
+ return CodeExecResult(stdout="", stderr=tb, exit_code=1)
build/lib/coding_env/server/transforms.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ """Transforms specific to coding environments."""
8
+
9
+ import ast
10
+ import re
11
+
12
+ from openenv.core.env_server.base_transforms import CompositeTransform
13
+ from openenv.core.env_server.interfaces import Transform
14
+ from openenv.core.env_server.types import Observation
15
+
16
+ from coding_env.models import CodeObservation
17
+
18
+
19
+ class CodeSafetyTransform(Transform):
20
+ """Evaluates code safety and assigns penalties for dangerous patterns."""
21
+
22
+ def __init__(self, penalty: float = -1.0):
23
+ self.penalty = penalty
24
+ self.dangerous_patterns = [
25
+ r"import\s+os",
26
+ r"import\s+subprocess",
27
+ r"eval\(",
28
+ r"exec\(",
29
+ r"__import__",
30
+ r"open\(",
31
+ ]
32
+
33
+ def __call__(self, observation: Observation) -> Observation:
34
+ if not isinstance(observation, CodeObservation):
35
+ return observation
36
+
37
+ if "last_code" in observation.metadata:
38
+ code = observation.metadata["last_code"]
39
+ for pattern in self.dangerous_patterns:
40
+ if re.search(pattern, code):
41
+ observation.reward = self.penalty
42
+ observation.metadata["safety_violation"] = pattern
43
+ break
44
+ else:
45
+ if observation.reward is None:
46
+ observation.reward = 0.0
47
+
48
+ return observation
49
+
50
+
51
+ class CodeQualityTransform(Transform):
52
+ """Evaluates and rewards code quality metrics."""
53
+
54
+ def __init__(
55
+ self,
56
+ concise_bonus: float = 0.1,
57
+ max_length_threshold: int = 100,
58
+ syntax_penalty: float = -0.2,
59
+ ):
60
+ self.concise_bonus = concise_bonus
61
+ self.max_length_threshold = max_length_threshold
62
+ self.syntax_penalty = syntax_penalty
63
+
64
+ def __call__(self, observation: Observation) -> Observation:
65
+ if not isinstance(observation, CodeObservation):
66
+ return observation
67
+
68
+ quality_score = 0.0
69
+
70
+ if "last_code" in observation.metadata:
71
+ code = observation.metadata["last_code"]
72
+
73
+ # Reward concise code
74
+ if len(code.strip()) <= self.max_length_threshold:
75
+ quality_score += self.concise_bonus
76
+
77
+ # Check syntax (redundant but useful for quality assessment)
78
+ try:
79
+ ast.parse(code)
80
+ except SyntaxError:
81
+ quality_score += self.syntax_penalty
82
+
83
+ # Add to existing reward
84
+ if observation.reward is None:
85
+ observation.reward = quality_score
86
+ else:
87
+ observation.reward += quality_score
88
+
89
+ return observation
90
+
91
+
92
+ def create_safe_coding_transform() -> CompositeTransform:
93
+ """Create a transform focused on safe coding practices and quality."""
94
+ return CompositeTransform([CodeSafetyTransform(), CodeQualityTransform()])
models.py CHANGED
@@ -6,12 +6,9 @@ Action/Observation types for the Coding environment.
6
 
7
  from __future__ import annotations
8
 
9
- from dataclasses import dataclass
10
 
11
- from openenv.core.env_server.interfaces import Action, Observation, State
12
 
13
-
14
- @dataclass
15
  class CodeAction(Action):
16
  """
17
  Represents a single code execution request.
@@ -21,7 +18,6 @@ class CodeAction(Action):
21
  # Optional: future fields like 'lint': bool, 'timeout_s': float, etc.
22
 
23
 
24
- @dataclass
25
  class CodeObservation(Observation):
26
  """
27
  Result of executing code in the environment.
@@ -32,7 +28,6 @@ class CodeObservation(Observation):
32
  exit_code: int = 0
33
 
34
 
35
- @dataclass
36
  class CodeState(State):
37
  """State for CodeAct environment with persistent execution context."""
38
 
 
6
 
7
  from __future__ import annotations
8
 
9
+ from openenv.core.env_server.types import Action, Observation, State
10
 
 
11
 
 
 
12
  class CodeAction(Action):
13
  """
14
  Represents a single code execution request.
 
18
  # Optional: future fields like 'lint': bool, 'timeout_s': float, etc.
19
 
20
 
 
21
  class CodeObservation(Observation):
22
  """
23
  Result of executing code in the environment.
 
28
  exit_code: int = 0
29
 
30
 
 
31
  class CodeState(State):
32
  """State for CodeAct environment with persistent execution context."""
33
 
openenv_coding_env.egg-info/PKG-INFO ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: openenv-coding_env
3
+ Version: 0.1.0
4
+ Summary: Coding Environment for OpenEnv
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: openenv-core[core]>=0.2.0
7
+ Requires-Dist: fastapi>=0.115.0
8
+ Requires-Dist: pydantic>=2.0.0
9
+ Requires-Dist: uvicorn[standard]>=0.24.0
10
+ Requires-Dist: requests>=2.31.0
11
+ Requires-Dist: smolagents<2,>=1.22.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
14
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
15
+ Requires-Dist: ipykernel>=6.29.5; extra == "dev"
openenv_coding_env.egg-info/SOURCES.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ README.md
2
+ __init__.py
3
+ client.py
4
+ models.py
5
+ openenv.yaml
6
+ pyproject.toml
7
+ ./__init__.py
8
+ ./client.py
9
+ ./models.py
10
+ ./openenv.yaml
11
+ ./build/lib/coding_env/openenv.yaml
12
+ openenv_coding_env.egg-info/PKG-INFO
13
+ openenv_coding_env.egg-info/SOURCES.txt
14
+ openenv_coding_env.egg-info/dependency_links.txt
15
+ openenv_coding_env.egg-info/entry_points.txt
16
+ openenv_coding_env.egg-info/requires.txt
17
+ openenv_coding_env.egg-info/top_level.txt
18
+ server/__init__.py
19
+ server/app.py
20
+ server/python_codeact_env.py
21
+ server/python_executor.py
22
+ server/transforms.py
openenv_coding_env.egg-info/dependency_links.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
openenv_coding_env.egg-info/entry_points.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [console_scripts]
2
+ server = coding_env.server.app:main
openenv_coding_env.egg-info/requires.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ openenv-core[core]>=0.2.0
2
+ fastapi>=0.115.0
3
+ pydantic>=2.0.0
4
+ uvicorn[standard]>=0.24.0
5
+ requests>=2.31.0
6
+ smolagents<2,>=1.22.0
7
+
8
+ [dev]
9
+ pytest>=8.0.0
10
+ pytest-cov>=4.0.0
11
+ ipykernel>=6.29.5
openenv_coding_env.egg-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ coding_env
pyproject.toml CHANGED
@@ -8,7 +8,7 @@ version = "0.1.0"
8
  description = "Coding Environment for OpenEnv"
9
  requires-python = ">=3.10"
10
  dependencies = [
11
- "openenv-core[core] @ git+https://github.com/meta-pytorch/OpenEnv.git@fix_load_environment_metadata",
12
  "fastapi>=0.115.0",
13
  "pydantic>=2.0.0",
14
  "uvicorn[standard]>=0.24.0",
 
8
  description = "Coding Environment for OpenEnv"
9
  requires-python = ">=3.10"
10
  dependencies = [
11
+ "openenv-core[core]>=0.2.0",
12
  "fastapi>=0.115.0",
13
  "pydantic>=2.0.0",
14
  "uvicorn[standard]>=0.24.0",