@@ -270,6 +270,7 @@ async def append_event(self, session: Session, event: Event) -> Event:
270270
271271 reasoning_engine_id = self ._get_reasoning_engine_id (session .app_name )
272272
273+ # Build config (Monolithic approach)
273274 config = {}
274275 if event .content :
275276 config ['content' ] = event .content .model_dump (
@@ -286,9 +287,6 @@ async def append_event(self, session: Session, event: Event) -> Event:
286287 k : json .loads (v .model_dump_json (exclude_none = True , by_alias = True ))
287288 for k , v in event .actions .requested_auth_configs .items ()
288289 },
289- # TODO: add requested_tool_confirmations, agent_state once
290- # they are available in the API.
291- # Note: compaction is stored via event_metadata.custom_metadata.
292290 }
293291 if event .error_code :
294292 config ['error_code' ] = event .error_code
@@ -311,10 +309,8 @@ async def append_event(self, session: Session, event: Event) -> Event:
311309 metadata_dict ['grounding_metadata' ] = event .grounding_metadata .model_dump (
312310 exclude_none = True , mode = 'json'
313311 )
314- # Store compaction data in custom_metadata since the Vertex AI service
315- # does not yet support the compaction field.
316- # TODO: Stop writing to custom_metadata once the Vertex AI service
317- # supports the compaction field natively in EventActions.
312+
313+ # ALWAYS write to custom_metadata
318314 if event .actions and event .actions .compaction :
319315 compaction_dict = event .actions .compaction .model_dump (
320316 exclude_none = True , mode = 'json'
@@ -324,8 +320,6 @@ async def append_event(self, session: Session, event: Event) -> Event:
324320 key = _COMPACTION_CUSTOM_METADATA_KEY ,
325321 value = compaction_dict ,
326322 )
327- # Store usage_metadata in custom_metadata since the Vertex AI service
328- # does not persist it in EventMetadata.
329323 if event .usage_metadata :
330324 usage_dict = event .usage_metadata .model_dump (
331325 exclude_none = True , mode = 'json'
@@ -335,7 +329,12 @@ async def append_event(self, session: Session, event: Event) -> Event:
335329 key = _USAGE_METADATA_CUSTOM_METADATA_KEY ,
336330 value = usage_dict ,
337331 )
332+
338333 config ['event_metadata' ] = metadata_dict
334+
335+ # Persist the full event state using raw_event. If the client-side SDK
336+ # does not support this field, it will raise a ValidationError, and we
337+ # will fall back to legacy field-based storage.
339338 config ['raw_event' ] = event .model_dump (
340339 exclude_none = True ,
341340 mode = 'json' ,
@@ -345,7 +344,8 @@ async def append_event(self, session: Session, event: Event) -> Event:
345344 # Retry without raw_event if client side validation fails for older SDK
346345 # versions.
347346 async with self ._get_api_client () as api_client :
348- try :
347+
348+ async def _do_append (cfg : dict [str , Any ]):
349349 await api_client .agent_engines .sessions .events .append (
350350 name = (
351351 f'reasoningEngines/{ reasoning_engine_id } /sessions/{ session .id } '
@@ -355,22 +355,16 @@ async def append_event(self, session: Session, event: Event) -> Event:
355355 timestamp = datetime .datetime .fromtimestamp (
356356 event .timestamp , tz = datetime .timezone .utc
357357 ),
358- config = config ,
358+ config = cfg ,
359359 )
360+
361+ try :
362+ await _do_append (config )
360363 except pydantic .ValidationError :
364+ logger .warning ('Vertex SDK does not support raw_event, falling back.' )
361365 if 'raw_event' in config :
362366 del config ['raw_event' ]
363- await api_client .agent_engines .sessions .events .append (
364- name = (
365- f'reasoningEngines/{ reasoning_engine_id } /sessions/{ session .id } '
366- ),
367- author = event .author ,
368- invocation_id = event .invocation_id ,
369- timestamp = datetime .datetime .fromtimestamp (
370- event .timestamp , tz = datetime .timezone .utc
371- ),
372- config = config ,
373- )
367+ await _do_append (config )
374368 return event
375369
376370 def _get_reasoning_engine_id (self , app_name : str ):
@@ -429,8 +423,8 @@ def _get_raw_event(api_event_obj: Any) -> dict[str, Any] | None:
429423
430424def _from_api_event (api_event_obj : vertexai .types .SessionEvent ) -> Event :
431425 """Converts an API event object to an Event object."""
432- # Read event data from raw_event first before falling back to top level
433- # fields.
426+ # Prioritize reading from raw_event to restore full state. Fall back to
427+ # top-level fields for older data that lacks raw_event .
434428 raw_event_dict = _get_raw_event (api_event_obj )
435429 if raw_event_dict :
436430 event_dict = copy .deepcopy (raw_event_dict )
@@ -439,8 +433,9 @@ def _from_api_event(api_event_obj: vertexai.types.SessionEvent) -> Event:
439433 'id' : api_event_obj .name .split ('/' )[- 1 ],
440434 'invocation_id' : getattr (api_event_obj , 'invocation_id' , None ),
441435 'author' : getattr (api_event_obj , 'author' , None ),
442- 'timestamp' : timestamp_obj .timestamp () if timestamp_obj else None ,
443436 })
437+ if timestamp_obj :
438+ event_dict ['timestamp' ] = timestamp_obj .timestamp ()
444439 return Event .model_validate (event_dict )
445440
446441 actions = getattr (api_event_obj , 'actions' , None )
@@ -514,6 +509,13 @@ def _from_api_event(api_event_obj: vertexai.types.SessionEvent) -> Event:
514509 usage_metadata_data
515510 )
516511
512+ timestamp_obj = getattr (api_event_obj , 'timestamp' , None )
513+ timestamp = (
514+ timestamp_obj .timestamp ()
515+ if timestamp_obj
516+ else datetime .datetime .now (datetime .timezone .utc ).timestamp ()
517+ )
518+
517519 return Event (
518520 id = api_event_obj .name .split ('/' )[- 1 ],
519521 invocation_id = api_event_obj .invocation_id ,
@@ -522,7 +524,7 @@ def _from_api_event(api_event_obj: vertexai.types.SessionEvent) -> Event:
522524 content = _session_util .decode_model (
523525 getattr (api_event_obj , 'content' , None ), types .Content
524526 ),
525- timestamp = api_event_obj . timestamp . timestamp () ,
527+ timestamp = timestamp ,
526528 error_code = getattr (api_event_obj , 'error_code' , None ),
527529 error_message = getattr (api_event_obj , 'error_message' , None ),
528530 partial = partial ,
0 commit comments