Arry7868 commited on
Commit
374f6bf
Β·
verified Β·
1 Parent(s): 76ec55d

Upload 36 files

Browse files
api/__pycache__/server.cpython-312.pyc CHANGED
Binary files a/api/__pycache__/server.cpython-312.pyc and b/api/__pycache__/server.cpython-312.pyc differ
 
api/server.py CHANGED
@@ -7,9 +7,9 @@ Endpoints:
7
 
8
  from __future__ import annotations
9
 
10
- from typing import Any
11
 
12
- from fastapi import FastAPI, HTTPException
13
  from pydantic import BaseModel, Field
14
 
15
  from kaggle_sim_env.environment import KaggleSimEnv
@@ -78,9 +78,14 @@ class ActionCategoryEntry(BaseModel):
78
  # ---------------------------------------------------------------------------
79
 
80
  @app.post("/reset", response_model=Observation)
81
- def reset(req: ResetRequest) -> Observation:
82
  try:
83
- return env.reset(task_id=req.task_id)
 
 
 
 
 
84
  except ValueError as exc:
85
  raise HTTPException(status_code=400, detail=str(exc))
86
 
@@ -307,4 +312,37 @@ def _baseline_plan(task_id: str) -> list[Action]:
307
 
308
  @app.get("/health")
309
  def health() -> dict[str, str]:
310
- return {"status": "ok"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  from __future__ import annotations
9
 
10
+ from typing import Any, Optional
11
 
12
+ from fastapi import Body, FastAPI, HTTPException, Request
13
  from pydantic import BaseModel, Field
14
 
15
  from kaggle_sim_env.environment import KaggleSimEnv
 
78
  # ---------------------------------------------------------------------------
79
 
80
  @app.post("/reset", response_model=Observation)
81
+ async def reset(request: Request) -> Observation:
82
  try:
83
+ body = await request.json()
84
+ task_id = body.get("task_id", "easy_churn")
85
+ except Exception:
86
+ task_id = "easy_churn"
87
+ try:
88
+ return env.reset(task_id=task_id)
89
  except ValueError as exc:
90
  raise HTTPException(status_code=400, detail=str(exc))
91
 
 
312
 
313
  @app.get("/health")
314
  def health() -> dict[str, str]:
315
+ """OpenEnv runtime check expects status == 'healthy'."""
316
+ return {"status": "healthy"}
317
+
318
+
319
+ @app.get("/metadata")
320
+ def metadata() -> dict[str, str]:
321
+ """OpenEnv standard metadata endpoint."""
322
+ return {
323
+ "name": "KaggleSimEnv",
324
+ "description": (
325
+ "RL environment simulating Kaggle competitions with hierarchical actions, "
326
+ "causal dataset properties, failure-mode traps, and contextual scoring."
327
+ ),
328
+ }
329
+
330
+
331
+ @app.get("/schema")
332
+ def schema_endpoint() -> dict[str, Any]:
333
+ """OpenEnv combined JSON Schema for action, observation, and state."""
334
+ return {
335
+ "action": Action.model_json_schema(),
336
+ "observation": Observation.model_json_schema(),
337
+ "state": EnvState.model_json_schema(),
338
+ }
339
+
340
+
341
+ @app.post("/mcp")
342
+ def mcp_stub(payload: dict[str, Any] = Body(default_factory=dict)) -> dict[str, Any]:
343
+ """Minimal JSON-RPC envelope for OpenEnv runtime validation."""
344
+ return {
345
+ "jsonrpc": "2.0",
346
+ "id": payload.get("id"),
347
+ "result": {"ok": True},
348
+ }
baseline/__init__.py CHANGED
@@ -0,0 +1 @@
 
 
1
+ """Baseline agent package."""
baseline/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (209 Bytes). View file
 
baseline/__pycache__/run_baseline.cpython-312.pyc ADDED
Binary file (15 kB). View file
 
inference.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Inference script β€” KaggleSimEnv (submission entrypoint)
3
+ ======================================================
4
+
5
+ MANDATORY (environment configuration)
6
+ -------------------------------------
7
+ - API_BASE_URL : LLM API base URL (e.g. https://router.huggingface.co/v1)
8
+ - MODEL_NAME : Model id for chat completions
9
+ - HF_TOKEN : Hugging Face / API key (passed to OpenAI client as api_key)
10
+
11
+ Optional
12
+ --------
13
+ - ENV_URL : Base URL of the running KaggleSimEnv Space or server (default: http://127.0.0.1:7860)
14
+
15
+ Requirements
16
+ ------------
17
+ - Named ``inference.py`` in the project root
18
+ - All LLM calls go through ``openai.OpenAI`` using the variables above
19
+ - Completes all tasks from ``GET /tasks``, runs each episode, then ``POST /grader``; prints scores in [0.0, 1.0]
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import os
26
+ import sys
27
+ from typing import Any
28
+
29
+ import requests
30
+ from openai import OpenAI
31
+
32
+ from baseline.run_baseline import SYSTEM_PROMPT, build_user_message, parse_llm_action
33
+ from kaggle_sim_env.models import Action
34
+
35
+ # Tunables (keep total runtime modest on 2 vCPU / 8GB)
36
+ REQUEST_TIMEOUT_S = 60
37
+ MAX_LLM_STEPS_PER_TASK = 14
38
+ TEMPERATURE = 0.0
39
+ MAX_TOKENS = 256
40
+
41
+
42
+ def _require_env(name: str) -> str:
43
+ v = os.getenv(name, "").strip()
44
+ if not v:
45
+ print(f"Error: required environment variable {name} is not set.", file=sys.stderr)
46
+ sys.exit(1)
47
+ return v
48
+
49
+
50
+ def _client() -> OpenAI:
51
+ base_url = _require_env("API_BASE_URL")
52
+ _require_env("MODEL_NAME")
53
+ # Spec: HF_TOKEN; allow OPENAI_API_KEY for local OpenAI-only runs
54
+ api_key = os.getenv("HF_TOKEN", "").strip() or os.getenv("OPENAI_API_KEY", "").strip()
55
+ if not api_key:
56
+ print(
57
+ "Error: set HF_TOKEN (or OPENAI_API_KEY for local OpenAI).",
58
+ file=sys.stderr,
59
+ )
60
+ sys.exit(1)
61
+ return OpenAI(base_url=base_url, api_key=api_key)
62
+
63
+
64
+ def _env_base() -> str:
65
+ return os.getenv("ENV_URL", "http://127.0.0.1:7860").rstrip("/")
66
+
67
+
68
+ def list_task_ids(base: str) -> list[str]:
69
+ r = requests.get(f"{base}/tasks", timeout=REQUEST_TIMEOUT_S)
70
+ r.raise_for_status()
71
+ data = r.json()
72
+ return [t["task_id"] for t in data]
73
+
74
+
75
+ def coerce_action_for_step(raw: Any) -> dict[str, Any]:
76
+ """Normalize LLM output so POST /step matches api.server StepRequest + Action."""
77
+ fallback = {"action_type": "submit", "parameters": {}}
78
+ if not isinstance(raw, dict):
79
+ return fallback
80
+ d = raw
81
+ if "action" in d and isinstance(d["action"], dict):
82
+ d = d["action"]
83
+ at = d.get("action_type")
84
+ params = d.get("parameters")
85
+ if params is None:
86
+ params = {}
87
+ if isinstance(params, str):
88
+ try:
89
+ params = json.loads(params)
90
+ except json.JSONDecodeError:
91
+ params = {}
92
+ if not isinstance(params, dict):
93
+ params = {}
94
+ if not at or not isinstance(at, str):
95
+ return fallback
96
+ at = at.strip()
97
+ try:
98
+ Action(action_type=at, parameters=params)
99
+ except Exception:
100
+ return fallback
101
+ return {"action_type": at, "parameters": params}
102
+
103
+
104
+ def run_episode(client: OpenAI, model: str, base: str, task_id: str) -> dict[str, Any]:
105
+ r = requests.post(f"{base}/reset", json={"task_id": task_id}, timeout=REQUEST_TIMEOUT_S)
106
+ r.raise_for_status()
107
+ obs_dict: dict[str, Any] = r.json()
108
+
109
+ messages: list[dict[str, str]] = [{"role": "system", "content": SYSTEM_PROMPT}]
110
+ steps = 0
111
+
112
+ while not obs_dict.get("done", False) and steps < MAX_LLM_STEPS_PER_TASK:
113
+ messages.append({"role": "user", "content": build_user_message(obs_dict)})
114
+ response = client.chat.completions.create(
115
+ model=model,
116
+ messages=messages,
117
+ temperature=TEMPERATURE,
118
+ max_tokens=MAX_TOKENS,
119
+ )
120
+ raw = response.choices[0].message.content or "{}"
121
+ messages.append({"role": "assistant", "content": raw})
122
+
123
+ try:
124
+ parsed = parse_llm_action(raw)
125
+ except Exception:
126
+ parsed = {"action_type": "submit", "parameters": {}}
127
+
128
+ action_dict = coerce_action_for_step(parsed)
129
+
130
+ r = requests.post(f"{base}/step", json=action_dict, timeout=REQUEST_TIMEOUT_S)
131
+ if not r.ok:
132
+ detail = (r.text or "")[:1000]
133
+ raise RuntimeError(f"POST /step HTTP {r.status_code}: {detail}")
134
+ step_data = r.json()
135
+ obs_dict = step_data["observation"]
136
+ steps += 1
137
+
138
+ r = requests.post(f"{base}/grader", timeout=REQUEST_TIMEOUT_S)
139
+ r.raise_for_status()
140
+ return r.json()
141
+
142
+
143
+ def _assert_score_range(grade: dict[str, Any], task_id: str) -> None:
144
+ for key in (
145
+ "final_score",
146
+ "performance_score",
147
+ "strategy_score",
148
+ "combo_score",
149
+ "trap_score",
150
+ ):
151
+ v = float(grade[key])
152
+ if not 0.0 <= v <= 1.0:
153
+ raise ValueError(f"{task_id}: {key}={v} not in [0, 1]")
154
+
155
+
156
+ def main() -> None:
157
+ client = _client()
158
+ model = _require_env("MODEL_NAME")
159
+ base = _env_base()
160
+
161
+ task_ids = list_task_ids(base)
162
+ if len(task_ids) < 3:
163
+ raise RuntimeError(f"Expected at least 3 tasks from /tasks, got {len(task_ids)}")
164
+
165
+ print(f"ENV_URL={base}")
166
+ print(f"Tasks: {task_ids}\n")
167
+
168
+ all_grades: list[dict[str, Any]] = []
169
+ for tid in task_ids:
170
+ print("=" * 60)
171
+ print(f"Task: {tid}")
172
+ print("=" * 60)
173
+ grade = run_episode(client, model, base, tid)
174
+ _assert_score_range(grade, tid)
175
+ all_grades.append(grade)
176
+ print(
177
+ f" final={grade['final_score']:.4f} perf={grade['performance_score']:.4f} "
178
+ f"strat={grade['strategy_score']:.4f} combo={grade['combo_score']:.4f} "
179
+ f"trap={grade['trap_score']:.4f}"
180
+ )
181
+
182
+ print("\n" + "=" * 60)
183
+ print("SUMMARY (all scores in [0.0, 1.0])")
184
+ print("=" * 60)
185
+ for g in all_grades:
186
+ print(f" {g['task_id']:<22} final={float(g['final_score']):.4f}")
187
+ avg = sum(float(g["final_score"]) for g in all_grades) / len(all_grades)
188
+ print(f"\n Mean final score: {avg:.4f}")
189
+
190
+
191
+ if __name__ == "__main__":
192
+ main()
pyproject.toml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "kaggle-sim-env"
3
+ version = "3.0.0"
4
+ description = "Kaggle Simulation Environment for OpenEnv – RL environment simulating Kaggle competitions"
5
+ authors = [{ name = "Aadi Gupta" }]
6
+ requires-python = ">=3.10"
7
+
8
+ dependencies = [
9
+ "fastapi>=0.115.0",
10
+ "uvicorn[standard]>=0.32.0",
11
+ "pydantic>=2.9.0",
12
+ "requests>=2.32.0",
13
+ "openai>=1.55.0",
14
+ "openenv-core>=0.2.0",
15
+ ]
16
+
17
+ [project.scripts]
18
+ server = "server.app:main"
19
+
20
+ [build-system]
21
+ requires = ["setuptools", "wheel"]
22
+ build-backend = "setuptools.build_meta"
requirements.txt CHANGED
@@ -3,3 +3,4 @@ uvicorn[standard]>=0.32.0
3
  pydantic>=2.9.0
4
  openai>=1.55.0
5
  requests>=2.32.0
 
 
3
  pydantic>=2.9.0
4
  openai>=1.55.0
5
  requests>=2.32.0
6
+ openenv-core>=0.2.0
server/__init__.py ADDED
File without changes
server/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (169 Bytes). View file
 
server/__pycache__/app.cpython-312.pyc ADDED
Binary file (11.1 kB). View file
 
server/app.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ OpenEnv-compatible server for KaggleSimEnv.
3
+
4
+ Wraps the KaggleSimEnv in the openenv Environment interface and exposes
5
+ it via ``create_app``. Additional custom endpoints (tasks, grader,
6
+ baseline, actions) are mounted on the same FastAPI app.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ from pydantic import Field
16
+
17
+ # Ensure project root is importable
18
+ _project_root = str(Path(__file__).resolve().parent.parent)
19
+ if _project_root not in sys.path:
20
+ sys.path.insert(0, _project_root)
21
+
22
+ from openenv.core.env_server.http_server import create_app
23
+ from openenv.core.env_server.interfaces import Environment
24
+ from openenv.core.env_server.types import (
25
+ Action as OEAction,
26
+ EnvironmentMetadata,
27
+ Observation as OEObservation,
28
+ State as OEState,
29
+ )
30
+
31
+ from kaggle_sim_env.environment import KaggleSimEnv
32
+ from kaggle_sim_env.grader import GradeResult, Grader
33
+ from kaggle_sim_env.models import (
34
+ Action as KSAction,
35
+ ActionType,
36
+ CATEGORY_MAP,
37
+ get_categories_for_action,
38
+ )
39
+ from kaggle_sim_env.tasks import TASK_REGISTRY, get_task
40
+
41
+
42
+ # ── OpenEnv-compatible Pydantic models ───────────────────────────────────
43
+
44
+ class KaggleAction(OEAction):
45
+ action_type: str
46
+ parameters: Dict[str, Any] = Field(default_factory=dict)
47
+
48
+
49
+ class KaggleObservation(OEObservation):
50
+ dataset_metadata: Dict[str, Any] = Field(default_factory=dict)
51
+ applied_strategies: List[str] = Field(default_factory=list)
52
+ current_cv_score: float = 0.0
53
+ leaderboard_rank: int = 0
54
+ step_count: int = 0
55
+ max_steps: int = 10
56
+ message: str = ""
57
+
58
+
59
+ class KaggleState(OEState):
60
+ task_id: str = ""
61
+ max_steps: int = 10
62
+ done: bool = False
63
+ cv_score: float = 0.0
64
+ test_score: float = 0.0
65
+ applied_strategies: List[str] = Field(default_factory=list)
66
+ strategy_history: List[str] = Field(default_factory=list)
67
+ leaderboard_rank: int = 0
68
+ leaderboard: List[Dict[str, Any]] = Field(default_factory=list)
69
+ submitted: bool = False
70
+ hint_count: int = 0
71
+ active_combos: List[str] = Field(default_factory=list)
72
+ traps_triggered: List[str] = Field(default_factory=list)
73
+
74
+
75
+ # ── OpenEnv Environment adapter ──────────────────────────────────────────
76
+
77
+ class KaggleSimEnvironment(Environment[KaggleAction, KaggleObservation, KaggleState]):
78
+ """Bridges KaggleSimEnv to the openenv ``Environment`` ABC."""
79
+
80
+ def __init__(self) -> None:
81
+ super().__init__()
82
+ self._env = KaggleSimEnv()
83
+ self._task_id = "easy_churn"
84
+
85
+ def reset(
86
+ self,
87
+ seed: Optional[int] = None,
88
+ episode_id: Optional[str] = None,
89
+ *,
90
+ task_id: str = "easy_churn",
91
+ **kwargs: Any,
92
+ ) -> KaggleObservation:
93
+ self._task_id = task_id
94
+ obs = self._env.reset(task_id=task_id)
95
+ return KaggleObservation(
96
+ done=obs.done,
97
+ reward=0.0,
98
+ dataset_metadata=obs.dataset_metadata.model_dump(),
99
+ applied_strategies=obs.applied_strategies,
100
+ current_cv_score=obs.current_cv_score,
101
+ leaderboard_rank=obs.leaderboard_rank,
102
+ step_count=obs.step_count,
103
+ max_steps=obs.max_steps,
104
+ message=obs.message,
105
+ )
106
+
107
+ def step(
108
+ self,
109
+ action: KaggleAction,
110
+ timeout_s: Optional[float] = None,
111
+ **kwargs: Any,
112
+ ) -> KaggleObservation:
113
+ ks_action = KSAction(
114
+ action_type=action.action_type,
115
+ parameters=action.parameters,
116
+ )
117
+ result = self._env.step(ks_action)
118
+ obs = result.observation
119
+ return KaggleObservation(
120
+ done=obs.done,
121
+ reward=result.reward.total,
122
+ metadata={
123
+ "info": result.info,
124
+ "breakdown": result.reward.breakdown.model_dump(),
125
+ },
126
+ dataset_metadata=obs.dataset_metadata.model_dump(),
127
+ applied_strategies=obs.applied_strategies,
128
+ current_cv_score=obs.current_cv_score,
129
+ leaderboard_rank=obs.leaderboard_rank,
130
+ step_count=obs.step_count,
131
+ max_steps=obs.max_steps,
132
+ message=obs.message,
133
+ )
134
+
135
+ @property
136
+ def state(self) -> KaggleState:
137
+ s = self._env.state()
138
+ return KaggleState(
139
+ episode_id=s.task_id,
140
+ step_count=s.step_count,
141
+ task_id=s.task_id,
142
+ max_steps=s.max_steps,
143
+ done=s.done,
144
+ cv_score=s.cv_score,
145
+ test_score=s.test_score,
146
+ applied_strategies=s.applied_strategies,
147
+ strategy_history=s.strategy_history,
148
+ leaderboard_rank=s.leaderboard_rank,
149
+ leaderboard=s.leaderboard,
150
+ submitted=s.submitted,
151
+ hint_count=s.hint_count,
152
+ active_combos=s.active_combos,
153
+ traps_triggered=s.traps_triggered,
154
+ )
155
+
156
+ def get_metadata(self) -> EnvironmentMetadata:
157
+ return EnvironmentMetadata(
158
+ name="KaggleSimEnv",
159
+ description=(
160
+ "RL environment simulating Kaggle competitions with hierarchical "
161
+ "actions, causal dataset properties, failure-mode traps, and "
162
+ "contextual scoring."
163
+ ),
164
+ version="3.0.0",
165
+ )
166
+
167
+
168
+ # ── Create the OpenEnv app ───────────────────────────────────────────────
169
+
170
+ app = create_app(
171
+ KaggleSimEnvironment,
172
+ KaggleAction,
173
+ KaggleObservation,
174
+ env_name="kaggle_sim_env",
175
+ max_concurrent_envs=1,
176
+ )
177
+
178
+
179
+ # ── Custom endpoints (tasks, grader, baseline, actions) ──────────────────
180
+
181
+ from pydantic import BaseModel
182
+
183
+ _grader = Grader()
184
+
185
+
186
+ class _TaskSummary(BaseModel):
187
+ task_id: str
188
+ title: str
189
+ difficulty: str
190
+ description: str
191
+ max_steps: int
192
+ num_expected_strategies: int
193
+ num_strategy_combos: int
194
+ num_failure_modes: int
195
+
196
+
197
+ class _BaselineRequest(BaseModel):
198
+ task_id: str = "easy_churn"
199
+
200
+
201
+ class _ActionCategoryEntry(BaseModel):
202
+ action_type: str
203
+ parameter_key: Optional[str] = None
204
+ categories: Dict[str, List[str]] = Field(default_factory=dict)
205
+
206
+
207
+ @app.get("/tasks", response_model=List[_TaskSummary], tags=["Custom"])
208
+ def list_tasks() -> list[_TaskSummary]:
209
+ return [
210
+ _TaskSummary(
211
+ task_id=t.task_id,
212
+ title=t.title,
213
+ difficulty=t.difficulty,
214
+ description=t.description,
215
+ max_steps=t.max_steps,
216
+ num_expected_strategies=len(t.expected_strategies),
217
+ num_strategy_combos=len(t.strategy_combos),
218
+ num_failure_modes=len(t.failure_modes),
219
+ )
220
+ for t in TASK_REGISTRY.values()
221
+ ]
222
+
223
+
224
+ @app.post("/grader", response_model=GradeResult, tags=["Custom"])
225
+ def grade(req: _BaselineRequest) -> GradeResult:
226
+ bl_env = KaggleSimEnv()
227
+ bl_env.reset(task_id=req.task_id)
228
+ s = bl_env.state()
229
+ return _grader.grade(s, get_task(s.task_id))
230
+
231
+
232
+ @app.get("/actions", response_model=List[_ActionCategoryEntry], tags=["Custom"])
233
+ def action_space() -> list[_ActionCategoryEntry]:
234
+ from kaggle_sim_env.models import _PARAM_KEY_MAP
235
+
236
+ entries: list[_ActionCategoryEntry] = []
237
+ for at, key in _PARAM_KEY_MAP.items():
238
+ cats = get_categories_for_action(at)
239
+ entries.append(_ActionCategoryEntry(action_type=at, parameter_key=key, categories=cats))
240
+ entries.append(
241
+ _ActionCategoryEntry(
242
+ action_type="pseudo_label",
243
+ parameter_key="iterations",
244
+ categories={"iterations": ["1", "2", "3"]},
245
+ )
246
+ )
247
+ entries.append(_ActionCategoryEntry(action_type="inspect_top_solution"))
248
+ entries.append(_ActionCategoryEntry(action_type="submit"))
249
+ return entries
250
+
251
+
252
+ # ── Entry points ─────────────────────────────────────────────────────────
253
+
254
+ def main(host: str = "0.0.0.0", port: int = 7860) -> None:
255
+ import uvicorn
256
+
257
+ uvicorn.run(app, host=host, port=port)
258
+
259
+
260
+ if __name__ == "__main__":
261
+ main()
uv.lock ADDED
The diff for this file is too large to render. See raw diff
 
validate.sh ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ ENV_URL=$1
4
+
5
+ echo "πŸ” Checking HF Space..."
6
+ curl -s -X POST "$ENV_URL/reset" \
7
+ -H "Content-Type: application/json" \
8
+ -d '{"task_id": "easy_churn"}' > /dev/null
9
+
10
+ if [ $? -eq 0 ]; then
11
+ echo "βœ… HF Space reachable"
12
+ else
13
+ echo "❌ HF Space failed"
14
+ exit 1
15
+ fi
16
+
17
+ echo "🐳 Checking Docker build..."
18
+ docker build -t test-env . > /dev/null
19
+
20
+ if [ $? -eq 0 ]; then
21
+ echo "βœ… Docker build passed"
22
+ else
23
+ echo "❌ Docker build failed"
24
+ exit 1
25
+ fi
26
+
27
+ echo "🧠 Checking OpenEnv spec..."
28
+ openenv validate
29
+
30
+ if [ $? -eq 0 ]; then
31
+ echo "βœ… OpenEnv validation passed"
32
+ else
33
+ echo "❌ OpenEnv validation failed"
34
+ exit 1
35
+ fi
36
+
37
+ echo "πŸŽ‰ ALL CHECKS PASSED"