dsh-eval-guard 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/ISSUES.md +239 -0
  2. package/README.md +48 -18
  3. package/assets/neural-cifar/workspace.zip +0 -0
  4. package/assets/neural-cpp/workspace.zip +0 -0
  5. package/assets/neural-fp32/workspace.zip +0 -0
  6. package/assets/neural-pendulum/SPEC.md +43 -35
  7. package/assets/neural-pendulum/TASK.md +139 -33
  8. package/assets/neural-pendulum/rl-feasibility/README.md +60 -0
  9. package/assets/neural-pendulum/rl-feasibility/RESULTS.md +370 -0
  10. package/assets/neural-pendulum/rl-feasibility/evaluate.py +107 -0
  11. package/assets/neural-pendulum/rl-feasibility/finalize_run.py +168 -0
  12. package/assets/neural-pendulum/rl-feasibility/models.py +124 -0
  13. package/assets/neural-pendulum/rl-feasibility/obs.py +205 -0
  14. package/assets/neural-pendulum/rl-feasibility/plot_selected.py +55 -0
  15. package/assets/neural-pendulum/rl-feasibility/reference_controller.py +112 -0
  16. package/assets/neural-pendulum/rl-feasibility/results/curves_selected.png +0 -0
  17. package/assets/neural-pendulum/rl-feasibility/results/reference_controller.json +417 -0
  18. package/assets/neural-pendulum/rl-feasibility/results/summary.json +1599 -0
  19. package/assets/neural-pendulum/rl-feasibility/results/table.md +39 -0
  20. package/assets/neural-pendulum/rl-feasibility/sim.py +324 -0
  21. package/assets/neural-pendulum/rl-feasibility/summarize.py +175 -0
  22. package/assets/neural-pendulum/rl-feasibility/task.py +342 -0
  23. package/assets/neural-pendulum/rl-feasibility/test_contract.py +188 -0
  24. package/assets/neural-pendulum/rl-feasibility/train_ppo.py +403 -0
  25. package/assets/neural-pendulum/rl-feasibility/train_sac.py +406 -0
  26. package/assets/neural-pendulum/sim.cpp +13 -2
  27. package/assets/neural-pendulum/sim_eval +0 -0
  28. package/assets/neural-pendulum/workspace.zip +0 -0
  29. package/eval/README.md +19 -9
  30. package/eval/bomberman.py +5 -3
  31. package/eval/commit0.py +10 -3
  32. package/eval/common.py +169 -5
  33. package/eval/ctf.py +4 -2
  34. package/eval/featurebench.py +12 -8
  35. package/eval/land.py +4 -2
  36. package/eval/legacy/bomberman.py +5 -2
  37. package/eval/legacy/ctf.py +5 -2
  38. package/eval/legacy/featurebench.py +55 -15
  39. package/eval/legacy/land.py +6 -3
  40. package/eval/legacy/swe_bench_pro.py +57 -16
  41. package/eval/legacy/tank.py +2 -2
  42. package/eval/legacy/warehouse.py +4 -4
  43. package/eval/neural-cpp.py +9 -6
  44. package/eval/neural-fp32.py +17 -5
  45. package/eval/swe-bench-pro.py +11 -7
  46. package/eval/tank.py +11 -2
  47. package/eval/warehouse.py +17 -1
  48. package/lib/client/index.js +56 -29
  49. package/lib/client/index.js.map +1 -1
  50. package/lib/index.js +883 -298
  51. package/native/dsh-eval-supervisor +0 -0
  52. package/native/supervisor.c +232 -36
  53. package/neural-cifar-eval/README.md +5 -0
  54. package/neural-cifar-eval/common.py +13 -0
  55. package/neural-cifar-eval/ops_eval.py +17 -5
  56. package/neural-cifar-eval/run_eval.py +42 -32
  57. package/neural-pendulum-eval/README.md +62 -0
  58. package/neural-pendulum-eval/ops_eval.py +603 -0
  59. package/neural-pendulum-eval/run_eval.py +218 -137
  60. package/neural-pendulum-eval/sim_eval +0 -0
  61. package/package.json +7 -11
  62. package/assets/neural-pendulum/hidden/test_infra.cpp +0 -631
  63. package/assets/neural-pendulum/include/nn/nn.hpp +0 -88
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env python3
2
2
  """Standalone evaluator for the Neural-Pendulum task.
3
3
 
4
- It uses the latest preserved submission under .eval/submissions (falling back to
5
- the live workspace), builds it from source, runs the public self-test, drives
6
- the controller against a fixed-initial-state evaluation simulator, and computes
7
- the task's time-integral score from .eval/events.jsonl.
4
+ It takes the latest host-side submission, builds it once, runs the same
5
+ PyTorch-referenced operator oracle as Neural-FP32 / Neural-CIFAR (every
6
+ operation must be executable and within its error threshold), then drives the
7
+ candidate controller against the fixed-initial-state evaluation simulator and
8
+ computes the task's time-integral score from the host events staged under
9
+ .eval/.
8
10
  """
