Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions .nojekyll
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
17 changes: 13 additions & 4 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -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 <https://maxr0ssi.github.io/rclc/demo/> and press **Run**. To serve it
locally, run this from the repository root and open <http://127.0.0.1:8765/demo/>:

Expand All @@ -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
Expand All @@ -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
Expand Down
54 changes: 54 additions & 0 deletions demo/coding/evidence.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Binary file added demo/coding/poster.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/coding/rclc-cacheback-4k.mp4
Binary file not shown.
34 changes: 24 additions & 10 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light">
<title>CacheBack</title>
<title>CacheBack | Booking relay</title>
<style>
* { box-sizing:border-box; }
body { margin:0; background:#fff; color:#222; font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
Expand All @@ -31,11 +31,12 @@
.method-heading { display:flex; align-items:baseline; gap:14px; margin-bottom:12px; }
.time { font-size:13px; color:#666; font-variant-numeric:tabular-nums; }
.chunk { border-top:1px solid #ddd; padding:12px 10px 0 0; height:230px; overflow:auto; scrollbar-width:thin; white-space:pre-wrap; font-size:12px; line-height:1.7; overflow-wrap:anywhere; }
.chunk::before { display:none; content:attr(data-agent); font:600 12px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; white-space:normal; margin-bottom:12px; }
.source { font-family:Georgia,"Times New Roman",serif; font-size:13px; }
.answer { font-size:13px; }
.answer-check { margin:14px 0 0; font-size:12px; font-weight:600; }
strong { font-weight:600; }
mark { color:inherit; background:linear-gradient(#d8f1ce,#d8f1ce) left/100% 100% no-repeat; box-decoration-break:clone; -webkit-box-decoration-break:clone; }
mark { color:inherit; background:linear-gradient(#bde8e3,#bde8e3) left/100% 100% no-repeat; box-decoration-break:clone; -webkit-box-decoration-break:clone; }
.revealing mark { animation:highlight .18s ease-out both; }
@keyframes highlight { from { background-size:0% 100%; } to { background-size:100% 100%; } }
footer { display:flex; justify-content:space-between; gap:20px; margin-top:28px; font-size:11px; color:#777; }
Expand All @@ -46,11 +47,13 @@
@media(max-width:800px) {
main { width:calc(100% - 32px); margin:24px auto; }
.question { align-items:flex-start; flex-direction:column; gap:18px; margin:24px 0; }
.flow { overflow-x:auto; padding-bottom:8px; }
.topology,.row { min-width:920px; gap:24px; }
.topology { display:none; }
.row { grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; }
.chunk::before { display:block; }
.chunk { height:260px; }
footer { flex-direction:column; gap:8px; }
}
@media(max-width:520px) { .row { grid-template-columns:1fr; } }
@media(prefers-reduced-motion:reduce) { .revealing mark { animation:none; } }
</style>
</head>
Expand All @@ -70,7 +73,7 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
</div>
</section>
<p id="error" role="alert" hidden></p>
<div class="flow" tabindex="0" aria-label="Four-agent relay. Scroll horizontally on smaller screens.">
<div class="flow" aria-label="Four-agent relay">
<p class="flow-label">Chain of agents</p>
<ol class="topology" aria-label="Agent topology">
<li><strong>Agent 1</strong><span>booking-confirmation.txt</span></li>
Expand All @@ -79,7 +82,7 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
<li><strong>Agent 4</strong><span>Final answer</span></li>
</ol>
<section class="method" aria-labelledby="rclc-title">
<div class="method-heading"><h2 id="rclc-title">RCLC</h2><span class="time" id="rclc-time"></span></div>
<div class="method-heading"><h2 id="rclc-title">CacheBack</h2><span class="time" id="rclc-time"></span></div>
<div class="row" id="rclc-row"></div>
</section>
<section class="method" aria-labelledby="text-title">
Expand All @@ -95,6 +98,14 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
</main>
<script>
const $ = id => document.getElementById(id);
const host = window.frameElement;
if (host) {
const main = document.querySelector("main");
new ResizeObserver(() => {
const bottomMargin = parseFloat(getComputedStyle(main).marginBottom);
host.style.height = `${Math.ceil(main.offsetTop + main.offsetHeight + bottomMargin) + 1}px`;
}).observe(main);
}
let trace, playing = false, frame;
const phases = {rclc:0, text:0};
const reviewedAnswer = 'Maya should collect her pass at **Harbour Hub** on Friday, and she should bring a **passport** as her ID.\n\n**Evidence:**\n- "Reference J7 has moved to Harbour Hub."\n- "Harbour Hub requires a passport for replacement building pass collections."';
Expand Down Expand Up @@ -143,6 +154,7 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
trace.sources.forEach((source, i) => {
const chunk = element("div"); chunk.className = "chunk";
const name = document.querySelectorAll(".topology li span")[i].textContent;
chunk.dataset.agent = `Agent ${i + 1} · ${name}`;
chunk.tabIndex = 0; chunk.setAttribute("aria-label", `${method === "rclc" ? "Source document" : "Text handoff"} ${i + 1}: ${name}`);
if (method === "rclc") {
chunk.classList.add("source");
Expand All @@ -157,7 +169,8 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
chunk.scrollTop = method === "text" && phase === i + 1 ? chunk.scrollHeight : scroll[i] || 0;
});
const answer = element("div"); answer.className = "chunk answer";
answer.tabIndex = 0; answer.setAttribute("aria-label", `${method === "rclc" ? "RCLC" : "Text"} final answer`);
answer.dataset.agent = "Agent 4 · Final answer";
answer.tabIndex = 0; answer.setAttribute("aria-label", `${method === "rclc" ? "CacheBack" : "Text"} final answer`);
if (phase === 4) {
prose(answer, recorded[method].answer.text || "(empty answer)");
if (recorded[method].answer.stop === "limit") answer.append(element("p", "Answer reached its token limit."));
Expand All @@ -168,7 +181,7 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
}
row.append(answer); answer.scrollTop = phase === 4 ? answer.scrollHeight : scroll[3] || 0;
$(`${method}-time`).textContent = phase === 4 ? `${recorded[method].seconds.toFixed(2)} s` : "";
$("status").textContent = `${method === "rclc" ? "RCLC" : "Text"}: ${phase === 4 ? "final answer received." : `${phase} of 3 handoffs received.`}`;
$("status").textContent = `${method === "rclc" ? "CacheBack" : "Text"}: ${phase === 4 ? "final answer received." : `${phase} of 3 handoffs received.`}`;
}

function play() {
Expand All @@ -182,8 +195,9 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
deadlines[method] = [...run.hops.map(hop => elapsed += hop.seconds * 1000), run.seconds * 1000];
phases[method] = 0; render(method);
}
const start = performance.now();
let start;
function advance(now) {
start ??= now;
const elapsed = (now - start) * speed;
for (const method of ["rclc", "text"]) {
const phase = deadlines[method].filter(ms => elapsed >= ms).length;
Expand All @@ -205,7 +219,7 @@ <h1 id="question">Where should Maya collect her pass on Friday, and what ID shou
$("play").textContent = "Replay";
}
}
advance(start);
frame = requestAnimationFrame(advance);
}
$("play").addEventListener("click", play);
$("clear").addEventListener("click", () => {
Expand Down
Loading
Loading