@@ -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,34 @@ 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 ()
174- if not file_path .is_relative_to (self ._artifact_dir .resolve ()):
180+ resolved_dir = self ._artifact_dir .resolve ()
181+ ref_path = Path (reference )
182+ file_path = ref_path .resolve () if len (ref_path .parts ) > 1 else (self ._artifact_dir / reference ).resolve ()
183+ if not file_path .is_relative_to (resolved_dir ):
184+ file_path = (self ._artifact_dir / reference ).resolve ()
185+ if not file_path .is_relative_to (resolved_dir ):
175186 raise KeyError (f"Reference not found: { reference } " )
176187 if not file_path .is_file ():
177188 raise KeyError (f"Reference not found: { reference } " )
178- content_type = self ._content_types .get (reference , "application/octet-stream" )
189+ filename = file_path .name
190+ content_type = self ._content_types .get (filename , "application/octet-stream" )
179191 return file_path .read_bytes (), content_type
180192
181193 def _load_metadata (self ) -> dict [str , str ]:
@@ -320,15 +332,15 @@ def __init__(
320332 self ._lock = threading .Lock ()
321333
322334 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.
335+ """Store content as an S3 object and return an ``s3://`` URI as reference.
324336
325337 Args:
326338 key: A unique key for this content block.
327339 content: The raw content bytes to store.
328340 content_type: MIME type of the content.
329341
330342 Returns:
331- The S3 object key used as the reference .
343+ An S3 URI (e.g., ``s3://bucket/prefix/1234_1_key``) .
332344
333345 Raises:
334346 botocore.exceptions.ClientError: If the S3 operation fails (e.g., bucket
@@ -348,22 +360,31 @@ def store(self, key: str, content: bytes, content_type: str = "text/plain") -> s
348360 ContentType = content_type ,
349361 )
350362
351- return s3_key
363+ return f"s3:// { self . _bucket } / { s3_key } "
352364
353365 def retrieve (self , reference : str ) -> tuple [bytes , str ]:
354366 """Retrieve content from an S3 object.
355367
368+ Accepts both ``s3://`` URIs (as returned by ``store()``) and raw
369+ S3 keys for backward compatibility.
370+
356371 Args:
357- reference: The S3 object key returned by store().
372+ reference: The S3 URI or object key returned by store().
358373
359374 Returns:
360375 A tuple of (content bytes, content type).
361376
362377 Raises:
363378 KeyError: If the object does not exist.
364379 """
380+ s3_key = reference
381+ if reference .startswith ("s3://" ):
382+ expected_prefix = f"s3://{ self ._bucket } /"
383+ if not reference .startswith (expected_prefix ):
384+ raise KeyError (f"Reference not found: { reference } " )
385+ s3_key = reference [len (expected_prefix ) :]
365386 try :
366- response = self ._client .get_object (Bucket = self ._bucket , Key = reference )
387+ response = self ._client .get_object (Bucket = self ._bucket , Key = s3_key )
367388 content : bytes = response ["Body" ].read ()
368389 content_type : str = response .get ("ContentType" , "application/octet-stream" )
369390 return content , content_type
0 commit comments