Vishvam10 commited on
Commit
a121494
·
1 Parent(s): fb9d8e3

debug: (inference) stuff

Browse files
Files changed (1) hide show
  1. inference.py +224 -142
inference.py CHANGED
@@ -1,188 +1,270 @@
1
- """
2
- Inference Script Example
3
- ===================================
4
- MANDATORY
5
- - Before submitting, ensure the following variables are defined in your environment configuration:
6
- API_BASE_URL The API endpoint for the LLM.
7
- MODEL_NAME The model identifier to use for inference.
8
- HF_TOKEN Your Hugging Face / API key.
9
- LOCAL_IMAGE_NAME The name of the local image to use for the environment if you are using from_docker_image()
10
- method
11
-
12
- - Defaults are set only for API_BASE_URL and MODEL_NAME
13
- (and should reflect your active inference setup):
14
- API_BASE_URL = os.getenv("API_BASE_URL", "<your-active-endpoint>")
15
- MODEL_NAME = os.getenv("MODEL_NAME", "<your-active-model>")
16
-
17
- - The inference script must be named `inference.py` and placed in the root directory of the project
18
- - Participants must use OpenAI Client for all LLM calls using above variables
19
-
20
- STDOUT FORMAT
21
- - The script must emit exactly three line types to stdout, in this order:
22
-
23
- [START] task=<task_name> env=<benchmark> model=<model_name>
24
- [STEP] step=<n> action=<action_str> reward=<0.00> done=<true|false> error=<msg|null>
25
- [END] success=<true|false> steps=<n> score=<score> rewards=<r1,r2,...,rn>
26
-
27
- Rules:
28
- - One [START] line at episode begin.
29
- - One [STEP] line per step, immediately after env.step() returns.
30
- - One [END] line after env.close(), always emitted (even on exception).
31
- - reward and rewards are formatted to 2 decimal places.
32
- - done and success are lowercase booleans: true or false.
33
- - error is the raw last_action_error string, or null if none.
34
- - All fields on a single line with no newlines within a line.
35
- - Each tasks should return score in [0, 1]
36
-
37
- Example:
38
- [START] task=click-test env=miniwob model=Qwen3-VL-30B
39
- [STEP] step=1 action=click('123') reward=0.00 done=false error=null
40
- [STEP] step=2 action=fill('456','text') reward=0.00 done=false error=null
41
- [STEP] step=3 action=click('789') reward=1.00 done=true error=null
42
- [END] success=true steps=3 score=1.00 rewards=0.00,0.00,1.00
43
- """
44
-
45
  import asyncio
 
46
  import os
47
- import textwrap
 
 
48
  from typing import List, Optional
49
-
 
50
  from openai import OpenAI
51
 
52
- from my_env_v4 import MyEnvV4Action, MyEnvV4Env
53
- IMAGE_NAME = os.getenv("IMAGE_NAME") # If you are using docker image
54
- API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
55
-
56
- API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
57
- MODEL_NAME = os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
58
- TASK_NAME = os.getenv("MY_ENV_V4_TASK", "echo")
59
- BENCHMARK = os.getenv("MY_ENV_V4_BENCHMARK", "my_env_v4")
60
- MAX_STEPS = 8
61
- TEMPERATURE = 0.7
62
- MAX_TOKENS = 150
63
- SUCCESS_SCORE_THRESHOLD = 0.1 # normalized score in [0, 1]
64
-
65
- # Max possible reward: each token contributes 0.1, across all steps
66
- _MAX_REWARD_PER_STEP = MAX_TOKENS * 0.1
67
- MAX_TOTAL_REWARD = MAX_STEPS * _MAX_REWARD_PER_STEP
68
-
69
- SYSTEM_PROMPT = textwrap.dedent(
70
- """
71
- You are interacting with a simple echo environment.
72
- Each turn you must send a message. The environment will echo it back.
73
- Reward is proportional to message length: reward = len(message) * 0.1
74
- Your goal is to maximize total reward by sending meaningful, substantive messages.
75
- Reply with exactly one message string — no quotes, no prefixes, just the message text.
76
- """
77
- ).strip()
78
 
79
 
80
  def log_start(task: str, env: str, model: str) -> None:
