File size: 11,034 Bytes
be32845
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# Julia Environment Server

HTTP server for executing Julia code with test result tracking and reward calculation.

## Overview

This server provides a Julia code execution environment through OpenEnv's HTTP interface. It executes Julia code, parses test results from the `Test` module, and calculates rewards based on execution success and test outcomes.

## Features

- βœ… Execute Julia code in isolated subprocess
- βœ… Parse `Test` module output (tests passed/failed)
- βœ… Calculate rewards based on execution results
- βœ… Safety transforms for output truncation
- βœ… Docker support for reproducible execution
- βœ… Compatible with GRPO training

## Docker Setup

### Prerequisites

First, build the OpenEnv base image (one-time setup):

```bash
# From OpenEnv root directory
docker build -t openenv-base:latest -f src/core/containers/images/Dockerfile .
```

### Build Julia Environment Image

```bash
# From OpenEnv root directory
docker build -t julia-env:latest -f src/envs/julia_env/server/Dockerfile .
```

### Run the Server

```bash
# Run in background with default settings (port 8000, 4 workers)
docker run -d -p 8000:8000 --name julia-env-server julia-env:latest

# OR run in foreground (to see logs)
docker run -p 8000:8000 --name julia-env-server julia-env:latest

# Run with custom port
docker run -d -p 9000:9000 -e PORT=9000 --name julia-env-server julia-env:latest

# Run with custom number of workers (uvicorn workers)
docker run -d -p 8000:8000 -e NUM_WORKER=8 --name julia-env-server julia-env:latest

# Run with custom Julia max workers (for process pool)
docker run -d -p 8000:8000 -e JULIA_MAX_WORKERS=32 --name julia-env-server julia-env:latest

# Run with all custom configurations
docker run -d -p 9000:9000 \
  -e PORT=9000 \
  -e NUM_WORKER=8 \
  -e JULIA_MAX_WORKERS=32 \
  --name julia-env-server julia-env:latest
```

### Test the Server

```bash
# Health check
curl http://localhost:8000/health
# Expected: {"status":"healthy"}

# Check Julia version inside container
docker exec julia-env-server julia --version
# Expected: julia version 1.10.0
```

### Docker Management Commands

```bash
# View logs
docker logs julia-env-server
docker logs -f julia-env-server  # Follow logs

# Stop/start container
docker stop julia-env-server
docker start julia-env-server

# Remove container
docker rm -f julia-env-server

# Rebuild after code changes
docker build -t julia-env:latest -f src/envs/julia_env/server/Dockerfile .
docker rm -f julia-env-server
docker run -d -p 8000:8000 --name julia-env-server julia-env:latest

# Interactive debugging
docker exec -it julia-env-server /bin/bash
```

## Local Development (Without Docker)

### Prerequisites

- Python 3.10+
- Julia 1.10.0+ installed and in PATH
- FastAPI and dependencies

### Install Julia

**Using juliaup (recommended):**
```bash
curl -fsSL https://install.julialang.org | sh
```

**Or download from:** https://julialang.org/downloads/

### Install Python Dependencies

```bash
pip install fastapi uvicorn
```

### Run Server Locally

```bash
# From OpenEnv root directory
export PYTHONPATH="${PWD}/src:${PYTHONPATH}"
python -m envs.julia_env.server.app
```

Server will start at: http://localhost:8000

## API Endpoints

### Health Check
```
GET /health
Response: {"status": "healthy"}
```

### Reset Environment
```
POST /reset
Response: {
  "observation": {
    "stdout": "",
    "stderr": "",
    "exit_code": 0,
    "tests_passed": 0,
    "tests_failed": 0,
    "reward": 0.0,
    "execution_time": 0.0
  }
}
```

### Execute Code (Step)
```
POST /step
Body: {"code": "function add(a,b)\n  a+b\nend\nusing Test\n@test add(2,3)==5"}
Response: {
  "observation": {
    "stdout": "Test Passed",
    "stderr": "",
    "exit_code": 0,
    "tests_passed": 1,
    "tests_failed": 0,
    "reward": 1.0,
    "execution_time": 0.15
  },
  "reward": 1.0,
  "done": false
}
```

### Get State
```
GET /state
Response: {
  "episode_id": "uuid",
  "step_count": 5,
  "last_exit_code": 0,
  "total_tests_passed": 10,
  "total_tests_failed": 2
}
```

## Reward Structure

The environment calculates rewards based on:

- **Failed execution** (exit_code != 0): `-0.5`
- **Clean execution** (exit_code == 0): `+0.2`
- **Tests passed**: `+0.3 Γ— (passed/total)`
- **Tests failed**: `-0.2 Γ— (failed/total)`
- **All tests passed bonus**: `+0.5`

Example:
```julia
# 3 tests pass, 1 fails β†’ exit_code 1
reward = -0.5  # Failed execution
# Total: -0.5

# 3 tests pass, 0 fail β†’ exit_code 0
reward = 0.2 + 0.3 Γ— 1.0 + 0.5 = 1.0
# Total: 1.0 (perfect score!)
```

## Test Parsing

The environment parses Julia's `Test` module output:

### Method 1: Error Message Pattern
```
Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken.
β†’ tests_passed=3, tests_failed=1
```

