@@ -282,3 +282,110 @@ def test_to_sdk_task_no_output() -> None:
282282 assert sdk_task .id == 'task-3'
283283 assert sdk_task .metadata == {}
284284 assert sdk_task .artifacts == []
285+
286+
287+ def test_sdk_task_state_conversion_round_trip () -> None :
288+ for state in TaskState :
289+ stored_state = to_stored_task_state (state )
290+ round_trip_state = to_sdk_task_state (stored_state )
291+ assert round_trip_state == state
292+
293+
294+ def test_sdk_part_text_conversion_round_trip () -> None :
295+ sdk_part = Part (root = TextPart (text = 'hello world' ))
296+ stored_part = to_stored_part (sdk_part )
297+ round_trip_sdk_part = to_sdk_part (stored_part )
298+ assert round_trip_sdk_part == sdk_part
299+
300+
301+ def test_sdk_part_data_conversion_round_trip () -> None :
302+ # A DataPart is converted to `inline_data` in Vertex AI, which lacks the original
303+ # `DataPart` vs `FilePart` distinction. When reading it back from the stored
304+ # protocol format, it becomes a `FilePart` with base64-encoded `FileWithBytes`
305+ # and `mime_type="application/json"`.
306+ sdk_part = Part (root = DataPart (data = {'key' : 'value' }))
307+ stored_part = to_stored_part (sdk_part )
308+ round_trip_sdk_part = to_sdk_part (stored_part )
309+
310+ expected_b64 = base64 .b64encode (b'{"key": "value"}' ).decode ('utf-8' )
311+ assert round_trip_sdk_part == Part (
312+ root = FilePart (
313+ file = FileWithBytes (
314+ bytes = expected_b64 ,
315+ mime_type = 'application/json' ,
316+ )
317+ )
318+ )
319+
320+
321+ def test_sdk_part_file_bytes_conversion_round_trip () -> None :
322+ encoded_b64 = base64 .b64encode (b'test data' ).decode ('utf-8' )
323+ sdk_part = Part (
324+ root = FilePart (
325+ file = FileWithBytes (
326+ bytes = encoded_b64 ,
327+ mime_type = 'text/plain' ,
328+ )
329+ )
330+ )
331+ stored_part = to_stored_part (sdk_part )
332+ round_trip_sdk_part = to_sdk_part (stored_part )
333+ assert round_trip_sdk_part == sdk_part
334+
335+
336+ def test_sdk_part_file_uri_conversion_round_trip () -> None :
337+ sdk_part = Part (
338+ root = FilePart (
339+ file = FileWithUri (
340+ uri = 'gs://test-bucket/file.txt' ,
341+ mime_type = 'text/plain' ,
342+ )
343+ )
344+ )
345+ stored_part = to_stored_part (sdk_part )
346+ round_trip_sdk_part = to_sdk_part (stored_part )
347+ assert round_trip_sdk_part == sdk_part
348+
349+
350+ def test_sdk_artifact_conversion_round_trip () -> None :
351+ sdk_artifact = Artifact (
352+ artifact_id = 'art-123' ,
353+ parts = [Part (root = TextPart (text = 'part_1' ))],
354+ )
355+ stored_artifact = to_stored_artifact (sdk_artifact )
356+ round_trip_sdk_artifact = to_sdk_artifact (stored_artifact )
357+ assert round_trip_sdk_artifact == sdk_artifact
358+
359+
360+ def test_sdk_task_conversion_round_trip () -> None :
361+ sdk_task = Task (
362+ id = 'task-1' ,
363+ context_id = 'ctx-1' ,
364+ status = TaskStatus (state = TaskState .working ),
365+ metadata = {'foo' : 'bar' },
366+ artifacts = [
367+ Artifact (
368+ artifact_id = 'art-1' ,
369+ parts = [Part (root = TextPart (text = 'stuff' ))],
370+ )
371+ ],
372+ history = [
373+ # History is not yet implemented and later will be supported
374+ # via events.
375+ ],
376+ )
377+ stored_task = to_stored_task (sdk_task )
378+ # Simulate Vertex storing the ID in the fully qualified resource name.
379+ # The task ID during creation gets appended to the parent name.
380+ stored_task .name = (
381+ f'projects/p/locations/l/agentEngines/e/tasks/{ sdk_task .id } '
382+ )
383+
384+ round_trip_sdk_task = to_sdk_task (stored_task )
385+
386+ assert round_trip_sdk_task .id == sdk_task .id
387+ assert round_trip_sdk_task .context_id == sdk_task .context_id
388+ assert round_trip_sdk_task .status == sdk_task .status
389+ assert round_trip_sdk_task .metadata == sdk_task .metadata
390+ assert round_trip_sdk_task .artifacts == sdk_task .artifacts
391+ assert round_trip_sdk_task .history == []
0 commit comments