@@ -131,15 +131,19 @@ def _extension_for(content_type: str) -> str:
131131 return f".{ content_type .split ('/' )[- 1 ]} "
132132
133133 def store (self , key : str , content : bytes , content_type : str = "text/plain" ) -> str :
134- """Store content as a file and return the filename as reference.
134+ """Store content as a file and return the path as reference.
135+
136+ The returned path preserves the form of ``artifact_dir`` passed to
137+ the constructor: a relative ``artifact_dir`` yields a relative
138+ reference, an absolute one yields an absolute reference.
135139
136140 Args:
137141 key: A unique key for this content block.
138142 content: The raw content bytes to store.
139143 content_type: MIME type of the content.
140144
141145 Returns:
142- The filename (not full path) used as the reference .
146+ The file path (e.g., ``./artifacts/1234_1_key.txt``) .
143147 """
144148 self ._artifact_dir .mkdir (parents = True , exist_ok = True )
145149
@@ -156,26 +160,33 @@ def store(self, key: str, content: bytes, content_type: str = "text/plain") -> s
156160 file_path = self ._artifact_dir / filename
157161 file_path .write_bytes (content )
158162
159- return filename
163+ return str ( file_path )
160164
161165 def retrieve (self , reference : str ) -> tuple [bytes , str ]:
162166 """Retrieve content from a stored file.
163167
168+ Accepts both full paths (as returned by ``store()``) and bare
169+ filenames for backward compatibility.
170+
164171 Args:
165- reference: The filename reference returned by store().
172+ reference: The file path or filename returned by store().
166173
167174 Returns:
168175 A tuple of (content bytes, content type).
169176
170177 Raises:
171178 KeyError: If the file does not exist.
172179 """
173- file_path = (self ._artifact_dir / reference ).resolve ()
180+ ref_path = Path (reference )
181+ file_path = ref_path .resolve () if len (ref_path .parts ) > 1 else (self ._artifact_dir / reference ).resolve ()
182+ if not file_path .is_relative_to (self ._artifact_dir .resolve ()):
183+ file_path = (self ._artifact_dir / reference ).resolve ()
174184 if not file_path .is_relative_to (self ._artifact_dir .resolve ()):
175185 raise KeyError (f"Reference not found: { reference } " )
176186 if not file_path .is_file ():
177187 raise KeyError (f"Reference not found: { reference } " )
178- content_type = self ._content_types .get (reference , "application/octet-stream" )
188+ filename = file_path .name
189+ content_type = self ._content_types .get (filename , "application/octet-stream" )
179190 return file_path .read_bytes (), content_type
180191
181192 def _load_metadata (self ) -> dict [str , str ]:
@@ -320,15 +331,15 @@ def __init__(
320331 self ._lock = threading .Lock ()
321332
322333 def store (self , key : str , content : bytes , content_type : str = "text/plain" ) -> str :
323- """Store content as an S3 object and return the object key as reference.
334+ """Store content as an S3 object and return an ``s3://`` URI as reference.
324335
325336 Args:
326337 key: A unique key for this content block.
327338 content: The raw content bytes to store.
328339 content_type: MIME type of the content.
329340
330341 Returns:
331- The S3 object key used as the reference .
342+ An S3 URI (e.g., ``s3://bucket/prefix/1234_1_key``) .
332343
333344 Raises:
334345 botocore.exceptions.ClientError: If the S3 operation fails (e.g., bucket
@@ -348,22 +359,31 @@ def store(self, key: str, content: bytes, content_type: str = "text/plain") -> s
348359 ContentType = content_type ,
349360 )
350361
351- return s3_key
362+ return f"s3:// { self . _bucket } / { s3_key } "
352363
353364 def retrieve (self , reference : str ) -> tuple [bytes , str ]:
354365 """Retrieve content from an S3 object.
355366
367+ Accepts both ``s3://`` URIs (as returned by ``store()``) and raw
368+ S3 keys for backward compatibility.
369+
356370 Args:
357- reference: The S3 object key returned by store().
371+ reference: The S3 URI or object key returned by store().
358372
359373 Returns:
360374 A tuple of (content bytes, content type).
361375
362376 Raises:
363377 KeyError: If the object does not exist.
364378 """
379+ s3_key = reference
380+ if reference .startswith ("s3://" ):
381+ expected_prefix = f"s3://{ self ._bucket } /"
382+ if not reference .startswith (expected_prefix ):
383+ raise KeyError (f"Reference not found: { reference } " )
384+ s3_key = reference [len (expected_prefix ):]
365385 try :
366- response = self ._client .get_object (Bucket = self ._bucket , Key = reference )
386+ response = self ._client .get_object (Bucket = self ._bucket , Key = s3_key )
367387 content : bytes = response ["Body" ].read ()
368388 content_type : str = response .get ("ContentType" , "application/octet-stream" )
369389 return content , content_type
0 commit comments