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/dagger.py from cklxx/laya-browser: direct link, hf CLI and curl.
- Browser
- Download file 8.18 kB
-
https://huggingface.co/cklxx/laya-browser/resolve/main/code/finetune/dagger.py
- Command line
-
hf download hf://cklxx/laya-browser/code/finetune/dagger.py
-
curl -L -o dagger.py https://huggingface.co/cklxx/laya-browser/resolve/main/code/finetune/dagger.py
8.18 kB
| """DAgger-style harvesting: run real tasks with the current laya agent, and at every step ask the local Qwen teacher which | |
| action is right given the page's element table. Disagreements (and agreements) become training cases with the *agent's* | |
| on-policy states, so the next model learns exactly where this one goes wrong. | |
| python finetune/dagger.py out/dagger_cases.jsonl [tasks.jsonl] (services: chromium 9222, laya 8791, sglang 30000) | |
| tasks.jsonl lines: {"url": ..., "goal": ...}; default = apps/browser_suite.TASKS plus extra goals below. | |
| """ | |
| import json, os, sys, time | |
| import httpx | |
| sys.path.insert(0, "/home/ckl/projects/S/jev-ultrafast"); sys.path.insert(0, "/home/ckl/projects/S/laya/apps") | |
| os.environ.update(BU_CDP_URL="http://127.0.0.1:9222", TYPESAFE_BASE_URL="http://127.0.0.1:8791", TYPESAFE_API_KEY="local", | |
| TEXT_MODEL_API_KEY="local", TEXT_MODEL_BASE_URL="http://127.0.0.1:30000/v1", TEXT_MODEL="Qwen/Qwen3-8B-AWQ", | |
| TEXT_MODEL_EXTRA_JSON='{"chat_template_kwargs": {"enable_thinking": false}}') | |
| from jev_ultrafast import Agent | |
| from jev_ultrafast.model import action_space | |
| from browser_suite import TASKS | |
| EXTRA = [ | |
| ("https://en.wikipedia.org/wiki/Main_Page", "Open the article about Albert Einstein."), | |
| ("https://news.ycombinator.com/", "Open the 'past' page."), | |
| ("https://news.ycombinator.com/", "Open the comments of the first story on the front page."), | |
| ("https://github.com/tile-ai/tilelang", "Open the Pull requests tab."), | |
| ("https://github.com/tile-ai/tilelang", "Open the README's 'examples' folder."), | |
| ("https://www.python.org/", "Open the documentation page."), | |
| ("https://docs.python.org/3/", "Open the tutorial."), | |
| ("https://books.toscrape.com/", "Open the 'Mystery' category and then open the first book in it."), | |
| ("https://books.toscrape.com/", "Go to page 2 of the catalogue."), | |
| ("https://the-internet.herokuapp.com/", "Open the 'Dropdown' example and select 'Option 2'."), | |
| ("https://the-internet.herokuapp.com/", "Open the 'Checkboxes' example and tick the first checkbox."), | |
| ("https://quotes.toscrape.com/", "Open the quotes tagged 'love'."), | |
| ("https://quotes.toscrape.com/", "Go to the next page of quotes."), | |
| ("https://arxiv.org/", "Open the listing of new submissions in cs.CL."), | |
| ("https://pypi.org/", "Search PyPI for 'tilelang' and open the project page."), | |
| ("https://duckduckgo.com/", "Search for 'ModernBERT paper'."), | |
| ("https://www.saucedemo.com/", "Log in with username 'standard_user' and password 'secret_sauce', then add the 'Sauce Labs Backpack' to the cart."), | |
| ("https://demo.opencart.com/", "Open the 'Desktops' category from the top menu."), | |
| ("https://www.demoblaze.com/", "Open the 'Laptops' category."), | |
| ("https://huggingface.co/models", "Search models for 'laya'."), | |
| ] | |
| TEACHER = os.environ["TEXT_MODEL_BASE_URL"] + "/chat/completions" | |
| client = httpx.Client(timeout=180) | |
| SYS = """You are the teacher for a browser agent. You see the user's goal, the actions taken so far, the current page (title, url, text excerpt) and a | |
| numbered table of the controls on it. Decide the single best NEXT step: | |
| - {"operation": "CLICK", "index": n} click control n | |
| - {"operation": "TYPE_TEXT", "index": n} type into text control n (a separate helper supplies the value) | |
| - {"operation": "SELECT", "index": n, "value": "..."} choose that option of select control n | |
| - {"operation": "DONE"} every requirement of the goal is already visibly satisfied on this page | |
| - {"operation": "WAIT"} / {"operation": "SCROLL_DOWN"} / {"operation": "SCROLL_UP"} | |
| Do not re-do satisfied steps; a field that already shows the requested value is done. Return JSON only.""" | |
| def teach(goal, history, page, elements): | |
| table = [{"index": e["index"], "label": e["label"][:70], "role": e.get("role"), "ops": e["operations"], **({"value": e["value"]} if e.get("value") else {})} for e in elements[:120]] | |
| user = {"goal": goal, "actions_so_far": [{k: h.get(k) for k in ("action", "kind", "text")} for h in history[-8:]], | |
| "page": {"title": page["title"], "url": page["url"], "text": page["text"][:2500]}, "controls": table} | |
| body = {"model": os.environ["TEXT_MODEL"], "max_tokens": 120, "temperature": 0.0, "response_format": {"type": "json_object"}, | |
| "chat_template_kwargs": {"enable_thinking": False}, | |
| "messages": [{"role": "system", "content": SYS}, {"role": "user", "content": json.dumps(user, ensure_ascii=False)}]} | |
| r = client.post(TEACHER, json=body).json() | |
| return json.loads(r["choices"][0]["message"]["content"]) | |
| def to_case(goal, history, page, verdict): | |
| elements, targets, controls = action_space(page["actions"]) | |
| op = str(verdict.get("operation", "")).upper() | |
| if op in ("DONE",): | |
| return {"page": -1, "url": page["url"], "title": page["title"], "goal": goal, "gold_op": "DONE", "gold_id": "DONE", "kind": "done", "label": "", | |
| "history": history, "source": "dagger", "page_obj": {k: page[k] for k in ("url", "title", "text", "actions")}} | |
| if op in ("CLICK", "TYPE_TEXT", "SELECT"): | |
| idx = str(verdict.get("index")) | |
| cands = targets.get(op, {}) | |
| if op == "SELECT": | |
| hit = [k for k, a in cands.items() if k.split(":")[0] == idx and (a["value"] == verdict.get("value") or a["label"].endswith(str(verdict.get("value"))))] | |
| key = hit[0] if hit else None | |
| else: | |
| key = idx if idx in cands else None | |
| if key is None: return None | |
| a = cands[key] | |
| return {"page": -1, "url": page["url"], "title": page["title"], "goal": goal, "gold_op": op, "gold_id": a["id"], "kind": a["kind"], "label": a["label"], | |
| "history": history, "source": "dagger", "page_obj": {k: page[k] for k in ("url", "title", "text", "actions")}} | |
| return None | |
| def main(): | |
| out = sys.argv[1] | |
| tasks = [(u, g) for _, u, g, _ in TASKS] + EXTRA | |
| if len(sys.argv) > 2: | |
| tasks += [(json.loads(l)["url"], json.loads(l)["goal"]) for l in open(sys.argv[2])] | |
| n_cases = n_dis = 0 | |
| with open(out, "a") as f: | |
| for url, goal in tasks: | |
| t0 = time.time() | |
| try: | |
| with Agent(url, goal) as agent: | |
| steps = 0 | |
| while agent.state["status"] not in ("done", "blocked") and steps < 12: | |
| page = agent.state["page"]; history = list(agent.state["history"]) | |
| elements = action_space(page["actions"])[0] | |
| try: | |
| verdict = teach(goal, history, page, elements) | |
| except Exception as e: | |
| print(" teacher fail", str(e)[:60]); break | |
| case = to_case(goal, [{k: h.get(k) for k in ("action", "kind", "text", "page_changed")} for h in history], page, verdict) | |
| if case: | |
| f.write(json.dumps(case, ensure_ascii=False) + "\n"); f.flush(); n_cases += 1 | |
| # step the agent with its own policy | |
| try: | |
| st = agent.command("tick") | |
| except Exception as e: | |
| print(" tick fail", type(e).__name__, str(e)[:50]); break | |
| d = st["decisions"][-1] if st["decisions"] else None | |
| agent_choice = (d["operation"], d.get("target")) if d else None | |
| teacher_choice = (str(verdict.get("operation", "")).upper(), str(verdict.get("index")) if verdict.get("index") is not None else None) | |
| if agent_choice and agent_choice[0] != teacher_choice[0] or (agent_choice and agent_choice[1] != teacher_choice[1] and teacher_choice[0] in ("CLICK", "TYPE_TEXT")): | |
| n_dis += 1 | |
| steps += 1 | |
| except Exception as e: | |
| print(f" task fail {type(e).__name__}: {str(e)[:60]}") | |
| print(f"{goal[:60]:60s} cases={n_cases} disagreements={n_dis} {time.time()-t0:.0f}s", flush=True) | |
| print("wrote", n_cases, "cases ->", out) | |
| if __name__ == "__main__": | |
| main() | |