audrey 0.14.0 → 0.16.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 +808 -681
- package/mcp-server/config.js +76 -76
- package/mcp-server/index.js +728 -437
- package/package.json +76 -77
- package/src/adaptive.js +53 -53
- package/src/affect.js +64 -64
- package/src/audrey.js +604 -570
- 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 +37 -0
- package/src/decay.js +84 -84
- package/src/embedding.js +256 -256
- 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/embedding.js
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} EmbeddingProvider
|
|
5
|
-
* @property {number} dimensions
|
|
6
|
-
* @property {string} modelName
|
|
7
|
-
* @property {string} modelVersion
|
|
8
|
-
* @property {(text: string) => Promise<number[]>} embed
|
|
9
|
-
* @property {(texts: string[]) => Promise<number[][]>} embedBatch
|
|
10
|
-
* @property {(vector: number[]) => Buffer} vectorToBuffer
|
|
11
|
-
* @property {(buffer: Buffer) => number[]} bufferToVector
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/** @implements {EmbeddingProvider} */
|
|
15
|
-
export class MockEmbeddingProvider {
|
|
16
|
-
constructor({ dimensions = 64 } = {}) {
|
|
17
|
-
this.dimensions = dimensions;
|
|
18
|
-
this.modelName = 'mock-embedding';
|
|
19
|
-
this.modelVersion = '1.0.0';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async embed(text) {
|
|
23
|
-
const hash = createHash('sha256').update(text).digest();
|
|
24
|
-
const vector = new Array(this.dimensions);
|
|
25
|
-
for (let i = 0; i < this.dimensions; i++) {
|
|
26
|
-
vector[i] = (hash[i % hash.length] / 255) * 2 - 1;
|
|
27
|
-
}
|
|
28
|
-
const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
29
|
-
return vector.map(v => v / magnitude);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async embedBatch(texts) {
|
|
33
|
-
return Promise.all(texts.map(t => this.embed(t)));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
vectorToBuffer(vector) {
|
|
37
|
-
return Buffer.from(new Float32Array(vector).buffer);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
bufferToVector(buffer) {
|
|
41
|
-
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** @implements {EmbeddingProvider} */
|
|
46
|
-
export class OpenAIEmbeddingProvider {
|
|
47
|
-
constructor({ apiKey, model = 'text-embedding-3-small', dimensions = 1536, timeout = 30000 } = {}) {
|
|
48
|
-
this.apiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
49
|
-
this.model = model;
|
|
50
|
-
this.dimensions = dimensions;
|
|
51
|
-
this.timeout = timeout;
|
|
52
|
-
this.modelName = model;
|
|
53
|
-
this.modelVersion = 'latest';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async embed(text) {
|
|
57
|
-
const controller = new AbortController();
|
|
58
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
59
|
-
try {
|
|
60
|
-
const response = await fetch('https://api.openai.com/v1/embeddings', {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers: {
|
|
63
|
-
'Authorization': `Bearer ${this.apiKey}`,
|
|
64
|
-
'Content-Type': 'application/json',
|
|
65
|
-
},
|
|
66
|
-
body: JSON.stringify({ input: text, model: this.model, dimensions: this.dimensions }),
|
|
67
|
-
signal: controller.signal,
|
|
68
|
-
});
|
|
69
|
-
if (!response.ok) throw new Error(`OpenAI embedding failed: ${response.status}`);
|
|
70
|
-
const data = await response.json();
|
|
71
|
-
return data.data[0].embedding;
|
|
72
|
-
} finally {
|
|
73
|
-
clearTimeout(timer);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async embedBatch(texts) {
|
|
78
|
-
const controller = new AbortController();
|
|
79
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
80
|
-
try {
|
|
81
|
-
const response = await fetch('https://api.openai.com/v1/embeddings', {
|
|
82
|
-
method: 'POST',
|
|
83
|
-
headers: {
|
|
84
|
-
'Authorization': `Bearer ${this.apiKey}`,
|
|
85
|
-
'Content-Type': 'application/json',
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify({ input: texts, model: this.model, dimensions: this.dimensions }),
|
|
88
|
-
signal: controller.signal,
|
|
89
|
-
});
|
|
90
|
-
if (!response.ok) throw new Error(`OpenAI embedding failed: ${response.status}`);
|
|
91
|
-
const data = await response.json();
|
|
92
|
-
return data.data.map(d => d.embedding);
|
|
93
|
-
} finally {
|
|
94
|
-
clearTimeout(timer);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
vectorToBuffer(vector) {
|
|
99
|
-
return Buffer.from(new Float32Array(vector).buffer);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
bufferToVector(buffer) {
|
|
103
|
-
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** @implements {EmbeddingProvider} */
|
|
108
|
-
export class LocalEmbeddingProvider {
|
|
109
|
-
constructor({ model = 'Xenova/all-MiniLM-L6-v2', device = 'gpu', batchSize = 64 } = {}) {
|
|
110
|
-
this.model = model;
|
|
111
|
-
this.dimensions = 384;
|
|
112
|
-
this.modelName = model;
|
|
113
|
-
this.modelVersion = '1.0.0';
|
|
114
|
-
this.device = device;
|
|
115
|
-
this.batchSize = batchSize;
|
|
116
|
-
this._pipeline = null;
|
|
117
|
-
this._readyPromise = null;
|
|
118
|
-
this._actualDevice = null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
ready() {
|
|
122
|
-
if (!this._readyPromise) {
|
|
123
|
-
this._readyPromise = (async () => {
|
|
124
|
-
const { pipeline } = await import('@huggingface/transformers');
|
|
125
|
-
try {
|
|
126
|
-
this._pipeline = await pipeline('feature-extraction', this.model, {
|
|
127
|
-
dtype: 'fp32', device: this.device,
|
|
128
|
-
});
|
|
129
|
-
this._actualDevice = this.device;
|
|
130
|
-
} catch {
|
|
131
|
-
this._pipeline = await pipeline('feature-extraction', this.model, {
|
|
132
|
-
dtype: 'fp32', device: 'cpu',
|
|
133
|
-
});
|
|
134
|
-
this._actualDevice = 'cpu';
|
|
135
|
-
}
|
|
136
|
-
})();
|
|
137
|
-
}
|
|
138
|
-
return this._readyPromise;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async embed(text) {
|
|
142
|
-
await this.ready();
|
|
143
|
-
const output = await this._pipeline(text, { pooling: 'mean', normalize: true });
|
|
144
|
-
return Array.from(output.data);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async embedBatch(texts) {
|
|
148
|
-
if (texts.length === 0) return [];
|
|
149
|
-
await this.ready();
|
|
150
|
-
const results = [];
|
|
151
|
-
for (let i = 0; i < texts.length; i += this.batchSize) {
|
|
152
|
-
const chunk = texts.slice(i, i + this.batchSize);
|
|
153
|
-
const output = await this._pipeline(chunk, { pooling: 'mean', normalize: true });
|
|
154
|
-
results.push(...output.tolist());
|
|
155
|
-
}
|
|
156
|
-
return results;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
vectorToBuffer(vector) {
|
|
160
|
-
return Buffer.from(new Float32Array(vector).buffer);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
bufferToVector(buffer) {
|
|
164
|
-
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** @implements {EmbeddingProvider} */
|
|
169
|
-
export class GeminiEmbeddingProvider {
|
|
170
|
-
constructor({ apiKey, model = 'gemini-embedding-001', timeout = 30000 } = {}) {
|
|
171
|
-
this.apiKey = apiKey || process.env.GOOGLE_API_KEY;
|
|
172
|
-
this.model = model;
|
|
173
|
-
this.dimensions = 3072;
|
|
174
|
-
this.timeout = timeout;
|
|
175
|
-
this.modelName = model;
|
|
176
|
-
this.modelVersion = 'latest';
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async embed(text) {
|
|
180
|
-
if (!this.apiKey) throw new Error('Gemini embedding requires GOOGLE_API_KEY');
|
|
181
|
-
const controller = new AbortController();
|
|
182
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
183
|
-
try {
|
|
184
|
-
const response = await fetch(
|
|
185
|
-
`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:embedContent?key=${this.apiKey}`,
|
|
186
|
-
{
|
|
187
|
-
method: 'POST',
|
|
188
|
-
headers: { 'Content-Type': 'application/json' },
|
|
189
|
-
body: JSON.stringify({ model: `models/${this.model}`, content: { parts: [{ text }] } }),
|
|
190
|
-
signal: controller.signal,
|
|
191
|
-
}
|
|
192
|
-
);
|
|
193
|
-
if (!response.ok) throw new Error(`Gemini embedding failed: ${response.status}`);
|
|
194
|
-
const data = await response.json();
|
|
195
|
-
return data.embedding.values;
|
|
196
|
-
} finally {
|
|
197
|
-
clearTimeout(timer);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async embedBatch(texts) {
|
|
202
|
-
if (texts.length === 0) return [];
|
|
203
|
-
if (!this.apiKey) throw new Error('Gemini embedding requires GOOGLE_API_KEY');
|
|
204
|
-
const results = [];
|
|
205
|
-
for (let i = 0; i < texts.length; i += 100) {
|
|
206
|
-
const chunk = texts.slice(i, i + 100);
|
|
207
|
-
const controller = new AbortController();
|
|
208
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
209
|
-
try {
|
|
210
|
-
const response = await fetch(
|
|
211
|
-
`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:batchEmbedContents?key=${this.apiKey}`,
|
|
212
|
-
{
|
|
213
|
-
method: 'POST',
|
|
214
|
-
headers: { 'Content-Type': 'application/json' },
|
|
215
|
-
body: JSON.stringify({
|
|
216
|
-
requests: chunk.map(text => ({
|
|
217
|
-
model: `models/${this.model}`,
|
|
218
|
-
content: { parts: [{ text }] },
|
|
219
|
-
})),
|
|
220
|
-
}),
|
|
221
|
-
signal: controller.signal,
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
if (!response.ok) throw new Error(`Gemini batch embedding failed: ${response.status}`);
|
|
225
|
-
const data = await response.json();
|
|
226
|
-
results.push(...data.embeddings.map(e => e.values));
|
|
227
|
-
} finally {
|
|
228
|
-
clearTimeout(timer);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return results;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
vectorToBuffer(vector) {
|
|
235
|
-
return Buffer.from(new Float32Array(vector).buffer);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
bufferToVector(buffer) {
|
|
239
|
-
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function createEmbeddingProvider(config) {
|
|
244
|
-
switch (config.provider) {
|
|
245
|
-
case 'mock':
|
|
246
|
-
return new MockEmbeddingProvider(config);
|
|
247
|
-
case 'openai':
|
|
248
|
-
return new OpenAIEmbeddingProvider(config);
|
|
249
|
-
case 'local':
|
|
250
|
-
return new LocalEmbeddingProvider(config);
|
|
251
|
-
case 'gemini':
|
|
252
|
-
return new GeminiEmbeddingProvider(config);
|
|
253
|
-
default:
|
|
254
|
-
throw new Error(`Unknown embedding provider: ${config.provider}. Valid: mock, openai, local, gemini`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} EmbeddingProvider
|
|
5
|
+
* @property {number} dimensions
|
|
6
|
+
* @property {string} modelName
|
|
7
|
+
* @property {string} modelVersion
|
|
8
|
+
* @property {(text: string) => Promise<number[]>} embed
|
|
9
|
+
* @property {(texts: string[]) => Promise<number[][]>} embedBatch
|
|
10
|
+
* @property {(vector: number[]) => Buffer} vectorToBuffer
|
|
11
|
+
* @property {(buffer: Buffer) => number[]} bufferToVector
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @implements {EmbeddingProvider} */
|
|
15
|
+
export class MockEmbeddingProvider {
|
|
16
|
+
constructor({ dimensions = 64 } = {}) {
|
|
17
|
+
this.dimensions = dimensions;
|
|
18
|
+
this.modelName = 'mock-embedding';
|
|
19
|
+
this.modelVersion = '1.0.0';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async embed(text) {
|
|
23
|
+
const hash = createHash('sha256').update(text).digest();
|
|
24
|
+
const vector = new Array(this.dimensions);
|
|
25
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
26
|
+
vector[i] = (hash[i % hash.length] / 255) * 2 - 1;
|
|
27
|
+
}
|
|
28
|
+
const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
29
|
+
return vector.map(v => v / magnitude);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async embedBatch(texts) {
|
|
33
|
+
return Promise.all(texts.map(t => this.embed(t)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
vectorToBuffer(vector) {
|
|
37
|
+
return Buffer.from(new Float32Array(vector).buffer);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
bufferToVector(buffer) {
|
|
41
|
+
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @implements {EmbeddingProvider} */
|
|
46
|
+
export class OpenAIEmbeddingProvider {
|
|
47
|
+
constructor({ apiKey, model = 'text-embedding-3-small', dimensions = 1536, timeout = 30000 } = {}) {
|
|
48
|
+
this.apiKey = apiKey || process.env.OPENAI_API_KEY;
|
|
49
|
+
this.model = model;
|
|
50
|
+
this.dimensions = dimensions;
|
|
51
|
+
this.timeout = timeout;
|
|
52
|
+
this.modelName = model;
|
|
53
|
+
this.modelVersion = 'latest';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async embed(text) {
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch('https://api.openai.com/v1/embeddings', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({ input: text, model: this.model, dimensions: this.dimensions }),
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) throw new Error(`OpenAI embedding failed: ${response.status}`);
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
return data.data[0].embedding;
|
|
72
|
+
} finally {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async embedBatch(texts) {
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch('https://api.openai.com/v1/embeddings', {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({ input: texts, model: this.model, dimensions: this.dimensions }),
|
|
88
|
+
signal: controller.signal,
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok) throw new Error(`OpenAI embedding failed: ${response.status}`);
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
return data.data.map(d => d.embedding);
|
|
93
|
+
} finally {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
vectorToBuffer(vector) {
|
|
99
|
+
return Buffer.from(new Float32Array(vector).buffer);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
bufferToVector(buffer) {
|
|
103
|
+
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** @implements {EmbeddingProvider} */
|
|
108
|
+
export class LocalEmbeddingProvider {
|
|
109
|
+
constructor({ model = 'Xenova/all-MiniLM-L6-v2', device = 'gpu', batchSize = 64 } = {}) {
|
|
110
|
+
this.model = model;
|
|
111
|
+
this.dimensions = 384;
|
|
112
|
+
this.modelName = model;
|
|
113
|
+
this.modelVersion = '1.0.0';
|
|
114
|
+
this.device = device;
|
|
115
|
+
this.batchSize = batchSize;
|
|
116
|
+
this._pipeline = null;
|
|
117
|
+
this._readyPromise = null;
|
|
118
|
+
this._actualDevice = null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ready() {
|
|
122
|
+
if (!this._readyPromise) {
|
|
123
|
+
this._readyPromise = (async () => {
|
|
124
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
125
|
+
try {
|
|
126
|
+
this._pipeline = await pipeline('feature-extraction', this.model, {
|
|
127
|
+
dtype: 'fp32', device: this.device,
|
|
128
|
+
});
|
|
129
|
+
this._actualDevice = this.device;
|
|
130
|
+
} catch {
|
|
131
|
+
this._pipeline = await pipeline('feature-extraction', this.model, {
|
|
132
|
+
dtype: 'fp32', device: 'cpu',
|
|
133
|
+
});
|
|
134
|
+
this._actualDevice = 'cpu';
|
|
135
|
+
}
|
|
136
|
+
})();
|
|
137
|
+
}
|
|
138
|
+
return this._readyPromise;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async embed(text) {
|
|
142
|
+
await this.ready();
|
|
143
|
+
const output = await this._pipeline(text, { pooling: 'mean', normalize: true });
|
|
144
|
+
return Array.from(output.data);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async embedBatch(texts) {
|
|
148
|
+
if (texts.length === 0) return [];
|
|
149
|
+
await this.ready();
|
|
150
|
+
const results = [];
|
|
151
|
+
for (let i = 0; i < texts.length; i += this.batchSize) {
|
|
152
|
+
const chunk = texts.slice(i, i + this.batchSize);
|
|
153
|
+
const output = await this._pipeline(chunk, { pooling: 'mean', normalize: true });
|
|
154
|
+
results.push(...output.tolist());
|
|
155
|
+
}
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
vectorToBuffer(vector) {
|
|
160
|
+
return Buffer.from(new Float32Array(vector).buffer);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
bufferToVector(buffer) {
|
|
164
|
+
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** @implements {EmbeddingProvider} */
|
|
169
|
+
export class GeminiEmbeddingProvider {
|
|
170
|
+
constructor({ apiKey, model = 'gemini-embedding-001', timeout = 30000 } = {}) {
|
|
171
|
+
this.apiKey = apiKey || process.env.GOOGLE_API_KEY;
|
|
172
|
+
this.model = model;
|
|
173
|
+
this.dimensions = 3072;
|
|
174
|
+
this.timeout = timeout;
|
|
175
|
+
this.modelName = model;
|
|
176
|
+
this.modelVersion = 'latest';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async embed(text) {
|
|
180
|
+
if (!this.apiKey) throw new Error('Gemini embedding requires GOOGLE_API_KEY');
|
|
181
|
+
const controller = new AbortController();
|
|
182
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
183
|
+
try {
|
|
184
|
+
const response = await fetch(
|
|
185
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:embedContent?key=${this.apiKey}`,
|
|
186
|
+
{
|
|
187
|
+
method: 'POST',
|
|
188
|
+
headers: { 'Content-Type': 'application/json' },
|
|
189
|
+
body: JSON.stringify({ model: `models/${this.model}`, content: { parts: [{ text }] } }),
|
|
190
|
+
signal: controller.signal,
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
if (!response.ok) throw new Error(`Gemini embedding failed: ${response.status}`);
|
|
194
|
+
const data = await response.json();
|
|
195
|
+
return data.embedding.values;
|
|
196
|
+
} finally {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async embedBatch(texts) {
|
|
202
|
+
if (texts.length === 0) return [];
|
|
203
|
+
if (!this.apiKey) throw new Error('Gemini embedding requires GOOGLE_API_KEY');
|
|
204
|
+
const results = [];
|
|
205
|
+
for (let i = 0; i < texts.length; i += 100) {
|
|
206
|
+
const chunk = texts.slice(i, i + 100);
|
|
207
|
+
const controller = new AbortController();
|
|
208
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
209
|
+
try {
|
|
210
|
+
const response = await fetch(
|
|
211
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:batchEmbedContents?key=${this.apiKey}`,
|
|
212
|
+
{
|
|
213
|
+
method: 'POST',
|
|
214
|
+
headers: { 'Content-Type': 'application/json' },
|
|
215
|
+
body: JSON.stringify({
|
|
216
|
+
requests: chunk.map(text => ({
|
|
217
|
+
model: `models/${this.model}`,
|
|
218
|
+
content: { parts: [{ text }] },
|
|
219
|
+
})),
|
|
220
|
+
}),
|
|
221
|
+
signal: controller.signal,
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
if (!response.ok) throw new Error(`Gemini batch embedding failed: ${response.status}`);
|
|
225
|
+
const data = await response.json();
|
|
226
|
+
results.push(...data.embeddings.map(e => e.values));
|
|
227
|
+
} finally {
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
vectorToBuffer(vector) {
|
|
235
|
+
return Buffer.from(new Float32Array(vector).buffer);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
bufferToVector(buffer) {
|
|
239
|
+
return Array.from(new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function createEmbeddingProvider(config) {
|
|
244
|
+
switch (config.provider) {
|
|
245
|
+
case 'mock':
|
|
246
|
+
return new MockEmbeddingProvider(config);
|
|
247
|
+
case 'openai':
|
|
248
|
+
return new OpenAIEmbeddingProvider(config);
|
|
249
|
+
case 'local':
|
|
250
|
+
return new LocalEmbeddingProvider(config);
|
|
251
|
+
case 'gemini':
|
|
252
|
+
return new GeminiEmbeddingProvider(config);
|
|
253
|
+
default:
|
|
254
|
+
throw new Error(`Unknown embedding provider: ${config.provider}. Valid: mock, openai, local, gemini`);
|
|
255
|
+
}
|
|
256
|
+
}
|
package/src/export.js
CHANGED
|
@@ -1,61 +1,67 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { join, dirname } from 'node:path';
|
|
4
|
-
import { safeJsonParse } from './utils.js';
|
|
5
|
-
|
|
6
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
8
|
-
|
|
9
|
-
export function exportMemories(db) {
|
|
10
|
-
const episodes = db.prepare(
|
|
11
|
-
'SELECT id, content, source, source_reliability, salience, context, affect, tags, causal_trigger, causal_consequence, created_at, supersedes, superseded_by, consolidated, "private" FROM episodes'
|
|
12
|
-
).all().map(ep => ({
|
|
13
|
-
...ep,
|
|
14
|
-
tags: safeJsonParse(ep.tags, null),
|
|
15
|
-
context: safeJsonParse(ep.context, null),
|
|
16
|
-
affect: safeJsonParse(ep.affect, null),
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
const semantics = db.prepare(
|
|
20
|
-
'SELECT id, content, state, conditions, evidence_episode_ids, evidence_count, supporting_count, contradicting_count, source_type_diversity, consolidation_checkpoint, created_at, last_reinforced_at, retrieval_count, challenge_count, interference_count, salience FROM semantics'
|
|
21
|
-
).all().map(sem => ({
|
|
22
|
-
...sem,
|
|
23
|
-
evidence_episode_ids: safeJsonParse(sem.evidence_episode_ids, []),
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
const procedures = db.prepare(
|
|
27
|
-
'SELECT id, content, state, trigger_conditions, evidence_episode_ids, success_count, failure_count, created_at, last_reinforced_at, retrieval_count, interference_count, salience FROM procedures'
|
|
28
|
-
).all().map(proc => ({
|
|
29
|
-
...proc,
|
|
30
|
-
evidence_episode_ids: safeJsonParse(proc.evidence_episode_ids, []),
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
const causalLinks = db.prepare('SELECT * FROM causal_links').all();
|
|
34
|
-
|
|
35
|
-
const contradictions = db.prepare(
|
|
36
|
-
'SELECT id, claim_a_id, claim_a_type, claim_b_id, claim_b_type, state, resolution, resolved_at, reopened_at, reopen_evidence_id, created_at FROM contradictions'
|
|
37
|
-
).all();
|
|
38
|
-
|
|
39
|
-
const consolidationRuns = db.prepare(
|
|
40
|
-
'SELECT id, input_episode_ids, output_memory_ids, started_at, completed_at, status FROM consolidation_runs'
|
|
41
|
-
).all().map(run => ({
|
|
42
|
-
...run,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import { safeJsonParse } from './utils.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
8
|
+
|
|
9
|
+
export function exportMemories(db) {
|
|
10
|
+
const episodes = db.prepare(
|
|
11
|
+
'SELECT id, content, source, source_reliability, salience, context, affect, tags, causal_trigger, causal_consequence, created_at, embedding_model, embedding_version, supersedes, superseded_by, consolidated, "private" FROM episodes'
|
|
12
|
+
).all().map(ep => ({
|
|
13
|
+
...ep,
|
|
14
|
+
tags: safeJsonParse(ep.tags, null),
|
|
15
|
+
context: safeJsonParse(ep.context, null),
|
|
16
|
+
affect: safeJsonParse(ep.affect, null),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const semantics = db.prepare(
|
|
20
|
+
'SELECT id, content, state, conditions, evidence_episode_ids, evidence_count, supporting_count, contradicting_count, source_type_diversity, consolidation_checkpoint, embedding_model, embedding_version, consolidation_model, consolidation_prompt_hash, created_at, last_reinforced_at, retrieval_count, challenge_count, interference_count, salience FROM semantics'
|
|
21
|
+
).all().map(sem => ({
|
|
22
|
+
...sem,
|
|
23
|
+
evidence_episode_ids: safeJsonParse(sem.evidence_episode_ids, []),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const procedures = db.prepare(
|
|
27
|
+
'SELECT id, content, state, trigger_conditions, evidence_episode_ids, success_count, failure_count, embedding_model, embedding_version, created_at, last_reinforced_at, retrieval_count, interference_count, salience FROM procedures'
|
|
28
|
+
).all().map(proc => ({
|
|
29
|
+
...proc,
|
|
30
|
+
evidence_episode_ids: safeJsonParse(proc.evidence_episode_ids, []),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const causalLinks = db.prepare('SELECT * FROM causal_links').all();
|
|
34
|
+
|
|
35
|
+
const contradictions = db.prepare(
|
|
36
|
+
'SELECT id, claim_a_id, claim_a_type, claim_b_id, claim_b_type, state, resolution, resolved_at, reopened_at, reopen_evidence_id, created_at FROM contradictions'
|
|
37
|
+
).all();
|
|
38
|
+
|
|
39
|
+
const consolidationRuns = db.prepare(
|
|
40
|
+
'SELECT id, checkpoint_cursor, input_episode_ids, output_memory_ids, confidence_deltas, consolidation_model, consolidation_prompt_hash, started_at, completed_at, status FROM consolidation_runs'
|
|
41
|
+
).all().map(run => ({
|
|
42
|
+
...run,
|
|
43
|
+
confidence_deltas: safeJsonParse(run.confidence_deltas, null),
|
|
44
|
+
input_episode_ids: safeJsonParse(run.input_episode_ids, []),
|
|
45
|
+
output_memory_ids: safeJsonParse(run.output_memory_ids, []),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const consolidationMetrics = db.prepare(
|
|
49
|
+
'SELECT id, run_id, min_cluster_size, similarity_threshold, episodes_evaluated, clusters_found, principles_extracted, created_at FROM consolidation_metrics'
|
|
50
|
+
).all();
|
|
51
|
+
|
|
52
|
+
const configRows = db.prepare('SELECT key, value FROM audrey_config').all();
|
|
53
|
+
const config = Object.fromEntries(configRows.map(r => [r.key, r.value]));
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
version: pkg.version,
|
|
57
|
+
exportedAt: new Date().toISOString(),
|
|
58
|
+
episodes,
|
|
59
|
+
semantics,
|
|
60
|
+
procedures,
|
|
61
|
+
causalLinks,
|
|
62
|
+
contradictions,
|
|
63
|
+
consolidationRuns,
|
|
64
|
+
consolidationMetrics,
|
|
65
|
+
config,
|
|
66
|
+
};
|
|
67
|
+
}
|