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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# deja examples

Runnable demos for each memory style. Both examples import directly from
package source — no build step required.

## `local/` — In-process vector memory

SQLite + real embeddings (all-MiniLM-L6-v2). Runs anywhere Bun runs.

```bash
cd examples/local
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).

## `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 # installs wrangler
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
154 changes: 154 additions & 0 deletions examples/edge/index.ts
Original file line number Diff line number Diff line change
@@ -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 '../../packages/deja-edge/src/index'

// --- 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<Response> {
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<MemoryDO>
}

export default {
async fetch(request: Request, env: Env): Promise<Response> {
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<Env>

// --- Interactive demo that exercises the full lifecycle ---

async function runDemo(env: Env): Promise<Response> {
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' } })
}
12 changes: 12 additions & 0 deletions examples/edge/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "deja-edge-example",
"private": true,
"type": "module",
"scripts": {
"dev": "npx wrangler dev",
"deploy": "npx wrangler deploy"
},
"devDependencies": {
"wrangler": "^4"
}
}
19 changes: 19 additions & 0 deletions examples/edge/wrangler.json
Original file line number Diff line number Diff line change
@@ -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"]
}
]
}
69 changes: 69 additions & 0 deletions examples/local/index.ts
Original file line number Diff line number Diff line change
@@ -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 '../../packages/deja-local/src/index'

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 ---')
11 changes: 11 additions & 0 deletions examples/local/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "deja-local-example",
"private": true,
"type": "module",
"scripts": {
"start": "bun run index.ts"
},
"dependencies": {
"@huggingface/transformers": "^3.8.1"
}
}
Loading