9
11
  from __future__ import annotations
10
12
 
13
+ import argparse
11
14
  import json
12
15
  import math
13
16
  import os
@@ -20,30 +23,74 @@ import time
20
23
  from pathlib import Path
21
24
 
22
25
  HERE = Path(__file__).resolve().parent
26
+ sys.path.insert(0, str(HERE))
27
+ import ops_eval # noqa: E402
28
+
23
29
  SIM = HERE / 'sim_eval'
30
+ HOST_EVAL_KEYS = frozenset({
31
+ 'EVAL_RESULT_FILE', 'EVAL_JOB_DIR', 'EVAL_INSTALL_TOKEN',
32
+ 'EVAL_BASELINE_DIR', 'EVAL_VENV_MANIFEST',
33
+ })
34
+
35
+
36
+ def child_env() -> dict[str, str]:
37
+ """Environment for build/simulator/candidate children."""
38
+ return {
39
+ key: value for key, value in os.environ.items()
40
+ if key not in HOST_EVAL_KEYS and key not in {'PYTHONPATH', 'PYTHONHOME'}
41
+ }
42
+
24
43
  DT = 0.01
25
44
  MAX_STEPS = 3000
26
45
  T_TGT = 10.0
27
46
  UP_TOL = 0.05
28
-
29
-
30
- def read_line(proc: subprocess.Popen, timeout: float) -> str:
31
- selector = selectors.DefaultSelector()
32
- selector.register(proc.stdout, selectors.EVENT_READ)
33
- try:
34
- if not selector.select(timeout):
35
- raise RuntimeError('timeout')
36
- line = proc.stdout.readline()
37
- if line == '':
38
- raise RuntimeError('process closed')
39
- return line.strip()
40
- finally:
41
- selector.close()
47
+ # The score changes at the test request instant. This process is started for
48
+ # one request, so module import is the closest available request timestamp.
49
+ TEST_TIME = time.time()
50
+ # Hard budgets so a pathological submission cannot stall the evaluation job.
51
+ OPS_DEADLINE_SECONDS = 240.0
52
+ CONTROL_DEADLINE_SECONDS = 360.0
53
+
54
+
55
+ class LineReader:
56
+ """Binary line reader with a per-read deadline.
57
+
58
+ A selector on the raw fd plus an internal buffer avoids the classic bug of
59
+ select() missing lines that a text wrapper already buffered.
60
+ """
61
+
62
+ def __init__(self, proc: subprocess.Popen) -> None:
63
+ assert proc.stdout is not None
64
+ self._fd = proc.stdout.fileno()
65
+ self._buffer = b''
66
+ self._selector = selectors.DefaultSelector()
67
+ self._selector.register(self._fd, selectors.EVENT_READ)
68
+
69
+ def readline(self, timeout: float) -> str:
70
+ deadline = time.monotonic() + timeout
71
+ while b'\n' not in self._buffer:
72
+ remaining = deadline - time.monotonic()
73
+ if remaining <= 0:
74
+ raise RuntimeError('timeout')
75
+ if not self._selector.select(remaining):
76
+ raise RuntimeError('timeout')
77
+ chunk = os.read(self._fd, 65536)
78
+ if not chunk:
79
+ raise RuntimeError('process closed')
80
+ self._buffer += chunk
81
+ line, self._buffer = self._buffer.split(b'\n', 1)
82
+ return line.decode('utf-8', 'replace').strip()
83
+
84
+ def close(self) -> None:
85
+ try:
86
+ self._selector.close()
87
+ except Exception:
88
+ pass
42
89
 
