Dhruv Goyal commited on
Commit
25a59d1
·
1 Parent(s): fdb7c86

fixes stuff

Browse files
Files changed (1) hide show
  1. inference.py +175 -104
inference.py CHANGED
@@ -1,120 +1,191 @@
1
- import asyncio
2
- import os
3
- import textwrap
4
- from typing import List, Optional
 
 
 
 
5
  from openai import OpenAI
6
-
7
- from my_env_v4 import MyEnvV4Action, MyEnvV4Env
8
-
9
- # ENV CONFIG
10
- IMAGE_NAME = os.getenv("IMAGE_NAME")
11
- API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
12
- API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
13
- MODEL_NAME = os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
14
-
15
- TASK_NAME = os.getenv("MY_ENV_V4_TASK", "echo")
16
- BENCHMARK = os.getenv("MY_ENV_V4_BENCHMARK", "my_env_v4")
17
-
18
- MAX_STEPS = 8
19
- MAX_TOKENS = 150
20
- TEMPERATURE = 0.7
21
-
22
- SUCCESS_SCORE_THRESHOLD = 0.1
23
-
24
- # reward normalization
25
- _MAX_REWARD_PER_STEP = MAX_TOKENS * 0.1
26
- MAX_TOTAL_REWARD = MAX_STEPS * _MAX_REWARD_PER_STEP
27
-
28
- SYSTEM_PROMPT = """You are interacting with an environment. Respond with a useful message each step."""
29
-
30
- # ---------------- LOGGING (STRICT FORMAT) ---------------- #
31
-
32
- def log_start():
33
- print(f"[START] task={TASK_NAME} env={BENCHMARK} model={MODEL_NAME}", flush=True)
34
-
35
- def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]):
36
- error_val = error if error else "null"
37
- print(
38
- f"[STEP] step={step} action={action} reward={reward:.2f} done={str(done).lower()} error={error_val}",
39
- flush=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  )
41
 
42
- def log_end(success: bool, steps: int, score: float, rewards: List[float]):
43
- rewards_str = ",".join(f"{r:.2f}" for r in rewards)
44
- print(
45
- f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}",
46
- flush=True
47
- )
48
 
49
- # ---------------- MODEL ---------------- #
 
 
 
 
50
 
51
- def get_model_message(client, step, history):
52
  try:
53
- completion = client.chat.completions.create(
54
- model=MODEL_NAME,
55
- messages=[
56
- {"role": "system", "content": SYSTEM_PROMPT},
57
- {"role": "user", "content": f"Step {step}. Continue interaction."},
58
- ],
59
- temperature=TEMPERATURE,
60
- max_tokens=MAX_TOKENS,
61
  )
62
- return completion.choices[0].message.content.strip() or "hello"
63
- except:
64
- return "hello"
65
-
66
- # ---------------- MAIN ---------------- #
67
-
68
- async def main():
69
- client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
70
- env = await MyEnvV4Env.from_docker_image(IMAGE_NAME)
71
 
72
- rewards = []
73
- history = []
74
 
75
- steps_taken = 0
76
- success = False
77
- score = 0.0
78
 
79
- log_start()
80
-
81
- try:
82
- result = await env.reset()
83
-
84
- for step in range(1, MAX_STEPS + 1):
85
- if result.done:
86
- break
87
-
88
- action = get_model_message(client, step, history)
89
-
90
- result = await env.step(MyEnvV4Action(message=action))
91
-
92
- reward = result.reward or 0.0
93
- done = result.done
94
-
95
- rewards.append(reward)
96
- steps_taken = step
97
-
98
- log_step(step, action, reward, done, None)
99
-
100
- history.append(action)
101
-
102
- if done:
103
- break
104
-
105
- # score calculation
106
- score = sum(rewards) / MAX_TOTAL_REWARD if MAX_TOTAL_REWARD else 0.0
107
- score = max(0.0, min(score, 1.0))
108
- success = score >= SUCCESS_SCORE_THRESHOLD
109
 
110
- finally:
111
  try:
112
- await env.close()
113
- except:
114
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- log_end(success, steps_taken, score, rewards)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
 
119
  if __name__ == "__main__":
120
- asyncio.run(main())
 
1
+ """
2
+ DataClean OpenEnv — Inference Script
3
+ =====================================
4
+ Runs all 4 tasks in parallel using concurrent.futures.ThreadPoolExecutor.
5
+ Uses env vars: HF_TOKEN, API_BASE_URL, MODEL_NAME (per hackathon spec).
6
+ """
7
+ import os, json, time, sys
8
+ import requests
9
  from openai import OpenAI
10
+ from concurrent.futures import ThreadPoolExecutor, as_completed
11
+ from typing import Dict, Tuple
12
+
13
+ ENV_URL = os.environ.get("ENV_URL", "http://localhost:7860")
14
+ API_KEY = os.environ.get("HF_TOKEN", "")
15
+ BASE_URL = os.environ.get("API_BASE_URL", "https://api.openai.com/v1")
16
+ MODEL = os.environ.get("MODEL_NAME", "gpt-4o-mini")
17
+
18
+ TASK_MAX_STEPS = {
19
+ "task1": 10,
20
+ "task2": 20,
21
+ "task3": 30,
22
+ "task4_data_drift": 40,
23
+ }
24
+
25
+ SYSTEM_PROMPT = """You are an expert data cleaning agent. Respond ONLY with valid JSON — no prose, no markdown.
26
+
27
+ Operations:
28
+ fill_nulls: {"operation":"fill_nulls","column":"<col>","strategy":"mean|median|mode|constant","table_name":"<tbl>"}
29
+ cast_column: {"operation":"cast_column","column":"<col>","dtype":"int|float|str|datetime","table_name":"<tbl>"}
30
+ remove_duplicates: {"operation":"remove_duplicates","table_name":"<tbl>"}
31
+ normalize_values: {"operation":"normalize_values","column":"<col>","method":"upper|lower|regex","table_name":"<tbl>"}
32
+ filter_outliers: {"operation":"filter_outliers","column":"<col>","method":"iqr|zscore","threshold":1.5,"table_name":"<tbl>"}
33
+ merge_tables: {"operation":"merge_tables","left_table":"orders","right_table":"customers","on":"customer_id","output_table":"merged"}
34
+ add_derived_column: {"operation":"add_derived_column","column_name":"order_year","source_column":"order_date","transform":"year_from_date","table_name":"merged"}
35
+ submit: {"operation":"submit"}
36
+
37
+ Task strategies:
38
+ task1: fill_nulls(age,median,main)->cast_column(age,int,main)->fill_nulls(salary,mean,main)->submit
39
+ task2: remove_duplicates(main)->normalize_values(country,upper,main)->cast_column(order_date,datetime,main)->fill_nulls(amount,mean,main)->submit
40
+ task3: merge_tables(orders,customers,customer_id)->fill_nulls(age,median,merged)->cast_column(age,int,merged)->filter_outliers(amount,iqr,1.5,merged)->add_derived_column(order_year,order_date,year_from_date,merged)->submit
41
+ task4_data_drift: filter_outliers(amount,iqr,1.5,stream)->fill_nulls(amount,mean,stream)->cast_column(amount,float,stream)->fill_nulls(category,mode,stream)->fill_nulls(region,mode,stream)->cast_column(event_ts,datetime,stream)->submit
42
+ """
43
+
44
+
45
+ def _make_client():
46
+ return OpenAI(api_key=API_KEY, base_url=BASE_URL)
47
+
48
+
49
+ def _build_prompt(obs: dict, task_id: str) -> str:
50
+ drift_note = ""
51
+ if task_id == "task4_data_drift":
52
+ drift_note = f"\nSTREAM ROW COUNT: {obs.get('row_count',{}).get('stream','?')}"
53
+ return (
54
+ f"Task: {obs['task_id']}\n"
55
+ f"Step: {obs['step_count']}/{obs['max_steps']}\n"
56
+ f"Score: {obs['partial_score']:.4f}\n"
57
+ f"Last message: {obs['message']}\n"
58
+ f"Schema errors: {obs['schema_errors'][:5]}\n"
59
+ f"Column dtypes: {json.dumps(obs['column_dtypes'])}\n"
60
+ f"Null counts: {json.dumps(obs['null_counts'])}\n"
61
+ f"Duplicate counts: {obs['duplicate_count']}\n"
62
+ f"Row counts: {obs['row_count']}\n"
63
+ f"Available ops: {obs['available_operations']}"
64
+ f"{drift_note}\n\nNext action JSON:"
65
  )
66
 
 
 
 
 
 
 
67
 
68
+ def run_episode(task_id: str, seed: int = 42) -> Tuple[str, float, float]:
69
+ session_id = f"inference_{task_id}"
70
+ client = _make_client()
71
+ t0 = time.time()
72
+ max_steps = TASK_MAX_STEPS[task_id]
73
 
 
74
  try:
75
+ resp = requests.post(
76
+ f"{ENV_URL}/reset",
77
+ json={"task_id": task_id, "seed": seed, "session_id": session_id},
78
+ timeout=30,
 
 
 
 
79
  )
