@@ -183,19 +183,13 @@ async def _prepare_a2a_filepart_for_adk(
183183 )
184184
185185 elif isinstance (part .file , FileWithUri ):
186- log .debug ("%s FilePart contains URI. Resolving to bytes and re-saving under agent namespace ." , log_id )
186+ log .debug ("%s FilePart contains URI. Loading metadata ." , log_id )
187187 uri = part .file .uri
188188 parsed_uri = urlparse (uri )
189189 path_parts = parsed_uri .path .strip ("/" ).split ("/" )
190190 if len (path_parts ) < 3 :
191191 raise ValueError (f"Invalid artifact URI format: { uri } " )
192192 # Canonical layout: artifact://{app_name}/{user_id}/{session_id}/{filename}
193- # The URI's path identifies the artifact's actual storage location
194- # (which may live in a different namespace from the receiving agent
195- # — e.g. a WebUI-uploaded artifact attached by reference). We
196- # rehydrate the bytes from that namespace and save a fresh copy
197- # under the agent's namespace so the LLM's `load_artifact` tool —
198- # which only knows the agent's context — can find it later.
199193 filename = path_parts [- 1 ]
200194 source_user_id = path_parts [- 3 ]
201195 source_session_id = path_parts [- 2 ]
@@ -205,53 +199,73 @@ async def _prepare_a2a_filepart_for_adk(
205199 # Fall back to "latest" so callers (e.g. the WebUI attach-artifact
206200 # dialog) don't have to know the numeric version up front; the
207201 # downstream loader resolves it against the live version list.
208- source_version = int (version_str ) if version_str else "latest"
202+ source_version : int | str = int (version_str ) if version_str else "latest"
209203
210- source_load = await load_artifact_content_or_metadata (
211- artifact_service = artifact_service ,
212- app_name = source_app_name ,
213- user_id = source_user_id ,
214- session_id = source_session_id ,
215- filename = filename ,
216- version = source_version ,
217- load_metadata_only = False ,
218- return_raw_bytes = True ,
204+ agent_session_id = get_original_session_id (session_id )
205+ same_namespace = (
206+ source_app_name == app_name
207+ and source_user_id == user_id
208+ and source_session_id == agent_session_id
219209 )
220- if source_load ["status" ] != "success" :
221- raise RuntimeError (
222- f"Failed to load referenced artifact: { source_load .get ('message' , 'unknown error' )} "
223- )
224210
225- content_bytes = source_load .get ("raw_bytes" )
226- mime_type = part .file .mime_type or source_load .get ("mime_type" ) or resolve_mime_type (filename , None )
227- if content_bytes is None :
228- raise RuntimeError (
229- f"Referenced artifact '{ filename } ' returned no bytes."
211+ if same_namespace :
212+ # The URI already points at the agent's own namespace — no need
213+ # to copy. Use the URI's coordinates for the metadata load
214+ # below; `load_artifact` later will find the artifact at the
215+ # same coords with no resave.
216+ version = source_version
217+ mime_type = part .file .mime_type or resolve_mime_type (filename , None )
218+ else :
219+ # Cross-namespace reference (e.g. a WebUI-uploaded artifact
220+ # attached by reference into an agent task). The LLM's
221+ # `load_artifact` tool only knows the agent's context, so we
222+ # rehydrate the bytes here and save a fresh copy under the
223+ # agent's namespace.
224+ source_load = await load_artifact_content_or_metadata (
225+ artifact_service = artifact_service ,
226+ app_name = source_app_name ,
227+ user_id = source_user_id ,
228+ session_id = source_session_id ,
229+ filename = filename ,
230+ version = source_version ,
231+ load_metadata_only = False ,
232+ return_raw_bytes = True ,
230233 )
234+ if source_load ["status" ] != "success" :
235+ raise RuntimeError (
236+ f"Failed to load referenced artifact: { source_load .get ('message' , 'unknown error' )} "
237+ )
231238
232- save_result = await save_artifact_with_metadata (
233- artifact_service = artifact_service ,
234- app_name = app_name ,
235- user_id = user_id ,
236- session_id = session_id ,
237- filename = filename ,
238- content_bytes = content_bytes ,
239- mime_type = mime_type ,
240- metadata_dict = {
241- "source" : "a2a_filepart_uri_reference" ,
242- "source_uri" : uri ,
243- },
244- timestamp = datetime .now (timezone .utc ),
245- )
246- if save_result ["status" ] != "success" :
247- raise IOError (
248- f"Failed to save resolved artifact and its metadata: { save_result ['message' ]} "
239+ content_bytes = source_load .get ("raw_bytes" )
240+ mime_type = part .file .mime_type or source_load .get ("mime_type" ) or resolve_mime_type (filename , None )
241+ if content_bytes is None :
242+ raise RuntimeError (
243+ f"Referenced artifact '{ filename } ' returned no bytes."
244+ )
245+
246+ save_result = await save_artifact_with_metadata (
247+ artifact_service = artifact_service ,
248+ app_name = app_name ,
249+ user_id = user_id ,
250+ session_id = session_id ,
251+ filename = filename ,
252+ content_bytes = content_bytes ,
253+ mime_type = mime_type ,
254+ metadata_dict = {
255+ "source" : "a2a_filepart_uri_reference" ,
256+ "source_uri" : uri ,
257+ },
258+ timestamp = datetime .now (timezone .utc ),
259+ )
260+ if save_result ["status" ] != "success" :
261+ raise IOError (
262+ f"Failed to save resolved artifact and its metadata: { save_result ['message' ]} "
263+ )
264+ version = save_result ["data_version" ]
265+ log .info (
266+ "%s Resolved URI to bytes and saved '%s' as v%d under agent namespace." ,
267+ log_id , filename , version ,
249268 )
250- version = save_result ["data_version" ]
251- log .info (
252- "%s Resolved URI to bytes and saved '%s' as v%d under agent namespace." ,
253- log_id , filename , version ,
254- )
255269
256270 else :
257271 raise TypeError ("FilePart contains neither bytes nor a valid URI." )
0 commit comments