43
90
 
44
91
  def write_line(proc: subprocess.Popen, obj: dict) -> None:
45
92
  assert proc.stdin is not None
46
- proc.stdin.write(json.dumps(obj) + '\n')
93
+ proc.stdin.write((json.dumps(obj) + '\n').encode('utf-8'))
47
94
  proc.stdin.flush()
48
95
 
49
96
 
@@ -87,7 +134,7 @@ def score_for(event: dict) -> float:
87
134
 
88
135
 
89
136
  def final_score(workspace: Path, events: list[dict], now: float | None = None) -> tuple[float, float | None]:
90
- """Return (integral, T_done); negative score is penalized over time."""
137
+ """Return (integral, T_done); a negative score is penalized over time."""
91
138
  now = time.time() if now is None else now
92
139
  start = task_start(workspace, events)
93
140
  tests = [e for e in events if (e.get('event') or e.get('type')) == 'test']
@@ -112,25 +159,6 @@ def final_score(workspace: Path, events: list[dict], now: float | None = None) -
112
159
  return total, done_at
113
160
 
114
161
 
115
- def archive_submissions(workspace: Path) -> int:
116
- root = os.environ.get('EVAL_SUBMISSIONS_ROOT', '')
117
- src_root = workspace / '.eval' / 'submissions'
118
- if not root or not src_root.is_dir():
119
- return 0
120
- target = Path(root)
121
- target.mkdir(parents=True, exist_ok=True)
122
- count = 0
123
- for source in src_root.iterdir():
124
- if not source.is_dir():
125
- continue
126
- destination = target / source.name
127
- if destination.exists():
128
- continue
129
- shutil.copytree(source, destination, symlinks=True)
130
- count += 1
131
- return count
132
-
133
-
134
162
  def latest_submission(workspace: Path) -> Path | None:
135
163
  root = workspace / '.eval' / 'submissions'
136
164
  if not root.is_dir():
@@ -141,106 +169,151 @@ def latest_submission(workspace: Path) -> Path | None:
141
169
  return max(dirs, key=lambda p: p.stat().st_mtime)
142
170
 
143
171
 
