** Description **
When you store an artifact Part.from_text() with GcsArtifactService, the artifact.text attribute is None after loading the artifact again. The data is however present in artifact.inline_data.
I would have expected the text attribute to contain the text.
To Reproduce
See this adapted weather sample to demonstrate the issue.
importosimportasynciofromgoogle.adk.agentsimportAgentfromgoogle.adk.sessionsimportInMemorySessionService, Sessionfromgoogle.adk.artifactsimportGcsArtifactServicefromgoogle.adk.runnersimportRunnerfromgoogle.adk.toolsimportBaseToolfromgoogle.adk.tools.tool_contextimportToolContextfromgoogle.genaiimporttypesimportloggingimportjsonlogging.basicConfig(level=logging.ERROR)
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] ="True"os.environ["GOOGLE_CLOUD_PROJECT"] ="your-favourite-project"os.environ["GOOGLE_CLOUD_LOCATION"] ="your-region"ARTIFACTS_BUCKET="your-favourite-bucket"AGENT_MODEL="gemini-2.5-flash-lite"APP_NAME="weather_tutorial_app"USER_ID="user_1"SESSION_ID="session_001"# Using a fixed ID for simplicitydefget_weather(city: str) ->dict:
"""Retrieves the current weather report for a specified city. Args: city (str): The name of the city (e.g., "New York", "London", "Tokyo"). Returns: dict: A dictionary containing the weather information. Includes a 'status' key ('success' or 'error'). If 'success', includes a 'report' key with weather details. If 'error', includes an 'error_message' key. """city_normalized=city.lower().replace(" ", "")
mock_weather_db= {
"newyork": {"status": "success", "report": "The weather in New York is sunny with a temperature of 25°C."},
"london": {"status": "success", "report": "It's cloudy in London with a temperature of 15°C."},
"tokyo": {"status": "success", "report": "Tokyo is experiencing light rain and a temperature of 18°C."},
}
ifcity_normalizedinmock_weather_db:
returnmock_weather_db[city_normalized]
else:
return {"status": "error", "error_message": f"Sorry, I don't have weather information for '{city}'."}
asyncdefcollect_artifacts(tool:BaseTool, args, tool_context:ToolContext, tool_response):
iftool.name=='transfer_to_agent':
returnawaittool_context.save_artifact('weather', types.Part.from_text(text=json.dumps(tool_response)))
root_agent=Agent(
name="weather_agent",
model=AGENT_MODEL,
description="The weather agent. Answers questions about the weather in certain places.",
instruction="You are the Weather Agent. Your primary responsibility is to provide weather information. ""Use the 'get_weather' tool for specific weather requests (e.g., 'weather in London'). ""For anything else, respond appropriately or state you cannot handle it.",
tools=[get_weather],
after_tool_callback=collect_artifacts
)
asyncdefrun_agent(runner: Runner, session: Session, content):
print(" ================================= ")
final_response_text=''deffrom_content(content: types.Part):
ifnotcontent:
return'<None>'else:
ifcontent.text:
returncontent.text.replace('\n', '\\n')
else:
return'<data structure>'asyncforeventinrunner.run_async(user_id=session.user_id, session_id=session.id, new_message=content):
ifeventandevent.contentandevent.content.parts:
forpartinevent.content.parts:
ifpart.thought:
event_type='Thought'elifpart.text:
event_type='Text'elifpart.function_call:
event_type='Function Call'elifpart.function_response:
event_type='Function Response'else:
event_type='Other event'print(f"--> [Event] Author: {event.author}, Type: {event_type}, Final: {event.is_final_response()}, Actions: {event.actionsisnotNone}, Content: {from_content(part)}")
print()
ifevent_type=='Text'andevent.is_final_response() andpart.text:
final_response_text+=f'{event.author}: {part.text} 'ifevent.is_final_response() andevent.actionsandevent.actions.escalate:
final_response_text=f"Agent escalated: {event.error_messageor'No specific message.'}"print(f"<<< Agent Response: {final_response_textiflen(final_response_text) else'Could not identify final response.'}")
ifrunner.artifact_service:
ifartifact:=awaitrunner.artifact_service.load_artifact(app_name=session.app_name, user_id=session.user_id, session_id=session.id, filename='weather'):
print(f'Weather artifact text contains: {artifact.text}')
print(f'inline data: {artifact.inline_data}')
asyncdefmain():
session_service=InMemorySessionService()
session=awaitsession_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner=Runner(agent=root_agent, app_name=session.app_name, session_service=session_service, artifact_service=GcsArtifactService(bucket_name=ARTIFACTS_BUCKET))
content=types.Content(role='user', parts=[types.Part(text='Tell me about the weather in New York')])
awaitrun_agent(runner, session, content)
if__name__=="__main__":
try:
asyncio.run(main())
exceptExceptionase:
print(f"An error occurred: {e}")Expected behavior
Weather artifact text should be json string of function calling result instead of None.
Desktop (please complete the following information):
- Mac OS
- Python 3.11.9
- ADK 1.16.0 (also tested in 1.15.1)
Model Information:
- gemini-2.5-flash-lite (tested with different models, not relevant)
- Vertex AI mode
** Description **
When you store an artifact Part.from_text() with GcsArtifactService, the artifact.text attribute is None after loading the artifact again. The data is however present in artifact.inline_data.
I would have expected the text attribute to contain the text.
To Reproduce
See this adapted weather sample to demonstrate the issue.
Expected behavior
Weather artifact text should be json string of function calling result instead of None.
Desktop (please complete the following information):
Model Information: