Skip to content

[decision_models] Compact the browser front's state for Laya's window - #9

Draft
chaimaerachdi wants to merge 1 commit into
ThinkFlowLab:mainfrom
chaimaerachdi:laya-compact-browser-state
Draft

chaimaerachdi wants to merge 1 commit into
ThinkFlowLab:mainfrom
chaimaerachdi:laya-compact-browser-state

Conversation

@chaimaerachdi

@chaimaerachdi chaimaerachdi commented Sep 24, 2026 •

Copy link
Copy Markdown

Why

The browser front sends every decision model the same state it sends Jev: a JSON object per element row, the
full page text, and ten actions of history. docs/benchmarks.md shows that state runs 18,785-23,654 tokens
on the Allrecipes run. Laya's window is 512 to 1024 tokens. The state fills the window before a single
instruction token is spent, which is why --model laya raises MODEL_SERVICE_CONFIG_ERROR on most real
pages today.

What

  • laya_state() in s1a/decision_models/laya.py, called from LayaModel._decide before every request:
    • page.text dropped. It's for the chat model's DONE answer, which Laya never writes; the choice heads
      already carry each candidate's own text.
    • Each element row: one short line (idx role label =value [flags]) instead of a JSON object with
      repeated key names.
    • recent_actions: last 3 kept instead of 10.
    • Anything that isn't this shape (tool front, rails, a plain string) passes through unchanged — those
      already fit the window.
  • On by default; LAYA_COMPACT_BROWSER_STATE=0 turns it off.
  • docs/decision-models.md, CHANGELOG.md updated.

Roughly a tenfold reduction in the JSON-shaped state's size on the fixtures used in the tests below.

What this does not do

This closes the "state is bigger than the window" gap, not the "is a filled window, on the real 0.4B
checkpoint, actually under 7s end to end on a real page" question. That needs uv sync --extra laya, the
real convaiinnovations/laya weights, and a live browser run — I don't have a GPU or a live browser session
in the environment I built this in, so I have not run that. Exact repro:

```
uv sync --extra laya
LAYA_MAX_LEN=1024 uv run s1a run flights --model laya
```

If 1024 still isn't enough on a heavier page, LAYA_MAX_LEN needs to go up further (2048, 4096); that knob
is unchanged by this PR.

Verification

  • uv run ruff format --check s1a/decision_models/laya.py tests/test_decision_models_laya.py
  • uv run ruff check s1a/decision_models/laya.py tests/test_decision_models_laya.py
  • uv run pytest tests/test_decision_models_laya.py -q: 34 passed, 2 skipped, 6 subtests passed
    (FakeLayaAgent, no torch/weights needed)
  • uv run pytest tests/ -q --ignore=tests/test_browser_policy.py: same 19 pre-existing failures on
    main before and after this change (missing ty binary and other environment-only gaps in the
    sandbox I built this in; tests/test_browser_policy.py fails to collect on main too, before this
    branch — openjiuwen.harness.schema.decision_policy isn't in the pinned [email protected] tag
    installed there). Neither is touched by this change.
  • Real-model / real-page latency and accuracy numbers against the <7s goal — not run, see above.

Laya reads a 512 to 1024 token window, but the browser front sends
every model the same state it sends Jev: a JSON object per element
row, the full page text, and ten actions of history. On a real page
that state fills the window well before a single instruction token
is spent (docs/benchmarks.md shows 18,785-23,654 input tokens for
Jev on the Allrecipes run), which is why `--model laya` routinely
raises MODEL_SERVICE_CONFIG_ERROR on the browser front today.

laya_state() folds a browser-shaped state before every call to
LayaModel._decide: page.text dropped (the choice heads already carry
each candidate's own text; the free-form dump is for the chat
model's DONE answer, which Laya never writes), each element row
rendered as one short line instead of a JSON object, and the last
three actions kept instead of ten. On by default; anything that
isn't the browser front's shape passes through unchanged (the tool
front already fits). LAYA_COMPACT_BROWSER_STATE=0 turns it off.

34 unit tests (tests/test_decision_models_laya.py) cover the
compaction itself, its wiring into LayaModel, and the env-var
opt-out, all against FakeLayaAgent (no torch/weights needed). Full
suite run against main: identical 19 pre-existing failures before
and after this change (missing `ty` binary and other env-only gaps
in this sandbox, unrelated to decision_models/laya.py).

Not done here, and worth flagging: this closes the "state is bigger
than the window" gap, not the "is Laya's window, even filled,
actually fast enough end to end on a real page" question. That
needs the real convaiinnovations/laya checkpoint (uv sync --extra
laya), a live browser run, and a real latency number. See the PR
description for exact commands.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@chaimaerachdi
chaimaerachdi force-pushed the laya-compact-browser-state branch from 56eac3a to 36175eb Compare September 24, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant