gnosisllm-knowledge 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- gnosisllm_knowledge/__init__.py +91 -39
- gnosisllm_knowledge/api/__init__.py +3 -2
- gnosisllm_knowledge/api/knowledge.py +287 -7
- gnosisllm_knowledge/api/memory.py +966 -0
- gnosisllm_knowledge/backends/__init__.py +14 -5
- gnosisllm_knowledge/backends/opensearch/agentic.py +341 -39
- gnosisllm_knowledge/backends/opensearch/config.py +49 -28
- gnosisllm_knowledge/backends/opensearch/indexer.py +1 -0
- gnosisllm_knowledge/backends/opensearch/mappings.py +2 -1
- gnosisllm_knowledge/backends/opensearch/memory/__init__.py +12 -0
- gnosisllm_knowledge/backends/opensearch/memory/client.py +1380 -0
- gnosisllm_knowledge/backends/opensearch/memory/config.py +127 -0
- gnosisllm_knowledge/backends/opensearch/memory/setup.py +322 -0
- gnosisllm_knowledge/backends/opensearch/searcher.py +235 -0
- gnosisllm_knowledge/backends/opensearch/setup.py +308 -148
- gnosisllm_knowledge/cli/app.py +378 -12
- gnosisllm_knowledge/cli/commands/agentic.py +11 -0
- gnosisllm_knowledge/cli/commands/memory.py +723 -0
- gnosisllm_knowledge/cli/commands/setup.py +24 -22
- gnosisllm_knowledge/cli/display/service.py +43 -0
- gnosisllm_knowledge/cli/utils/config.py +58 -0
- gnosisllm_knowledge/core/domain/__init__.py +41 -0
- gnosisllm_knowledge/core/domain/document.py +5 -0
- gnosisllm_knowledge/core/domain/memory.py +440 -0
- gnosisllm_knowledge/core/domain/result.py +11 -3
- gnosisllm_knowledge/core/domain/search.py +2 -0
- gnosisllm_knowledge/core/events/types.py +76 -0
- gnosisllm_knowledge/core/exceptions.py +134 -0
- gnosisllm_knowledge/core/interfaces/__init__.py +17 -0
- gnosisllm_knowledge/core/interfaces/memory.py +524 -0
- gnosisllm_knowledge/core/interfaces/streaming.py +127 -0
- gnosisllm_knowledge/core/streaming/__init__.py +36 -0
- gnosisllm_knowledge/core/streaming/pipeline.py +228 -0
- gnosisllm_knowledge/loaders/base.py +3 -4
- gnosisllm_knowledge/loaders/sitemap.py +129 -1
- gnosisllm_knowledge/loaders/sitemap_streaming.py +258 -0
- gnosisllm_knowledge/services/indexing.py +67 -75
- gnosisllm_knowledge/services/search.py +47 -11
- gnosisllm_knowledge/services/streaming_pipeline.py +302 -0
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.3.0.dist-info}/METADATA +44 -1
- gnosisllm_knowledge-0.3.0.dist-info/RECORD +77 -0
- gnosisllm_knowledge-0.2.0.dist-info/RECORD +0 -64
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.3.0.dist-info}/WHEEL +0 -0
- {gnosisllm_knowledge-0.2.0.dist-info → gnosisllm_knowledge-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
"""High-level Memory API facade."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from gnosisllm_knowledge.backends.opensearch.memory.client import OpenSearchMemoryClient
|
|
10
|
+
from gnosisllm_knowledge.backends.opensearch.memory.config import MemoryConfig
|
|
11
|
+
from gnosisllm_knowledge.core.domain.memory import (
|
|
12
|
+
ContainerConfig,
|
|
13
|
+
ContainerInfo,
|
|
14
|
+
HistoryEntry,
|
|
15
|
+
MemoryEntry,
|
|
16
|
+
MemoryStats,
|
|
17
|
+
MemoryStrategy,
|
|
18
|
+
MemoryType,
|
|
19
|
+
Message,
|
|
20
|
+
Namespace,
|
|
21
|
+
PayloadType,
|
|
22
|
+
RecallResult,
|
|
23
|
+
SessionInfo,
|
|
24
|
+
StoreRequest,
|
|
25
|
+
StoreResult,
|
|
26
|
+
StrategyConfig,
|
|
27
|
+
)
|
|
28
|
+
from gnosisllm_knowledge.core.events.emitter import EventEmitter
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Memory:
|
|
34
|
+
"""High-level facade for Agentic Memory operations.
|
|
35
|
+
|
|
36
|
+
Provides a developer-friendly interface for storing, recalling, and
|
|
37
|
+
managing conversational memories using OpenSearch Agentic Memory.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
```python
|
|
41
|
+
from gnosisllm_knowledge import Memory, MemoryStrategy, StrategyConfig, Message
|
|
42
|
+
|
|
43
|
+
# Quick start from environment
|
|
44
|
+
memory = Memory.from_env()
|
|
45
|
+
|
|
46
|
+
# Create a container with strategy-to-namespace mapping
|
|
47
|
+
container = await memory.create_container(
|
|
48
|
+
name="agent-memory",
|
|
49
|
+
strategies=[
|
|
50
|
+
StrategyConfig(type=MemoryStrategy.SEMANTIC, namespace=["user_id"]),
|
|
51
|
+
StrategyConfig(type=MemoryStrategy.USER_PREFERENCE, namespace=["user_id"]),
|
|
52
|
+
StrategyConfig(type=MemoryStrategy.SUMMARY, namespace=["session_id"]),
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Store conversation
|
|
57
|
+
await memory.store(
|
|
58
|
+
container_id=container.id,
|
|
59
|
+
messages=[
|
|
60
|
+
Message(role="user", content="I'm Sarah Chen, VP of Engineering"),
|
|
61
|
+
Message(role="assistant", content="Hello Sarah!"),
|
|
62
|
+
],
|
|
63
|
+
user_id="sarah-123",
|
|
64
|
+
infer=True,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Recall memories
|
|
68
|
+
result = await memory.recall(
|
|
69
|
+
container_id=container.id,
|
|
70
|
+
query="user information",
|
|
71
|
+
user_id="sarah-123",
|
|
72
|
+
)
|
|
73
|
+
for entry in result.items:
|
|
74
|
+
print(f"[{entry.strategy}] {entry.content}")
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
*,
|
|
81
|
+
client: OpenSearchMemoryClient,
|
|
82
|
+
config: MemoryConfig,
|
|
83
|
+
events: EventEmitter | None = None,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Initialize Memory facade.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
client: OpenSearch memory client.
|
|
89
|
+
config: Memory configuration.
|
|
90
|
+
events: Optional event emitter.
|
|
91
|
+
"""
|
|
92
|
+
self._client = client
|
|
93
|
+
self._config = config
|
|
94
|
+
self._events = events or EventEmitter()
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_opensearch(
|
|
98
|
+
cls,
|
|
99
|
+
host: str = "localhost",
|
|
100
|
+
port: int = 9200,
|
|
101
|
+
*,
|
|
102
|
+
username: str | None = None,
|
|
103
|
+
password: str | None = None,
|
|
104
|
+
use_ssl: bool = False,
|
|
105
|
+
verify_certs: bool = True,
|
|
106
|
+
llm_model_id: str | None = None,
|
|
107
|
+
embedding_model_id: str | None = None,
|
|
108
|
+
llm_result_path: str = "$.choices[0].message.content",
|
|
109
|
+
config: MemoryConfig | None = None,
|
|
110
|
+
**kwargs: Any,
|
|
111
|
+
) -> Memory:
|
|
112
|
+
"""Create Memory instance with OpenSearch backend.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
host: OpenSearch host.
|
|
116
|
+
port: OpenSearch port.
|
|
117
|
+
username: Optional username.
|
|
118
|
+
password: Optional password.
|
|
119
|
+
use_ssl: Use SSL connection.
|
|
120
|
+
verify_certs: Verify SSL certificates.
|
|
121
|
+
llm_model_id: LLM model ID for inference.
|
|
122
|
+
embedding_model_id: Embedding model ID.
|
|
123
|
+
llm_result_path: JSONPath to extract LLM response.
|
|
124
|
+
config: Optional MemoryConfig (overrides other params).
|
|
125
|
+
**kwargs: Additional config options.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Configured Memory instance.
|
|
129
|
+
"""
|
|
130
|
+
if config is None:
|
|
131
|
+
config = MemoryConfig(
|
|
132
|
+
host=host,
|
|
133
|
+
port=port,
|
|
134
|
+
username=username,
|
|
135
|
+
password=password,
|
|
136
|
+
use_ssl=use_ssl,
|
|
137
|
+
verify_certs=verify_certs,
|
|
138
|
+
llm_model_id=llm_model_id,
|
|
139
|
+
embedding_model_id=embedding_model_id,
|
|
140
|
+
llm_result_path=llm_result_path,
|
|
141
|
+
**kwargs,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
client = OpenSearchMemoryClient(config)
|
|
145
|
+
return cls(client=client, config=config)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_env(cls) -> Memory:
|
|
149
|
+
"""Create Memory instance from environment variables.
|
|
150
|
+
|
|
151
|
+
Environment Variables:
|
|
152
|
+
OPENSEARCH_HOST: OpenSearch host (default: localhost)
|
|
153
|
+
OPENSEARCH_PORT: OpenSearch port (default: 9200)
|
|
154
|
+
OPENSEARCH_USERNAME: Username
|
|
155
|
+
OPENSEARCH_PASSWORD: Password
|
|
156
|
+
OPENSEARCH_USE_SSL: Use SSL (default: false)
|
|
157
|
+
OPENSEARCH_VERIFY_CERTS: Verify certs (default: true)
|
|
158
|
+
OPENSEARCH_LLM_MODEL_ID: LLM model ID for inference
|
|
159
|
+
OPENSEARCH_EMBEDDING_MODEL_ID: Embedding model ID
|
|
160
|
+
OPENSEARCH_LLM_RESULT_PATH: JSONPath for LLM response
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Configured Memory instance.
|
|
164
|
+
"""
|
|
165
|
+
config = MemoryConfig.from_env()
|
|
166
|
+
client = OpenSearchMemoryClient(config)
|
|
167
|
+
return cls(client=client, config=config)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def from_config(cls, config: MemoryConfig) -> Memory:
|
|
171
|
+
"""Create Memory instance from configuration.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
config: Memory configuration.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Configured Memory instance.
|
|
178
|
+
"""
|
|
179
|
+
client = OpenSearchMemoryClient(config)
|
|
180
|
+
return cls(client=client, config=config)
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def events(self) -> EventEmitter:
|
|
184
|
+
"""Get the event emitter."""
|
|
185
|
+
return self._events
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def is_configured(self) -> bool:
|
|
189
|
+
"""Check if memory is properly configured for inference.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
True if both LLM and embedding models are configured.
|
|
193
|
+
"""
|
|
194
|
+
return self._config.is_configured
|
|
195
|
+
|
|
196
|
+
# === Container Management ===
|
|
197
|
+
|
|
198
|
+
async def create_container(
|
|
199
|
+
self,
|
|
200
|
+
name: str,
|
|
201
|
+
*,
|
|
202
|
+
strategies: list[StrategyConfig],
|
|
203
|
+
description: str | None = None,
|
|
204
|
+
llm_model_id: str | None = None,
|
|
205
|
+
embedding_model_id: str | None = None,
|
|
206
|
+
embedding_dimension: int = 1536,
|
|
207
|
+
**options: Any,
|
|
208
|
+
) -> ContainerInfo:
|
|
209
|
+
"""Create a new memory container.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
name: Container name.
|
|
213
|
+
strategies: Strategy configurations with namespace scoping (REQUIRED).
|
|
214
|
+
description: Optional description.
|
|
215
|
+
llm_model_id: Override LLM model ID.
|
|
216
|
+
embedding_model_id: Override embedding model ID.
|
|
217
|
+
embedding_dimension: Embedding dimension.
|
|
218
|
+
**options: Additional options.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Created container info.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
ValueError: If strategies is empty.
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
```python
|
|
228
|
+
container = await memory.create_container(
|
|
229
|
+
name="agent-memory",
|
|
230
|
+
strategies=[
|
|
231
|
+
StrategyConfig(type=MemoryStrategy.SEMANTIC, namespace=["user_id"]),
|
|
232
|
+
StrategyConfig(type=MemoryStrategy.SUMMARY, namespace=["session_id"]),
|
|
233
|
+
],
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
"""
|
|
237
|
+
if not strategies:
|
|
238
|
+
raise ValueError(
|
|
239
|
+
"strategies is required - each strategy must be scoped to namespace fields"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
container_config = ContainerConfig(
|
|
243
|
+
name=name,
|
|
244
|
+
description=description,
|
|
245
|
+
strategies=strategies,
|
|
246
|
+
llm_model_id=llm_model_id or self._config.llm_model_id,
|
|
247
|
+
embedding_model_id=embedding_model_id or self._config.embedding_model_id,
|
|
248
|
+
embedding_dimension=embedding_dimension,
|
|
249
|
+
llm_result_path=self._config.llm_result_path,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
return await self._client.create_container(container_config, **options)
|
|
253
|
+
|
|
254
|
+
async def get_container(self, container_id: str) -> ContainerInfo | None:
|
|
255
|
+
"""Get container by ID.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
container_id: Container ID.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Container info or None if not found.
|
|
262
|
+
"""
|
|
263
|
+
return await self._client.get_container(container_id)
|
|
264
|
+
|
|
265
|
+
async def list_containers(self, limit: int = 100) -> list[ContainerInfo]:
|
|
266
|
+
"""List all containers.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
limit: Maximum number of containers to return.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
List of container info.
|
|
273
|
+
"""
|
|
274
|
+
return await self._client.list_containers(limit)
|
|
275
|
+
|
|
276
|
+
async def delete_container(self, container_id: str) -> bool:
|
|
277
|
+
"""Delete a container.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
container_id: Container ID.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
True if deleted, False if not found.
|
|
284
|
+
"""
|
|
285
|
+
return await self._client.delete_container(container_id)
|
|
286
|
+
|
|
287
|
+
async def update_container(
|
|
288
|
+
self,
|
|
289
|
+
container_id: str,
|
|
290
|
+
*,
|
|
291
|
+
description: str | None = None,
|
|
292
|
+
strategies: list[StrategyConfig] | None = None,
|
|
293
|
+
**options: Any,
|
|
294
|
+
) -> ContainerInfo:
|
|
295
|
+
"""Update a container.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
container_id: Container ID.
|
|
299
|
+
description: Updated description.
|
|
300
|
+
strategies: Updated strategy configurations.
|
|
301
|
+
**options: Additional options.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Updated container info.
|
|
305
|
+
"""
|
|
306
|
+
config = ContainerConfig(
|
|
307
|
+
name="", # Not updated
|
|
308
|
+
description=description,
|
|
309
|
+
strategies=strategies or [],
|
|
310
|
+
)
|
|
311
|
+
return await self._client.update_container(container_id, config, **options)
|
|
312
|
+
|
|
313
|
+
# === Memory Storage ===
|
|
314
|
+
|
|
315
|
+
async def store(
|
|
316
|
+
self,
|
|
317
|
+
container_id: str,
|
|
318
|
+
messages: list[Message] | None = None,
|
|
319
|
+
*,
|
|
320
|
+
structured_data: dict[str, Any] | None = None,
|
|
321
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
322
|
+
user_id: str | None = None,
|
|
323
|
+
session_id: str | None = None,
|
|
324
|
+
agent_id: str | None = None,
|
|
325
|
+
infer: bool = True,
|
|
326
|
+
metadata: dict[str, Any] | None = None,
|
|
327
|
+
tags: dict[str, str] | None = None,
|
|
328
|
+
**options: Any,
|
|
329
|
+
) -> StoreResult:
|
|
330
|
+
"""Store conversation or data with optional LLM inference.
|
|
331
|
+
|
|
332
|
+
Which strategies run is determined by:
|
|
333
|
+
1. How the container was configured (strategy -> namespace field mapping)
|
|
334
|
+
2. Which namespace fields are present in the request
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
container_id: Target container ID.
|
|
338
|
+
messages: Conversation messages.
|
|
339
|
+
structured_data: Structured data (alternative to messages).
|
|
340
|
+
namespace: Full namespace or dict.
|
|
341
|
+
user_id: Shorthand for namespace user_id.
|
|
342
|
+
session_id: Shorthand for namespace session_id.
|
|
343
|
+
agent_id: Shorthand for namespace agent_id.
|
|
344
|
+
infer: Enable LLM inference for fact extraction.
|
|
345
|
+
metadata: Custom metadata.
|
|
346
|
+
tags: Custom tags.
|
|
347
|
+
**options: Additional options.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Store result with IDs and counts.
|
|
351
|
+
|
|
352
|
+
Example:
|
|
353
|
+
```python
|
|
354
|
+
# Store with namespace shorthand
|
|
355
|
+
await memory.store(
|
|
356
|
+
container_id=container.id,
|
|
357
|
+
messages=[
|
|
358
|
+
Message(role="user", content="I prefer dark mode"),
|
|
359
|
+
Message(role="assistant", content="Dark mode enabled!"),
|
|
360
|
+
],
|
|
361
|
+
user_id="alice-123",
|
|
362
|
+
infer=True,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Store with full namespace
|
|
366
|
+
await memory.store(
|
|
367
|
+
container_id=container.id,
|
|
368
|
+
messages=messages,
|
|
369
|
+
namespace={"user_id": "alice", "org_id": "acme"},
|
|
370
|
+
)
|
|
371
|
+
```
|
|
372
|
+
"""
|
|
373
|
+
# Build namespace
|
|
374
|
+
if isinstance(namespace, dict):
|
|
375
|
+
ns = Namespace(namespace)
|
|
376
|
+
elif namespace is None:
|
|
377
|
+
ns = Namespace()
|
|
378
|
+
else:
|
|
379
|
+
ns = namespace
|
|
380
|
+
|
|
381
|
+
# Add shorthand values
|
|
382
|
+
if user_id:
|
|
383
|
+
ns["user_id"] = user_id
|
|
384
|
+
if session_id:
|
|
385
|
+
ns["session_id"] = session_id
|
|
386
|
+
if agent_id:
|
|
387
|
+
ns["agent_id"] = agent_id
|
|
388
|
+
|
|
389
|
+
# Determine payload type
|
|
390
|
+
payload_type = PayloadType.CONVERSATIONAL if messages else PayloadType.DATA
|
|
391
|
+
|
|
392
|
+
request = StoreRequest(
|
|
393
|
+
messages=messages,
|
|
394
|
+
structured_data=structured_data,
|
|
395
|
+
namespace=ns,
|
|
396
|
+
payload_type=payload_type,
|
|
397
|
+
infer=infer,
|
|
398
|
+
metadata=metadata or {},
|
|
399
|
+
tags=tags or {},
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return await self._client.store(container_id, request, **options)
|
|
403
|
+
|
|
404
|
+
# === Memory Recall ===
|
|
405
|
+
|
|
406
|
+
async def recall(
|
|
407
|
+
self,
|
|
408
|
+
container_id: str,
|
|
409
|
+
query: str,
|
|
410
|
+
*,
|
|
411
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
412
|
+
user_id: str | None = None,
|
|
413
|
+
session_id: str | None = None,
|
|
414
|
+
agent_id: str | None = None,
|
|
415
|
+
strategies: list[MemoryStrategy] | None = None,
|
|
416
|
+
min_score: float | None = None,
|
|
417
|
+
limit: int = 10,
|
|
418
|
+
after: datetime | None = None,
|
|
419
|
+
before: datetime | None = None,
|
|
420
|
+
**options: Any,
|
|
421
|
+
) -> RecallResult:
|
|
422
|
+
"""Semantic search over long-term memories.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
container_id: Container ID.
|
|
426
|
+
query: Search query text.
|
|
427
|
+
namespace: Full namespace or dict.
|
|
428
|
+
user_id: Shorthand for namespace user_id.
|
|
429
|
+
session_id: Shorthand for namespace session_id.
|
|
430
|
+
agent_id: Shorthand for namespace agent_id.
|
|
431
|
+
strategies: Filter by specific strategies.
|
|
432
|
+
min_score: Minimum similarity score.
|
|
433
|
+
limit: Maximum results.
|
|
434
|
+
after: Filter by created after timestamp.
|
|
435
|
+
before: Filter by created before timestamp.
|
|
436
|
+
**options: Additional options.
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
Recall result with memory entries.
|
|
440
|
+
|
|
441
|
+
Example:
|
|
442
|
+
```python
|
|
443
|
+
result = await memory.recall(
|
|
444
|
+
container_id=container.id,
|
|
445
|
+
query="user preferences",
|
|
446
|
+
user_id="alice-123",
|
|
447
|
+
limit=5,
|
|
448
|
+
)
|
|
449
|
+
for entry in result.items:
|
|
450
|
+
print(f"{entry.content} (score: {entry.score})")
|
|
451
|
+
```
|
|
452
|
+
"""
|
|
453
|
+
# Build namespace
|
|
454
|
+
if isinstance(namespace, dict):
|
|
455
|
+
ns = Namespace(namespace)
|
|
456
|
+
elif namespace is None:
|
|
457
|
+
ns = Namespace()
|
|
458
|
+
else:
|
|
459
|
+
ns = namespace
|
|
460
|
+
|
|
461
|
+
if user_id:
|
|
462
|
+
ns["user_id"] = user_id
|
|
463
|
+
if session_id:
|
|
464
|
+
ns["session_id"] = session_id
|
|
465
|
+
if agent_id:
|
|
466
|
+
ns["agent_id"] = agent_id
|
|
467
|
+
|
|
468
|
+
return await self._client.recall(
|
|
469
|
+
container_id=container_id,
|
|
470
|
+
query=query,
|
|
471
|
+
namespace=ns if ns.values else None,
|
|
472
|
+
strategies=strategies,
|
|
473
|
+
min_score=min_score,
|
|
474
|
+
limit=limit,
|
|
475
|
+
after=after,
|
|
476
|
+
before=before,
|
|
477
|
+
**options,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
async def get_memory(
|
|
481
|
+
self,
|
|
482
|
+
container_id: str,
|
|
483
|
+
memory_id: str,
|
|
484
|
+
memory_type: MemoryType,
|
|
485
|
+
**options: Any,
|
|
486
|
+
) -> MemoryEntry | None:
|
|
487
|
+
"""Get a specific memory by ID.
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
container_id: Container ID.
|
|
491
|
+
memory_id: Memory document ID.
|
|
492
|
+
memory_type: Memory type (WORKING or LONG_TERM).
|
|
493
|
+
**options: Additional options.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
Memory entry or None if not found.
|
|
497
|
+
"""
|
|
498
|
+
return await self._client.get_memory(
|
|
499
|
+
container_id=container_id,
|
|
500
|
+
memory_id=memory_id,
|
|
501
|
+
memory_type=memory_type,
|
|
502
|
+
**options,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
async def delete_memory(
|
|
506
|
+
self,
|
|
507
|
+
container_id: str,
|
|
508
|
+
memory_id: str,
|
|
509
|
+
memory_type: MemoryType,
|
|
510
|
+
**options: Any,
|
|
511
|
+
) -> bool:
|
|
512
|
+
"""Delete a specific memory by ID.
|
|
513
|
+
|
|
514
|
+
Args:
|
|
515
|
+
container_id: Container ID.
|
|
516
|
+
memory_id: Memory document ID.
|
|
517
|
+
memory_type: Memory type (WORKING or LONG_TERM).
|
|
518
|
+
**options: Additional options.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
True if deleted, False if not found.
|
|
522
|
+
"""
|
|
523
|
+
return await self._client.delete_memory(
|
|
524
|
+
container_id=container_id,
|
|
525
|
+
memory_id=memory_id,
|
|
526
|
+
memory_type=memory_type,
|
|
527
|
+
**options,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
async def update_memory(
|
|
531
|
+
self,
|
|
532
|
+
container_id: str,
|
|
533
|
+
memory_id: str,
|
|
534
|
+
memory_type: MemoryType,
|
|
535
|
+
*,
|
|
536
|
+
memory: str | None = None,
|
|
537
|
+
tags: dict[str, str] | None = None,
|
|
538
|
+
**options: Any,
|
|
539
|
+
) -> MemoryEntry:
|
|
540
|
+
"""Update a specific memory.
|
|
541
|
+
|
|
542
|
+
Note: History memory type does NOT support updates.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
container_id: Container ID.
|
|
546
|
+
memory_id: Memory document ID.
|
|
547
|
+
memory_type: Memory type (working, long-term, sessions).
|
|
548
|
+
memory: Updated memory content (for long-term).
|
|
549
|
+
tags: Updated tags.
|
|
550
|
+
**options: Additional options.
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
Updated memory entry.
|
|
554
|
+
"""
|
|
555
|
+
return await self._client.update_memory(
|
|
556
|
+
container_id=container_id,
|
|
557
|
+
memory_id=memory_id,
|
|
558
|
+
memory_type=memory_type,
|
|
559
|
+
memory=memory,
|
|
560
|
+
tags=tags,
|
|
561
|
+
**options,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
async def clear_working_memory(
|
|
565
|
+
self,
|
|
566
|
+
container_id: str,
|
|
567
|
+
session_id: str | None = None,
|
|
568
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
569
|
+
user_id: str | None = None,
|
|
570
|
+
**options: Any,
|
|
571
|
+
) -> int:
|
|
572
|
+
"""Clear working memory.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
container_id: Container ID.
|
|
576
|
+
session_id: Optional session filter.
|
|
577
|
+
namespace: Full namespace or dict.
|
|
578
|
+
user_id: Shorthand for namespace user_id.
|
|
579
|
+
**options: Additional options.
|
|
580
|
+
|
|
581
|
+
Returns:
|
|
582
|
+
Number of messages deleted.
|
|
583
|
+
"""
|
|
584
|
+
if isinstance(namespace, dict):
|
|
585
|
+
ns = Namespace(namespace)
|
|
586
|
+
elif namespace is None:
|
|
587
|
+
ns = Namespace()
|
|
588
|
+
else:
|
|
589
|
+
ns = namespace
|
|
590
|
+
|
|
591
|
+
if user_id:
|
|
592
|
+
ns["user_id"] = user_id
|
|
593
|
+
|
|
594
|
+
return await self._client.clear_working_memory(
|
|
595
|
+
container_id=container_id,
|
|
596
|
+
session_id=session_id,
|
|
597
|
+
namespace=ns if ns.values else None,
|
|
598
|
+
**options,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
async def delete_memories(
|
|
602
|
+
self,
|
|
603
|
+
container_id: str,
|
|
604
|
+
session_id: str | None = None,
|
|
605
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
606
|
+
user_id: str | None = None,
|
|
607
|
+
before: datetime | None = None,
|
|
608
|
+
memory_type: MemoryType = MemoryType.WORKING,
|
|
609
|
+
**options: Any,
|
|
610
|
+
) -> int:
|
|
611
|
+
"""Delete memories by filter.
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
container_id: Container ID.
|
|
615
|
+
session_id: Optional session filter.
|
|
616
|
+
namespace: Full namespace or dict.
|
|
617
|
+
user_id: Shorthand for namespace user_id.
|
|
618
|
+
before: Delete memories created before this timestamp.
|
|
619
|
+
memory_type: Memory type to delete (default: WORKING).
|
|
620
|
+
**options: Additional options.
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
Number of memories deleted.
|
|
624
|
+
"""
|
|
625
|
+
if isinstance(namespace, dict):
|
|
626
|
+
ns = Namespace(namespace)
|
|
627
|
+
elif namespace is None:
|
|
628
|
+
ns = Namespace()
|
|
629
|
+
else:
|
|
630
|
+
ns = namespace
|
|
631
|
+
|
|
632
|
+
if user_id:
|
|
633
|
+
ns["user_id"] = user_id
|
|
634
|
+
|
|
635
|
+
return await self._client.delete_memories(
|
|
636
|
+
container_id=container_id,
|
|
637
|
+
session_id=session_id,
|
|
638
|
+
namespace=ns if ns.values else None,
|
|
639
|
+
before=before,
|
|
640
|
+
memory_type=memory_type,
|
|
641
|
+
**options,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
async def delete_by_query(
|
|
645
|
+
self,
|
|
646
|
+
container_id: str,
|
|
647
|
+
memory_type: MemoryType,
|
|
648
|
+
query: dict[str, Any],
|
|
649
|
+
**options: Any,
|
|
650
|
+
) -> int:
|
|
651
|
+
"""Delete memories matching an OpenSearch Query DSL query.
|
|
652
|
+
|
|
653
|
+
Provides full flexibility for complex deletion criteria.
|
|
654
|
+
|
|
655
|
+
Args:
|
|
656
|
+
container_id: Container ID.
|
|
657
|
+
memory_type: Memory type to delete from.
|
|
658
|
+
query: OpenSearch Query DSL query.
|
|
659
|
+
**options: Additional options.
|
|
660
|
+
|
|
661
|
+
Returns:
|
|
662
|
+
Number of documents deleted.
|
|
663
|
+
|
|
664
|
+
Example:
|
|
665
|
+
```python
|
|
666
|
+
# Delete all memories for a user older than 30 days
|
|
667
|
+
await memory.delete_by_query(
|
|
668
|
+
container_id=container_id,
|
|
669
|
+
memory_type=MemoryType.WORKING,
|
|
670
|
+
query={
|
|
671
|
+
"bool": {
|
|
672
|
+
"must": [
|
|
673
|
+
{"term": {"namespace.user_id": "user123"}},
|
|
674
|
+
{"range": {"created_time": {"lt": "now-30d"}}}
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
)
|
|
679
|
+
```
|
|
680
|
+
"""
|
|
681
|
+
return await self._client.delete_by_query(
|
|
682
|
+
container_id=container_id,
|
|
683
|
+
memory_type=memory_type,
|
|
684
|
+
query=query,
|
|
685
|
+
**options,
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
# === Working Memory ===
|
|
689
|
+
|
|
690
|
+
async def get_working_memory(
|
|
691
|
+
self,
|
|
692
|
+
container_id: str,
|
|
693
|
+
session_id: str | None = None,
|
|
694
|
+
*,
|
|
695
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
696
|
+
limit: int = 50,
|
|
697
|
+
offset: int = 0,
|
|
698
|
+
**options: Any,
|
|
699
|
+
) -> list[Message]:
|
|
700
|
+
"""Get working memory messages.
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
container_id: Container ID.
|
|
704
|
+
session_id: Optional session filter.
|
|
705
|
+
namespace: Optional namespace filter.
|
|
706
|
+
limit: Maximum messages to return.
|
|
707
|
+
offset: Number of messages to skip.
|
|
708
|
+
**options: Additional options.
|
|
709
|
+
|
|
710
|
+
Returns:
|
|
711
|
+
List of messages.
|
|
712
|
+
"""
|
|
713
|
+
ns = Namespace(namespace) if isinstance(namespace, dict) else namespace
|
|
714
|
+
|
|
715
|
+
return await self._client.get_working_memory(
|
|
716
|
+
container_id=container_id,
|
|
717
|
+
session_id=session_id,
|
|
718
|
+
namespace=ns,
|
|
719
|
+
limit=limit,
|
|
720
|
+
offset=offset,
|
|
721
|
+
**options,
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
# === Session Management ===
|
|
725
|
+
|
|
726
|
+
async def create_session(
|
|
727
|
+
self,
|
|
728
|
+
container_id: str,
|
|
729
|
+
*,
|
|
730
|
+
session_id: str | None = None,
|
|
731
|
+
summary: str | None = None,
|
|
732
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
733
|
+
user_id: str | None = None,
|
|
734
|
+
agent_id: str | None = None,
|
|
735
|
+
metadata: dict[str, Any] | None = None,
|
|
736
|
+
**options: Any,
|
|
737
|
+
) -> SessionInfo:
|
|
738
|
+
"""Create a new session.
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
container_id: Container ID.
|
|
742
|
+
session_id: Custom session ID (auto-generated if not provided).
|
|
743
|
+
summary: Session summary text.
|
|
744
|
+
namespace: Full namespace or dict.
|
|
745
|
+
user_id: Shorthand for namespace user_id.
|
|
746
|
+
agent_id: Shorthand for namespace agent_id.
|
|
747
|
+
metadata: Custom metadata.
|
|
748
|
+
**options: Additional options.
|
|
749
|
+
|
|
750
|
+
Returns:
|
|
751
|
+
Created session info.
|
|
752
|
+
"""
|
|
753
|
+
if isinstance(namespace, dict):
|
|
754
|
+
ns = Namespace(namespace)
|
|
755
|
+
elif namespace is None:
|
|
756
|
+
ns = Namespace()
|
|
757
|
+
else:
|
|
758
|
+
ns = namespace
|
|
759
|
+
|
|
760
|
+
if user_id:
|
|
761
|
+
ns["user_id"] = user_id
|
|
762
|
+
if agent_id:
|
|
763
|
+
ns["agent_id"] = agent_id
|
|
764
|
+
|
|
765
|
+
return await self._client.create_session(
|
|
766
|
+
container_id=container_id,
|
|
767
|
+
session_id=session_id,
|
|
768
|
+
summary=summary,
|
|
769
|
+
namespace=ns if ns.values else None,
|
|
770
|
+
metadata=metadata,
|
|
771
|
+
**options,
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
async def get_session(
|
|
775
|
+
self,
|
|
776
|
+
container_id: str,
|
|
777
|
+
session_id: str,
|
|
778
|
+
*,
|
|
779
|
+
include_messages: bool = False,
|
|
780
|
+
message_limit: int = 50,
|
|
781
|
+
**options: Any,
|
|
782
|
+
) -> SessionInfo | None:
|
|
783
|
+
"""Get session by ID.
|
|
784
|
+
|
|
785
|
+
Args:
|
|
786
|
+
container_id: Container ID.
|
|
787
|
+
session_id: Session ID.
|
|
788
|
+
include_messages: Include session messages.
|
|
789
|
+
message_limit: Max messages to include.
|
|
790
|
+
**options: Additional options.
|
|
791
|
+
|
|
792
|
+
Returns:
|
|
793
|
+
Session info or None if not found.
|
|
794
|
+
"""
|
|
795
|
+
return await self._client.get_session(
|
|
796
|
+
container_id=container_id,
|
|
797
|
+
session_id=session_id,
|
|
798
|
+
include_messages=include_messages,
|
|
799
|
+
message_limit=message_limit,
|
|
800
|
+
**options,
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
async def list_sessions(
|
|
804
|
+
self,
|
|
805
|
+
container_id: str,
|
|
806
|
+
*,
|
|
807
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
808
|
+
user_id: str | None = None,
|
|
809
|
+
limit: int = 100,
|
|
810
|
+
**options: Any,
|
|
811
|
+
) -> list[SessionInfo]:
|
|
812
|
+
"""List sessions.
|
|
813
|
+
|
|
814
|
+
Args:
|
|
815
|
+
container_id: Container ID.
|
|
816
|
+
namespace: Full namespace or dict.
|
|
817
|
+
user_id: Shorthand for namespace user_id.
|
|
818
|
+
limit: Maximum sessions to return.
|
|
819
|
+
**options: Additional options.
|
|
820
|
+
|
|
821
|
+
Returns:
|
|
822
|
+
List of session info.
|
|
823
|
+
"""
|
|
824
|
+
if isinstance(namespace, dict):
|
|
825
|
+
ns = Namespace(namespace)
|
|
826
|
+
elif namespace is None:
|
|
827
|
+
ns = Namespace()
|
|
828
|
+
else:
|
|
829
|
+
ns = namespace
|
|
830
|
+
|
|
831
|
+
if user_id:
|
|
832
|
+
ns["user_id"] = user_id
|
|
833
|
+
|
|
834
|
+
return await self._client.list_sessions(
|
|
835
|
+
container_id=container_id,
|
|
836
|
+
namespace=ns if ns.values else None,
|
|
837
|
+
limit=limit,
|
|
838
|
+
**options,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
async def update_session(
|
|
842
|
+
self,
|
|
843
|
+
container_id: str,
|
|
844
|
+
session_id: str,
|
|
845
|
+
*,
|
|
846
|
+
summary: str | None = None,
|
|
847
|
+
metadata: dict[str, Any] | None = None,
|
|
848
|
+
**options: Any,
|
|
849
|
+
) -> SessionInfo:
|
|
850
|
+
"""Update a session.
|
|
851
|
+
|
|
852
|
+
Args:
|
|
853
|
+
container_id: Container ID.
|
|
854
|
+
session_id: Session ID.
|
|
855
|
+
summary: Updated summary text.
|
|
856
|
+
metadata: Updated metadata.
|
|
857
|
+
**options: Additional options.
|
|
858
|
+
|
|
859
|
+
Returns:
|
|
860
|
+
Updated session info.
|
|
861
|
+
"""
|
|
862
|
+
return await self._client.update_session(
|
|
863
|
+
container_id=container_id,
|
|
864
|
+
session_id=session_id,
|
|
865
|
+
summary=summary,
|
|
866
|
+
metadata=metadata,
|
|
867
|
+
**options,
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
async def delete_session(
|
|
871
|
+
self,
|
|
872
|
+
container_id: str,
|
|
873
|
+
session_id: str,
|
|
874
|
+
**options: Any,
|
|
875
|
+
) -> bool:
|
|
876
|
+
"""Delete a session.
|
|
877
|
+
|
|
878
|
+
Args:
|
|
879
|
+
container_id: Container ID.
|
|
880
|
+
session_id: Session ID.
|
|
881
|
+
**options: Additional options.
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
True if deleted, False if not found.
|
|
885
|
+
"""
|
|
886
|
+
return await self._client.delete_session(
|
|
887
|
+
container_id=container_id,
|
|
888
|
+
session_id=session_id,
|
|
889
|
+
**options,
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
# === History (Audit Trail) ===
|
|
893
|
+
|
|
894
|
+
async def get_history_entry(
|
|
895
|
+
self,
|
|
896
|
+
container_id: str,
|
|
897
|
+
history_id: str,
|
|
898
|
+
**options: Any,
|
|
899
|
+
) -> HistoryEntry | None:
|
|
900
|
+
"""Get a specific history entry by ID.
|
|
901
|
+
|
|
902
|
+
History entries are READ-ONLY audit trail records.
|
|
903
|
+
|
|
904
|
+
Args:
|
|
905
|
+
container_id: Container ID.
|
|
906
|
+
history_id: History entry ID.
|
|
907
|
+
**options: Additional options.
|
|
908
|
+
|
|
909
|
+
Returns:
|
|
910
|
+
History entry or None if not found.
|
|
911
|
+
"""
|
|
912
|
+
return await self._client.get_history_entry(
|
|
913
|
+
container_id=container_id,
|
|
914
|
+
history_id=history_id,
|
|
915
|
+
**options,
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
async def list_history(
|
|
919
|
+
self,
|
|
920
|
+
container_id: str,
|
|
921
|
+
memory_id: str | None = None,
|
|
922
|
+
namespace: Namespace | dict[str, str] | None = None,
|
|
923
|
+
limit: int = 100,
|
|
924
|
+
**options: Any,
|
|
925
|
+
) -> list[HistoryEntry]:
|
|
926
|
+
"""List history entries.
|
|
927
|
+
|
|
928
|
+
Args:
|
|
929
|
+
container_id: Container ID.
|
|
930
|
+
memory_id: Filter by specific memory ID.
|
|
931
|
+
namespace: Optional namespace filter.
|
|
932
|
+
limit: Maximum entries to return.
|
|
933
|
+
**options: Additional options.
|
|
934
|
+
|
|
935
|
+
Returns:
|
|
936
|
+
List of history entries (most recent first).
|
|
937
|
+
"""
|
|
938
|
+
ns = Namespace(namespace) if isinstance(namespace, dict) else namespace
|
|
939
|
+
|
|
940
|
+
return await self._client.list_history(
|
|
941
|
+
container_id=container_id,
|
|
942
|
+
memory_id=memory_id,
|
|
943
|
+
namespace=ns,
|
|
944
|
+
limit=limit,
|
|
945
|
+
**options,
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
# === Statistics ===
|
|
949
|
+
|
|
950
|
+
async def get_stats(self, container_id: str) -> MemoryStats:
|
|
951
|
+
"""Get container statistics.
|
|
952
|
+
|
|
953
|
+
Args:
|
|
954
|
+
container_id: Container ID.
|
|
955
|
+
|
|
956
|
+
Returns:
|
|
957
|
+
Memory statistics for the container.
|
|
958
|
+
"""
|
|
959
|
+
return await self._client.get_stats(container_id)
|
|
960
|
+
|
|
961
|
+
# === Cleanup ===
|
|
962
|
+
|
|
963
|
+
async def close(self) -> None:
|
|
964
|
+
"""Close connections and clean up resources."""
|
|
965
|
+
# Currently no resources to clean up, but method is here for future use
|
|
966
|
+
pass
|