chuk-artifacts 0.2.2__py3-none-any.whl → 0.4__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_artifacts/core.py +73 -0
- chuk_artifacts/grid.py +140 -7
- chuk_artifacts/metadata.py +5 -4
- chuk_artifacts/models.py +3 -3
- chuk_artifacts/providers/filesystem.py +23 -3
- chuk_artifacts/providers/ibm_cos.py +61 -59
- chuk_artifacts/providers/ibm_cos_iam.py +51 -7
- chuk_artifacts/providers/memory.py +32 -4
- chuk_artifacts/store.py +76 -3
- chuk_artifacts-0.4.dist-info/METADATA +730 -0
- chuk_artifacts-0.4.dist-info/RECORD +23 -0
- chuk_artifacts-0.2.2.dist-info/METADATA +0 -719
- chuk_artifacts-0.2.2.dist-info/RECORD +0 -24
- chuk_artifacts-0.2.2.dist-info/licenses/LICENSE +0 -21
- {chuk_artifacts-0.2.2.dist-info → chuk_artifacts-0.4.dist-info}/WHEEL +0 -0
- {chuk_artifacts-0.2.2.dist-info → chuk_artifacts-0.4.dist-info}/top_level.txt +0 -0
@@ -36,6 +36,7 @@ class _MemoryS3Client:
|
|
36
36
|
If None, creates isolated per-instance storage.
|
37
37
|
"""
|
38
38
|
self._store: Dict[str, Dict[str, Any]] = shared_store if shared_store is not None else {}
|
39
|
+
self._is_shared_store = shared_store is not None
|
39
40
|
self._lock = asyncio.Lock()
|
40
41
|
self._closed = False
|
41
42
|
|
@@ -231,8 +232,11 @@ class _MemoryS3Client:
|
|
231
232
|
async def close(self):
|
232
233
|
"""Clean up resources and mark client as closed."""
|
233
234
|
if not self._closed:
|
234
|
-
|
235
|
-
|
235
|
+
# Only clear the store if it's NOT shared
|
236
|
+
# If it's shared, other clients may still need the data
|
237
|
+
if not self._is_shared_store:
|
238
|
+
async with self._lock:
|
239
|
+
self._store.clear()
|
236
240
|
self._closed = True
|
237
241
|
|
238
242
|
# ------------------------------------------------------------
|
@@ -253,6 +257,10 @@ class _MemoryS3Client:
|
|
253
257
|
"total_objects": total_objects,
|
254
258
|
"total_bytes": total_bytes,
|
255
259
|
"closed": self._closed,
|
260
|
+
"is_shared_store": self._is_shared_store,
|
261
|
+
"store_id": id(self._store), # Memory address for debugging
|
262
|
+
"client_id": id(self), # Client instance ID
|
263
|
+
"store_keys": list(self._store.keys())[:5], # First 5 keys for debugging
|
256
264
|
}
|
257
265
|
|
258
266
|
@classmethod
|
@@ -263,17 +271,32 @@ class _MemoryS3Client:
|
|
263
271
|
|
264
272
|
# ---- public factory -------------------------------------------------------
|
265
273
|
|
274
|
+
# Global shared storage for memory provider when used as default
|
275
|
+
_default_shared_store: Dict[str, Dict[str, Any]] = {}
|
276
|
+
|
266
277
|
def factory(shared_store: Optional[Dict[str, Dict[str, Any]]] = None) -> Callable[[], AsyncContextManager]:
|
267
278
|
"""
|
268
279
|
Return a **zero-arg** factory that yields an async-context client.
|
269
280
|
|
281
|
+
Key behavior for memory provider:
|
282
|
+
- If shared_store is provided, uses that specific storage
|
283
|
+
- If shared_store is None, ALWAYS uses the global shared storage
|
284
|
+
- This ensures all memory clients in the same process share data
|
285
|
+
- Prevents issues where ArtifactStore operations can't see each other's data
|
286
|
+
|
270
287
|
Parameters
|
271
288
|
----------
|
272
289
|
shared_store : dict, optional
|
273
290
|
If provided, all clients created by this factory will share
|
274
|
-
the same storage dict.
|
275
|
-
|
291
|
+
the same storage dict. If None, will use a global shared store
|
292
|
+
to ensure consistency across operations within the same process.
|
276
293
|
"""
|
294
|
+
|
295
|
+
# CRITICAL: Always use global shared storage when none specified
|
296
|
+
# This prevents the common issue where each ArtifactStore operation
|
297
|
+
# gets a different isolated storage and can't see each other's data
|
298
|
+
if shared_store is None:
|
299
|
+
shared_store = _default_shared_store
|
277
300
|
|
278
301
|
@asynccontextmanager
|
279
302
|
async def _ctx():
|
@@ -307,6 +330,11 @@ async def clear_all_memory_stores():
|
|
307
330
|
Emergency cleanup function that clears all active memory stores.
|
308
331
|
Useful for test teardown.
|
309
332
|
"""
|
333
|
+
# Clear the global shared store
|
334
|
+
global _default_shared_store
|
335
|
+
_default_shared_store.clear()
|
336
|
+
|
337
|
+
# Close all active instances
|
310
338
|
instances = list(_MemoryS3Client._instances)
|
311
339
|
for instance in instances:
|
312
340
|
try:
|
chuk_artifacts/store.py
CHANGED
@@ -12,7 +12,9 @@ Grid Architecture:
|
|
12
12
|
|
13
13
|
from __future__ import annotations
|
14
14
|
|
15
|
-
import os
|
15
|
+
import os
|
16
|
+
import logging
|
17
|
+
import uuid
|
16
18
|
from datetime import datetime
|
17
19
|
from typing import Any, Dict, List, Callable, AsyncContextManager, Optional, Union
|
18
20
|
from chuk_sessions.session_manager import SessionManager
|
@@ -37,7 +39,6 @@ except ImportError:
|
|
37
39
|
from .exceptions import ArtifactStoreError, ProviderError
|
38
40
|
|
39
41
|
# Import chuk_sessions instead of local session manager
|
40
|
-
from chuk_sessions.session_manager import SessionManager
|
41
42
|
|
42
43
|
# Configure structured logging
|
43
44
|
logger = logging.getLogger(__name__)
|
@@ -161,6 +162,32 @@ class ArtifactStore:
|
|
161
162
|
session_id=session_id,
|
162
163
|
ttl=ttl,
|
163
164
|
)
|
165
|
+
|
166
|
+
async def update_file(
|
167
|
+
self,
|
168
|
+
artifact_id: str,
|
169
|
+
*,
|
170
|
+
data: Optional[bytes] = None,
|
171
|
+
meta: Optional[Dict[str, Any]] = None,
|
172
|
+
filename: Optional[str] = None,
|
173
|
+
summary: Optional[str] = None,
|
174
|
+
mime: Optional[str] = None,
|
175
|
+
) -> bool:
|
176
|
+
"""
|
177
|
+
Update an artifact's content, metadata, filename, summary, or mime type.
|
178
|
+
All parameters are optional. At least one must be provided.
|
179
|
+
"""
|
180
|
+
if not any([data, meta, filename, summary, mime]):
|
181
|
+
raise ValueError("At least one update parameter must be provided.")
|
182
|
+
|
183
|
+
return await self._core.update_file(
|
184
|
+
artifact_id=artifact_id,
|
185
|
+
new_data=data,
|
186
|
+
meta=meta,
|
187
|
+
filename=filename,
|
188
|
+
summary=summary,
|
189
|
+
mime=mime,
|
190
|
+
)
|
164
191
|
|
165
192
|
async def retrieve(self, artifact_id: str) -> bytes:
|
166
193
|
"""Retrieve artifact data."""
|
@@ -606,4 +633,50 @@ class ArtifactStore:
|
|
606
633
|
return self
|
607
634
|
|
608
635
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
609
|
-
await self.close()
|
636
|
+
await self.close()
|
637
|
+
|
638
|
+
async def get_sandbox_info(self) -> Dict[str, Any]:
|
639
|
+
"""
|
640
|
+
Get sandbox information and metadata.
|
641
|
+
|
642
|
+
Returns
|
643
|
+
-------
|
644
|
+
Dict[str, Any]
|
645
|
+
Dictionary containing sandbox information including:
|
646
|
+
- sandbox_id: The current sandbox identifier
|
647
|
+
- bucket: The storage bucket name
|
648
|
+
- storage_provider: The storage provider type
|
649
|
+
- session_provider: The session provider type
|
650
|
+
- session_ttl_hours: Default session TTL
|
651
|
+
- grid_prefix_pattern: The grid path pattern for this sandbox
|
652
|
+
- created_at: Timestamp of when this info was retrieved
|
653
|
+
"""
|
654
|
+
from datetime import datetime
|
655
|
+
|
656
|
+
# Get session manager stats if available
|
657
|
+
session_stats = {}
|
658
|
+
try:
|
659
|
+
session_stats = self._session_manager.get_cache_stats()
|
660
|
+
except Exception:
|
661
|
+
pass # Session manager might not have stats
|
662
|
+
|
663
|
+
# Get storage stats if available
|
664
|
+
storage_stats = {}
|
665
|
+
try:
|
666
|
+
storage_stats = await self._admin.get_stats()
|
667
|
+
except Exception:
|
668
|
+
pass # Storage might not have stats
|
669
|
+
|
670
|
+
return {
|
671
|
+
"sandbox_id": self.sandbox_id,
|
672
|
+
"bucket": self.bucket,
|
673
|
+
"storage_provider": self._storage_provider_name,
|
674
|
+
"session_provider": self._session_provider_name,
|
675
|
+
"session_ttl_hours": self.session_ttl_hours,
|
676
|
+
"max_retries": self.max_retries,
|
677
|
+
"grid_prefix_pattern": self.get_session_prefix_pattern(),
|
678
|
+
"created_at": datetime.utcnow().isoformat() + "Z",
|
679
|
+
"session_stats": session_stats,
|
680
|
+
"storage_stats": storage_stats,
|
681
|
+
"closed": self._closed,
|
682
|
+
}
|