audrey 0.15.0 → 0.16.1
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 +808 -681
- package/mcp-server/config.js +80 -76
- package/mcp-server/index.js +728 -456
- package/package.json +76 -77
- package/src/adaptive.js +53 -53
- package/src/affect.js +64 -64
- package/src/audrey.js +604 -647
- package/src/causal.js +95 -95
- package/src/confidence.js +120 -120
- package/src/consolidate.js +265 -242
- package/src/context.js +15 -15
- package/src/db.js +370 -333
- package/src/decay.js +84 -84
- package/src/embedding.js +256 -256
- package/src/encode.js +63 -85
- package/src/export.js +67 -61
- package/src/forget.js +111 -111
- package/src/import.js +245 -123
- package/src/index.js +27 -20
- package/src/interference.js +51 -51
- package/src/introspect.js +48 -48
- package/src/llm.js +246 -240
- package/src/migrate.js +58 -58
- package/src/prompts.js +223 -223
- package/src/recall.js +352 -329
- package/src/rollback.js +42 -42
- package/src/ulid.js +18 -18
- package/src/utils.js +38 -38
- package/src/validate.js +172 -172
package/src/causal.js
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import { generateId } from './ulid.js';
|
|
2
|
-
import { buildCausalArticulationPrompt } from './prompts.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @param {import('better-sqlite3').Database} db
|
|
6
|
-
* @param {{ causeId: string, effectId: string, linkType?: string, mechanism?: string, confidence?: number }} params
|
|
7
|
-
* @returns {string}
|
|
8
|
-
*/
|
|
9
|
-
export function addCausalLink(db, { causeId, effectId, linkType = 'causal', mechanism, confidence }) {
|
|
10
|
-
const id = generateId();
|
|
11
|
-
const now = new Date().toISOString();
|
|
12
|
-
|
|
13
|
-
db.prepare(`
|
|
14
|
-
INSERT INTO causal_links (id, cause_id, effect_id, link_type, mechanism, confidence, created_at)
|
|
15
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
16
|
-
`).run(id, causeId, effectId, linkType, mechanism, confidence, now);
|
|
17
|
-
|
|
18
|
-
return id;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param {import('better-sqlite3').Database} db
|
|
23
|
-
* @param {string} memoryId
|
|
24
|
-
* @param {{ depth?: number }} [options]
|
|
25
|
-
* @returns {Object[]}
|
|
26
|
-
*/
|
|
27
|
-
export function getCausalChain(db, memoryId, options = {}) {
|
|
28
|
-
const { depth = 10 } = options;
|
|
29
|
-
const results = [];
|
|
30
|
-
const visited = new Set();
|
|
31
|
-
const queue = [memoryId];
|
|
32
|
-
let currentDepth = 0;
|
|
33
|
-
|
|
34
|
-
while (queue.length > 0 && currentDepth < depth) {
|
|
35
|
-
const nextQueue = [];
|
|
36
|
-
for (const nodeId of queue) {
|
|
37
|
-
if (visited.has(nodeId)) continue;
|
|
38
|
-
visited.add(nodeId);
|
|
39
|
-
|
|
40
|
-
const links = db.prepare(
|
|
41
|
-
'SELECT * FROM causal_links WHERE cause_id = ?'
|
|
42
|
-
).all(nodeId);
|
|
43
|
-
|
|
44
|
-
for (const link of links) {
|
|
45
|
-
if (!visited.has(link.effect_id)) {
|
|
46
|
-
results.push(link);
|
|
47
|
-
nextQueue.push(link.effect_id);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
queue.length = 0;
|
|
52
|
-
queue.push(...nextQueue);
|
|
53
|
-
currentDepth++;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return results;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {import('better-sqlite3').Database} db
|
|
61
|
-
* @param {import('./llm.js').LLMProvider} llmProvider
|
|
62
|
-
* @param {{ id: string, content: string, source: string }} cause
|
|
63
|
-
* @param {{ id: string, content: string, source: string }} effect
|
|
64
|
-
* @returns {Promise<{ linkId: string|null, mechanism: string, linkType: string, confidence: number, spurious: boolean }>}
|
|
65
|
-
*/
|
|
66
|
-
export async function articulateCausalLink(db, llmProvider, cause, effect) {
|
|
67
|
-
const messages = buildCausalArticulationPrompt(cause, effect);
|
|
68
|
-
const result = await llmProvider.json(messages);
|
|
69
|
-
|
|
70
|
-
if (result.spurious) {
|
|
71
|
-
return {
|
|
72
|
-
linkId: null,
|
|
73
|
-
mechanism: result.mechanism,
|
|
74
|
-
linkType: result.linkType,
|
|
75
|
-
confidence: result.confidence,
|
|
76
|
-
spurious: true,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const linkId = addCausalLink(db, {
|
|
81
|
-
causeId: cause.id,
|
|
82
|
-
effectId: effect.id,
|
|
83
|
-
linkType: result.linkType || 'correlational',
|
|
84
|
-
mechanism: result.mechanism,
|
|
85
|
-
confidence: result.confidence,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
linkId,
|
|
90
|
-
mechanism: result.mechanism,
|
|
91
|
-
linkType: result.linkType,
|
|
92
|
-
confidence: result.confidence,
|
|
93
|
-
spurious: false,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
1
|
+
import { generateId } from './ulid.js';
|
|
2
|
+
import { buildCausalArticulationPrompt } from './prompts.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {import('better-sqlite3').Database} db
|
|
6
|
+
* @param {{ causeId: string, effectId: string, linkType?: string, mechanism?: string, confidence?: number }} params
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function addCausalLink(db, { causeId, effectId, linkType = 'causal', mechanism, confidence }) {
|
|
10
|
+
const id = generateId();
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
|
|
13
|
+
db.prepare(`
|
|
14
|
+
INSERT INTO causal_links (id, cause_id, effect_id, link_type, mechanism, confidence, created_at)
|
|
15
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
16
|
+
`).run(id, causeId, effectId, linkType, mechanism, confidence, now);
|
|
17
|
+
|
|
18
|
+
return id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {import('better-sqlite3').Database} db
|
|
23
|
+
* @param {string} memoryId
|
|
24
|
+
* @param {{ depth?: number }} [options]
|
|
25
|
+
* @returns {Object[]}
|
|
26
|
+
*/
|
|
27
|
+
export function getCausalChain(db, memoryId, options = {}) {
|
|
28
|
+
const { depth = 10 } = options;
|
|
29
|
+
const results = [];
|
|
30
|
+
const visited = new Set();
|
|
31
|
+
const queue = [memoryId];
|
|
32
|
+
let currentDepth = 0;
|
|
33
|
+
|
|
34
|
+
while (queue.length > 0 && currentDepth < depth) {
|
|
35
|
+
const nextQueue = [];
|
|
36
|
+
for (const nodeId of queue) {
|
|
37
|
+
if (visited.has(nodeId)) continue;
|
|
38
|
+
visited.add(nodeId);
|
|
39
|
+
|
|
40
|
+
const links = db.prepare(
|
|
41
|
+
'SELECT * FROM causal_links WHERE cause_id = ?'
|
|
42
|
+
).all(nodeId);
|
|
43
|
+
|
|
44
|
+
for (const link of links) {
|
|
45
|
+
if (!visited.has(link.effect_id)) {
|
|
46
|
+
results.push(link);
|
|
47
|
+
nextQueue.push(link.effect_id);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
queue.length = 0;
|
|
52
|
+
queue.push(...nextQueue);
|
|
53
|
+
currentDepth++;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {import('better-sqlite3').Database} db
|
|
61
|
+
* @param {import('./llm.js').LLMProvider} llmProvider
|
|
62
|
+
* @param {{ id: string, content: string, source: string }} cause
|
|
63
|
+
* @param {{ id: string, content: string, source: string }} effect
|
|
64
|
+
* @returns {Promise<{ linkId: string|null, mechanism: string, linkType: string, confidence: number, spurious: boolean }>}
|
|
65
|
+
*/
|
|
66
|
+
export async function articulateCausalLink(db, llmProvider, cause, effect) {
|
|
67
|
+
const messages = buildCausalArticulationPrompt(cause, effect);
|
|
68
|
+
const result = await llmProvider.json(messages);
|
|
69
|
+
|
|
70
|
+
if (result.spurious) {
|
|
71
|
+
return {
|
|
72
|
+
linkId: null,
|
|
73
|
+
mechanism: result.mechanism,
|
|
74
|
+
linkType: result.linkType,
|
|
75
|
+
confidence: result.confidence,
|
|
76
|
+
spurious: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const linkId = addCausalLink(db, {
|
|
81
|
+
causeId: cause.id,
|
|
82
|
+
effectId: effect.id,
|
|
83
|
+
linkType: result.linkType || 'correlational',
|
|
84
|
+
mechanism: result.mechanism,
|
|
85
|
+
confidence: result.confidence,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
linkId,
|
|
90
|
+
mechanism: result.mechanism,
|
|
91
|
+
linkType: result.linkType,
|
|
92
|
+
confidence: result.confidence,
|
|
93
|
+
spurious: false,
|
|
94
|
+
};
|
|
95
|
+
}
|
package/src/confidence.js
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
/** @type {Record<string, number>} */
|
|
2
|
-
export const DEFAULT_SOURCE_RELIABILITY = {
|
|
3
|
-
'direct-observation': 0.95,
|
|
4
|
-
'told-by-user': 0.90,
|
|
5
|
-
'tool-result': 0.85,
|
|
6
|
-
'inference': 0.60,
|
|
7
|
-
'model-generated': 0.40,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/** @type {{ source: number, evidence: number, recency: number, retrieval: number }} */
|
|
11
|
-
export const DEFAULT_WEIGHTS = {
|
|
12
|
-
source: 0.30,
|
|
13
|
-
evidence: 0.35,
|
|
14
|
-
recency: 0.20,
|
|
15
|
-
retrieval: 0.15,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/** @type {{ episodic: number, semantic: number, procedural: number }} */
|
|
19
|
-
export const DEFAULT_HALF_LIVES = {
|
|
20
|
-
episodic: 7,
|
|
21
|
-
semantic: 30,
|
|
22
|
-
procedural: 90,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/** @type {number} */
|
|
26
|
-
export const MODEL_GENERATED_CONFIDENCE_CAP = 0.6;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {string} sourceType
|
|
30
|
-
* @param {Record<string, number>} [customReliability]
|
|
31
|
-
* @returns {number}
|
|
32
|
-
*/
|
|
33
|
-
export function sourceReliability(sourceType, customReliability) {
|
|
34
|
-
const table = customReliability || DEFAULT_SOURCE_RELIABILITY;
|
|
35
|
-
const value = table[sourceType];
|
|
36
|
-
if (value === undefined) {
|
|
37
|
-
throw new Error(`Unknown source type: ${sourceType}. Valid types: ${Object.keys(table).join(', ')}`);
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {number} supportingCount
|
|
44
|
-
* @param {number} contradictingCount
|
|
45
|
-
* @returns {number}
|
|
46
|
-
*/
|
|
47
|
-
export function evidenceAgreement(supportingCount, contradictingCount) {
|
|
48
|
-
const total = supportingCount + contradictingCount;
|
|
49
|
-
if (total === 0) return 1.0;
|
|
50
|
-
return supportingCount / total;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @param {number} ageDays
|
|
55
|
-
* @param {number} halfLifeDays
|
|
56
|
-
* @returns {number}
|
|
57
|
-
*/
|
|
58
|
-
export function recencyDecay(ageDays, halfLifeDays) {
|
|
59
|
-
const lambda = Math.LN2 / halfLifeDays;
|
|
60
|
-
return Math.exp(-lambda * ageDays);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @param {number} retrievalCount
|
|
65
|
-
* @param {number} daysSinceRetrieval
|
|
66
|
-
* @returns {number}
|
|
67
|
-
*/
|
|
68
|
-
export function retrievalReinforcement(retrievalCount, daysSinceRetrieval) {
|
|
69
|
-
if (retrievalCount === 0) return 0;
|
|
70
|
-
const lambdaRet = Math.LN2 / 14;
|
|
71
|
-
const baseReinforcement = 0.3 * Math.log(1 + retrievalCount);
|
|
72
|
-
const recencyWeight = Math.exp(-lambdaRet * daysSinceRetrieval);
|
|
73
|
-
const spacedBonus = Math.min(0.15, 0.02 * Math.log(1 + daysSinceRetrieval));
|
|
74
|
-
return Math.min(1.0, baseReinforcement * recencyWeight + spacedBonus);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function salienceModifier(salience) {
|
|
78
|
-
const s = salience ?? 0.5;
|
|
79
|
-
return 0.5 + s;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @param {object} params
|
|
84
|
-
* @param {string} params.sourceType
|
|
85
|
-
* @param {number} params.supportingCount
|
|
86
|
-
* @param {number} params.contradictingCount
|
|
87
|
-
* @param {number} params.ageDays
|
|
88
|
-
* @param {number} params.halfLifeDays
|
|
89
|
-
* @param {number} params.retrievalCount
|
|
90
|
-
* @param {number} params.daysSinceRetrieval
|
|
91
|
-
* @param {{ source: number, evidence: number, recency: number, retrieval: number }} [params.weights]
|
|
92
|
-
* @param {Record<string, number>} [params.customSourceReliability]
|
|
93
|
-
* @returns {number}
|
|
94
|
-
*/
|
|
95
|
-
export function computeConfidence({
|
|
96
|
-
sourceType,
|
|
97
|
-
supportingCount,
|
|
98
|
-
contradictingCount,
|
|
99
|
-
ageDays,
|
|
100
|
-
halfLifeDays,
|
|
101
|
-
retrievalCount,
|
|
102
|
-
daysSinceRetrieval,
|
|
103
|
-
weights,
|
|
104
|
-
customSourceReliability,
|
|
105
|
-
}) {
|
|
106
|
-
const w = weights || DEFAULT_WEIGHTS;
|
|
107
|
-
|
|
108
|
-
const s = sourceReliability(sourceType, customSourceReliability);
|
|
109
|
-
const e = evidenceAgreement(supportingCount, contradictingCount);
|
|
110
|
-
const r = recencyDecay(ageDays, halfLifeDays);
|
|
111
|
-
const ret = retrievalReinforcement(retrievalCount, daysSinceRetrieval);
|
|
112
|
-
|
|
113
|
-
let confidence = w.source * s + w.evidence * e + w.recency * r + w.retrieval * ret;
|
|
114
|
-
|
|
115
|
-
if (sourceType === 'model-generated') {
|
|
116
|
-
confidence = Math.min(confidence, MODEL_GENERATED_CONFIDENCE_CAP);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return Math.max(0, Math.min(1, confidence));
|
|
120
|
-
}
|
|
1
|
+
/** @type {Record<string, number>} */
|
|
2
|
+
export const DEFAULT_SOURCE_RELIABILITY = {
|
|
3
|
+
'direct-observation': 0.95,
|
|
4
|
+
'told-by-user': 0.90,
|
|
5
|
+
'tool-result': 0.85,
|
|
6
|
+
'inference': 0.60,
|
|
7
|
+
'model-generated': 0.40,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/** @type {{ source: number, evidence: number, recency: number, retrieval: number }} */
|
|
11
|
+
export const DEFAULT_WEIGHTS = {
|
|
12
|
+
source: 0.30,
|
|
13
|
+
evidence: 0.35,
|
|
14
|
+
recency: 0.20,
|
|
15
|
+
retrieval: 0.15,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** @type {{ episodic: number, semantic: number, procedural: number }} */
|
|
19
|
+
export const DEFAULT_HALF_LIVES = {
|
|
20
|
+
episodic: 7,
|
|
21
|
+
semantic: 30,
|
|
22
|
+
procedural: 90,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @type {number} */
|
|
26
|
+
export const MODEL_GENERATED_CONFIDENCE_CAP = 0.6;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} sourceType
|
|
30
|
+
* @param {Record<string, number>} [customReliability]
|
|
31
|
+
* @returns {number}
|
|
32
|
+
*/
|
|
33
|
+
export function sourceReliability(sourceType, customReliability) {
|
|
34
|
+
const table = customReliability || DEFAULT_SOURCE_RELIABILITY;
|
|
35
|
+
const value = table[sourceType];
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
throw new Error(`Unknown source type: ${sourceType}. Valid types: ${Object.keys(table).join(', ')}`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {number} supportingCount
|
|
44
|
+
* @param {number} contradictingCount
|
|
45
|
+
* @returns {number}
|
|
46
|
+
*/
|
|
47
|
+
export function evidenceAgreement(supportingCount, contradictingCount) {
|
|
48
|
+
const total = supportingCount + contradictingCount;
|
|
49
|
+
if (total === 0) return 1.0;
|
|
50
|
+
return supportingCount / total;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {number} ageDays
|
|
55
|
+
* @param {number} halfLifeDays
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
58
|
+
export function recencyDecay(ageDays, halfLifeDays) {
|
|
59
|
+
const lambda = Math.LN2 / halfLifeDays;
|
|
60
|
+
return Math.exp(-lambda * ageDays);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {number} retrievalCount
|
|
65
|
+
* @param {number} daysSinceRetrieval
|
|
66
|
+
* @returns {number}
|
|
67
|
+
*/
|
|
68
|
+
export function retrievalReinforcement(retrievalCount, daysSinceRetrieval) {
|
|
69
|
+
if (retrievalCount === 0) return 0;
|
|
70
|
+
const lambdaRet = Math.LN2 / 14;
|
|
71
|
+
const baseReinforcement = 0.3 * Math.log(1 + retrievalCount);
|
|
72
|
+
const recencyWeight = Math.exp(-lambdaRet * daysSinceRetrieval);
|
|
73
|
+
const spacedBonus = Math.min(0.15, 0.02 * Math.log(1 + daysSinceRetrieval));
|
|
74
|
+
return Math.min(1.0, baseReinforcement * recencyWeight + spacedBonus);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function salienceModifier(salience) {
|
|
78
|
+
const s = salience ?? 0.5;
|
|
79
|
+
return 0.5 + s;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {object} params
|
|
84
|
+
* @param {string} params.sourceType
|
|
85
|
+
* @param {number} params.supportingCount
|
|
86
|
+
* @param {number} params.contradictingCount
|
|
87
|
+
* @param {number} params.ageDays
|
|
88
|
+
* @param {number} params.halfLifeDays
|
|
89
|
+
* @param {number} params.retrievalCount
|
|
90
|
+
* @param {number} params.daysSinceRetrieval
|
|
91
|
+
* @param {{ source: number, evidence: number, recency: number, retrieval: number }} [params.weights]
|
|
92
|
+
* @param {Record<string, number>} [params.customSourceReliability]
|
|
93
|
+
* @returns {number}
|
|
94
|
+
*/
|
|
95
|
+
export function computeConfidence({
|
|
96
|
+
sourceType,
|
|
97
|
+
supportingCount,
|
|
98
|
+
contradictingCount,
|
|
99
|
+
ageDays,
|
|
100
|
+
halfLifeDays,
|
|
101
|
+
retrievalCount,
|
|
102
|
+
daysSinceRetrieval,
|
|
103
|
+
weights,
|
|
104
|
+
customSourceReliability,
|
|
105
|
+
}) {
|
|
106
|
+
const w = weights || DEFAULT_WEIGHTS;
|
|
107
|
+
|
|
108
|
+
const s = sourceReliability(sourceType, customSourceReliability);
|
|
109
|
+
const e = evidenceAgreement(supportingCount, contradictingCount);
|
|
110
|
+
const r = recencyDecay(ageDays, halfLifeDays);
|
|
111
|
+
const ret = retrievalReinforcement(retrievalCount, daysSinceRetrieval);
|
|
112
|
+
|
|
113
|
+
let confidence = w.source * s + w.evidence * e + w.recency * r + w.retrieval * ret;
|
|
114
|
+
|
|
115
|
+
if (sourceType === 'model-generated') {
|
|
116
|
+
confidence = Math.min(confidence, MODEL_GENERATED_CONFIDENCE_CAP);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return Math.max(0, Math.min(1, confidence));
|
|
120
|
+
}
|