144
- def fail(message: str, infra_ok: bool = False, workspace: Path | None = None) -> dict:
145
- result = {'infra_ok': infra_ok, 't_can': 0.0, 'success': False, 'error': message}
146
- if workspace is not None:
147
- events = read_events(workspace)
148
- result['final_score'], done = final_score(workspace, events)
149
- result['done_at'] = done
150
- result['submissions'] = len(list((workspace / '.eval' / 'submissions').glob('*'))) if (workspace / '.eval' / 'submissions').is_dir() else 0
172
+ def submission_count(workspace: Path) -> int:
173
+ root = workspace / '.eval' / 'submissions'
174
+ return len(list(root.glob('*'))) if root.is_dir() else 0
175
+
176
+
177
+ def emit(result: dict) -> int:
178
+ target = os.environ.get('EVAL_RESULT_FILE')
179
+ text = json.dumps(result, ensure_ascii=False, indent=2)
180
+ if target:
181
+ Path(target).write_text(text, encoding='utf-8')
182
+ else:
183
+ print(text)
184
+ return 0
185
+
186
+
187
+ def final_score_with_current(workspace: Path, events: list[dict], stage: str,
188
+ t_can: float, success: bool) -> tuple[float, float | None]:
189
+ """Score including the test this process is performing at TEST_TIME."""
190
+ current = {
191
+ 'event': 'test', 'time': TEST_TIME, 'stage': stage,
192
+ 't_can': float(t_can), 'completed': bool(success),
193
+ }
194
+ return final_score(workspace, [*events, current])
195
+
196
+
197
+ def build_result(stage: str, workspace: Path, events: list[dict], error: str = '',
198
+ ops: dict | None = None, source: str = '', t_can: float = 0.0,
199
+ success: bool = False) -> dict:
200
+ integral, done_at = final_score_with_current(workspace, events, stage, t_can, success)
201
+ result = {
202
+ 'task': 'neural-pendulum',
203
+ 'ok': True,
204
+ 'stage': stage,
205
+ 'infra_ok': False,
206
+ 't_can': float(t_can),
207
+ 'success': bool(success),
208
+ 'error': error,
209
+ 'final_score': integral,
210
+ 'done_at': done_at,
211
+ 'submissions': submission_count(workspace),
212
+ 'source': source,
213
+ }
214
+ if ops is not None:
215
+ result.update({
216
+ 'all_operations_executable': ops.get('all_operations_executable'),
217
+ 'all_operations_passed': ops.get('all_operations_passed'),
218
+ 'max_weighted_mean_relative_error': ops.get('max_weighted_mean_relative_error'),
219
+ 'speed_geomean': ops.get('speed_geomean'),
220
+ 'operations': ops.get('operations', []),
221
+ })
151
222
  return result
152
223
 
153
224
 
225
+ def fail(message: str, stage: str, workspace: Path, events: list[dict],
226
+ ops: dict | None = None) -> dict:
227
+ return build_result(stage, workspace, events, error=message, ops=ops)
228
+
229
+
154
230
  def main() -> int:
155
- import argparse
156
- parser = argparse.ArgumentParser()
231
+ parser = argparse.ArgumentParser(description='Neural-Pendulum evaluator')
157
232
  parser.add_argument('--workspace', required=True)
158
233
  args = parser.parse_args()
159
-
160
234
  workspace = Path(args.workspace).resolve()
161
235
  events = read_events(workspace)
162
236
  root = Path(tempfile.mkdtemp(prefix='neural-pendulum-eval-'))
163
- candidate = root / 'candidate'
164
- result = {'infra_ok': False, 't_can': 0.0, 'success': False, 'error': ''}
165
237
  sim = None
166
238
  control = None
239
+ sim_reader = None
240
+ control_reader = None
167
241
  try:
168
- archived = archive_submissions(workspace)
169
242
  selected = latest_submission(workspace)
170
243
  if selected is None:
