Deadman922 commited on
Commit
c968973
·
verified ·
1 Parent(s): 0f8302e

Delete inference.py

Browse files
Files changed (1) hide show
  1. inference.py +0 -462
inference.py DELETED
@@ -1,462 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import os
4
- import sys
5
- import json
6
- import re
7
- from typing import List, Optional
8
- from openai import OpenAI
9
-
10
- # ============================================================================
11
- # LOAD ENVIRONMENT VARIABLES
12
- # ============================================================================
13
-
14
- # Try to load from .env file if it exists
15
- try:
16
- from dotenv import load_dotenv
17
- load_dotenv()
18
- except ImportError:
19
- pass # dotenv is optional
20
-
21
- # ============================================================================
22
- # MANDATORY ENVIRONMENT VARIABLES
23
- # ============================================================================
24
-
25
- # Before submitting, ensure following variables are defined in your environment:
26
- # API_BASE_URL The API endpoint for LLM.
27
- # MODEL_NAME The model identifier to use for inference.
28
- # HF_TOKEN Your Hugging Face / API key.
29
- # LOCAL_IMAGE_NAME The name of local image to use for environment if you are using from_docker_image() method
30
-
31
- # Defaults are set only for API_BASE_URL and MODEL_NAME
32
- # (and should reflect your active inference setup):
33
- API_BASE_URL = (
34
- os.getenv("API_BASE_URL") or
35
- os.getenv("HF_API_BASE_URL") or
36
- "https://router.huggingface.co/v1"
37
- )
38
- MODEL_NAME = (
39
- os.getenv("MODEL_NAME") or
40
- "meta-llama/Llama-3.3-70B-Instruct"
41
- )
42
- API_KEY = (
43
- os.getenv("API_KEY") or
44
- os.getenv("HF_TOKEN") or
45
- os.getenv("OPENAI_API_KEY")
46
- )
47
- LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
48
-
49
- # SQL Quality Environment specific variables
50
- ENV_BASE_URL = (
51
- os.getenv("ENV_BASE_URL") or
52
- os.getenv("SPACE_URL") or
53
- "https://deadman922-sql-quality-env.hf.space"
54
- )
55
- TASK_NAME = os.getenv("SQL_QUALITY_TASK", "null_audit")
56
- BENCHMARK = "sql_quality_env"
57
- MAX_STEPS = 20
58
- SUCCESS_SCORE_THRESHOLD = 0.1 # normalized score in [0, 1]
59
-
60
- import argparse
61
-
62
- def _parse_args():
63
- parser = argparse.ArgumentParser(
64
- description="SQL Quality Environment Inference Script"
65
- )
66
- parser.add_argument(
67
- "--task", type=str, default=None,
68
- help="Task ID to run"
69
- )
70
- parser.add_argument(
71
- "--url", type=str, default=None,
72
- help="Environment base URL"
73
- )
74
- parser.add_argument(
75
- "--seed", type=int, default=42,
76
- help="Random seed"
77
- )
78
- return parser.parse_args()
79
-
80
- # ============================================================================
81
- # LOGGING FUNCTIONS (STDOUT FORMAT)
82
- # ============================================================================
83
-
84
- def log_start(task: str, env: str, model: str) -> None:
85
- """Log [START] line according to required format."""
86
- print(f"[START] task={task} env={env} model={model}", flush=True)
87
-
88
-
89
- def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
90
- """Log [STEP] line according to required format."""
91
- error_val = error if error else "null"
92
- done_val = str(done).lower()
93
- print(
94
- f"[STEP] step={step} action={action} reward={reward:.2f} done={done_val} error={error_val}",
95
- flush=True,
96
- )
97
-
98
-
99
- def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
100
- """Log [END] line according to required format."""
101
- rewards_str = ",".join(f"{r:.2f}" for r in rewards)
102
- print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}", flush=True)
103
-
104
-
105
- # ============================================================================
106
- # SQL QUALITY AGENT CLASS
107
- # ============================================================================
108
-
109
- class SQLQualityAgent:
110
- """SQL Quality Environment Agent adapted for stdout format requirements."""
111
-
112
- def __init__(self, llm_base_url: str, model_name: str, api_key: str, env_base_url: str):
113
- self.llm_base_url = llm_base_url
114
- self.model_name = model_name
115
- self.client = OpenAI(base_url=llm_base_url, api_key=api_key)
116
- self.env_base_url = env_base_url
117
- self.episode_id = None
118
- self.task_id = None
119
-
120
- def health_check(self) -> bool:
121
- """Check if environment is healthy."""
122
- try:
123
- import requests
124
- response = requests.get(f"{self.env_base_url}/health", timeout=5)
125
- return response.status_code == 200
126
- except Exception:
127
- return False
128
-
129
- def reset(self, task_id: str, seed: int = None) -> dict:
130
- """Reset environment for a specific task."""
131
- try:
132
- import requests
133
- data = {"task_id": task_id}
134
- if seed is not None:
135
- data["seed"] = seed
136
-
137
- response = requests.post(f"{self.env_base_url}/reset", json=data, timeout=10)
138
- if response.status_code == 200:
139
- result = response.json()
140
- self.episode_id = result.get("episode_id")
141
- self.task_id = task_id
142
- return result
143
- else:
144
- raise Exception(f"Reset failed: {response.status_code} - {response.text}")
145
- except Exception as e:
146
- raise Exception(f"Reset error: {e}")
147
-
148
- def step(self, action_type: str, sql: str = None, column_name: str = None) -> dict:
149
- """Execute a step in the environment."""
150
- try:
151
- import requests
152
- data = {
153
- "task_id": self.task_id,
154
- "episode_id": self.episode_id,
155
- "action_type": action_type
156
- }
157
-
158
- if sql:
159
- data["sql"] = sql
160
- if column_name:
161
- data["column_name"] = column_name
162
-
163
- response = requests.post(f"{self.env_base_url}/step", json=data, timeout=10)
164
- if response.status_code == 200:
165
- return response.json()
166
- else:
167
- raise Exception(f"Step failed: {response.status_code} - {response.text}")
168
- except Exception as e:
169
- raise Exception(f"Step error: {e}")
170
-
171
- def generate_sql(self, task_description: str, schema_info: list, sample_rows: dict,
172
- last_action: str = None, last_reward: float = None,
173
- step: int = 1) -> str:
174
- """Generate SQL using LLM based on current state."""
175
-
176
- # Build system prompt
177
- system_prompt = """You are a SQL data quality expert. Given a task, schema, and step number, output exactly one SQL statement or one keyword: QUERY, PROFILE, FIX, or SUBMIT.
178
-
179
- STRICT RULES BY STEP:
180
- - Steps 1-3: Use QUERY with SELECT statements to investigate the problem.
181
- - Steps 4-6: Use FIX with UPDATE/DELETE/CREATE to fix the identified issue.
182
- - Step 7+: Output SUBMIT immediately. Do not query further.
183
- - If step >= 15: Output SUBMIT regardless of anything else.
184
-
185
- TASK-SPECIFIC FIX PATTERNS:
186
- - type_repair: UPDATE transactions SET amount = CAST(amount AS REAL) WHERE typeof(amount) != 'real'
187
- - dedup_bug: DELETE FROM order_items WHERE rowid NOT IN (SELECT MIN(rowid) FROM order_items GROUP BY order_id)
188
- - fk_integrity: DELETE FROM invoices WHERE customer_id NOT IN (SELECT id FROM customers)
189
- - schema_drift: CREATE TABLE canonical_events AS SELECT event_id,user_id,event_type,CAST(amount AS REAL) as amount,event_time FROM events_v1 UNION ALL SELECT event_id,user_id,event_type,amount,event_time FROM events_v2
190
-
191
- Output only the SQL or keyword. No explanation."""
192
-
193
- # Build user prompt
194
- schema_text = json.dumps(schema_info, indent=2)
195
- sample_text = json.dumps(sample_rows, indent=2)
196
-
197
- user_prompt = f"""Task: {task_description}
198
-
199
- Step: {step}
200
- Last Action: {last_action or 'None'}
201
- Last Reward: {last_reward or 0.00}
202
-
203
- Schema:
204
- {schema_text}
205
-
206
- Sample Data:
207
- {sample_text}
208
-
209
- Generate your next action (SQL or action name):"""
210
-
211
- try:
212
- completion = self.client.chat.completions.create(
213
- model=self.model_name,
214
- messages=[
215
- {"role": "system", "content": system_prompt},
216
- {"role": "user", "content": user_prompt},
217
- ],
218
- temperature=0.7,
219
- max_tokens=200,
220
- stream=False,
221
- )
222
-
223
- text = (completion.choices[0].message.content or "").strip()
224
-
225
- # Extract SQL or action from response
226
- if text.upper().startswith(('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP')):
227
- return text
228
- elif text.upper() in ['QUERY', 'PROFILE', 'FIX', 'SUBMIT']:
229
- return text.upper()
230
- else:
231
- # Try to extract SQL from response
232
- sql_match = re.search(r'((?:SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP)[\s\S]*?(?:;|$))', text, re.IGNORECASE | re.DOTALL)
233
- if sql_match:
234
- return sql_match.group(1).strip()
235
- return 'QUERY' # Default fallback
236
-
237
- except Exception as e:
238
- print(f"[DEBUG] LLM generation failed: {e}", flush=True, file=sys.stderr)
239
- return 'SELECT 1;' # Safe fallback
240
-
241
- def get_fallback_actions(self, task_id):
242
- fallbacks = {
243
- "null_audit": [
244
- ("query",
245
- "SELECT COUNT(*) as total, "
246
- "SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) "
247
- "as null_email FROM users",
248
- None),
249
- ("submit", None, "email"),
250
- ],
251
- "type_repair": [
252
- ("query",
253
- "SELECT typeof(amount), COUNT(*) "
254
- "FROM transactions GROUP BY typeof(amount)",
255
- None),
256
- ("fix",
257
- "CREATE TABLE transactions_new AS "
258
- "SELECT id, user_id, "
259
- "CAST(amount AS REAL) as amount, "
260
- "description, category, date, "
261
- "transaction_date FROM transactions",
262
- None),
263
- ("fix",
264
- "DROP TABLE transactions",
265
- None),
266
- ("fix",
267
- "ALTER TABLE transactions_new RENAME TO transactions",
268
- None),
269
- ("submit", None, ""),
270
- ],
271
- "dedup_bug": [
272
- ("query",
273
- "SELECT order_id, COUNT(*) as cnt "
274
- "FROM order_items GROUP BY order_id "
275
- "HAVING cnt > 1 LIMIT 5",
276
- None),
277
- ("fix",
278
- "DELETE FROM order_items WHERE rowid NOT IN "
279
- "(SELECT MIN(rowid) FROM order_items "
280
- "GROUP BY order_id)",
281
- None),
282
- ("submit", None, ""),
283
- ],
284
- "fk_integrity": [
285
- ("query",
286
- "SELECT COUNT(*) FROM invoices "
287
- "WHERE customer_id NOT IN "
288
- "(SELECT id FROM customers)",
289
- None),
290
- ("fix",
291
- "DELETE FROM invoices WHERE customer_id "
292
- "NOT IN (SELECT id FROM customers)",
293
- None),
294
- ("submit", None, ""),
295
- ],
296
- "schema_drift": [
297
- ("query",
298
- "SELECT * FROM events_v1 LIMIT 2",
299
- None),
300
- ("query",
301
- "SELECT * FROM events_v2 LIMIT 2",
302
- None),
303
- ("fix",
304
- "CREATE TABLE canonical_events AS "
305
- "SELECT id as event_id, user_id, "
306
- "event_name as event_type, "
307
- "CAST(amount_text AS REAL) as amount, "
308
- "event_time FROM events_v1 "
309
- "UNION ALL "
310
- "SELECT event_id, "
311
- "account_id as user_id, event_type, "
312
- "amount, event_ts as event_time "
313
- "FROM events_v2",
314
- None),
315
- ("submit", None, "canonical_events"),
316
- ],
317
- }
318
- return fallbacks.get(task_id, [])
319
-
320
- def run_task(self, task_id: str, seed: int = None):
321
- rewards: List[float] = []
322
- steps_taken = 0
323
- score = 0.0
324
- success = False
325
-
326
- try:
327
- reset_result = self.reset(task_id, seed)
328
- task_description = reset_result.get("task_description", "")
329
- schema_info = reset_result.get("schema_info", [])
330
- sample_rows = reset_result.get("sample_rows", {})
331
-
332
- _ = self.generate_sql(
333
- task_description, schema_info, sample_rows,
334
- None, None, 1
335
- )
336
-
337
- fallback_actions = self.get_fallback_actions(task_id)
338
- use_fallback = len(fallback_actions) > 0
339
-
340
- last_action = None
341
- last_reward = None
342
-
343
- action_source = (
344
- fallback_actions if use_fallback
345
- else []
346
- )
347
- fallback_index = 0
348
-
349
- for step in range(1, MAX_STEPS + 1):
350
- if use_fallback and fallback_index < len(fallback_actions):
351
- action_type, sql, answer = (
352
- action_source[fallback_index]
353
- )
354
- fallback_index += 1
355
- else:
356
- action_text = self.generate_sql(
357
- task_description, schema_info,
358
- sample_rows, last_action,
359
- last_reward, step
360
- )
361
- if step >= 7:
362
- action_text = "SUBMIT"
363
- if action_text.upper() == "SUBMIT":
364
- action_type = "submit"
365
- sql = None
366
- answer = None
367
- elif re.match(
368
- r"^\s*(UPDATE|INSERT|DELETE|ALTER|CREATE)\b",
369
- action_text, re.IGNORECASE
370
- ):
371
- action_type = "fix"
372
- sql = action_text
373
- answer = None
374
- else:
375
- action_type = "query"
376
- sql = action_text
377
- answer = None
378
-
379
- try:
380
- step_result = self.step(
381
- action_type, sql, None
382
- )
383
- reward_data = step_result.get("reward", {})
384
- reward = reward_data.get("step_reward", 0.0)
385
- done = step_result.get("done", False)
386
-
387
- rewards.append(reward)
388
- steps_taken = step
389
- last_action = action_type + ":" + str(
390
- sql or answer or "N/A"
391
- )
392
- last_reward = reward
393
-
394
- log_step(
395
- step=step,
396
- action=last_action,
397
- reward=reward,
398
- done=done,
399
- error=None
400
- )
401
-
402
- if done:
403
- score = reward_data.get("score", 0.0)
404
- success = (
405
- score >= SUCCESS_SCORE_THRESHOLD
406
- )
407
- break
408
-
409
- except Exception as e:
410
- log_step(
411
- step=step,
412
- action=action_type,
413
- reward=0.0,
414
- done=False,
415
- error=str(e)
416
- )
417
- break
418
-
419
- except Exception as e:
420
- print(
421
- "[DEBUG] Task failed: " + str(e),
422
- file=sys.stderr, flush=True
423
- )
424
-
425
- if not success and steps_taken == MAX_STEPS:
426
- score = max(0.001, score)
427
-
428
- return success, steps_taken, score, rewards
429
-
430
-
431
- # ============================================================================
432
- # MAIN EXECUTION
433
- # ============================================================================
434
-
435
- def main():
436
- args = _parse_args()
437
-
438
- task_name = args.task or TASK_NAME
439
- env_url = args.url or ENV_BASE_URL
440
- seed = args.seed
441
-
442
- if not API_KEY:
443
- print("Error: API_KEY or HF_TOKEN environment variable is required",
444
- file=sys.stderr, flush=True)
445
- sys.exit(1)
446
-
447
- agent = SQLQualityAgent(API_BASE_URL, MODEL_NAME, API_KEY, env_url)
448
-
449
- if not agent.health_check():
450
- print("Error: Environment health check failed",
451
- flush=True, file=sys.stderr)
452
- sys.exit(1)
453
-
454
- log_start(task=task_name, env=BENCHMARK, model=MODEL_NAME)
455
- success, steps, score, rewards = agent.run_task(task_name, seed=seed)
456
- safe_score = round(max(0.001, min(0.999, score)), 4)
457
- log_end(success=success, steps=steps,
458
- score=safe_score, rewards=rewards)
459
-
460
-
461
- if __name__ == "__main__":
462
- main()