From d1ecd6cb4817c710faff6d6c55c85c79a39fa979 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 09:40:08 +0000 Subject: [PATCH 1/2] feat: add runnable examples for deja-local and deja-edge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standalone demos that walk through the full memory lifecycle: remember → recall → confirm/reject → forget - examples/local/ — Bun + SQLite + vector embeddings - examples/edge/ — Cloudflare Worker + DO + FTS5 https://claude.ai/code/session_01Fb57JFi8VM36x8pCM1BFTw --- examples/README.md | 41 ++++++++++ examples/edge/index.ts | 154 ++++++++++++++++++++++++++++++++++++ examples/edge/package.json | 15 ++++ examples/edge/wrangler.json | 19 +++++ examples/local/index.ts | 69 ++++++++++++++++ examples/local/package.json | 11 +++ 6 files changed, 309 insertions(+) create mode 100644 examples/README.md create mode 100644 examples/edge/index.ts create mode 100644 examples/edge/package.json create mode 100644 examples/edge/wrangler.json create mode 100644 examples/local/index.ts create mode 100644 examples/local/package.json diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a0fc2cc --- /dev/null +++ b/examples/README.md @@ -0,0 +1,41 @@ +# deja examples + +Runnable demos for each memory style. + +## `local/` — In-process vector memory + +SQLite + real embeddings (all-MiniLM-L6-v2). Runs anywhere Bun runs. + +```bash +cd examples/local +bun install +bun run start +``` + +First run downloads the embedding model (~23MB, cached after that). + +## `edge/` — Edge memory for Cloudflare Workers + +FTS5 full-text search inside a Durable Object. No embeddings, no external deps. + +```bash +cd examples/edge +npm install +npx wrangler dev +# then visit http://localhost:8787/demo +``` + +Or deploy it: + +```bash +npx wrangler deploy +``` + +## Memory lifecycle + +Both examples walk through the same lifecycle: + +1. **remember** — store memories (with automatic deduplication) +2. **recall** — search by context (vector similarity or full-text) +3. **confirm/reject** — feedback loop to adjust confidence +4. **forget** — remove a memory entirely diff --git a/examples/edge/index.ts b/examples/edge/index.ts new file mode 100644 index 0000000..0d2fd56 --- /dev/null +++ b/examples/edge/index.ts @@ -0,0 +1,154 @@ +/** + * deja-edge example — Edge memory inside a Cloudflare Durable Object. + * + * Deploy: cd examples/edge && npx wrangler deploy + * Dev: cd examples/edge && npx wrangler dev + * + * Then hit the endpoints: + * curl http://localhost:8787/demo + * + * Uses FTS5 full-text search. No embeddings, no external deps. + */ + +import { createEdgeMemory, type EdgeMemoryStore } from 'deja-edge' + +// --- Durable Object: wraps deja-edge memory with HTTP routes --- + +export class MemoryDO extends DurableObject { + private memory: EdgeMemoryStore + + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env) + this.memory = createEdgeMemory(ctx) + } + + async fetch(request: Request): Promise { + const url = new URL(request.url) + const path = url.pathname + + // POST /remember { text: "..." } + if (request.method === 'POST' && path === '/remember') { + const { text } = await request.json<{ text: string }>() + return Response.json(this.memory.remember(text), { status: 201 }) + } + + // POST /recall { context: "..." } + if (request.method === 'POST' && path === '/recall') { + const { context } = await request.json<{ context: string }>() + return Response.json(this.memory.recall(context)) + } + + // POST /confirm/:id + if (request.method === 'POST' && path.startsWith('/confirm/')) { + const id = path.slice('/confirm/'.length) + return Response.json({ ok: this.memory.confirm(id) }) + } + + // POST /reject/:id + if (request.method === 'POST' && path.startsWith('/reject/')) { + const id = path.slice('/reject/'.length) + return Response.json({ ok: this.memory.reject(id) }) + } + + // DELETE /forget/:id + if (request.method === 'DELETE' && path.startsWith('/forget/')) { + const id = path.slice('/forget/'.length) + return Response.json({ ok: this.memory.forget(id) }) + } + + // GET /list + if (request.method === 'GET' && path === '/list') { + return Response.json(this.memory.list()) + } + + // GET /size + if (request.method === 'GET' && path === '/size') { + return Response.json({ size: this.memory.size }) + } + + return Response.json({ error: 'not found' }, { status: 404 }) + } +} + +// --- Worker: routes requests to the DO --- + +interface Env { + MEMORY: DurableObjectNamespace +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url) + + // /demo — run the full lifecycle and return the results + if (url.pathname === '/demo') { + return runDemo(env) + } + + // Everything else → forward to the DO + const id = env.MEMORY.idFromName('default') + const stub = env.MEMORY.get(id) + return stub.fetch(request) + }, +} satisfies ExportedHandler + +// --- Interactive demo that exercises the full lifecycle --- + +async function runDemo(env: Env): Promise { + const id = env.MEMORY.idFromName('demo') + const stub = env.MEMORY.get(id) + const base = 'http://do' + const log: string[] = [] + + log.push('--- deja-edge demo ---\n') + + // 1. Remember + log.push('1. Remembering things...') + const m1 = await stub.fetch(new Request(`${base}/remember`, { + method: 'POST', + body: JSON.stringify({ text: 'Always run tests before deploying to production' }), + })).then(r => r.json()) as { id: string; text: string } + + const m2 = await stub.fetch(new Request(`${base}/remember`, { + method: 'POST', + body: JSON.stringify({ text: 'The API rate limit is 100 requests per minute' }), + })).then(r => r.json()) as { id: string; text: string } + + const m3 = await stub.fetch(new Request(`${base}/remember`, { + method: 'POST', + body: JSON.stringify({ text: 'Use wrangler.toml for Cloudflare Workers config' }), + })).then(r => r.json()) as { id: string; text: string } + + const size = await stub.fetch(new Request(`${base}/size`)).then(r => r.json()) as { size: number } + log.push(` Stored ${size.size} memories\n`) + + // 2. Recall + log.push('2. Recalling "deploying production"...') + const results = await stub.fetch(new Request(`${base}/recall`, { + method: 'POST', + body: JSON.stringify({ context: 'deploying production' }), + })).then(r => r.json()) as Array<{ id: string; text: string; score: number }> + + for (const r of results) { + log.push(` [score=${r.score.toFixed(3)}] ${r.text}`) + } + + // 3. Confirm + if (results.length > 0) { + log.push('\n3. Confirming top result...') + await stub.fetch(new Request(`${base}/confirm/${results[0].id}`, { method: 'POST' })) + log.push(` Confirmed: "${results[0].text}"`) + } + + // 4. Forget + log.push('\n4. Forgetting a memory...') + await stub.fetch(new Request(`${base}/forget/${m3.id}`, { method: 'DELETE' })) + log.push(` Forgot: "${m3.text}"`) + + const finalSize = await stub.fetch(new Request(`${base}/size`)).then(r => r.json()) as { size: number } + log.push(` Memories remaining: ${finalSize.size}`) + + log.push('\n--- done ---') + + return new Response(log.join('\n'), { headers: { 'Content-Type': 'text/plain' } }) +} diff --git a/examples/edge/package.json b/examples/edge/package.json new file mode 100644 index 0000000..78ab11c --- /dev/null +++ b/examples/edge/package.json @@ -0,0 +1,15 @@ +{ + "name": "deja-edge-example", + "private": true, + "type": "module", + "scripts": { + "dev": "npx wrangler dev", + "deploy": "npx wrangler deploy" + }, + "dependencies": { + "deja-edge": "workspace:*" + }, + "devDependencies": { + "wrangler": "^4" + } +} diff --git a/examples/edge/wrangler.json b/examples/edge/wrangler.json new file mode 100644 index 0000000..88d086e --- /dev/null +++ b/examples/edge/wrangler.json @@ -0,0 +1,19 @@ +{ + "name": "deja-edge-example", + "main": "index.ts", + "compatibility_date": "2024-12-01", + "durable_objects": { + "bindings": [ + { + "name": "MEMORY", + "class_name": "MemoryDO" + } + ] + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["MemoryDO"] + } + ] +} diff --git a/examples/local/index.ts b/examples/local/index.ts new file mode 100644 index 0000000..912c94a --- /dev/null +++ b/examples/local/index.ts @@ -0,0 +1,69 @@ +/** + * deja-local example — In-process vector memory for agents. + * + * Run: bun run examples/local/index.ts + * + * This demo walks through the full memory lifecycle: + * remember → recall → confirm/reject → forget + * + * Uses SQLite + real embeddings (all-MiniLM-L6-v2). + * First run downloads the model (~23MB, cached after that). + */ + +import { createMemory } from 'deja-local' + +const mem = await createMemory({ path: './demo-memory.db' }) + +console.log('--- deja-local demo ---\n') + +// 1. Remember — store some memories +console.log('1. Remembering things...') +const m1 = await mem.remember('Always run tests before deploying to production') +const m2 = await mem.remember('The API rate limit is 100 requests per minute') +const m3 = await mem.remember('Use wrangler.toml for Cloudflare Workers config') +console.log(` Stored ${mem.size} memories\n`) + +// 2. Recall — semantic search finds relevant memories +console.log('2. Recalling "how to deploy safely"...') +const results = await mem.recall('how to deploy safely') +for (const r of results) { + console.log(` [score=${r.score.toFixed(3)}] ${r.text}`) +} + +// 3. Confirm — boost confidence of useful memories +console.log('\n3. Confirming the top result was helpful...') +if (results.length > 0) { + mem.confirm(results[0].id) + console.log(` Confirmed: "${results[0].text}"`) +} + +// 4. Reject — lower confidence of unhelpful memories +console.log('\n4. Rejecting the last result...') +if (results.length > 1) { + const last = results[results.length - 1] + mem.reject(last.id) + console.log(` Rejected: "${last.text}"`) +} + +// 5. Deduplication — similar memories are merged, not duplicated +console.log('\n5. Testing deduplication...') +const sizeBefore = mem.size +await mem.remember('Always run tests before deploying to production!') +console.log(` Size before: ${sizeBefore}, after: ${mem.size} (deduped: ${sizeBefore === mem.size})`) + +// 6. Forget — remove a memory entirely +console.log('\n6. Forgetting a memory...') +mem.forget(m3.id) +console.log(` Forgot: "${m3.text}"`) +console.log(` Memories remaining: ${mem.size}`) + +// 7. Audit log — see what the agent recalled and when +console.log('\n7. Recall audit log:') +const log = mem.recallLog({ limit: 3 }) +for (const entry of log) { + console.log(` [${entry.timestamp}] query="${entry.context}" → ${entry.results.length} results`) +} + +// Cleanup +mem.close() +console.log('\n--- done ---') diff --git a/examples/local/package.json b/examples/local/package.json new file mode 100644 index 0000000..c7b2d72 --- /dev/null +++ b/examples/local/package.json @@ -0,0 +1,11 @@ +{ + "name": "deja-local-example", + "private": true, + "type": "module", + "scripts": { + "start": "bun run index.ts" + }, + "dependencies": { + "deja-local": "workspace:*" + } +} From 6b1d109fde82a86611bb5a502554ac96962bf716 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 09:50:12 +0000 Subject: [PATCH 2/2] fix(examples): replace workspace:* deps with direct source imports Addresses PR #11 review comments: - P1: workspace:* references failed because repo has no workspaces config - P2: packages only export dist/ which isn't checked in Examples now import directly from package source via relative paths. Both Bun and wrangler's bundler handle TS natively, so no build step needed. https://claude.ai/code/session_01Fb57JFi8VM36x8pCM1BFTw --- examples/README.md | 9 +++++---- examples/edge/index.ts | 2 +- examples/edge/package.json | 3 --- examples/local/index.ts | 2 +- examples/local/package.json | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/README.md b/examples/README.md index a0fc2cc..13d4ea3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,7 @@ # deja examples -Runnable demos for each memory style. +Runnable demos for each memory style. Both examples import directly from +package source — no build step required. ## `local/` — In-process vector memory @@ -8,8 +9,8 @@ SQLite + real embeddings (all-MiniLM-L6-v2). Runs anywhere Bun runs. ```bash cd examples/local -bun install -bun run start +bun install # installs @huggingface/transformers +bun run start # or: bun run examples/local/index.ts from repo root ``` First run downloads the embedding model (~23MB, cached after that). @@ -20,7 +21,7 @@ FTS5 full-text search inside a Durable Object. No embeddings, no external deps. ```bash cd examples/edge -npm install +npm install # installs wrangler npx wrangler dev # then visit http://localhost:8787/demo ``` diff --git a/examples/edge/index.ts b/examples/edge/index.ts index 0d2fd56..63adb1a 100644 --- a/examples/edge/index.ts +++ b/examples/edge/index.ts @@ -10,7 +10,7 @@ * Uses FTS5 full-text search. No embeddings, no external deps. */ -import { createEdgeMemory, type EdgeMemoryStore } from 'deja-edge' +import { createEdgeMemory, type EdgeMemoryStore } from '../../packages/deja-edge/src/index' // --- Durable Object: wraps deja-edge memory with HTTP routes --- diff --git a/examples/edge/package.json b/examples/edge/package.json index 78ab11c..a3ca125 100644 --- a/examples/edge/package.json +++ b/examples/edge/package.json @@ -6,9 +6,6 @@ "dev": "npx wrangler dev", "deploy": "npx wrangler deploy" }, - "dependencies": { - "deja-edge": "workspace:*" - }, "devDependencies": { "wrangler": "^4" } diff --git a/examples/local/index.ts b/examples/local/index.ts index 912c94a..788aea5 100644 --- a/examples/local/index.ts +++ b/examples/local/index.ts @@ -10,7 +10,7 @@ * First run downloads the model (~23MB, cached after that). */ -import { createMemory } from 'deja-local' +import { createMemory } from '../../packages/deja-local/src/index' const mem = await createMemory({ path: './demo-memory.db' }) diff --git a/examples/local/package.json b/examples/local/package.json index c7b2d72..60c3427 100644 --- a/examples/local/package.json +++ b/examples/local/package.json @@ -6,6 +6,6 @@ "start": "bun run index.ts" }, "dependencies": { - "deja-local": "workspace:*" + "@huggingface/transformers": "^3.8.1" } }