171
- return emit(fail('no submission', workspace=workspace))
172
- source = selected
173
- shutil.copytree(source, candidate, symlinks=True)
174
- if not (candidate / 'CMakeLists.txt').is_file():
175
- return emit(fail('CMakeLists.txt missing', workspace=workspace))
176
-
177
- env = dict(os.environ)
178
- env.pop('PYTHONPATH', None)
179
- env.pop('PYTHONHOME', None)
180
- build = candidate / 'build'
181
- if build.exists():
182
- shutil.rmtree(build, ignore_errors=True)
183
- cfg = subprocess.run(
184
- ['cmake', '-S', '.', '-B', 'build', '-DCMAKE_BUILD_TYPE=Release'],
185
- cwd=str(candidate), capture_output=True, text=True, env=env, timeout=180,
186
- )
187
- if cfg.returncode != 0:
188
- return emit(fail('cmake configure failed\n' + (cfg.stdout + cfg.stderr)[-4000:], workspace=workspace))
189
- made = subprocess.run(
190
- ['cmake', '--build', 'build', '-j4'],
191
- cwd=str(candidate), capture_output=True, text=True, env=env, timeout=1800,
192
- )
193
- if made.returncode != 0:
194
- return emit(fail('build failed\n' + (made.stdout + made.stderr)[-4000:], workspace=workspace))
195
-
196
- lib = candidate / 'build' / 'libnn.a'
197
- if not lib.is_file():
198
- return emit(fail('build/libnn.a missing', workspace=workspace))
199
- hidden = HERE.parent / 'assets' / 'neural-pendulum' / 'hidden' / 'test_infra.cpp'
200
- if not hidden.is_file():
201
- return emit(fail('hidden infrastructure test missing', workspace=workspace))
202
- infra_exe = root / 'test_infra'
203
- compiler = shutil.which('g++')
204
- if compiler is None:
205
- return emit(fail('g++ missing', workspace=workspace))
206
- ic = subprocess.run([compiler, '-std=c++17', '-O3', '-pthread',
207
- '-I', str(candidate / 'include'), str(hidden), str(lib),
208
- '-lm', '-o', str(infra_exe)], cwd=str(root), capture_output=True,
209
- text=True, env=env, timeout=300)
210
- if ic.returncode != 0:
211
- return emit(fail('hidden infrastructure test compile failed\n' + (ic.stdout + ic.stderr)[-4000:], workspace=workspace))
212
- it = subprocess.run([str(infra_exe)], cwd=str(root), capture_output=True,
213
- text=True, env=env, timeout=600)
214
- if it.returncode != 0:
215
- return emit(fail('hidden infrastructure test failed\n' + (it.stdout + it.stderr)[-4000:], workspace=workspace))
216
-
217
- control_path = candidate / 'build' / 'control'
218
- if not control_path.is_file():
219
- return emit(fail('build/control missing', infra_ok=True, workspace=workspace))
244
+ return emit(fail('no submission', 'no_submission', workspace, events))
245
+ source_name = str(selected.relative_to(workspace))
246
+
247
+ try:
248
+ source = ops_eval.prepare_source(selected, root)
249
+ build = ops_eval.build_binaries(source)
250
+ except Exception as exc:
251
+ return emit(fail(str(exc), 'compile', workspace, events))
252
+
253
+ ops_binary = build / 'nn_task'
254
+ control_path = build / 'control'
255
+ missing = [name for name, path in (('build/nn_task', ops_binary), ('build/control', control_path))
256
+ if not path.is_file()]
257
+ if missing:
258
+ return emit(fail('build output missing: ' + ', '.join(missing), 'compile', workspace, events))
220
259
 
260
+ try:
261
+ ops = ops_eval.evaluate_ops(ops_binary, root, deadline=time.monotonic() + OPS_DEADLINE_SECONDS)
262
+ except Exception as exc:
263
+ return emit(fail('operator evaluation failed: ' + str(exc), 'infra', workspace, events))
264
+ ops_eval.print_summary(ops)
265
+ if not ops['all_operations_passed']:
266
+ return emit(fail(ops_eval.infrastructure_error(ops), 'infra', workspace, events, ops=ops))
267
+
268
+ env = child_env()
221
269
  sim = subprocess.Popen([str(SIM), '--eval'], stdin=subprocess.PIPE,
222
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
223
- text=True, bufsize=1)
224
- control = subprocess.Popen([str(control_path)], cwd=str(candidate),
225
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
226
- stderr=subprocess.PIPE, text=True, bufsize=1)
270
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, env=env)
271
+ control = subprocess.Popen([str(control_path)], cwd=str(source),
272
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
273
+ stderr=subprocess.DEVNULL, env=env)
274
+ sim_reader = LineReader(sim)
275
+ control_reader = LineReader(control)
227
276
 
228
277
  write_line(sim, {'cmd': 'reset'})
