@@ -911,3 +911,131 @@ async def test_append_event_with_compaction_and_custom_metadata():
911911 # User custom_metadata is preserved without the internal _compaction key
912912 assert appended_event .custom_metadata == {'user_key' : 'user_value' }
913913 assert '_compaction' not in (appended_event .custom_metadata or {})
914+
915+
916+ @pytest .mark .asyncio
917+ @pytest .mark .usefixtures ('mock_get_api_client' )
918+ async def test_append_event_with_usage_metadata ():
919+ """usage_metadata round-trips through append_event and get_session."""
920+ session_service = mock_vertex_ai_session_service ()
921+ session = await session_service .get_session (
922+ app_name = '123' , user_id = 'user' , session_id = '1'
923+ )
924+ assert session is not None
925+
926+ event_to_append = Event (
927+ invocation_id = 'usage_invocation' ,
928+ author = 'model' ,
929+ timestamp = 1734005536.0 ,
930+ usage_metadata = genai_types .GenerateContentResponseUsageMetadata (
931+ prompt_token_count = 150 ,
932+ candidates_token_count = 50 ,
933+ total_token_count = 200 ,
934+ ),
935+ )
936+
937+ await session_service .append_event (session , event_to_append )
938+
939+ retrieved_session = await session_service .get_session (
940+ app_name = '123' , user_id = 'user' , session_id = '1'
941+ )
942+ assert retrieved_session is not None
943+
944+ appended_event = retrieved_session .events [- 1 ]
945+ assert appended_event .usage_metadata is not None
946+ assert appended_event .usage_metadata .prompt_token_count == 150
947+ assert appended_event .usage_metadata .candidates_token_count == 50
948+ assert appended_event .usage_metadata .total_token_count == 200
949+ # custom_metadata should remain None when only usage_metadata was stored
950+ assert appended_event .custom_metadata is None
951+
952+
953+ @pytest .mark .asyncio
954+ @pytest .mark .usefixtures ('mock_get_api_client' )
955+ async def test_append_event_with_usage_metadata_and_custom_metadata ():
956+ """Both usage_metadata and user custom_metadata survive the round-trip."""
957+ session_service = mock_vertex_ai_session_service ()
958+ session = await session_service .get_session (
959+ app_name = '123' , user_id = 'user' , session_id = '1'
960+ )
961+ assert session is not None
962+
963+ event_to_append = Event (
964+ invocation_id = 'usage_and_meta_invocation' ,
965+ author = 'model' ,
966+ timestamp = 1734005537.0 ,
967+ usage_metadata = genai_types .GenerateContentResponseUsageMetadata (
968+ prompt_token_count = 300 ,
969+ total_token_count = 400 ,
970+ ),
971+ custom_metadata = {'my_key' : 'my_value' },
972+ )
973+
974+ await session_service .append_event (session , event_to_append )
975+
976+ retrieved_session = await session_service .get_session (
977+ app_name = '123' , user_id = 'user' , session_id = '1'
978+ )
979+ assert retrieved_session is not None
980+
981+ appended_event = retrieved_session .events [- 1 ]
982+ # usage_metadata is restored
983+ assert appended_event .usage_metadata is not None
984+ assert appended_event .usage_metadata .prompt_token_count == 300
985+ assert appended_event .usage_metadata .total_token_count == 400
986+ # User custom_metadata is preserved without internal keys
987+ assert appended_event .custom_metadata == {'my_key' : 'my_value' }
988+ assert '_usage_metadata' not in (appended_event .custom_metadata or {})
989+
990+
991+ @pytest .mark .asyncio
992+ @pytest .mark .usefixtures ('mock_get_api_client' )
993+ async def test_append_event_with_usage_metadata_and_compaction ():
994+ """usage_metadata, compaction, and user custom_metadata all coexist."""
995+ session_service = mock_vertex_ai_session_service ()
996+ session = await session_service .get_session (
997+ app_name = '123' , user_id = 'user' , session_id = '1'
998+ )
999+ assert session is not None
1000+
1001+ compaction = EventCompaction (
1002+ start_timestamp = 500.0 ,
1003+ end_timestamp = 600.0 ,
1004+ compacted_content = genai_types .Content (
1005+ parts = [genai_types .Part (text = 'compacted' )]
1006+ ),
1007+ )
1008+ event_to_append = Event (
1009+ invocation_id = 'all_three_invocation' ,
1010+ author = 'model' ,
1011+ timestamp = 1734005538.0 ,
1012+ actions = EventActions (compaction = compaction ),
1013+ usage_metadata = genai_types .GenerateContentResponseUsageMetadata (
1014+ prompt_token_count = 1000 ,
1015+ candidates_token_count = 250 ,
1016+ total_token_count = 1250 ,
1017+ ),
1018+ custom_metadata = {'extra' : 'info' },
1019+ )
1020+
1021+ await session_service .append_event (session , event_to_append )
1022+
1023+ retrieved_session = await session_service .get_session (
1024+ app_name = '123' , user_id = 'user' , session_id = '1'
1025+ )
1026+ assert retrieved_session is not None
1027+
1028+ appended_event = retrieved_session .events [- 1 ]
1029+ # Compaction is restored
1030+ assert appended_event .actions .compaction is not None
1031+ assert appended_event .actions .compaction .start_timestamp == 500.0
1032+ assert appended_event .actions .compaction .end_timestamp == 600.0
1033+ # usage_metadata is restored
1034+ assert appended_event .usage_metadata is not None
1035+ assert appended_event .usage_metadata .prompt_token_count == 1000
1036+ assert appended_event .usage_metadata .candidates_token_count == 250
1037+ assert appended_event .usage_metadata .total_token_count == 1250
1038+ # User custom_metadata is preserved without internal keys
1039+ assert appended_event .custom_metadata == {'extra' : 'info' }
1040+ assert '_compaction' not in (appended_event .custom_metadata or {})
1041+ assert '_usage_metadata' not in (appended_event .custom_metadata or {})
0 commit comments