81
  print(f"[START] task={task} env={env} model={model}", flush=True)
82
 
83
 
84
- def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
 
 
85
  error_val = error if error else "null"
86
  done_val = str(done).lower()
 
87
  print(
88
- f"[STEP] step={step} action={action} reward={reward:.2f} done={done_val} error={error_val}",
 
89
  flush=True,
90
  )
91
 
92
 
93
- def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
 
 
94
  rewards_str = ",".join(f"{r:.2f}" for r in rewards)
95
- print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}", flush=True)
 
 
 
 
96
 
97
 
98
- def build_user_prompt(step: int, last_echoed: str, last_reward: float, history: List[str]) -> str:
99
- history_block = "\n".join(history[-4:]) if history else "None"
100
- return textwrap.dedent(
101
- f"""
102
- Step: {step}
103
- Last echoed message: {last_echoed!r}
104
- Last reward: {last_reward:.2f}
105
- Previous steps:
106
- {history_block}
107
- Send your next message.
108
- """
109
- ).strip()
110
 
111
 
112
- def get_model_message(client: OpenAI, step: int, last_echoed: str, last_reward: float, history: List[str]) -> str:
113
- user_prompt = build_user_prompt(step, last_echoed, last_reward, history)
114
- try:
115
- completion = client.chat.completions.create(
116
- model=MODEL_NAME,
117
- messages=[
118
- {"role": "system", "content": SYSTEM_PROMPT},
119
- {"role": "user", "content": user_prompt},
120
- ],
121
- temperature=TEMPERATURE,
122
- max_tokens=MAX_TOKENS,
123
- stream=False,
124
  )
125
- text = (completion.choices[0].message.content or "").strip()
126
- return text if text else "hello"
127
- except Exception as exc:
128
- print(f"[DEBUG] Model request failed: {exc}", flush=True)
129
- return "hello"
130
 
 
 
 
 
 
131
 
132
- async def main() -> None:
133
- client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
134
 
135
- env = await MyEnvV4Env.from_docker_image(IMAGE_NAME)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- history: List[str] = []
138
- rewards: List[float] = []
139
- steps_taken = 0
140
- score = 0.0
141
- success = False
142
 
143
- log_start(task=TASK_NAME, env=BENCHMARK, model=MODEL_NAME)
 
 
144
 
 
 
 
 
145
  try:
146
- result = await env.reset() # OpenENV.reset()
147
- last_echoed = result.observation.echoed_message
148
- last_reward = 0.0
149
 
150
- for step in range(1, MAX_STEPS + 1):
151
- if result.done:
152
- break
153
 
154
- message = get_model_message(client, step, last_echoed, last_reward, history)
 
155
 
156
- result = await env.step(MyEnvV4Action(message=message))
157
- obs = result.observation
158
 
159
- reward = result.reward or 0.0
160
- done = result.done
161
- error = None
162
 
163
- rewards.append(reward)
164
- steps_taken = step
165
- last_echoed = obs.echoed_message
166
- last_reward = reward
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
- log_step(step=step, action=message, reward=reward, done=done, error=error)
169
 
170
- history.append(f"Step {step}: {message!r} -> reward {reward:+.2f}")
 
 
171
 
172
- if done:
173
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- score = sum(rewards) / MAX_TOTAL_REWARD if MAX_TOTAL_REWARD > 0 else 0.0
176
- score = min(max(score, 0.0), 1.0) # clamp to [0, 1]
177
- success = score >= SUCCESS_SCORE_THRESHOLD
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- finally:
180
  try:
181
- await env.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  except Exception as e:
183
- print(f"[DEBUG] env.close() error (container cleanup): {e}", flush=True)
184
- log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
 
187
  if __name__ == "__main__":
