This CL fixes several bugs in the BigQuery Agent Analytics plugin and refactors the internal data-passing pattern for better type safety and maintainability.
- **Stale Loop State Validation:** Use `loop.is_closed()` — a public, reliable API — to detect and clean up stale asyncio loop states in `_batch_processor_prop`, `_get_loop_state`, and `flush`. The previous approach used `asyncio.Queue._loop` which is `None` on Python 3.10+, causing the check to always treat states as stale.
- **Quota Project ID Fallback:** Remove the `or project_id` fallback when setting `quota_project_id` on `BigQueryWriteAsyncClient`. This fixes Workload Identity Federation flows where the federated identity lacks `serviceusage.services.use` on the quota project.
- **Kwargs Passthrough:** Pass `**kwargs` through to `_log_event` in all callbacks. Previously only model callbacks forwarded them, causing custom attributes (e.g. `customer_id`) to silently drop for agent, tool, run, and error events.
- **State Delta Logging:** Replace the dead `on_state_change_callback` (never invoked by the framework) with `on_event_callback`, which is already dispatched by the runner for every event. Remove duplicate `STATE_DELTA` logging from `after_tool_callback`.
- **EventData Dataclass:** Replace the `**kwargs`-as-data-bus pattern in `_log_event` with an explicit `EventData` dataclass. This makes the interface self-documenting, catches typos at construction time, and eliminates shared dict mutation across `_resolve_span_ids`, `_extract_latency`, and `_enrich_attributes`. All 12 callback call sites now construct typed `EventData` instances.
- **Multi-Subagent Tool Logging Tests:** Add `TestMultiSubagentToolLogging` (6 tests) verifying that tool events are correctly attributed to subagents in multi-turn, multi-agent scenarios. Total tests: 111 (up from 60).
Co-authored-by: Haiyuan Cao <haiyuan@google.com>
PiperOrigin-RevId: 871381533
When we added the session_lock_map, it resulted in pickling errors during Agent Engine deployment. To fix, we implemented custom getstate and setstate methods to exclude the lock map lock and session lock map from pickling. Closes https://github.com/google/adk-python/issues/4486.
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 871056554
meanwhile also centralize input-stream creation in registration
Move the LiveRequestQueue stream creation from _call_live
(function_tool.py) to the lazy registration block in
_process_function_live_helper (functions.py). This centralizes the
input_stream: LiveRequestQueue annotation check and stream creation
in one place, and ensures the stream is also recreated on
re-invocation after stop_streaming resets it to None.
_call_live now simply passes the existing .stream if set, without
needing to know about LiveRequestQueue at all.
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 869935204
add_events_to_memory now supports memory_write_mode to select generate (event-based extraction/consolidation) or create (direct raw fact writes via memory_facts). This now lets custom memory pipelines while keeping generate as the default path
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 869897256
Using one lock and checking for tables creation instead of schema version.
Closes issue #4445
Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 869808097
Merge https://github.com/google/adk-python/pull/4185
**Description**
This PR resolves#4100 by making the `new_message` field optional in the `RunAgentRequest` model.
**The Problem:**
When attempting to resume an agent session via the FastAPI web server, the request would fail with a `422 Unprocessable Entity` if `new_message` was omitted. This prevented "resume-only" workflows where a user just wants to wake up an existing session.
**The Solution:**
Updated `RunAgentRequest.new_message` to be `Optional[types.Content] = None`. The underlying `runner.run_async` logic already supports `None` for resuming purposes, so no further logic changes were required.
**Verification:**
Verified that `RunAgentRequest` now validates successfully when `new_message` is missing, defaulting the field to `None`.
Co-authored-by: Liang Wu <wuliang@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4185 from Akshat8510:fix/fastapi-resume-4100 b6d252636aa5f96186507fccf47a278fe733a362
PiperOrigin-RevId: 869761179
previously we only register streaming tool that accept stream input at runner, now uniformly register all streaming tool at runner.
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 869447996
currently we started to relay live request to streaming tool even when the tool was not called yet.
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 869421826
This allows users to load skills from a directory and pass it into the SkillToolset constructor.
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 868929937
Merge https://github.com/google/adk-python/pull/4435
### Link to Issue or Description of Change
- Closes: #4302
**Problem:**
`VertexAiSessionService.list_sessions()` only returns the first ~100 sessions. The `sessions_iterator` from `api_client.agent_engines.sessions.list()` is an `AsyncPager` — it implements `__aiter__`/`__anext__` for fetching subsequent pages, but the code uses a plain `for` loop which only calls `__iter__`/`__next__`, so it never fetches beyond the first page.
**Solution:**
Changed `for api_session in sessions_iterator` to `async for api_session in sessions_iterator` so the `AsyncPager` actually paginates. Updated the test mock to return an `AsyncIterableList` (supports both sync and async iteration) instead of a bare list, so the tests properly simulate real `AsyncPager` behaviour.
### Testing Plan
**Unit Tests:**
```
$ pytest tests/unittests/sessions/
115 passed, 1 warning in 2.25s
```
The existing `test_list_sessions`, `test_list_sessions_with_pagination`, and `test_list_sessions_all_users` all continue to pass with the updated mock.
Co-authored-by: Liang Wu <wuliang@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/4435 from anmolg1997:fix/vertex-ai-session-service-pagination 14c71b607ecbf2215f4b9ba6eb4b0ff6b9eaf740
PiperOrigin-RevId: 868466166
Sessions were being erroneously cached and reused across different asyncio event loops, causing "Event loop is closed" in environments with transient loops. This updates the session caching to be loop-aware: before reusing a cached session, check that the stored loop matches the current loop. Also, if session is disconnected and loops do not match, discard the cached entry without calling aclose().
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 868380746