229
- obs = json.loads(read_line(sim, 10.0))['obs']
230
- write_line(control, {'type': 'reset'})
231
- ready = json.loads(read_line(control, 10.0))
232
- if ready.get('type') != 'ready':
233
- return emit(fail('controller did not reply ready', infra_ok=True, workspace=workspace))
278
+ try:
279
+ reply = json.loads(sim_reader.readline(10.0))
280
+ obs = reply['obs']
281
+ except Exception as exc:
282
+ return emit(fail('simulator reset failed: ' + str(exc), 'simulation', workspace, events, ops=ops))
283
+ try:
284
+ write_line(control, {'type': 'reset'})
285
+ ready = json.loads(control_reader.readline(10.0))
286
+ except Exception as exc:
287
+ return emit(fail('controller reset failed: ' + str(exc), 'controller', workspace, events, ops=ops))
288
+ if not isinstance(ready, dict) or ready.get('type') != 'ready':
289
+ return emit(fail('controller did not reply ready', 'controller', workspace, events, ops=ops))
234
290
 
235
291
  t_can = 0.0
236
292
  up_start = None
237
293
  completed = False
238
294
  error = ''
295
+ stage = 'scoring'
296
+ control_deadline = time.monotonic() + CONTROL_DEADLINE_SECONDS
239
297
  for step in range(MAX_STEPS):
298
+ if time.monotonic() > control_deadline:
299
+ error = 'control evaluation deadline exceeded'
300
+ stage = 'simulation'
301
+ break
240
302
  if not isinstance(obs, list) or len(obs) != 3:
241
303
  error = 'simulator returned bad observation'
304
+ stage = 'simulation'
305
+ break
306
+ try:
307
+ angles = [float(x) for x in obs]
308
+ except Exception:
309
+ error = 'simulator returned non-numeric observation'
310
+ stage = 'simulation'
242
311
  break
243
- if max(abs(float(x)) for x in obs) < UP_TOL:
312
+ if not all(math.isfinite(x) for x in angles):
313
+ error = 'simulator returned non-finite observation'
314
+ stage = 'simulation'
315
+ break
316
+ if max(abs(x) for x in angles) <= UP_TOL:
244
317
  if up_start is None:
245
318
  up_start = step * DT
246
319
  t_can = max(t_can, step * DT - up_start)
@@ -248,33 +321,51 @@ def main() -> int:
248
321
  up_start = None
249
322
  if t_can >= T_TGT:
250
323
  completed = True
324
+ stage = 'completed'
251
325
  break
252
326
 
253
- write_line(control, {'type': 'step', 'step': step, 'state': obs})
254
- action = json.loads(read_line(control, 0.1))
255
- torque = action.get('torque')
327
+ write_line(control, {'type': 'step', 'step': step, 'state': angles})
328
+ try:
329
+ action = json.loads(control_reader.readline(0.1))
330
+ except Exception as exc:
331
+ error = 'controller did not reply in time: ' + str(exc)
332
+ stage = 'controller'
333
+ break
334
+ torque = action.get('torque') if isinstance(action, dict) else None
256
335
  if (not isinstance(torque, list) or len(torque) != 2
257
- or not all(isinstance(v, (int, float)) and math.isfinite(v) for v in torque)
336
+ or not all(isinstance(v, (int, float)) and not isinstance(v, bool)
337
+ and math.isfinite(v) for v in torque)
258
338
  or any(abs(float(v)) > 1.0 for v in torque)):
259
339
  error = 'controller returned invalid torque'
340
+ stage = 'controller'
341
+ break
342
+ write_line(sim, {'cmd': 'step', 'torque': [float(torque[0]), float(torque[1])]})
343
+ try:
344
+ reply = json.loads(sim_reader.readline(10.0))
345
+ except Exception as exc:
346
+ error = 'simulator did not reply: ' + str(exc)
347
+ stage = 'simulation'
348
+ break
349
+ if not isinstance(reply, dict):
350
+ error = 'simulator returned bad reply'
351
+ stage = 'simulation'
260
352
  break
