Spaces:
Sleeping
Sleeping
| import os | |
| import random | |
| from typing import List, Optional | |
| from openai import OpenAI | |
| from env.environments import DebugMLEnv | |
| from env.models import Action | |
| API_BASE_URL = os.environ.get("API_BASE_URL", "https://router.huggingface.co/v1") | |
| MODEL_NAME = os.environ.get("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct") | |
| MAX_STEPS = 15 | |
| SUCCESS_THRESHOLD = 0.8 | |
| def get_api_key() -> str: | |
| api_key = os.environ.get("HF_TOKEN") or os.environ.get("OPENAI_API_KEY") | |
| if not api_key: | |
| raise RuntimeError( | |
| "Missing API key. Set HF_TOKEN for the Hugging Face router or OPENAI_API_KEY before running this script." | |
| ) | |
| return api_key | |
| client = OpenAI(base_url=API_BASE_URL, api_key=get_api_key()) | |
| # --------------------------------------------------------------------------- | |
| # Loggers | |
| # --------------------------------------------------------------------------- | |
| def log_start(task: str, env: str, model: str): | |
| print(f"[START] task={task} env={env} model={model}", flush=True) | |
| def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]): | |
| error_val = error if error else "null" | |
| print( | |
| f"[STEP] step={step} action={action} reward={reward:.2f} done={str(done).lower()} error={error_val}", | |
| flush=True, | |
| ) | |
| def clamp_score(score: float) -> float: | |
| return max(0.01, min(0.99, score)) | |
| def log_end(success: bool, steps: int, rewards: List[float], score: float): | |
| rewards_str = ",".join(f"{r:.2f}" for r in rewards) | |
| print( | |
| f"[END] success={str(success).lower()} steps={steps} rewards={rewards_str} score={clamp_score(score):.2f}", | |
| flush=True, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Agent | |
| # --------------------------------------------------------------------------- | |
| def build_prompt(obs, last_action: Optional[str], last_reward: Optional[float]) -> str: | |
| current_score = round(0.5 * obs.accuracy + 0.25 * obs.precision + 0.25 * obs.recall, 2) | |
| target_score = 0.85 | |
| prev_info = "" | |
| if last_action and last_reward is not None: | |
| prev_info = f""" | |
| Previous info: | |
| last action: {last_action} | |
| last reward: {last_reward}""" | |
| return f""" | |
| You are an agent optimizing a machine learning pipeline. Your goal is to maximize accuracy by selecting the best action at each step. | |
| Current state: | |
| - Accuracy: {obs.accuracy} | |
| - Scaling applied: {obs.scaling} | |
| - Feature count: {obs.feature_count} | |
| - Model type: {obs.model_type} | |
| {prev_info} | |
| Score: | |
| - current_score: {current_score} | |
| - target_score: {target_score} | |
| - gap: {round(target_score - current_score, 2)} | |
| Available actions: | |
| - add_scaling → enables feature scaling (only useful if scaling is False) | |
| - fix_split → adjusts train/test split | |
| - add_feature → adds a feature (only useful if feature_count < 6) | |
| - remove_feature → removes a feature (only useful if feature_count > 1) | |
| Constraints you must respect: | |
| - If scaling is True, do NOT choose add_scaling | |
| - If feature_count >= 6, do NOT choose add_feature | |
| - If feature_count <= 1, do NOT choose remove_feature | |
| - Do not repeat an action that returned a negative reward | |
| Reason step by step: | |
| 1. Look at the current state | |
| 2. Eliminate any actions that violate the constraints above | |
| 3. From the remaining actions, pick the one most likely to improve accuracy | |
| 4. Focus on actions that reduce the score gap. | |
| 5. If score is already high (>0.85), avoid unnecessary changes. | |
| 6. If the pipeline is already good, avoid unnecessary changes. | |
| 7. Respond with ONLY the action name, nothing else. | |
| Action: | |
| """ | |
| def clean_action(output: str) -> str: | |
| text = output.strip().lower().replace(" ", "_") | |
| valid = ["add_scaling", "fix_split", "add_feature", "remove_feature"] | |
| for action in valid: | |
| if action in text: | |
| return action | |
| return random.choice(valid) | |
| def get_action(obs, last_action: Optional[str], last_reward: Optional[float]) -> str: | |
| prompt = build_prompt(obs, last_action, last_reward) | |
| response = client.chat.completions.create( | |
| model=MODEL_NAME, | |
| messages=[{"role": "system", "content": prompt}], | |
| temperature=0.2, | |
| max_tokens=10, | |
| ) | |
| raw = response.choices[0].message.content or "" | |
| return clean_action(raw) | |
| # --------------------------------------------------------------------------- | |
| # Task runner | |
| # --------------------------------------------------------------------------- | |
| def run_task(task_name: str) -> float: | |
| env = DebugMLEnv() | |
| rewards: List[float] = [] | |
| info = {} | |
| last_action = None | |
| last_reward = None | |
| log_start(task=task_name, env="debugml", model=MODEL_NAME) | |
| try: | |
| obs = env.reset(task_name) | |
| for step_num in range(1, MAX_STEPS + 1): | |
| action = get_action(obs, last_action, last_reward) | |
| error = None | |
| try: | |
| obs, reward, done, info = env.step(Action(type=action)) | |
| except Exception as e: | |
| error = str(e) | |
| reward = 0.0 | |
| done = True | |
| rewards.append(reward) | |
| last_action = action | |
| last_reward = reward | |
| log_step(step=step_num, action=action, reward=reward, done=done, error=error) | |
| if done: | |
| break | |
| except Exception as e: | |
| print(f"[CRASH] {e}", flush=True) | |
| log_end(success=False, steps=len(rewards), rewards=rewards, score=0.01) | |
| return 0.01 | |
| score = clamp_score(info["task_score"] if "task_score" in info else 0.01) | |
| success = score >= SUCCESS_THRESHOLD | |
| log_end(success=success, steps=len(rewards), rewards=rewards, score=score) | |
| return score | |
| # --------------------------------------------------------------------------- | |
| # Main | |
| # --------------------------------------------------------------------------- | |
| def main(): | |
| tasks = ["fix_basics", "optimize_features", "full_pipeline_optimization", "stability_optimization"] | |
| for task_name in tasks: | |
| run_task(task_name) | |
| print("", flush=True) | |
| if __name__ == "__main__": | |
| main() | |