Skip to content

Artifact stored as text is not retrieved as text #3157

Description

@hcsama

** 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

Metadata

Metadata

Labels

good first issue[Community] This issue is good for newcomers to participateneeds review[Status] The PR/issue is awaiting review from the maintainerservices[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions