thorodin103 commited on
Commit
734c6cd
·
verified ·
1 Parent(s): 5f45585

Upload inference.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. inference.py +276 -0
inference.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ """
3
+ Inference Script — Data Cleaning OpenEnv
4
+ =========================================
5
+ Runs a baseline LLM agent against all 3 tasks
6
+ and produces reproducible scores.
7
+
8
+ Required environment variables:
9
+ API_BASE_URL — LLM API endpoint
10
+ MODEL_NAME — model identifier
11
+ HF_TOKEN — Hugging Face API key
12
+ """
13
+
14
+ import os
15
+ import sys
16
+ import json
17
+ import time
18
+ from typing import List, Dict, Any
19
+
20
+ from openai import OpenAI
21
+
22
+ # ─────────────────────────────────────────
23
+ # CONFIG
24
+ # ─────────────────────────────────────────
25
+
26
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
27
+ API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY", "")
28
+ MODEL_NAME = os.getenv("MODEL_NAME", "meta-llama/Llama-3.3-70B-Instruct")
29
+
30
+ MAX_STEPS = 15
31
+ TEMPERATURE = 0.1
32
+ MAX_TOKENS = 400
33
+
34
+ VALID_TASKS = [
35
+ "easy_dedup_rename",
36
+ "medium_missing_dtype",
37
+ "hard_full_pipeline"
38
+ ]
39
+
40
+ SYSTEM_PROMPT = """
41
+ You are an expert data cleaning agent.
42
+ You will receive information about a dirty dataset and must clean it
43
+ step by step using the available operations.
44
+
45
+ Available operations:
46
+ - remove_duplicates: {"operation": "remove_duplicates", "parameters": {}}
47
+ - fill_missing: {"operation": "fill_missing", "parameters": {"strategy": "mean|median|mode"}}
48
+ - fix_dtype: {"operation": "fix_dtype", "parameters": {"dtype": "auto"}}
49
+ - remove_outliers: {"operation": "remove_outliers", "parameters": {"method": "iqr"}}
50
+ - rename_columns: {"operation": "rename_columns", "parameters": {}}
51
+ - validate_schema: {"operation": "validate_schema", "parameters": {}}
52
+ - finish: {"operation": "finish", "parameters": {}}
53
+
54
+ Rules:
55
+ 1. Always respond with ONLY a valid JSON object
56
+ 2. No explanations, no markdown, no extra text
57
+ 3. Just the JSON action object
58
+ 4. Call finish when you think the dataset is clean
59
+
60
+ Example response:
61
+ {"operation": "remove_duplicates", "parameters": {}}
62
+ """
63
+
64
+
65
+ def build_user_prompt(observation: Dict[str, Any]) -> str:
66
+ return f"""
67
+ Task: {observation.get("task_description", "")}
68
+ Step: {observation.get("step", 0)}
69
+ Last message: {observation.get("message", "")}
70
+
71
+ Current dataset info:
72
+ - Shape: {observation.get("shape", [])}
73
+ - Columns: {observation.get("columns", [])}
74
+ - Duplicate rows: {observation.get("duplicate_count", 0)}
75
+ - Missing values: {observation.get("missing_values", {})}
76
+ - Data types: {observation.get("dtypes", {})}
77
+
78
+ Sample rows (first 3):
79
+ {json.dumps(observation.get("sample_rows", []), indent=2)}
80
+
81
+ Available operations: {observation.get("available_operations", [])}
82
+
83
+ What is your next action? Respond with JSON only.
84
+ """
85
+
86
+
87
+ def parse_action(response_text: str) -> Dict[str, Any]:
88
+ """Parse LLM response into action dict."""
89
+ text = response_text.strip()
90
+
91
+ # Remove markdown if present
92
+ if "```json" in text:
93
+ text = text.split("```json")[1].split("```")[0].strip()
94
+ elif "```" in text:
95
+ text = text.split("```")[1].split("```")[0].strip()
96
+
97
+ try:
98
+ action = json.loads(text)
99
+ if "operation" not in action:
100
+ return {"operation": "finish", "parameters": {}}
101
+ if "parameters" not in action:
102
+ action["parameters"] = {}
103
+ return action
104
+ except json.JSONDecodeError:
105
+ # Try to find JSON in text
106
+ import re
107
+ match = re.search(r"\{.*\}", text, re.DOTALL)
108
+ if match:
109
+ try:
110
+ return json.loads(match.group())
111
+ except Exception:
112
+ pass
113
+ return {"operation": "finish", "parameters": {}}
114
+
115
+
116
+ def run_task(
117
+ client: OpenAI,
118
+ env_module,
119
+ task_id: str
120
+ ) -> Dict[str, Any]:
121
+ """Run one full episode for a task."""
122
+ print(f"\n{'='*50}")
123
+ print(f" Task: {task_id}")
124
+ print(f"{'='*50}")
125
+
126
+ # Import here to use local environment
127
+ from environment import DataCleaningEnv
128
+ from models import Action
129
+
130
+ env = DataCleaningEnv(task_id=task_id)
131
+
132
+ # Reset
133
+ result = env.reset()
134
+ obs = result.observation.dict()
135
+ done = result.done
136
+ step = 0
137
+ rewards = []
138
+ actions_taken = []
139
+
140
+ print(f" Description: {obs.get('task_description', '')[:80]}...")
141
+ print(f" Initial shape: {obs.get('shape', [])}")
142
+ print(f" Duplicates: {obs.get('duplicate_count', 0)}")
143
+ print(f" Missing: {sum(obs.get('missing_values', {}).values())}")
144
+
145
+ while not done and step < MAX_STEPS:
146
+ step += 1
147
+
148
+ # Build prompt
149
+ user_prompt = build_user_prompt(obs)
150
+ messages = [
151
+ {"role": "system", "content": SYSTEM_PROMPT},
152
+ {"role": "user", "content": user_prompt}
153
+ ]
154
+
155
+ # Call LLM
156
+ try:
157
+ completion = client.chat.completions.create(
158
+ model=MODEL_NAME,
159
+ messages=messages,
160
+ temperature=TEMPERATURE,
161
+ max_tokens=MAX_TOKENS,
162
+ stream=False
163
+ )
164
+ response_text = completion.choices[0].message.content or ""
165
+ except Exception as e:
166
+ print(f" [Step {step}] LLM error: {e}")
167
+ response_text = '{"operation": "finish", "parameters": {}}'
168
+
169
+ # Parse action
170
+ action_dict = parse_action(response_text)
171
+ action = Action(
172
+ operation=action_dict.get("operation", "finish"),
173
+ parameters=action_dict.get("parameters", {})
174
+ )
175
+
176
+ print(f" [Step {step}] Action: {action.operation} {action.parameters}")
177
+
178
+ # Step environment
179
+ result = env.step(action)
180
+ obs = result.observation.dict()
181
+ done = result.done
182
+ reward = result.reward.total
183
+ rewards.append(reward)
184
+ actions_taken.append(action.operation)
185
+
186
+ print(f" Reward: {reward:.4f} | Message: {obs.get('message', '')[:60]}")
187
+
188
+ if done:
189
+ print(f" Episode done at step {step}")
190
+ break
191
+
192
+ # Small delay to avoid rate limiting
193
+ time.sleep(0.5)
194
+
195
+ final_reward = rewards[-1] if rewards else 0.0
196
+ print(f"\n Final Score: {final_reward:.4f}")
197
+ print(f" Steps taken: {step}")
198
+ print(f" Actions: {actions_taken}")
199
+
200
+ return {
201
+ "task_id": task_id,
202
+ "final_score": round(final_reward, 4),
203
+ "steps": step,
204
+ "rewards": rewards,
205
+ "actions": actions_taken,
206
+ "done": done
207
+ }
208
+
209
+
210
+ def main():
211
+ print("\n" + "="*50)
212
+ print(" Data Cleaning OpenEnv — Baseline Inference")
213
+ print("="*50)
214
+ print(f" Model: {MODEL_NAME}")
215
+ print(f" API URL: {API_BASE_URL}")
216
+ print(f" Tasks: {VALID_TASKS}")
217
+ print("="*50)
218
+
219
+ # Validate config
220
+ if not API_KEY:
221
+ print("ERROR: HF_TOKEN or API_KEY not set")
222
+ sys.exit(1)
223
+
224
+ if not MODEL_NAME:
225
+ print("ERROR: MODEL_NAME not set")
226
+ sys.exit(1)
227
+
228
+ # Init client
229
+ client = OpenAI(
230
+ base_url=API_BASE_URL,
231
+ api_key=API_KEY
232
+ )
233
+
234
+ # Run all tasks
235
+ all_results = []
236
+ for task_id in VALID_TASKS:
237
+ try:
238
+ result = run_task(client, None, task_id)
239
+ all_results.append(result)
240
+ except Exception as e:
241
+ print(f" ERROR on task {task_id}: {e}")
242
+ all_results.append({
243
+ "task_id": task_id,
244
+ "final_score": 0.0,
245
+ "error": str(e)
246
+ })
247
+
248
+ # Summary
249
+ print("\n" + "="*50)
250
+ print(" FINAL RESULTS SUMMARY")
251
+ print("="*50)
252
+ total_score = 0.0
253
+ for r in all_results:
254
+ score = r.get("final_score", 0.0)
255
+ total_score += score
256
+ status = "ERROR" if "error" in r else "OK"
257
+ print(f" {r['task_id']:<30} Score: {score:.4f} [{status}]")
258
+
259
+ avg_score = total_score / len(all_results)
260
+ print(f"\n Average Score: {avg_score:.4f}")
261
+ print("="*50)
262
+
263
+ # Save results
264
+ output = {
265
+ "model": MODEL_NAME,
266
+ "tasks": all_results,
267
+ "average_score": round(avg_score, 4)
268
+ }
269
+ with open("baseline_results.json", "w") as f:
270
+ json.dump(output, f, indent=2)
271
+ print("\n Results saved to baseline_results.json")
272
+ print(" Done!")
273
+
274
+
275
+ if __name__ == "__main__":
276
+ main()