P-Karthik-Mohan commited on
Commit
a772363
Β·
1 Parent(s): 7a189b8

added mandatory stdout format :)

Browse files
Files changed (1) hide show
  1. inference.py +113 -64
inference.py CHANGED
@@ -1,6 +1,12 @@
1
  """
2
  inference.py β€” Baseline AI agent for SQL Analyst OpenEnv
 
 
 
 
 
3
  """
 
4
  import os
5
  import sys
6
  import json
@@ -8,48 +14,68 @@ import time
8
  import requests
9
  from openai import OpenAI
10
  from dotenv import load_dotenv
 
11
  load_dotenv()
12
 
13
  ENV_BASE_URL = "https://p-karthik-mohan-sql-analyst-env.hf.space"
14
  MAX_ATTEMPTS = 5
15
  TASK_IDS = [1, 2, 3, 4, 5, 6, 7, 8]
 
16
 
17
  API_BASE_URL = os.environ.get("API_BASE_URL", "https://api.groq.com/openai/v1")
18
  MODEL_NAME = os.environ.get("MODEL_NAME", "llama-3.1-8b-instant")
19
- HF_TOKEN = os.environ.get("HF_TOKEN","")
20
 
21
  client = OpenAI(
22
  base_url=API_BASE_URL,
23
  api_key=HF_TOKEN if HF_TOKEN else "no-key-needed",
24
-
25
  )
26
 
27
- def env_reset(task_id: int) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  r = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": task_id})
29
  r.raise_for_status()
30
  return r.json()
31
 
32
- def env_step(sql: str) -> dict:
33
  r = requests.post(f"{ENV_BASE_URL}/step", json={"action": sql})
34
  r.raise_for_status()
35
  return r.json()
36
 
37
- def wait_for_server(retries: int = 10, delay: float = 2.0):
38
- print("Waiting for environment server...")
39
  for i in range(retries):
40
  try:
41
  r = requests.get(f"{ENV_BASE_URL}/health", timeout=3)
42
  if r.status_code == 200:
43
- print("Server is ready.\n")
44
  return
45
  except requests.exceptions.ConnectionError:
46
  pass
47
- print(f" Not ready yet... ({i+1}/{retries})")
48
  time.sleep(delay)
49
- print("ERROR: Server did not start in time.")
50
  sys.exit(1)
51
 
52
- def build_system_prompt() -> str:
 
 
53
  return """You are an expert SQL analyst. Your job is to write correct SQLite queries.
54
 
55
  Rules:
@@ -57,7 +83,8 @@ Rules:
57
  - Always match the exact column names specified in the task.
58
  - Always filter WHERE status = 'completed' unless told otherwise.
59
  - Use STRFTIME('%Y', order_date) for year filtering in SQLite.
60
- - RANK() OVER (...) is supported in SQLite 3.25+.
 
61
  - Return ONLY the raw SQL query β€” no explanation, no markdown, no backticks.
62
  - If a previous attempt scored less than 1.0, study the feedback and fix the query.
63
  """
@@ -91,9 +118,7 @@ Attempt number: {attempt}
91
  def ask_llm(task_description, schema, hint, attempt, previous_attempts):
92
  messages = [
93
  {"role": "system", "content": build_system_prompt()},
94
- {"role": "user", "content": build_user_prompt(
95
- task_description, schema, hint, attempt, previous_attempts
96
- )},
97
  ]
98
  response = client.chat.completions.create(
99
  model=MODEL_NAME,
@@ -104,17 +129,13 @@ def ask_llm(task_description, schema, hint, attempt, previous_attempts):
104
  sql = response.choices[0].message.content.strip()
105
  if sql.startswith("```"):
106
  lines = sql.split("\n")
107
- sql = "\n".join(
108
- line for line in lines
109
- if not line.strip().startswith("```")
110
- ).strip()
111
  return sql
112
 
113
- def solve_task(task_id: int) -> dict:
114
- print(f"\n{'='*60}")
115
- print(f"TASK {task_id}")
116
- print('='*60)
117
 
 
 
118
  reset_resp = env_reset(task_id)
119
  obs = reset_resp["observation"]
120
  task_desc = obs["task_description"]
@@ -122,31 +143,55 @@ def solve_task(task_id: int) -> dict:
122
  hint = obs["hint"]
123
  difficulty = obs["difficulty"]
124
 
125
- print(f"Difficulty : {difficulty.upper()}")
126
- print(f"Task : {task_desc}\n")
 
 
 
127
 
128
  previous_attempts = []
129
- best_reward = 0.0
130
- final_sql = ""
 
 
 
131
 
132
  for attempt in range(1, MAX_ATTEMPTS + 1):
133
- print(f" Attempt {attempt}/{MAX_ATTEMPTS} β€” asking LLM...")
134
- sql = ask_llm(task_desc, schema, hint, attempt, previous_attempts)
135
- print(f" SQL: {sql[:120]}{'...' if len(sql) > 120 else ''}")
136
-
137
- step_resp = env_step(sql)
138
- reward = step_resp["reward"]
139
- done = step_resp["done"]
140
- details = step_resp["observation"].get("reward_breakdown", {})
141
 
142
- print(f" Reward: {reward:.3f} "
143
- f"(cols={details.get('column_score',0):.2f} "
144
- f"rows={details.get('row_score',0):.2f} "
145
- f"vals={details.get('value_score',0):.2f})")
 
 
 
 
 
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  best_reward = max(best_reward, reward)
148
  final_sql = sql
149
 
 
 
 
 
150
  previous_attempts.append({
151
  "attempt": attempt,
152
  "sql": sql,
@@ -155,57 +200,61 @@ def solve_task(task_id: int) -> dict:
155
  })
156
 
157
  if done:
158
- print(f" PERFECT SCORE on attempt {attempt}!")
 
159
  break
160
  elif reward >= 0.8:
161
- print(f" Score is close ({reward:.3f}). Trying to improve...")
162
  else:
163
- print(f" Score is low ({reward:.3f}). Refining query...")
 
 
164
 
165
  return {
166
  "task_id": task_id,
 
167
  "difficulty": difficulty,
168
  "best_reward": best_reward,
169
- "attempts": len(previous_attempts),
170
  "final_sql": final_sql,
171
- "solved": best_reward >= 1.0,
172
  }
173
 
 
 
174
  def main():
175
- print("SQL Analyst OpenEnv β€” Baseline Inference Agent")
176
- print(f"Model : {MODEL_NAME}")
177
- print(f"API Base : {API_BASE_URL}")
178
- print(f"Env Server : {ENV_BASE_URL}")
179
 
180
  wait_for_server()
181
 
182
- results = []
 
 
183
  for task_id in TASK_IDS:
184
  result = solve_task(task_id)
185
  results.append(result)
 
186
 
187
- print(f"\n{'='*60}")
188
- print("FINAL RESULTS")
189
- print('='*60)
190
 
191
- total_score = 0.0
192
  for r in results:
193
  status = "SOLVED" if r["solved"] else f"best={r['best_reward']:.3f}"
194
- print(f" Task {r['task_id']} ({r['difficulty']:6s}) {status} "
195
- f"in {r['attempts']} attempt(s)")
196
- total_score += r["best_reward"]
 
197
 
198
- avg_score = total_score / len(results)
199
- print(f"\n Average reward : {avg_score:.3f} / 1.000")
200
- print(f" Tasks solved : {sum(1 for r in results if r['solved'])} / {len(results)}")
201
 
202
  with open("results.json", "w") as f:
203
- json.dump({
204
- "results": results,
205
- "avg_score": round(avg_score, 3),
206
- "tasks_solved": sum(1 for r in results if r["solved"]),
207
- }, f, indent=2)
208
- print(f"\n Results saved to results.json")
209
 
210
  if __name__ == "__main__":
211
  main()
 
1
  """
2
  inference.py β€” Baseline AI agent for SQL Analyst OpenEnv
3
+ ---------------------------------------------------------
4
+ Stdout format (mandatory):
5
+ [START] task=<task_name> env=<benchmark> model=<model_name>
6
+ [STEP] step=<n> action=<sql> reward=<0.00> done=<true|false> error=<msg|null>
7
+ [END] success=<true|false> steps=<n> score=<0.000> rewards=<r1,r2,...>
8
  """
9
+
10
  import os
11
  import sys
12
  import json
 
14
  import requests
15
  from openai import OpenAI
16
  from dotenv import load_dotenv
17
+
18
  load_dotenv()
19
 
20
  ENV_BASE_URL = "https://p-karthik-mohan-sql-analyst-env.hf.space"
21
  MAX_ATTEMPTS = 5
22
  TASK_IDS = [1, 2, 3, 4, 5, 6, 7, 8]
23
+ BENCHMARK = "sql-analyst-env"
24
 
25
  API_BASE_URL = os.environ.get("API_BASE_URL", "https://api.groq.com/openai/v1")
26
  MODEL_NAME = os.environ.get("MODEL_NAME", "llama-3.1-8b-instant")
27
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
28
 
29
  client = OpenAI(
30
  base_url=API_BASE_URL,
31
  api_key=HF_TOKEN if HF_TOKEN else "no-key-needed",
 
32
  )
33
 
34
+ # ── Mandatory stdout log functions ────────────────────────────────────────────
35
+
36
+ def log_start(task, env, model):
37
+ print(f"[START] task={task} env={env} model={model}", flush=True)
38
+
39
+ def log_step(step, action, reward, done, error=None):
40
+ action_clean = action.replace("\n", " ").strip()[:120]
41
+ error_val = error if error else "null"
42
+ done_val = str(done).lower()
43
+ print(f"[STEP] step={step} action={action_clean} reward={reward:.2f} done={done_val} error={error_val}", flush=True)
44
+
45
+ def log_end(success, steps, score, rewards):
46
+ rewards_str = ",".join(f"{r:.2f}" for r in rewards)
47
+ print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}", flush=True)
48
+
49
+ # ── Environment helpers ───────────────────────────────────────────────────────
50
+
51
+ def env_reset(task_id):
52
  r = requests.post(f"{ENV_BASE_URL}/reset", json={"task_id": task_id})
