Feature Extraction
Transformers
Safetensors
English
multilingual
laya_browser
laya
custom_code
system-1
browser-agent
web-navigation
decision-model
mmbert
mind2web
tilelang
Instructions to use cklxx/laya-browser with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use cklxx/laya-browser with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="cklxx/laya-browser", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("cklxx/laya-browser", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download code/finetune/webgym/eval_gym.py from cklxx/laya-browser: direct link, hf CLI and curl.
- Browser
- Download file 6.6 kB
-
https://huggingface.co/cklxx/laya-browser/resolve/main/code/finetune/webgym/eval_gym.py
- Command line
-
hf download hf://cklxx/laya-browser/code/finetune/webgym/eval_gym.py
-
curl -L -o eval_gym.py https://huggingface.co/cklxx/laya-browser/resolve/main/code/finetune/webgym/eval_gym.py
6.6 kB
| """Run the real agent (jev loop + the laya systemone server) on HELD-OUT webgym seeds (seed % 10 == 0, never generated | |
| for training) and judge success from the page's own state. | |
| python finetune/webgym/eval_gym.py [n_per_kind=10] [kinds=flight,hotel,shop,car,restaurant,signup,filter] [max_steps=40] | |
| env: JEV_PLANNER=1 to plan sub-goals first; JEV_MACROS=1 date/count widgets as one action; S1_URL=http://...; GYM_OUT=results.json | |
| Success: flight/hotel/car = on the results page with every target value (+ filter/sort/opened car), shop/filter = the | |
| listing/product state the task asks for, restaurant/signup = the confirmation page carries every target value. The agent's DONE is not trusted; a run that ends without reaching the state fails. | |
| """ | |
| import json, os, sys, time, urllib.parse | |
| sys.path.insert(0, "/home/ckl/projects/S/jev-ultrafast") | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "apps")) | |
| import browser_suite # noqa: F401 (sets the jev env: CDP, systemone URL, text model) | |
| if os.environ.get("S1_URL"): # a second laya server (e.g. another checkpoint while the main one is busy) | |
| os.environ["TYPESAFE_BASE_URL"] = os.environ["S1_URL"] | |
| from jev_ultrafast import Agent | |
| import spec as S | |
| BASE = os.environ.get("GYM_URL", "http://127.0.0.1:8811") | |
| def same(sp, key, got, want): | |
| """Value equality with the widget's semantics (autocomplete keys / free text, lists joined by '|', booleans as strings).""" | |
| f = next((x for x in sp.get("fields", []) if x["key"] == key), None) | |
| if f and f["w"] == "combo": | |
| if not isinstance(got, str): return False | |
| if got == want or got == want + "-ap": return True | |
| opt = next((o for o in f["options"] if o["key"] == want), None) | |
| name = (opt or {}).get("name") or (opt or {}).get("short") or want | |
| return got.startswith("text:") and name.lower() in got.lower() | |
| if isinstance(want, list): | |
| return sorted(got if isinstance(got, list) else [x for x in str(got or "").split("|") if x]) == sorted(want) | |
| if isinstance(want, bool): | |
| return str(got).lower() == str(want).lower() | |
| return str(got) == str(want) | |
| def success(sp, url, meta): | |
| meta = meta or {} | |
| if sp["kind"] == "car": | |
| res = meta.get("results") | |
| if res is None or not all(same(sp, k, res.get(k), v) for k, v in sp["target"].items()): | |
| return False | |
| post = sp["post"] | |
| if "ctype" in post and post["ctype"] not in (res.get("ctype") or "").split("|"): return False | |
| if "trans" in post and res.get("trans") != post["trans"]: return False | |
| if "csort" in post and res.get("sort") != post["csort"]: return False | |
| if "open" in post and meta.get("car") != post["open"]: return False | |
| return True | |
| if sp["kind"] in ("restaurant", "signup"): | |
| car = meta.get("carried") or {} | |
| return bool(meta.get("done")) and all(same(sp, k, car.get(k), v) for k, v in sp["target"].items()) | |
| if sp["kind"] == "filter": | |
| tg = sp["target"] | |
| if "open" in tg: | |
| return meta.get("product") == tg["open"] | |
| lst = meta.get("listing") or {} | |
| return (all(sorted(lst.get(k) or []) == sorted(tg[k]) for k in S.FACETS) and (lst.get("pmin") or None) == tg["pmin"] and (lst.get("pmax") or None) == tg["pmax"] | |
| and (lst.get("rating") or None) == tg["rating"] and (lst.get("sort") or None) == tg["sort"] and lst.get("page") == tg["page"]) | |
| if sp["kind"] in ("flight", "hotel"): | |
| res = (meta or {}).get("results") | |
| if res is None: | |
| return False | |
| for k, v in sp["target"].items(): | |
| got = (res.get(k) or "") | |
| if isinstance(v, str) and len(v) == 3 and v.isalpha(): | |
| name = next(c[1] for c in S.CITIES if c[0] == v) | |
| if got.replace("-ap", "") != v and not (got.startswith("text:") and name.lower() in got.lower()): return False | |
| elif str(got) != str(v): | |
| return False | |
| post = sp.get("post") or {} | |
| if "filter" in post and post["filter"] not in (res.get("filter") or "").split("|"): return False | |
| if "sort" in post and res.get("sort") != post["sort"]: return False | |
| return True | |
| post = sp["post"] | |
| if "open" in post: | |
| return (meta or {}).get("product") == post["open"] | |
| lst = (meta or {}).get("listing") or {} | |
| return all((lst.get(k) or (1 if k == "page" else None)) == v if k != "q" else (lst.get("q") or "").lower() == v for k, v in post.items()) | |
| def run(kind, seed, max_steps): | |
| sp = S.make(kind, seed) | |
| t0 = time.time(); steps = 0; status = "error"; meta = None; url = ""; plan = None | |
| try: | |
| with Agent(f"{BASE}/t/{kind}/{seed}", sp["goal"]) as agent: | |
| plan = agent.state.get("plan") | |
| for state in agent.run(): | |
| steps = len(state["history"]); status = state["status"] | |
| if steps >= max_steps: break | |
| time.sleep(0.8) | |
| meta = json.loads(agent.browser.evaluate("JSON.stringify(window.__gym ? window.__gym.meta : null)") or "null") | |
| url = agent.browser.evaluate("location.href") | |
| except Exception as e: | |
| status = f"error:{type(e).__name__}:{str(e)[:60]}" | |
| ok = success(sp, url, meta) | |
| return {"kind": kind, "seed": seed, "ok": ok, "steps": steps, "status": status, "wall": round(time.time() - t0, 1), "url": url, | |
| "goal": sp["goal"], "plan": plan} | |
| def main(): | |
| n = int(sys.argv[1]) if len(sys.argv) > 1 else 10 | |
| kinds = (sys.argv[2] if len(sys.argv) > 2 else "flight,hotel,shop").split(",") | |
| max_steps = int(sys.argv[3]) if len(sys.argv) > 3 else 40 | |
| rows = [] | |
| for kind in kinds: | |
| for i in range(n): | |
| seed = 900000 + 10 * i # held out: never generated for training (and disjoint from training ranges) | |
| r = run(kind, seed, max_steps); rows.append(r) | |
| print(f"{'PASS' if r['ok'] else 'FAIL'} {kind:6s} seed={seed} steps={r['steps']:2d} {r['status'][:28]:28s} {r['wall']:5.1f}s {r['goal'][:70]}", flush=True) | |
| for kind in kinds: | |
| k = [r for r in rows if r["kind"] == kind] | |
| print(f"== {kind}: {sum(r['ok'] for r in k)}/{len(k)} median steps {sorted(r['steps'] for r in k)[len(k)//2]}") | |
| print(f"== total {sum(r['ok'] for r in rows)}/{len(rows)} ({100*sum(r['ok'] for r in rows)/max(1,len(rows)):.0f}%)") | |
| json.dump(rows, open(os.environ.get("GYM_OUT", "/tmp/gym_eval.json"), "w"), indent=1) | |
| if __name__ == "__main__": | |
| main() | |