confused-ai-core 0.1.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/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory memory store implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
MemoryStore,
|
|
7
|
+
MemoryEntry,
|
|
8
|
+
MemoryQuery,
|
|
9
|
+
MemoryFilter,
|
|
10
|
+
MemoryType,
|
|
11
|
+
MemorySearchResult,
|
|
12
|
+
MemoryStoreConfig,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
import type { EntityId } from '../core/types.js';
|
|
15
|
+
import { DebugLogger, createDebugLogger } from '../debug-logger.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Default configuration for in-memory store
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_CONFIG: Required<MemoryStoreConfig> = {
|
|
21
|
+
maxShortTermEntries: 100,
|
|
22
|
+
defaultQueryLimit: 10,
|
|
23
|
+
similarityThreshold: 0.7,
|
|
24
|
+
embeddingDimension: 1536,
|
|
25
|
+
debug: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* In-memory implementation of MemoryStore
|
|
30
|
+
* Suitable for development and testing
|
|
31
|
+
*/
|
|
32
|
+
export class InMemoryStore implements MemoryStore {
|
|
33
|
+
private memories: Map<EntityId, MemoryEntry> = new Map();
|
|
34
|
+
private config: Required<MemoryStoreConfig>;
|
|
35
|
+
private logger: DebugLogger;
|
|
36
|
+
|
|
37
|
+
constructor(config: MemoryStoreConfig = {}) {
|
|
38
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
39
|
+
this.logger = createDebugLogger('MemoryStore', this.config.debug);
|
|
40
|
+
this.logger.debug('InMemoryStore initialized', undefined, this.config);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async store(entry: Omit<MemoryEntry, 'id' | 'createdAt'>): Promise<MemoryEntry> {
|
|
44
|
+
const id = this.generateId();
|
|
45
|
+
const createdAt = new Date();
|
|
46
|
+
|
|
47
|
+
const fullEntry: MemoryEntry = {
|
|
48
|
+
...entry,
|
|
49
|
+
id,
|
|
50
|
+
createdAt,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
this.memories.set(id, fullEntry);
|
|
54
|
+
this.logger.debug('Stored memory entry', undefined, {
|
|
55
|
+
id,
|
|
56
|
+
type: entry.type,
|
|
57
|
+
tags: entry.metadata.tags,
|
|
58
|
+
agentId: entry.metadata.agentId,
|
|
59
|
+
sessionId: entry.metadata.sessionId,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Enforce short-term memory limits
|
|
63
|
+
if (entry.type === MemoryType.SHORT_TERM) {
|
|
64
|
+
this.enforceShortTermLimit();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return fullEntry;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async retrieve(query: MemoryQuery): Promise<MemorySearchResult[]> {
|
|
71
|
+
const limit = query.limit ?? this.config.defaultQueryLimit;
|
|
72
|
+
const threshold = query.threshold ?? this.config.similarityThreshold;
|
|
73
|
+
|
|
74
|
+
let entries = Array.from(this.memories.values());
|
|
75
|
+
|
|
76
|
+
// Filter by type
|
|
77
|
+
if (query.type) {
|
|
78
|
+
entries = entries.filter(e => e.type === query.type);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Apply filters
|
|
82
|
+
if (query.filter) {
|
|
83
|
+
entries = this.applyFilter(entries, query.filter);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Calculate similarity scores (simplified - just keyword matching for in-memory)
|
|
87
|
+
const scored = entries.map(entry => ({
|
|
88
|
+
entry,
|
|
89
|
+
score: this.calculateSimilarity(query.query, entry.content),
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
// Filter by threshold and sort by score
|
|
93
|
+
const results = scored
|
|
94
|
+
.filter(r => r.score >= threshold)
|
|
95
|
+
.sort((a, b) => b.score - a.score)
|
|
96
|
+
.slice(0, limit);
|
|
97
|
+
|
|
98
|
+
this.logger.debug('Retrieved memory results', undefined, {
|
|
99
|
+
query: query.query.slice(0, 50),
|
|
100
|
+
type: query.type,
|
|
101
|
+
filter: query.filter,
|
|
102
|
+
totalMatches: results.length,
|
|
103
|
+
limit: limit,
|
|
104
|
+
threshold: threshold,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async get(id: EntityId): Promise<MemoryEntry | null> {
|
|
111
|
+
const entry = this.memories.get(id) ?? null;
|
|
112
|
+
if (entry) {
|
|
113
|
+
this.logger.debug('Retrieved memory entry', undefined, { id, type: entry.type });
|
|
114
|
+
} else {
|
|
115
|
+
this.logger.debug('Memory entry not found', undefined, { id });
|
|
116
|
+
}
|
|
117
|
+
return entry;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async update(
|
|
121
|
+
id: EntityId,
|
|
122
|
+
updates: Partial<Omit<MemoryEntry, 'id' | 'createdAt'>>
|
|
123
|
+
): Promise<MemoryEntry> {
|
|
124
|
+
const existing = this.memories.get(id);
|
|
125
|
+
if (!existing) {
|
|
126
|
+
throw new Error(`Memory entry not found: ${id}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const updated: MemoryEntry = {
|
|
130
|
+
...existing,
|
|
131
|
+
...updates,
|
|
132
|
+
id: existing.id,
|
|
133
|
+
createdAt: existing.createdAt,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
this.memories.set(id, updated);
|
|
137
|
+
return updated;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async delete(id: EntityId): Promise<boolean> {
|
|
141
|
+
return this.memories.delete(id);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async clear(type?: MemoryType): Promise<void> {
|
|
145
|
+
if (type) {
|
|
146
|
+
for (const [id, entry] of this.memories) {
|
|
147
|
+
if (entry.type === type) {
|
|
148
|
+
this.memories.delete(id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
this.memories.clear();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getRecent(limit: number, type?: MemoryType): Promise<MemoryEntry[]> {
|
|
157
|
+
let entries = Array.from(this.memories.values());
|
|
158
|
+
|
|
159
|
+
if (type) {
|
|
160
|
+
entries = entries.filter(e => e.type === type);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return entries
|
|
164
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
|
165
|
+
.slice(0, limit);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async snapshot(): Promise<MemoryEntry[]> {
|
|
169
|
+
return Array.from(this.memories.values());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get the number of stored memories
|
|
174
|
+
*/
|
|
175
|
+
size(): number {
|
|
176
|
+
return this.memories.size;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Generate a unique ID
|
|
181
|
+
*/
|
|
182
|
+
private generateId(): EntityId {
|
|
183
|
+
return `mem-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Apply filter to memory entries
|
|
188
|
+
*/
|
|
189
|
+
private applyFilter(entries: MemoryEntry[], filter: MemoryFilter): MemoryEntry[] {
|
|
190
|
+
return entries.filter(entry => {
|
|
191
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
192
|
+
const entryTags = entry.metadata.tags ?? [];
|
|
193
|
+
if (!filter.tags.some(tag => entryTags.includes(tag))) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (filter.source && entry.metadata.source !== filter.source) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (filter.agentId && entry.metadata.agentId !== filter.agentId) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (filter.sessionId && entry.metadata.sessionId !== filter.sessionId) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (filter.before && entry.createdAt > filter.before) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (filter.after && entry.createdAt < filter.after) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return true;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Calculate simple similarity score between query and content
|
|
224
|
+
* In production, use proper embeddings
|
|
225
|
+
*/
|
|
226
|
+
private calculateSimilarity(query: string, content: string): number {
|
|
227
|
+
const queryWords = query.toLowerCase().split(/\s+/);
|
|
228
|
+
const contentWords = content.toLowerCase().split(/\s+/);
|
|
229
|
+
|
|
230
|
+
let matches = 0;
|
|
231
|
+
for (const word of queryWords) {
|
|
232
|
+
if (contentWords.some(cw => cw.includes(word) || word.includes(cw))) {
|
|
233
|
+
matches++;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return matches / Math.max(queryWords.length, 1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Enforce short-term memory entry limit
|
|
242
|
+
*/
|
|
243
|
+
private enforceShortTermLimit(): void {
|
|
244
|
+
const shortTermEntries = Array.from(this.memories.values())
|
|
245
|
+
.filter(e => e.type === MemoryType.SHORT_TERM)
|
|
246
|
+
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
247
|
+
|
|
248
|
+
const excess = shortTermEntries.length - this.config.maxShortTermEntries;
|
|
249
|
+
if (excess > 0) {
|
|
250
|
+
for (let i = 0; i < excess; i++) {
|
|
251
|
+
this.memories.delete(shortTermEntries[i].id);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory store types and interfaces
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EntityId } from '../core/types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Types of memory supported
|
|
9
|
+
*/
|
|
10
|
+
export enum MemoryType {
|
|
11
|
+
SHORT_TERM = 'short_term',
|
|
12
|
+
LONG_TERM = 'long_term',
|
|
13
|
+
EPISODIC = 'episodic',
|
|
14
|
+
SEMANTIC = 'semantic',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A memory entry
|
|
19
|
+
*/
|
|
20
|
+
export interface MemoryEntry {
|
|
21
|
+
readonly id: EntityId;
|
|
22
|
+
readonly type: MemoryType;
|
|
23
|
+
readonly content: string;
|
|
24
|
+
readonly embedding?: number[];
|
|
25
|
+
readonly metadata: MemoryMetadata;
|
|
26
|
+
readonly createdAt: Date;
|
|
27
|
+
readonly expiresAt?: Date;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Metadata for memory entries
|
|
32
|
+
*/
|
|
33
|
+
export interface MemoryMetadata {
|
|
34
|
+
readonly source?: string;
|
|
35
|
+
readonly importance?: number;
|
|
36
|
+
readonly tags?: string[];
|
|
37
|
+
readonly agentId?: EntityId;
|
|
38
|
+
readonly sessionId?: string;
|
|
39
|
+
readonly custom?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Query options for memory retrieval
|
|
44
|
+
*/
|
|
45
|
+
export interface MemoryQuery {
|
|
46
|
+
readonly query: string;
|
|
47
|
+
readonly type?: MemoryType;
|
|
48
|
+
readonly limit?: number;
|
|
49
|
+
readonly threshold?: number;
|
|
50
|
+
readonly filter?: MemoryFilter;
|
|
51
|
+
readonly includeEmbeddings?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Filter for memory queries
|
|
56
|
+
*/
|
|
57
|
+
export interface MemoryFilter {
|
|
58
|
+
readonly tags?: string[];
|
|
59
|
+
readonly source?: string;
|
|
60
|
+
readonly agentId?: EntityId;
|
|
61
|
+
readonly sessionId?: string;
|
|
62
|
+
readonly before?: Date;
|
|
63
|
+
readonly after?: Date;
|
|
64
|
+
readonly custom?: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Configuration for memory store
|
|
69
|
+
*/
|
|
70
|
+
export interface MemoryStoreConfig {
|
|
71
|
+
readonly maxShortTermEntries?: number;
|
|
72
|
+
readonly defaultQueryLimit?: number;
|
|
73
|
+
readonly similarityThreshold?: number;
|
|
74
|
+
readonly embeddingDimension?: number;
|
|
75
|
+
/** Enable debug logging */
|
|
76
|
+
readonly debug?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Result from memory search
|
|
81
|
+
*/
|
|
82
|
+
export interface MemorySearchResult {
|
|
83
|
+
readonly entry: MemoryEntry;
|
|
84
|
+
readonly score: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Abstract memory store interface
|
|
89
|
+
*/
|
|
90
|
+
export interface MemoryStore {
|
|
91
|
+
/**
|
|
92
|
+
* Store a new memory entry
|
|
93
|
+
*/
|
|
94
|
+
store(entry: Omit<MemoryEntry, 'id' | 'createdAt'>): Promise<MemoryEntry>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Retrieve memories by query (semantic search)
|
|
98
|
+
*/
|
|
99
|
+
retrieve(query: MemoryQuery): Promise<MemorySearchResult[]>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get a specific memory by ID
|
|
103
|
+
*/
|
|
104
|
+
get(id: EntityId): Promise<MemoryEntry | null>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Update an existing memory
|
|
108
|
+
*/
|
|
109
|
+
update(id: EntityId, updates: Partial<Omit<MemoryEntry, 'id' | 'createdAt'>>): Promise<MemoryEntry>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Delete a memory by ID
|
|
113
|
+
*/
|
|
114
|
+
delete(id: EntityId): Promise<boolean>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Clear all memories (optionally filtered by type)
|
|
118
|
+
*/
|
|
119
|
+
clear(type?: MemoryType): Promise<void>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get recent memories
|
|
123
|
+
*/
|
|
124
|
+
getRecent(limit: number, type?: MemoryType): Promise<MemoryEntry[]>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a snapshot of current memories
|
|
128
|
+
*/
|
|
129
|
+
snapshot(): Promise<MemoryEntry[]>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Embedding provider interface for vector operations
|
|
134
|
+
*/
|
|
135
|
+
export interface EmbeddingProvider {
|
|
136
|
+
/**
|
|
137
|
+
* Generate embeddings for text
|
|
138
|
+
*/
|
|
139
|
+
embed(text: string): Promise<number[]>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Generate embeddings for multiple texts
|
|
143
|
+
*/
|
|
144
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the dimension of embeddings
|
|
148
|
+
*/
|
|
149
|
+
getDimension(): number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Vector store adapter interface
|
|
154
|
+
*/
|
|
155
|
+
export interface VectorStoreAdapter {
|
|
156
|
+
/**
|
|
157
|
+
* Store vectors with metadata
|
|
158
|
+
*/
|
|
159
|
+
upsert(vectors: VectorEntry[]): Promise<void>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Search for similar vectors
|
|
163
|
+
*/
|
|
164
|
+
search(query: number[], limit: number, filter?: Record<string, unknown>): Promise<VectorSearchResult[]>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Delete vectors by ID
|
|
168
|
+
*/
|
|
169
|
+
delete(ids: EntityId[]): Promise<void>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Clear all vectors
|
|
173
|
+
*/
|
|
174
|
+
clear(): Promise<void>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Vector entry for storage
|
|
179
|
+
*/
|
|
180
|
+
export interface VectorEntry {
|
|
181
|
+
readonly id: EntityId;
|
|
182
|
+
readonly vector: number[];
|
|
183
|
+
readonly metadata: Record<string, unknown>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Vector search result
|
|
188
|
+
*/
|
|
189
|
+
export interface VectorSearchResult {
|
|
190
|
+
readonly id: EntityId;
|
|
191
|
+
readonly score: number;
|
|
192
|
+
readonly metadata: Record<string, unknown>;
|
|
193
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector-based memory store implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
MemoryStore,
|
|
7
|
+
MemoryEntry,
|
|
8
|
+
MemoryQuery,
|
|
9
|
+
MemoryFilter,
|
|
10
|
+
MemoryType,
|
|
11
|
+
MemorySearchResult,
|
|
12
|
+
MemoryStoreConfig,
|
|
13
|
+
VectorStoreAdapter,
|
|
14
|
+
EmbeddingProvider,
|
|
15
|
+
} from './types.js';
|
|
16
|
+
import type { EntityId } from '../core/types.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for vector memory store
|
|
20
|
+
*/
|
|
21
|
+
export interface VectorMemoryStoreConfig extends MemoryStoreConfig {
|
|
22
|
+
vectorStore: VectorStoreAdapter;
|
|
23
|
+
embeddingProvider: EmbeddingProvider;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Vector-based memory store using external vector database
|
|
28
|
+
*/
|
|
29
|
+
export class VectorMemoryStore implements MemoryStore {
|
|
30
|
+
private config: Required<MemoryStoreConfig>;
|
|
31
|
+
private vectorStore: VectorStoreAdapter;
|
|
32
|
+
private embeddingProvider: EmbeddingProvider;
|
|
33
|
+
private entryCache: Map<EntityId, MemoryEntry> = new Map();
|
|
34
|
+
|
|
35
|
+
constructor(config: VectorMemoryStoreConfig) {
|
|
36
|
+
this.vectorStore = config.vectorStore;
|
|
37
|
+
this.embeddingProvider = config.embeddingProvider;
|
|
38
|
+
this.config = {
|
|
39
|
+
maxShortTermEntries: config.maxShortTermEntries ?? 100,
|
|
40
|
+
defaultQueryLimit: config.defaultQueryLimit ?? 10,
|
|
41
|
+
similarityThreshold: config.similarityThreshold ?? 0.7,
|
|
42
|
+
embeddingDimension: config.embeddingDimension ?? this.embeddingProvider.getDimension(),
|
|
43
|
+
debug: config.debug ?? false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async store(entry: Omit<MemoryEntry, 'id' | 'createdAt'>): Promise<MemoryEntry> {
|
|
48
|
+
const id = this.generateId();
|
|
49
|
+
const createdAt = new Date();
|
|
50
|
+
|
|
51
|
+
// Generate embedding for the content
|
|
52
|
+
const embedding = await this.embeddingProvider.embed(entry.content);
|
|
53
|
+
|
|
54
|
+
const fullEntry: MemoryEntry = {
|
|
55
|
+
...entry,
|
|
56
|
+
id,
|
|
57
|
+
createdAt,
|
|
58
|
+
embedding,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Store in vector database
|
|
62
|
+
await this.vectorStore.upsert([
|
|
63
|
+
{
|
|
64
|
+
id,
|
|
65
|
+
vector: embedding,
|
|
66
|
+
metadata: {
|
|
67
|
+
type: entry.type,
|
|
68
|
+
content: entry.content,
|
|
69
|
+
createdAt: createdAt.toISOString(),
|
|
70
|
+
...entry.metadata,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
// Cache locally
|
|
76
|
+
this.entryCache.set(id, fullEntry);
|
|
77
|
+
|
|
78
|
+
return fullEntry;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async retrieve(query: MemoryQuery): Promise<MemorySearchResult[]> {
|
|
82
|
+
const limit = query.limit ?? this.config.defaultQueryLimit;
|
|
83
|
+
|
|
84
|
+
// Generate embedding for the query
|
|
85
|
+
const queryEmbedding = await this.embeddingProvider.embed(query.query);
|
|
86
|
+
|
|
87
|
+
// Build filter for vector store
|
|
88
|
+
const filter: Record<string, unknown> = {};
|
|
89
|
+
if (query.type) {
|
|
90
|
+
filter.type = query.type;
|
|
91
|
+
}
|
|
92
|
+
if (query.filter) {
|
|
93
|
+
Object.assign(filter, this.convertFilter(query.filter));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Search vector store
|
|
97
|
+
const results = await this.vectorStore.search(queryEmbedding, limit, filter);
|
|
98
|
+
|
|
99
|
+
// Convert to MemorySearchResult
|
|
100
|
+
return results.map(r => {
|
|
101
|
+
const entry = this.entryCache.get(r.id) ?? this.reconstructEntry(r.id, r.metadata);
|
|
102
|
+
return {
|
|
103
|
+
entry,
|
|
104
|
+
score: r.score,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async get(id: EntityId): Promise<MemoryEntry | null> {
|
|
110
|
+
// Check cache first
|
|
111
|
+
const cached = this.entryCache.get(id);
|
|
112
|
+
if (cached) {
|
|
113
|
+
return cached;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Would need to implement get by ID in vector store
|
|
117
|
+
// For now, return null
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async update(
|
|
122
|
+
id: EntityId,
|
|
123
|
+
updates: Partial<Omit<MemoryEntry, 'id' | 'createdAt'>>
|
|
124
|
+
): Promise<MemoryEntry> {
|
|
125
|
+
const existing = await this.get(id);
|
|
126
|
+
if (!existing) {
|
|
127
|
+
throw new Error(`Memory entry not found: ${id}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// If content is updated, regenerate embedding
|
|
131
|
+
let embedding = existing.embedding;
|
|
132
|
+
if (updates.content && updates.content !== existing.content) {
|
|
133
|
+
embedding = await this.embeddingProvider.embed(updates.content);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const updated: MemoryEntry = {
|
|
137
|
+
...existing,
|
|
138
|
+
...updates,
|
|
139
|
+
id: existing.id,
|
|
140
|
+
createdAt: existing.createdAt,
|
|
141
|
+
embedding: embedding ?? existing.embedding,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Update in vector store
|
|
145
|
+
await this.vectorStore.upsert([
|
|
146
|
+
{
|
|
147
|
+
id,
|
|
148
|
+
vector: updated.embedding ?? [],
|
|
149
|
+
metadata: {
|
|
150
|
+
type: updated.type,
|
|
151
|
+
content: updated.content,
|
|
152
|
+
createdAt: updated.createdAt.toISOString(),
|
|
153
|
+
...updated.metadata,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
// Update cache
|
|
159
|
+
this.entryCache.set(id, updated);
|
|
160
|
+
|
|
161
|
+
return updated;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async delete(id: EntityId): Promise<boolean> {
|
|
165
|
+
await this.vectorStore.delete([id]);
|
|
166
|
+
this.entryCache.delete(id);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async clear(type?: MemoryType): Promise<void> {
|
|
171
|
+
if (type) {
|
|
172
|
+
// Vector stores typically don't support clearing by metadata filter
|
|
173
|
+
// This would need to be implemented in the adapter
|
|
174
|
+
const allEntries = Array.from(this.entryCache.values());
|
|
175
|
+
const toDelete = allEntries.filter(e => e.type === type).map(e => e.id);
|
|
176
|
+
await this.vectorStore.delete(toDelete);
|
|
177
|
+
for (const id of toDelete) {
|
|
178
|
+
this.entryCache.delete(id);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
await this.vectorStore.clear();
|
|
182
|
+
this.entryCache.clear();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async getRecent(limit: number, type?: MemoryType): Promise<MemoryEntry[]> {
|
|
187
|
+
// Get all cached entries sorted by date
|
|
188
|
+
let entries = Array.from(this.entryCache.values());
|
|
189
|
+
|
|
190
|
+
if (type) {
|
|
191
|
+
entries = entries.filter(e => e.type === type);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return entries
|
|
195
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
|
196
|
+
.slice(0, limit);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async snapshot(): Promise<MemoryEntry[]> {
|
|
200
|
+
return Array.from(this.entryCache.values());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generate a unique ID
|
|
205
|
+
*/
|
|
206
|
+
private generateId(): EntityId {
|
|
207
|
+
return `vec-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Convert memory filter to vector store filter
|
|
212
|
+
*/
|
|
213
|
+
private convertFilter(filter: MemoryFilter): Record<string, unknown> {
|
|
214
|
+
const result: Record<string, unknown> = {};
|
|
215
|
+
|
|
216
|
+
if (filter.tags) {
|
|
217
|
+
result.tags = filter.tags;
|
|
218
|
+
}
|
|
219
|
+
if (filter.source) {
|
|
220
|
+
result.source = filter.source;
|
|
221
|
+
}
|
|
222
|
+
if (filter.agentId) {
|
|
223
|
+
result.agentId = filter.agentId;
|
|
224
|
+
}
|
|
225
|
+
if (filter.sessionId) {
|
|
226
|
+
result.sessionId = filter.sessionId;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Reconstruct a memory entry from vector store metadata
|
|
234
|
+
*/
|
|
235
|
+
private reconstructEntry(id: EntityId, metadata: Record<string, unknown>): MemoryEntry {
|
|
236
|
+
return {
|
|
237
|
+
id,
|
|
238
|
+
type: (metadata.type as MemoryType) ?? MemoryType.SHORT_TERM,
|
|
239
|
+
content: (metadata.content as string) ?? '',
|
|
240
|
+
metadata: {
|
|
241
|
+
source: metadata.source as string,
|
|
242
|
+
importance: metadata.importance as number,
|
|
243
|
+
tags: metadata.tags as string[],
|
|
244
|
+
agentId: metadata.agentId as EntityId,
|
|
245
|
+
sessionId: metadata.sessionId as string,
|
|
246
|
+
custom: metadata.custom as Record<string, unknown>,
|
|
247
|
+
},
|
|
248
|
+
createdAt: new Date(metadata.createdAt as string),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|