53
  r.raise_for_status()
54
  return r.json()
55
 
56
+ def env_step(sql):
57
  r = requests.post(f"{ENV_BASE_URL}/step", json={"action": sql})
58
  r.raise_for_status()
59
  return r.json()
60
 
61
+ def wait_for_server(retries=10, delay=2.0):
62
+ print("Waiting for environment server...", flush=True)
63
  for i in range(retries):
64
  try:
65
  r = requests.get(f"{ENV_BASE_URL}/health", timeout=3)
66
  if r.status_code == 200:
67
+ print("Server is ready.\n", flush=True)
68
  return
69
  except requests.exceptions.ConnectionError:
70
  pass
71
+ print(f" Not ready yet... ({i+1}/{retries})", flush=True)
72
  time.sleep(delay)
73
+ print("ERROR: Server did not start in time.", flush=True)
74
  sys.exit(1)
75
 
76
+ # ── LLM ───────────────────────────────────────────────────────────────────────
77
+
78
+ def build_system_prompt():
79
  return """You are an expert SQL analyst. Your job is to write correct SQLite queries.
80
 
81
  Rules:
 
83
  - Always match the exact column names specified in the task.
84
  - Always filter WHERE status = 'completed' unless told otherwise.
85
  - Use STRFTIME('%Y', order_date) for year filtering in SQLite.
86
+ - Use STRFTIME('%Y-%m', order_date) for year-month formatting.
87
+ - RANK() OVER (...) and LAG() OVER (...) are supported in SQLite 3.25+.
88
  - Return ONLY the raw SQL query β€” no explanation, no markdown, no backticks.
89
  - If a previous attempt scored less than 1.0, study the feedback and fix the query.
90
  """
 
118
  def ask_llm(task_description, schema, hint, attempt, previous_attempts):
119
  messages = [
120
  {"role": "system", "content": build_system_prompt()},
121
+ {"role": "user", "content": build_user_prompt(task_description, schema, hint, attempt, previous_attempts)},
 
 
122
  ]
123
  response = client.chat.completions.create(
124
  model=MODEL_NAME,
 
129
  sql = response.choices[0].message.content.strip()
130
  if sql.startswith("```"):
131
  lines = sql.split("\n")
132
+ sql = "\n".join(line for line in lines if not line.strip().startswith("```")).strip()
 
 
 
133
  return sql
134
 
135
+ # ── Task solver ───────────────────────────────────────────────────────────────
 
 
 
136
 
137
+ def solve_task(task_id):
138
+ task_name = f"sql-task-{task_id}"
139
  reset_resp = env_reset(task_id)
140
  obs = reset_resp["observation"]
141
  task_desc = obs["task_description"]
 
143
  hint = obs["hint"]
144
  difficulty = obs["difficulty"]
145
 
146
+ print(f"\n{'='*60}", flush=True)
147
+ print(f"TASK {task_id} ({difficulty.upper()})", flush=True)
148
+ print(f"Task: {task_desc}\n", flush=True)
149
+
150
+ log_start(task=task_name, env=BENCHMARK, model=MODEL_NAME)
151
 
152
  previous_attempts = []
153
+ all_rewards = []
154
+ best_reward = 0.0
155
+ final_sql = ""
156
+ steps_taken = 0
157
+ success = False
158
 
159
  for attempt in range(1, MAX_ATTEMPTS + 1):
160
+ print(f" Attempt {attempt}/{MAX_ATTEMPTS} β€” asking LLM...", flush=True)
161
+ error = None
162
+ sql = ""
 
 
 
 
 
163
 
164
+ try:
165
+ sql = ask_llm(task_desc, schema, hint, attempt, previous_attempts)
166
+ print(f" SQL: {sql[:120]}{'...' if len(sql) > 120 else ''}", flush=True)
167
+ except Exception as e:
168
+ error = str(e)
169
+ log_step(step=attempt, action="", reward=0.0, done=False, error=error)
170
+ all_rewards.append(0.0)
171
+ steps_taken = attempt
172
+ continue
173
 
174
+ try:
175
+ step_resp = env_step(sql)
176
+ reward = step_resp["reward"]
177
+ done = step_resp["done"]
178
+ details = step_resp["observation"].get("reward_breakdown", {})
179
+ except Exception as e:
180
+ error = str(e)
181
+ log_step(step=attempt, action=sql, reward=0.0, done=False, error=error)
182
+ all_rewards.append(0.0)
183
+ steps_taken = attempt
184
+ continue
185
+
186
+ all_rewards.append(reward)
187
+ steps_taken = attempt
188
  best_reward = max(best_reward, reward)
189
  final_sql = sql
190
 
191
+ print(f" Reward: {reward:.3f} (cols={details.get('column_score',0):.2f} rows={details.get('row_score',0):.2f} vals={details.get('value_score',0):.2f})", flush=True)
192
+
193
+ log_step(step=attempt, action=sql, reward=reward, done=done, error=error)
194
+
195
  previous_attempts.append({
196
  "attempt": attempt,
197
  "sql": sql,
 
200
  })
201
 
202
  if done:
203
+ print(f" PERFECT SCORE on attempt {attempt}!", flush=True)
204
+ success = True
205
  break
206
  elif reward >= 0.8:
207
+ print(f" Score is close ({reward:.3f}). Trying to improve...", flush=True)
208
  else:
209
+ print(f" Score is low ({reward:.3f}). Refining query...", flush=True)
210
+
211
+ log_end(success=success, steps=steps_taken, score=best_reward, rewards=all_rewards)
212
 
213
  return {
214
  "task_id": task_id,
215
+ "task_name": task_name,
216
  "difficulty": difficulty,
217
  "best_reward": best_reward,
218
+ "attempts": steps_taken,
219
  "final_sql": final_sql,
220
+ "solved": success,
221
  }
222
 
223
+ # ── Main ──────────────────────────────────────────────────────────────────────
224
+
225
  def main():
226
+ print("SQL Analyst OpenEnv β€” Baseline Inference Agent", flush=True)
227
+ print(f"Model : {MODEL_NAME}", flush=True)
228
+ print(f"API Base : {API_BASE_URL}", flush=True)
229
+ print(f"Env Server : {ENV_BASE_URL}", flush=True)
230
 
231
  wait_for_server()
232
 
233
+ results = []
234
+ total_score = 0.0
235
+
236
  for task_id in TASK_IDS:
237
  result = solve_task(task_id)
238
  results.append(result)
239
+ total_score += result["best_reward"]
240
 
241
+ print(f"\n{'='*60}", flush=True)
242
+ print("FINAL RESULTS", flush=True)
243
+ print('='*60, flush=True)
244
 
 
245
  for r in results:
246
  status = "SOLVED" if r["solved"] else f"best={r['best_reward']:.3f}"
247
+ print(f" Task {r['task_id']} ({r['difficulty']:6s}) {status} in {r['attempts']} attempt(s)", flush=True)
248
+
249
+ avg_score = total_score / len(results)
250
+ tasks_solved = sum(1 for r in results if r["solved"])
251
 
252
+ print(f"\n Average reward : {avg_score:.3f} / 1.000", flush=True)
253
+ print(f" Tasks solved : {tasks_solved} / {len(results)}", flush=True)
 
254
 
255
  with open("results.json", "w") as f:
256
+ json.dump({"results": results, "avg_score": round(avg_score, 3), "tasks_solved": tasks_solved}, f, indent=2)
257
+ print(f"\n Results saved to results.json", flush=True)
 
 
 
 
258
 
259
  if __name__ == "__main__":
260
  main()