audrey 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +310 -643
- package/benchmarks/baselines.js +169 -0
- package/benchmarks/cases.js +421 -0
- package/benchmarks/reference-results.js +70 -0
- package/benchmarks/report.js +255 -0
- package/benchmarks/run.js +514 -0
- package/docs/assets/benchmarks/local-benchmark.svg +45 -0
- package/docs/assets/benchmarks/operations-benchmark.svg +45 -0
- package/docs/assets/benchmarks/published-memory-standards.svg +50 -0
- package/docs/benchmarking.md +151 -0
- package/docs/production-readiness.md +96 -0
- package/examples/fintech-ops-demo.js +67 -0
- package/examples/healthcare-ops-demo.js +67 -0
- package/examples/stripe-demo.js +105 -0
- package/mcp-server/config.js +81 -24
- package/mcp-server/index.js +611 -75
- package/mcp-server/serve.js +482 -0
- package/package.json +24 -5
- package/src/audrey.js +51 -13
- package/src/consolidate.js +70 -54
- package/src/db.js +22 -1
- package/src/embedding.js +16 -12
- package/src/encode.js +8 -2
- package/src/fts.js +134 -0
- package/src/import.js +28 -0
- package/src/llm.js +6 -3
- package/src/migrate.js +2 -2
- package/src/recall.js +253 -32
- package/src/utils.js +25 -0
- package/types/index.d.ts +434 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Benchmarking Audrey
|
|
2
|
+
|
|
3
|
+
Audrey now ships with a memory benchmark harness that does three different jobs:
|
|
4
|
+
|
|
5
|
+
1. It runs Audrey against a local retrieval suite inspired by LongMemEval, plus privacy and abstention checks that matter in production.
|
|
6
|
+
2. It runs Audrey against an operation-level suite for update, overwrite, delete, merge, and abstain behavior.
|
|
7
|
+
3. It overlays published leaderboard numbers from leading memory systems on LoCoMo so you can place Audrey in the current market and research landscape without pretending the measurements are identical.
|
|
8
|
+
|
|
9
|
+
That split is deliberate. A lot of memory tooling mixes internal demos with external benchmark claims. Audrey should not do that.
|
|
10
|
+
|
|
11
|
+
## Run It
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm run bench:memory
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The package script is the intended operator entrypoint:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run bench:memory
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Artifacts are written to `benchmarks/output/`:
|
|
24
|
+
|
|
25
|
+
- `summary.json`
|
|
26
|
+
- `report.html`
|
|
27
|
+
- `local-overall.svg`
|
|
28
|
+
- `retrieval-overall.svg`
|
|
29
|
+
- `operations-overall.svg`
|
|
30
|
+
- `published-locomo.svg`
|
|
31
|
+
|
|
32
|
+
For CI, JSON-only output is available:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run bench:memory:json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For regression gating, use:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm run bench:memory:check
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That command fails if Audrey falls below its minimum local score, pass rate, or required lead over the strongest naive baseline.
|
|
45
|
+
|
|
46
|
+
To refresh the committed SVGs used in the README:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm run bench:memory:readme-assets
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That writes stable chart assets to `docs/assets/benchmarks/` so the GitHub repo surface shows the same benchmark posture as the generated report.
|
|
53
|
+
|
|
54
|
+
To run a single local track:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run bench:memory:retrieval
|
|
58
|
+
npm run bench:memory:operations
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## What The Local Retrieval Benchmark Measures
|
|
62
|
+
|
|
63
|
+
The retrieval suite covers eight memory families:
|
|
64
|
+
|
|
65
|
+
- `information_extraction`
|
|
66
|
+
- `knowledge_updates`
|
|
67
|
+
- `multi_session_reasoning`
|
|
68
|
+
- `temporal_reasoning`
|
|
69
|
+
- `abstention`
|
|
70
|
+
- `conflict_resolution`
|
|
71
|
+
- `procedural_learning`
|
|
72
|
+
- `privacy_boundary`
|
|
73
|
+
|
|
74
|
+
This is intentionally closer to how operators evaluate memory in production than a single retrieval-accuracy number. Audrey should not only retrieve facts. It should:
|
|
75
|
+
|
|
76
|
+
- prefer fresh state over stale state
|
|
77
|
+
- avoid leaking private memory
|
|
78
|
+
- consolidate repeated episodes into reusable procedures
|
|
79
|
+
- handle conflict without amplifying low-reliability noise
|
|
80
|
+
|
|
81
|
+
## What The Local Operations Benchmark Measures
|
|
82
|
+
|
|
83
|
+
The operations suite covers four lifecycle families:
|
|
84
|
+
|
|
85
|
+
- `update_overwrite`
|
|
86
|
+
- `delete_and_abstain`
|
|
87
|
+
- `semantic_merge`
|
|
88
|
+
- `procedural_merge`
|
|
89
|
+
|
|
90
|
+
This suite exists because leading memory systems are often compared on offline recall, while real agent memory succeeds or fails on memory operations:
|
|
91
|
+
|
|
92
|
+
- can a newer fact overwrite stale state without leaking both
|
|
93
|
+
- can a delete actually prevent future recall
|
|
94
|
+
- can repeated raw events merge into reusable semantic knowledge
|
|
95
|
+
- can repeated events merge into an actionable procedure instead of another inert blob of text
|
|
96
|
+
|
|
97
|
+
Those are not implementation details. They are the actual product surface of memory.
|
|
98
|
+
|
|
99
|
+
## What The Published Leaderboard Means
|
|
100
|
+
|
|
101
|
+
The LoCoMo chart in the generated report is a research context layer, not a claim that Audrey has already reproduced those exact scores.
|
|
102
|
+
|
|
103
|
+
Current published anchors included in the report:
|
|
104
|
+
|
|
105
|
+
- MIRIX: LoCoMo `85.4` from the MIRIX paper
|
|
106
|
+
- Letta Filesystem: LoCoMo `74.0` from Letta's benchmark write-up
|
|
107
|
+
- Mem0 Graph Memory: LoCoMo `68.5` from the Mem0 paper
|
|
108
|
+
- Mem0: LoCoMo `66.9` from the Mem0 paper
|
|
109
|
+
- OpenAI Memory baseline: LoCoMo `52.9` as reported in the Mem0 paper
|
|
110
|
+
|
|
111
|
+
Use this chart to answer: "Where is the frontier today?" not "Has Audrey already matched that exact benchmark protocol?"
|
|
112
|
+
|
|
113
|
+
## March 23, 2026 Research Readout
|
|
114
|
+
|
|
115
|
+
The most important memory trends right now:
|
|
116
|
+
|
|
117
|
+
1. Typed memory systems are replacing flat retrieval.
|
|
118
|
+
MemOS frames memory as an operating system concern with scheduling and memory-object abstractions, not just vector lookup.
|
|
119
|
+
|
|
120
|
+
2. Realistic long-horizon benchmarks are replacing toy recall tests.
|
|
121
|
+
LongMemEval emphasizes multi-session reasoning, temporal updates, abstraction, and knowledge revision.
|
|
122
|
+
|
|
123
|
+
3. Context engineering is now a first-class competitor to retrieval-only memory.
|
|
124
|
+
Letta's filesystem and memory-block work argues that editable context structure can outperform simpler retrieval-only designs.
|
|
125
|
+
|
|
126
|
+
4. Production memory is now judged on latency and token cost too.
|
|
127
|
+
Mem0 explicitly reports quality alongside lower token and latency overhead.
|
|
128
|
+
|
|
129
|
+
5. Temporal and multimodal memory are moving into the frontier.
|
|
130
|
+
MIRIX pushes beyond text-only episodic recall into typed multimodal memory with compression.
|
|
131
|
+
|
|
132
|
+
## What Audrey Should Do Next
|
|
133
|
+
|
|
134
|
+
The benchmark highlights the next credible roadmap for Audrey:
|
|
135
|
+
|
|
136
|
+
- first-party LoCoMo and LongMemEval adapters so Audrey can publish directly reproducible external benchmark numbers
|
|
137
|
+
- contradiction-state and truth-resolution benchmark cases, not just retrieval outcomes
|
|
138
|
+
- cost, latency, and storage curves against long-context baselines and simpler memory systems
|
|
139
|
+
- a typed memory graph layer for cross-memory state transitions and time-aware reasoning
|
|
140
|
+
|
|
141
|
+
## Source Links
|
|
142
|
+
|
|
143
|
+
- LongMemEval: [arXiv 2410.10813](https://arxiv.org/abs/2410.10813)
|
|
144
|
+
- Mem0: [arXiv 2504.19413](https://arxiv.org/abs/2504.19413)
|
|
145
|
+
- MIRIX: [arXiv 2507.07957](https://arxiv.org/abs/2507.07957)
|
|
146
|
+
- MemOS: [arXiv 2507.03724](https://arxiv.org/abs/2507.03724)
|
|
147
|
+
- MemGPT: [arXiv 2310.08560](https://arxiv.org/abs/2310.08560)
|
|
148
|
+
- Letta memory blocks: [Letta blog](https://www.letta.com/blog/memory-blocks)
|
|
149
|
+
- Letta benchmarking: [Letta benchmark write-up](https://www.letta.com/blog/benchmarking-ai-agent-memory)
|
|
150
|
+
- LoCoMo benchmark repo: [snap-research/locomo](https://github.com/snap-research/locomo)
|
|
151
|
+
- LongMemEval repo: [xiaowu0162/LongMemEval](https://github.com/xiaowu0162/LongMemEval)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Audrey Production Readiness
|
|
2
|
+
|
|
3
|
+
Audrey is ready to be the memory layer inside a production agent system, but it is not a complete regulated-platform package by itself. Treat it as stateful infrastructure: pin providers, isolate tenants, monitor health, and wrap it with the controls your environment requires.
|
|
4
|
+
|
|
5
|
+
## Best Vertical Fit
|
|
6
|
+
|
|
7
|
+
### 1. Financial Services Operations
|
|
8
|
+
|
|
9
|
+
Best fit:
|
|
10
|
+
|
|
11
|
+
- Payments operations copilots
|
|
12
|
+
- Fraud and dispute investigation agents
|
|
13
|
+
- KYC/KYB review assistants
|
|
14
|
+
- Internal support agents that need durable incident and policy memory
|
|
15
|
+
|
|
16
|
+
Why Audrey fits:
|
|
17
|
+
|
|
18
|
+
- Contradiction tracking helps surface conflicting customer, tool, and policy evidence.
|
|
19
|
+
- Confidence scoring and source lineage make escalations more reviewable.
|
|
20
|
+
- Local SQLite storage keeps memory close to the application boundary.
|
|
21
|
+
- Dream-cycle consolidation turns repeated incidents into reusable operational principles.
|
|
22
|
+
|
|
23
|
+
Guardrails:
|
|
24
|
+
|
|
25
|
+
- Do not store PAN, CVV, raw bank credentials, or secrets in memory.
|
|
26
|
+
- Isolate memory stores by environment, customer, and business unit.
|
|
27
|
+
- Keep export and purge paths in your incident-response runbook.
|
|
28
|
+
- Add encryption at rest and backup retention outside Audrey.
|
|
29
|
+
|
|
30
|
+
### 2. Healthcare Operations
|
|
31
|
+
|
|
32
|
+
Best fit:
|
|
33
|
+
|
|
34
|
+
- Care coordination assistants
|
|
35
|
+
- Prior-authorization workflow agents
|
|
36
|
+
- Intake, referral, and scheduling copilots
|
|
37
|
+
- Internal knowledge assistants for clinical operations teams
|
|
38
|
+
|
|
39
|
+
Why Audrey fits:
|
|
40
|
+
|
|
41
|
+
- Longitudinal recall preserves operational context across multi-step handoffs.
|
|
42
|
+
- Private memories support role-specific context without making it part of public recall.
|
|
43
|
+
- Contradiction detection helps catch conflicting workflow instructions and stale operating assumptions.
|
|
44
|
+
- Local embeddings allow offline-first or reduced-data-egress deployments.
|
|
45
|
+
|
|
46
|
+
Guardrails:
|
|
47
|
+
|
|
48
|
+
- Audrey is not a medical device and should not be treated as a clinical decision engine.
|
|
49
|
+
- Use de-identified or minimum-necessary data unless the full deployment boundary is HIPAA-ready.
|
|
50
|
+
- Enforce access controls and audit logging in the host application, not just in Audrey.
|
|
51
|
+
- Separate patient-facing and staff-only memory scopes.
|
|
52
|
+
|
|
53
|
+
## Production Checklist
|
|
54
|
+
|
|
55
|
+
1. Pin `AUDREY_EMBEDDING_PROVIDER` and `AUDREY_LLM_PROVIDER` explicitly. Do not rely on key-based auto-detection in production.
|
|
56
|
+
2. Set a dedicated `AUDREY_DATA_DIR` per environment and per tenant boundary.
|
|
57
|
+
3. Add a health check that runs `npx audrey status --json --fail-on-unhealthy`.
|
|
58
|
+
4. Alert on `health.healthy=false` or `health.reembed_recommended=true`.
|
|
59
|
+
5. Schedule `npx audrey dream` during low-traffic windows so consolidation and decay stay current.
|
|
60
|
+
6. Backup the SQLite data directory before changing embedding dimensions or providers.
|
|
61
|
+
7. Treat re-embedding as a controlled maintenance action and validate with `npx audrey status`.
|
|
62
|
+
8. Keep API keys, bearer tokens, and raw credentials out of encoded memory content.
|
|
63
|
+
9. Decide whether `private` memories are allowed for your use case and document who can create them.
|
|
64
|
+
10. Add application-level encryption, access control, logging, and retention policies around Audrey.
|
|
65
|
+
11. On graceful shutdown paths, call `await brain.waitForIdle()` before `brain.close()` so tracked background work drains cleanly.
|
|
66
|
+
|
|
67
|
+
## Operations Commands
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Human-readable health
|
|
71
|
+
npx audrey status
|
|
72
|
+
|
|
73
|
+
# Monitoring-friendly health
|
|
74
|
+
npx audrey status --json
|
|
75
|
+
|
|
76
|
+
# Fail the process if the index is unhealthy or unreadable
|
|
77
|
+
npx audrey status --json --fail-on-unhealthy
|
|
78
|
+
|
|
79
|
+
# Nightly memory maintenance
|
|
80
|
+
npx audrey dream
|
|
81
|
+
|
|
82
|
+
# Repair vector/index drift after provider or dimension changes
|
|
83
|
+
npx audrey reembed
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Example Deployment Pattern
|
|
87
|
+
|
|
88
|
+
Use Audrey as a local sidecar to the agent service:
|
|
89
|
+
|
|
90
|
+
- One Audrey data directory per tenant or deployment shard
|
|
91
|
+
- Health checks wired to `status --json`
|
|
92
|
+
- Scheduled dream/reembed jobs
|
|
93
|
+
- Backups handled by the host platform
|
|
94
|
+
- Regulated-data filtering handled before `memory_encode`
|
|
95
|
+
|
|
96
|
+
That keeps Audrey focused on memory integrity while the host system owns compliance, tenancy, and transport security.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Audrey } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
async function demo() {
|
|
4
|
+
console.log('=== Audrey Demo: Financial Services Operations ===\n');
|
|
5
|
+
|
|
6
|
+
const brain = new Audrey({
|
|
7
|
+
dataDir: './fintech-demo-data',
|
|
8
|
+
agent: 'payments-ops-agent',
|
|
9
|
+
embedding: { provider: 'mock', dimensions: 64 },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.log('--- Encoding payment-operations incidents ---');
|
|
13
|
+
await brain.encode({
|
|
14
|
+
content: 'Processor X returned HTTP 429 when payout retries exceeded 120 requests per minute for marketplace merchants.',
|
|
15
|
+
source: 'direct-observation',
|
|
16
|
+
salience: 0.9,
|
|
17
|
+
tags: ['payments', 'payouts', 'rate-limit'],
|
|
18
|
+
context: { domain: 'finserv', workflow: 'payout-incident' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await brain.encode({
|
|
22
|
+
content: 'On-call notes show payout incident volume drops after retry batches are capped at 50 merchants per worker.',
|
|
23
|
+
source: 'tool-result',
|
|
24
|
+
salience: 0.8,
|
|
25
|
+
tags: ['payments', 'payouts', 'ops'],
|
|
26
|
+
context: { domain: 'finserv', workflow: 'payout-incident' },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await brain.encode({
|
|
30
|
+
content: 'Risk operations requested automatic escalation when payout failures affect more than three merchants in the same hour.',
|
|
31
|
+
source: 'told-by-user',
|
|
32
|
+
salience: 0.7,
|
|
33
|
+
tags: ['payments', 'escalation', 'risk'],
|
|
34
|
+
context: { domain: 'finserv', workflow: 'payout-incident' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log('\n--- Consolidating incidents into an ops principle ---');
|
|
38
|
+
await brain.consolidate({
|
|
39
|
+
minClusterSize: 3,
|
|
40
|
+
similarityThreshold: -0.3,
|
|
41
|
+
extractPrinciple: () => ({
|
|
42
|
+
content: 'When payout retries spike, cap retry batches and escalate once multiple merchants are affected in the same hour.',
|
|
43
|
+
type: 'procedural',
|
|
44
|
+
conditions: ['payout failures > 3 merchants per hour', 'processor returns 429 or throttling errors'],
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log('\n--- Recalling during a live payout incident ---');
|
|
49
|
+
const recalled = await brain.recall('payout retries throttled by processor', {
|
|
50
|
+
limit: 5,
|
|
51
|
+
context: { domain: 'finserv', workflow: 'payout-incident' },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
for (const memory of recalled) {
|
|
55
|
+
console.log(`[${memory.type}] ${memory.content}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
brain.close();
|
|
59
|
+
|
|
60
|
+
const { rmSync } = await import('node:fs');
|
|
61
|
+
rmSync('./fintech-demo-data', { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
demo().catch(err => {
|
|
65
|
+
console.error(err);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Audrey } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
async function demo() {
|
|
4
|
+
console.log('=== Audrey Demo: Healthcare Operations ===\n');
|
|
5
|
+
|
|
6
|
+
const brain = new Audrey({
|
|
7
|
+
dataDir: './healthcare-demo-data',
|
|
8
|
+
agent: 'care-ops-agent',
|
|
9
|
+
embedding: { provider: 'mock', dimensions: 64 },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.log('--- Encoding care-coordination observations ---');
|
|
13
|
+
await brain.encode({
|
|
14
|
+
content: 'Referral queue delays drop when missing imaging notes are requested before prior-authorization submission.',
|
|
15
|
+
source: 'direct-observation',
|
|
16
|
+
salience: 0.9,
|
|
17
|
+
tags: ['healthcare-ops', 'prior-auth', 'referrals'],
|
|
18
|
+
context: { domain: 'healthcare', workflow: 'prior-auth' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await brain.encode({
|
|
22
|
+
content: 'Scheduling team reports the highest callback completion rate between 4pm and 6pm for discharge follow-up.',
|
|
23
|
+
source: 'tool-result',
|
|
24
|
+
salience: 0.8,
|
|
25
|
+
tags: ['healthcare-ops', 'follow-up', 'scheduling'],
|
|
26
|
+
context: { domain: 'healthcare', workflow: 'discharge-followup' },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await brain.encode({
|
|
30
|
+
content: 'Care coordinators want interpreter requirements captured in every handoff note before outreach starts.',
|
|
31
|
+
source: 'told-by-user',
|
|
32
|
+
salience: 0.7,
|
|
33
|
+
tags: ['healthcare-ops', 'handoff', 'interpreter'],
|
|
34
|
+
context: { domain: 'healthcare', workflow: 'care-coordination' },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log('\n--- Consolidating into a reusable workflow ---');
|
|
38
|
+
await brain.consolidate({
|
|
39
|
+
minClusterSize: 3,
|
|
40
|
+
similarityThreshold: -0.3,
|
|
41
|
+
extractPrinciple: () => ({
|
|
42
|
+
content: 'For care-coordination workflows, collect missing documentation and communication preferences before outreach or prior-auth submission.',
|
|
43
|
+
type: 'procedural',
|
|
44
|
+
conditions: ['prior-auth missing documentation', 'handoff note lacks outreach constraints'],
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log('\n--- Recalling during a care-coordination handoff ---');
|
|
49
|
+
const recalled = await brain.recall('care coordination handoff missing documentation', {
|
|
50
|
+
limit: 5,
|
|
51
|
+
context: { domain: 'healthcare', workflow: 'care-coordination' },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
for (const memory of recalled) {
|
|
55
|
+
console.log(`[${memory.type}] ${memory.content}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
brain.close();
|
|
59
|
+
|
|
60
|
+
const { rmSync } = await import('node:fs');
|
|
61
|
+
rmSync('./healthcare-demo-data', { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
demo().catch(err => {
|
|
65
|
+
console.error(err);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// examples/stripe-demo.js
|
|
2
|
+
// Proof-of-concept demo showing the full Audrey pipeline:
|
|
3
|
+
// encode episodic memories → consolidate into principles → recall proactively
|
|
4
|
+
//
|
|
5
|
+
// Run: node examples/stripe-demo.js
|
|
6
|
+
// No external dependencies required (uses mock embeddings).
|
|
7
|
+
|
|
8
|
+
import { Audrey } from '../src/index.js';
|
|
9
|
+
|
|
10
|
+
async function demo() {
|
|
11
|
+
console.log('=== Audrey Demo: Stripe Rate Limit Learning ===\n');
|
|
12
|
+
|
|
13
|
+
const brain = new Audrey({
|
|
14
|
+
dataDir: './demo-data',
|
|
15
|
+
agent: 'stripe-agent',
|
|
16
|
+
embedding: { provider: 'mock', dimensions: 64 },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
brain.on('encode', ({ id, content }) => {
|
|
20
|
+
console.log(` [ENCODE] ${id.slice(0, 8)}... "${content.slice(0, 60)}"`);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
brain.on('consolidation', ({ principlesExtracted, clustersFound }) => {
|
|
24
|
+
console.log(` [CONSOLIDATE] Found ${clustersFound} clusters, extracted ${principlesExtracted} principles`);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
brain.on('reinforcement', ({ episodeId, similarity }) => {
|
|
28
|
+
console.log(` [REINFORCE] Episode ${episodeId.slice(0, 8)}... reinforced existing knowledge (sim: ${similarity?.toFixed(2) || 'N/A'})`);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// --- Scenario: Agent encounters Stripe rate limits ---
|
|
32
|
+
|
|
33
|
+
console.log('--- Episode 1: First rate limit hit ---');
|
|
34
|
+
await brain.encode({
|
|
35
|
+
content: 'Stripe API returned HTTP 429 when batch-processing 150 payments per second',
|
|
36
|
+
source: 'direct-observation',
|
|
37
|
+
salience: 0.9,
|
|
38
|
+
causal: { trigger: 'batch-payment-job', consequence: 'payment-queue-stalled' },
|
|
39
|
+
tags: ['stripe', 'rate-limit', 'production'],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log('\n--- Episode 2: Second hit from different code path ---');
|
|
43
|
+
await brain.encode({
|
|
44
|
+
content: 'Stripe webhook verification endpoint returned 429 Too Many Requests during high traffic',
|
|
45
|
+
source: 'tool-result',
|
|
46
|
+
salience: 0.7,
|
|
47
|
+
causal: { trigger: 'webhook-flood', consequence: 'missed-webhook-events' },
|
|
48
|
+
tags: ['stripe', 'rate-limit', 'webhooks'],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log('\n--- Episode 3: Third observation from monitoring ---');
|
|
52
|
+
await brain.encode({
|
|
53
|
+
content: 'Stripe API rate limit triggered at approximately 100 requests per second threshold',
|
|
54
|
+
source: 'direct-observation',
|
|
55
|
+
salience: 0.8,
|
|
56
|
+
tags: ['stripe', 'rate-limit', 'monitoring'],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// --- Consolidation ---
|
|
60
|
+
console.log('\n--- Running consolidation ("sleep" cycle) ---');
|
|
61
|
+
await brain.consolidate({
|
|
62
|
+
minClusterSize: 3,
|
|
63
|
+
// Mock embeddings are hash-based (not semantic), so cosine similarity
|
|
64
|
+
// between related texts is near-random. In production with real embeddings
|
|
65
|
+
// (e.g. OpenAI text-embedding-3-small), a threshold of 0.80+ works well.
|
|
66
|
+
// We drop it here so the demo pipeline runs end-to-end.
|
|
67
|
+
similarityThreshold: -0.3,
|
|
68
|
+
extractPrinciple: (episodes) => ({
|
|
69
|
+
content: `Stripe enforces ~100 req/s rate limit across all endpoints. Exceeding this causes 429 errors that can stall payment queues and cause missed webhooks. Implement request throttling.`,
|
|
70
|
+
type: 'semantic',
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// --- Proactive recall ---
|
|
75
|
+
console.log('\n--- Agent encounters Stripe again, recalls proactively ---');
|
|
76
|
+
const memories = await brain.recall('stripe api request rate', {
|
|
77
|
+
minConfidence: 0.3,
|
|
78
|
+
limit: 5,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
console.log(`\nRecalled ${memories.length} memories:`);
|
|
82
|
+
for (const mem of memories) {
|
|
83
|
+
console.log(` [${mem.type.toUpperCase()}] (conf: ${mem.confidence.toFixed(2)}, score: ${mem.score.toFixed(3)}) ${mem.content.slice(0, 80)}${mem.content.length > 80 ? '...' : ''}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// --- Introspection ---
|
|
87
|
+
console.log('\n--- Brain stats ---');
|
|
88
|
+
const stats = brain.introspect();
|
|
89
|
+
console.log(` Episodic memories: ${stats.episodic}`);
|
|
90
|
+
console.log(` Semantic principles: ${stats.semantic}`);
|
|
91
|
+
console.log(` Procedural workflows: ${stats.procedural}`);
|
|
92
|
+
console.log(` Causal links: ${stats.causalLinks}`);
|
|
93
|
+
console.log(` Consolidation runs: ${stats.totalConsolidationRuns}`);
|
|
94
|
+
console.log(` Dormant memories: ${stats.dormant}`);
|
|
95
|
+
|
|
96
|
+
brain.close();
|
|
97
|
+
|
|
98
|
+
// Cleanup demo data
|
|
99
|
+
const { rmSync } = await import('node:fs');
|
|
100
|
+
rmSync('./demo-data', { recursive: true, force: true });
|
|
101
|
+
|
|
102
|
+
console.log('\n=== Demo complete ===');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
demo().catch(console.error);
|
package/mcp-server/config.js
CHANGED
|
@@ -1,18 +1,47 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
|
|
4
|
-
export const VERSION = '0.16.
|
|
5
|
+
export const VERSION = '0.16.1';
|
|
5
6
|
export const SERVER_NAME = 'audrey-memory';
|
|
6
7
|
export const DEFAULT_DATA_DIR = join(homedir(), '.audrey', 'data');
|
|
8
|
+
export const MCP_ENTRYPOINT = fileURLToPath(new URL('./index.js', import.meta.url));
|
|
9
|
+
const VALID_EMBEDDING_PROVIDERS = new Set(['mock', 'local', 'gemini', 'openai']);
|
|
10
|
+
const VALID_LLM_PROVIDERS = new Set(['mock', 'anthropic', 'openai']);
|
|
11
|
+
|
|
12
|
+
function assertValidProvider(provider, validProviders, envVar) {
|
|
13
|
+
if (!validProviders.has(provider)) {
|
|
14
|
+
throw new Error(`Unsupported ${envVar} value: ${provider}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function defaultEmbeddingDimensions(provider) {
|
|
19
|
+
switch (provider) {
|
|
20
|
+
case 'mock':
|
|
21
|
+
return 64;
|
|
22
|
+
case 'openai':
|
|
23
|
+
return 1536;
|
|
24
|
+
case 'gemini':
|
|
25
|
+
return 3072;
|
|
26
|
+
case 'local':
|
|
27
|
+
default:
|
|
28
|
+
return 384;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveDataDir(env = process.env) {
|
|
33
|
+
return env.AUDREY_DATA_DIR || DEFAULT_DATA_DIR;
|
|
34
|
+
}
|
|
7
35
|
|
|
8
36
|
/**
|
|
9
37
|
* Resolves which embedding provider to use.
|
|
10
38
|
* Priority: explicit config -> gemini (if GOOGLE_API_KEY exists) -> local
|
|
11
39
|
* OpenAI is NEVER auto-selected -- must be set explicitly via AUDREY_EMBEDDING_PROVIDER=openai.
|
|
12
40
|
*/
|
|
13
|
-
export function resolveEmbeddingProvider(env, explicit) {
|
|
41
|
+
export function resolveEmbeddingProvider(env, explicit = env.AUDREY_EMBEDDING_PROVIDER) {
|
|
14
42
|
if (explicit && explicit !== 'auto') {
|
|
15
|
-
|
|
43
|
+
assertValidProvider(explicit, VALID_EMBEDDING_PROVIDERS, 'AUDREY_EMBEDDING_PROVIDER');
|
|
44
|
+
const dims = defaultEmbeddingDimensions(explicit);
|
|
16
45
|
const apiKey = explicit === 'gemini'
|
|
17
46
|
? (env.GOOGLE_API_KEY || env.GEMINI_API_KEY)
|
|
18
47
|
: explicit === 'openai'
|
|
@@ -28,49 +57,77 @@ export function resolveEmbeddingProvider(env, explicit) {
|
|
|
28
57
|
return { provider: 'local', dimensions: 384, device: env.AUDREY_DEVICE || 'gpu' };
|
|
29
58
|
}
|
|
30
59
|
|
|
60
|
+
export function resolveLLMProvider(env, explicit = env.AUDREY_LLM_PROVIDER) {
|
|
61
|
+
if (explicit && explicit !== 'auto') {
|
|
62
|
+
assertValidProvider(explicit, VALID_LLM_PROVIDERS, 'AUDREY_LLM_PROVIDER');
|
|
63
|
+
if (explicit === 'anthropic') {
|
|
64
|
+
return { provider: 'anthropic', apiKey: env.ANTHROPIC_API_KEY };
|
|
65
|
+
}
|
|
66
|
+
if (explicit === 'openai') {
|
|
67
|
+
return { provider: 'openai', apiKey: env.OPENAI_API_KEY };
|
|
68
|
+
}
|
|
69
|
+
return { provider: 'mock' };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
73
|
+
return { provider: 'anthropic', apiKey: env.ANTHROPIC_API_KEY };
|
|
74
|
+
}
|
|
75
|
+
if (env.OPENAI_API_KEY) {
|
|
76
|
+
return { provider: 'openai', apiKey: env.OPENAI_API_KEY };
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
31
81
|
export function buildAudreyConfig() {
|
|
32
|
-
const dataDir = process.env
|
|
82
|
+
const dataDir = resolveDataDir(process.env);
|
|
33
83
|
const agent = process.env.AUDREY_AGENT || 'claude-code';
|
|
34
84
|
const explicitProvider = process.env.AUDREY_EMBEDDING_PROVIDER;
|
|
35
|
-
const llmProvider = process.env.AUDREY_LLM_PROVIDER;
|
|
36
85
|
|
|
37
86
|
const embedding = resolveEmbeddingProvider(process.env, explicitProvider);
|
|
87
|
+
const llm = resolveLLMProvider(process.env, process.env.AUDREY_LLM_PROVIDER);
|
|
38
88
|
|
|
39
89
|
const config = { dataDir, agent, embedding };
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
config.llm = { provider: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY };
|
|
43
|
-
} else if (llmProvider === 'openai') {
|
|
44
|
-
config.llm = { provider: 'openai', apiKey: process.env.OPENAI_API_KEY };
|
|
45
|
-
} else if (llmProvider === 'mock') {
|
|
46
|
-
config.llm = { provider: 'mock' };
|
|
90
|
+
if (llm) {
|
|
91
|
+
config.llm = llm;
|
|
47
92
|
}
|
|
48
93
|
|
|
49
94
|
return config;
|
|
50
95
|
}
|
|
51
96
|
|
|
52
97
|
export function buildInstallArgs(env = process.env) {
|
|
53
|
-
const envPairs =
|
|
98
|
+
const envPairs = new Map();
|
|
99
|
+
const addEnv = (key, value) => {
|
|
100
|
+
if (value === undefined || value === null || value === '') return;
|
|
101
|
+
envPairs.set(key, `${key}=${value}`);
|
|
102
|
+
};
|
|
54
103
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
104
|
+
addEnv('AUDREY_DATA_DIR', resolveDataDir(env));
|
|
105
|
+
|
|
106
|
+
const embedding = resolveEmbeddingProvider(env, env.AUDREY_EMBEDDING_PROVIDER);
|
|
107
|
+
addEnv('AUDREY_EMBEDDING_PROVIDER', embedding.provider);
|
|
108
|
+
if (embedding.provider === 'local') {
|
|
109
|
+
addEnv('AUDREY_DEVICE', embedding.device || env.AUDREY_DEVICE || 'gpu');
|
|
110
|
+
} else if (embedding.provider === 'gemini') {
|
|
111
|
+
addEnv('GOOGLE_API_KEY', embedding.apiKey);
|
|
59
112
|
} else if (embedding.provider === 'openai') {
|
|
60
|
-
|
|
61
|
-
envPairs.push(`OPENAI_API_KEY=${env.OPENAI_API_KEY}`);
|
|
113
|
+
addEnv('OPENAI_API_KEY', embedding.apiKey);
|
|
62
114
|
}
|
|
63
115
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
116
|
+
const llm = resolveLLMProvider(env, env.AUDREY_LLM_PROVIDER);
|
|
117
|
+
if (llm) {
|
|
118
|
+
addEnv('AUDREY_LLM_PROVIDER', llm.provider);
|
|
119
|
+
if (llm.provider === 'anthropic') {
|
|
120
|
+
addEnv('ANTHROPIC_API_KEY', llm.apiKey);
|
|
121
|
+
} else if (llm.provider === 'openai') {
|
|
122
|
+
addEnv('OPENAI_API_KEY', llm.apiKey);
|
|
123
|
+
}
|
|
67
124
|
}
|
|
68
125
|
|
|
69
126
|
const args = ['mcp', 'add', '-s', 'user', SERVER_NAME];
|
|
70
|
-
for (const pair of envPairs) {
|
|
127
|
+
for (const pair of envPairs.values()) {
|
|
71
128
|
args.push('-e', pair);
|
|
72
129
|
}
|
|
73
|
-
args.push('--',
|
|
130
|
+
args.push('--', process.execPath, MCP_ENTRYPOINT);
|
|
74
131
|
|
|
75
132
|
return args;
|
|
76
133
|
}
|