80
+ resp.raise_for_status()
81
+ obs = resp.json()
82
+ done = obs.get("done", False)
83
+ except Exception as e:
84
+ print(f" [{task_id}] Reset failed: {e}")
85
+ return task_id, 0.0, 0.0
 
 
 
86
 
87
+ print(f" [{task_id}] started | score={obs['partial_score']:.3f}")
 
88
 
89
+ for step_num in range(max_steps):
90
+ if done:
91
+ break
92
 
93
+ try:
94
+ prompt = _build_prompt(obs, task_id)
95
+ response = client.chat.completions.create(
96
+ model=MODEL,
97
+ messages=[
98
+ {"role": "system", "content": SYSTEM_PROMPT},
99
+ {"role": "user", "content": prompt},
100
+ ],
101
+ temperature=0.0,
102
+ max_tokens=300,
103
+ )
104
+ raw = response.choices[0].message.content.strip()
105
+ if "```" in raw:
106
+ raw = raw.split("```")[1]
107
+ if raw.startswith("json"):
108
+ raw = raw[4:]
109
+ action = json.loads(raw)
110
+ except Exception as e:
111
+ print(f" [{task_id}] LLM error step {step_num+1}: {e} — submitting")
112
+ action = {"operation": "submit"}
 
 
 
 
 
 
 
 
 
 
113
 
 
114
  try:
115
+ step_resp = requests.post(
116
+ f"{ENV_URL}/step?session_id={session_id}",
117
+ json=action,
118
+ timeout=30,
119
+ )
120
+ step_resp.raise_for_status()
121
+ data = step_resp.json()
122
+ obs = data["observation"]
123
+ done = data["done"]
124
+ except Exception as e:
125
+ print(f" [{task_id}] Step error: {e}")
126
+ break
127
+
128
+ print(f" [{task_id}] step {step_num+1:2d} | op={action.get('operation'):20s} | "
129
+ f"reward={data.get('reward', 0):+.4f} | score={obs['partial_score']:.4f}")
130
+ time.sleep(0.3)
131
+
132
+ elapsed = round(time.time() - t0, 2)
133
+ score = float(obs.get("partial_score", 0.0))
134
+ print(f" [{task_id}] FINAL score={score:.4f} | {elapsed}s")
135
+ return task_id, score, elapsed
136
+
137
+
138
+ def main():
139
+ if not API_KEY:
140
+ print("ERROR: HF_TOKEN not set")
141
+ sys.exit(1)
142
 
143
+ try:
144
+ h = requests.get(f"{ENV_URL}/health", timeout=10)
145
+ print(f"Server health: {h.json()}")
146
+ except Exception as e:
147
+ print(f"ERROR: Cannot reach server at {ENV_URL}: {e}")
148
+ sys.exit(1)
149
+
150
+ tasks = list(TASK_MAX_STEPS.keys())
151
+ print(f"\n{'='*60}")
152
+ print(f" DataClean Inference | model={MODEL} | seed=42")
153
+ print(f" Running {len(tasks)} tasks SIMULTANEOUSLY")
154
+ print(f"{'='*60}\n")
155
+
156
+ t_total = time.time()
157
+ scores: Dict[str, float] = {}
158
+ elapsed: Dict[str, float] = {}
159
+
160
+ with ThreadPoolExecutor(max_workers=len(tasks)) as pool:
161
+ futures = {
162
+ pool.submit(run_episode, task_id, 42): task_id
163
+ for task_id in tasks
164
+ }
165
+ for future in as_completed(futures):
166
+ task_id = futures[future]
167
+ try:
168
+ tid, score, secs = future.result()
169
+ scores[tid] = round(score, 4)
170
+ elapsed[tid] = secs
171
+ except Exception as exc:
172
+ print(f" [{task_id}] FAILED: {exc}")
173
+ scores[task_id] = 0.0
174
+ elapsed[task_id] = -1.0
175
+
176
+ wall_time = round(time.time() - t_total, 2)
177
+ mean = round(sum(scores.values()) / len(scores), 4) if scores else 0.0
178
+
179
+ print(f"\n{'='*60}")
180
+ print(f" RESULTS (wall time: {wall_time}s)")
181
+ print(f"{'='*60}")
182
+ for k, v in scores.items():
183
+ bar = "#" * int(v * 25)
184
+ print(f" {k:<25} {v:.4f} {bar}")
185
+ print(f" {'mean':<25} {mean:.4f}")
186
+ print(f"{'='*60}\n")
187
+ print(json.dumps({**scores, "mean": mean, "wall_time_seconds": wall_time}, indent=2))
188
 
189
 
190
  if __name__ == "__main__":
191
+ main()