chuk-ai-session-manager 0.7.1__py3-none-any.whl → 0.8__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.
- chuk_ai_session_manager/__init__.py +84 -40
- chuk_ai_session_manager/api/__init__.py +1 -1
- chuk_ai_session_manager/api/simple_api.py +53 -59
- chuk_ai_session_manager/exceptions.py +31 -17
- chuk_ai_session_manager/guards/__init__.py +118 -0
- chuk_ai_session_manager/guards/bindings.py +217 -0
- chuk_ai_session_manager/guards/cache.py +163 -0
- chuk_ai_session_manager/guards/manager.py +819 -0
- chuk_ai_session_manager/guards/models.py +498 -0
- chuk_ai_session_manager/guards/ungrounded.py +159 -0
- chuk_ai_session_manager/infinite_conversation.py +86 -79
- chuk_ai_session_manager/memory/__init__.py +247 -0
- chuk_ai_session_manager/memory/artifacts_bridge.py +469 -0
- chuk_ai_session_manager/memory/context_packer.py +347 -0
- chuk_ai_session_manager/memory/fault_handler.py +507 -0
- chuk_ai_session_manager/memory/manifest.py +307 -0
- chuk_ai_session_manager/memory/models.py +1084 -0
- chuk_ai_session_manager/memory/mutation_log.py +186 -0
- chuk_ai_session_manager/memory/pack_cache.py +206 -0
- chuk_ai_session_manager/memory/page_table.py +275 -0
- chuk_ai_session_manager/memory/prefetcher.py +192 -0
- chuk_ai_session_manager/memory/tlb.py +247 -0
- chuk_ai_session_manager/memory/vm_prompts.py +238 -0
- chuk_ai_session_manager/memory/working_set.py +574 -0
- chuk_ai_session_manager/models/__init__.py +21 -9
- chuk_ai_session_manager/models/event_source.py +3 -1
- chuk_ai_session_manager/models/event_type.py +10 -1
- chuk_ai_session_manager/models/session.py +103 -68
- chuk_ai_session_manager/models/session_event.py +69 -68
- chuk_ai_session_manager/models/session_metadata.py +9 -10
- chuk_ai_session_manager/models/session_run.py +21 -22
- chuk_ai_session_manager/models/token_usage.py +76 -76
- chuk_ai_session_manager/procedural_memory/__init__.py +70 -0
- chuk_ai_session_manager/procedural_memory/formatter.py +407 -0
- chuk_ai_session_manager/procedural_memory/manager.py +523 -0
- chuk_ai_session_manager/procedural_memory/models.py +371 -0
- chuk_ai_session_manager/sample_tools.py +79 -46
- chuk_ai_session_manager/session_aware_tool_processor.py +27 -16
- chuk_ai_session_manager/session_manager.py +238 -197
- chuk_ai_session_manager/session_prompt_builder.py +163 -111
- chuk_ai_session_manager/session_storage.py +45 -52
- {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/METADATA +79 -3
- chuk_ai_session_manager-0.8.dist-info/RECORD +45 -0
- {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/WHEEL +1 -1
- chuk_ai_session_manager-0.7.1.dist-info/RECORD +0 -22
- {chuk_ai_session_manager-0.7.1.dist-info → chuk_ai_session_manager-0.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# chuk_ai_session_manager/memory/artifacts_bridge.py
|
|
2
|
+
"""
|
|
3
|
+
Artifacts Bridge for AI Virtual Memory.
|
|
4
|
+
|
|
5
|
+
The ArtifactsBridge integrates with chuk-artifacts for persistent page storage.
|
|
6
|
+
It handles storing/loading pages to/from the L3 and L4 tiers.
|
|
7
|
+
|
|
8
|
+
When chuk-artifacts is not installed, falls back to in-memory storage.
|
|
9
|
+
|
|
10
|
+
Design principles:
|
|
11
|
+
- Async-native: All I/O operations are async
|
|
12
|
+
- Optional dependency: Works without chuk-artifacts
|
|
13
|
+
- Pydantic-native: All models are BaseModel subclasses
|
|
14
|
+
- No magic strings: Uses constants for MIME types
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import Any, Dict, List, Optional, Protocol
|
|
19
|
+
|
|
20
|
+
from pydantic import BaseModel, Field
|
|
21
|
+
|
|
22
|
+
from .models import (
|
|
23
|
+
MEMORY_PAGE_MIME_TYPE,
|
|
24
|
+
VM_CHECKPOINT_MIME_TYPE,
|
|
25
|
+
MemoryPage,
|
|
26
|
+
StorageStats,
|
|
27
|
+
StorageTier,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Try to import chuk-artifacts
|
|
31
|
+
try:
|
|
32
|
+
from chuk_artifacts import ArtifactStore
|
|
33
|
+
from chuk_artifacts.models import StorageScope
|
|
34
|
+
|
|
35
|
+
ARTIFACTS_AVAILABLE = True
|
|
36
|
+
except ImportError:
|
|
37
|
+
ARTIFACTS_AVAILABLE = False
|
|
38
|
+
ArtifactStore = None # type: ignore
|
|
39
|
+
StorageScope = None # type: ignore
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class StorageBackend(Protocol):
|
|
43
|
+
"""Protocol for page storage backends."""
|
|
44
|
+
|
|
45
|
+
async def store(
|
|
46
|
+
self,
|
|
47
|
+
page: MemoryPage,
|
|
48
|
+
tier: StorageTier,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Store a page and return its artifact_id."""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
async def load(
|
|
54
|
+
self,
|
|
55
|
+
artifact_id: str,
|
|
56
|
+
) -> Optional[MemoryPage]:
|
|
57
|
+
"""Load a page by artifact_id."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
async def delete(
|
|
61
|
+
self,
|
|
62
|
+
artifact_id: str,
|
|
63
|
+
) -> bool:
|
|
64
|
+
"""Delete a page by artifact_id."""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PageMetadata(BaseModel):
|
|
69
|
+
"""Metadata stored with a page in artifacts."""
|
|
70
|
+
|
|
71
|
+
page_id: str
|
|
72
|
+
modality: str
|
|
73
|
+
compression_level: int
|
|
74
|
+
tier: str
|
|
75
|
+
session_id: Optional[str] = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CheckpointMetadata(BaseModel):
|
|
79
|
+
"""Metadata stored with a checkpoint."""
|
|
80
|
+
|
|
81
|
+
checkpoint_name: str
|
|
82
|
+
page_count: int
|
|
83
|
+
session_id: Optional[str] = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CheckpointEntry(BaseModel):
|
|
87
|
+
"""Entry for a page in a checkpoint manifest."""
|
|
88
|
+
|
|
89
|
+
page_id: str
|
|
90
|
+
artifact_id: str
|
|
91
|
+
modality: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CheckpointManifest(BaseModel):
|
|
95
|
+
"""Manifest for a checkpoint."""
|
|
96
|
+
|
|
97
|
+
name: str
|
|
98
|
+
created_at: datetime
|
|
99
|
+
page_count: int
|
|
100
|
+
pages: List[CheckpointEntry] = Field(default_factory=list)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class InMemoryBackend(BaseModel):
|
|
104
|
+
"""
|
|
105
|
+
Simple in-memory storage backend for testing/development.
|
|
106
|
+
|
|
107
|
+
Not persistent - pages are lost when process exits.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
pages: Dict[str, bytes] = Field(default_factory=dict)
|
|
111
|
+
counter: int = Field(default=0)
|
|
112
|
+
|
|
113
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
114
|
+
|
|
115
|
+
async def store(
|
|
116
|
+
self,
|
|
117
|
+
page: MemoryPage,
|
|
118
|
+
tier: StorageTier,
|
|
119
|
+
) -> str:
|
|
120
|
+
"""Store a page in memory."""
|
|
121
|
+
self.counter += 1
|
|
122
|
+
artifact_id = f"mem_{tier.value}_{self.counter}"
|
|
123
|
+
|
|
124
|
+
# Serialize page to JSON bytes
|
|
125
|
+
data = page.model_dump_json().encode("utf-8")
|
|
126
|
+
self.pages[artifact_id] = data
|
|
127
|
+
|
|
128
|
+
return artifact_id
|
|
129
|
+
|
|
130
|
+
async def load(
|
|
131
|
+
self,
|
|
132
|
+
artifact_id: str,
|
|
133
|
+
) -> Optional[MemoryPage]:
|
|
134
|
+
"""Load a page from memory."""
|
|
135
|
+
data = self.pages.get(artifact_id)
|
|
136
|
+
if not data:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
# Deserialize
|
|
140
|
+
return MemoryPage.model_validate_json(data)
|
|
141
|
+
|
|
142
|
+
async def delete(
|
|
143
|
+
self,
|
|
144
|
+
artifact_id: str,
|
|
145
|
+
) -> bool:
|
|
146
|
+
"""Delete a page from memory."""
|
|
147
|
+
if artifact_id in self.pages:
|
|
148
|
+
del self.pages[artifact_id]
|
|
149
|
+
return True
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def clear(self) -> None:
|
|
153
|
+
"""Clear all stored pages."""
|
|
154
|
+
self.pages.clear()
|
|
155
|
+
self.counter = 0
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class ArtifactsBridge(BaseModel):
|
|
159
|
+
"""
|
|
160
|
+
Bridge between VM and chuk-artifacts storage.
|
|
161
|
+
|
|
162
|
+
Handles page serialization/deserialization and tier-appropriate storage.
|
|
163
|
+
|
|
164
|
+
Usage:
|
|
165
|
+
# With chuk-artifacts
|
|
166
|
+
bridge = ArtifactsBridge()
|
|
167
|
+
await bridge.configure(artifact_store)
|
|
168
|
+
|
|
169
|
+
# Without chuk-artifacts (in-memory fallback)
|
|
170
|
+
bridge = ArtifactsBridge()
|
|
171
|
+
await bridge.configure() # Uses InMemoryBackend
|
|
172
|
+
|
|
173
|
+
# Store a page
|
|
174
|
+
artifact_id = await bridge.store_page(page, StorageTier.L3)
|
|
175
|
+
|
|
176
|
+
# Load a page
|
|
177
|
+
page = await bridge.load_page(artifact_id)
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
# Configuration
|
|
181
|
+
session_id: Optional[str] = None
|
|
182
|
+
_backend: Optional[InMemoryBackend] = None
|
|
183
|
+
_artifact_store: Optional[Any] = None
|
|
184
|
+
_using_artifacts: bool = False
|
|
185
|
+
|
|
186
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
187
|
+
|
|
188
|
+
async def configure(
|
|
189
|
+
self,
|
|
190
|
+
artifact_store: Optional[Any] = None,
|
|
191
|
+
session_id: Optional[str] = None,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Configure the bridge with a storage backend.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
artifact_store: Optional ArtifactStore instance
|
|
198
|
+
session_id: Optional session ID for scoping
|
|
199
|
+
|
|
200
|
+
If no artifact_store is provided, uses in-memory backend.
|
|
201
|
+
"""
|
|
202
|
+
self.session_id = session_id
|
|
203
|
+
|
|
204
|
+
if artifact_store and ARTIFACTS_AVAILABLE:
|
|
205
|
+
self._artifact_store = artifact_store
|
|
206
|
+
self._using_artifacts = True
|
|
207
|
+
else:
|
|
208
|
+
self._backend = InMemoryBackend()
|
|
209
|
+
self._using_artifacts = False
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def is_persistent(self) -> bool:
|
|
213
|
+
"""Check if using persistent storage."""
|
|
214
|
+
return self._using_artifacts
|
|
215
|
+
|
|
216
|
+
async def store_page(
|
|
217
|
+
self,
|
|
218
|
+
page: MemoryPage,
|
|
219
|
+
tier: StorageTier = StorageTier.L3,
|
|
220
|
+
) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Store a page to the specified tier.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
page: MemoryPage to store
|
|
226
|
+
tier: Target storage tier (L3 or L4)
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Artifact ID for later retrieval
|
|
230
|
+
"""
|
|
231
|
+
if self._using_artifacts and self._artifact_store:
|
|
232
|
+
return await self._store_with_artifacts(page, tier)
|
|
233
|
+
elif self._backend:
|
|
234
|
+
return await self._backend.store(page, tier)
|
|
235
|
+
else:
|
|
236
|
+
raise RuntimeError("ArtifactsBridge not configured")
|
|
237
|
+
|
|
238
|
+
async def _store_with_artifacts(
|
|
239
|
+
self,
|
|
240
|
+
page: MemoryPage,
|
|
241
|
+
tier: StorageTier,
|
|
242
|
+
) -> str:
|
|
243
|
+
"""Store using chuk-artifacts."""
|
|
244
|
+
# Serialize page
|
|
245
|
+
data = page.model_dump_json().encode("utf-8")
|
|
246
|
+
|
|
247
|
+
# Determine scope based on tier
|
|
248
|
+
if StorageScope:
|
|
249
|
+
scope = (
|
|
250
|
+
StorageScope.SESSION if tier == StorageTier.L3 else StorageScope.SANDBOX
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
scope = "session"
|
|
254
|
+
|
|
255
|
+
# Build metadata
|
|
256
|
+
metadata = PageMetadata(
|
|
257
|
+
page_id=page.page_id,
|
|
258
|
+
modality=page.modality.value,
|
|
259
|
+
compression_level=page.compression_level
|
|
260
|
+
if isinstance(page.compression_level, int)
|
|
261
|
+
else page.compression_level.value,
|
|
262
|
+
tier=tier.value,
|
|
263
|
+
session_id=self.session_id,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Store in artifacts
|
|
267
|
+
artifact_id = await self._artifact_store.store(
|
|
268
|
+
data=data,
|
|
269
|
+
mime=MEMORY_PAGE_MIME_TYPE,
|
|
270
|
+
scope=scope,
|
|
271
|
+
summary=f"VM page: {page.page_id}",
|
|
272
|
+
metadata=metadata.model_dump(),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return artifact_id
|
|
276
|
+
|
|
277
|
+
async def load_page(
|
|
278
|
+
self,
|
|
279
|
+
artifact_id: str,
|
|
280
|
+
) -> Optional[MemoryPage]:
|
|
281
|
+
"""
|
|
282
|
+
Load a page by artifact ID.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
artifact_id: ID returned from store_page
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
MemoryPage or None if not found
|
|
289
|
+
"""
|
|
290
|
+
if self._using_artifacts and self._artifact_store:
|
|
291
|
+
return await self._load_with_artifacts(artifact_id)
|
|
292
|
+
elif self._backend:
|
|
293
|
+
return await self._backend.load(artifact_id)
|
|
294
|
+
else:
|
|
295
|
+
raise RuntimeError("ArtifactsBridge not configured")
|
|
296
|
+
|
|
297
|
+
async def _load_with_artifacts(
|
|
298
|
+
self,
|
|
299
|
+
artifact_id: str,
|
|
300
|
+
) -> Optional[MemoryPage]:
|
|
301
|
+
"""Load using chuk-artifacts."""
|
|
302
|
+
try:
|
|
303
|
+
# Get artifact data
|
|
304
|
+
data = await self._artifact_store.retrieve(artifact_id)
|
|
305
|
+
if not data:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
# Deserialize
|
|
309
|
+
if isinstance(data, bytes):
|
|
310
|
+
return MemoryPage.model_validate_json(data)
|
|
311
|
+
elif isinstance(data, str):
|
|
312
|
+
return MemoryPage.model_validate_json(data.encode())
|
|
313
|
+
else:
|
|
314
|
+
return None
|
|
315
|
+
except Exception:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
async def delete_page(
|
|
319
|
+
self,
|
|
320
|
+
artifact_id: str,
|
|
321
|
+
) -> bool:
|
|
322
|
+
"""
|
|
323
|
+
Delete a stored page.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
artifact_id: ID of the artifact to delete
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
True if deleted, False if not found
|
|
330
|
+
"""
|
|
331
|
+
if self._using_artifacts and self._artifact_store:
|
|
332
|
+
try:
|
|
333
|
+
await self._artifact_store.delete(artifact_id)
|
|
334
|
+
return True
|
|
335
|
+
except Exception:
|
|
336
|
+
return False
|
|
337
|
+
elif self._backend:
|
|
338
|
+
return await self._backend.delete(artifact_id)
|
|
339
|
+
else:
|
|
340
|
+
raise RuntimeError("ArtifactsBridge not configured")
|
|
341
|
+
|
|
342
|
+
async def store_checkpoint(
|
|
343
|
+
self,
|
|
344
|
+
pages: List[MemoryPage],
|
|
345
|
+
checkpoint_name: str,
|
|
346
|
+
) -> str:
|
|
347
|
+
"""
|
|
348
|
+
Store a checkpoint of multiple pages.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
pages: List of pages to checkpoint
|
|
352
|
+
checkpoint_name: Name for the checkpoint
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Checkpoint artifact ID
|
|
356
|
+
"""
|
|
357
|
+
# Create checkpoint manifest
|
|
358
|
+
manifest = CheckpointManifest(
|
|
359
|
+
name=checkpoint_name,
|
|
360
|
+
created_at=datetime.utcnow(),
|
|
361
|
+
page_count=len(pages),
|
|
362
|
+
pages=[],
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Store each page and record artifact IDs
|
|
366
|
+
for page in pages:
|
|
367
|
+
artifact_id = await self.store_page(page, StorageTier.L3)
|
|
368
|
+
manifest.pages.append(
|
|
369
|
+
CheckpointEntry(
|
|
370
|
+
page_id=page.page_id,
|
|
371
|
+
artifact_id=artifact_id,
|
|
372
|
+
modality=page.modality.value,
|
|
373
|
+
)
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Store manifest itself
|
|
377
|
+
manifest_data = manifest.model_dump_json().encode("utf-8")
|
|
378
|
+
|
|
379
|
+
if self._using_artifacts and self._artifact_store:
|
|
380
|
+
if StorageScope:
|
|
381
|
+
scope = StorageScope.SESSION
|
|
382
|
+
else:
|
|
383
|
+
scope = "session"
|
|
384
|
+
|
|
385
|
+
checkpoint_metadata = CheckpointMetadata(
|
|
386
|
+
checkpoint_name=checkpoint_name,
|
|
387
|
+
page_count=len(pages),
|
|
388
|
+
session_id=self.session_id,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
checkpoint_id = await self._artifact_store.store(
|
|
392
|
+
data=manifest_data,
|
|
393
|
+
mime=VM_CHECKPOINT_MIME_TYPE,
|
|
394
|
+
scope=scope,
|
|
395
|
+
summary=f"VM checkpoint: {checkpoint_name}",
|
|
396
|
+
metadata=checkpoint_metadata.model_dump(),
|
|
397
|
+
)
|
|
398
|
+
return checkpoint_id
|
|
399
|
+
else:
|
|
400
|
+
# In-memory: store manifest directly
|
|
401
|
+
if self._backend:
|
|
402
|
+
self._backend.counter += 1
|
|
403
|
+
checkpoint_id = f"checkpoint_{self._backend.counter}"
|
|
404
|
+
self._backend.pages[checkpoint_id] = manifest_data
|
|
405
|
+
return checkpoint_id
|
|
406
|
+
raise RuntimeError("ArtifactsBridge not configured")
|
|
407
|
+
|
|
408
|
+
async def load_checkpoint(
|
|
409
|
+
self,
|
|
410
|
+
checkpoint_id: str,
|
|
411
|
+
) -> List[MemoryPage]:
|
|
412
|
+
"""
|
|
413
|
+
Load all pages from a checkpoint.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
checkpoint_id: ID from store_checkpoint
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
List of MemoryPages
|
|
420
|
+
"""
|
|
421
|
+
# Load manifest
|
|
422
|
+
manifest_data: Optional[bytes] = None
|
|
423
|
+
if self._using_artifacts and self._artifact_store:
|
|
424
|
+
manifest_data = await self._artifact_store.retrieve(checkpoint_id)
|
|
425
|
+
elif self._backend:
|
|
426
|
+
manifest_data = self._backend.pages.get(checkpoint_id)
|
|
427
|
+
else:
|
|
428
|
+
raise RuntimeError("ArtifactsBridge not configured")
|
|
429
|
+
|
|
430
|
+
if not manifest_data:
|
|
431
|
+
return []
|
|
432
|
+
|
|
433
|
+
if isinstance(manifest_data, bytes):
|
|
434
|
+
manifest_str = manifest_data.decode("utf-8")
|
|
435
|
+
else:
|
|
436
|
+
manifest_str = manifest_data
|
|
437
|
+
|
|
438
|
+
manifest = CheckpointManifest.model_validate_json(manifest_str)
|
|
439
|
+
|
|
440
|
+
# Load each page
|
|
441
|
+
pages: List[MemoryPage] = []
|
|
442
|
+
for entry in manifest.pages:
|
|
443
|
+
page = await self.load_page(entry.artifact_id)
|
|
444
|
+
if page:
|
|
445
|
+
pages.append(page)
|
|
446
|
+
|
|
447
|
+
return pages
|
|
448
|
+
|
|
449
|
+
def get_stats(self) -> StorageStats:
|
|
450
|
+
"""Get storage statistics."""
|
|
451
|
+
if self._using_artifacts:
|
|
452
|
+
return StorageStats(
|
|
453
|
+
backend="chuk-artifacts",
|
|
454
|
+
persistent=True,
|
|
455
|
+
session_id=self.session_id,
|
|
456
|
+
pages_stored=0, # Would need to query artifact store
|
|
457
|
+
)
|
|
458
|
+
elif self._backend:
|
|
459
|
+
return StorageStats(
|
|
460
|
+
backend="in-memory",
|
|
461
|
+
persistent=False,
|
|
462
|
+
session_id=self.session_id,
|
|
463
|
+
pages_stored=len(self._backend.pages),
|
|
464
|
+
)
|
|
465
|
+
else:
|
|
466
|
+
return StorageStats(
|
|
467
|
+
backend="unconfigured",
|
|
468
|
+
persistent=False,
|
|
469
|
+
)
|