Skip to content

Commit f6a5263

Browse files
committed
fix: restore .text attribute when loading text artifacts from GCS
GcsArtifactService._load_artifact() always used Part.from_bytes() which populates inline_data even for text/plain content. This caused Part.from_text() artifacts to lose their .text attribute after a save/load cycle. Now detects text/plain content type and returns Part(text=...) instead, matching the behavior of FileArtifactService which already handles this correctly. Fixes #3157
1 parent 19ac679 commit f6a5263

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

src/google/adk/artifacts/gcs_artifact_service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ def _load_artifact(
268268
artifact_bytes = blob.download_as_bytes()
269269
if not artifact_bytes:
270270
return None
271+
# Restore text artifacts with the .text attribute rather than
272+
# inline_data so that round-tripping Part.from_text() works correctly.
273+
if blob.content_type and blob.content_type.startswith("text/plain"):
274+
return types.Part(text=artifact_bytes.decode("utf-8"))
271275
artifact = types.Part.from_bytes(
272276
data=artifact_bytes, mime_type=blob.content_type
273277
)

tests/unittests/artifacts/test_artifact_service.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,3 +896,49 @@ async def test_save_artifact_with_snake_case_dict(
896896
assert loaded is not None
897897
assert loaded.inline_data is not None
898898
assert loaded.inline_data.mime_type == "text/plain"
899+
900+
901+
@pytest.mark.asyncio
902+
@pytest.mark.parametrize(
903+
"service_type",
904+
[
905+
ArtifactServiceType.IN_MEMORY,
906+
ArtifactServiceType.GCS,
907+
ArtifactServiceType.FILE,
908+
],
909+
)
910+
async def test_text_artifact_roundtrip(service_type, artifact_service_factory):
911+
"""Text artifacts saved via Part.from_text() should preserve .text on load.
912+
913+
Regression test for https://github.com/google/adk-python/issues/3157
914+
"""
915+
artifact_service = artifact_service_factory(service_type)
916+
app_name = "app0"
917+
user_id = "user0"
918+
session_id = "sess0"
919+
filename = "report.txt"
920+
text_content = '{"status": "success", "report": "Sunny, 25C"}'
921+
922+
artifact = types.Part.from_text(text=text_content)
923+
assert artifact.text == text_content # sanity check
924+
925+
version = await artifact_service.save_artifact(
926+
app_name=app_name,
927+
user_id=user_id,
928+
session_id=session_id,
929+
filename=filename,
930+
artifact=artifact,
931+
)
932+
assert version == 0
933+
934+
loaded = await artifact_service.load_artifact(
935+
app_name=app_name,
936+
user_id=user_id,
937+
session_id=session_id,
938+
filename=filename,
939+
)
940+
assert loaded is not None
941+
assert loaded.text == text_content, (
942+
f"Expected .text='{text_content}', got .text={loaded.text!r} "
943+
f"(inline_data={loaded.inline_data!r})"
944+
)

0 commit comments

Comments
 (0)