Spaces:
Running
Running
Download app.py from code2lora/code2lora-gru-demo: direct link, hf CLI and curl.
- Browser
- Download file 28.6 kB
-
https://huggingface.co/spaces/code2lora/code2lora-gru-demo/resolve/main/app.py
- Command line
-
hf download hf://spaces/code2lora/code2lora-gru-demo/app.py
-
curl -L -o app.py https://huggingface.co/spaces/code2lora/code2lora-gru-demo/resolve/main/app.py
28.6 kB
| """Code2LoRA timeline demo. | |
| Pick a held-out repository, move through its commit history, generate the | |
| Code2LoRA-Evo adapter at any commit, and compare the adapted model against the | |
| plain base model on the test assertions that commit introduced, or on your own | |
| code prompt. | |
| """ | |
| from __future__ import annotations | |
| import gzip | |
| import html | |
| import json | |
| import os | |
| import threading | |
| import time | |
| from collections import OrderedDict | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from typing import Dict, List, Optional, Tuple | |
| import gradio as gr | |
| import numpy as np | |
| import torch | |
| from huggingface_hub import hf_hub_download | |
| from matplotlib.figure import Figure | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, StoppingCriteria, StoppingCriteriaList | |
| from c2l.core import ( | |
| Code2LoRAHead, | |
| clear_all_lora_weights, | |
| discover_module_types_and_dims, | |
| get_module_specs, | |
| inject_lora_weights, | |
| replace_with_lora, | |
| ) | |
| from c2l.metrics import compute_metrics, strip_fim_tokens | |
| BASE_MODEL = "Qwen/Qwen2.5-Coder-1.5B" | |
| CKPT_REPO, CKPT_FILE = "code2lora/code2lora-gru", "code2lora_gru.pt" | |
| TARGET_MODULES = ["q_proj", "k_proj", "v_proj", "o_proj", "up_proj", "gate_proj", "down_proj"] | |
| DATA = Path(os.environ.get("C2L_DATA_DIR", Path(__file__).parent / "data")) | |
| DEVICE = "cuda" if torch.cuda.is_available() else "cpu" | |
| DTYPE = torch.bfloat16 if DEVICE == "cuda" else torch.float32 | |
| LIVE_MAX_INPUT = 4096 if DEVICE == "cuda" else 1024 | |
| PAPER_URL = "https://arxiv.org/abs/2606.06492" | |
| SITE_URL = "https://code2lora.app" | |
| torch.set_grad_enabled(False) | |
| # --------------------------------------------------------------------------- | |
| # Data | |
| # --------------------------------------------------------------------------- | |
| INDEX: List[dict] = json.loads((DATA / "index.json").read_text()) | |
| INDEX_BY_SAFE = {r["safe"]: r for r in INDEX} | |
| def load_repo(safe: str) -> dict: | |
| with gzip.open(DATA / f"{safe}.json.gz", "rt", encoding="utf-8") as f: | |
| d = json.load(f) | |
| d["ctx"] = np.load(DATA / f"{safe}.ctx.npy") | |
| d["tt_commits"] = [k for k, c in enumerate(d["commits"]) if c.get("time_travel")] | |
| d["live_only"] = INDEX_BY_SAFE[safe].get("c2l_em") is None | |
| d["jump_targets"] = d["tt_commits"] or [k for k, g in enumerate(d["qnas"]) if g] | |
| return d | |
| def repo_label(r: dict) -> str: | |
| return f"{r['repo_id']} · {r['n_commits']} commits" | |
| # --------------------------------------------------------------------------- | |
| # Model | |
| # --------------------------------------------------------------------------- | |
| class NewlineStop(StoppingCriteria): | |
| def __init__(self, tok, start: int): | |
| self.tok, self.start = tok, start | |
| def __call__(self, input_ids, scores, **kwargs): | |
| done = "\n" in self.tok.decode(input_ids[0, self.start:], skip_special_tokens=True) | |
| return torch.full((input_ids.shape[0],), done, dtype=torch.bool, device=input_ids.device) | |
| class Engine: | |
| def __init__(self) -> None: | |
| t0 = time.time() | |
| self.tok = AutoTokenizer.from_pretrained(BASE_MODEL) | |
| if self.tok.pad_token is None: | |
| self.tok.pad_token = self.tok.eos_token | |
| self.bos = self.tok.bos_token_id if self.tok.bos_token_id is not None else self.tok.eos_token_id | |
| self.model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, dtype=DTYPE).to(DEVICE).eval() | |
| path = hf_hub_download(CKPT_REPO, CKPT_FILE) | |
| try: | |
| state = torch.load(path, map_location="cpu", weights_only=False, mmap=True) | |
| except Exception: | |
| state = torch.load(path, map_location="cpu", weights_only=False) | |
| hc = state["head_config"] | |
| self.specs = get_module_specs(self.model, TARGET_MODULES) | |
| type_dims = discover_module_types_and_dims(self.specs) | |
| self.rank = int(hc.get("rank", 16)) | |
| self.head = Code2LoRAHead(input_dim=int(hc["input_dim"]), type_dims=type_dims, | |
| hidden_dim=int(hc["hidden_dim"]), rank=self.rank) | |
| self.head.load_state_dict(state["head_state"]) | |
| self.head.to(DEVICE).eval() | |
| alpha = float((state.get("args") or {}).get("alpha", 32.0)) | |
| del state | |
| replace_with_lora(self.model, self.specs, rank=self.rank, alpha=alpha) | |
| self.n_adapter_params = sum(self.rank * (i + o) for i, o in type_dims.values()) | |
| self.lock = threading.Lock() | |
| self.adapters: "OrderedDict[Tuple[str, int], dict]" = OrderedDict() | |
| print(f"[engine] ready on {DEVICE} in {time.time() - t0:.0f}s", flush=True) | |
| def adapter(self, safe: str, k: int) -> Tuple[dict, float]: | |
| key = (safe, k) | |
| if key in self.adapters: | |
| self.adapters.move_to_end(key) | |
| return self.adapters[key], 0.0 | |
| ctx = torch.from_numpy(load_repo(safe)["ctx"][k:k + 1]).to(DEVICE) | |
| t0 = time.perf_counter() | |
| out = self.head(ctx) | |
| if DEVICE == "cuda": | |
| torch.cuda.synchronize() | |
| ms = (time.perf_counter() - t0) * 1000 | |
| self.adapters[key] = out | |
| while len(self.adapters) > 32: | |
| self.adapters.popitem(last=False) | |
| return out, ms | |
| def _ids(self, prompt: str, had_bos: bool, max_input: int) -> List[int]: | |
| ids = ([self.bos] if had_bos and self.bos is not None else []) + \ | |
| self.tok.encode(prompt, add_special_tokens=False) | |
| return ids[-max_input:] | |
| def generate(self, prompt: str, adapter: Optional[dict], *, had_bos: bool = True, | |
| max_input: int = LIVE_MAX_INPUT, max_new: int = 64, | |
| stop_newline: bool = True) -> Tuple[str, float, int]: | |
| ids = self._ids(prompt, had_bos, max_input) | |
| x = torch.tensor([ids], device=DEVICE) | |
| stop = StoppingCriteriaList([NewlineStop(self.tok, len(ids))]) if stop_newline else None | |
| with self.lock: | |
| t0 = time.perf_counter() | |
| if adapter is None: | |
| clear_all_lora_weights(self.model, self.specs) | |
| else: | |
| inject_lora_weights(self.model, self.specs, adapter, batch_index=0) | |
| try: | |
| out = self.model.generate( | |
| input_ids=x, attention_mask=torch.ones_like(x), max_new_tokens=int(max_new), | |
| do_sample=False, pad_token_id=self.tok.pad_token_id, | |
| eos_token_id=self.tok.eos_token_id, stopping_criteria=stop, use_cache=True) | |
| finally: | |
| clear_all_lora_weights(self.model, self.specs) | |
| sec = time.perf_counter() - t0 | |
| text = self.tok.decode(out[0, len(ids):].tolist(), skip_special_tokens=True) | |
| return text, sec, len(ids) | |
| ENGINE = Engine() | |
| # --------------------------------------------------------------------------- | |
| # Rendering helpers | |
| # --------------------------------------------------------------------------- | |
| def esc(s) -> str: | |
| return html.escape(str(s if s is not None else "")) | |
| def short_date(ts: Optional[str]) -> str: | |
| return (ts or "")[:10] | |
| def verdict(em: Optional[float]) -> str: | |
| if em is None: | |
| return "" | |
| return '<span class="ok">✓ exact match</span>' if em == 1.0 else '<span class="bad">✗ wrong</span>' | |
| def pct(x: Optional[float]) -> str: | |
| return "–" if x is None else f"{100 * x:.0f}%" | |
| def repo_header(safe: str) -> str: | |
| r = INDEX_BY_SAFE[safe] | |
| stars = f"★ {r['stars']:,} · " if r.get("stars") else "" | |
| desc = f"<div class='desc'>{esc(r.get('description'))}</div>" if r.get("description") else "" | |
| if r.get("c2l_em") is None: | |
| em = ("<div class='repo-em'>Live mode: cached GPU scores for this repository are still being computed. " | |
| "Press <b>Adapt</b> and both models answer live on this Space.</div>") | |
| else: | |
| em = (f"<div class='repo-em'>Exact match across this repo's history: base model <b>{pct(r['base_em'])}</b>" | |
| f" → Code2LoRA <b class='accent'>{pct(r['c2l_em'])}</b></div>") | |
| return f""" | |
| <div class="repo-card"> | |
| <div class="repo-title"><a href="https://github.com/{esc(r['repo_id'])}" target="_blank">{esc(r['repo_id'])}</a> | |
| <span class="pill held">held out — never seen in training</span></div> | |
| {desc} | |
| <div class="repo-stats">{stars}{r['n_commits']} commits · {short_date(r['first_date'])} → {short_date(r['last_date'])} | |
| · {r['n_qnas']:,} test assertions</div> | |
| {em} | |
| </div>""" | |
| def commit_card(safe: str, k: int, adapted_k: Optional[int]) -> str: | |
| d = load_repo(safe) | |
| c = d["commits"][k] | |
| n = len(d["commits"]) | |
| url = f"https://github.com/{d['repo_id']}/commit/{c['sha']}" | |
| tt = "<span class='pill star'>✨ adapter learns something new here</span>" if c.get("time_travel") else "" | |
| if adapted_k == k: | |
| status = "<span class='pill live'>● adapter active for this commit</span>" | |
| elif adapted_k is not None: | |
| status = f"<span class='pill warn'>adapter is from commit {adapted_k + 1}: press Adapt to update</span>" | |
| else: | |
| status = "<span class='pill idle'>no adapter yet: press Adapt</span>" | |
| return f""" | |
| <div class="commit-card"> | |
| <div class="commit-top"><b>Commit {k + 1} / {n}</b> · <a href="{url}" target="_blank"><code>{c['sha'][:10]}</code></a> | |
| · {short_date(c.get('timestamp'))} · {c.get('n_new_assertions', 0)} new test assertions {tt}</div> | |
| <div class="commit-status">{status}</div> | |
| </div>""" | |
| def timeline_plot(safe: str, k: int) -> Figure: | |
| d = load_repo(safe) | |
| cs = d["commits"] | |
| x = np.arange(1, len(cs) + 1) | |
| def rolling(key: str) -> np.ndarray: | |
| v = np.array([c[key] if c.get(key) is not None else np.nan for c in cs], dtype=float) | |
| w = max(3, len(v) // 12) | |
| out = np.full_like(v, np.nan) | |
| for i in range(len(v)): | |
| seg = v[max(0, i - w + 1): i + 1] | |
| seg = seg[~np.isnan(seg)] | |
| out[i] = seg.mean() * 100 if len(seg) else np.nan | |
| return out | |
| fig = Figure(figsize=(9, 2.4), dpi=110) | |
| ax = fig.add_subplot(111) | |
| if d["live_only"]: | |
| ax.bar(x, [len(g) for g in d["qnas"]], color="#c5cbf5", width=0.9) | |
| ax.set_ylabel("Test assertions\nadded") | |
| else: | |
| ax.plot(x, rolling("base_em"), color="#9aa3b5", lw=1.6, label="Base model") | |
| ax.plot(x, rolling("c2l_em"), color="#4f5bd5", lw=2.2, label="Code2LoRA (adapter at each commit)") | |
| for t in d["tt_commits"]: | |
| ax.axvline(t + 1, color="#22c3a6", alpha=0.18, lw=1) | |
| ax.set_ylim(0, 100) | |
| ax.set_ylabel("Exact match %\n(rolling)") | |
| ax.legend(loc="lower right", fontsize=8, frameon=False) | |
| ax.axvline(k + 1, color="#ff6b8b", lw=2) | |
| ax.set_xlim(0.5, max(2, len(cs)) + 0.5) | |
| ax.set_xlabel("Commit") | |
| for s in ("top", "right"): | |
| ax.spines[s].set_visible(False) | |
| fig.tight_layout() | |
| return fig | |
| def assertion_choices(safe: str, k: int) -> List[Tuple[str, int]]: | |
| qs = load_repo(safe)["qnas"][k] | |
| out = [] | |
| for i, q in enumerate(qs): | |
| star = "✨ " if q.get("prev_em") == 0.0 else "" | |
| out.append((f"{star}{Path(q['test_file']).name}:{q['lineno']} {q.get('test_function') or ''}", i)) | |
| return out | |
| def context_tail(prompt: str, n_lines: int = 28) -> str: | |
| return "\n".join(prompt.rstrip("\n").split("\n")[-n_lines:]) | |
| def pred_row(label: str, cls: str, pred: Optional[str], em: Optional[float], note: str = "") -> str: | |
| if pred is None: | |
| return "" | |
| shown = strip_fim_tokens(pred).split("\n")[0] | |
| return f"""<tr class="{cls}"><td class="who">{label}</td><td><code>{esc(shown) or '∅'}</code></td> | |
| <td>{verdict(em)}</td><td class="note">{note}</td></tr>""" | |
| def assertion_panel(safe: str, k: int, i: Optional[int], adapted_k: Optional[int]) -> Tuple[str, str]: | |
| qs = load_repo(safe)["qnas"][k] | |
| if i is None or i >= len(qs): | |
| return "", "<div class='hint'>Pick an assertion above.</div>" | |
| q = qs[i] | |
| truth = f"""<tr class="truth"><td class="who">Ground truth</td><td><code>{esc(q['target'])}</code></td><td></td> | |
| <td class="note">what the developer actually wrote</td></tr>""" | |
| if q.get("base_pred") is None: | |
| hint = ("Both models are answering live below…" if adapted_k == k else | |
| "Press <b>Adapt at this commit</b>: the adapter is generated and both models answer live.") | |
| return context_tail(q["prompt"]), f"<table class='preds'>{truth}</table><div class='hint'>{hint}</div>" | |
| rows = [truth, | |
| pred_row("Base model", "base", q["base_pred"], q["base_em"], "Qwen2.5-Coder-1.5B, no adapter")] | |
| if adapted_k == k: | |
| if q.get("prev_pred") is not None: | |
| rows.append(pred_row(f"Adapter @ commit {k}", "prev", q["prev_pred"], q["prev_em"], | |
| "one commit earlier")) | |
| rows.append(pred_row(f"Code2LoRA @ commit {k + 1}", "c2l", q["c2l_pred"], q["c2l_em"], | |
| "adapter generated from history up to this commit")) | |
| foot = ("<div class='hint'>Cached results were computed on a GPU with the exact adapter you just " | |
| f"generated ({q['n_prompt_tokens']:,}-token context). Press <b>Run live</b> to regenerate on this Space.</div>") | |
| else: | |
| rows.append("<tr class='c2l'><td class='who'>Code2LoRA</td><td colspan='3' class='hint'>" | |
| "Press <b>Adapt at this commit</b> to generate the adapter and reveal its answer.</td></tr>") | |
| foot = "" | |
| return context_tail(q["prompt"]), f"<table class='preds'>{''.join(rows)}</table>{foot}" | |
| # --------------------------------------------------------------------------- | |
| # Event handlers | |
| # --------------------------------------------------------------------------- | |
| def default_commit(safe: str) -> int: | |
| d = load_repo(safe) | |
| if not d["jump_targets"]: | |
| return len(d["commits"]) - 1 | |
| return d["jump_targets"][-1] if d["live_only"] else d["jump_targets"][0] | |
| def on_repo(safe: str): | |
| d = load_repo(safe) | |
| k = default_commit(safe) | |
| choices = assertion_choices(safe, k) | |
| first = choices[0][1] if choices else None | |
| ctx, preds = assertion_panel(safe, k, first, None) | |
| jump = "Jump to next commit with tests" if d["live_only"] else "Jump to next ✨ commit" | |
| return (gr.Button(value=jump), repo_header(safe), | |
| gr.Slider(minimum=1, maximum=len(d["commits"]), value=k + 1, step=1), | |
| commit_card(safe, k, None), timeline_plot(safe, k), | |
| d["commits"][k].get("diff_preview") or "", | |
| gr.Radio(choices=choices, value=first), ctx, preds, None, "", "") | |
| def on_commit(safe: str, k1: int, adapted: Optional[Tuple[str, int]]): | |
| k = int(k1) - 1 | |
| adapted_k = adapted[1] if adapted and adapted[0] == safe else None | |
| d = load_repo(safe) | |
| choices = assertion_choices(safe, k) | |
| first = next((v for lbl, v in choices if lbl.startswith("✨")), choices[0][1] if choices else None) | |
| ctx, preds = assertion_panel(safe, k, first, adapted_k) | |
| return (commit_card(safe, k, adapted_k), timeline_plot(safe, k), | |
| d["commits"][k].get("diff_preview") or "", | |
| gr.Radio(choices=choices, value=first), ctx, preds, "") | |
| def step(safe: str, k1: int, delta: int) -> int: | |
| n = len(load_repo(safe)["commits"]) | |
| return int(min(max(1, int(k1) + delta), n)) | |
| def next_star(safe: str, k1: int) -> int: | |
| tt = load_repo(safe)["jump_targets"] | |
| k = int(k1) - 1 | |
| later = [t for t in tt if t > k] | |
| return (later[0] if later else (tt[0] if tt else k)) + 1 | |
| def on_adapt(safe: str, k1: int, i: Optional[int]): | |
| k = int(k1) - 1 | |
| out, ms = ENGINE.adapter(safe, k) | |
| norms = {t: float(out["B"][t][0].norm() * out["A"][t][0].norm()) for t in sorted(out["A"])} | |
| top = max(norms.values()) or 1.0 | |
| bars = "".join(f"<div class='bar'><span>{t}</span><i style='width:{100 * v / top:.0f}%'></i></div>" | |
| for t, v in norms.items()) | |
| timing = "served from cache" if ms == 0.0 else f"generated in {ms:.0f} ms on {DEVICE.upper()}" | |
| status = f""" | |
| <div class="adapt-card"> | |
| <div class="adapt-head">⚡ Adapter for commit {k + 1} {timing}</div> | |
| <div class="adapt-sub">{ENGINE.n_adapter_params:,} LoRA parameters (rank {ENGINE.rank}) for 7 projection types, | |
| shared across all 28 layers · no training, no retrieved context</div> | |
| <div class="bars">{bars}</div> | |
| </div>""" | |
| ctx, preds = assertion_panel(safe, k, i, k) | |
| return status, (safe, k), commit_card(safe, k, k), ctx, preds | |
| def on_pick(safe: str, k1: int, i: Optional[int], adapted: Optional[Tuple[str, int]]): | |
| k = int(k1) - 1 | |
| adapted_k = adapted[1] if adapted and adapted[0] == safe else None | |
| ctx, preds = assertion_panel(safe, k, i, adapted_k) | |
| return ctx, preds, "" | |
| def live_table(rows: List[Tuple[str, str, str, Optional[dict], float]], target: Optional[str]) -> str: | |
| body = [] | |
| for label, cls, pred, m, sec in rows: | |
| shown = (m["pred_clean"] if m else strip_fim_tokens(pred).split("\n")[0]) | |
| body.append(f"""<tr class="{cls}"><td class="who">{label}</td><td><code>{esc(shown) or '∅'}</code></td> | |
| <td>{verdict(float(m['exact_match'])) if m else ''}</td><td class="note">{sec:.1f}s</td></tr>""") | |
| truth = (f"<tr class='truth'><td class='who'>Ground truth</td><td><code>{esc(target)}</code></td>" | |
| f"<td></td><td></td></tr>") if target else "" | |
| return f"<div class='live-head'>Live on this Space ({DEVICE.upper()})</div><table class='preds'>{truth}{''.join(body)}</table>" | |
| def on_live(safe: str, k1: int, i: Optional[int], adapted: Optional[Tuple[str, int]], | |
| progress=gr.Progress()): | |
| k = int(k1) - 1 | |
| if not adapted or adapted != (safe, k): | |
| return "<div class='hint warn'>Press <b>Adapt at this commit</b> first.</div>" | |
| if i is None: | |
| return "<div class='hint warn'>Pick an assertion first.</div>" | |
| q = load_repo(safe)["qnas"][k][i] | |
| adapter, _ = ENGINE.adapter(safe, k) | |
| progress(0.1, desc="Base model…") | |
| b, bs, n = ENGINE.generate(q["prompt"], None, had_bos=q.get("had_bos", True)) | |
| progress(0.55, desc="Code2LoRA adapter…") | |
| a, as_, _ = ENGINE.generate(q["prompt"], adapter, had_bos=q.get("had_bos", True)) | |
| note = "" if n >= q["n_prompt_tokens"] else ( | |
| f"<div class='hint'>CPU mode uses the last {n:,} of {q['n_prompt_tokens']:,} context tokens, " | |
| "so live answers can differ from the cached GPU run.</div>") | |
| return live_table([("Base model", "base", b, compute_metrics(b, q["target"]), bs), | |
| (f"Code2LoRA @ commit {k + 1}", "c2l", a, compute_metrics(a, q["target"]), as_)], | |
| q["target"]) + note | |
| def on_auto_live(safe: str, k1: int, i: Optional[int], adapted: Optional[Tuple[str, int]], | |
| progress=gr.Progress()): | |
| if not load_repo(safe)["live_only"] or adapted != (safe, int(k1) - 1) or i is None: | |
| return gr.update() | |
| return on_live(safe, k1, i, adapted, progress) | |
| def on_prefill(safe: str, k1: int, i: Optional[int]) -> str: | |
| qs = load_repo(safe)["qnas"][int(k1) - 1] | |
| if i is None or i >= len(qs): | |
| return "" | |
| return context_tail(qs[i]["prompt"], 40) | |
| def on_custom(safe: str, k1: int, prompt: str, max_new: int, | |
| adapted: Optional[Tuple[str, int]], progress=gr.Progress()): | |
| k = int(k1) - 1 | |
| if not prompt.strip(): | |
| return "<div class='hint warn'>Type or paste a code prefix first.</div>" | |
| if not adapted or adapted != (safe, k): | |
| return "<div class='hint warn'>Press <b>Adapt at this commit</b> first.</div>" | |
| adapter, _ = ENGINE.adapter(safe, k) | |
| progress(0.1, desc="Base model…") | |
| b, bs, _ = ENGINE.generate(prompt, None, max_new=max_new, stop_newline=False) | |
| progress(0.55, desc="Code2LoRA adapter…") | |
| a, as_, _ = ENGINE.generate(prompt, adapter, max_new=max_new, stop_newline=False) | |
| rows = [] | |
| for label, cls, text, sec in (("Base model", "base", b, bs), (f"Code2LoRA @ commit {k + 1}", "c2l", a, as_)): | |
| rows.append(f"<div class='gen {cls}'><div class='who'>{label} · {sec:.1f}s</div>" | |
| f"<pre>{esc(strip_fim_tokens(text))}</pre></div>") | |
| return f"<div class='live-head'>Live on this Space ({DEVICE.upper()})</div><div class='gens'>{''.join(rows)}</div>" | |
| # --------------------------------------------------------------------------- | |
| # UI | |
| # --------------------------------------------------------------------------- | |
| CSS = """ | |
| .gradio-container {max-width: 1180px !important} | |
| #hero h1 {font-size: 2rem; margin-bottom: 4px; letter-spacing: -0.02em} | |
| #hero p {color: #5a6275; margin: 0} | |
| .repo-card, .commit-card, .adapt-card {border: 1px solid #e3e6ef; border-radius: 12px; padding: 14px 16px; background: #fff} | |
| .repo-title {font-size: 1.15rem; font-weight: 700} | |
| .repo-title a, .commit-top a {color: #2b3487; text-decoration: none} | |
| .desc {color: #5a6275; margin: 4px 0} | |
| .repo-stats, .commit-status {color: #6b7287; font-size: 0.9rem; margin-top: 4px} | |
| .repo-em {margin-top: 8px} | |
| .accent {color: #4f5bd5} | |
| .pill {display: inline-block; font-size: 0.75rem; font-weight: 600; padding: 2px 9px; border-radius: 999px; margin-left: 6px; vertical-align: middle} | |
| .pill.held {background: #eef0ff; color: #3d4ee0} | |
| .pill.star {background: #e2f8f3; color: #0d8a75} | |
| .pill.live {background: #e2f8f3; color: #0d8a75; margin-left: 0} | |
| .pill.warn {background: #fff4e0; color: #a35f00; margin-left: 0} | |
| .pill.idle {background: #f1f2f6; color: #6b7287; margin-left: 0} | |
| .adapt-card {background: linear-gradient(135deg, #f4f5ff, #effbf8)} | |
| .adapt-head {font-weight: 700; font-size: 1.05rem} | |
| .adapt-sub {color: #5a6275; font-size: 0.88rem; margin: 2px 0 10px} | |
| .bars .bar {display: flex; align-items: center; gap: 8px; font-size: 0.78rem; color: #5a6275; margin: 3px 0} | |
| .bars .bar span {width: 76px; font-family: monospace} | |
| .bars .bar i {height: 8px; border-radius: 4px; background: linear-gradient(90deg, #6d7cff, #22d3b6)} | |
| table.preds {width: 100%; border-collapse: collapse; margin-top: 6px} | |
| table.preds td {padding: 8px 10px; border-bottom: 1px solid #eef0f5; vertical-align: top} | |
| table.preds td.who {width: 150px; font-weight: 600; white-space: nowrap} | |
| table.preds td.note {width: 130px; color: #8a91a5; font-size: 0.8rem} | |
| table.preds tr.truth {background: #f7f8fb} | |
| table.preds tr.c2l {background: #f4f5ff} | |
| table.preds tr.prev {background: #fffaf2} | |
| table.preds code {font-size: 0.92rem; white-space: pre-wrap; word-break: break-word} | |
| .ok {color: #0d8a75; font-weight: 700; white-space: nowrap} | |
| .bad {color: #d6455e; font-weight: 700; white-space: nowrap} | |
| .hint {color: #6b7287; font-size: 0.86rem; margin-top: 8px} | |
| .hint.warn {color: #a35f00} | |
| .live-head {font-weight: 700; margin: 6px 0 2px} | |
| .gens {display: grid; grid-template-columns: 1fr 1fr; gap: 12px} | |
| .gen {border: 1px solid #e3e6ef; border-radius: 10px; padding: 10px} | |
| .gen.c2l {background: #f4f5ff} | |
| .gen pre {white-space: pre-wrap; font-size: 0.85rem; margin: 6px 0 0} | |
| """ | |
| INTRO = f""" | |
| <div id="hero"> | |
| <h1>Code2LoRA: watch an adapter learn a repository, commit by commit</h1> | |
| <p>Pick a repository the model never saw during training, move through its git history, and generate the | |
| repository-specific LoRA adapter at any point in time. Then compare the adapted model with the plain base model | |
| on the test assertions that commit introduced, or on your own code. | |
| <a href="{PAPER_URL}" target="_blank">Paper</a> · <a href="{SITE_URL}" target="_blank">code2lora.app</a> · | |
| <a href="https://pypi.org/project/code2lora/" target="_blank">pip install code2lora</a></p> | |
| </div> | |
| """ | |
| HOW = """ | |
| **How it works.** For every commit, the production-code diff was embedded once with a frozen | |
| Qwen3-Embedding-0.6B and folded into a GRU state (precomputed for this demo, 8 KB per commit). | |
| **Adapt** runs the Code2LoRA hypernetwork head live on that state and emits a rank-16 LoRA for all attention and | |
| MLP projections of the frozen Qwen2.5-Coder-1.5B. The base model never sees retrieved context: everything it | |
| knows about the repository comes from the adapter. | |
| **What you see.** Each commit's test assertions are the ones the developer added in that commit, scored | |
| exactly as in the paper (first ≤ 8 per commit, exact match after normalization). ✨ marks commits where the | |
| adapter gets an assertion right that the adapter from one commit earlier got wrong. | |
| """ | |
| choices = [(repo_label(r), r["safe"]) for r in INDEX] | |
| start = max(INDEX, key=lambda r: ((r["c2l_em"] or 0) - (r["base_em"] or 0), r.get("stars") or 0))["safe"] | |
| with gr.Blocks(css=CSS, title="Code2LoRA timeline demo", theme=gr.themes.Soft(primary_hue="indigo")) as demo: | |
| adapted_state = gr.State(None) | |
| gr.HTML(INTRO) | |
| with gr.Row(): | |
| repo_dd = gr.Dropdown(choices=choices, value=start, label="Repository (held out from training)", scale=3) | |
| repo_html = gr.HTML() | |
| with gr.Group(): | |
| commit_slider = gr.Slider(1, 2, value=1, step=1, label="Commit") | |
| with gr.Row(): | |
| prev_btn = gr.Button("◀ Previous", size="sm") | |
| next_btn = gr.Button("Next ▶", size="sm") | |
| star_btn = gr.Button("Jump to next ✨ commit", size="sm") | |
| adapt_btn = gr.Button("⚡ Adapt at this commit", variant="primary", size="sm") | |
| commit_html = gr.HTML() | |
| plot = gr.Plot(show_label=False) | |
| adapt_html = gr.HTML() | |
| with gr.Tabs(): | |
| with gr.Tab("Test assertions from this commit"): | |
| pick = gr.Radio(label="Assertions added in this commit", choices=[]) | |
| with gr.Row(): | |
| with gr.Column(scale=5): | |
| ctx_code = gr.Code(label="Test file, right before the assertion's expected value", | |
| language="python", interactive=False, lines=18) | |
| with gr.Column(scale=6): | |
| preds_html = gr.HTML() | |
| live_btn = gr.Button("Run live on this Space") | |
| live_html = gr.HTML() | |
| with gr.Tab("Your own prompt"): | |
| gr.Markdown("Paste a code prefix (for example a test ending in `assert result ==`). " | |
| "Both models continue it; the adapter is the one generated for the selected commit.") | |
| prefill_btn = gr.Button("Prefill with the selected assertion's context", size="sm") | |
| custom_in = gr.Code(language="python", lines=14, label="Code prefix") | |
| max_new = gr.Slider(8, 128, value=48, step=8, label="Max new tokens") | |
| custom_btn = gr.Button("Generate with base model and Code2LoRA", variant="primary") | |
| custom_out = gr.HTML() | |
| with gr.Tab("Commit diff"): | |
| diff_code = gr.Code(label="Production-code diff (first 6,000 characters)", interactive=False, lines=24) | |
| with gr.Tab("About"): | |
| gr.Markdown(HOW + ("\n\n*Running on CPU: live generation uses the last " | |
| f"{LIVE_MAX_INPUT:,} context tokens and takes roughly 10–40 s.*" | |
| if DEVICE == "cpu" else "")) | |
| repo_outputs = [star_btn, repo_html, commit_slider, commit_html, plot, diff_code, pick, ctx_code, preds_html, | |
| adapted_state, adapt_html, live_html] | |
| commit_outputs = [commit_html, plot, diff_code, pick, ctx_code, preds_html, live_html] | |
| demo.load(on_repo, repo_dd, repo_outputs) | |
| repo_dd.change(on_repo, repo_dd, repo_outputs) | |
| commit_slider.release(on_commit, [repo_dd, commit_slider, adapted_state], commit_outputs) | |
| prev_btn.click(lambda s, k: step(s, k, -1), [repo_dd, commit_slider], commit_slider).then( | |
| on_commit, [repo_dd, commit_slider, adapted_state], commit_outputs) | |
| next_btn.click(lambda s, k: step(s, k, 1), [repo_dd, commit_slider], commit_slider).then( | |
| on_commit, [repo_dd, commit_slider, adapted_state], commit_outputs) | |
| star_btn.click(next_star, [repo_dd, commit_slider], commit_slider).then( | |
| on_commit, [repo_dd, commit_slider, adapted_state], commit_outputs) | |
| live_inputs = [repo_dd, commit_slider, pick, adapted_state] | |
| adapt_btn.click(on_adapt, [repo_dd, commit_slider, pick], | |
| [adapt_html, adapted_state, commit_html, ctx_code, preds_html], concurrency_limit=1).then( | |
| on_auto_live, live_inputs, live_html, concurrency_limit=1) | |
| pick.change(on_pick, live_inputs, [ctx_code, preds_html, live_html]).then( | |
| on_auto_live, live_inputs, live_html, concurrency_limit=1) | |
| live_btn.click(on_live, [repo_dd, commit_slider, pick, adapted_state], live_html, concurrency_limit=1) | |
| prefill_btn.click(on_prefill, [repo_dd, commit_slider, pick], custom_in) | |
| custom_btn.click(on_custom, [repo_dd, commit_slider, custom_in, max_new, adapted_state], custom_out, | |
| concurrency_limit=1) | |
| if __name__ == "__main__": | |
| demo.queue(max_size=32).launch() | |