Skip to content

Commit ae9166f

Browse files
Merge main into node-modules-operations
Both branches assigned schema version 6 to independent migrations. Keep the path-aware push cursor at version 6 and move the tombstone index to version 7 so existing workspaces apply both upgrades in order.
2 parents 0f0f397 + fc5b743 commit ae9166f

102 files changed

Lines changed: 22313 additions & 931 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ public surface. Each is a Worker workspace with its own README.
6666
- [`examples/mcp`](examples/mcp) — a Computer MCP example:
6767
one Code Mode `code` tool backed by a durable workspace, a Worker shell,
6868
and a full Linux container.
69+
- [`examples/rlm`](examples/rlm) — shows how generated JavaScript can read long
70+
context from a Computer Workspace, call bounded model workers, and reduce their
71+
structured results with code.
6972
- [`examples/think`](examples/think) — a [`@cloudflare/think`](https://www.npmjs.com/package/@cloudflare/think)
7073
chat agent that uses the workspace as its working directory, reachable
7174
from a terminal.

docs/08_capnweb_interface.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ interface SyncRPC {
8686
// when it wants to wait for the wire to drain. pushRev /
8787
// fetchCursor only move when the receiver is acting as a sync
8888
// peer; otherwise they sit at 0 / { rev: 0, path: null }.
89-
watermarks(): Promise<{
89+
// `settle` runs the same disk-to-VFS reconciliation as
90+
// fetchChanges before reading currentRev. Deferred command sync
91+
// uses it to capture a target that includes the command's writes.
92+
watermarks(input?: { settle?: boolean }): Promise<{
9093
currentRev: number;
9194
pushRev: number;
9295
fetchCursor: { rev: number; path: string | null };

examples/artifacts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"devDependencies": {
1616
"@cloudflare/workers-types": "^4.20260616.1",
1717
"typescript": "^6.0.3",
18-
"wrangler": "^4.107.1"
18+
"wrangler": "^4.130.0"
1919
}
2020
}

examples/assets/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"devDependencies": {
1616
"@cloudflare/workers-types": "^4.20260616.1",
1717
"typescript": "^6.0.3",
18-
"wrangler": "^4.107.1"
18+
"wrangler": "^4.130.0"
1919
}
2020
}

examples/container/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"devDependencies": {
1818
"@cloudflare/workers-types": "^4.20260616.1",
1919
"typescript": "^6.0.3",
20-
"wrangler": "^4.107.1"
20+
"wrangler": "^4.130.0"
2121
}
2222
}

examples/egress/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
},
1616
"devDependencies": {
1717
"typescript": "^6.0.3",
18-
"vitest": "^4.1.7",
19-
"wrangler": "^4.107.1"
18+
"vitest": "^4.1.11",
19+
"wrangler": "^4.130.0"
2020
}
2121
}

examples/mcp/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
"zod": "^4.4.3"
2222
},
2323
"devDependencies": {
24-
"@cloudflare/vitest-pool-workers": "^0.16.10",
24+
"@cloudflare/vitest-pool-workers": "^0.22.0",
2525
"@cloudflare/workers-types": "^5.20260722.1",
2626
"typescript": "^6.0.3",
27-
"vitest": "^4.1.7",
28-
"wrangler": "^4.107.1"
27+
"vitest": "^4.1.11",
28+
"wrangler": "^4.130.0"
2929
}
3030
}

examples/rlm/.dev.vars.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Optional local fallback when a remote Workers AI binding is protected by Cloudflare Access.
2+
# Copy this file to .dev.vars and create a token with Workers AI read access.
3+
# The AI binding remains the default unless this fallback is explicitly enabled.
4+
WORKERS_AI_USE_REST=true
5+
CLOUDFLARE_ACCOUNT_ID=your-account-id
6+
CLOUDFLARE_API_TOKEN=your-workers-ai-api-token

examples/rlm/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.dev.vars
2+
.wrangler/
3+
dist/

