File size: 4,311 Bytes
e601c29
 
 
 
39419fc
e601c29
39419fc
 
e601c29
39419fc
b3fe1a4
e601c29
 
 
 
39419fc
e601c29
 
 
 
 
 
 
 
 
 
 
 
 
 
39419fc
e601c29
 
 
 
39419fc
e601c29
 
39419fc
 
 
 
 
 
 
 
 
e601c29
 
39419fc
e601c29
 
 
39419fc
 
 
e601c29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39419fc
 
e601c29
 
39419fc
e601c29
 
 
 
 
39419fc
e601c29
 
 
39419fc
e601c29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import asyncio
from typing import List
from openai import OpenAI
from server.sql_env_environment import SqlEnvironment
from models import SqlAction
from dotenv import load_dotenv
load_dotenv()

API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
MODEL_NAME   = os.getenv("MODEL_NAME", "meta-llama/Llama-3.1-8B-Instruct")
HF_TOKEN     = os.getenv("HF_TOKEN")
MAX_STEPS    = 10
SUCCESS_SCORE_THRESHOLD = 0.6


if not HF_TOKEN:
    raise ValueError("HF_TOKEN is required")

def log_start(model):
    print(f"[START] env=sql_env model={model}", flush=True)

def log_step(step, action, reward, done, error):
    err = error if error else "null"
    print(f"[STEP] step={step} action={repr(action)} reward={reward:.2f} done={str(done).lower()} error={err}", flush=True)

def log_end(success, steps, score, rewards):
    r_str = ",".join(f"{r:.2f}" for r in rewards)
    print(f"[END] success={str(success).lower()} steps={steps} score={score:.2f} rewards={r_str}", flush=True)

def get_model_query(client, task_description: str, last_output: str, history: List[str]) -> SqlAction:
    history_str = "\n".join(history[-4:]) if history else "None"
    prompt = f"""You are an expert SQL developer working with a SQLite database.



The database has these tables:

- employees (id, name, department, salary)  -- department is a foreign key (integer ID)

- departments (id, name, budget)



IMPORTANT RULES:

- employees.department is an INTEGER foreign key referencing departments.id

- Always use table aliases to avoid ambiguous column names (e.g. e.name, d.name)

- To get department name, JOIN departments and use d.name AS department_name



YOUR TASK:

{task_description}



LAST QUERY RESULT:

{last_output}



RECENT HISTORY (learn from these, don't repeat low-scoring queries):

{history_str}



Instructions:

- Write a single valid SQLite SQL query that completes the task above

- Reward is between 0.0 and 1.0 — aim for 1.0

- If last result was an ERROR, fix the query — do NOT fall back to SELECT *

- Reply with ONLY one line:

QUERY: <your sql here>

"""
    try:
        completion = client.chat.completions.create(
            model=MODEL_NAME,
            messages=[{"role": "user", "content": prompt}],
            max_tokens=200
        )
        text = completion.choices[0].message.content.strip()
        lines = {l.split(":")[0].strip(): ":".join(l.split(":")[1:]).strip()
                 for l in text.splitlines() if ":" in l}
        query = lines.get("QUERY", "SELECT * FROM employees")
        return SqlAction(query=query)
    except Exception as e:
        print(f"[DEBUG] LLM error: {e}", flush=True)
        return SqlAction(query="SELECT * FROM employees")

async def main():
    client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
    env = SqlEnvironment()

    rewards: List[float] = []
    history: List[str] = []
    steps_taken = 0
    score = 0.0
    success = False

    log_start(model=MODEL_NAME)

    try:
        obs = env.reset()
        task_description = obs.echoed_message  # ← save task
        last_output = obs.echoed_message        # ← no metadata yet on reset

        for step in range(1, MAX_STEPS + 1):
            action = get_model_query(client, task_description, last_output, history)  # ← pass task_description too
            obs = env.step(action)

            metadata = obs.metadata or {}
            reward = metadata.get("reward", 0.0)
            error = metadata.get("error", None)
            done = reward >= 0.7

            rewards.append(reward)
            steps_taken = step
            last_output = f"ERROR: {error}" if error else obs.echoed_message
            history.append(f"Step {step}: {action.query[:80]} → reward {reward:.2f}")

            log_step(step=step, action=action.query[:100],
                     reward=reward, done=done, error=error)

            if done:
                break

        score = sum(rewards) / max(len(rewards), 1)
        score = min(max(score, 0.0), 1.0)
        success = score >= SUCCESS_SCORE_THRESHOLD

    finally:
        log_end(success=success, steps=steps_taken, score=score, rewards=rewards)

if __name__ == "__main__":
    asyncio.run(main())