CoolFace
Apppublic

s123hree/green-code-optimizer-a100

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
inference.py244 linesDownload Raw Back to root
1"""2inference.py — Inference endpoint for the Green-Code Optimizer agent.3 4Supports two modes:5  1. Local: loads adapter from grpo_output/final_adapter (with Unsloth)6  2. Docker/HF Spaces: downloads adapter from HuggingFace Hub (vanilla transformers+peft)7 8Environment variables:9  HF_ADAPTER_REPO  – HuggingFace repo ID for the adapter (e.g. "username/adapter-name")10  HF_TOKEN          – HuggingFace token for private repos11  ADAPTER_LOCAL_PATH – Override local path to adapter directory12  GRPO_OUTPUT_DIR   – Training output root (default: ./grpo_output); Dockerfile sets /tmp/grpo_output on HF13"""14 15import os16import json17import torch18from peft import PeftModel19from transformers import AutoModelForCausalLM, AutoTokenizer20 21# ── Configuration ────────────────────────────────────────────────────────────22BASE_MODEL = "Qwen/Qwen2.5-Coder-1.5B-Instruct"23MAX_SEQ_LENGTH = 409624 25# Adapter source: HuggingFace Hub repo OR local path (must match train_grpo.py output layout)26HF_ADAPTER_REPO = os.getenv("HF_ADAPTER_REPO", "")27_repo_root = os.path.dirname(__file__)28_default_grpo_out = os.path.abspath(os.path.join(_repo_root, "grpo_output"))29_grpo_out = os.path.abspath(os.path.expanduser(os.environ.get("GRPO_OUTPUT_DIR", _default_grpo_out)))30ADAPTER_LOCAL_PATH = os.getenv(31    "ADAPTER_LOCAL_PATH",32    os.path.join(_grpo_out, "final_adapter"),33)34HF_TOKEN = os.getenv("HF_TOKEN", None)35 36SYSTEM_PROMPT = (37    "You are an expert Python refactoring agent focused on ENERGY EFFICIENCY.\n"38    "Your goal: minimise CPU cycles and peak memory while preserving program logic.\n"39    "Specifically prefer:\n"40    "  • List/dict/set comprehensions over append-loops\n"41    "  • Vectorised / built-in operations (sum, map) over manual accumulation\n"42    "  • Hoisting loop-invariant work outside the loop\n"43    "  • Eliminating dead code and redundant computation\n"44    "  • Flattening unnecessarily nested loops\n"45    "Do NOT alter test files or break any existing assertions.\n"46    "Return edited files using EXACTLY this XML format:\n"47    '<file name="filename.py">\n... complete new code ...\n</file>\n'48    "Provide the full updated file content (do not omit any code)."49)50 51 52def _load_model():53    """Load the base model + LoRA adapter. Works with or without Unsloth."""54    device = "cuda" if torch.cuda.is_available() else "cpu"55    use_4bit = torch.cuda.is_available()56 57    # ── Try Unsloth first (faster, used during training) ─────────────────58    try:59        from unsloth import FastLanguageModel60 61        adapter_path = ADAPTER_LOCAL_PATH62        if HF_ADAPTER_REPO:63            adapter_path = HF_ADAPTER_REPO64 65        print(f"Loading via Unsloth: {adapter_path}")66        model, tokenizer = FastLanguageModel.from_pretrained(67            model_name=adapter_path,68            max_seq_length=MAX_SEQ_LENGTH,69            load_in_4bit=use_4bit,70        )71        FastLanguageModel.for_inference(model)72        if tokenizer.pad_token is None:73            tokenizer.pad_token = tokenizer.eos_token74        print("✅ Model loaded via Unsloth")75        return model, tokenizer76 77    except Exception as e:78        # Catch ImportError, runtime errors, OOM, etc. and fall back.79        print(f"Unsloth unavailable ({type(e).__name__}: {e}), "80              f"falling back to transformers+peft...")81 82    # ── Fallback: vanilla transformers + peft (Docker/CPU) ───────────────83    print(f"Loading base model: {BASE_MODEL}")84    tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, token=HF_TOKEN)85    if tokenizer.pad_token is None:86        tokenizer.pad_token = tokenizer.eos_token87 88    model = AutoModelForCausalLM.from_pretrained(89        BASE_MODEL,90        torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,91        device_map="auto" if device == "cuda" else None,92        token=HF_TOKEN,93    )94 95    # Load adapter from HF Hub or local path96    adapter_source = HF_ADAPTER_REPO if HF_ADAPTER_REPO else ADAPTER_LOCAL_PATH97    if os.path.isdir(adapter_source) or HF_ADAPTER_REPO:98        print(f"Loading LoRA adapter: {adapter_source}")99        model = PeftModel.from_pretrained(model, adapter_source, token=HF_TOKEN)100        model = model.merge_and_unload()101        print("✅ LoRA adapter merged")102    else:103        print(f"⚠️  No adapter found at {adapter_source}, using base model")104 105    model.eval()106    print("✅ Model loaded via transformers+peft")107    return model, tokenizer108 109 110# ── Singleton model loading ──────────────────────────────────────────────────111_model = None112_tokenizer = None113 114 115def get_model():116    """Lazy-load the model on first call."""117    global _model, _tokenizer118    if _model is None:119        _model, _tokenizer = _load_model()120    return _model, _tokenizer121 122 123def run_inference(observation: dict) -> dict:124    """125    Given an environment observation, generate the next agent action.126    127    For the OpenEnv step-by-step interface (server.py):128      observation = {files, violation_report, steps_remaining, ...}129    130    Returns a tool-call dict: {"tool": "...", "args": {...}}131    """132    model, tokenizer = get_model()133 134    steps_remaining = observation.get("steps_remaining", 0)135    report = observation.get("violation_report", {})136    files = observation.get("files", {})137 138    # Build context from files139    code_context = ""140    for fname, content in files.items():141        block = f"\n--- {fname} ---\n```python\n{content}\n```\n"142        if len(code_context) + len(block) > 8000:143            break144        code_context += block145 146    user_prompt = (147        f"Steps remaining: {steps_remaining}\n"148        f"Violation report: {json.dumps(report, indent=2)}\n"149        f"\nHere is the codebase to refactor:\n{code_context}\n"150        f"\nPlease refactor and return the updated files."151    )152 153    messages = [154        {"role": "system", "content": SYSTEM_PROMPT},155        {"role": "user", "content": user_prompt},156    ]157 158    text_prompt = tokenizer.apply_chat_template(159        messages, tokenize=False, add_generation_prompt=True160    )161    inputs = tokenizer(text_prompt, return_tensors="pt").to(model.device)162 163    with torch.no_grad():164        outputs = model.generate(165            **inputs,166            max_new_tokens=512,167            do_sample=True,168            temperature=0.3,169            pad_token_id=tokenizer.pad_token_id,170        )171 172    generated = tokenizer.decode(173        outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True174    )175 176    # Parse the generated XML into edit actions177    import re178    file_pattern = r'<file name="(.*?)">(.*?)</file>'179    matches = re.findall(file_pattern, generated, flags=re.DOTALL)180 181    if matches:182        # Return the first edit as an action183        fname, content = matches[0]184        return {185            "tool": "edit_file",186            "args": {"filename": fname.strip(), "content": content.strip()},187        }188 189    # Fallback: check compliance190    return {"tool": "check_compliance", "args": {}}191 192 193# ── CLI entrypoint for quick testing ─────────────────────────────────────────194if __name__ == "__main__":195    print("=" * 60)196    print("🔍 Loading Model...")197    print("=" * 60)198 199    model, tokenizer = get_model()200 201    messy_code = '''202def calculate(x, y):203    import os204    a = x + y205    if a > 10:206        return a207    else:208        return 0209'''210 211    user_prompt = (212        f"Here is the codebase to refactor:\n\n"213        f"--- messy.py ---\n```python\n{messy_code}\n```\n\n"214        f"Please refactor and return the updated files."215    )216    messages = [217        {"role": "system", "content": SYSTEM_PROMPT},218        {"role": "user", "content": user_prompt},219    ]220    text_prompt = tokenizer.apply_chat_template(221        messages, tokenize=False, add_generation_prompt=True222    )223    inputs = tokenizer(text_prompt, return_tensors="pt").to(model.device)224 225    print("\n" + "=" * 60)226    print("🤖 Generating Refactored Code...")227    print("=" * 60)228 229    with torch.no_grad():230        outputs = model.generate(231            **inputs,232            max_new_tokens=512,233            do_sample=True,234            temperature=0.3,235            pad_token_id=tokenizer.pad_token_id,236        )237 238    generated_text = tokenizer.decode(239        outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True240    )241    print("--- AI OUTPUT ---")242    print(generated_text)243    print("-----------------")244