Skip to content

Commit 54a9940

Browse files
committed
fix(a2a): skip URI-rehydrate when source already in agent namespace
The blanket rehydrate path I introduced for cross-namespace URI references (WebUI artifacts attached by reference into agent tasks) was running for *every* URI FilePart, including ones that already pointed at the agent's own (app_name, user_id, session_id). For those, save_artifact_with_metadata bumped the version (e.g. v0 → v1 because metadata already existed at v0), and the LLM saw a metadata header for v1 even though the URI explicitly referenced v0. Compare the URI's coordinates to the agent's own. If they match, skip the load+save round trip and let the metadata-only load below work directly against the URI's path-extracted version. The cross-namespace case (different app/user/session) still rehydrates into the agent's namespace so load_artifact remains reachable. Restores the integration scenario `test_filepart_by_reference` and preserves the fix for the WebUI attach-existing-artifact flow.
1 parent 22bd1a1 commit 54a9940

1 file changed

Lines changed: 62 additions & 48 deletions

File tree

src/solace_agent_mesh/common/a2a/translation.py

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)