Ajjack4 commited on
Commit
5d4432b
·
1 Parent(s): 2f0f7b7

add LOCAL_IMAGE_NAME Docker support, use API_KEY var, debug logging for env/model URLs

Browse files
Files changed (1) hide show
  1. inference.py +122 -46
inference.py CHANGED
@@ -1,49 +1,109 @@
1
  """
2
  Inference script — Dev Environment Debugger (OpenEnv Hackathon)
3
 
4
- Required environment variables:
5
- API_BASE_URL OpenAI-compatible LLM API endpoint
6
- MODEL_NAME Model identifier for inference
7
- HF_TOKEN HuggingFace / API key
8
-
9
- Optional:
10
- ENV_BASE_URL Base URL of the deployed OpenEnv Space
11
- (default: http://localhost:7860)
 
 
12
  """
13
 
14
  import asyncio
15
  import json
16
  import os
 
17
  import sys
 
18
  from typing import List, Optional
19
 
20
  import requests
21
  from openai import OpenAI
22
 
23
  # ---------------------------------------------------------------------------
24
- # Config
25
  # ---------------------------------------------------------------------------
26
 
27
- # Validator injects API_BASE_URL and API_KEY via LiteLLM proxy.
28
- # Also accept HF_TOKEN / OPENAI_API_KEY for local testing.
29
  API_BASE_URL: str = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
30
  MODEL_NAME: str = os.getenv("MODEL_NAME", "gpt-4o-mini")
31
- HF_TOKEN: str = (
32
- os.getenv("API_KEY") # validator-injected key (LiteLLM proxy)
 
 
33
  or os.getenv("HF_TOKEN")
34
  or os.getenv("OPENAI_API_KEY")
35
  or ""
36
  )
37
- ENV_BASE_URL: str = os.getenv("ENV_BASE_URL", "https://ajjack404-dev-env-debugger.hf.space")
38
 
39
- if not HF_TOKEN:
40
- print("[DEBUG] Warning: no API key found. LLM calls will fail.", flush=True)
 
 
 
41
 
42
  TASK_IDS = ["task1", "task2", "task3"]
43
  BENCHMARK = "dev-env-debugger"
44
  MAX_STEPS = 20
45
  SUCCESS_SCORE_THRESHOLD = 0.5
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  # ---------------------------------------------------------------------------
48
  # Structured logging — [START] / [STEP] / [END]
49
  # ---------------------------------------------------------------------------
@@ -95,7 +155,7 @@ def get_model_message(
95
  return '{"type": "submit"}'
96
 
97
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
98
- messages.extend(history[-10:]) # keep last 10 turns to stay within context
99
  messages.append({"role": "user", "content": obs_text})
100
 
101
  try:
@@ -108,7 +168,7 @@ def get_model_message(
108
  return response.choices[0].message.content.strip()
109
  except Exception as exc:
110
  print(f"[DEBUG] Model request failed: {exc}", flush=True)
111
- return '{"type": "submit"}' # safe fallback — ends episode cleanly
112
 
113
 
114
  # ---------------------------------------------------------------------------
@@ -116,39 +176,45 @@ def get_model_message(
116
  # ---------------------------------------------------------------------------
117
 
118
  class EnvAdapter:
119
- """Thin wrapper around the OpenEnv HTTP API."""
120
-
121
  def __init__(self, base_url: str) -> None:
122
  self.base_url = base_url.rstrip("/")
123
 
124
  def reset(self, task_id: str) -> dict:
125
- import time
126
  for attempt in range(5):
127
  try:
128
  resp = requests.post(
129
- f"{self.base_url}/reset", params={"task_id": task_id}, timeout=60
130
  )
131
  resp.raise_for_status()
132
  return resp.json()
133
  except Exception as exc:
134
- print(f"[DEBUG] reset() attempt {attempt+1} failed: {exc}", flush=True)
135
  if attempt < 4:
136
- time.sleep(5)
137
- return {"step": 0, "services": {}, "last_action_result": {"success": False, "message": "env unreachable"}, "available_actions": [], "done": True}
 
 
 
 
138
 
139
  def step(self, action: dict) -> dict:
140
  try:
141
- resp = requests.post(f"{self.base_url}/step", json=action, timeout=60)
142
  resp.raise_for_status()
143
  return resp.json()
144
  except Exception as exc:
145
  print(f"[DEBUG] step() failed: {exc}", flush=True)
146
- return {"observation": {"step": 0, "services": {}, "last_action_result": {"success": False, "message": str(exc)}, "available_actions": [], "done": True}, "reward": 0.0, "done": True, "info": {}}
 
 
 
 
 
147
 
148
  def grader(self, task_id: str) -> dict:
149
  try:
150
  resp = requests.get(
151
- f"{self.base_url}/grader", params={"task_id": task_id}, timeout=60
152
  )
153
  resp.raise_for_status()
154
  return resp.json()
@@ -165,7 +231,7 @@ def obs_to_text(obs: dict) -> str:
165
  )
166
  last = obs.get("last_action_result", {})
167
  return (
168
- f"Step {obs['step']}\n"
169
  f"Services:\n{svc_lines}\n"
170
  f"Last action: {'OK' if last.get('success') else 'FAIL'} — {last.get('message', '')}\n"
171
  f"Done: {obs.get('done', False)}"
@@ -176,7 +242,7 @@ def obs_to_text(obs: dict) -> str:
176
  # Episode runner
177
  # ---------------------------------------------------------------------------
178
 
179
- async def run_task(client: OpenAI, env: EnvAdapter, task_id: str) -> None:
180
  history: List[dict] = []
181
  rewards: List[float] = []
182
  steps_taken = 0
@@ -199,7 +265,7 @@ async def run_task(client: OpenAI, env: EnvAdapter, task_id: str) -> None:
199
  try:
200
  action = json.loads(raw_action)
201
  except json.JSONDecodeError:
202
- error = f"Invalid JSON from model: {raw_action}"
203
  action = {"type": "submit"}
204
 
205
  step_data = env.step(action)
@@ -226,8 +292,6 @@ async def run_task(client: OpenAI, env: EnvAdapter, task_id: str) -> None:
226
 
227
  except Exception as exc:
228
  print(f"[DEBUG] Task {task_id} error: {exc}", flush=True)
229
- score = 0.0
230
- success = False
231
 
232
  finally:
233
  log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
@@ -238,24 +302,36 @@ async def run_task(client: OpenAI, env: EnvAdapter, task_id: str) -> None:
238
  # ---------------------------------------------------------------------------
239
 
240
  async def main() -> None:
241
- # Use placeholder key if none provided — real failure happens at API call
242
- # level and is caught inside get_model_message(), not here.
243
- api_key = HF_TOKEN if HF_TOKEN else "none"
 
 
 
 
 
 
 
 
 
 
 
244
  try:
245
- client = OpenAI(base_url=API_BASE_URL, api_key=api_key)
246
  except Exception as exc:
247
  print(f"[DEBUG] OpenAI client init failed: {exc}", flush=True)
248
- client = None
249
 
250
- env = EnvAdapter(ENV_BASE_URL)
251
 
252
- for task_id in TASK_IDS:
253
- # Wrap individually — one task failing must not prevent others logging.
254
- try:
255
- await run_task(client, env, task_id)
256
- except Exception as exc:
257
- print(f"[DEBUG] run_task({task_id}) uncaught: {exc}", flush=True)
258
- print(f"[END] success=False steps=0 score=0.0 rewards=[]", flush=True)
 
 
259
 
260
 
261
  if __name__ == "__main__":
 
1
  """
2
  Inference script — Dev Environment Debugger (OpenEnv Hackathon)
3
 
4
+ Environment variables (injected by validator):
5
+ API_BASE_URL LiteLLM proxy endpoint for LLM calls
6
+ API_KEY API key for the LiteLLM proxy
7
+ MODEL_NAME Model identifier (default: gpt-4o-mini)
8
+ LOCAL_IMAGE_NAME Docker image name to start the environment container
9
+
10
+ Optional (for local testing):
11
+ ENV_BASE_URL Override environment URL instead of starting Docker
12
+ HF_TOKEN Alias for API_KEY (local testing)
13
+ OPENAI_API_KEY Alias for API_KEY (local testing)
14
  """
15
 
16
  import asyncio
17
  import json
18
  import os
19
+ import subprocess
20
  import sys
21
+ import time
22
  from typing import List, Optional
23
 
24
  import requests
25
  from openai import OpenAI
26
 
27
  # ---------------------------------------------------------------------------
28
+ # Config — follow validator spec exactly
29
  # ---------------------------------------------------------------------------
30
 
 
 
31
  API_BASE_URL: str = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
32
  MODEL_NAME: str = os.getenv("MODEL_NAME", "gpt-4o-mini")
33
+
34
+ # Validator injects API_KEY. Accept aliases for local testing.
35
+ API_KEY: str = (
36
+ os.getenv("API_KEY")
37
  or os.getenv("HF_TOKEN")
38
  or os.getenv("OPENAI_API_KEY")
39
  or ""
40
  )
 
41
 
42
+ LOCAL_IMAGE_NAME: str = os.getenv("LOCAL_IMAGE_NAME", "")
43
+ ENV_BASE_URL: str = os.getenv("ENV_BASE_URL", "")
44
+
45
+ if not API_KEY:
46
+ print("[DEBUG] Warning: no API key set (API_KEY / HF_TOKEN / OPENAI_API_KEY).", flush=True)
47
 
48
  TASK_IDS = ["task1", "task2", "task3"]
49
  BENCHMARK = "dev-env-debugger"
50
  MAX_STEPS = 20
51
  SUCCESS_SCORE_THRESHOLD = 0.5
52
 
53
+ # ---------------------------------------------------------------------------
54
+ # Docker container lifecycle
55
+ # ---------------------------------------------------------------------------
56
+
57
+ _container_id: str = ""
58
+
59
+
60
+ def start_env_container(image_name: str) -> str:
61
+ """Start the environment Docker container. Returns its base URL."""
62
+ global _container_id
63
+ print(f"[DEBUG] Starting container from image: {image_name}", flush=True)
64
+ try:
65
+ result = subprocess.run(
66
+ ["docker", "run", "-d", "--rm", "-p", "7860:7860", image_name],
67
+ capture_output=True, text=True, timeout=60,
68
+ )
69
+ if result.returncode == 0:
70
+ _container_id = result.stdout.strip()
71
+ print(f"[DEBUG] Container started: {_container_id[:12]}", flush=True)
72
+ # Wait for the server inside the container to be ready
73
+ for _ in range(12):
74
+ time.sleep(5)
75
+ try:
76
+ r = requests.get("http://localhost:7860/health", timeout=5)
77
+ if r.status_code == 200:
78
+ print("[DEBUG] Container health check passed.", flush=True)
79
+ return "http://localhost:7860"
80
+ except Exception:
81
+ pass
82
+ print("[DEBUG] Container started but health check timed out.", flush=True)
83
+ return "http://localhost:7860"
84
+ else:
85
+ print(f"[DEBUG] docker run failed: {result.stderr.strip()}", flush=True)
86
+ except FileNotFoundError:
87
+ print("[DEBUG] docker not found in PATH.", flush=True)
88
+ except Exception as exc:
89
+ print(f"[DEBUG] start_env_container error: {exc}", flush=True)
90
+ return ""
91
+
92
+
93
+ def stop_env_container() -> None:
94
+ global _container_id
95
+ if _container_id:
96
+ try:
97
+ subprocess.run(
98
+ ["docker", "stop", _container_id],
99
+ capture_output=True, timeout=15,
100
+ )
101
+ print(f"[DEBUG] Container stopped: {_container_id[:12]}", flush=True)
102
+ except Exception as exc:
103
+ print(f"[DEBUG] stop_env_container error: {exc}", flush=True)
104
+ _container_id = ""
105
+
106
+
107
  # ---------------------------------------------------------------------------
108
  # Structured logging — [START] / [STEP] / [END]
109
  # ---------------------------------------------------------------------------
 
155
  return '{"type": "submit"}'
156
 
157
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
158
+ messages.extend(history[-10:])
159
  messages.append({"role": "user", "content": obs_text})
160
 
161
  try:
 
168
  return response.choices[0].message.content.strip()
169
  except Exception as exc:
170
  print(f"[DEBUG] Model request failed: {exc}", flush=True)
171
+ return '{"type": "submit"}'
172
 
173
 
174
  # ---------------------------------------------------------------------------
 
176
  # ---------------------------------------------------------------------------
177
 
178
  class EnvAdapter:
 
 
179
  def __init__(self, base_url: str) -> None:
180
  self.base_url = base_url.rstrip("/")
181
 
182
  def reset(self, task_id: str) -> dict:
 
183
  for attempt in range(5):
184
  try:
185
  resp = requests.post(
186
+ f"{self.base_url}/reset", params={"task_id": task_id}, timeout=30
187
  )
188
  resp.raise_for_status()
189
  return resp.json()
190
  except Exception as exc:
191
+ print(f"[DEBUG] reset() attempt {attempt + 1} failed: {exc}", flush=True)
192
  if attempt < 4:
193
+ time.sleep(3)
194
+ return {
195
+ "step": 0, "services": {}, "done": True,
196
+ "last_action_result": {"success": False, "message": "env unreachable"},
197
+ "available_actions": [],
198
+ }
199
 
200
  def step(self, action: dict) -> dict:
201
  try:
202
+ resp = requests.post(f"{self.base_url}/step", json=action, timeout=30)
203
  resp.raise_for_status()
204
  return resp.json()
205
  except Exception as exc:
206
  print(f"[DEBUG] step() failed: {exc}", flush=True)
207
+ return {
208
+ "observation": {"step": 0, "services": {}, "done": True,
209
+ "last_action_result": {"success": False, "message": str(exc)},
210
+ "available_actions": []},
211
+ "reward": 0.0, "done": True, "info": {},
212
+ }
213
 
214
  def grader(self, task_id: str) -> dict:
215
  try:
216
  resp = requests.get(
217
+ f"{self.base_url}/grader", params={"task_id": task_id}, timeout=30
218
  )
219
  resp.raise_for_status()
220
  return resp.json()
 
231
  )
232
  last = obs.get("last_action_result", {})
233
  return (
234
+ f"Step {obs.get('step', 0)}\n"
235
  f"Services:\n{svc_lines}\n"
236
  f"Last action: {'OK' if last.get('success') else 'FAIL'} — {last.get('message', '')}\n"
237
  f"Done: {obs.get('done', False)}"
 
242
  # Episode runner
243
  # ---------------------------------------------------------------------------
244
 
245
+ async def run_task(client: Optional[OpenAI], env: EnvAdapter, task_id: str) -> None:
246
  history: List[dict] = []
247
  rewards: List[float] = []
248
  steps_taken = 0
 
265
  try:
266
  action = json.loads(raw_action)
267
  except json.JSONDecodeError:
268
+ error = f"Invalid JSON: {raw_action}"
269
  action = {"type": "submit"}
270
 
271
  step_data = env.step(action)
 
292
 
293
  except Exception as exc:
294
  print(f"[DEBUG] Task {task_id} error: {exc}", flush=True)
 
 
295
 
296
  finally:
297
  log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
 
302
  # ---------------------------------------------------------------------------
303
 
304
  async def main() -> None:
305
+ # Resolve environment URL: Docker container > explicit ENV_BASE_URL > HF Space
306
+ if LOCAL_IMAGE_NAME:
307
+ env_url = start_env_container(LOCAL_IMAGE_NAME)
308
+ if not env_url:
309
+ env_url = ENV_BASE_URL or "https://ajjack404-dev-env-debugger.hf.space"
310
+ else:
311
+ env_url = ENV_BASE_URL or "https://ajjack404-dev-env-debugger.hf.space"
312
+
313
+ print(f"[DEBUG] Using env URL: {env_url}", flush=True)
314
+ print(f"[DEBUG] Using API_BASE_URL: {API_BASE_URL}", flush=True)
315
+ print(f"[DEBUG] Using MODEL_NAME: {MODEL_NAME}", flush=True)
316
+
317
+ # Initialize OpenAI client pointed at validator's LiteLLM proxy
318
+ client: Optional[OpenAI] = None
319
  try:
320
+ client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY or "none")
321
  except Exception as exc:
322
  print(f"[DEBUG] OpenAI client init failed: {exc}", flush=True)
 
323
 
324
+ env = EnvAdapter(env_url)
325
 
326
+ try:
327
+ for task_id in TASK_IDS:
328
+ try:
329
+ await run_task(client, env, task_id)
330
+ except Exception as exc:
331
+ print(f"[DEBUG] run_task({task_id}) uncaught: {exc}", flush=True)
332
+ print(f"[END] success=False steps=0 score=0.0 rewards=[]", flush=True)
333
+ finally:
334
+ stop_env_container()
335
 
336
 
337
  if __name__ == "__main__":