This change updates `RemoteA2AAgent` to extract and forward custom metadata from session events to the `a2a-sdk`'s `send_message` method. The metadata is looked for under the key `A2A_METADATA_PREFIX + "metadata"` within the `custom_metadata` of the relevant session events. The `a2a-sdk` dependency is also updated to a version that supports this feature.
This feature was added in v0.3.11 of the a2a-sdk library: https://github.com/a2aproject/a2a-python/releases/tag/v0.3.11
PiperOrigin-RevId: 831120978
This change introduces a shutdown lifecycle hook for plugins. The `PluginManager` now has an `async def shutdown()` method that will call `await plugin.shutdown()` on any registered plugins that implement the method. This is called from `Runner.close()`, allowing plugins to perform cleanup tasks like flushing logs or closing connections when the runner instance is being closed. This improves the reliability of plugins that perform background operations.
PiperOrigin-RevId: 831037737
The agent's instructions are updated to guide it to guess a positive integer, starting from 50 and decreasing, using the `guess_number_tool` until the target is found. The maximum number of retries for the `CustomRetryPlugin` is increased from 6 to 10.
Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 830984481
Merge https://github.com/google/adk-python/pull/2167, during which:
- Let the already added `max_query_result_rows` field cover for `max_downloaded_rows` field added in the PR
- Revert `max_rows` parameter added to execute_sql function, the tool config control should serve most practical use cases
- Keep the relevant tests for tool config
🤖 Generated with [Claude Code](https://claude.ai/code)
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2167 from lupuletic:feature/configurable-max-downloaded-rows 23c56905c297d7aec2be4f1eb86ea23c8178bf21
PiperOrigin-RevId: 830701093
Enhanced `save_artifact` in `callback_context.py` to accept `custom_metadata` and added `get_artifact_version` to retrieve artifact details.
Introduced a new sample, `context_offloading_with_artifact`, demonstrating how to use ADK artifacts to offload large data from the LLM context. The sample includes:
- `QueryLargeDataTool`: Generates mock sales reports, saves them as artifacts with custom metadata, and injects the artifact content into the LLM request immediately after creation.
- `CustomLoadArtifactsTool`: Provides summaries of available artifacts to the LLM based on metadata and loads artifact content on demand when `load_artifacts` is called.
Co-authored-by: Hangfei Lin <hangfei@google.com>
PiperOrigin-RevId: 830592786
Merge https://github.com/google/adk-python/pull/2889
# Implement Full async DatabaseSessionService
**Target Issue:** #1005
## Overview
This PR introduces an asynchronous implementation of the `DatabaseSessionService` with minimal breaking changes. The primary goal is to enable effective use of ADK in fully async environments and API endpoints while avoiding event loop blocking during database I/O operations.
## Changes
- Converted `DatabaseSessionService` to use async/await patterns throughout
## Testing Plan
The implementation has been tested following the project's contribution guidelines:
### Unit Tests
- All existing unit tests pass successfully
- Minor update to test requirements added to support `aiosqlite`
### Manual End-to-End Testing
- E2E tests performed using:
- **LLM Provider:** LiteLLM
- **Database:** PostgreSQL with `asyncpg` driver
```python
from google.adk.sessions.database_session_service import DatabaseSessionService
connection_string: str = (
"postgresql+asyncpg://PG_USER:PG_PSWD@PG_HOST:5432/PG_DB"
)
session_service: DatabaseSessionService = DatabaseSessionService(
db_url=connection_string
)
session = await session_service.create_session(
app_name="test_app", session_id="test_session", user_id="test_user"
)
assert session is not None
sessions = await session_service.list_sessions(app_name="test_app", user_id="test_user")
assert len(sessions.sessions) > 0
session = await session_service.get_session(
app_name="test_app", session_id="test_session", user_id="test_user"
)
assert session is not None
await session_service.delete_session(
app_name="test_app", session_id="test_session", user_id="test_user"
)
assert (
await session_service.get_session(
app_name="test_app", session_id="test_session", user_id="test_user"
)
is None
)
```
The implementation have been also tested using the following configurations for llm provider and Runner:
```python
def get_azure_openai_model(deployment_id: str | None = None) -> LiteLlm:
...
if not deployment_id:
deployment_id = os.getenv("AZURE_OPENAI_DEPLOYMENT_ID")
logger.info(f"Using Azure OpenAI deployment ID: {deployment_id}")
return LiteLlm(
model=f"azure/{os.getenv('AZURE_OPENAI_DEPLOYMENT_ID')}",
stream=True,
)
...
@staticmethod
def _get_runner(agent: Agent) -> Runner:
storage=DatabaseSessionService(db_url=get_pg_connection_string())
return Runner(
agent=agent,
app_name=APP_NAME,
session_service=storage,
)
...
async for event in self.runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=content,
run_config=(
RunConfig(
streaming_mode=StreamingMode.SSE, response_modalities=["TEXT"]
)
if stream
else RunConfig()
),
):
last_event = event
if stream:
yield event
...
```
## Breaking Changes
- Database connection string format may need updates for async drivers
Co-authored-by: Shangjie Chen <deanchen@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2889 from GitMarco27:feature/async_database_session_service e1b1b14934c1fb7975a6832cdd1549e94acab985
PiperOrigin-RevId: 830525148
The tool description now clarifies that `load_artifacts` should be called when a user uploads files, to make those artifacts accessible within the session.
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 830469802
The upper bound for the `fastapi` dependency is adjusted from `<1.119.0` to `<0.119.0`.
Close#3422
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 830465681
Update `_function_declaration_to_tool_param` to use `getattr` when accessing the `required` field within `function_declaration.parameters`. This prevents `AttributeError` when a `FunctionDeclaration`'s parameters schema does not include a `required` attribute
Close#2891
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 830225582
Merge https://github.com/google/adk-python/pull/3352
Replace send() method with send_realtime_input() to fix DeprecationWarning
### Link to Issue or Description of Change
**1. Link to an existing issue (if applicable):**
- Closes: #2393
### Testing Plan
Have run unit test for test_live_request_queue.py
**Unit Tests:**
- [ ] I have added or updated unit tests for my change.
- [x] All unit tests pass locally.
Summary :
tests/unittests/agents/test_live_request_queue.py::test_send_realtime PASSED [100%]
**Manual End-to-End (E2E) Tests:**
### Checklist
- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing unit tests pass locally with my changes.
- [x] I have manually tested my changes end-to-end.
- [ ] Any dependent changes have been merged and published in downstream modules.
Tested both API variants.
Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3352 from SanjaySiddharth:fix-dep-session.send b23b641a63ff20d1f2dbd9abd519f8facf0aab77
PiperOrigin-RevId: 830182844
Merge https://github.com/google/adk-python/pull/2875
related: https://github.com/google/adk-python/issues/2876
## Reason for this change:
Currently, there is no direct way to access the user_id from within agent contexts, plugins, or callbacks. This limitation prevents several important use cases:
1. **User-specific logging and tracing**: When debugging or monitoring agent behavior, it's crucial to associate actions with specific users for better observability.
2. **User-scoped operations**: Plugins and callbacks often need to perform user-specific operations, such as accessing user-specific resources or applying user-level configurations.
3. **Session management**: The user_id is a key component of session identification, but it's not accessible through the ReadonlyContext interface, requiring workarounds to access it.
## Changes made:
- Added a `user_id` property to the `ReadonlyContext` class in `src/google/adk/agents/readonly_context.py`
- The property exposes the user_id from the underlying invocation context as a readonly field
## Impact:
This change will:
- Enable plugins and callbacks to access the current user's ID directly through the context
- Improve logging and tracing capabilities by allowing user-specific tracking
- Simplify code that needs to perform user-scoped operations without requiring access to internal implementation details
- Maintain backward compatibility as this is an additive change
### Before:
- No direct way to access user_id from ReadonlyContext
### After:
```python
@property
def user_id(self) -> str:
"""The id of the user. READONLY field."""
return self._invocation_context.user_id
```
This is a non-breaking change that adds a new readonly property to the existing ReadonlyContext interface.
Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2875 from ammmr:ammmr-add-user_id-property-to-readonly-context 771734ebf39cb0748a5dc826436236268fbb58f8
PiperOrigin-RevId: 830170908
The new Sqlite version has fixed schema and use a single column to store Event data, this should avoid DB migration for future add to the Event object.
- This change introduces `SqliteSessionService`, an asynchronous session service using `aiosqlite` that stores event data as JSON within SQLite.
- A migration script, `migrate_from_sqlalchemy_sqlite.py`, is included to transition data from the older SQLAlchemy-based SQLite schema to this new format.
- The CLI service registry is updated to use SqliteSessionService for sqlite:// URIs.
- Throw error when user trying to access a legacy DB and advice the user to do the migration.
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 829971174
NOTE:
- This is the expected behavior, so that SSE streaming and non-streaming behaviors are idential to each other in the persisted session.
- The underlying google_llm.py hasn't been fully comply with this behavior, which will be addressed in future changes.
Co-authored-by: Wei Sun (Jack) <weisun@google.com>
PiperOrigin-RevId: 829878175
This change updates the `BigQueryAgentAnalyticsPlugin` (formerly in `bigquery_logging_plugin.py`) to perform BigQuery writes asynchronously in background tasks, preventing blocking of the main agent execution flow. Initialization of BigQuery clients and table creation is also made asynchronous. The content logged in various callbacks has been streamlined and simplified. The plugin file has been renamed to `bigquery_agent_analytics_plugin.py` to match the class name.
PiperOrigin-RevId: 829603260
Merge https://github.com/google/adk-python/pull/2600
**Reason for this change:**
Currently, the detailed llm_request object is logged at the info level when connecting to the live API. This causes two main issues:
1. Excessive log output with large instructions: When instructions are large, the unstructured log output becomes problematic. Since the logs are not structured, any
newline characters in the instruction content cause each line to be output as a separate log entry, resulting in massive log output that floods the logging system.
2. Unnecessary verbosity in production: The detailed request information is primarily useful for debugging purposes and should not appear in standard info-level logs,
especially when connections are established frequently.
**Changes made:**
- Changed logger.info to logger.debug in src/google/adk/models/google_llm.py at line 294 for the live connection logging statement.
**Impact:**
This change will:
- Reduce log noise in production environments where info-level logging is enabled
- Keep the detailed connection information available when debug-level logging is needed for troubleshooting
- Improve log readability by showing only essential information at the info level
**Before:**
logger.info('Connecting to live with llm_request:%s', llm_request)
**After:**
logger.debug('Connecting to live with llm_request:%s', llm_request)
This is a non-breaking change that only affects logging verbosity.
Co-authored-by: Hangfei Lin <hangfei@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2600 from ammmr:ammmr-info-to-debug-connect-log 36eab157f5d5ca00b44ee6ad8a4e1aeec871600c
PiperOrigin-RevId: 829596092
We need to set project and location parameters when we instantiate the Google GenAI library Client for VertexAi. This used to work before through environment variables but it doesn't seem to work anymore. So we are updating the `ApigeeLlm` wrapper to read from `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` environment variables and pass to the Client constructor.
PiperOrigin-RevId: 829569362