261
- write_line(sim, {'cmd': 'step', 'torque': torque})
262
- reply = json.loads(read_line(sim, 10.0))
263
353
  if 'error' in reply:
354
+ error = 'simulator error: ' + str(reply['error'])
355
+ stage = 'simulation'
264
356
  break
265
- obs = reply['obs']
357
+ obs = reply.get('obs')
266
358
 
267
- integral, done_at = final_score(workspace, events)
268
- result = {'infra_ok': True, 't_can': float(t_can),
269
- 'success': bool(completed), 'error': error,
270
- 'final_score': integral, 'done_at': done_at,
271
- 'submissions': len(list((workspace / '.eval' / 'submissions').glob('*'))) if (workspace / '.eval' / 'submissions').is_dir() else 0,
272
- 'archived_submissions': archived,
273
- 'source': str(selected.relative_to(workspace)) if selected is not None else 'live-workspace'}
359
+ result = build_result(stage, workspace, events, error=error, ops=ops, source=source_name,
360
+ t_can=t_can, success=completed)
361
+ result['infra_ok'] = True
274
362
  return emit(result)
275
363
  except Exception as exc:
276
- return emit(fail(str(exc), workspace=workspace))
364
+ return emit(fail(str(exc), 'simulation', workspace, events))
277
365
  finally:
366
+ for reader in (control_reader, sim_reader):
367
+ if reader is not None:
368
+ reader.close()
278
369
  for proc in (control, sim):
279
370
  if proc is not None:
280
371
  try:
@@ -288,15 +379,5 @@ def main() -> int:
288
379
  shutil.rmtree(root, ignore_errors=True)
289
380
 
290
381
 
291
- def emit(result: dict) -> int:
292
- target = os.environ.get('EVAL_RESULT_FILE')
293
- text = json.dumps(result, ensure_ascii=False, indent=2)
294
- if target:
295
- Path(target).write_text(text, encoding='utf-8')
296
- else:
297
- print(text)
298
- return 0
299
-
300
-
301
382
  if __name__ == '__main__':
302
383
  raise SystemExit(main())
Binary file
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "dsh-eval-guard",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "type": "module",
5
- "description": "Evaluation guard for DSH: workspace-only filesystem, no network/subagents, wall-clock limits, and task installation for the meow-ai-arena tank task.",
5
+ "description": "Evaluation guard for DSH: workspace-only filesystem, no network/subagents, wall-clock limits, and task installation for meow-ai-arena and benchmark tasks.",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "huhu23333",
@@ -28,14 +28,8 @@
28
28
  ],
29
29
  "main": "lib/index.js",
30
30
  "exports": {
31
- ".": {
32
- "types": "./lib/index.d.ts",
33
- "default": "./lib/index.js"
34
- },
35
- "./client": {
36
- "types": "./lib/client/index.d.ts",
37
- "default": "./lib/client/index.js"
38
- },
31
+ ".": "./lib/index.js",
32
+ "./client": "./lib/client/index.js",
39
33
  "./package.json": "./package.json"
40
34
  },
41
35
  "files": [
@@ -45,14 +39,16 @@
45
39
  "eval",
46
40
  "cordis.patch.yml",
47
41
  "README.md",
42
+ "ISSUES.md",
48
43
  "scripts",
49
44
  "neural-cifar-eval",
50
45
  "neural-pendulum-eval"
51
46
  ],
52
47
  "scripts": {
53
- "build": "tsdown",
48
+ "build": "tsdown && node scripts/build-native.mjs",
54
49
  "build:native": "node scripts/build-native.mjs",
55
50
  "prepare": "tsdown",
51
+ "test": "node test/simulate.ts && node test/scoring.ts && node test/jobs.ts && node test/pendulum-sim.ts",
56
52
  "typecheck": "tsc --noEmit -p tsconfig.json"
57
53
  },
58
54
  "dsh": {