Spaces:
Sleeping
Sleeping
File size: 8,843 Bytes
508bc3b | 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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | #!/usr/bin/env python3
"""
Baseline inference script for the Data Cleaning Environment.
Uses the OpenAI API client to run an LLM agent against the environment
for all 3 tasks (easy, medium, hard) and prints reproducible scores.
Usage:
# Set your API key
export OPENAI_API_KEY=sk-...
# Run against local server (default)
python baseline.py
# Run against a deployed HF Space
python baseline.py --base-url https://your-username-data-cleaning-env.hf.space
Requirements:
pip install openai requests
"""
import argparse
import json
import os
import sys
import requests
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
from huggingface_hub import InferenceClient
try:
from openai import OpenAI
except ImportError:
print("openai package not found. Install with: pip install openai")
sys.exit(1)
try:
from openai import OpenAI
except ImportError:
print("openai package not found. Install with: pip install openai")
sys.exit(1)
# ---------------------------------------------------------------------------
# Deterministic rule-based agent (no LLM needed for baseline)
# ---------------------------------------------------------------------------
RULE_POLICIES = {
"easy": ["impute_mean", "impute_mode", "drop_missing_rows"],
"medium": ["remove_duplicates", "fix_type_errors", "drop_missing_rows"],
"hard": [
"fill_quantity_mean",
"drop_missing_rows",
"remove_duplicates",
"fix_type_errors",
"remove_outliers",
"normalize_text",
],
}
def run_rule_baseline(base_url: str) -> dict[str, float]:
"""Run deterministic rule-based baseline β no LLM required."""
scores = {}
for task in ["easy", "medium", "hard"]:
# Reset
resp = requests.post(f"{base_url}/reset", json={"task": task}, timeout=10)
resp.raise_for_status()
# Apply each operation in the policy
for op in RULE_POLICIES[task]:
resp = requests.post(
f"{base_url}/step",
json={"action": {"operation": op}},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
if data.get("done"):
break
# Grade
resp = requests.post(f"{base_url}/grader", timeout=10)
resp.raise_for_status()
result = resp.json()
scores[task] = result["score"]
return scores
# ---------------------------------------------------------------------------
# LLM agent (uses OpenAI API)
# ---------------------------------------------------------------------------
SYSTEM_PROMPT = """You are a data cleaning agent. You will be shown a dirty dataset
as a text table and must choose ONE cleaning operation to apply per turn.
Available operations:
impute_mean β Fill numeric missing values with the column mean
impute_mode β Fill categorical missing values with the most common value
drop_missing_rows β Drop all rows that have any missing value
remove_duplicates β Remove exact duplicate rows
fix_type_errors β Coerce non-numeric values in numeric columns to float
remove_outliers β Drop rows where price <= 0 or price >= 500
normalize_text β Strip whitespace and title-case all string columns
fill_quantity_mean β Fill missing quantity values with the column mean
Respond ONLY with a JSON object like:
{"operation": "remove_duplicates"}
or with an optional column:
{"operation": "impute_mean", "column": "age"}
No explanation. JSON only."""
def run_llm_baseline(base_url: str, api_key: str, max_steps: int = 10) -> dict[str, float]:
"""Run an LLM agent (GPT-4o-mini) against the environment."""
client = OpenAI(api_key=api_key)
# client = InferenceClient(api_key=api_key)
scores = {}
for task in ["easy", "medium", "hard"]:
print(f"\n [LLM] Task: {task}")
resp = requests.post(f"{base_url}/reset", json={"task": task}, timeout=10)
resp.raise_for_status()
obs = resp.json()
for step in range(max_steps):
current_text = obs["observation"].get("current_text", "")
metadata = obs["observation"].get("metadata", {})
quality = metadata.get("quality_score", "?")
valid_ops = metadata.get("valid_operations", [])
user_msg = (
f"Current dataset (quality score: {quality}):\n"
f"{current_text}\n\n"
f"Valid operations: {valid_ops}\n"
f"Choose ONE operation to improve data quality."
)
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_msg},
],
temperature=0,
max_tokens=64,
)
raw = completion.choices[0].message.content.strip()
try:
action = json.loads(raw)
except json.JSONDecodeError:
# Extract JSON from response if wrapped in markdown
import re
match = re.search(r"\{.*\}", raw, re.DOTALL)
action = json.loads(match.group()) if match else {"operation": "drop_missing_rows"}
print(f" step {step+1}: {action}")
resp = requests.post(
f"{base_url}/step",
json={"action": action},
timeout=10,
)
resp.raise_for_status()
obs = resp.json()
if obs.get("done"):
print(f" Episode done at step {step+1}")
break
# Grade
resp = requests.post(f"{base_url}/grader", timeout=10)
resp.raise_for_status()
result = resp.json()
scores[task] = result["score"]
print(f" [LLM] {task} score: {scores[task]}")
return scores
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="Data Cleaning Env baseline script")
parser.add_argument(
"--base-url",
default="http://localhost:8000",
help="Base URL of the running environment server",
)
parser.add_argument(
"--mode",
choices=["rule", "llm", "both"],
default="rule",
help="Baseline mode: 'rule' (no API key needed), 'llm' (needs OPENAI_API_KEY), 'both'",
)
args = parser.parse_args()
base_url = args.base_url.rstrip("/")
# Health check
try:
r = requests.get(f"{base_url}/health", timeout=5)
r.raise_for_status()
print(f"β Server healthy at {base_url}")
except Exception as e:
print(f"β Cannot reach server at {base_url}: {e}")
sys.exit(1)
# ββ Rule-based baseline (always runs) ββββββββββββββββββββββββββββββββββ
if args.mode in ("rule", "both"):
print("\n=== Rule-based Baseline ===")
try:
scores = run_rule_baseline(base_url)
print("\nScores:")
for task, score in scores.items():
bar = "β" * int(score * 20)
print(f" {task:<8} {score:.4f} {bar}")
print(f"\n Mean: {sum(scores.values()) / len(scores):.4f}")
except Exception as e:
print(f"Rule baseline failed: {e}")
# ββ LLM baseline βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if args.mode in ("llm", "both"):
api_key = os.getenv("OPENAI_API_KEY") or os.getenv("HF_TOKEN")
if not api_key:
print("\nSkipping LLM baseline: OPENAI_API_KEY not set.")
else:
print("\n=== LLM Baseline (gpt-4o-mini) ===")
try:
scores = run_llm_baseline(base_url, api_key)
print("\nScores:")
for task, score in scores.items():
bar = "β" * int(score * 20)
print(f" {task:<8} {score:.4f} {bar}")
print(f"\n Mean: {sum(scores.values()) / len(scores):.4f}")
except Exception as e:
print(f"LLM baseline failed: {e}")
if __name__ == "__main__":
main() |