diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5fc0fe..b943933 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,18 @@ on: pull_request: jobs: + website: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + - name: Check project website and demo paths + run: | + node --check website/assets/app.js + node website/check.mjs + checks: runs-on: ubuntu-latest strategy: diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/README.md b/README.md index 93087c2..cc0ad36 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Receiver-conditioned latent communication for agents.** Code for *Receiver-Conditioned Latent Communication gives 94% CacheBack*. -**[Paper](https://arxiv.org/abs/2609.32046).** [Python API](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md) · [Paper replication](https://github.com/maxr0ssi/rclc/blob/main/docs/replication.md) +**[Website](https://maxr0ssi.github.io/rclc/)** · **[Paper](https://arxiv.org/abs/2609.32046)** · [Python API](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md) · [Paper replication](https://github.com/maxr0ssi/rclc/blob/main/docs/replication.md) Agents distribute large contexts across senders and receivers. Text messages require decoding and can omit evidence. Full KV-cache messages accumulate every @@ -18,18 +18,22 @@ that request to choose which sender positions enter the handoff. with 50 concurrent tasks on eight H100 GPUs. Operating points differ by family. Right: the receiver query guides which sender positions enter the handoff.* -## Watch the handoff +## Watch the demo -**[▶ Try the demo in your browser](https://maxr0ssi.github.io/rclc/demo/)** +**[▶ Watch the 36-second coding demo](https://maxr0ssi.github.io/rclc/#demo)** -![Four-agent relay: RCLC highlights the source positions it hands off and answers correctly in 3.4 s; the text relay is still answering at 26 s.](https://raw.githubusercontent.com/maxr0ssi/rclc/main/docs/assets/demo.gif) +[![CacheBack finishes a Django fix while text workers are still writing reports.](demo/coding/poster.jpg)](https://maxr0ssi.github.io/rclc/#demo) -*Replayed at 2× from a recorded Qwen3-8B run on one A100.* +Seven Qwen3-8B workers help a coordinator fix a Django bug. CacheBack completes +this recorded case in **25.66 s**, versus **113.21 s** for text: **4.41× faster**. +Both produce the same patch and subsequently pass all 88 tests. Startup and +test grading are excluded from the clocks. The silent video reconstructs the +interface from separate runs and varies playback speed. This is one case, +not an aggregate benchmark. [Recorded results](demo/coding/evidence.json). -The demo follows a booking through four agents and compares RCLC with a text -relay. Latencies come from recorded runs, not a benchmark. The -[demo guide](https://github.com/maxr0ssi/rclc/blob/main/demo/README.md) covers -running it locally and recording your own. +Also explore the [interactive booking relay](https://maxr0ssi.github.io/rclc/demo/) +to inspect selected positions and text messages at each handoff. Its +[demo guide](demo/README.md) covers recording your own. ## Install diff --git a/demo/README.md b/demo/README.md index a02f781..8b94c23 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,10 +1,15 @@ -# CacheBack demo +# CacheBack booking relay + +The replay labels the method CacheBack; `rclc` remains its recording key. Three agents pass evidence to a fourth. Maya's booking reference J7 leads to Harbour Hub, whose passport requirement overrides the original desk's ID advice. ## Watch +The [booking section](../index.html#booking-demo), below the main coding video, presents the recorded final answers +and completion times before the replay. Selected positions use teal highlights. + Open and press **Run**. To serve it locally, run this from the repository root and open : @@ -22,8 +27,11 @@ intermediate messages, exact token origins and timings. `collection-id-rules.txt`. Each shows the source document with RCLC highlights and the generated text handoff. The last column shows both unedited answers and their total times. -- Text boxes follow the newest generated line. Documents and messages scroll; on - a small screen, scroll the whole diagram horizontally. +- Text boxes follow the newest generated line. Documents and messages scroll + within their compact panes. On smaller screens, agent cards form two columns, + then one on phones, with their agent labels repeated above the content. + An embedded replay resizes to its content, so its outer frame never needs + horizontal or vertical scrolling. Scroll the containing page normally. - **Replay** restarts. **Clear** stops playback and resets the highlights, messages and clocks. - The reviewed 8B answer shows `✓ Harbour Hub · Passport` when it finishes. The @@ -33,7 +41,8 @@ intermediate messages, exact token origins and timings. Playback defaults to **1x**; **2x** and **4x** speed it up. Each method progresses independently from its measured handoff durations and total completion time, so the faster one finishes while the other keeps streaming, and -the clocks show recorded time. Completion times are measured; streaming is +the clocks show recorded time. Playback starts from the first animation-frame +timestamp, keeping embedded-frame clock origins from producing negative time. Completion times are measured; streaming is illustrative. Text streams at an estimated constant character rate within each stage, because the recording has no per-token timestamps or separate prefill/decode timings. Reduced-motion preferences disable the highlight diff --git a/demo/coding/evidence.json b/demo/coding/evidence.json new file mode 100644 index 0000000..47e5561 --- /dev/null +++ b/demo/coding/evidence.json @@ -0,0 +1,54 @@ +{ + "run_id": "bughunt-16333-run-20261001T051520Z", + "source": "s3://lce-pr2789-paper/rclc-demo/bughunt-16333-run-20261001T051520Z/", + "model": "Qwen/Qwen3-8B", + "issue": "django__django-16333", + "arms": { + "text": { + "seconds": 113.20635836, + "worker_seconds": 100.612943677, + "worker_done": [ + 100.612943677, + 38.252793639, + 25.637343035, + 33.867839988, + 22.492231897, + 78.432600727, + 29.442396709 + ], + "status": "resolved", + "patch": "diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py\n--- a/django/contrib/auth/forms.py\n+++ b/django/contrib/auth/forms.py\n@@ -141,6 +141,7 @@\n user.set_password(self.cleaned_data[\"password1\"])\n if commit:\n user.save()\n+ self.save_m2m()\n return user\n \n \n", + "new_tests_passed": 1, + "regression_tests_passed": 87, + "failed_tests": 0, + "handoff_bytes": 24969, + "handoff_rows": 0, + "coordinator_tokens": 1470, + "source_result": "s3://lce-pr2789-paper/rclc-demo/bughunt-16333-run-20261001T051520Z/text/result.json", + "source_grade": "s3://lce-pr2789-paper/rclc-demo/bughunt-16333-run-20261001T051520Z/grades/text.json" + }, + "latent": { + "seconds": 25.66205141, + "worker_seconds": 6.196859804, + "worker_done": [ + 6.196859804, + 6.093338203, + 6.146028218, + 6.18476604, + 6.18007457, + 5.908353919, + 6.053007381 + ], + "status": "resolved", + "patch": "diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py\n--- a/django/contrib/auth/forms.py\n+++ b/django/contrib/auth/forms.py\n@@ -141,6 +141,7 @@\n user.set_password(self.cleaned_data[\"password1\"])\n if commit:\n user.save()\n+ self.save_m2m()\n return user\n \n \n", + "new_tests_passed": 1, + "regression_tests_passed": 87, + "failed_tests": 0, + "handoff_bytes": 675872768, + "handoff_rows": 82504, + "coordinator_tokens": 921, + "source_result": "s3://lce-pr2789-paper/rclc-demo/bughunt-16333-run-20261001T051520Z/latent/result.json", + "source_grade": "s3://lce-pr2789-paper/rclc-demo/bughunt-16333-run-20261001T051520Z/grades/latent.json" + } + } +} diff --git a/demo/coding/poster.jpg b/demo/coding/poster.jpg new file mode 100644 index 0000000..c6f47e9 Binary files /dev/null and b/demo/coding/poster.jpg differ diff --git a/demo/coding/rclc-cacheback-4k.mp4 b/demo/coding/rclc-cacheback-4k.mp4 new file mode 100644 index 0000000..4eea1cc Binary files /dev/null and b/demo/coding/rclc-cacheback-4k.mp4 differ diff --git a/demo/index.html b/demo/index.html index 6eb3de0..e412c8a 100644 --- a/demo/index.html +++ b/demo/index.html @@ -4,7 +4,7 @@ - CacheBack + CacheBack | Booking relay @@ -70,7 +73,7 @@

Where should Maya collect her pass on Friday, and what ID shou -
+

Chain of agents

  1. Agent 1booking-confirmation.txt
  2. @@ -79,7 +82,7 @@

    Where should Maya collect her pass on Friday, and what ID shou
  3. Agent 4Final answer
-

RCLC

+

CacheBack

@@ -95,6 +98,14 @@

Where should Maya collect her pass on Friday, and what ID shou + + + + +
+
+
+

September 2026

+

CacheBack

+

Receiver-conditioned communication
between LLM agents.

+
+
  • Maximillian Rossi
  • Prajwal Raghunath
  • Haoqing Xuan
  • Yusen Zhang
  • Eugene Wu
+

DAP LabColumbia University

+
+

The receiver asks for what it needs.

+

CacheBack uses that request to select which internal state gets sent.

+

Our simplest implementation of receiver-conditioning: robust and training-free.

+ +
+ +
+
+ +
+
+

Written messages between agents can lose evidence; passing their entire internal state becomes expensive. Receiver-conditioning lets the receiver describe what it needs, so the sender can select what to pass on. CacheBack is our simplest, robust, training-free implementation.

+

On FanOutQA with Qwen 3 8B, CacheBack sends 75% fewer positions than full-state transfer. Compared with same-size text communication, strict accuracy improves by 14.7 percentage points and median completion is 3.2× faster.

+
+
Read the full abstract +
+

Multi-agent systems distribute large contexts across agents that communicate to solve a task. Text messages are compact but require decoding and may omit evidence the receiving agent needs. Recent latent communication instead transfers KV caches. This avoids text generation and can improve accuracy and latency. However, a full KV cache grows linearly with both the context an individual agent processes, and the number of agents that coordinate together.

+

This raises memory and context costs, often far exceeding available GPU resources and context window sizes. Our key observation is that agents need only send what the receiving agent requires for its local task -- which we call receiver-conditioned communication. The receiver agent passes the sender a small description of its information needs, which serves to filter and compress the sender agent's KV cache.

+

CacheBack is a simple, robust, training-free instance of receiver conditioning based on the sender's attention weights. On FanOutQA, CacheBack with Qwen 3 removes 75% of the state the agent would otherwise receive, improving accuracy by 14.7 percentage points and reducing median task-completion latency by 3.2× relative to text communication. We show comparable improvements across model families that span dense Transformers, Mamba-attention hybrids, and sliding-window attention.

+

A KV cache stores intermediate state from the text a model has processed. Abstract from the published paper, by Rossi et al. CC BY 4.0; emphasis added.

+
+
+
+
+
+
+

How it works

+

The receiver describes what it needs for its next task. CacheBack uses that request to select part of the sender’s internal numerical state, or latent state, to send back.

+
+
+
+

Sender

Select state using
the request

+ +

Receiver

Has the next task

+
+
+

CacheBack

+

Our simplest implementation.
Robust and training-free.

+

Uses attention to the receiver’s request to score sender positions, then selects a subset within the message budget.

+
+
+
What crosses the handoff?

CacheBack selects positions using the sender’s attention to the receiver’s request. In the paper’s selected-embedding channel, the corresponding input vectors are sent and the receiver rebuilds its KV cache. A position is not an isolated fact, and an attention score is not a guarantee of relevance. The experiments use matching models; cross-model translation is outside this work. Read the method and limitations.

+
+
+

Results

On FanOutQA, three agents read separate sources and send state to one answering agent. These curves compare accuracy and completion time across message budgets and text-sender sizes.

+
+

Accuracy and completion time

Higher is more accurate. Further left is faster. Each point is a measured setting; lines join the best observed tradeoffs within each method. Text points vary sender size; CacheBack points vary compression or message budget. The receiver stays fixed.

Reading r4, r8, r16: rX means keeping about 1/X of the sender’s sequence positions. For example, r4 keeps 25%, r8 keeps 12.5%, and r16 keeps 6.25%. Larger numbers mean more compression and a smaller message.

+
● ━ CacheBack▲ ┄ Text communication× Question only
+
+
FanOutQA, Qwen 3 8B: CacheBack r4 reaches 55.3% strict accuracy at 192 seconds, compared with 40.7% at 612 seconds for same-size text; weaker settings are also plotted.
Qwen 3: r4 improves strict accuracy from 40.7% to 55.3% and median completion from 612 to 192 seconds.
+
FanOutQA, Nemotron Nano 2 12B: CacheBack r8 reaches 50% accuracy near 102 seconds versus 38.7% near 131 seconds for same-size text. A smaller text sender is faster at lower accuracy.
Nemotron: the benefit also appears in a model combining Mamba and attention. A smaller text sender is faster, but less accurate.
+
+
Repeated handoffs: LongBench v2

On LongBench v2 Easy, four agents read successive sections of a long document. A final receiver answers from the last handoff. Evidence from the beginning must survive the chain.

A relative budget uses the rX fraction above, so message size grows with the sender’s context. A fixed budget caps the number of message positions, as in the labels “32K fixed” and “64K fixed”.

+
● ━ CacheBack · relative budget■ ━ CacheBack · fixed budget▲ ┄ Text× Question only
+
+
LongBench v2 Easy, Qwen 3 8B: both relative and fixed CacheBack budgets include settings faster and more accurate than same-size text. Stronger compression eventually lowers accuracy.
Qwen 3: both message-budget schemes improve the measured tradeoff. More aggressive compression eventually reduces accuracy.
+
LongBench v2 Easy, Nemotron Nano 2 12B: CacheBack r8 reaches 48% accuracy near 133 seconds, while fixed-budget settings show a different tradeoff; smaller text senders can be faster.
Nemotron: a fixed budget caps message positions; a relative budget grows with the material read. Neither denotes total GPU memory.
+
+
+

Redrawn from the paper’s plotting data for Qwen and Nemotron. Each benchmark uses 50 tasks submitted together on eight H100s; completion includes queueing. Accuracy averages three receiver draws. Qwen uses a 0 to 1,000 s time axis, Nemotron 0 to 400 s; each model keeps its scale across benchmarks. Figures 5, 11, and 12 in the paper.

+

About “94% CacheBack”: r16 retains one sender position in sixteen, removing 93.75%. The selected Qwen result above uses r4, or 75% fewer positions. Position reduction is not total GPU memory saved.

+
+
+
+
+

Watch the demo

Seven workers help a coordinator fix a Django bug. Compare latent handoffs with written reports.

+
+

36 seconds · 4K · Recorded coding task

Fixing a Django bug with seven coding agents

Qwen3-8B produces the patch 4.41× faster with CacheBack than with written reports on this case. Both runs make the same one-line code change and pass all 88 tests.

+
+

One recorded case: CacheBack 25.66 s; text 113.21 s. Startup and test grading excluded. Silent video; playback speed varies. Open in full window

+
Recording details and text description

Seven Qwen3-8B workers pass information to a coordinator. The supplied bug report identifies a missing save_m2m() call in Django’s user creation form. Both runs add that call after saving the user. The CacheBack run finishes while the text workers are still generating reports.

The interface is reconstructed from separate recorded runs, aligned at their start. The final response and patch come from the recorded outputs. Both patches subsequently pass one new test and 87 regression tests; grading is outside the clocks. This is an individual demonstration, not an aggregate benchmark. Recorded results

+
+
+
+

Recorded model run

Where should Maya collect her pass on Friday, and what ID should she bring?

Three documents contain the booking, a change of location, and the new desk’s ID rules. The final agent must connect all three.

+
+
+

CacheBack

3.42 s
+

Maya should collect her pass at Harbour Hub on Friday, and she should bring a passport as her ID.

+
Evidence quoted by the model
Evidence: +- "Reference J7 has moved to Harbour Hub." +- "Harbour Hub requires a passport for replacement building pass collections."
+
+
+

Text messages

26.09 s
+

Maya should collect her pass at the assigned desk based on her reference J7, not at the Central Desk. She should check the Friday operations notice using her reference J7 to confirm the exact location. The ID she should bring depends on the desk where she collects her pass. If she is collecting at Harbour Hub, she must bring a passport, as a driving licence is not accepted. The name on the ID must match her booking, and the ID requirements for the specific desk apply.

+
Evidence quoted by the model
Evidence: +- "Maya should collect her pass at the assigned desk based on her reference, not at the Central Desk." +- "She should check the Friday operations notice using her reference J7 to confirm the exact location." +- "If she is collecting at Harbour Hub, a passport is required, and a driving licence is not accepted."
+
+
+

Qwen3-8B · one A100 · r4 · one warm run per method. Times include three handoffs and final answer generation, excluding model loading, tokenization, and warmup. This recording is an example, not a benchmark. Raw recording · Setup

+
Replay the booking handoffs + +
+
+
+ +
+
+

Quickstart

Bind a sender and receiver to matching models, then pass the receiver’s request to rcc.transfer. CacheBack is the default selector. Continue generation with your model’s native API.

Code and documentation

Local Qwen 3 weights, using Hugging Face or vLLM.
Hosted chat APIs do not expose the required state.

Python
import rcc
+
+sender = rcc.bind(
+    model, tokenizer,
+    messages=sender_history, backend="hf",
+)
+receiver = rcc.bind(
+    model, tokenizer,
+    messages=receiver_history, backend="hf",
+)
+await rcc.transfer(
+    sender, receiver,
+    "Who owns the Cedar booking?",
+)
+inputs = receiver.pop()

Model, tokenizer, and histories loaded; run in an async function or notebook. Runnable quickstart

+

THE PAPER

Receiver-Conditioned Latent Communication gives 94% CacheBack

Maximillian Rossi, Prajwal Raghunath, Haoqing Xuan, Yusen Zhang, and Eugene Wu.

Cite this work BibTeX
@misc{rossi2026cacheback,
+  title = {Receiver-Conditioned Latent Communication
+           gives 94\% CacheBack},
+  author = {Rossi, Maximillian and Raghunath, Prajwal
+            and Xuan, Haoqing and Zhang, Yusen
+            and Wu, Eugene},
+  year = {2026},
+  eprint = {2609.32046},
+  archivePrefix = {arXiv},
+  primaryClass = {cs.AI},
+  url = {https://arxiv.org/abs/2609.32046}
+}
+
+ +
+ + diff --git a/tests/demo.html b/tests/demo.html index 8969d6b..ffc79c0 100644 --- a/tests/demo.html +++ b/tests/demo.html @@ -9,7 +9,7 @@ const assert = (value, message) => { if (!value) throw new Error(message); }; const plain = text => text.replace(/\*\*([^*\n]+)\*\*/g, "$1"); try { - await new Promise(resolve => { frame.onload = resolve; frame.src = "../demo/"; }); + await new Promise(resolve => { frame.onload = resolve; frame.src = "../demo/index.html"; }); const win = frame.contentWindow, doc = frame.contentDocument; const play = doc.querySelector("#play"), speed = doc.querySelector("#speed"), clear = doc.querySelector("#clear"); await new Promise((resolve, reject) => { @@ -26,7 +26,7 @@ let next; win.requestAnimationFrame = callback => { next = callback; return 1; }; win.cancelAnimationFrame = () => { next = null; }; - Object.defineProperty(win.performance, "now", {value:() => 0}); + Object.defineProperty(win.performance, "now", {value:() => 10000}); assert(doc.querySelectorAll("button").length === 2 && [...speed.options].map(option => option.value).join() === "1,2,4", "Only playback, clear and speed controls"); assert(speed.value === "1", "Recorded speed is the default"); assert(doc.querySelectorAll(".topology li").length === 4, "Four-agent topology"); @@ -44,6 +44,7 @@ for (const method of ["rclc", "text"]) { const row = doc.querySelector(`#${method}-row`), run = recorded[method]; const complete = ends[method].filter(end => elapsed >= end).length; + assert(Number.parseFloat(doc.querySelector(`#${method}-time`).textContent) >= 0, "Playback uses one animation clock, even when performance.now has a different origin"); assert(row.children.length === 4, "All chunks remain visible"); if (method === "rclc") { trace.sources.forEach((source,i) => assert(row.children[i].textContent === source.text, "Source text is unchanged")); @@ -73,9 +74,14 @@ assert(!doc.querySelector("mark") && !doc.querySelector("#text-row").textContent && !doc.querySelector(".answer").textContent, "Clear removes all output and highlights"); assert([...doc.querySelectorAll(".time")].every(node => !node.textContent) && play.textContent === "Run", "Clear resets clocks"); frame.style.width = "390px"; + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); assert(doc.documentElement.scrollWidth === doc.documentElement.clientWidth, "Mobile page fits its viewport"); - assert(doc.querySelector(".flow").scrollWidth > doc.querySelector(".flow").clientWidth, "Mobile topology scrolls as one diagram"); + assert(doc.querySelector(".flow").scrollWidth === doc.querySelector(".flow").clientWidth, "Mobile relay fits without horizontal scrolling"); + assert(doc.documentElement.scrollHeight <= win.innerHeight, "Embedded replay grows without a vertical frame scrollbar"); + assert([...doc.querySelectorAll(".chunk")].every(node => node.dataset.agent.startsWith("Agent ")), "Stacked cards retain agent labels"); frame.style.width = "100%"; + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + assert(doc.documentElement.scrollHeight <= win.innerHeight, "Embedded replay shrinks back to desktop content height"); await new Promise(resolve => { frame.onload = resolve; win.location.reload(); }); const live = frame.contentDocument, livePlay = live.querySelector("#play"); await new Promise(resolve => { @@ -90,7 +96,7 @@ assert(!livePlay.disabled && livePlay.textContent === "Replay", "Native animation clock completes playback after testing"); for (const method of ["rclc", "text"]) assert(live.querySelector(`#${method}-row .answer`).textContent.startsWith(plain(recorded[method].answer.text)), "Real-time playback reaches both full answers"); live.querySelector("#clear").click(); live.querySelector("#speed").value = "1"; - const page = await (await fetch("../demo/")).text(); + const page = await (await fetch("../demo/index.html")).text(); for (const change of ["answer", "question"]) { const altered = structuredClone(trace); if (change === "answer") altered.cases[0].rclc.answer.text = "Maya should go to Central Desk."; @@ -107,11 +113,29 @@ if (!changedPlay.disabled) { clearTimeout(timeout); observer.disconnect(); resolve(); } }); frame.contentWindow.requestAnimationFrame = callback => { next = callback; return 1; }; - changedPlay.click(); next(frame.contentWindow.performance.now() + Math.max(recorded.rclc.seconds, recorded.text.seconds) * 1000 + 1000); + changedPlay.click(); next(0); next(Math.max(recorded.rclc.seconds, recorded.text.seconds) * 1000 + 1000); assert(!changed.querySelector(".answer-check"), "Unreviewed recordings are not labelled correct"); URL.revokeObjectURL(url); } - await new Promise(resolve => { frame.onload = resolve; frame.removeAttribute("srcdoc"); frame.src = "../demo/"; }); + await new Promise(resolve => { frame.onload = resolve; frame.removeAttribute("srcdoc"); frame.src = `../index.html?check=${Date.now()}`; }); + const home = frame.contentDocument, disclosure = home.querySelector(".demo-replay"); + const replay = home.querySelector("#flagship-demo"); + await new Promise(resolve => { + replay.onload = resolve; replay.loading = "eager"; disclosure.open = true; + }); + for (const width of [1280, 390, 1280]) { + frame.style.width = `${width}px`; + for (const open of [false, true]) { + disclosure.open = open; + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + } + const inside = replay.contentDocument, view = replay.contentWindow; + assert(inside.documentElement.scrollHeight <= view.innerHeight, "Actual homepage fits the full replay height without suppressing overflow"); + assert(inside.querySelector("footer").getBoundingClientRect().bottom <= view.innerHeight, "Replay footer is inside the frame after resizing and reopening"); + assert(inside.documentElement.scrollWidth <= view.innerWidth, "Actual homepage replay has no horizontal overflow"); + assert(!["hidden", "clip"].includes(view.getComputedStyle(inside.documentElement).overflowY), "Unexpected overflow remains accessible rather than clipped"); + assert(view.getComputedStyle(inside.querySelector(".chunk")).overflowY === "auto", "Document panes remain scrollable"); + } document.title = "Replay check passed"; } catch (error) { result.hidden = false; result.textContent = `FAIL: ${error.message}`; diff --git a/website/README.md b/website/README.md new file mode 100644 index 0000000..56a0d68 --- /dev/null +++ b/website/README.md @@ -0,0 +1,115 @@ +# Project website + +The project page lives at . GitHub Pages +publishes the repository root from `main`, using the existing Pages settings. +Merging a homepage change into `main` updates the site automatically. +The root `.nojekyll` file serves these static files without Jekyll processing. +The canonical URL and social preview point to this Pages address and the coding +video poster. Share the homepage, or append `#demo` to link to the main demo. + +## Layout + +- `index.html` at the repository root: the homepage and published abstract. + The top-left header reads RCLC. CacheBack is the main title, with + receiver-conditioned communication between LLM agents as its subtitle. + Authors appear directly beneath it, followed by DAP Lab and Columbia + University. The linked lab logo loads from the official + [DAP Lab website](https://daplab.cs.columbia.edu/). +- `website/assets/`: CSS, browser JavaScript, selected results, and paper curves. +- `demo/`: the existing recorded booking relay, still served at `/rclc/demo/`. +- `demo/coding/`: the main 36-second coding video, poster, and recorded results. +- `website/check.mjs`: dependency-free Node checks, also run by CI. + +Links to assets and the embedded demo are relative to the homepage. The same +files work under the GitHub Pages `/rclc/` prefix and a local HTTP server. +There is no build step, API, model server, or hosting account to configure. +Google Fonts is optional; system fonts are the fallback. The replay reads its +recorded JSON from this repository and does not run inference. + +## Preview and verify + +From the repository root, using Node 22 or newer: + +```sh +node --check website/assets/app.js +node website/check.mjs +python3 -m http.server 4174 --bind 127.0.0.1 +``` + +Open and check the hero, narrow-screen layout, +copy buttons, main video playback, fullscreen, and embedded booking replay. The offline +check validates local assets and anchors, the demo route and recording, +the displayed benchmark bars and annotations, and homepage answers and times +against the raw demo recording. +The homepage and its assets stay below 100 KB, with JavaScript below 6 KB +gzip. Demo media and recordings are separate from that budget. The native video +player uses `preload="none"`, so the 5.8 MB MP4 loads when requested. There is +no video service, autoplay, or additional dependency. A small play-button overlay +starts the native player; native controls remain available without JavaScript. + +## Content and demos + +The page runs from introduction and the four-model comparison to a visible +TL;DR, method, detailed results, demos, quickstart, and citation. The overview +explains the problem, contribution, and selected Qwen result; the unchanged +published abstract expands directly beneath it. LongBench curves expand under +the results. +Use two names in the explanatory copy: receiver-conditioning is the idea; +CacheBack is its simplest, robust, training-free implementation. Keep RCLC in +the small header and repository references. Introduce latent state in the +method section; label the charts and recorded demo CacheBack. The published +abstract, paper title, citation, and recorded answers remain unchanged. +Teal identifies CacheBack, including selected positions in the recorded replay. +Desktop layouts above 800px render at 85% scale, about 5% larger than the +previous 81% layout, with wider outer margins. Medium widths use 94.5%; phones +at 520px and below retain their original text sizing. + +The method section diagrams receiver-conditioning: a request travels from receiver +to sender, and selected state returns to the receiver. CacheBack is introduced +beneath it as the simplest, robust, training-free implementation. This is a +static explanation of the protocol, not a measured attention visualization. + +`assets/results.json` contains the selected FanOutQA points. The hero shows +all four models with shared zero-based accuracy and seconds scales. Gains +and speedups use the unrounded values. Four SVG curves show the paper's +fuller results. Keep captions, experimental setup, and measured values +together when editing. The abstract is reproduced under CC BY 4.0 with +emphasis added; its attribution is on the page. + +The main demo is the supplied 3840×2160, 60 fps, silent video in +`demo/coding/rclc-cacheback-4k.mp4`. Preserve its original bytes. Its poster is +a frame from the video; native controls provide playback and fullscreen. +The README links to the homepage's `#demo` anchor with the same poster. + +The video reconstructs a Django patch task from the recorded Qwen3-8B run +`bughunt-16333-run-20261001T051520Z`, sample `s1`. Seven workers provide input +to a coordinator. The supplied report identifies the missing `save_m2m()` call. +CacheBack finishes in 25.66205141 s and text in 113.20635836 s; the 4.41× ratio +applies to this case only. Both produce the same patch and pass one new test +and 87 regression tests. Startup and subsequent grading are excluded from +the clocks. The two arms ran separately, and video playback speed varies. +`demo/coding/evidence.json` retains the recorded results used in the caption; +the homepage offers a text description of the silent video. The offline check +validates the displayed times and ratio against these results. + +The secondary booking relay shows both recorded final answers and measured total times +first, with model-quoted evidence and the embedded replay in native disclosures. +The full-width teal "Replay the booking handoffs" control opens the replay. +The frame follows the replay's content height. Agent cards wrap on narrow +screens; only the individual document and message panes retain internal scrolling. +Embedded sizing uses the content's layout height, top position, and bottom margin. +Overflow stays available as a fallback; content is never hidden to remove a +scrollbar. The browser regression checks full document height and footer bounds +in the actual homepage, at desktop and phone widths and through disclosure toggles. +The answer text, evidence, and times in `index.html` must match the first case +in `demo/trace-w4.json`; the offline check enforces this. See +[the demo guide](../demo/README.md) to record or update it. + +The two `upcoming-demo-*` slots remain inside the hidden `upcoming-demos` +container. Replace their content and remove `hidden` when the demos are ready. +Use a real recording or iframe, +with the task, model, hardware, and timing scope stated in its caption. +Keep measured benchmark results distinct from individual demonstrations. + +Hugging Face resources are omitted until an upload is ready. Paper, code, +and citation links are already live. diff --git a/website/assets/app.js b/website/assets/app.js new file mode 100644 index 0000000..c171406 --- /dev/null +++ b/website/assets/app.js @@ -0,0 +1,35 @@ +if (typeof document !== 'undefined') { + const byId = (id) => document.getElementById(id); + byId('cite-link').addEventListener('click', () => { byId('citation').open = true; }); + const video = byId('coding-video'); + const play = byId('play-demo'); + play.hidden = false; + play.addEventListener('click', async () => { + try { + await video.play(); + video.focus(); + } catch { + byId('copy-status').textContent = 'Playback unavailable. Try the Open in full window link below the video.'; + } + }); + video.addEventListener('play', () => { play.hidden = true; }); + + document.querySelectorAll('[data-copy]').forEach((button) => { + button.addEventListener('click', async () => { + const content = byId(button.dataset.copy); + try { + await navigator.clipboard.writeText(content.textContent); + button.textContent = 'Copied'; + byId('copy-status').textContent = 'Copied to clipboard.'; + } catch { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(content); + selection.removeAllRanges(); + selection.addRange(range); + button.textContent = 'Text selected'; + byId('copy-status').textContent = 'Clipboard unavailable. Text selected; use your device’s copy command.'; + } + }); + }); +} diff --git a/website/assets/figures/fanoutqa-nemotron.svg b/website/assets/figures/fanoutqa-nemotron.svg new file mode 100644 index 0000000..850312a --- /dev/null +++ b/website/assets/figures/fanoutqa-nemotron.svg @@ -0,0 +1,215 @@ + + + + + +image/svg+xml + + +Matplotlib v3.11.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + +0 + + + + + +100 + + + + + +200 + + + + + +300 + + + + + +400 + + + +Median completion time (seconds) + + + + + + + + + +0 + + + + + + + + +20 + + + + + + + + +40 + + + + + + + + +60 + + + +Accuracy (%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamba + attention + + + + + +r64 + + + + + +r16 + + + + + +r8 + + +same-size text + + +Nemotron Nano 2 · 12B + + + + + + + + + diff --git a/website/assets/figures/fanoutqa-qwen.svg b/website/assets/figures/fanoutqa-qwen.svg new file mode 100644 index 0000000..8617ae9 --- /dev/null +++ b/website/assets/figures/fanoutqa-qwen.svg @@ -0,0 +1,233 @@ + + + + + +image/svg+xml + + +Matplotlib v3.11.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + +0 + + + + + +250 + + + + + +500 + + + + + +750 + + + + + +1000 + + + +Median completion time (seconds) + + + + + + + + + +0 + + + + + + + + +20 + + + + + + + + +40 + + + + + + + + +60 + + + +Accuracy (%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Global-attention Transformer + + + + + +r128 + + + + + +r64 + + + + + +r32 + + + + + +r16 + + + + + +r8 + + + + + +r4 + + +same-size text + + +Qwen 3 · 8B + + + + + + + + + diff --git a/website/assets/figures/longbench-nemotron.svg b/website/assets/figures/longbench-nemotron.svg new file mode 100644 index 0000000..34e163c --- /dev/null +++ b/website/assets/figures/longbench-nemotron.svg @@ -0,0 +1,247 @@ + + + + + +image/svg+xml + + +Matplotlib v3.11.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + +0 + + + + + +100 + + + + + +200 + + + + + +300 + + + + + +400 + + + +Median completion time (seconds) + + + + + + + + + +0 + + + + + + + + +20 + + + + + + + + +40 + + + + + + + + +60 + + + +Accuracy (%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mamba + attention + + + + + +r128 + + + + + +r16 + + + + + +r8 + + +same-size text + + +32K fixed + + +Nemotron Nano 2 · 12B + + + + + + + + + diff --git a/website/assets/figures/longbench-qwen.svg b/website/assets/figures/longbench-qwen.svg new file mode 100644 index 0000000..884c55d --- /dev/null +++ b/website/assets/figures/longbench-qwen.svg @@ -0,0 +1,252 @@ + + + + + +image/svg+xml + + +Matplotlib v3.11.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + +0 + + + + + +250 + + + + + +500 + + + + + +750 + + + + + +1000 + + + +Median completion time (seconds) + + + + + + + + + +0 + + + + + + + + +20 + + + + + + + + +40 + + + + + + + + +60 + + + +Accuracy (%) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Global-attention Transformer + + + + + +r64 + + + + + +r16 + + + + + +r4 + + +same-size text + + +64K fixed + + +Qwen 3 · 8B + + + + + + + + + diff --git a/website/assets/results.json b/website/assets/results.json new file mode 100644 index 0000000..d786790 --- /dev/null +++ b/website/assets/results.json @@ -0,0 +1,61 @@ +{ + "source": "https://arxiv.org/html/2609.32046v1", + "table": "Table 1: selected operating points, FanOutQA", + "tasks": 50, + "gpus": "8 H100", + "receiver_draws": 3, + "models": { + "qwen": { + "name": "Qwen 3", + "size": "8B", + "ratio": 4, + "text": { + "accuracy": 0.4067, + "seconds": 611.9 + }, + "cache": { + "accuracy": 0.5533, + "seconds": 191.9 + } + }, + "nemotron": { + "name": "Nemotron Nano 2", + "size": "12B", + "ratio": 8, + "text": { + "accuracy": 0.38666666666666666, + "seconds": 131.2367 + }, + "cache": { + "accuracy": 0.5, + "seconds": 101.6259 + } + }, + "gemma": { + "name": "Gemma 4", + "size": "12B", + "ratio": 4, + "text": { + "accuracy": 0.52, + "seconds": 603.1 + }, + "cache": { + "accuracy": 0.5933, + "seconds": 201.3 + } + }, + "ministral": { + "name": "Ministral 3", + "size": "14B", + "ratio": 32, + "text": { + "accuracy": 0.3867, + "seconds": 1536.9 + }, + "cache": { + "accuracy": 0.5933, + "seconds": 193.3 + } + } + } +} diff --git a/website/assets/styles.css b/website/assets/styles.css new file mode 100644 index 0000000..47b94f1 --- /dev/null +++ b/website/assets/styles.css @@ -0,0 +1,310 @@ +@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;450;500;550;600;650;700&family=Instrument+Serif&display=swap'); + +:root { --paper:#fafafa; --ink:#202828; --muted:#636b6b; --line:#dce3e2; --soft:#f0f5f4; --accent:#bde8e3; --rclc:#0f766e; --text:#995319; --fixed:#795697; --serif:'Instrument Serif',Georgia,serif; --sans:'DM Sans',Arial,sans-serif; } +* { box-sizing:border-box; } +html { scroll-behavior:smooth; scroll-padding-top:35px; } +body { margin:0; background:var(--paper); color:var(--ink); font:16px/1.65 var(--sans); -webkit-font-smoothing:antialiased; } +::selection { background:var(--accent); color:var(--ink); } +a { color:inherit; text-underline-offset:5px; } +button,select,input { font:inherit; } +button,a,input,select,summary { -webkit-tap-highlight-color:transparent; } +button,summary { cursor:pointer; } +button { color:inherit; } +button:disabled { cursor:default; } +:focus-visible { outline:3px solid var(--rclc); outline-offset:5px; } +h1,h2,h3,p,figure { margin:0; } +h1,h2,h3 { font-weight:500; } +em { font-family:var(--serif); font-style:normal; font-weight:400; } +.wrap { width:min(1240px,calc(100% - 112px)); margin-inline:auto; } +.site-header { min-height:100px; display:flex; align-items:center; justify-content:space-between; gap:24px; border-bottom:1px solid var(--line); } +.brand { display:inline-flex; align-items:center; gap:12px; font-size:21px; letter-spacing:-.7px; font-weight:650; text-decoration:none; white-space:nowrap; } +.brand-mark { display:grid; grid-template-columns:repeat(2,9px); gap:3px; } +.brand-mark i { width:9px; height:9px; border-radius:1px; background:var(--ink); } +.brand-mark i:last-child { background:var(--rclc); } +.site-header nav { display:flex; align-items:center; gap:30px; font-size:14px; } +.site-header nav a { text-decoration:none; color:var(--muted); } +.site-header nav a:hover,.text-link:hover { color:var(--rclc); } +.button { display:inline-flex; justify-content:center; align-items:center; gap:12px; padding:13px 21px; border:1px solid var(--ink); border-radius:5px; text-decoration:none; font-size:14px; font-weight:550; transition:background .16s,transform .16s; } +.button:hover { background:var(--soft); transform:translateY(-1px); } +.button.primary { background:var(--ink); color:white; } +.button.primary:hover { background:var(--rclc); } +.hero { display:grid; grid-template-columns:1fr 1fr; align-items:start; gap:74px; padding-block:80px 70px; } +.eyebrow,.mono { font-family:var(--sans); font-size:12px; font-weight:600; letter-spacing:.12em; } +.eyebrow { color:var(--muted); display:flex; align-items:center; gap:12px; } +h1 { font-size:clamp(56px,6vw,84px); font-weight:650; line-height:1.05; letter-spacing:-3px; margin:20px 0 12px; } +.hero-subtitle { font-size:26px; line-height:1.3; letter-spacing:-.6px; margin-bottom:27px; } +.hero-copy>.eyebrow { text-transform:uppercase; } +.hero-authors { margin-bottom:28px; } +.hero-authors ul { display:flex; flex-wrap:wrap; gap:3px 19px; list-style:none; padding:0; margin:0; font-size:19px; font-weight:550; line-height:1.5; } +.hero-authors li { white-space:nowrap; } +.hero-authors .affiliations { display:flex; align-items:center; flex-wrap:wrap; gap:10px 14px; margin-top:13px; font-size:14px; color:var(--muted); } +.affiliations a { display:flex; } +.affiliations img { display:block; height:28px; width:auto; } +.hero-intro { font-size:22px; line-height:1.45; letter-spacing:-.4px; } +.hero-intro .rclc-emphasis { color:var(--rclc); font-weight:700; } +.hero-description { margin-top:19px; max-width:490px; color:var(--muted); font-size:16px; line-height:1.7; } +.hero-actions { display:flex; flex-wrap:wrap; align-items:center; gap:24px; margin-top:29px; } +.text-link { font-size:14px; font-weight:500; } +.resource-links { display:flex; flex-wrap:wrap; align-items:center; gap:9px; margin-top:31px; font-size:14px; } +.resource-links a { display:inline-flex; align-items:center; justify-content:center; gap:6px; min-height:44px; padding:10px 15px; font-weight:650; text-decoration:none; border:1px solid var(--ink); border-radius:12px; background:#fff; color:var(--ink); transition:background .15s,color .15s,border-color .15s; } +.resource-links a:is(:hover,:focus-visible) { background:var(--ink); color:#fff; border-color:transparent; } +.resource-links a:first-child { background:var(--rclc); border-color:var(--rclc); color:#fff; } +.resource-links a:first-child:is(:hover,:focus-visible) { background:#fff; color:var(--rclc); border-color:transparent; } +.resource-links a:nth-child(2) { background:var(--ink); border-color:var(--ink); color:#fff; } +.resource-links a:nth-child(2):is(:hover,:focus-visible) { background:#fff; color:var(--ink); border-color:transparent; } +.resource-links a>span { font-size:10px; letter-spacing:.03em; color:inherit; } +strong,b { font-weight:650; } +.rclc-emphasis { color:var(--rclc); } +.text-emphasis { color:var(--text); } +.fixed-emphasis { color:var(--fixed); } +mark { background:var(--rclc); color:#fff; font-weight:700; padding:.08em .22em; -webkit-box-decoration-break:clone; box-decoration-break:clone; } +.contribution { margin-top:15px; font-size:14px; color:var(--muted); } +.contribution strong { color:var(--rclc); } +.hero-results { min-width:0; } +.hero-results .eyebrow { line-height:1.65; } +.chart-context { color:var(--muted); font-size:13px; line-height:1.5; margin-top:9px; max-width:400px; } +.hero-results .technical { margin-top:20px; font-size:13px; border-bottom:0; padding-bottom:0; } +.hero-results .technical p { font-size:12px; } +.comparison-plot { margin-top:20px; } +.plot-legend { display:flex; flex-wrap:wrap; gap:18px; font-size:13px; margin-bottom:18px; } +.plot-legend span { display:flex; align-items:center; gap:7px; } +.plot-legend i { width:16px; height:8px; background:#67726b; border-radius:2px; } +.plot-legend .rclc-emphasis i { background:var(--rclc); } +.paired-metrics { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; } +.chart-heading { border-bottom:1px solid var(--line); padding-bottom:15px; font-size:14px; line-height:1.4; } +.model-row { padding:14px 0; border-bottom:1px solid var(--line); } +.model-heading { margin-bottom:8px; } +.model-label { display:flex; justify-content:space-between; align-items:baseline; gap:8px; min-width:0; } +.accuracy-gain,.speedup { color:var(--rclc); font-size:14px; white-space:nowrap; } +.accuracy-gain abbr { text-decoration:none; } +.latency-bars { position:relative; } +.speed-guide { position:absolute; inset:0 58px 0 0; color:var(--rclc); pointer-events:none; } +.speedup { position:absolute; left:calc(var(--cache-end) + 6px); top:25px; font-size:12px; line-height:20px; } +.model-heading h3 { font-size:15px; font-weight:650; letter-spacing:0; } +.model-heading small { color:var(--muted); font-size:12px; font-weight:400; } +.bar-row { line-height:20px; display:grid; grid-template-columns:minmax(0,1fr) 50px; gap:8px; align-items:center; font-size:12px; } +.bar-row+.bar-row { margin-top:5px; } +.bar-row>b { text-align:right; font-variant-numeric:tabular-nums; } +.bar-track { border-left:1px solid #b5bfb7; background:repeating-linear-gradient(to right,transparent 0 calc(50% - 1px),#dce2dc calc(50% - 1px) 50%); } +.bar-track i { display:block; height:17px; background:#67726b; border-radius:0 2px 2px 0; } +.rclc-bar { color:var(--rclc); } +.rclc-bar i { background:var(--rclc); } +.chart-axis { margin:8px 58px 0 0; color:var(--muted); font-size:12px; position:relative; height:20px; } +.chart-axis span { position:absolute; transform:translateX(-50%); white-space:nowrap; } +.chart-axis span:nth-child(1) { left:0; } +.chart-axis span:nth-child(2) { left:50%; } +.chart-axis span:nth-child(3) { left:100%; } + +.abstract-section { display:grid; grid-template-columns:1fr 2.1fr; gap:60px; padding-block:64px; border-top:1px solid var(--line); } +.abstract-label h2 { margin:0 0 18px; } +.tldr { font-size:18px; line-height:1.75; } +.tldr p+p { margin-top:18px; } +.tldr strong { color:var(--rclc); } +.full-abstract { margin-top:26px; border-top:1px solid var(--line); padding-top:18px; } +.full-abstract>summary { color:var(--rclc); font-weight:600; } +.full-abstract[open]>summary { margin-bottom:24px; } +.abstract-text p { margin-bottom:18px; line-height:1.8; } +.abstract-text strong { font-weight:700; } +.abstract-text .rclc-emphasis { color:var(--rclc); text-decoration:underline; text-decoration-color:#79c9c1; text-decoration-thickness:3px; text-underline-offset:4px; } +.abstract-text .result-highlight { background:var(--rclc); color:#fff; padding:.08em .3em; -webkit-box-decoration-break:clone; box-decoration-break:clone; } +.abstract-text .abstract-note { color:var(--muted); font-size:12px; margin-bottom:0; line-height:1.65; } +.paper-curves { margin-top:30px; } +.curves-heading { max-width:810px; } +.curves-heading h3 { font-size:27px; margin-bottom:12px; } +.curves-heading p { color:var(--muted); font-size:15px; } +.curves-heading p+p { margin-top:16px; } +.curve-legend { display:flex; flex-wrap:wrap; gap:10px 24px; font-size:13px; margin-block:23px 5px; } +.curve-grid { display:grid; grid-template-columns:1fr 1fr; gap:34px; } +.curve-grid figure { min-width:0; } +.curve-grid img { width:100%; height:auto; display:block; } +.curve-grid figcaption { border-top:1px solid var(--line); padding-top:15px; color:var(--muted); font-size:13px; } +.curve-grid figcaption strong { color:var(--ink); } +.chain-results { margin-top:32px; border-block:1px solid var(--line); padding:20px 0; } +.chain-results>summary { font-size:20px; } +.chain-results[open]>summary { margin-bottom:22px; } +.curve-caption,.compression-note { color:var(--muted); font-size:12px; margin-top:26px; } +.compression-note { border-left:2px solid #9acac5; padding-left:16px; max-width:900px; } +.section { padding-block:64px; border-top:1px solid var(--line); } +.section-heading { display:grid; grid-template-columns:1fr 1fr; column-gap:90px; align-items:start; } +h2 { font-size:36px; line-height:1.2; letter-spacing:-1.1px; } +.section-heading>p:last-child { color:var(--muted); max-width:450px; margin-bottom:3px; } +h3 { font-size:20px; letter-spacing:-.5px; } +.method-heading { align-items:start; } +.method-heading>p:last-child { padding-top:4px; } +.method-heading strong { color:var(--ink); } +.handoff { margin-top:28px; border:1px solid var(--line); border-radius:16px; overflow:hidden; background:#fff; } +.handoff-flow { display:grid; grid-template-columns:1fr 1.6fr 1fr; align-items:center; gap:40px; padding:34px 48px; } +.handoff-party { text-align:center; } +.handoff-party h3 { font-size:32px; letter-spacing:-1px; } +.handoff-party p { margin-top:4px; font-size:14px; color:var(--muted); } +.handoff-receiver h3 { color:var(--rclc); } +.handoff-messages { display:grid; gap:27px; font-size:15px; font-weight:600; text-align:center; } +.handoff-messages span { font-size:11px; font-weight:450; margin-right:7px; } +.handoff-messages i { display:block; position:relative; height:2px; margin-top:10px; background:currentColor; } +.handoff-messages i::after { content:""; position:absolute; width:9px; height:9px; top:-3.5px; border-top:2px solid; border-right:2px solid; } +.handoff-request i::after { left:0; transform:rotate(-135deg); } +.handoff-state { color:var(--rclc); } +.handoff-state i::after { right:0; transform:rotate(45deg); } +.selector-note { display:grid; grid-template-columns:.75fr 1.15fr 1.6fr; gap:32px; align-items:center; padding:27px 36px; background:var(--rclc); color:#fff; } +.selector-note h3 { font-size:28px; letter-spacing:-.8px; } +.selector-note strong { font-size:16px; } +.selector-note p:last-child { font-size:14px; color:#e1f3f1; } +.technical { border-top:1px solid var(--line); border-bottom:1px solid var(--line); margin-top:32px; padding:15px 0; font-size:14px; } +summary { font-weight:500; } +.technical p { color:var(--muted); max-width:960px; margin-top:17px; } +.demo-section { padding:70px 0; border-block:1px solid var(--line); } +.demo-heading { display:grid; grid-template-columns:1fr 1fr; align-items:end; gap:85px; margin-bottom:32px; } +.demo-heading p { color:var(--muted); max-width:460px; } +.recorded-demo { background:#fff; border:1px solid var(--line); border-radius:14px; overflow:hidden; } +.recorded-demo+.recorded-demo { margin-top:48px; } +.coding-demo video { display:block; width:100%; height:auto; aspect-ratio:16/9; background:#f4f5f5; border-top:1px solid var(--line); } +.coding-demo .demo-evidence { margin-top:12px; } +.coding-demo .demo-evidence p { margin-top:12px; max-width:100ch; } +.demo-player { position:relative; } +.play-demo { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); border:0; border-radius:12px; padding:18px 24px; background:var(--rclc); color:#fff; font-size:18px; font-weight:700; white-space:nowrap; box-shadow:0 4px 24px #163b3526; } +.play-demo:hover { background:var(--ink); } +.play-demo span { margin-right:8px; } +.demo-description { display:flex; align-items:flex-start; justify-content:space-between; gap:36px; padding:27px 30px; } +.demo-description h3 { font-size:28px; margin:7px 0 10px; } +.demo-description p { color:var(--muted); font-size:14px; max-width:74ch; } +.demo-description .demo-type { font-size:11px; font-weight:650; text-transform:uppercase; letter-spacing:.09em; color:var(--rclc); } +.demo-outcomes { display:grid; grid-template-columns:1fr 1fr; padding:0 30px 26px; gap:30px; } +.demo-outcome { min-width:0; border-top:2px solid #67726b; padding-top:18px; } +.demo-outcome:first-child { border-color:var(--rclc); } +.demo-outcome header { display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:8px; margin-bottom:16px; } +.demo-outcome h4 { margin:0; font-size:16px; font-weight:650; } +.demo-outcome:first-child h4,.demo-outcome:first-child .outcome-time { color:var(--rclc); } +.outcome-time { font-size:23px; font-weight:600; font-variant-numeric:tabular-nums; } +.demo-outcome blockquote { margin:0; font-size:15px; line-height:1.75; } +.demo-evidence { margin-top:18px; color:var(--muted); font-size:12px; } +.demo-evidence>div { white-space:pre-wrap; margin-top:12px; } +.demo-replay { border-top:1px solid var(--line); } +.demo-replay>summary { display:flex; align-items:center; justify-content:center; gap:16px; padding:28px 30px; background:var(--rclc); color:#fff; font-size:24px; font-weight:700; line-height:1.3; list-style:none; } +.demo-replay>summary::-webkit-details-marker { display:none; } +.demo-replay>summary svg { flex-shrink:0; } +.demo-replay>summary:hover { background:#fff; color:var(--rclc); } +.demo-replay>summary:focus-visible { outline:3px solid var(--ink); outline-offset:-6px; } +.demo-replay-link { padding:20px 30px; } +.demo-window { border-top:1px solid var(--line); background:#fff; } +iframe { display:block; width:100%; height:815px; border:0; background:#fff; } +.demo-caption { color:var(--muted); font-size:12px; padding:17px 30px; border-top:1px solid var(--line); } +.upcoming-demos:not([hidden]) { display:grid; grid-template-columns:1fr 1fr; gap:24px; margin-top:28px; } +.demo-slot { border:1px solid var(--line); border-radius:14px; overflow:hidden; } +.demo-slot-surface { aspect-ratio:16/9; min-height:250px; display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-end; padding:28px; background:#f2f4f0; } +.demo-slot-surface>.mono { margin-bottom:auto; color:#758172; } +.demo-slot-surface h3 { font-size:22px; letter-spacing:-.4px; color:#556151; } +.demo-slot-surface p { color:var(--muted); font-size:14px; margin-top:7px; } +.results-note { color:var(--muted); font-size:12px; margin-top:21px; } +.code-section { background:var(--soft); border-block:1px solid var(--line); padding:75px 0; } +.code-layout { display:grid; grid-template-columns:.9fr 1.1fr; gap:90px; align-items:center; } +.code-layout h2 { margin:0 0 20px; } +.code-layout>div>p:not(.eyebrow) { color:var(--muted); max-width:370px; } +.code-layout .button { margin-top:25px; } +.code-layout .code-note { font-size:12px; margin-top:23px; } +.code-card { background:#162c2c; color:#e8f3f2; border-radius:12px; overflow:hidden; min-width:0; } +.code-bar { display:flex; align-items:center; justify-content:space-between; gap:10px; border-bottom:1px solid #345151; padding:12px 24px; color:#b6cdcb; font-size:12px; } +.copy-button { border:1px solid var(--line); background:transparent; border-radius:3px; padding:5px 10px; font-size:12px; } +.copy-button:hover { background:var(--accent); } +.code-card .copy-button { border:0; border-radius:7px; background:#2d4c4b; color:#e8f3f2; padding:6px 11px; } +.code-card .copy-button:hover { background:#e8f3f2; color:#162c2c; } +.code-card pre { padding:22px 24px 18px; line-height:1.65; } +.code-keyword { color:#bacafa; } +.code-function { color:#8bd9d1; } +.code-string { color:#e6ce99; } +.code-focus { display:block; background:#214442; border-left:2px solid #8bd9d1; margin:15px -24px 0; padding:12px 22px; } +pre { margin:0; padding:23px; overflow:auto; font:13px/1.85 'SFMono-Regular',Consolas,monospace; tab-size:2; } +.snippet-note { font-size:12px; line-height:1.65; color:#adc7c4; padding:0 24px 21px; } +.snippet-note a { display:block; margin-top:8px; color:#e8f3f2; } +.paper-section { display:grid; grid-template-columns:1.05fr .95fr; gap:80px; align-items:start; } +.paper-section h2 { font-size:33px; margin:19px 0; letter-spacing:-1px; } +.paper-section p:not(.eyebrow) { font-size:13px; color:var(--muted); } +.citation { border-block:1px solid var(--line); padding:18px 0; margin-top:38px; min-width:0; } +.citation summary { font-size:14px; } +.citation summary>span { float:right; color:var(--muted); font-size:12px; } +.citation pre { padding:18px 0 0; font-size:12px; } +.citation button { margin-top:16px; } +.site-footer { padding:27px 0; border-top:1px solid var(--line); display:flex; align-items:center; justify-content:space-between; gap:20px; color:var(--muted); font-size:12px; } +.site-footer .brand { color:var(--ink); font-size:17px; } +.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; } +.skip-link { position:fixed; top:-100px; left:15px; z-index:10; background:var(--accent); padding:10px; } +.skip-link:focus { top:10px; } +@media(min-width:1500px) { .hero { padding-block:95px 85px; } } +@media(max-width:1100px) { +.wrap { width:calc(100% - 64px); } +.hero { gap:38px; padding-block:60px; } +h1 { font-size:64px; } +.section-heading,.demo-heading { gap:40px; } +.code-layout { gap:45px; } +} +@media(min-width:521px) { body { zoom:.945; } } +@media(min-width:801px) { +body { zoom:.85; } +.wrap { width:min(1240px,calc(100% - 144px)); } +} +@media(max-width:800px) { + +.hero-results { width:100%; max-width:600px; } +.abstract-section { grid-template-columns:1fr; gap:28px; padding-block:45px; } +.abstract-label h2 { margin-bottom:13px; } +.curve-grid { grid-template-columns:1fr; gap:30px; } +.site-header { min-height:80px; } +.site-header nav { gap:19px; } +.hero { gap:35px; grid-template-columns:1fr; padding-block:45px; } +.hero-copy { max-width:600px; } +h1 { font-size:72px; } +.section { padding-block:60px; } +.section-heading,.demo-heading { grid-template-columns:1fr; gap:19px; } +.section-heading>p:last-child { max-width:none; } +.handoff-flow { gap:24px; padding:35px 24px; } +.selector-note { grid-template-columns:1fr 2fr; gap:10px 24px; padding:25px; } +.selector-note h3 { grid-row:span 2; align-self:start; } +.selector-note p:last-child { grid-column:2; } +.code-layout,.paper-section { grid-template-columns:1fr; gap:30px; } +.code-layout>div>p:not(.eyebrow) { max-width:none; } +.citation { margin-top:0; } +iframe { height:890px; } +.site-footer>span { display:none; } +.upcoming-demos:not([hidden]) { grid-template-columns:1fr; } +} +@media(max-width:520px) { +.hero-authors ul { font-size:17px; gap:4px 17px; } + +.curve-legend { gap:8px 15px; font-size:12px; } +.wrap { width:calc(100% - 36px); } +.site-header { min-height:74px; gap:12px; } +.brand { font-size:18px; gap:8px; } +.brand-mark { grid-template-columns:repeat(2,7px); gap:2px; } +.brand-mark i { width:7px; height:7px; } +.site-header nav { gap:12px; font-size:12px; } +h1 { font-size:56px; letter-spacing:-2px; } +.hero-subtitle { font-size:23px; } +.hero-intro { font-size:21px; } +.hero-description { font-size:16px; } +.hero-actions { gap:20px; } +.resource-links { gap:8px; } +.resource-links a { padding-inline:12px; } +h2 { font-size:32px; } +.handoff-flow { grid-template-columns:1fr 1.5fr 1fr; gap:12px; padding:30px 16px; } +.handoff-party h3 { font-size:20px; letter-spacing:-.4px; } +.handoff-party p { font-size:12px; line-height:1.5; } +.handoff-messages { font-size:12px; gap:24px; } +.handoff-messages span { display:none; } +.selector-note { grid-template-columns:1fr; padding:24px; gap:10px; } +.selector-note h3 { grid-row:auto; } +.selector-note p:last-child { grid-column:auto; } +.demo-section { padding-top:51px; } +.demo-outcomes { grid-template-columns:1fr; gap:26px; padding:0 20px 24px; } +.demo-replay>summary { padding:24px 20px; gap:12px; font-size:21px; } +.demo-replay-link { padding-inline:20px; } +.demo-caption { padding:17px 20px; } +.demo-description { flex-direction:column; gap:18px; padding:22px 20px; } +.demo-slot-surface { min-height:220px; padding:24px; } +.code-section { padding-block:50px; } +.paper-section h2 { font-size:32px; } +.site-footer { flex-wrap:wrap; font-size:12px; } +.citation pre { font-size:12px; } +} +@media(prefers-reduced-motion:reduce) { +html { scroll-behavior:auto; } +*,*::before,*::after { transition:none!important; animation:none!important; } +} diff --git a/website/check.mjs b/website/check.mjs new file mode 100644 index 0000000..9eec915 --- /dev/null +++ b/website/check.mjs @@ -0,0 +1,112 @@ +import assert from 'node:assert/strict'; +import { readFileSync, readdirSync, statSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { resolve, dirname } from 'node:path'; +import { gzipSync } from 'node:zlib'; + +const root = dirname(fileURLToPath(import.meta.url)); +const dist = resolve(root, 'assets'); +const project = resolve(root, '..'); +const read = (name) => readFileSync(resolve(dist, name), 'utf8'); +const html = readFileSync(resolve(project, 'index.html'), 'utf8'); +const css = read('styles.css'); +const js = read('app.js'); +const data = JSON.parse(read('results.json')); +const ids = [...html.matchAll(/\bid="([^"]+)"/g)].map((match) => match[1]); +assert.equal(new Set(ids).size, ids.length, 'Duplicate HTML IDs'); +assert.equal((html.match(//); +assert.match(html, /name="viewport"/); +assert.match(html, /CacheBack \|/); +const sectionOrder = ['result-summary', 'abstract', 'idea', 'results', 'demo', 'code', 'paper']; +sectionOrder.slice(1).forEach((id, i) => { + assert.ok(html.indexOf(`id="${sectionOrder[i]}"`) < html.indexOf(`id="${id}"`), `Section order: ${id}`); +}); +assert.equal((html.match(/<img src="website\/assets\/figures\//g) || []).length, 4); + +for (const [, url] of html.matchAll(/(?:href|src|poster)="([^"]+)"/g)) { + if (/^(https:|data:)/.test(url)) continue; + if (url.startsWith('#')) { + assert.ok(url === '#' || ids.includes(url.slice(1)), `Missing anchor: ${url}`); + } else { + const pathname = new URL(url, 'https://example.invalid/rclc/').pathname; + assert.ok(pathname.startsWith('/rclc/'), `Link escapes the project path: ${url}`); + const path = pathname.slice('/rclc/'.length); + const asset = path.endsWith('/') ? `${path}index.html` : path; + assert.ok(statSync(resolve(project, asset)).isFile(), `Missing local asset: ${url}`); + } +} +for (const [, id] of js.matchAll(/byId\('([^']+)'\)/g)) { + assert.ok(ids.includes(id), `Script refers to missing element: ${id}`); +} +assert.equal(data.models.gemma.ratio, 4, 'Use the paper selected point, not Gemma maximum accuracy'); +assert.equal(data.models.ministral.ratio, 32); +assert.equal(data.models.nemotron.ratio, 8); +for (const model of Object.values(data.models)) { + assert.ok(model.ratio >= 2 && Number.isInteger(model.ratio)); + for (const arm of [model.text, model.cache]) { + assert.ok(arm.accuracy >= 0 && arm.accuracy <= 1); + assert.ok(arm.seconds > 0 && Number.isFinite(arm.seconds)); + } +} +// The displayed bar lengths must encode the published data on shared zero-based scales. +const plottedWidths = [...html.matchAll(/<i style="width:([\d.]+)%"/g)].map(match => Number(match[1])); +const expectedWidths = Object.values(data.models).flatMap(model => [ + model.text.accuracy * 100, model.cache.accuracy * 100, + model.text.seconds / 1600 * 100, model.cache.seconds / 1600 * 100, +]); +assert.equal(plottedWidths.length, 16); +plottedWidths.forEach((width, i) => assert.ok(Math.abs(width - expectedWidths[i]) < 0.0001)); +const gains = [...html.matchAll(/class="accuracy-gain">\+([\d.]+)<abbr/g)].map(match => Number(match[1])); +const speedups = [...html.matchAll(/<strong class="speedup">([\d.]+)× faster<\/strong>/g)].map(match => Number(match[1])); +assert.deepEqual(gains, Object.values(data.models).map(m => Number(((m.cache.accuracy - m.text.accuracy) * 100).toFixed(1)))); +assert.deepEqual(speedups, Object.values(data.models).map(m => Number((m.text.seconds / m.cache.seconds).toFixed(1)))); +assert.doesNotMatch(html, /name="model"/); +assert.match(html, /This recording is an example, not a benchmark/); +assert.match(html, /93\.75% removed/); +assert.match(html, /loading="lazy" sandbox="allow-scripts allow-same-origin"/); +assert.match(css, /prefers-reduced-motion:reduce/); +assert.match(css, /:focus-visible/); +assert.match(html, /id="bibtex"/); +assert.match(html, /<iframe[^>]+src="demo\/index\.html\?v=fit-3"/); +assert.match(readFileSync(resolve(project, 'demo/index.html'), 'utf8'), /fetch\("trace-w4\.json"\)/); +const recording = JSON.parse(readFileSync(resolve(project, 'demo/trace-w4.json'), 'utf8')).cases[0]; +const plain = text => text.replace(/<[^>]+>/g, '').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&').replace(/\s+/g, ' ').trim(); +assert.ok(html.includes(`<h3 id="booking-title">${recording.question}</h3>`)); +for (const method of ['rclc', 'text']) { + const [answer, ...evidence] = recording[method].answer.text.split('\n\n'); + for (const [part, tag, expected] of [['answer', 'p', answer], ['evidence', 'div', evidence.join('\n\n')]]) { + const displayed = html.match(new RegExp(`<${tag} id="demo-${method}-${part}">([\\s\\S]*?)</${tag}>`)); + assert.ok(displayed, `Missing recorded ${method} ${part}`); + assert.equal(plain(displayed[1]), plain(expected.replace(/\*\*/g, '')), `Preserve the recorded ${method} ${part}`); + } + assert.ok(html.includes(`id="demo-${method}-time">${recording[method].seconds.toFixed(2)} s</span>`), `Measured ${method} completion time`); +} + +const coding = JSON.parse(readFileSync(resolve(project, 'demo/coding/evidence.json'), 'utf8')).arms; +assert.ok(html.indexOf('id="coding-demo"') < html.indexOf('id="booking-demo"'), 'Coding video is the main demo'); +const video = html.match(/<video\b[^>]*>[\s\S]*?<\/video>/)[0]; +assert.match(video, /controls playsinline preload="none"/); +assert.doesNotMatch(video, /autoplay/); +assert.match(video, /src="demo\/coding\/rclc-cacheback-4k.mp4"/); +assert.match(video, /poster="demo\/coding\/poster.jpg"/); +assert.equal(readFileSync(resolve(project, 'demo/coding/rclc-cacheback-4k.mp4')).toString('ascii', 4, 8), 'ftyp', 'Real MP4, not a Git LFS pointer'); +assert.ok(html.includes(`${(coding.text.seconds / coding.latent.seconds).toFixed(2)}× faster`)); +assert.ok(html.includes(`CacheBack ${coding.latent.seconds.toFixed(2)} s; text ${coding.text.seconds.toFixed(2)} s`)); +assert.equal(coding.latent.patch, coding.text.patch); +for (const arm of Object.values(coding)) { + assert.equal(arm.new_tests_passed + arm.regression_tests_passed, 88); + assert.equal(arm.failed_tests, 0); +} + +assert.ok(gzipSync(js).length < 6000, 'Keep page JavaScript below 6 KB gzip'); +assert.ok(!/[\u2013\u2014\u200b-\u200d\ufeff\u2060]/u.test(html), 'Banned punctuation or invisible character: index.html'); +let bytes = Buffer.byteLength(html); +for (const name of readdirSync(dist, { recursive: true })) { + if (!statSync(resolve(dist, name)).isFile()) continue; + const content = readFileSync(resolve(dist, name)); + bytes += content.length; + assert.ok(!/[\u2013\u2014\u200b-\u200d\ufeff\u2060]/u.test(content.toString()), `Banned punctuation or invisible character: ${name}`); +} +assert.ok(bytes < 100000, 'Keep first-party static assets below 100 KB'); +console.log(`Website checks passed: anchors, assets, benchmark points, accessibility hooks; ${bytes} bytes, ${gzipSync(js).length} bytes gzipped JS.`);