1616from abc import ABC
1717from abc import abstractmethod
1818from datetime import datetime
19+ import logging
1920from typing import Any
2021from typing import Optional
22+ from typing import Union
2123
2224from google .genai import types
2325from pydantic import alias_generators
2426from pydantic import BaseModel
2527from pydantic import ConfigDict
2628from pydantic import Field
2729
30+ logger = logging .getLogger ("google_adk." + __name__ )
31+
2832
2933class ArtifactVersion (BaseModel ):
3034 """Metadata describing a specific version of an artifact."""
@@ -60,6 +64,26 @@ class ArtifactVersion(BaseModel):
6064 )
6165
6266
67+ def ensure_part (artifact : Union [types .Part , dict [str , Any ]]) -> types .Part :
68+ """Normalizes an artifact to a ``types.Part`` instance.
69+
70+ External callers may provide artifacts as
71+ plain dictionaries with camelCase keys (``inlineData``) instead of properly
72+ deserialized ``types.Part`` objects. ``model_validate`` handles both
73+ camelCase and snake_case dictionaries transparently via Pydantic aliases.
74+
75+ Args:
76+ artifact: A ``types.Part`` instance or a dictionary representation.
77+
78+ Returns:
79+ A validated ``types.Part`` instance.
80+ """
81+ if isinstance (artifact , dict ):
82+ logger .debug ("Normalizing artifact dict to types.Part: %s" , list (artifact ))
83+ return types .Part .model_validate (artifact )
84+ return artifact
85+
86+
6387class BaseArtifactService (ABC ):
6488 """Abstract base class for artifact services."""
6589
@@ -70,7 +94,7 @@ async def save_artifact(
7094 app_name : str ,
7195 user_id : str ,
7296 filename : str ,
73- artifact : types .Part ,
97+ artifact : Union [ types .Part , dict [ str , Any ]] ,
7498 session_id : Optional [str ] = None ,
7599 custom_metadata : Optional [dict [str , Any ]] = None ,
76100 ) -> int :
@@ -84,10 +108,12 @@ async def save_artifact(
84108 app_name: The app name.
85109 user_id: The user ID.
86110 filename: The filename of the artifact.
87- artifact: The artifact to save. If the artifact consists of `file_data`,
88- the artifact service assumes its content has been uploaded separately,
89- and this method will associate the `file_data` with the artifact if
90- necessary.
111+ artifact: The artifact to save. Accepts a ``types.Part`` instance or a
112+ plain dictionary (camelCase or snake_case keys) which will be
113+ normalized via ``ensure_part``. If the artifact consists of
114+ ``file_data``, the artifact service assumes its content has been
115+ uploaded separately, and this method will associate the ``file_data``
116+ with the artifact if necessary.
91117 session_id: The session ID. If `None`, the artifact is user-scoped.
92118 custom_metadata: custom metadata to associate with the artifact.
93119
0 commit comments