Ajjack4 commited on
Commit
87aa853
·
1 Parent(s): a07333b

rewrite inference.py to match sample exactly: IMAGE_NAME, HF_TOKEN first, correct log format

Browse files
Files changed (2) hide show
  1. inference.py +72 -101
  2. requirements.txt +1 -1
inference.py CHANGED
@@ -1,23 +1,17 @@
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
 
@@ -25,32 +19,47 @@ 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
- # Validator runs our Docker container at localhost:7860 before running inference.py.
44
- # LOCAL_IMAGE_NAME is used when we need to start it ourselves.
45
- ENV_BASE_URL: str = os.getenv("ENV_BASE_URL", "http://localhost:7860")
46
 
47
- if not API_KEY:
48
- print("[DEBUG] Warning: no API key set (API_KEY / HF_TOKEN / OPENAI_API_KEY).", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- TASK_IDS = ["task1", "task2", "task3"]
51
- BENCHMARK = "dev-env-debugger"
52
- MAX_STEPS = 20
53
- SUCCESS_SCORE_THRESHOLD = 0.5
54
 
55
  # ---------------------------------------------------------------------------
56
  # Docker container lifecycle
@@ -60,7 +69,7 @@ _container_id: str = ""
60
 
61
 
62
  def start_env_container(image_name: str) -> str:
63
- """Start the environment Docker container. Returns its base URL."""
64
  global _container_id
65
  print(f"[DEBUG] Starting container from image: {image_name}", flush=True)
66
  try:
@@ -71,7 +80,6 @@ def start_env_container(image_name: str) -> str:
71
  if result.returncode == 0:
72
  _container_id = result.stdout.strip()
73
  print(f"[DEBUG] Container started: {_container_id[:12]}", flush=True)
74
- # Wait for the server inside the container to be ready
75
  for _ in range(12):
76
  time.sleep(5)
77
  try:
@@ -81,7 +89,7 @@ def start_env_container(image_name: str) -> str:
81
  return "http://localhost:7860"
82
  except Exception:
83
  pass
84
- print("[DEBUG] Container started but health check timed out.", flush=True)
85
  return "http://localhost:7860"
86
  else:
87
  print(f"[DEBUG] docker run failed: {result.stderr.strip()}", flush=True)
@@ -96,35 +104,13 @@ def stop_env_container() -> None:
96
  global _container_id
97
  if _container_id:
98
  try:
99
- subprocess.run(
100
- ["docker", "stop", _container_id],
101
- capture_output=True, timeout=15,
102
- )
103
  print(f"[DEBUG] Container stopped: {_container_id[:12]}", flush=True)
104
  except Exception as exc:
105
  print(f"[DEBUG] stop_env_container error: {exc}", flush=True)
106
  _container_id = ""
107
 
108
 
109
- # ---------------------------------------------------------------------------
110
- # Structured logging — [START] / [STEP] / [END]
111
- # ---------------------------------------------------------------------------
112
-
113
- def log_start(*, task: str, env: str, model: str) -> None:
114
- print(f"[START] task={task} env={env} model={model}", flush=True)
115
-
116
-
117
- def log_step(
118
- *, step: int, action: str, reward: float, done: bool, error: Optional[str]
119
- ) -> None:
120
- error_str = error if error else "none"
121
- print(f"[STEP] step={step} reward={reward} done={done} error={error_str}", flush=True)
122
-
123
-
124
- def log_end(*, success: bool, steps: int, score: float, rewards: List[float]) -> None:
125
- print(f"[END] success={success} steps={steps} score={score} rewards={rewards}", flush=True)
126
-
127
-
128
  # ---------------------------------------------------------------------------
129
  # LLM agent
130
  # ---------------------------------------------------------------------------
@@ -150,16 +136,10 @@ STRATEGY:
150
  """
151
 
152
 
153
- def get_model_message(
154
- client: Optional[OpenAI], obs_text: str, history: List[dict]
155
- ) -> str:
156
- if client is None:
157
- return '{"type": "submit"}'
158
-
159
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
160
  messages.extend(history[-10:])
161
  messages.append({"role": "user", "content": obs_text})
162
-
163
  try:
164
  response = client.chat.completions.create(
165
  model=MODEL_NAME,
@@ -167,7 +147,7 @@ def get_model_message(
167
  temperature=0.2,
168
  max_tokens=200,
169
  )
170
- return response.choices[0].message.content.strip()
171
  except Exception as exc:
172
  print(f"[DEBUG] Model request failed: {exc}", flush=True)
173
  return '{"type": "submit"}'
@@ -207,9 +187,11 @@ class EnvAdapter:
207
  except Exception as exc:
208
  print(f"[DEBUG] step() failed: {exc}", flush=True)
209
  return {
210
- "observation": {"step": 0, "services": {}, "done": True,
211
- "last_action_result": {"success": False, "message": str(exc)},
212
- "available_actions": []},
 
 
213
  "reward": 0.0, "done": True, "info": {},
214
  }
215
 
@@ -224,6 +206,9 @@ class EnvAdapter:
224
  print(f"[DEBUG] grader() failed: {exc}", flush=True)
225
  return {"grader_result": {"score": 0.0, "reason": str(exc)}}
226
 
 
 
 
227
 
228
  def obs_to_text(obs: dict) -> str:
229
  services = obs.get("services", {})
@@ -241,10 +226,10 @@ def obs_to_text(obs: dict) -> str:
241
 
242
 
243
  # ---------------------------------------------------------------------------
244
- # Episode runner
245
  # ---------------------------------------------------------------------------
246
 
247
- async def run_task(client: Optional[OpenAI], env: EnvAdapter, task_id: str) -> None:
248
  history: List[dict] = []
249
  rewards: List[float] = []
250
  steps_taken = 0
@@ -267,7 +252,7 @@ async def run_task(client: Optional[OpenAI], env: EnvAdapter, task_id: str) -> N
267
  try:
268
  action = json.loads(raw_action)
269
  except json.JSONDecodeError:
270
- error = f"Invalid JSON: {raw_action}"
271
  action = {"type": "submit"}
272
 
273
  step_data = env.step(action)
@@ -292,52 +277,38 @@ async def run_task(client: Optional[OpenAI], env: EnvAdapter, task_id: str) -> N
292
  score = min(max(score, 0.0), 1.0)
293
  success = score >= SUCCESS_SCORE_THRESHOLD
294
 
295
- except Exception as exc:
296
- print(f"[DEBUG] Task {task_id} error: {exc}", flush=True)
297
-
298
  finally:
 
 
 
 
299
  log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
300
 
301
 
302
  # ---------------------------------------------------------------------------
303
- # Entry point
304
  # ---------------------------------------------------------------------------
305
 
306
  async def main() -> None:
307
- # Resolve environment URL: Docker container > explicit ENV_BASE_URL > HF Space
308
- if LOCAL_IMAGE_NAME:
309
- env_url = start_env_container(LOCAL_IMAGE_NAME)
310
  if not env_url:
311
- env_url = ENV_BASE_URL # already defaults to localhost:7860
312
  else:
313
- env_url = ENV_BASE_URL # localhost:7860 or whatever validator set
314
-
315
- print(f"[DEBUG] Using env URL: {env_url}", flush=True)
316
- print(f"[DEBUG] Using API_BASE_URL: {API_BASE_URL}", flush=True)
317
- print(f"[DEBUG] Using MODEL_NAME: {MODEL_NAME}", flush=True)
318
 
319
- # Initialize OpenAI client pointed at validator's LiteLLM proxy
320
- client: Optional[OpenAI] = None
321
- try:
322
- client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY or "none")
323
- except Exception as exc:
324
- print(f"[DEBUG] OpenAI client init failed: {exc}", flush=True)
325
 
 
326
  env = EnvAdapter(env_url)
327
 
328
  try:
329
  for task_id in TASK_IDS:
330
- try:
331
- await run_task(client, env, task_id)
332
- except Exception as exc:
333
- print(f"[DEBUG] run_task({task_id}) uncaught: {exc}", flush=True)
334
- print(f"[END] success=False steps=0 score=0.0 rewards=[]", flush=True)
335
  finally:
336
  stop_env_container()
337
 
338
 
339
  if __name__ == "__main__":
340
- try:
341
- asyncio.run(main())
342
- except Exception as exc:
343
- print(f"[DEBUG] Fatal error: {exc}", flush=True)
 
1
  """
2
+ Inference Script — Dev Environment Debugger (OpenEnv Hackathon)
3
+
4
+ Required environment variables (injected by validator):
5
+ API_BASE_URL The API endpoint for the LLM.
6
+ MODEL_NAME The model identifier to use for inference.
7
+ HF_TOKEN Your Hugging Face / API key.
8
+ IMAGE_NAME Docker image name for the environment (if using from_docker_image).
 
 
 
 
 
9
  """
10
 
11
  import asyncio
12
  import json
13
  import os
14
  import subprocess
 
15
  import time
16
  from typing import List, Optional
17
 
 
19
  from openai import OpenAI
20
 
21
  # ---------------------------------------------------------------------------
22
+ # Config — match sample inference.py exactly
23
  # ---------------------------------------------------------------------------
24
 
25
+ IMAGE_NAME: str = os.getenv("IMAGE_NAME", "")
26
+
27
+ # HF_TOKEN checked first (validator injects HF_TOKEN), API_KEY as fallback
28
+ API_KEY: Optional[str] = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
29
+
30
+ API_BASE_URL: str = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
31
+ MODEL_NAME: str = os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
32
+
33
+ BENCHMARK: str = "dev-env-debugger"
34
+ TASK_IDS: List[str] = ["task1", "task2", "task3"]
35
+ MAX_STEPS: int = 20
36
+ SUCCESS_SCORE_THRESHOLD: float = 0.5
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Structured logging — exact format from sample
40
+ # ---------------------------------------------------------------------------
41
 
42
+ def log_start(task: str, env: str, model: str) -> None:
43
+ print(f"[START] task={task} env={env} model={model}", flush=True)
 
 
 
 
 
44
 
 
 
 
 
45
 
46
+ def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
47
+ error_val = error if error else "null"
48
+ done_val = str(done).lower()
49
+ print(
50
+ f"[STEP] step={step} action={action} reward={reward:.2f} done={done_val} error={error_val}",
51
+ flush=True,
52
+ )
53
+
54
+
55
+ def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
56
+ rewards_str = ",".join(f"{r:.2f}" for r in rewards)
57
+ success_val = str(success).lower()
58
+ print(
59
+ f"[END] success={success_val} steps={steps} score={score:.3f} rewards={rewards_str}",
60
+ flush=True,
61
+ )
62
 
 
 
 
 
63
 
64
  # ---------------------------------------------------------------------------
65
  # Docker container lifecycle
 
69
 
70
 
71
  def start_env_container(image_name: str) -> str:
72
+ """Start the environment Docker container. Returns its base URL or empty string."""
73
  global _container_id
74
  print(f"[DEBUG] Starting container from image: {image_name}", flush=True)
75
  try:
 
80
  if result.returncode == 0:
81
  _container_id = result.stdout.strip()
82
  print(f"[DEBUG] Container started: {_container_id[:12]}", flush=True)
 
83
  for _ in range(12):
84
  time.sleep(5)
85
  try:
 
89
  return "http://localhost:7860"
90
  except Exception:
91
  pass
92
+ print("[DEBUG] Container health check timed out, continuing anyway.", flush=True)
93
  return "http://localhost:7860"
94
  else:
95
  print(f"[DEBUG] docker run failed: {result.stderr.strip()}", flush=True)
 
104
  global _container_id
105
  if _container_id:
106
  try:
107
+ subprocess.run(["docker", "stop", _container_id], capture_output=True, timeout=15)
 
 
 
108
  print(f"[DEBUG] Container stopped: {_container_id[:12]}", flush=True)
109
  except Exception as exc:
110
  print(f"[DEBUG] stop_env_container error: {exc}", flush=True)
111
  _container_id = ""
112
 
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  # ---------------------------------------------------------------------------
115
  # LLM agent
116
  # ---------------------------------------------------------------------------
 
136
  """
137
 
138
 
139
+ def get_model_message(client: OpenAI, obs_text: str, history: List[dict]) -> str:
 
 
 
 
 
140
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
141
  messages.extend(history[-10:])
142
  messages.append({"role": "user", "content": obs_text})
 
143
  try:
144
  response = client.chat.completions.create(
145
  model=MODEL_NAME,
 
147
  temperature=0.2,
148
  max_tokens=200,
149
  )
150
+ return (response.choices[0].message.content or "").strip()
151
  except Exception as exc:
152
  print(f"[DEBUG] Model request failed: {exc}", flush=True)
153
  return '{"type": "submit"}'
 
187
  except Exception as exc:
188
  print(f"[DEBUG] step() failed: {exc}", flush=True)
189
  return {
190
+ "observation": {
191
+ "step": 0, "services": {}, "done": True,
192
+ "last_action_result": {"success": False, "message": str(exc)},
193
+ "available_actions": [],
194
+ },
195
  "reward": 0.0, "done": True, "info": {},
196
  }
197
 
 
206
  print(f"[DEBUG] grader() failed: {exc}", flush=True)
207
  return {"grader_result": {"score": 0.0, "reason": str(exc)}}
208
 
209
+ def close(self) -> None:
210
+ pass # HTTP adapter — no persistent connection to close
211
+
212
 
213
  def obs_to_text(obs: dict) -> str:
214
  services = obs.get("services", {})
 
226
 
227
 
228
  # ---------------------------------------------------------------------------
229
+ # Episode runner — mirrors sample inference.py structure
230
  # ---------------------------------------------------------------------------
231
 
232
+ async def run_task(client: OpenAI, env: EnvAdapter, task_id: str) -> None:
233
  history: List[dict] = []
234
  rewards: List[float] = []
235
  steps_taken = 0
 
252
  try:
253
  action = json.loads(raw_action)
254
  except json.JSONDecodeError:
255
+ error = f"invalid_json"
256
  action = {"type": "submit"}
257
 
258
  step_data = env.step(action)
 
277
  score = min(max(score, 0.0), 1.0)
278
  success = score >= SUCCESS_SCORE_THRESHOLD
279
 
 
 
 
280
  finally:
281
+ try:
282
+ env.close()
283
+ except Exception as exc:
284
+ print(f"[DEBUG] env.close() error: {exc}", flush=True)
285
  log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
286
 
287
 
288
  # ---------------------------------------------------------------------------
289
+ # Entry point — mirrors sample inference.py structure
290
  # ---------------------------------------------------------------------------
291
 
292
  async def main() -> None:
293
+ # Resolve environment URL
294
+ if IMAGE_NAME:
295
+ env_url = start_env_container(IMAGE_NAME)
296
  if not env_url:
297
+ env_url = os.getenv("ENV_BASE_URL", "http://localhost:7860")
298
  else:
299
+ env_url = os.getenv("ENV_BASE_URL", "http://localhost:7860")
 
 
 
 
300
 
301
+ print(f"[DEBUG] env_url={env_url} api_base={API_BASE_URL} model={MODEL_NAME}", flush=True)
 
 
 
 
 
302
 
303
+ client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
304
  env = EnvAdapter(env_url)
305
 
306
  try:
307
  for task_id in TASK_IDS:
308
+ await run_task(client, env, task_id)
 
 
 
 
309
  finally:
310
  stop_env_container()
311
 
312
 
313
  if __name__ == "__main__":
314
+ asyncio.run(main())
 
 
 
requirements.txt CHANGED
@@ -3,4 +3,4 @@ uvicorn==0.30.1
3
  pydantic==2.7.1
4
  requests==2.32.3
5
  pyyaml==6.0.1
6
- openai==1.30.1
 
3
  pydantic==2.7.1
4
  requests==2.32.3
5
  pyyaml==6.0.1
6
+ openai>=2.7.2