memories-lite 0.99.5 → 0.99.9
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/README.md +123 -150
- package/ROADMAP.md +75 -0
- package/dist/memory/index.d.ts +14 -0
- package/dist/memory/index.js +127 -0
- package/dist/memory/memory.types.d.ts +2 -0
- package/dist/prompts/index.d.ts +52 -106
- package/dist/prompts/index.js +151 -216
- package/memories-lite-a42ac5108869b599bcbac21069f63fb47f07452fcc4b87e89b3c06a945612d0b.db +0 -0
- package/memories-lite-a9137698d8d3fdbf27efcdc8cd372084b52d484e8db866c5455bbb3f85299b54.db +0 -0
- package/package.json +1 -1
- package/src/memory/index.ts +181 -0
- package/src/memory/memory.types.ts +4 -0
- package/src/prompts/index.ts +151 -237
- package/tests/memory.captureOrUpdate.test.ts +379 -0
- package/tests/memory.capturePrompt.test.ts +248 -0
- package/tests/memory.users.test.ts +60 -40
- package/MEMORIES.md +0 -39
package/README.md
CHANGED
|
@@ -1,196 +1,169 @@
|
|
|
1
|
-
#
|
|
1
|
+
# memories-lite
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Lightweight memory layer for AI agents — LLM-based extraction, vector embeddings for retrieval, per-user isolation.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
- [Quick Start](#-quick-start)
|
|
7
|
-
- [Installation](#-installation)
|
|
8
|
-
- [Basic Usage](#-basic-usage)
|
|
9
|
-
- [Key Features](#-key-features)
|
|
10
|
-
- [Memory Types](#-memory-types)
|
|
11
|
-
- [Use Cases](#-use-cases)
|
|
12
|
-
- [Advanced Configuration](#-advanced-configuration)
|
|
13
|
-
- [Documentation](#-documentation)
|
|
14
|
-
- [Acknowledgements](#-acknowledgements)
|
|
5
|
+
## Quick Start
|
|
15
6
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
# Install the package
|
|
20
|
-
npm install memories-lite
|
|
21
|
-
|
|
22
|
-
# Basic usage
|
|
7
|
+
```typescript
|
|
23
8
|
import { MemoriesLite } from 'memories-lite';
|
|
24
9
|
|
|
25
10
|
const memory = new MemoriesLite({
|
|
26
|
-
llm: {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
embedder: {
|
|
31
|
-
provider: 'openai',
|
|
32
|
-
config: { apiKey: 'YOUR_API_KEY', model: 'text-embedding-3-small' }
|
|
33
|
-
}
|
|
11
|
+
llm: { provider: 'openai', config: { apiKey: 'KEY', model: 'gpt-5-mini' } },
|
|
12
|
+
embedder: { provider: 'openai', config: { apiKey: 'KEY', model: 'text-embedding-3-small', dimension: 768 } },
|
|
13
|
+
vectorStore: { provider: 'lite', config: { dimension: 768, rootPath: './data' } }
|
|
34
14
|
});
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
|
|
16
|
+
const userId = 'user-123';
|
|
17
|
+
|
|
18
|
+
// Capture a discussion → {title, summary}
|
|
19
|
+
await memory.capture(messages, userId, { metadata: { type: 'discussion' } });
|
|
38
20
|
|
|
39
|
-
//
|
|
40
|
-
|
|
21
|
+
// Capture or merge preferences into an existing memory (auto-load by ID)
|
|
22
|
+
await memory.captureOrUpdate(messages, userId, {
|
|
23
|
+
existingMemoryId: 'abc-123', // loads content + title from store
|
|
24
|
+
metadata: { type: 'discussion', applyMode: 'MEM_ALWAYS' }
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Retrieve by semantic search
|
|
28
|
+
const results = await memory.retrieve('réponses courtes', userId, { filters: { type: 'discussion' } });
|
|
29
|
+
|
|
30
|
+
// CRUD
|
|
31
|
+
await memory.update(memoryId, 'New content', userId, { title: 'New title' });
|
|
32
|
+
await memory.delete(memoryId, userId);
|
|
33
|
+
const all = await memory.getAll(userId, { type: 'discussion' });
|
|
41
34
|
```
|
|
42
35
|
|
|
43
|
-
##
|
|
36
|
+
## Architecture
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
- **Business-Centric Design**: Simplified API and workflows specifically tailored for business use cases
|
|
47
|
-
- **Advanced Hybrid Scoring**: Improved relevance through a custom scoring algorithm that balances vector similarity, recency, and importance
|
|
48
|
-
- **Enhanced Security**: One database per user architecture that provides stronger isolation and data protection
|
|
49
|
-
- **Streamlined Implementation**: Focused on essential features with minimal dependencies
|
|
38
|
+
### Instruction mémorisable
|
|
50
39
|
|
|
51
|
-
|
|
40
|
+
Une instruction mémorisable est une règle de comportement durable liée à l'utilisateur courant, indiquant comment l'assistant doit se comporter au-delà du cas en cours.
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
yarn add memories-lite
|
|
57
|
-
```
|
|
42
|
+
Exemples :
|
|
43
|
+
- `L'utilisateur souhaite qu'à l'avenir tu le tutoies et que tu répondes toujours en français.`
|
|
44
|
+
- `L'utilisateur utilise Windows 11 avec PowerShell (pas Linux).`
|
|
58
45
|
|
|
59
|
-
|
|
46
|
+
Ce qui n'est **pas** mémorisable : actions ponctuelles ("envoie un email"), données métier, questions procédurales.
|
|
60
47
|
|
|
61
|
-
|
|
62
|
-
import { MemoriesLite } from 'memories-lite';
|
|
48
|
+
### Two Capture Flows
|
|
63
49
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
```
|
|
51
|
+
Flow 1: capture() — Discussion → New Memory
|
|
52
|
+
─────────────────────────────────────────────
|
|
53
|
+
User clicks "Memorize" on a discussion
|
|
54
|
+
└─> capture(messages, userId, { capturePrompt?, metadata })
|
|
55
|
+
└─> LLM → { title, summary }
|
|
56
|
+
└─> createMemory(summary, embed(title))
|
|
57
|
+
|
|
58
|
+
Flow 2: captureOrUpdate() — Discussion → Create or Merge
|
|
59
|
+
──────────────────────────────────────────────────────────
|
|
60
|
+
Agent tool or explicit user action
|
|
61
|
+
└─> captureOrUpdate(messages, userId, {
|
|
62
|
+
existingMemoryId?, // if provided → auto-load + UPDATE (merge)
|
|
63
|
+
metadata
|
|
64
|
+
})
|
|
65
|
+
├─ if existingMemoryId:
|
|
66
|
+
│ └─> get(existingMemoryId) → { memory, title }
|
|
67
|
+
│ └─> buildCapturePrompt(memory) → merge prompt
|
|
68
|
+
│ └─> LLM → complete merged set
|
|
69
|
+
│ └─> updateMemory() (preserves title + ID)
|
|
70
|
+
└─ else:
|
|
71
|
+
└─> buildCapturePrompt() → capture prompt
|
|
72
|
+
└─> LLM → { title, summary }
|
|
73
|
+
└─> createMemory()
|
|
74
|
+
```
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
const userId = 'user-123';
|
|
76
|
+
`existingMemoryId` est prioritaire : le contenu et le titre sont toujours chargés depuis le store.
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
await memory.capture('I love Italian food', userId);
|
|
78
|
+
### Memory Apply Modes
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
| Mode | Injection | Use case |
|
|
81
|
+
|------|-----------|----------|
|
|
82
|
+
| `MEM_ALWAYS` | Automatic, every request | User preferences, permanent instructions |
|
|
83
|
+
| `MEM_SMART` | Semantic search match | Discussion syntheses, knowledge patterns |
|
|
84
|
+
| `MEM_MANUAL` | Explicit via `rules[]` | Session-specific rules |
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
if (results.results.length > 0) {
|
|
89
|
-
await memory.update(results.results[0].id, 'I love Italian and French cuisine', userId);
|
|
90
|
-
}
|
|
86
|
+
### Context Injection Flow
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
if (results.results.length > 0) {
|
|
94
|
-
await memory.delete(results.results[0].id, userId);
|
|
95
|
-
}
|
|
88
|
+
Memories are injected into the agent's system prompt via `enrichSystemWithMemory()`:
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
```
|
|
91
|
+
enrichSystemWithMemory()
|
|
92
|
+
├─> getAll(userId, {type: 'discussion'})
|
|
93
|
+
├─> filter MEM_ALWAYS → globalInstructionsStr
|
|
94
|
+
├─> filter rules[] → sessionInstructionsStr
|
|
95
|
+
└─> renderContextInjection(profile, global, session, history)
|
|
96
|
+
|
|
97
|
+
Produces:
|
|
98
|
+
<instructions>
|
|
99
|
+
GLOBAL:
|
|
100
|
+
L'utilisateur préfère les réponses courtes et structurées.
|
|
101
|
+
L'utilisateur utilise Windows 11 et souhaite des exemples en PowerShell.
|
|
102
|
+
</instructions>
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
### Merge Rules (captureOrUpdate)
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
- **Contextual Retrieval**: Find memories most relevant to the current query
|
|
105
|
-
- **User Isolation**: Each user's memories are stored separately for privacy and security
|
|
106
|
-
- **Memory Types**: Support for different types of memories (factual, episodic, etc.)
|
|
107
|
-
- **Custom Scoring**: Hybrid scoring system balancing similarity, recency, and importance
|
|
107
|
+
When merging new instructions with existing preferences:
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
| Case | Existing | New discussion | Result |
|
|
110
|
+
|------|----------|---------------|--------|
|
|
111
|
+
| Duplicate | "préfère le français" | "parle-moi en français" | Keep existing |
|
|
112
|
+
| Refinement | "réponses courtes" | "courtes sauf pour les rapports" | Replace with refined |
|
|
113
|
+
| Contradiction | "tutoiement" | "vouvoie-moi" | Replace with new |
|
|
114
|
+
| New | _(nothing)_ | "j'utilise macOS" | Add with date |
|
|
110
115
|
|
|
111
|
-
|
|
116
|
+
The LLM returns the **complete merged set** (not a diff). The title is preserved from the existing memory.
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
- User preferences, traits, and personal information
|
|
115
|
-
- Example: "User likes Italian cuisine"
|
|
118
|
+
### Memory Content Format
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
- Time-based events and interactions
|
|
119
|
-
- Example: "User has a meeting tomorrow at 2pm"
|
|
120
|
+
3rd person descriptive, grouped by date:
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
```
|
|
123
|
+
**2025-12-15**
|
|
124
|
+
L'utilisateur préfère les réponses courtes et structurées pour les résultats mfiles.
|
|
125
|
+
L'utilisateur souhaite que les formats "en gras" soient supprimés lors de la rédaction d'emails.
|
|
126
|
+
**2025-11-01**
|
|
127
|
+
L'utilisateur utilise Windows 11 et souhaite des exemples en shell.
|
|
128
|
+
```
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
- Step-by-step processes and workflows
|
|
127
|
-
- Example: "Steps to configure the company VPN"
|
|
130
|
+
## API
|
|
128
131
|
|
|
129
|
-
|
|
132
|
+
### `capture(messages, userId, config)`
|
|
133
|
+
Captures a discussion and generates a new memory with `{title, summary}`.
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- **Business Applications**: Integrate with enterprise systems to maintain contextual awareness
|
|
134
|
-
- **Educational Tools**: Create learning assistants that remember student progress
|
|
135
|
+
### `captureOrUpdate(messages, userId, config)`
|
|
136
|
+
Captures and creates a new memory, or merges into an existing one if `existingMemoryId` is provided. The existing memory is auto-loaded from the store (content + title preserved).
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
### `retrieve(query, userId, config)`
|
|
139
|
+
Semantic search. Returns only `MEM_SMART` memories.
|
|
137
140
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const customMemory = new MemoriesLite({
|
|
141
|
-
llm: {
|
|
142
|
-
provider: 'openai',
|
|
143
|
-
config: { apiKey: 'YOUR_API_KEY' }
|
|
144
|
-
},
|
|
145
|
-
embedder: {
|
|
146
|
-
provider: 'openai',
|
|
147
|
-
config: { apiKey: 'YOUR_API_KEY' }
|
|
148
|
-
},
|
|
149
|
-
vectorStore: {
|
|
150
|
-
provider: 'lite',
|
|
151
|
-
config: {
|
|
152
|
-
dimension: 1536,
|
|
153
|
-
scoring: {
|
|
154
|
-
// Prioritize factual memories with long retention
|
|
155
|
-
factual: { alpha: 0.7, beta: 0.2, gamma: 0.1, halfLifeDays: 365 },
|
|
156
|
-
// Make preferences permanently available
|
|
157
|
-
assistant_preference: { alpha: 0.6, beta: 0.0, gamma: 0.4, halfLifeDays: Infinity },
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
```
|
|
141
|
+
### `getAll(userId, config)`
|
|
142
|
+
List all memories (all apply modes).
|
|
163
143
|
|
|
164
|
-
|
|
144
|
+
### `get(memoryId, userId)`
|
|
145
|
+
Get a single memory by ID.
|
|
165
146
|
|
|
166
|
-
|
|
147
|
+
### `update(memoryId, data, userId, metadata?)`
|
|
148
|
+
Update memory content directly (no LLM).
|
|
167
149
|
|
|
168
|
-
|
|
169
|
-
|
|
150
|
+
### `delete(memoryId, userId)`
|
|
151
|
+
Delete a memory.
|
|
170
152
|
|
|
171
|
-
##
|
|
153
|
+
## Prompts
|
|
172
154
|
|
|
173
|
-
|
|
155
|
+
Built-in prompts (customizable via `capturePrompt`):
|
|
174
156
|
|
|
175
|
-
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
- **Reflexion**: Language Agents with Verbal Reinforcement Learning
|
|
157
|
+
- `DEFAULT_DISCUSSION_PROMPT` — Synthesis of a discussion into title + summary
|
|
158
|
+
- `DEFAULT_CAPTURE_PROMPT` — Extraction of memorizable user instructions (3rd person format)
|
|
159
|
+
- `DEFAULT_MERGE_RULES` — Merge extension with `<existing-preferences>` injection
|
|
179
160
|
|
|
180
|
-
|
|
161
|
+
Utilities:
|
|
162
|
+
- `buildCapturePrompt(existingContent?)` — Assembles capture or merge prompt
|
|
163
|
+
- `formatExistingPreferences(memories)` — Formats memories as date-grouped bullet points
|
|
181
164
|
|
|
182
|
-
|
|
183
|
-
- [x] **Implicit Memory Updates**: Auto-merging memories based on context without explicit ID references
|
|
184
|
-
- [x] **Virtual Sessions/Context Grouping**: Group memories related to specific conversation contexts
|
|
185
|
-
- [x] **User Isolation**: Separate storage per user for enhanced security and data privacy
|
|
186
|
-
- [x] **Memory Type Detection**: LLM-based automatic classification of memory types
|
|
187
|
-
- [x] **Core Memory Operations**: Basic CRUD operations with user-specific isolation
|
|
188
|
-
- [x] **Memory Decay & Scoring**: Hybrid scoring with recency decay and importance weights
|
|
189
|
-
- [ ] **Reflexion Pattern Integration**: Self-correction loops for memory refinement
|
|
190
|
-
- [x] **Memory Recency**: Prioritizing memories based on importance and time decay
|
|
191
|
-
- [x] **Edge Case Tests**: Complete unit tests for episodic and factual memory edge cases
|
|
192
|
-
- [ ] **Middleware Support**: Hooks and middleware for custom processing pipelines
|
|
165
|
+
## Acknowledgements
|
|
193
166
|
|
|
194
|
-
|
|
167
|
+
Forked from [Mem0](https://github.com/mem0ai/mem0).
|
|
195
168
|
|
|
196
|
-
|
|
169
|
+
Inspired by: A-MEM, MemoryLLM, Reflexion.
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# memories-lite — Roadmap
|
|
2
|
+
|
|
3
|
+
## Completed
|
|
4
|
+
|
|
5
|
+
- [x] Semantic Memory Typing & Structuring (factual, episodic, procedural)
|
|
6
|
+
- [x] Implicit Memory Updates via LLM
|
|
7
|
+
- [x] Virtual Sessions/Context Grouping
|
|
8
|
+
- [x] User Isolation (per-user vector store)
|
|
9
|
+
- [x] Memory Decay & Scoring (hybrid: cosine similarity + recency + importance)
|
|
10
|
+
- [x] Memory Recency (half-life per type)
|
|
11
|
+
- [x] Discussion Synthesis (`capture()` with title + summary)
|
|
12
|
+
- [x] ApplyMode system (MEM_ALWAYS / MEM_SMART / MEM_MANUAL)
|
|
13
|
+
- [x] `captureOrUpdate()` with merge support
|
|
14
|
+
|
|
15
|
+
## In Progress
|
|
16
|
+
|
|
17
|
+
- [ ] Tool `saveUserPreference` integration in agentic-server
|
|
18
|
+
|
|
19
|
+
## Planned
|
|
20
|
+
|
|
21
|
+
- [ ] Reflexion Pattern Integration — self-correction loops for memory refinement
|
|
22
|
+
- [ ] Middleware Support — hooks for custom processing pipelines
|
|
23
|
+
|
|
24
|
+
## Deprecated (v1 → v2 migration)
|
|
25
|
+
|
|
26
|
+
### Automatic 4-type extraction (deprecated)
|
|
27
|
+
|
|
28
|
+
The original system automatically classified memories into 4 types via LLM. This produced too many false positives in production. The v2 approach lets the user explicitly control what gets memorized.
|
|
29
|
+
|
|
30
|
+
#### Memory Types (historical reference)
|
|
31
|
+
|
|
32
|
+
1. **Factual Memory** — User preferences, traits, personal information
|
|
33
|
+
- Example: "Aime la cuisine italienne", "Parle couramment l'espagnol"
|
|
34
|
+
|
|
35
|
+
2. **Episodic Memory** — Events associated with time/place
|
|
36
|
+
- Example: "A assisté à un concert à Paris en 2023"
|
|
37
|
+
|
|
38
|
+
3. **Semantic Memory** — Concepts, relations, general knowledge
|
|
39
|
+
- Example: "Le yoga est bénéfique pour la santé mentale"
|
|
40
|
+
|
|
41
|
+
4. **Procedural Memory** — Processes, action sequences
|
|
42
|
+
- Example: "Sait comment préparer un café latte"
|
|
43
|
+
|
|
44
|
+
#### Why deprecated
|
|
45
|
+
|
|
46
|
+
- Too many false positives in type classification
|
|
47
|
+
- Automatic extraction stored irrelevant information
|
|
48
|
+
- Update/merge was fragile (LLM deciding ADD/UPDATE/DELETE)
|
|
49
|
+
- The v2 approach: user controls when to memorize, format is "L'utilisateur [verbe] [context]"
|
|
50
|
+
|
|
51
|
+
### Scoring Coefficients (still active for compatibility)
|
|
52
|
+
|
|
53
|
+
| Type | α (similarity) | β (recency) | γ (importance) |
|
|
54
|
+
|------|---------------|-------------|----------------|
|
|
55
|
+
| procedural | 0.30 | 0.40 | 0.05 |
|
|
56
|
+
| episodic | 0.40 | 0.50 | 0.10 |
|
|
57
|
+
| assistant_preference | 0.60 | 0.05 | 0.35 |
|
|
58
|
+
| discussion | 1.00 | 0.00 | 0.00 |
|
|
59
|
+
|
|
60
|
+
### Half-life per type
|
|
61
|
+
|
|
62
|
+
| Type | HL (days) | λ |
|
|
63
|
+
|------|-----------|---|
|
|
64
|
+
| procedural | 1 | 0.693 |
|
|
65
|
+
| episodic | 7 | 0.099 |
|
|
66
|
+
| assistant_preference | ∞ | 0 |
|
|
67
|
+
| discussion | ∞ | 0 |
|
|
68
|
+
|
|
69
|
+
## Research References
|
|
70
|
+
|
|
71
|
+
- [Zep: Temporal Knowledge Graph for Agent Memory](https://arxiv.org/abs/2501.13956)
|
|
72
|
+
- [A-MEM: Agentic Memory for LLM Agents](https://arxiv.org/abs/2402.12110)
|
|
73
|
+
- [Reflexion: Language Agents with Verbal Reinforcement Learning](https://arxiv.org/abs/2303.11366)
|
|
74
|
+
- [MemoryLLM: Self-Updatable Large Language Models](https://arxiv.org/abs/2402.04624)
|
|
75
|
+
- [OpenAI Context Engineering for Personalization](https://cookbook.openai.com/examples/agents_sdk/context_personalization)
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -31,6 +31,20 @@ export declare class MemoriesLite {
|
|
|
31
31
|
* @param config - Options incluant capturePrompt pour personnaliser la synthèse
|
|
32
32
|
*/
|
|
33
33
|
capture(messages: string | Message[], userId: string, config: AddMemoryOptions): Promise<SearchResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Capture une discussion et crée ou met à jour une mémoire existante (merge)
|
|
36
|
+
*
|
|
37
|
+
* - Sans existingMemoryId → CREATE : extraction d'instructions + nouvelle mémoire
|
|
38
|
+
* - Avec existingMemoryId → UPDATE : charge la mémoire depuis le store, merge les
|
|
39
|
+
* nouvelles instructions avec les existantes, met à jour en conservant le titre
|
|
40
|
+
*
|
|
41
|
+
* existingMemoryId est prioritaire sur existingContent (toujours chargé depuis le store).
|
|
42
|
+
*
|
|
43
|
+
* @param messages - Messages de la discussion ou texte brut
|
|
44
|
+
* @param userId - ID utilisateur
|
|
45
|
+
* @param config - Options (existingMemoryId pour le merge, metadata, capturePrompt override)
|
|
46
|
+
*/
|
|
47
|
+
captureOrUpdate(messages: string | Message[], userId: string, config: AddMemoryOptions): Promise<SearchResult>;
|
|
34
48
|
get(memoryId: string, userId: string): Promise<MemoryItem | null>;
|
|
35
49
|
retrieve(query: string, userId: string, config: SearchMemoryOptions): Promise<SearchResult>;
|
|
36
50
|
/**
|
package/dist/memory/index.js
CHANGED
|
@@ -193,6 +193,133 @@ class MemoriesLite {
|
|
|
193
193
|
results: vectorStoreResult,
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Capture une discussion et crée ou met à jour une mémoire existante (merge)
|
|
198
|
+
*
|
|
199
|
+
* - Sans existingMemoryId → CREATE : extraction d'instructions + nouvelle mémoire
|
|
200
|
+
* - Avec existingMemoryId → UPDATE : charge la mémoire depuis le store, merge les
|
|
201
|
+
* nouvelles instructions avec les existantes, met à jour en conservant le titre
|
|
202
|
+
*
|
|
203
|
+
* existingMemoryId est prioritaire sur existingContent (toujours chargé depuis le store).
|
|
204
|
+
*
|
|
205
|
+
* @param messages - Messages de la discussion ou texte brut
|
|
206
|
+
* @param userId - ID utilisateur
|
|
207
|
+
* @param config - Options (existingMemoryId pour le merge, metadata, capturePrompt override)
|
|
208
|
+
*/
|
|
209
|
+
async captureOrUpdate(messages, userId, config) {
|
|
210
|
+
const { agentId, runId, metadata = {}, filters = {}, capturePrompt, existingMemoryId, existingContent, } = config;
|
|
211
|
+
if (agentId)
|
|
212
|
+
filters.agentId = metadata.agentId = agentId;
|
|
213
|
+
if (runId)
|
|
214
|
+
filters.runId = metadata.runId = runId;
|
|
215
|
+
if (!userId && !filters.agentId && !filters.runId) {
|
|
216
|
+
throw new Error("One of the filters: userId, agentId or runId is required!");
|
|
217
|
+
}
|
|
218
|
+
const parsedMessages = Array.isArray(messages)
|
|
219
|
+
? messages
|
|
220
|
+
: [{ role: "user", content: messages }];
|
|
221
|
+
const final_parsedMessages = await (0, memory_1.parse_vision_messages)(parsedMessages);
|
|
222
|
+
//
|
|
223
|
+
// existingMemoryId est prioritaire : toujours charger depuis le store pour le contenu et le titre
|
|
224
|
+
let resolvedContent = existingContent;
|
|
225
|
+
let existingTitle;
|
|
226
|
+
if (existingMemoryId) {
|
|
227
|
+
const existing = await this.get(existingMemoryId, userId);
|
|
228
|
+
if (existing) {
|
|
229
|
+
resolvedContent = existing.memory;
|
|
230
|
+
existingTitle = existing.metadata?.title;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//
|
|
234
|
+
// Construire le prompt : merge si resolvedContent disponible, sinon capture simple
|
|
235
|
+
const effectivePrompt = capturePrompt || (0, prompts_1.buildCapturePrompt)(resolvedContent);
|
|
236
|
+
const $t = this.$t;
|
|
237
|
+
const vectorStore = await this.getVectorStore(userId);
|
|
238
|
+
//
|
|
239
|
+
// Formater les messages pour la synthèse
|
|
240
|
+
const formattedMessages = final_parsedMessages
|
|
241
|
+
.filter((m) => typeof m.content === 'string')
|
|
242
|
+
.map((m) => `**${m.role.toUpperCase()}**: ${$t(m.content)}`)
|
|
243
|
+
.join("\n\n");
|
|
244
|
+
//
|
|
245
|
+
// Générer la synthèse via LLM
|
|
246
|
+
const [systemPrompt, userPrompt] = (0, prompts_1.getDiscussionSynthesisMessages)(formattedMessages, effectivePrompt);
|
|
247
|
+
const response = await this.llm.generateResponse([
|
|
248
|
+
{ role: "system", content: systemPrompt },
|
|
249
|
+
{ role: "user", content: userPrompt },
|
|
250
|
+
], { ...(0, zod_1.zodResponseFormat)(prompts_1.DiscussionSynthesisSchema, "DiscussionSynthesis") }, [], false);
|
|
251
|
+
//
|
|
252
|
+
// Parser la réponse
|
|
253
|
+
const parsedResponse = (res) => {
|
|
254
|
+
try {
|
|
255
|
+
if (typeof res === 'object')
|
|
256
|
+
return res;
|
|
257
|
+
const cleanResponse = (0, prompts_1.removeCodeBlocks)(res);
|
|
258
|
+
return JSON.parse(cleanResponse);
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
console.error("Failed to parse synthesis from LLM response:", res, e);
|
|
262
|
+
return { title: "Sans titre", summary: "" };
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const { title: llmTitle, summary } = parsedResponse(response);
|
|
266
|
+
//
|
|
267
|
+
// En mode merge, conserver le titre existant (chargé depuis le store ou fourni par metadata)
|
|
268
|
+
const title = (existingMemoryId && (existingTitle || metadata?.title))
|
|
269
|
+
? (existingTitle || metadata.title)
|
|
270
|
+
: llmTitle;
|
|
271
|
+
//
|
|
272
|
+
// Nettoyage du summary : trim + lignes vides
|
|
273
|
+
// L'idempotence est assurée par le prompt (règles d'extraction + merge)
|
|
274
|
+
const filteredSummary = summary
|
|
275
|
+
.split("\n")
|
|
276
|
+
.map((l) => l.trim())
|
|
277
|
+
.filter((l) => l.length > 0)
|
|
278
|
+
.join("\n");
|
|
279
|
+
if (!filteredSummary) {
|
|
280
|
+
console.warn("-- ⚠️ Empty summary from LLM, skipping memory creation/update");
|
|
281
|
+
return { results: [] };
|
|
282
|
+
}
|
|
283
|
+
//
|
|
284
|
+
// Créer l'embedding sur le title
|
|
285
|
+
const embedding = await this.embedder.embed(title);
|
|
286
|
+
//
|
|
287
|
+
// Préparer les métadonnées
|
|
288
|
+
const memoryType = metadata.type || 'discussion';
|
|
289
|
+
const applyMode = metadata.applyMode || types_1.DEFAULT_MEMORY_APPLY_MODE;
|
|
290
|
+
const memoryMetadata = {
|
|
291
|
+
...metadata,
|
|
292
|
+
title,
|
|
293
|
+
type: memoryType,
|
|
294
|
+
applyMode,
|
|
295
|
+
userId,
|
|
296
|
+
};
|
|
297
|
+
//
|
|
298
|
+
// UPDATE si existingMemoryId fourni, sinon CREATE
|
|
299
|
+
if (existingMemoryId) {
|
|
300
|
+
await this.updateMemory(existingMemoryId, filteredSummary, { [filteredSummary]: embedding }, memoryMetadata, userId);
|
|
301
|
+
return {
|
|
302
|
+
results: [{
|
|
303
|
+
id: existingMemoryId,
|
|
304
|
+
memory: filteredSummary,
|
|
305
|
+
type: memoryType,
|
|
306
|
+
metadata: { title, event: "UPDATE" },
|
|
307
|
+
}],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
//
|
|
311
|
+
// CREATE nouvelle mémoire
|
|
312
|
+
const memoryId = await this.createMemory(filteredSummary, { [filteredSummary]: embedding }, memoryMetadata, userId);
|
|
313
|
+
console.log(`-- 🧠 Memory created: "${title}" (${memoryType})`);
|
|
314
|
+
return {
|
|
315
|
+
results: [{
|
|
316
|
+
id: memoryId,
|
|
317
|
+
memory: filteredSummary,
|
|
318
|
+
type: memoryType,
|
|
319
|
+
metadata: { title, event: "ADD" },
|
|
320
|
+
}],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
196
323
|
async get(memoryId, userId) {
|
|
197
324
|
const vectorStore = await this.getVectorStore(userId);
|
|
198
325
|
const memory = await vectorStore.get(memoryId);
|
|
@@ -9,6 +9,8 @@ export interface AddMemoryOptions extends Entity {
|
|
|
9
9
|
metadata?: Record<string, any>;
|
|
10
10
|
filters?: SearchFilters;
|
|
11
11
|
capturePrompt?: string;
|
|
12
|
+
existingMemoryId?: string;
|
|
13
|
+
existingContent?: string;
|
|
12
14
|
}
|
|
13
15
|
export interface SearchMemoryOptions extends Entity {
|
|
14
16
|
limit?: number;
|