@@ -766,3 +766,102 @@ async def test_file_save_artifact_rejects_absolute_path_within_scope(tmp_path):
766766 filename = str (absolute_in_scope ),
767767 artifact = part ,
768768 )
769+
770+ @pytest .mark .asyncio
771+ @pytest .mark .parametrize (
772+ "service_type" ,
773+ [
774+ ArtifactServiceType .IN_MEMORY ,
775+ ArtifactServiceType .GCS ,
776+ ArtifactServiceType .FILE ,
777+ ],
778+ )
779+ async def test_save_load_dict_shaped_artifact (
780+ service_type , artifact_service_factory
781+ ):
782+ """Tests saving and loading dict-shaped artifacts.
783+
784+ This tests the fix for accepting dict-shaped (serialized) artifacts
785+ in the save_artifact method. Dict-shaped artifacts are commonly used
786+ when artifacts are stored/retrieved from JSON or other serialization formats.
787+ """
788+ artifact_service = artifact_service_factory (service_type )
789+ # Create a dict-shaped artifact by serializing a real Part instance
790+ part = types .Part .from_bytes (data = b"test_data" , mime_type = "text/plain" )
791+ dict_artifact = part .model_dump (exclude_none = True )
792+
793+ app_name = "app0"
794+ user_id = "user0"
795+ session_id = "123"
796+ filename = "dict_file.txt"
797+
798+ # Save the dict-shaped artifact
799+ version = await artifact_service .save_artifact (
800+ app_name = app_name ,
801+ user_id = user_id ,
802+ session_id = session_id ,
803+ filename = filename ,
804+ artifact = dict_artifact ,
805+ )
806+ assert version == 0
807+
808+ # Load and verify the artifact
809+ loaded = await artifact_service .load_artifact (
810+ app_name = app_name ,
811+ user_id = user_id ,
812+ session_id = session_id ,
813+ filename = filename ,
814+ )
815+ assert loaded is not None
816+ assert loaded .inline_data is not None
817+ assert loaded .inline_data .mime_type == "text/plain"
818+
819+
820+ @pytest .mark .asyncio
821+ @pytest .mark .parametrize (
822+ "service_type" ,
823+ [
824+ ArtifactServiceType .IN_MEMORY ,
825+ ArtifactServiceType .GCS ,
826+ ArtifactServiceType .FILE ,
827+ ],
828+ )
829+ async def test_save_text_dict_shaped_artifact (
830+ service_type , artifact_service_factory
831+ ):
832+ """Tests saving and loading dict-shaped artifacts with text content."""
833+ artifact_service = artifact_service_factory (service_type )
834+ # Create a dict-shaped artifact by serializing a real Part instance
835+ part = types .Part (text = "Hello, World!" )
836+ dict_artifact = part .model_dump (exclude_none = True )
837+
838+ app_name = "app0"
839+ user_id = "user0"
840+ session_id = "123"
841+ filename = "text_file.txt"
842+
843+ # Save the dict-shaped artifact
844+ await artifact_service .save_artifact (
845+ app_name = app_name ,
846+ user_id = user_id ,
847+ session_id = session_id ,
848+ filename = filename ,
849+ artifact = dict_artifact ,
850+ )
851+
852+ # Load and verify the artifact
853+ loaded = await artifact_service .load_artifact (
854+ app_name = app_name ,
855+ user_id = user_id ,
856+ session_id = session_id ,
857+ filename = filename ,
858+ )
859+ assert loaded is not None
860+ # GCS/File services may return text as inline_data bytes; accept either form.
861+ if loaded .text is not None :
862+ assert loaded .text == "Hello, World!"
863+ else :
864+ assert (
865+ loaded .inline_data is not None
866+ and loaded .inline_data .data == b"Hello, World!"
867+ )
0 commit comments