### Method 2: Test Summary Table
```
Test Summary:      | Pass  Fail  Total  Time
Add function Tests |    3     1      4  0.5s
β†’ tests_passed=3, tests_failed=1
```

## Example Usage

### From Python Client

```python
from envs.julia_env import JuliaEnv, JuliaAction

# Connect to server
env = JuliaEnv(base_url="http://localhost:8000")

# Reset
result = env.reset()

# Execute Julia code with tests
code = """
function fibonacci(n)
    if n <= 1
        return n
    end
    return fibonacci(n-1) + fibonacci(n-2)
end

using Test
@test fibonacci(0) == 0
@test fibonacci(1) == 1
@test fibonacci(5) == 5
@test fibonacci(10) == 55
"""

result = env.step(JuliaAction(code=code))

print(f"Exit code: {result.observation.exit_code}")
print(f"Tests passed: {result.observation.tests_passed}")
print(f"Tests failed: {result.observation.tests_failed}")
print(f"Reward: {result.reward}")

# Close connection
env.close()
```

### Example Script

```bash
# From OpenEnv root
python examples/julia_simple.py
```

## GRPO Training Integration

This environment is designed for GRPO (Group Relative Policy Optimization) training:

```python
# In your GRPO training loop
async def play_julia_game(game_idx, game_id, server_url, policy, tokenizer):
    env = JuliaEnv(base_url=server_url)

    # Generate code with LLM
    prompt = format_julia_prompt(task)
    responses = await policy.generate.route(prompt)
    code = extract_julia_code(responses[0].text)

    # Execute in environment
    result = env.step(JuliaAction(code=code))

    # Get reward
    reward = result.observation.reward

    return {
        "prompt": prompt,
        "response": responses[0],
        "reward": reward,
        "tests_passed": result.observation.tests_passed,
        "tests_failed": result.observation.tests_failed
    }
```

See `examples/grpo_blackjack/` for a complete GRPO training example that can be adapted for Julia.

## Configuration

### Docker Environment Variables

The Docker container accepts the following environment variables:

- **`PORT`**: HTTP server port (default: `8000`)
  - Controls which port the FastAPI server listens on
  - Must match the port mapping in `-p` flag (e.g., `-p 9000:9000 -e PORT=9000`)

- **`NUM_WORKER`**: Number of uvicorn worker processes (default: `4`)
  - Controls parallel request handling capacity
  - More workers = more concurrent requests but higher memory usage
  - Recommended: 2-8 workers for typical workloads

- **`JULIA_MAX_WORKERS`**: Maximum Julia process pool size (default: `16`)
  - Controls maximum concurrent Julia code executions
  - Higher values allow more parallel Julia executions
  - Each worker consumes memory; tune based on available resources
  - Recommended: 8-32 workers depending on your workload

### Runtime Environment Variables

These can be set when running locally (non-Docker):

- `HOST`: Server host (default: 0.0.0.0)
- `JULIA_TIMEOUT`: Julia execution timeout in seconds (default: 60)

### Dockerfile Customization

To use a different Julia version:

```dockerfile
# In Dockerfile, change the version
RUN curl -fsSL https://install.julialang.org | sh -s -- --yes --default-channel 1.11
```

## Troubleshooting

### Julia not found
```bash
# Verify Julia is in PATH
julia --version

# In Docker, check installation
docker exec julia-env-server julia --version
```

### Port already in use
```bash
# Use different port
docker run -p 8001:8000 --name julia-env-server julia-env:latest

# Update client base_url
env = JuliaEnv(base_url="http://localhost:8001")
```

### Container exits immediately
```bash
# Check logs
docker logs julia-env-server

# Run in foreground to see errors
docker run -p 8000:8000 julia-env:latest
```

### Build failures
```bash
# Clean build with no cache
docker build --no-cache -t julia-env:latest -f src/envs/julia_env/server/Dockerfile .

# Verbose output
docker build --progress=plain -t julia-env:latest -f src/envs/julia_env/server/Dockerfile .
```

## Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Python Client (HTTP)              β”‚
β”‚   JuliaEnv                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚ HTTP POST /step
             β”‚ {"code": "..."}
             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   FastAPI Server                    β”‚
β”‚   app.py                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   JuliaCodeActEnv                   β”‚
β”‚   - Execute code via JuliaExecutor  β”‚
β”‚   - Parse test results              β”‚
β”‚   - Calculate rewards               β”‚
β”‚   - Apply transforms                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   JuliaExecutor (subprocess)        β”‚
β”‚   - Write code to temp file         β”‚
β”‚   - Run: julia temp_file.jl         β”‚
β”‚   - Capture stdout/stderr           β”‚
β”‚   - Return results                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Development

### Running Tests

```bash
# Unit tests
pytest tests/envs/julia_env/

# Integration test
python examples/julia_simple.py
```

### Code Structure

```
server/
β”œβ”€β”€ Dockerfile              # Docker build instructions
β”œβ”€β”€ README.md              # This file
β”œβ”€β”€ __init__.py            # Package initialization
β”œβ”€β”€ app.py                 # FastAPI server entry point
β”œβ”€β”€ julia_codeact_env.py   # Environment implementation
└── julia_transforms.py    # Output transforms
```

## License

BSD-style license. See LICENSE file in repository root.