From 06d0cd2222f9fa52a86cd7330b247c03cf1bb165 Mon Sep 17 00:00:00 2001 From: Max Rossi <113717219+maxr0ssi@users.noreply.github.com> Date: Thu, 1 Oct 2026 13:02:49 -0400 Subject: [PATCH 1/4] Update project links for the CacheBack organization --- README.md | 46 ++++++++++++++++++++++----------------------- demo/README.md | 2 +- demo/index.html | 2 +- docs/development.md | 2 +- index.html | 18 +++++++++--------- pyproject.toml | 6 +++--- website/README.md | 9 +++++---- 7 files changed, 43 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index cc0ad36..35e3592 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*. -**[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) +**[Website](https://agentcacheback.github.io/)** · **[Paper](https://arxiv.org/abs/2609.32046)** · [Python API](https://github.com/agentcacheback/rclc/blob/main/docs/api.md) · [Paper replication](https://github.com/agentcacheback/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 @@ -12,7 +12,7 @@ sender's context at the receiver, increasing memory use and context length. in a request. **CacheBack** is a training-free selector that uses attention to that request to choose which sender positions enter the handoff. -![FanOutQA accuracy and latency across four model families, alongside receiver-conditioned selection of sender state.](https://raw.githubusercontent.com/maxr0ssi/rclc/main/docs/assets/cacheback-overview.png) +![FanOutQA accuracy and latency across four model families, alongside receiver-conditioned selection of sender state.](https://raw.githubusercontent.com/agentcacheback/rclc/main/docs/assets/cacheback-overview.png) *Highest-accuracy CacheBack setting per family versus same-size text on FanOutQA, with 50 concurrent tasks on eight H100 GPUs. Operating points differ by family. @@ -20,9 +20,9 @@ Right: the receiver query guides which sender positions enter the handoff.* ## Watch the demo -**[▶ Watch the 36-second coding demo](https://maxr0ssi.github.io/rclc/#demo)** +**[▶ Watch the 36-second coding demo](https://agentcacheback.github.io/#demo)** -[![CacheBack finishes a Django fix while text workers are still writing reports.](demo/coding/poster.jpg)](https://maxr0ssi.github.io/rclc/#demo) +[![CacheBack finishes a Django fix while text workers are still writing reports.](demo/coding/poster.jpg)](https://agentcacheback.github.io/#demo) 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**. @@ -31,7 +31,7 @@ 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). -Also explore the [interactive booking relay](https://maxr0ssi.github.io/rclc/demo/) +Also explore the [interactive booking relay](https://agentcacheback.github.io/demo/) to inspect selected positions and text messages at each handoff. Its [demo guide](demo/README.md) covers recording your own. @@ -54,10 +54,10 @@ python -m pip install . ``` The engine needs the capture connector; see the -[vLLM setup](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#vllm-default) and -[integration example](https://github.com/maxr0ssi/rclc/blob/main/examples/vllm_transfer.py). +[vLLM setup](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#vllm-default) and +[integration example](https://github.com/agentcacheback/rclc/blob/main/examples/vllm_transfer.py). vLLM 0.26.0 is an explicit opt-in. Both pins passed a Qwen3-8B GPU journey on an -A100 ([results](https://github.com/maxr0ssi/rclc/blob/main/docs/development.md#gpu-validation)). +A100 ([results](https://github.com/agentcacheback/rclc/blob/main/docs/development.md#gpu-validation)). ## Use it @@ -92,23 +92,23 @@ supported. - Outside an async function or notebook, use `rcc.transfer_sync(...)` with the same options. Native generation and `update` block; finish each operation before reusing the model - ([concurrency](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#concurrency)). + ([concurrency](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#concurrency)). - vLLM: `rcc.bind(llm, backend="vllm", messages=history)` with the - [engine setup](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#vllm-default). + [engine setup](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#vllm-default). Local dense Qwen3 weights are required; hosted chat APIs are not supported. - `await agent_y.append(new_text_or_ids)` adds a tool result without losing received state; `await agent_y.inputs()` continues locally; `save`/`load` snapshot retained state - ([continuation and snapshots](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#continue-append-and-resume)). + ([continuation and snapshots](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#continue-append-and-resume)). - `agent_y.inspect()` shows queued handoffs; `record_selection=True` plus `agent_y.selection_html()` shows what survived compression - ([selection inspection](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#inspect-check-and-view-selection)). + ([selection inspection](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#inspect-check-and-view-selection)). - `python -m rcc doctor` (`--backend vllm` for CUDA) or `rcc.check(agent_y)` checks setup without running a model or downloading weights. -Examples: [quickstart](https://github.com/maxr0ssi/rclc/blob/main/examples/quickstart.py), -[cached conversation](https://github.com/maxr0ssi/rclc/blob/main/examples/existing_agents.py), -[Colab GPU notebook](https://github.com/maxr0ssi/rclc/blob/main/examples/colab.ipynb). +Examples: [quickstart](https://github.com/agentcacheback/rclc/blob/main/examples/quickstart.py), +[cached conversation](https://github.com/agentcacheback/rclc/blob/main/examples/existing_agents.py), +[Colab GPU notebook](https://github.com/agentcacheback/rclc/blob/main/examples/colab.ipynb). ### Options @@ -125,10 +125,10 @@ await rcc.transfer(senders, receivers, requests, Each argument is one item or a list: two senders, two receivers and two requests produce four deliveries of two messages each. See -[budgets](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#relative-and-bounded-budgets), -[selectors](https://github.com/maxr0ssi/rclc/blob/main/docs/selectors.md), -[reasoning](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#reasoning) and -[representations](https://github.com/maxr0ssi/rclc/blob/main/docs/api.md#representations). +[budgets](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#relative-and-bounded-budgets), +[selectors](https://github.com/agentcacheback/rclc/blob/main/docs/selectors.md), +[reasoning](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#reasoning) and +[representations](https://github.com/agentcacheback/rclc/blob/main/docs/api.md#representations). ## Results in the paper @@ -142,10 +142,10 @@ the paper's benchmark results, not performance claims for this package. ## Bring your own selector or benchmark Pass any callable as `selector=` and compare it with CacheBack using the -[small evaluation script](https://github.com/maxr0ssi/rclc/blob/main/CONTRIBUTING.md#2-run-a-small-comparison). +[small evaluation script](https://github.com/agentcacheback/rclc/blob/main/CONTRIBUTING.md#2-run-a-small-comparison). The paper's **FanOutQA** and **LongBench v2** panels need separate integration. New benchmarks and negative results are welcome; see -[Contributing](https://github.com/maxr0ssi/rclc/blob/main/CONTRIBUTING.md). +[Contributing](https://github.com/agentcacheback/rclc/blob/main/CONTRIBUTING.md). ## Branches @@ -155,9 +155,9 @@ New benchmarks and negative results are welcome; see | `paper` | Four-family runtimes, both benchmarks, twelve experiment configs, pinned serving stacks, data bundles | The `paper-v1` tag fixes the replication snapshot; see the -[replication guide](https://github.com/maxr0ssi/rclc/blob/main/docs/replication.md). +[replication guide](https://github.com/agentcacheback/rclc/blob/main/docs/replication.md). For checks, tests and GPU validation, see the -[development guide](https://github.com/maxr0ssi/rclc/blob/main/docs/development.md). +[development guide](https://github.com/agentcacheback/rclc/blob/main/docs/development.md). ## License and citation diff --git a/demo/README.md b/demo/README.md index 8b94c23..aff1fbd 100644 --- a/demo/README.md +++ b/demo/README.md @@ -10,7 +10,7 @@ Harbour Hub, whose passport requirement overrides the original desk's ID advice. 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 +Open and press **Run**. To serve it locally, run this from the repository root and open : ```bash diff --git a/demo/index.html b/demo/index.html index e412c8a..290bc82 100644 --- a/demo/index.html +++ b/demo/index.html @@ -93,7 +93,7 @@

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

@@ -39,7 +39,7 @@

CacheBack

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.

+
Figure 2: sending every agent's full cache exceeds the receiver's context limit. CacheBack sends selected positions and latent steps, leaving room for the receiver's prompt.
Figure 2. Select what the receiver needs, instead of accumulating every sender’s context.
+

TL;DR

Agents can share internal model state instead of writing reports. Sending all of it, however, overwhelms the receiver.

The receiver asks for what it needs. CacheBack uses attention to that request to select which state gets sent. Simple, robust, and training-free.

-

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.

+

Results

-

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.

+

FanOutQA

Three senders read separate sources; one receiver answers. Higher is more accurate; left is faster. CacheBack points vary message size; text points vary sender size. rX keeps 1/X of the sender’s positions: r4 keeps 25%.

● ━ 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.
+
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.
+
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.
-
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”.

+

LongBench v2 Easy

Four agents read a long document in sequence, passing state to a final answering agent. Relative budgets (rX) grow with context; fixed budgets (32K, 64K) cap message positions. Stronger compression can reduce accuracy.

● ━ 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.
+
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.
+
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.
-
-

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.

+
+

50 concurrent tasks on 8 H100s; accuracy averaged over three receiver draws. Completion includes queueing. Full setup and results in the paper.

-

Watch the demo

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

+

Watch the demo

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.

-

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

+

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
+    

Use CacheBack

Install rclc, bind your agents, and pass the receiver’s request.

Code and documentation

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

Python
import rclc
 
-sender = rcc.bind(
+sender = rclc.bind(
     model, tokenizer,
     messages=sender_history, backend="hf",
 )
-receiver = rcc.bind(
+receiver = rclc.bind(
     model, tokenizer,
     messages=receiver_history, backend="hf",
 )
-await rcc.transfer(
+await rclc.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,
+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

@misc{rossi2026cacheback,
   title = {Receiver-Conditioned Latent Communication
            gives 94\% CacheBack},
   author = {Rossi, Maximillian and Raghunath, Prajwal
@@ -194,9 +150,9 @@ 

CacheBack

archivePrefix = {arXiv}, primaryClass = {cs.AI}, url = {https://arxiv.org/abs/2609.32046} -}
+}
- +
diff --git a/pyproject.toml b/pyproject.toml index 2bec38e..4281976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,18 +35,18 @@ dependencies = [ ] [project.urls] -Repository = "https://github.com/agentcacheback/rclc" -Documentation = "https://github.com/agentcacheback/rclc/blob/main/docs/api.md" -Issues = "https://github.com/agentcacheback/rclc/issues" +Repository = "https://github.com/agentcacheback/cacheback" +Documentation = "https://github.com/agentcacheback/cacheback/blob/main/docs/api.md" +Issues = "https://github.com/agentcacheback/cacheback/issues" [project.optional-dependencies] dev = ["ruff==0.15.20", "pyright==1.1.411", "pytest>=8.0"] [tool.hatch.build.targets.sdist] -include = ["/src/rcc", "/README.md", "/LICENSE", "/pyproject.toml"] +include = ["/src/rclc", "/README.md", "/LICENSE", "/pyproject.toml"] [tool.hatch.build.targets.wheel] -packages = ["src/rcc"] +packages = ["src/rclc"] [tool.ruff] line-length = 100 @@ -68,8 +68,8 @@ ignore = ["D203", "D213", "SIM105", "SIM102", "SIM108", "SIM103", "SIM118"] convention = "google" [tool.pyright] -include = ["src/rcc"] -strict = ["src/rcc"] +include = ["src/rclc"] +strict = ["src/rclc"] pythonVersion = "3.10" venvPath = "." venv = ".venv" diff --git a/scripts/validate_vllm.py b/scripts/validate_vllm.py index 8a35fa2..0db4d65 100644 --- a/scripts/validate_vllm.py +++ b/scripts/validate_vllm.py @@ -10,11 +10,11 @@ import torch -from rcc import Delivery, SenderState, bind, latent_mass, transfer -from rcc._cache import cache_kv -from rcc.capture.connector import require_runtime -from rcc.reasoning import ReasoningContext -from rcc.vllm import discard_capture, request_capture, sender_from_vllm +from rclc import Delivery, SenderState, bind, latent_mass, transfer +from rclc._cache import cache_kv +from rclc.capture.connector import require_runtime +from rclc.reasoning import ReasoningContext +from rclc.vllm import discard_capture, request_capture, sender_from_vllm async def _latent_prompts( @@ -174,7 +174,7 @@ async def run( kv_transfer_config={ "kv_connector": "RCCCaptureConnector", "kv_role": "kv_both", - "kv_connector_module_path": "rcc.capture.connector", + "kv_connector_module_path": "rclc.capture.connector", "kv_connector_extra_config": {"allow_unstable": allow_unstable}, }, **({"attention_backend": "FLASH_ATTN"} if pin == "0.26.0" else {}), diff --git a/src/rcc/__init__.py b/src/rclc/__init__.py similarity index 58% rename from src/rcc/__init__.py rename to src/rclc/__init__.py index 63cb473..8795d5a 100644 --- a/src/rcc/__init__.py +++ b/src/rclc/__init__.py @@ -1,10 +1,10 @@ """Receiver-conditioned state transfer between existing agents.""" -from rcc.diagnostics import check -from rcc.hf import Agent, HFReceiver, sender_from_hf -from rcc.latent import latent_mass, latent_mass_sync -from rcc.message import Message -from rcc.transport import Delivery, SenderState, transfer, transfer_sync +from rclc.diagnostics import check +from rclc.hf import Agent, HFReceiver, sender_from_hf +from rclc.latent import latent_mass, latent_mass_sync +from rclc.message import Message +from rclc.transport import Delivery, SenderState, transfer, transfer_sync bind = Agent diff --git a/src/rcc/__main__.py b/src/rclc/__main__.py similarity index 85% rename from src/rcc/__main__.py rename to src/rclc/__main__.py index 04ae9e6..f6b67b0 100644 --- a/src/rcc/__main__.py +++ b/src/rclc/__main__.py @@ -4,12 +4,12 @@ import json import sys -from rcc.diagnostics import check +from rclc.diagnostics import check def main() -> None: """Write a JSON diagnostic and exit nonzero when setup needs attention.""" - parser = argparse.ArgumentParser(prog="python -m rcc", description=__doc__) + parser = argparse.ArgumentParser(prog="python -m rclc", description=__doc__) parser.add_argument("command", choices=("doctor",)) parser.add_argument("--backend", choices=("hf", "vllm"), default="hf") parser.add_argument("--allow-unstable", action="store_true") diff --git a/src/rcc/_async.py b/src/rclc/_async.py similarity index 100% rename from src/rcc/_async.py rename to src/rclc/_async.py diff --git a/src/rcc/_cache.py b/src/rclc/_cache.py similarity index 100% rename from src/rcc/_cache.py rename to src/rclc/_cache.py diff --git a/src/rcc/capture/__init__.py b/src/rclc/capture/__init__.py similarity index 100% rename from src/rcc/capture/__init__.py rename to src/rclc/capture/__init__.py diff --git a/src/rcc/capture/aliasing.py b/src/rclc/capture/aliasing.py similarity index 100% rename from src/rcc/capture/aliasing.py rename to src/rclc/capture/aliasing.py diff --git a/src/rcc/capture/connector.py b/src/rclc/capture/connector.py similarity index 99% rename from src/rcc/capture/connector.py rename to src/rclc/capture/connector.py index 2ef96be..4d02f14 100644 --- a/src/rcc/capture/connector.py +++ b/src/rclc/capture/connector.py @@ -11,7 +11,7 @@ import torch -from rcc.capture.pages import DenseKV, ExtractSpec, LayerPages, extract_dense_kv, read_layer_pages +from rclc.capture.pages import DenseKV, ExtractSpec, LayerPages, extract_dense_kv, read_layer_pages @dataclass diff --git a/src/rcc/capture/pages.py b/src/rclc/capture/pages.py similarity index 100% rename from src/rcc/capture/pages.py rename to src/rclc/capture/pages.py diff --git a/src/rcc/checkpoint.py b/src/rclc/checkpoint.py similarity index 96% rename from src/rcc/checkpoint.py rename to src/rclc/checkpoint.py index c4e5217..74bc9e8 100644 --- a/src/rcc/checkpoint.py +++ b/src/rclc/checkpoint.py @@ -16,11 +16,11 @@ from safetensors import safe_open from safetensors import torch as tensor_io -from rcc.latent import forward_rows -from rcc.transport import SenderState, validate_model, validate_rows +from rclc.latent import forward_rows +from rclc.transport import SenderState, validate_model, validate_rows if TYPE_CHECKING: - from rcc.hf import Agent + from rclc.hf import Agent save_file: Callable[[dict[str, torch.Tensor], str, dict[str, str]], None] = cast( Any, tensor_io @@ -131,11 +131,11 @@ def _read_payload(path: str | Path) -> dict[str, Any]: metadata = cast(dict[str, str], archive.metadata() or {}) raw: Any = json.loads(metadata.get("rcc", "{}")) if not isinstance(raw, dict): - raise ValueError("not an RCC state snapshot") + raise ValueError("not an RCLC state snapshot") payload = cast(dict[str, Any], raw) fields = {"format", "identity", "latent_steps", "inherited_positions"} if set(payload) != fields or type(payload["format"]) is not int or payload["format"] != 1: - raise ValueError("unsupported RCC state snapshot format") + raise ValueError("unsupported RCLC state snapshot format") keys = set(cast(list[str], archive.keys())) if "rows" not in keys or keys - {"rows", "ids"}: raise ValueError("snapshot must contain rows and optional token IDs") @@ -147,7 +147,7 @@ def _read_payload(path: str | Path) -> dict[str, Any]: @torch.inference_mode() def read_state(path: str | Path, agent: Agent) -> SenderState: """Validate an input snapshot before rebuilding its cache on the caller's engine.""" - from rcc import vllm + from rclc import vllm validate_model(agent.model) if agent.engine is not None: diff --git a/src/rcc/diagnostics.py b/src/rclc/diagnostics.py similarity index 97% rename from src/rcc/diagnostics.py rename to src/rclc/diagnostics.py index 3a8712d..19dcdf7 100644 --- a/src/rcc/diagnostics.py +++ b/src/rclc/diagnostics.py @@ -11,7 +11,7 @@ import torch if TYPE_CHECKING: - from rcc.hf import Agent + from rclc.hf import Agent def _version(package: str) -> str | None: @@ -47,8 +47,8 @@ def _vllm_issues(versions: dict[str, str | None], cuda: bool, allow_unstable: bo def _agent_issues(agent: Agent) -> list[str]: - from rcc import vllm - from rcc.transport import validate_model + from rclc import vllm + from rclc.transport import validate_model try: validate_model(agent.model) diff --git a/src/rcc/hf.py b/src/rclc/hf.py similarity index 98% rename from src/rcc/hf.py rename to src/rclc/hf.py index e0095db..9632b2a 100644 --- a/src/rcc/hf.py +++ b/src/rclc/hf.py @@ -11,11 +11,11 @@ import torch from transformers.generation.utils import GenerateDecoderOnlyOutput -from rcc import vllm -from rcc._async import run_in_worker -from rcc._cache import clone_cache -from rcc.latent import forward_rows -from rcc.transport import Delivery, SenderState, token_ids, validate_model +from rclc import vllm +from rclc._async import run_in_worker +from rclc._cache import clone_cache +from rclc.latent import forward_rows +from rclc.transport import Delivery, SenderState, token_ids, validate_model Chat = Sequence[dict[str, Any]] @@ -191,7 +191,7 @@ def validate_sender(self, sender: SenderState) -> None: def _next(self) -> tuple[Delivery, PreparedRequest]: if not self._deliveries: raise IndexError( - "no queued handoff; await rcc.transfer (or call transfer_sync) before pop" + "no queued handoff; await rclc.transfer (or call transfer_sync) before pop" ) return self._deliveries[0] @@ -398,7 +398,7 @@ def _require_settled(self) -> None: async def save(self, path: str | Path) -> None: """Atomically save retained input state, checking that no pending work would be lost.""" - from rcc.checkpoint import write_state + from rclc.checkpoint import write_state self._require_settled() state = await run_in_worker(self.sender_state) @@ -406,7 +406,7 @@ async def save(self, path: str | Path) -> None: async def load(self, path: str | Path) -> None: """Check a saved state's checkpoint identity and re-prefill it on this bound model.""" - from rcc.checkpoint import read_state + from rclc.checkpoint import read_state self._require_settled() state = await run_in_worker(read_state, path, self) diff --git a/src/rcc/latent.py b/src/rclc/latent.py similarity index 97% rename from src/rcc/latent.py rename to src/rclc/latent.py index b0ba2e4..0f7b1e4 100644 --- a/src/rcc/latent.py +++ b/src/rclc/latent.py @@ -7,11 +7,11 @@ import torch -from rcc._async import run_in_worker -from rcc._cache import ATTENTION_LOCK, cache_kv, clone_cache, get_backbone +from rclc._async import run_in_worker +from rclc._cache import ATTENTION_LOCK, cache_kv, clone_cache, get_backbone if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState def _validate_count(value: int, name: str) -> None: diff --git a/src/rcc/message.py b/src/rclc/message.py similarity index 98% rename from src/rcc/message.py rename to src/rclc/message.py index 9b485ae..7f85d07 100644 --- a/src/rcc/message.py +++ b/src/rclc/message.py @@ -9,7 +9,7 @@ import torch import torch.nn.functional as functional -from rcc.selection import Selection +from rclc.selection import Selection Representation = Literal["embeddings", "token_ids+continuous"] diff --git a/src/rcc/py.typed b/src/rclc/py.typed similarity index 100% rename from src/rcc/py.typed rename to src/rclc/py.typed diff --git a/src/rcc/reasoning.py b/src/rclc/reasoning.py similarity index 96% rename from src/rcc/reasoning.py rename to src/rclc/reasoning.py index bfeaf65..1e52c4e 100644 --- a/src/rcc/reasoning.py +++ b/src/rclc/reasoning.py @@ -10,12 +10,12 @@ import torch -from rcc._async import run_in_worker -from rcc._cache import cache_kv, clone_cache -from rcc.latent import latent_mass, latent_mass_sync +from rclc._async import run_in_worker +from rclc._cache import cache_kv, clone_cache +from rclc.latent import latent_mass, latent_mass_sync if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState ReasoningContext = Literal["full", "full_with_request", "selected", "selected_with_request"] # Default assert_close tolerances, so recomputed prefixes may differ by kernel roundoff. @@ -58,7 +58,7 @@ def resolve_options( def _validate_result(state: SenderState, result: SenderState, maximum: int) -> None: """Check append-only rows, metadata and cache geometry at the callable boundary.""" - from rcc.transport import SenderState + from rclc.transport import SenderState if type(result) is not SenderState: raise ValueError("reasoning must return a SenderState") diff --git a/src/rcc/selection.py b/src/rclc/selection.py similarity index 90% rename from src/rcc/selection.py rename to src/rclc/selection.py index 6e964e5..83a4add 100644 --- a/src/rcc/selection.py +++ b/src/rclc/selection.py @@ -11,7 +11,7 @@ import torch if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState Kind = Literal["token", "latent", "continuous", "unknown"] @@ -112,12 +112,12 @@ def selection_html(selections: Sequence[Selection | None], request: str) -> str: '' '' "Transfer selection
' + ".rclc-selection h2{font-size:16px}.rclc-selection p{overflow-wrap:anywhere}" + ".rclc-selection pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit}" + ".rclc-selection mark{background:#c9f0cb;color:#18391c;text-decoration:underline}" + '.rclc-selection section{margin-top:28px}
' f"

Request: {escape(request)}

" "

Green underlined spans were kept. Hover for source positions.

" + "".join(sections) diff --git a/src/rcc/selectors/__init__.py b/src/rclc/selectors/__init__.py similarity index 68% rename from src/rcc/selectors/__init__.py rename to src/rclc/selectors/__init__.py index a402d89..983a739 100644 --- a/src/rcc/selectors/__init__.py +++ b/src/rclc/selectors/__init__.py @@ -5,12 +5,12 @@ import torch -from rcc.selectors.cacheback import cacheback -from rcc.selectors.chunkkv import chunkkv -from rcc.selectors.qsnap import qsnap +from rclc.selectors.cacheback import cacheback +from rclc.selectors.chunkkv import chunkkv +from rclc.selectors.qsnap import qsnap if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState Selector: TypeAlias = Callable[["SenderState", torch.Tensor, int], Sequence[int] | torch.Tensor] diff --git a/src/rcc/selectors/_capture.py b/src/rclc/selectors/_capture.py similarity index 98% rename from src/rcc/selectors/_capture.py rename to src/rclc/selectors/_capture.py index 1082e99..99cba2d 100644 --- a/src/rcc/selectors/_capture.py +++ b/src/rclc/selectors/_capture.py @@ -14,9 +14,9 @@ import torch -from rcc._cache import ATTENTION_LOCK, clone_cache, get_backbone +from rclc._cache import ATTENTION_LOCK, clone_cache, get_backbone -CAPTURE_IMPL = "rcc_vote_capture" +CAPTURE_IMPL = "rclc_vote_capture" Attention = Callable[..., object] diff --git a/src/rcc/selectors/_support.py b/src/rclc/selectors/_support.py similarity index 99% rename from src/rcc/selectors/_support.py rename to src/rclc/selectors/_support.py index 3a26898..4803792 100644 --- a/src/rcc/selectors/_support.py +++ b/src/rclc/selectors/_support.py @@ -10,7 +10,7 @@ import torch import torch.nn.functional as functional -from rcc.selectors._capture import run_capture +from rclc.selectors._capture import run_capture ROW_CHUNK = 256 POOL_KERNEL = 7 diff --git a/src/rcc/selectors/cacheback.py b/src/rclc/selectors/cacheback.py similarity index 93% rename from src/rcc/selectors/cacheback.py rename to src/rclc/selectors/cacheback.py index 70af538..c954a56 100644 --- a/src/rcc/selectors/cacheback.py +++ b/src/rclc/selectors/cacheback.py @@ -6,11 +6,11 @@ import torch -from rcc.selectors import _support -from rcc.selectors.fixed_spans import keep_spans, selects +from rclc.selectors import _support +from rclc.selectors.fixed_spans import keep_spans, selects if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState def check_context(sender: SenderState, request_ids: torch.Tensor) -> None: diff --git a/src/rcc/selectors/chunkkv.py b/src/rclc/selectors/chunkkv.py similarity index 94% rename from src/rcc/selectors/chunkkv.py rename to src/rclc/selectors/chunkkv.py index e0e6413..5fc98bb 100644 --- a/src/rcc/selectors/chunkkv.py +++ b/src/rclc/selectors/chunkkv.py @@ -12,12 +12,12 @@ import torch import torch.nn.functional as functional -from rcc.selectors._capture import run_capture -from rcc.selectors.cacheback import finite -from rcc.selectors.fixed_spans import keep_spans, selects, span_means +from rclc.selectors._capture import run_capture +from rclc.selectors.cacheback import finite +from rclc.selectors.fixed_spans import keep_spans, selects, span_means if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState class _WindowFold: diff --git a/src/rcc/selectors/fixed_spans.py b/src/rclc/selectors/fixed_spans.py similarity index 98% rename from src/rcc/selectors/fixed_spans.py rename to src/rclc/selectors/fixed_spans.py index eeddcf2..320ba53 100644 --- a/src/rcc/selectors/fixed_spans.py +++ b/src/rclc/selectors/fixed_spans.py @@ -9,7 +9,7 @@ import torch.nn.functional as functional if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState def _partial_interval(available: Sequence[int], room: int, peak: int) -> range: diff --git a/src/rcc/selectors/qsnap.py b/src/rclc/selectors/qsnap.py similarity index 83% rename from src/rcc/selectors/qsnap.py rename to src/rclc/selectors/qsnap.py index b419ecd..712a38b 100644 --- a/src/rcc/selectors/qsnap.py +++ b/src/rclc/selectors/qsnap.py @@ -6,12 +6,12 @@ import torch -from rcc.selectors import _support -from rcc.selectors.cacheback import check_context, finite -from rcc.selectors.fixed_spans import keep_spans, selects +from rclc.selectors import _support +from rclc.selectors.cacheback import check_context, finite +from rclc.selectors.fixed_spans import keep_spans, selects if TYPE_CHECKING: - from rcc.transport import SenderState + from rclc.transport import SenderState def qsnap_scores(sender: SenderState, request_ids: torch.Tensor) -> torch.Tensor: diff --git a/src/rcc/transport.py b/src/rclc/transport.py similarity index 97% rename from src/rcc/transport.py rename to src/rclc/transport.py index 64b680b..8d5c73d 100644 --- a/src/rcc/transport.py +++ b/src/rclc/transport.py @@ -12,16 +12,16 @@ import torch -from rcc._async import run_in_worker -from rcc.latent import selected_state -from rcc.message import Message, Representation, encode_message -from rcc.reasoning import Reasoning, ReasoningContext, apply_reasoning, resolve_options -from rcc.selection import record_selection as capture_selection -from rcc.selection import selection_html -from rcc.selectors import Selector, cacheback, chunkkv, qsnap +from rclc._async import run_in_worker +from rclc.latent import selected_state +from rclc.message import Message, Representation, encode_message +from rclc.reasoning import Reasoning, ReasoningContext, apply_reasoning, resolve_options +from rclc.selection import record_selection as capture_selection +from rclc.selection import selection_html +from rclc.selectors import Selector, cacheback, chunkkv, qsnap if TYPE_CHECKING: - from rcc.hf import Agent, HFReceiver, PreparedRequest + from rclc.hf import Agent, HFReceiver, PreparedRequest # Snapshots and selection serialize; use separate processes for model isolation. _PREPARATION_LOCK = threading.Lock() @@ -262,7 +262,7 @@ def _receiver_plans( senders: list[SenderState], receivers: list[Receiver], requests: list[str | torch.Tensor] ) -> list[list[PreparedRequest | None]]: """Snapshot configured prompts and validate every receiver before selecting anything.""" - from rcc.hf import HFReceiver + from rclc.hf import HFReceiver for receiver in receivers: if isinstance(receiver, HFReceiver): @@ -327,7 +327,7 @@ def transfer_sync( except RuntimeError: pass else: - raise RuntimeError("use await rcc.transfer(...) inside a running event loop") + raise RuntimeError("use await rclc.transfer(...) inside a running event loop") return asyncio.run( transfer( sender, @@ -401,7 +401,7 @@ def _snapshot_transfer( list[list[PreparedRequest | None]], ]: """Snapshot participating state and receiver prompts before reasoning or selection.""" - from rcc.hf import Agent + from rclc.hf import Agent with _PREPARATION_LOCK: sources = [sender] if isinstance(sender, (SenderState, Agent)) else list(sender) diff --git a/src/rcc/vllm.py b/src/rclc/vllm.py similarity index 96% rename from src/rcc/vllm.py rename to src/rclc/vllm.py index 1df45e0..ea93f90 100644 --- a/src/rcc/vllm.py +++ b/src/rclc/vllm.py @@ -10,11 +10,11 @@ import torch -from rcc.capture.aliasing import hf_view_model -from rcc.capture.connector import discard_capture as discard_capture -from rcc.capture.connector import get_capture, require_runtime, validate_capture_config -from rcc.capture.connector import request_capture as request_capture -from rcc.transport import SenderState +from rclc.capture.aliasing import hf_view_model +from rclc.capture.connector import discard_capture as discard_capture +from rclc.capture.connector import get_capture, require_runtime, validate_capture_config +from rclc.capture.connector import request_capture as request_capture +from rclc.transport import SenderState def require_idle(llm: Any) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index d75966f..5bf129b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ from tokenizers import Tokenizer, models, pre_tokenizers from transformers import PreTrainedTokenizerFast, Qwen3Config, Qwen3ForCausalLM -from rcc import SenderState +from rclc import SenderState def tiny_config(attn_implementation: str = "sdpa") -> Qwen3Config: diff --git a/tests/test_agent_lifecycle.py b/tests/test_agent_lifecycle.py index 6b10553..7705163 100644 --- a/tests/test_agent_lifecycle.py +++ b/tests/test_agent_lifecycle.py @@ -15,13 +15,13 @@ from safetensors import safe_open from safetensors.torch import load_file, save_file -import rcc -from rcc import checkpoint -from rcc._cache import cache_kv +import rclc +from rclc import checkpoint +from rclc._cache import cache_kv def test_saved_agent_continues_and_transfers( - senders: list[rcc.SenderState], tmp_path: Path, monkeypatch: Any + senders: list[rclc.SenderState], tmp_path: Path, monkeypatch: Any ) -> None: model, tokenizer = senders[0].model, senders[0].tokenizer tokenizer.chat_template = "{% for m in messages %}{{ m['content'] }} {% endfor %}?" @@ -33,13 +33,13 @@ def generate(inputs: dict[str, Any]) -> Any: ) async def journey() -> None: - receiver = rcc.bind(model, tokenizer, max_new_tokens=2) - inbox: list[rcc.Delivery] = [] - await rcc.transfer(senders, [receiver, inbox.append], "Who owns Cedar ?") + receiver = rclc.bind(model, tokenizer, max_new_tokens=2) + inbox: list[rclc.Delivery] = [] + await rclc.transfer(senders, [receiver, inbox.append], "Who owns Cedar ?") with monkeypatch.context() as patch: patch.setattr(model.model, "forward", lambda **kw: pytest.fail("inspection forwarded")) report = receiver.inspect() - assert rcc.check(receiver)["ok"] + assert rclc.check(receiver)["ok"] assert report["queued_requests"] == len(receiver) == 1 delivery = report["deliveries"][0] assert delivery["senders"] == 2 @@ -49,7 +49,7 @@ async def journey() -> None: assert delivery["remaining_positions"] == 512 - positions - 2 assert json.loads(json.dumps(report)) == report with pytest.raises(ValueError, match="bound agent"): - rcc.check(receiver, backend="vllm") + rclc.check(receiver, backend="vllm") with pytest.raises(ValueError, match="queued"): await receiver.save(path) inputs = receiver.pop() @@ -75,7 +75,7 @@ def failed_write(*args: Any, **kwargs: Any) -> None: assert path.read_bytes() == original_file and list(tmp_path.iterdir()) == [path] saved = load_file(path) assert saved["rows"].device.type == "cpu" and "past_key_values" not in saved - restored = rcc.bind(copy.deepcopy(model), tokenizer, max_new_tokens=2) + restored = rclc.bind(copy.deepcopy(model), tokenizer, max_new_tokens=2) await restored.load(path) loaded = restored.sender_state() assert loaded.inherited_positions == state.inherited_positions @@ -102,7 +102,7 @@ def failed_write(*args: Any, **kwargs: Any) -> None: with monkeypatch.context() as patch: patch.setattr(checkpoint, "_hash_weights", lambda _: pytest.fail("rehashed weights")) await restored.save(path) - await rcc.transfer(restored, receiver, "When Birch launches ?") + await rclc.transfer(restored, receiver, "When Birch launches ?") assert len(receiver) == 1 corrupted = load_file(path) @@ -120,7 +120,7 @@ def failed_write(*args: Any, **kwargs: Any) -> None: edited = copy.deepcopy(model) with torch.no_grad(): edited.get_input_embeddings().weight[0, 0].add_(1) - changed = rcc.bind(edited, tokenizer, prompt=torch.tensor([[10, 11]])) + changed = rclc.bind(edited, tokenizer, prompt=torch.tensor([[10, 11]])) retained = changed.sender_state() with pytest.raises(ValueError, match="identical weights"): await changed.load(path) @@ -132,11 +132,11 @@ def failed_write(*args: Any, **kwargs: Any) -> None: asyncio.run(journey()) command = subprocess.run( - [sys.executable, "-m", "rcc", "doctor"], capture_output=True, text=True, check=True + [sys.executable, "-m", "rclc", "doctor"], capture_output=True, text=True, check=True ) assert json.loads(command.stdout)["scope"] == "environment" failed_command = subprocess.run( - [sys.executable, "-m", "rcc", "doctor", "--backend", "vllm"], + [sys.executable, "-m", "rclc", "doctor", "--backend", "vllm"], env={**os.environ, "VLLM_ENABLE_V1_MULTIPROCESSING": "1"}, capture_output=True, text=True, @@ -144,26 +144,26 @@ def failed_write(*args: Any, **kwargs: Any) -> None: ) assert failed_command.returncode == 1 assert any("MULTIPROCESSING" in issue for issue in json.loads(failed_command.stdout)["issues"]) - from rcc import diagnostics + from rclc import diagnostics with monkeypatch.context() as patch: patch.setattr(diagnostics.torch.cuda, "is_available", lambda: False) patch.delenv("VLLM_ENABLE_V1_MULTIPROCESSING", raising=False) versions = {"torch": "2.4.0", "transformers": "4.57.1", "vllm": "0.11.1"} patch.setattr(diagnostics, "_version", versions.get) - report = rcc.check(backend="vllm") + report = rclc.check(backend="vllm") assert not report["ok"] and any("CUDA" in issue for issue in report["issues"]) assert any("torch==2.9.0" in issue for issue in report["issues"]) def test_cancelled_restore_keeps_current_state( - senders: list[rcc.SenderState], tmp_path: Path, monkeypatch: Any + senders: list[rclc.SenderState], tmp_path: Path, monkeypatch: Any ) -> None: model, tokenizer = senders[0].model, senders[0].tokenizer async def journey() -> None: - agent = rcc.bind(model, tokenizer, prompt=torch.tensor([[10, 11, 12]])) + agent = rclc.bind(model, tokenizer, prompt=torch.tensor([[10, 11, 12]])) path = tmp_path / "saved.safetensors" await agent.save(path) await agent.append(torch.tensor([[13]])) diff --git a/tests/test_async.py b/tests/test_async.py index 4396a86..52ec1af 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -8,35 +8,37 @@ import pytest import torch -import rcc -from rcc._cache import cache_kv -from rcc.selectors import cacheback +import rclc +from rclc._cache import cache_kv +from rclc.selectors import cacheback def test_async_handoffs_keep_loop_live_and_cancel_without_delivery( - senders: list[rcc.SenderState], + senders: list[rclc.SenderState], monkeypatch: Any, ) -> None: model, tokenizer = senders[0].model, senders[0].tokenizer tokenizer.chat_template = "{% for m in messages %}{{ m['content'] }} {% endfor %}?" snapshots = [[(k.clone(), v.clone()) for k, v in cache_kv(s.past_key_values)] for s in senders] requests = ["Who owns Cedar ?", "When Birch launches ?"] - reference: list[rcc.Delivery] = [] - rcc.transfer_sync(senders, reference.append, requests, selector=partial(cacheback, span_size=4)) + reference: list[rclc.Delivery] = [] + rclc.transfer_sync( + senders, reference.append, requests, selector=partial(cacheback, span_size=4) + ) async def journey() -> None: loop = asyncio.get_running_loop() main_thread = threading.get_ident() - receiver = rcc.bind(model, tokenizer, max_new_tokens=2) - async_received: list[rcc.Delivery] = [] - sync_received: list[rcc.Delivery] = [] + receiver = rclc.bind(model, tokenizer, max_new_tokens=2) + async_received: list[rclc.Delivery] = [] + sync_received: list[rclc.Delivery] = [] - async def receive(delivery: rcc.Delivery) -> None: + async def receive(delivery: rclc.Delivery) -> None: assert threading.get_ident() == main_thread await asyncio.sleep(0) async_received.append(delivery) - await rcc.transfer( + await rclc.transfer( senders, [receiver, sync_received.append, receive], requests, @@ -56,8 +58,8 @@ async def receive(delivery: rcc.Delivery) -> None: return_dict_in_generate=True, ) receiver.update(inputs=inputs, generation=output) - returned: list[rcc.Delivery] = [] - await rcc.transfer(receiver, returned.append, requests[0]) + returned: list[rclc.Delivery] = [] + await rclc.transfer(receiver, returned.append, requests[0]) assert returned[0].messages[0].positions > 0 started = asyncio.Event() @@ -65,7 +67,7 @@ async def receive(delivery: rcc.Delivery) -> None: active = 0 calls = 0 - def paused_selector(state: rcc.SenderState, ids: torch.Tensor, budget: int) -> list[int]: + def paused_selector(state: rclc.SenderState, ids: torch.Tensor, budget: int) -> list[int]: nonlocal active, calls assert threading.get_ident() != main_thread active += 1 @@ -79,11 +81,11 @@ def paused_selector(state: rcc.SenderState, ids: torch.Tensor, budget: int) -> l active -= 1 first = asyncio.create_task( - rcc.transfer(senders, receive, requests, selector=paused_selector) + rclc.transfer(senders, receive, requests, selector=paused_selector) ) await asyncio.wait_for(started.wait(), 10) second = asyncio.create_task( - rcc.transfer(senders, receive, requests, selector=paused_selector) + rclc.transfer(senders, receive, requests, selector=paused_selector) ) await asyncio.sleep(0) assert not first.done() and active == 1 @@ -95,7 +97,7 @@ def paused_selector(state: rcc.SenderState, ids: torch.Tensor, budget: int) -> l release.clear() before = len(async_received) cancelled = asyncio.create_task( - rcc.transfer(senders[0], [receiver, receive], requests[0], selector=paused_selector) + rclc.transfer(senders[0], [receiver, receive], requests[0], selector=paused_selector) ) await asyncio.wait_for(started.wait(), 10) cancelled.cancel() @@ -107,7 +109,7 @@ def paused_selector(state: rcc.SenderState, ids: torch.Tensor, budget: int) -> l await cancelled assert active == 0 and len(receiver) == 0 and len(async_received) == before - from rcc import hf + from rclc import hf state = receiver.sender_state() started.clear() @@ -138,19 +140,19 @@ def fail(*args: Any) -> list[int]: raise ValueError("selection failed") with pytest.raises(ValueError, match="selection failed"): - await rcc.transfer(senders, [receiver, receive], requests, selector=fail) + await rclc.transfer(senders, [receiver, receive], requests, selector=fail) assert len(receiver) == 0 and len(async_received) == before - async def failed_receiver(delivery: rcc.Delivery) -> None: + async def failed_receiver(delivery: rclc.Delivery) -> None: raise RuntimeError("receiver failed") with pytest.raises(RuntimeError, match="receiver failed"): - await rcc.transfer(senders, [receive, failed_receiver], requests[0]) + await rclc.transfer(senders, [receive, failed_receiver], requests[0]) assert len(async_received) == before + 1 - await rcc.transfer(senders, receiver, requests[0]) + await rclc.transfer(senders, receiver, requests[0]) assert len(receiver) == 1 - with pytest.raises(RuntimeError, match=r"await rcc\.transfer"): - rcc.transfer_sync(senders, receive, requests[0]) + with pytest.raises(RuntimeError, match=r"await rclc\.transfer"): + rclc.transfer_sync(senders, receive, requests[0]) asyncio.run(journey()) for sender, snapshot in zip(senders, snapshots, strict=True): @@ -160,10 +162,10 @@ async def failed_receiver(delivery: rcc.Delivery) -> None: def test_async_thoughts_support_custom_methods_and_cancellation( - senders: list[rcc.SenderState], + senders: list[rclc.SenderState], monkeypatch: Any, ) -> None: - from rcc import latent + from rclc import latent state = senders[0] query = "Who owns Cedar ?" @@ -172,32 +174,32 @@ def test_async_thoughts_support_custom_methods_and_cancellation( async def journey() -> None: loop = asyncio.get_running_loop() - direct = await rcc.latent_mass(state, steps=2, request=query) - reference = rcc.latent_mass_sync(state, steps=2, request=query) + direct = await rclc.latent_mass(state, steps=2, request=query) + reference = rclc.latent_mass_sync(state, steps=2, request=query) torch.testing.assert_close(direct.input_embeds, reference.input_embeds, rtol=0, atol=0) for actual, expected in zip( cache_kv(direct.past_key_values), cache_kv(reference.past_key_values), strict=True ): torch.testing.assert_close(actual, expected, rtol=0, atol=0) - assert await rcc.latent_mass(state, steps=0) is state + assert await rclc.latent_mass(state, steps=0) is state with pytest.raises(ValueError, match="max_positions"): - await rcc.latent_mass(state, steps=3, max_positions=2) + await rclc.latent_mass(state, steps=3, max_positions=2) - async def custom(current: rcc.SenderState, **kwargs: Any) -> rcc.SenderState: + async def custom(current: rclc.SenderState, **kwargs: Any) -> rclc.SenderState: assert asyncio.get_running_loop() is loop await asyncio.sleep(0) - return await rcc.latent_mass(current, steps=2, **kwargs) + return await rclc.latent_mass(current, steps=2, **kwargs) for context in ("full", "full_with_request", "selected", "selected_with_request"): - actual: list[rcc.Delivery] = [] - expected: list[rcc.Delivery] = [] + actual: list[rclc.Delivery] = [] + expected: list[rclc.Delivery] = [] options = dict(reasoning_context=context, reasoning_budget=2, budget=24) - await rcc.transfer(senders, actual.append, [query, query], reasoning=custom, **options) - await rcc.transfer( + await rclc.transfer(senders, actual.append, [query, query], reasoning=custom, **options) + await rclc.transfer( senders, expected.append, [query, query], - reasoning=partial(rcc.latent_mass_sync, steps=2), + reasoning=partial(rclc.latent_mass_sync, steps=2), **options, ) assert len(actual) == len(expected) == 2 @@ -210,9 +212,9 @@ async def custom(current: rcc.SenderState, **kwargs: Any) -> rcc.SenderState: started, stopped = asyncio.Event(), asyncio.Event() waiting = asyncio.Event() - inbox: list[rcc.Delivery] = [] + inbox: list[rclc.Delivery] = [] - async def wait_for_tool(current: rcc.SenderState, **kwargs: Any) -> rcc.SenderState: + async def wait_for_tool(current: rclc.SenderState, **kwargs: Any) -> rclc.SenderState: current.input_embeds.zero_() started.set() try: @@ -222,11 +224,11 @@ async def wait_for_tool(current: rcc.SenderState, **kwargs: Any) -> rcc.SenderSt stopped.set() task = asyncio.create_task( - rcc.transfer(state, inbox.append, query, reasoning=wait_for_tool) + rclc.transfer(state, inbox.append, query, reasoning=wait_for_tool) ) await asyncio.wait_for(started.wait(), 10) - independent: list[rcc.Delivery] = [] - await asyncio.wait_for(rcc.transfer(state, independent.append, query), 10) + independent: list[rclc.Delivery] = [] + await asyncio.wait_for(rclc.transfer(state, independent.append, query), 10) assert independent and not inbox task.cancel() with pytest.raises(asyncio.CancelledError): @@ -236,7 +238,7 @@ async def wait_for_tool(current: rcc.SenderState, **kwargs: Any) -> rcc.SenderSt waiting.set() with pytest.raises(ValueError, match="preserve the sender prefix"): - await rcc.transfer(state, inbox.append, query, reasoning=wait_for_tool) + await rclc.transfer(state, inbox.append, query, reasoning=wait_for_tool) assert not inbox started.clear() @@ -250,7 +252,7 @@ def paused_forward(*args: Any, **kwargs: Any) -> Any: with monkeypatch.context() as patch: patch.setattr(latent, "forward_rows", paused_forward) - task = asyncio.create_task(rcc.latent_mass(state, steps=2)) + task = asyncio.create_task(rclc.latent_mass(state, steps=2)) await asyncio.wait_for(started.wait(), 10) task.cancel() await asyncio.sleep(0) @@ -258,7 +260,7 @@ def paused_forward(*args: Any, **kwargs: Any) -> Any: release.set() with pytest.raises(asyncio.CancelledError): await task - assert (await rcc.latent_mass(state, steps=1)).latent_steps == state.latent_steps + 1 + assert (await rclc.latent_mass(state, steps=1)).latent_steps == state.latent_steps + 1 asyncio.run(journey()) torch.testing.assert_close(state.input_embeds, rows, rtol=0, atol=0) diff --git a/tests/test_demo.py b/tests/test_demo.py index 763769f..0a4ded9 100644 --- a/tests/test_demo.py +++ b/tests/test_demo.py @@ -10,7 +10,7 @@ import transformers from demo.record import ANSWER_INSTRUCTION, DOCUMENTS, QUESTIONS, record, respond, save -from rcc import SenderState +from rclc import SenderState def test_demo_records_source_positions_messages_answers_and_json( @@ -196,4 +196,4 @@ def test_colab_records_offline_from_a_source_archive_and_downloads_a_replay( case["expected_text"].lower() in case["answer"].lower() ) assert b"Saved" in archive.read("run.log") - assert b"from rcc import bind, transfer" in archive.read("run.py") + assert b"from rclc import bind, transfer" in archive.read("run.py") diff --git a/tests/test_evaluation.py b/tests/test_evaluation.py index 28b44a6..0b48a9d 100644 --- a/tests/test_evaluation.py +++ b/tests/test_evaluation.py @@ -7,8 +7,8 @@ import pytest from examples.evaluate_selectors import evaluate, load_cases, recent -from rcc import SenderState -from rcc.selectors import cacheback, chunkkv, qsnap +from rclc import SenderState +from rclc.selectors import cacheback, chunkkv, qsnap def test_selector_comparison(senders: list[SenderState], tmp_path: Path) -> None: diff --git a/tests/test_hf.py b/tests/test_hf.py index 136d193..25e3d03 100644 --- a/tests/test_hf.py +++ b/tests/test_hf.py @@ -9,7 +9,7 @@ import torch from examples.existing_agents import handoff -from rcc import ( +from rclc import ( Agent, Delivery, HFReceiver, @@ -123,7 +123,7 @@ def test_hf_agents_transfer_and_generate(senders: list[SenderState], monkeypatch def test_existing_agent_history_and_receiver_budget( senders: list[SenderState], monkeypatch: Any ) -> None: - from rcc._cache import cache_kv + from rclc._cache import cache_kv model, tokenizer = senders[0].model, senders[0].tokenizer tokenizer.chat_template = ( @@ -293,7 +293,7 @@ def traced_forward(**kwargs: Any) -> Any: transfer_sync(bound, [roomy, recorded.append], requests[0]) assert recorded[-1].messages[0].positions == 10 + (bound.input_embeds.shape[0] - 10 + 3) // 4 roomy.pop() - from rcc.selectors import cacheback + from rclc.selectors import cacheback for selector in (cacheback, partial(cacheback, span_size=4)): with pytest.raises(ValueError, match="required positions"): @@ -413,7 +413,7 @@ def traced_forward(**kwargs: Any) -> Any: assert agent_y.sender_state().inherited_positions > 0 with pytest.raises(ValueError, match="backend='hf'"): bind(model, tokenizer, backend="vllm") - from rcc import vllm + from rclc import vllm with monkeypatch.context() as patch: patch.setattr(vllm, "binding_model", lambda llm, tok, *args: (model, tok, 512)) diff --git a/tests/test_latent.py b/tests/test_latent.py index 4383749..ecf34cd 100644 --- a/tests/test_latent.py +++ b/tests/test_latent.py @@ -10,9 +10,9 @@ import torch import torch.nn.functional as functional -from rcc import Delivery, SenderState, latent_mass_sync, transfer_sync -from rcc._cache import cache_kv -from rcc.selectors import cacheback +from rclc import Delivery, SenderState, latent_mass_sync, transfer_sync +from rclc._cache import cache_kv +from rclc.selectors import cacheback @torch.inference_mode() @@ -262,7 +262,7 @@ def _adaptive_mean( request: torch.Tensor | None = None, tolerance: float = 0.01, ) -> SenderState: - from rcc.latent import forward_rows + from rclc.latent import forward_rows assert torch.is_grad_enabled() and not torch.is_inference_mode_enabled() limit = 2 if request is None else int(request[0, 0]) % 4 @@ -338,7 +338,7 @@ def _latent(steps: Any, context: str = "full") -> dict[str, Any]: def _check_callable_journey(senders: list[SenderState]) -> None: - from rcc import HFReceiver, bind + from rclc import HFReceiver, bind queries = ["Who owns Cedar ?", "When Birch launches ?"] state = senders[0] diff --git a/tests/test_numerics.py b/tests/test_numerics.py index 34be8f6..bb0b040 100644 --- a/tests/test_numerics.py +++ b/tests/test_numerics.py @@ -2,8 +2,8 @@ import torch -from rcc.selectors.cacheback import support_score -from rcc.selectors.fixed_spans import fixed_span_keep +from rclc.selectors.cacheback import support_score +from rclc.selectors.fixed_spans import fixed_span_keep def test_support_dose_and_zero_energy_have_known_answers() -> None: diff --git a/tests/test_selection.py b/tests/test_selection.py index a2d3395..2dbfd91 100644 --- a/tests/test_selection.py +++ b/tests/test_selection.py @@ -11,21 +11,21 @@ import pytest import torch -import rcc +import rclc def test_recorded_selection_survives_fan_in_reasoning_and_native_generation( - senders: list[rcc.SenderState], tmp_path: Path, monkeypatch: Any + senders: list[rclc.SenderState], tmp_path: Path, monkeypatch: Any ) -> None: model, tokenizer = senders[0].model, senders[0].tokenizer tokenizer.add_tokens([""]) tokenizer.chat_template = "{% for m in messages %}{{ m['content'] }} {% endfor %}?" - source = rcc.sender_from_hf( + source = rclc.sender_from_hf( model, tokenizer, "Who owns Cedar ? Who owns ?" ) - sources = [source, rcc.sender_from_hf(model, tokenizer, "When Birch launches ? " * 3)] + sources = [source, rclc.sender_from_hf(model, tokenizer, "When Birch launches ? " * 3)] - def choose(state: rcc.SenderState, query: torch.Tensor, budget: int) -> list[int]: + def choose(state: rclc.SenderState, query: torch.Tensor, budget: int) -> list[int]: return [len(state.input_embeds) - 1, 6, 2, 0] async def journey() -> None: @@ -34,20 +34,20 @@ async def journey() -> None: {} if context is None else { - "reasoning": partial(rcc.latent_mass, steps=2), + "reasoning": partial(rclc.latent_mass, steps=2), "reasoning_context": context, "reasoning_budget": 2, } ) for representation in ("embeddings", "token_ids+continuous"): - receiver = rcc.bind(model, tokenizer, max_new_tokens=2) - baseline: list[rcc.Delivery] = [] - recorded: list[rcc.Delivery] = [] + receiver = rclc.bind(model, tokenizer, max_new_tokens=2) + baseline: list[rclc.Delivery] = [] + recorded: list[rclc.Delivery] = [] warning = ( pytest.warns(UserWarning) if representation != "embeddings" else nullcontext() ) with warning: - await rcc.transfer( + await rclc.transfer( sources, baseline.append, ["Who owns Cedar ?", "When Birch launches ?"], @@ -56,7 +56,7 @@ async def journey() -> None: representation=representation, **options, ) - await rcc.transfer( + await rclc.transfer( sources, [receiver, recorded.append], ["Who owns Cedar ?", "When Birch launches ?"], @@ -111,7 +111,7 @@ async def journey() -> None: right.positions == len(selection.indices) + selection.added_latent_positions ) - peer = rcc.bind(model, tokenizer, max_new_tokens=2) + peer = rclc.bind(model, tokenizer, max_new_tokens=2) peer(baseline[0]) left, right = peer.pop(), receiver.pop() a = model.generate(**left, do_sample=False, pad_token_id=0) @@ -120,9 +120,9 @@ async def journey() -> None: assert len(receiver) == 1 with pytest.raises(ValueError, match="request_index"): receiver.selection_html(1) - unknown: list[rcc.Delivery] = [] + unknown: list[rclc.Delivery] = [] state = replace(senders[0], token_ids=None) - await rcc.transfer( + await rclc.transfer( state, unknown.append, "Who owns Cedar ?", selector=choose, record_selection=True ) selection = unknown[0].messages[0].selection @@ -131,8 +131,8 @@ async def journey() -> None: assert all(span["text"] is None for span in selection.spans) ids = senders[0].token_ids.clone() ids[0] = -1 - continuous: list[rcc.Delivery] = [] - await rcc.transfer( + continuous: list[rclc.Delivery] = [] + await rclc.transfer( replace(senders[0], token_ids=ids), continuous.append, source.request_ids("Who owns Cedar ?"), diff --git a/tests/test_transfer.py b/tests/test_transfer.py index a26f95e..7076e43 100644 --- a/tests/test_transfer.py +++ b/tests/test_transfer.py @@ -10,13 +10,13 @@ import torch import torch.nn.functional as functional -from rcc import Delivery, SenderState, transfer_sync, transport -from rcc._cache import cache_kv -from rcc.selectors import _capture, _support, cacheback, chunkkv, qsnap -from rcc.selectors.cacheback import cacheback_scores -from rcc.selectors.chunkkv import chunkkv_scores -from rcc.selectors.fixed_spans import fixed_span_keep -from rcc.selectors.qsnap import qsnap_scores +from rclc import Delivery, SenderState, transfer_sync, transport +from rclc._cache import cache_kv +from rclc.selectors import _capture, _support, cacheback, chunkkv, qsnap +from rclc.selectors.cacheback import cacheback_scores +from rclc.selectors.chunkkv import chunkkv_scores +from rclc.selectors.fixed_spans import fixed_span_keep +from rclc.selectors.qsnap import qsnap_scores def _snapshot(state: SenderState) -> list[tuple[torch.Tensor, torch.Tensor]]: diff --git a/tests/test_vllm.py b/tests/test_vllm.py index 96d3866..8371ac4 100644 --- a/tests/test_vllm.py +++ b/tests/test_vllm.py @@ -10,10 +10,10 @@ from tests.conftest import tiny_config from transformers import Qwen3ForCausalLM -from rcc import latent_mass, transfer_sync -from rcc._cache import cache_kv -from rcc.capture import connector -from rcc.vllm import binding_model, prefill_state, sender_from_vllm +from rclc import latent_mass, transfer_sync +from rclc._cache import cache_kv +from rclc.capture import connector +from rclc.vllm import binding_model, prefill_state, sender_from_vllm def test_vllm_capture_layouts_aliasing_and_explicit_unstable_opt_in( @@ -329,7 +329,7 @@ def step( assert len(captured_rows) == 5 assert torch.equal(captured_rows[0], model.get_input_embeddings()(ids)) assert not connector._REQUESTED and not connector._EXTRACTED - from rcc import bind + from rclc import bind tokenizer = senders[0].tokenizer tokenizer.chat_template = "{% for m in messages %}{{ m['content'] }} {% endfor %}?" @@ -389,7 +389,7 @@ def abort(request_ids: list[str], aborted: Any = aborted, pending: Any = pending patch.setattr(engine.vllm_config.model_config, "enable_prompt_embeds", False) with pytest.raises(ValueError, match="enable_prompt_embeds"): bind(llm, backend="vllm") - from rcc import check + from rclc import check assert any("enable_prompt_embeds" in issue for issue in check(agent)["issues"]) diff --git a/website/assets/app.js b/website/assets/app.js index c171406..30212b2 100644 --- a/website/assets/app.js +++ b/website/assets/app.js @@ -1,6 +1,5 @@ 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; diff --git a/website/assets/figures/communication-overview.webp b/website/assets/figures/communication-overview.webp new file mode 100644 index 0000000000000000000000000000000000000000..b4d5c548b287e93c6e1b3a1b29424a327840e92a GIT binary patch literal 12200 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 { 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; } +.curve-caption { color:var(--muted); font-size:12px; margin-top:26px; } .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; } @@ -188,13 +152,6 @@ summary { font-weight:500; } .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; } @@ -208,21 +165,16 @@ iframe { display:block; width:100%; height:815px; border:0; background:#fff; } .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 h3 { font-size:18px; } .citation pre { padding:18px 0 0; font-size:12px; } -.citation button { margin-top:16px; } +.citation button { margin-top:12px; } .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; } @@ -245,7 +197,6 @@ body { zoom:.85; } .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; } @@ -254,17 +205,11 @@ body { zoom:.85; } 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; } @@ -284,21 +229,12 @@ h1 { font-size:56px; letter-spacing:-2px; } .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; } @@ -308,3 +244,7 @@ h2 { font-size:32px; } html { scroll-behavior:auto; } *,*::before,*::after { transition:none!important; animation:none!important; } } + +.code-keyword { color:#bacafa; } +.code-function { color:#8bd9d1; } +.code-string { color:#e6ce99; } diff --git a/website/check.mjs b/website/check.mjs index 9eec915..e3a13e5 100644 --- a/website/check.mjs +++ b/website/check.mjs @@ -18,11 +18,11 @@ assert.equal((html.match(//); assert.match(html, /name="viewport"/); assert.match(html, /CacheBack \|/); -const sectionOrder = ['result-summary', 'abstract', 'idea', 'results', 'demo', 'code', 'paper']; +const sectionOrder = ['result-summary', 'abstract', '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); +assert.equal((html.match(/<img src="website\/assets\/figures\//g) || []).length, 5); for (const [, url] of html.matchAll(/(?:href|src|poster)="([^"]+)"/g)) { if (/^(https:|data:)/.test(url)) continue; From 6ac669d0f0dc07d9d19323d594efbdaab7c8801e Mon Sep 17 00:00:00 2001 From: Max Rossi <113717219+maxr0ssi@users.noreply.github.com> Date: Thu, 1 Oct 2026 13:14:42 -0400 Subject: [PATCH 3/4] Lead with the coding demo and use copy icons --- index.html | 67 ++++++++++++++------------------------- website/assets/app.js | 8 +++-- website/assets/styles.css | 28 ++++------------ website/check.mjs | 21 ++++-------- 4 files changed, 43 insertions(+), 81 deletions(-) diff --git a/index.html b/index.html index 52a2e49..6428533 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ <link rel="canonical" href="https://agentcacheback.github.io/"> <title>CacheBack | Receiver-conditioned communication - + @@ -35,8 +35,6 @@

CacheBack

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.

-
-
-

Watch the demo

-
-

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 - -
-
-
+
+
-

Use CacheBack

Install rclc, bind your agents, and pass the receiver’s request.

Code and documentation

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

Python
import rclc
+    

Use CacheBack

Install rclc, bind your agents, and pass the receiver’s request.

Code and documentation

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

Python
import rclc
 
 sender = rclc.bind(
     model, tokenizer,
@@ -139,7 +118,7 @@ 

Results

"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

@misc{rossi2026cacheback,
+    

THE PAPER

Receiver-Conditioned Latent Communication gives 94% CacheBack

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

Cite this work

@misc{rossi2026cacheback,
   title = {Receiver-Conditioned Latent Communication
            gives 94\% CacheBack},
   author = {Rossi, Maximillian and Raghunath, Prajwal
diff --git a/website/assets/app.js b/website/assets/app.js
index 30212b2..ee76664 100644
--- a/website/assets/app.js
+++ b/website/assets/app.js
@@ -14,11 +14,12 @@ if (typeof document !== 'undefined') {
   video.addEventListener('play', () => { play.hidden = true; });
 
   document.querySelectorAll('[data-copy]').forEach((button) => {
+    let reset;
     button.addEventListener('click', async () => {
       const content = byId(button.dataset.copy);
       try {
         await navigator.clipboard.writeText(content.textContent);
-        button.textContent = 'Copied';
+        button.querySelector('.copy-feedback').textContent = 'Copied';
         byId('copy-status').textContent = 'Copied to clipboard.';
       } catch {
         const selection = window.getSelection();
@@ -26,9 +27,12 @@ if (typeof document !== 'undefined') {
         range.selectNodeContents(content);
         selection.removeAllRanges();
         selection.addRange(range);
-        button.textContent = 'Text selected';
+        button.querySelector('.copy-feedback').textContent = 'Selected';
         byId('copy-status').textContent = 'Clipboard unavailable. Text selected; use your device’s copy command.';
       }
+      clearTimeout(reset);
+      button.classList.add('is-copied');
+      reset = setTimeout(() => button.classList.remove('is-copied'), 1800);
     });
   });
 }
diff --git a/website/assets/styles.css b/website/assets/styles.css
index 611b2e4..6b26bfb 100644
--- a/website/assets/styles.css
+++ b/website/assets/styles.css
@@ -41,7 +41,6 @@ h1 { font-size:clamp(56px,6vw,84px); font-weight:650; line-height:1.05; letter-s
 .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; }
@@ -56,8 +55,6 @@ strong,b { font-weight:650; }
 .rclc-emphasis { color:var(--rclc); }
 .text-emphasis { color:var(--text); }
 .fixed-emphasis { color:var(--fixed); }
-.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; }
@@ -120,7 +117,6 @@ summary { font-weight:500; }
 .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; }
@@ -132,22 +128,8 @@ summary { font-weight:500; }
 .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; }
@@ -224,14 +206,11 @@ iframe { height:890px; }
 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; }
 .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; }
@@ -248,3 +227,10 @@ html { scroll-behavior:auto; }
 .code-keyword { color:#bacafa; }
 .code-function { color:#8bd9d1; }
 .code-string { color:#e6ce99; }
+
+.copy-button { position:relative; display:inline-grid; place-items:center; min-width:72px; min-height:36px; }
+.copy-icon,.copy-feedback { grid-area:1/1; transition:opacity .6s ease; }
+.copy-feedback { opacity:0; }
+.is-copied .copy-icon { opacity:0; }
+.is-copied .copy-feedback { opacity:1; }
+@media(prefers-reduced-motion:reduce) { .copy-icon,.copy-feedback { transition:none; } }
diff --git a/website/check.mjs b/website/check.mjs
index e3a13e5..09c0af6 100644
--- a/website/check.mjs
+++ b/website/check.mjs
@@ -18,7 +18,7 @@ assert.equal((html.match(//);
 assert.match(html, /name="viewport"/);
 assert.match(html, /CacheBack \|/);
-const sectionOrder = ['result-summary', 'abstract', 'results', 'demo', 'code', 'paper'];
+const sectionOrder = ['result-summary', 'demo', 'abstract', 'results', 'booking-demo', 'code', 'paper'];
 sectionOrder.slice(1).forEach((id, i) => {
   assert.ok(html.indexOf(`id="${sectionOrder[i]}"`) < html.indexOf(`id="${id}"`), `Section order: ${id}`);
 });
@@ -62,7 +62,6 @@ const speedups = [...html.matchAll(/<strong class="speedup">([\d.]+)× faster<\/
 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/);
@@ -70,18 +69,12 @@ 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`);
-}
+// The booking record is rendered by the embedded replay, not duplicated in the page.
+const replay = readFileSync(resolve(project, 'demo/index.html'), 'utf8');
+assert.match(replay, /trace-w4\.json/);
+assert.match(html, /aria-label="Interactive booking demo"/);
+assert.doesNotMatch(html, /Recorded model run|demo-outcomes|hero-description|class="contribution"/);
+for (const label of ['Copy code', 'Copy citation']) assert.ok(html.includes(`aria-label="${label}"`));
 
 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');

From 09caa2ffb55aa22f66a4d76b7617477146024d99 Mon Sep 17 00:00:00 2001
From: Max Rossi <113717219+maxr0ssi@users.noreply.github.com>
Date: Thu, 1 Oct 2026 13:16:08 -0400
Subject: [PATCH 4/4] Remove copy icon borders

---
 website/assets/styles.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/website/assets/styles.css b/website/assets/styles.css
index 6b26bfb..247e686 100644
--- a/website/assets/styles.css
+++ b/website/assets/styles.css
@@ -142,7 +142,7 @@ iframe { display:block; width:100%; height:815px; border:0; background:#fff; }
 .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 { border:0; 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; }