examples/rlm/README.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Build an RLM with Cloudflare Computer
2+
3+
This example shows how to build a recursive language model (RLM) with [`@cloudflare/computer`](../../packages/computer).
4+
5+
The main idea is:
6+
7+
> Use models for meaning. Use code for totals.
8+
9+
A long corpus lives in a Computer Workspace instead of the parent model's prompt. The model writes a JavaScript module that reads the corpus, sends bounded partitions to model workers, validates their structured answers, and combines them with normal code.
10+
11+
```text
12+
long context in a Workspace
13+
14+
generated JavaScript
15+
16+
bounded model calls over partitions
17+
18+
JavaScript validation and reduction
19+
20+
answer
21+
```
22+
23+
The browser runs the same long-context task three ways so you can see what the RLM adds:
24+
25+
| Strategy | What it can use |
26+
|---|---|
27+
| Direct context | One model call with the complete corpus in its prompt |
28+
| JavaScript only | A Workspace and generated JavaScript |
29+
| Structured RLM | A Workspace, generated JavaScript, and bounded model calls through `ws:model` |
30+
31+
## Why use this pattern?
32+
33+
RLMs are useful when a task needs broad semantic coverage across more data than you want to place in one prompt. Examples include:
34+
35+
- Classifying every record in a large collection, then counting the labels.
36+
- Extracting events or entities from many documents, then sorting or grouping them.
37+
- Comparing evidence across many files instead of retrieving only the top few matches.
38+
- Keeping a large working set in durable storage while returning a small answer to the parent model.
39+
40+
The model workers handle questions such as “is this sentence formal?” or “was this spell upcast?” JavaScript handles operations such as counting, validation, ordering, deduplication, and tie-breaking.
41+
42+
This pattern is less useful when the context already fits comfortably in one prompt, retrieval can find a small relevant subset, or ordinary code can solve the whole task without semantic interpretation.
43+
44+
## How the example works
45+
46+
1. The host fetches an official Oolong task and writes its corpus and manifest to a Workspace.
47+
2. The parent model receives the question, file paths, and the available Computer tool. It does not receive the complete corpus.
48+
3. The parent writes a complete ECMAScript module for the Worker JavaScript backend.
49+
4. That module reads record-aligned partitions from the Workspace.
50+
5. The module calls `ws:model` with a bounded batch of classification or extraction requests.
51+
6. The module validates the returned JSON and reduces it to one answer with JavaScript.
52+
7. The UI shows the generated module, map calls, token use, duration, and official Oolong score.
53+
54+
The default task classifies 2,433 sentences as formal or informal. The RLM maps 22 partitions through model workers and then adds the returned label counts exactly.
55+
56+
## The Computer pieces
57+
58+
The host gives generated code a read-only Workspace and one trusted model module:
59+
60+
```ts
61+
const modelCapability = createModelCapability(model, hooks);
62+
63+
const backend = new WorkerJavaScriptBackend({
64+
id: "oolong-rlm-javascript",
65+
loader: env.LOADER,
66+
root: "/workspace",
67+
access: "read",
68+
egress: { mode: "none" },
69+
trustedModules: {
70+
"ws:model": modelCapability,
71+
},
72+
});
73+
74+
const workspace = new Workspace({
75+
storage: ctx.storage,
76+
backends: [backend],
77+
});
78+
```
79+
80+
The important line is `trustedModules`. Generated code cannot read model credentials or call the network directly. It can only use the host-owned `ws:model` interface.
81+
82+
A generated module follows this shape:
83+
84+
```js
85+
import fs from "node:fs/promises";
86+
import { call as callModel } from "ws:model";
87+
88+
export default async function () {
89+
const manifest = JSON.parse(
90+
await fs.readFile("/workspace/oolong-real/manifest.json", "utf8"),
91+
);
92+
const requests = await Promise.all(
93+
manifest.contextChunks.map(async (chunk) => ({
94+
prompt: "Classify every record and return JSON label counts.",
95+
input: await fs.readFile(chunk.path, "utf8"),
96+
})),
97+
);
98+
99+
const mapped = await callModel("batch", requests);
100+
const totals = validateAndSum(mapped);
101+
return { answer: largestLabel(totals) };
102+
}
103+
```
104+
105+
The real module is written by the parent model at runtime. Open **Generated map/reduce module** in the RLM lane to inspect it.
106+
107+
## Run it locally
108+
109+
Workers AI is remote during local development. A complete comparison can take several minutes, use more than 200,000 model tokens, and incur usage charges.
110+
111+
From the repository root:
112+
113+
```sh
114+
npm install
115+
npm run build --workspace @cloudflare/computer
116+
npx wrangler login
117+
npm run dev --workspace @cloudflare/example-rlm
118+
```
119+
120+
Open the Vite URL, normally <http://localhost:5173>, and choose **Run comparison**. The Structured RLM lane starts first. As soon as its map work begins, both baselines start together so the expensive parts overlap without making the RLM parent compete to author its program. You can cancel the complete comparison or stop one lane. Durations reflect shared provider load and are not controlled benchmark timings.
121+
122+
The authenticated Workers AI binding is the default. If local Access policy blocks that binding, copy the optional REST fallback:
123+
124+
```sh
125+
cp examples/rlm/.dev.vars.example examples/rlm/.dev.vars
126+
```
127+
128+
Set `WORKERS_AI_USE_REST=true`, `CLOUDFLARE_ACCOUNT_ID`, and `CLOUDFLARE_API_TOKEN`, then restart Vite. `.dev.vars` is ignored and must not be committed.
129+
130+
## What to look for
131+
132+
Start with **Classify 2,433 records** and compare the lanes:
133+
134+
- **Direct context** asks one model to interpret and aggregate the whole corpus in one generation.
135+
- **JavaScript only** can read every byte and count perfectly, but it has no semantic function that labels the sentences.
136+
- **Structured RLM** exposes semantic inference as a bounded function inside generated JavaScript.
137+
138+
In the RLM lane, look for:
139+
140+
- One map worker for each Workspace partition.
141+
- A compact JSON schema for each semantic map call.
142+
- `Validated model JSON → JavaScript reduce`.
143+
- Separate parent, map, and total token counts.
144+
- The complete generated module and its bounded answer.
145+
146+
The reducer is exact relative to its inputs. Model classifications can still be wrong, so an RLM does not make probabilistic inference deterministic.
147+
148+
## Explore the code
149+
150+
The shortest path through the example is:
151+
152+
1. [`worker/rlm-agent.ts`](worker/rlm-agent.ts) wires together the model, Workspace, Worker JavaScript backend, executor tool, and `ws:model`.
153+
2. [`worker/capability.ts`](worker/capability.ts) implements the bounded `ws:model("batch", requests)` interface.
154+
3. [`worker/structured-rlm.ts`](worker/structured-rlm.ts) describes the map result and JavaScript reduction for each task family.
155+
4. [`worker/agent-common.ts`](worker/agent-common.ts) writes the same corpus into each Computer Workspace.
156+
5. [`worker/executor-tool.ts`](worker/executor-tool.ts) creates the native Computer executor tool and keeps its browser-facing result small.
157+
6. [`src/App.tsx`](src/App.tsx) runs the three strategies and renders their traces side by side.
158+
159+
The two baselines live in [`worker/direct-agent.ts`](worker/direct-agent.ts) and [`worker/executor-agent.ts`](worker/executor-agent.ts). Most other files load and score Oolong data or render the comparison UI; the RLM integration itself is concentrated in the first three files above.
160+
161+
## Bounds used by the example
162+
163+
`ws:model` is deliberately small and predictable:
164+
165+
- Up to 24 model requests in one run.
166+
- Up to four model calls at a time.
167+
- Up to 16 KiB of instructions and 48 KiB of input per request.
168+
- Up to 1,024 output tokens per request.
169+
- One host-selected model for parent and map calls: `@cf/zai-org/glm-5.2`.
170+
- Abort propagation from the browser through generated execution and active model calls.
171+
172+
These are example limits, not requirements of `@cloudflare/computer`. Change them in [`worker/capability.ts`](worker/capability.ts) to match your task and model.
173+
174+
## Demo data
175+
176+
The UI uses official [Oolong-synth](https://huggingface.co/datasets/oolongbench/oolong-synth) and [Oolong-real](https://huggingface.co/datasets/oolongbench/oolong-real) tasks. It fetches rows at runtime, checks their complete source hashes, and scores answers with a TypeScript version of the official evaluator.
177+
178+
The selected rows, revisions, and hashes live in [`benchmarks/development.json`](benchmarks/development.json). Raw dataset rows are not stored in this repository.
179+
180+
This is an independent Computer-backed example of the RLM method described in [Prime Intellect's RLM evaluation](https://www.primeintellect.ai/blog/rlm), not a reproduction of their run or an aggregate benchmark. The task-aware schemas and reducers make this a **Structured RLM** example.
181+
182+
Oolong-real is derived from CRD3 material licensed CC BY-SA 4.0. Oolong-synth's Hugging Face card does not declare a dataset license. Review the upstream terms before redistributing fetched data.
183+
184+
## Verify changes
185+
186+
```sh
187+
npm test --workspace @cloudflare/example-rlm
188+
npm run typecheck --workspace @cloudflare/example-rlm
189+
npm run build --workspace @cloudflare/example-rlm
190+
```
191+
192+
The build checks that local secret files and values do not appear in `dist`.
193+
194+
## Optional deployment
195+
196+
This repository example is designed for learning and local experiments, not as a public multi-user application. If you deploy it, set a strong token before exposing the Worker:
197+
198+
```sh
199+
cd examples/rlm
200+
openssl rand -hex 32 | npx wrangler secret put DEMO_TOKEN
201+
cd ../..
202+
npm run deploy --workspace @cloudflare/example-rlm
203+
```
204+
205+
The token protects access to the costly model routes. Add your own access control, rate limits, usage monitoring, and Workspace retention policy for a shared or long-lived deployment.

0 commit comments

Comments
 (0)