Spaces:
Sleeping
Sleeping
File size: 6,415 Bytes
9b9c562 9496a0e e0a6d43 9496a0e ebcd8b6 9b9c562 e0a6d43 6d2ec98 ebcd8b6 e0a6d43 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 755655b 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 755655b 6d2ec98 9496a0e e0a6d43 6d2ec98 e0a6d43 6d2ec98 e0a6d43 6d2ec98 9b9c562 ebcd8b6 6d2ec98 ebcd8b6 6d2ec98 ebcd8b6 6d2ec98 c11d056 6d2ec98 e0a6d43 ebcd8b6 6d2ec98 e0a6d43 755655b 6d2ec98 e0a6d43 6d2ec98 755655b e0a6d43 ebcd8b6 e0a6d43 6d2ec98 ebcd8b6 6d2ec98 ebcd8b6 e0a6d43 6d2ec98 ebcd8b6 e0a6d43 ebcd8b6 e0a6d43 755655b ebcd8b6 c11d056 755655b e0a6d43 ebcd8b6 e0a6d43 ebcd8b6 9b9c562 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | import os
import re
import json
import requests
from openai import OpenAI
import sys
API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY", "")
MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct")
ENV_URL = os.getenv("ENV_URL", "http://localhost:7860")
ENV_NAME = "sql_debugger"
MAX_STEPS = 6
TEMPERATURE = 0.1
MAX_TOKENS = 500
client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
SYSTEM_PROMPT = """You are an expert SQL developer working with a SQLite database.
Your job depends on the task type:
- write_query: Write a correct SQL SELECT query from scratch
- fix_query: You are shown broken SQL. Fix it so it runs correctly
- optimize_query: You are shown slow SQL. Rewrite it using CTEs (WITH clause)
or window functions (ROW_NUMBER, RANK) instead of correlated subqueries
RESPONSE FORMAT — always respond with ONLY this JSON:
{"action_type": "write_query", "sql": "SELECT ...", "explanation": "brief reason"}
RULES:
- Only SELECT statements are allowed
- No DROP, DELETE, INSERT, UPDATE, CREATE, ALTER, TRUNCATE
- For fix_query: keep the same intent, just fix the bugs
- For optimize_query: use WITH clause or window functions
- Respond ONLY with the JSON object, no other text"""
def build_prompt(obs: dict, step: int, history: list) -> str:
parts = []
parts.append(f"=== TASK ===\n{obs.get('task_description', '')}")
schema = obs.get('schema_info', '')
if schema:
parts.append(f"=== DATABASE SCHEMA ===\n{chr(10).join(schema.split(chr(10))[:30])}")
data = obs.get('sample_data', '')
if data:
parts.append(f"=== SAMPLE DATA ===\n{chr(10).join(data.split(chr(10))[:20])}")
hints = obs.get('expected_description', '')
if hints:
parts.append(f"=== HINTS ===\n{hints}")
last_sql = obs.get('last_sql')
if last_sql:
parts.append(f"=== YOUR PREVIOUS SQL ===\n{last_sql}")
last_result = obs.get('last_result')
if last_result:
parts.append(f"=== RESULT OF PREVIOUS SQL ===\n{last_result}")
last_error = obs.get('last_error')
if last_error:
parts.append(f"=== ERROR ===\n{last_error}")
feedback = obs.get('feedback', '')
if feedback and step > 1:
parts.append(f"=== GRADER FEEDBACK ===\n{feedback}")
if history:
hist_str = "\n".join(history[-3:])
parts.append(f"=== RECENT HISTORY ===\n{hist_str}")
parts.append("Respond with ONLY a JSON action object.")
return "\n\n".join(parts)
def parse_action(response_text: str, task_type: str) -> dict:
text = response_text.strip()
try:
return json.loads(text)
except json.JSONDecodeError:
pass
match = re.search(r'\{[^{}]+\}', text)
if match:
try:
return json.loads(match.group(0))
except json.JSONDecodeError:
pass
sql_match = re.search(r'(?:SELECT|WITH).+', text, re.DOTALL | re.IGNORECASE)
if sql_match:
sql = sql_match.group(0).strip()
if sql.endswith("```"): sql = sql[:-3].strip()
return {"action_type": task_type, "sql": sql, "explanation": "Regex parsed"}
return {"action_type": task_type, "sql": "SELECT 1", "explanation": "parse failed"}
def run_episode(task_id: str):
try:
res = requests.post(f"{ENV_URL}/reset", json={"task_id": task_id, "difficulty": None})
res.raise_for_status()
obs_obj = res.json()
obs = obs_obj["observation"]
except Exception as e:
# Failsafe reset
obs = {"task_type": "write_query"}
print(f"[START] task={task_id} env={ENV_NAME} model={MODEL_NAME}", flush=True)
best_reward = 0.05
history = []
step = 0
done = False
for step in range(1, MAX_STEPS + 1):
if obs.get("done", False):
break
prompt = build_prompt(obs, step, history)
try:
completion = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt}
],
temperature=TEMPERATURE,
max_tokens=MAX_TOKENS
)
response_text = completion.choices[0].message.content or ""
except Exception as e:
response_text = ""
action = parse_action(response_text, obs.get("task_type", "write_query"))
# Safe JSON stringification for the action output in the log
action_str = json.dumps(action)
try:
step_res = requests.post(f"{ENV_URL}/step", json=action)
step_res.raise_for_status()
step_data = step_res.json()
except Exception as e:
# Fallback if step errors
step_data = {"reward": 0.05, "observation": {"done": True}, "done": True, "error": str(e)}
reward = step_data.get("reward", 0.05)
done = step_data.get("done", False)
err = step_data.get("error")
# Format the numbers cleanly
reward_formatted = f"{reward:.2f}"
best_reward = max(best_reward, reward)
obs = step_data.get("observation", {})
# Convert flags to lowercase 'true' or 'false' for validator tracking
done_str = "true" if done else "false"
err_str = "null" if not err else f'"{str(err)}"'
print(f"[STEP] step={step} action={action_str} reward={reward_formatted} done={done_str} error={err_str}", flush=True)
history.append(f"Step {step}: reward={reward_formatted}")
if done:
break
# Track final outcome: successes usually represent the max reward crossing some threshold
success_str = "true" if best_reward > 0.8 else "false"
best_reward_formatted = f"{best_reward:.2f}"
print(f"[END] task={task_id} success={success_str} steps={step} score={best_reward_formatted}", flush=True)
def main():
# Only process the tasks quietly!
task_ids = ["easy_01", "easy_02", "medium_01", "medium_02", "hard_01", "hard_02"]
for tid in task_ids:
run_episode(tid)
if __name__ == "__main__":
main()
|