188
- asyncio.run(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
+ import json
3
  import os
4
+ import sys
5
+ import re
6
+ from pathlib import Path
7
  from typing import List, Optional
8
+ import httpx
9
+ from dotenv import load_dotenv
10
  from openai import OpenAI
11
 
12
+ ROOT = Path(__file__).resolve().parent
13
+ REPO_ROOT = ROOT.parent
14
+
15
+ if str(REPO_ROOT) not in sys.path:
16
+ sys.path.insert(0, str(REPO_ROOT))
17
+
18
+ load_dotenv()
19
+
20
+ TASK_DIR = ROOT / "tasks"
21
+ SYSTEM_PROMPT = (ROOT / "system_prompt.txt").read_text().strip()
22
+
23
+ HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
24
+ MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o")
25
+
26
+ BASE_URL = "https://vishvam10-cloudenv.hf.space"
27
+
28
+ TASK_ORDER = ["easy-task", "medium-task", "hard-task"]
29
+
30
+ MAX_PARSE_RETRIES = 3
31
+ MAX_STEPS = 5
32
+
33
+ client = OpenAI(base_url="https://api.openai.com/v1", api_key=HF_TOKEN)
 
 
 
 
34
 
35
 
36
  def log_start(task: str, env: str, model: str) -> None:
37
  print(f"[START] task={task} env={env} model={model}", flush=True)
38
 
39
 
40
+ def log_step(
41
+ step: int, action: str, reward: float, done: bool, error: Optional[str]
42
+ ) -> None:
43
  error_val = error if error else "null"
44
  done_val = str(done).lower()
45
+ action_safe = action.replace("\n", " ").replace("\r", "")[:120]
46
  print(
47
+ f"[STEP] step={step} action={action_safe} reward={reward:.2f} "
48
+ f"done={done_val} error={error_val}",
49
  flush=True,
50
  )
51
 
52
 
53
+ def log_end(
54
+ success: bool, steps: int, score: float, rewards: List[float]
55
+ ) -> None:
56
  rewards_str = ",".join(f"{r:.2f}" for r in rewards)
57
+ print(
58
+ f"[END] success={str(success).lower()} steps={steps} "
59
+ f"score={score:.3f} rewards={rewards_str}",
60
+ flush=True,
61
+ )
62
 
63
 
64
+ def load_task(name: str):
65
+ return json.loads((TASK_DIR / f"{name}.json").read_text())
 
 
 
 
 
 
 
 
 
 
66
 
67
 
68
+ async def register_pipeline(task: dict):
69
+ async with httpx.AsyncClient(timeout=30) as http:
70
+ r = await http.post(
71
+ f"{BASE_URL}/api/pipelines",
72
+ json={
73
+ "pipeline_id": task["pipeline_id"],
74
+ "broken_pipeline": task["broken_pipeline"],
75
+ "ideal_pipeline": task["ideal_pipeline"],
76
+ },
 
 
 
77
  )
 
 
 
 
 
78
 
79
+ if r.status_code not in (200, 201, 400):
80
+ print(
81
+ f"[PIPELINE REGISTER ERROR] {r.status_code} {r.text}",
82
+ flush=True,
83
+ )
84
 
 
 
85
 
86
+ async def env_reset(task_id: str, episode_id: str):
87
+ async with httpx.AsyncClient(timeout=30) as http:
88
+ r = await http.post(
89
+ f"{BASE_URL}/reset",
90
+ json={"task_id": task_id, "episode_id": episode_id},
91
+ )
92
+ r.raise_for_status()
93
+ return r.json()
94
+
95
+
96
+ async def env_step(action: dict):
97
+ async with httpx.AsyncClient(timeout=30) as http:
98
+ r = await http.post(
99
+ f"{BASE_URL}/step",
100
+ json={"action": action},
101
+ )
102
+ r.raise_for_status()
103
+ return r.json()
104
+
105
 
106
+ def clean_json(raw: str) -> str:
107
+ raw = raw.strip()
108
+ raw = re.sub(r"^```(?:json)?\s*", "", raw)
109
+ raw = re.sub(r"\s*```$", "", raw)
 
110
 
111
+ match = re.search(r"\{.*\}", raw, re.DOTALL)
112
+ if match:
113
+ return match.group(0)
114
 
115
+ return raw
116
+
117
+
118
+ def build_action(raw: str, task_id: str, episode_id: str):
119
  try:
120
+ raw = clean_json(raw)
121
+ data = json.loads(raw)
 
122
 
123
+ if isinstance(data, dict) and "action" in data:
124
+ data = data["action"]
 
125
 
126
+ if not isinstance(data, dict):
127
+ return None
128
 
129
+ service = data.get("service")
130
+ operation = data.get("operation")
131
 
132
+ if not service or not operation:
133
+ return None
 
134
 
135
+ instance_id = data.get("instance_id")
136
+
137
+ if isinstance(service, str) and ":" in service:
138
+ service, parsed_instance = service.split(":", 1)
139
+ instance_id = instance_id or parsed_instance
140
+
141
+ return {
142
+ "task_id": task_id,
143
+ "episode_id": episode_id,
144
+ "service": service.lower(),
145
+ "instance_id": instance_id,
146
+ "operation": operation.strip(),
147
+ "payload": data.get("payload", {})
148
+ if isinstance(data.get("payload"), dict)
149
+ else {},
150
+ "principal_arn": data.get("principal_arn"),
151
+ "intent": data.get("intent"),
152
+ }
153
+
154
+ except Exception:
155
+ return None
156
 
 
157
 
158
+ def call_model(step, state, broken, ideal, history):
159
+ prompt = f"""
160
+ Return ONLY valid JSON.
161
 
162
+ STEP {step}
163
+ STATE {json.dumps(state)[:1000]}
164
+ BROKEN {json.dumps(broken)[:1000]}
165
+ IDEAL {json.dumps(ideal)[:1000]}
166
+ HISTORY {history[-5:]}
167
+ """
168
+
169
+ res = client.chat.completions.create(
170
+ model=MODEL_NAME,
171
+ messages=[
172
+ {"role": "system", "content": SYSTEM_PROMPT},
173
+ {"role": "user", "content": prompt},
174
+ ],
175
+ temperature=0.3,
176
+ max_tokens=500,
177
+ )
178
+
179
+ return (res.choices[0].message.content or "").strip()
180
+
181
+
182
+ async def run_episode(task):
183
+ task_id = task["pipeline_id"]
184
+ episode_id = f"{task_id}-episode-1"
185
+
186
+ log_start(task=task_id, env="http", model=MODEL_NAME)
187
 
188
+ result = await env_reset(task_id, episode_id)
189
+
190
+ await env_step(
191
+ {
192
+ "task_id": task_id,
193
+ "episode_id": episode_id,
194
+ "service": "internal",
195
+ "instance_id": None,
196
+ "operation": "InitializeEnvironment",
197
+ "payload": {},
198
+ }
199
+ )
200
+
201
+ await register_pipeline(task)
202
+
203
+ obs = {}
204
+ history = []
205
+ rewards = []
206
+ step_count = 0
207
+
208
+ for step in range(MAX_STEPS):
209
+ if obs.get("done"):
210
+ break
211
+
212
+ error = None
213
 
 
214
  try:
215
+ raw = call_model(
216
+ step,
217
+ obs,
218
+ task["broken_pipeline"],
219
+ task["ideal_pipeline"],
220
+ history,
221
+ )
222
+
223
+ action = build_action(raw, task_id, episode_id)
224
+
225
+ if action is None:
226
+ error = "invalid_action"
227
+ log_step(step, "INVALID_ACTION", 0.0, False, error)
228
+ continue
229
+
230
+ result = await env_step(action)
231
+ obs = result["observation"]
232
+
233
+ reward = float(obs.get("reward", 0.0))
234
+ done = bool(obs.get("done", False))
235
+
236
+ rewards.append(reward)
237
+ history.append(raw)
238
+ step_count += 1
239
+
240
+ log_step(
241
+ step=step_count,
242
+ action=str(action),
243
+ reward=reward,
244
+ done=done,
245
+ error=None,
246
+ )
247
+
248
  except Exception as e:
249
+ error = str(e)
250
+
251
+ log_step(step, "ERROR", 0.0, False, error)
252
+ break
253
+
254
+ if obs.get("done"):
255
+ break
256
+
257
+ success = obs.get("done", False)
258
+ score = sum(rewards) / (len(rewards) or 1)
259
+
260
+ log_end(success=success, steps=step_count, score=score, rewards=rewards)
261
+
262
+
263
+ async def main():
264
+ for name in TASK_ORDER:
265
+ task = load_task(name)
266
+ await run_episode(task)
267
 
268
 
269
  if __name__ == "__main__":
270
+ asyncio.run(main())