From 78b1c4e56a321e738b19c05669931df34239e7be Mon Sep 17 00:00:00 2001 From: Marcus Motill Date: Mon, 19 Jan 2026 02:32:45 +0000 Subject: [PATCH 1/5] feat: extract durable runtime support changes Moves runtime, event, and session service changes to a separate branch. These changes support deterministic execution required for durable integrations. --- pr_description.md | 50 +++++++++++++++++ src/google/adk/events/event.py | 5 +- src/google/adk/runtime.py | 53 ++++++++++++++++++ .../adk/sessions/in_memory_session_service.py | 5 +- tests/unittests/test_runtime.py | 56 +++++++++++++++++++ 5 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 pr_description.md create mode 100644 src/google/adk/runtime.py create mode 100644 tests/unittests/test_runtime.py diff --git a/pr_description.md b/pr_description.md new file mode 100644 index 0000000000..bf1c961c47 --- /dev/null +++ b/pr_description.md @@ -0,0 +1,50 @@ +# Refactor: Temporal Integration Cleanup & Documentation + +## Summary +This PR refactors the `TemporalPlugin` to remove implicit default configurations and adds comprehensive documentation for the ADK-Temporal integration. The goal is to make the integration stricter, clearer, and easier to debug by forcing explicit configuration from the user. + +## Key Changes + +### 1. `TemporalPlugin` Refactor +* **Removed Implicit Defaults**: The plugin no longer contains hardcoded default timeouts (previously 60s) or retry policies. Users must now explicitly provide `activity_options` when initializing the plugin. +* **Removed Magic Model Resolution**: Removed the fallback logic that attempted to guess the model name from the agent context if missing. Failed requests will now raise a clear `ValueError` instead of behaving unpredictably. +* **Simplified `before_model_callback`**: The interception hook is now a pure passthrough to `workflow.execute_activity` without additional logic or conditional checks (`workflow.in_workflow()` check removed to fail fast if misused). + +### 2. Documentation (`README.md`) +* Added a new `src/google/adk/integrations/temporal/README.md`. +* **Internals Explained**: Documented the "Interception Flow" (how it short-circuits ADK) and "Dynamic Activities" (how `AgentName.generate_content` works without registration). +* **Clear Usage Examples**: provided distinct, copy-pasteable snippets for **Agent Setup** (Workflow side) and **Worker Setup**, clarifying the role of `AdkWorkerPlugin`. + +## Impact +* **Safety**: Impossible to accidentally run with unsafe defaults. +* **Debuggability**: Errors are now explicit (missing options, missing model). +* **Clarity**: Users understand exactly *why* they are adding plugins (serialization, runtime determinism, activity routing) via the new docs. + +## Usage Example + +### Agent Side +```python +# You MUST now provide explicit options +activity_options = { + "start_to_close_timeout": timedelta(minutes=1), + "retry_policy": RetryPolicy(maximum_attempts=3) +} + +agent = Agent( + model="gemini-2.5-pro", + plugins=[ + # Routes model calls to Temporal Activities + TemporalPlugin(activity_options=activity_options) + ] +) +``` + +### Worker Side +```python +worker = Worker( + client, + task_queue="my-queue", + # Configures ADK Runtime, Pydantic Support & Unsandboxed Runner + plugins=[AdkWorkerPlugin()] +) +``` diff --git a/src/google/adk/events/event.py b/src/google/adk/events/event.py index 2c6a6cd66c..cca65295cb 100644 --- a/src/google/adk/events/event.py +++ b/src/google/adk/events/event.py @@ -18,6 +18,7 @@ from typing import Optional import uuid +from google.adk import runtime from google.genai import types from pydantic import alias_generators from pydantic import ConfigDict @@ -70,7 +71,7 @@ class Event(LlmResponse): # Do not assign the ID. It will be assigned by the session. id: str = '' """The unique identifier of the event.""" - timestamp: float = Field(default_factory=lambda: datetime.now().timestamp()) + timestamp: float = Field(default_factory=lambda: runtime.get_time()) """The timestamp of the event.""" def model_post_init(self, __context): @@ -125,4 +126,4 @@ def has_trailing_code_execution_result( @staticmethod def new_id(): - return str(uuid.uuid4()) + return runtime.new_uuid() diff --git a/src/google/adk/runtime.py b/src/google/adk/runtime.py new file mode 100644 index 0000000000..edd6be9045 --- /dev/null +++ b/src/google/adk/runtime.py @@ -0,0 +1,53 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Runtime module for abstracting system primitives like time and UUIDs.""" + +import time +import uuid +from typing import Callable + +_time_provider: Callable[[], float] = time.time +_id_provider: Callable[[], str] = lambda: str(uuid.uuid4()) + + +def set_time_provider(provider: Callable[[], float]) -> None: + """Sets the provider for the current time. + + Args: + provider: A callable that returns the current time in seconds since the + epoch. + """ + global _time_provider + _time_provider = provider + + +def set_id_provider(provider: Callable[[], str]) -> None: + """Sets the provider for generating unique IDs. + + Args: + provider: A callable that returns a unique ID string. + """ + global _id_provider + _id_provider = provider + + +def get_time() -> float: + """Returns the current time in seconds since the epoch.""" + return _time_provider() + + +def new_uuid() -> str: + """Returns a new unique ID.""" + return _id_provider() diff --git a/src/google/adk/sessions/in_memory_session_service.py b/src/google/adk/sessions/in_memory_session_service.py index d782072d6a..b856f3b584 100644 --- a/src/google/adk/sessions/in_memory_session_service.py +++ b/src/google/adk/sessions/in_memory_session_service.py @@ -22,6 +22,7 @@ from typing_extensions import override +from google.adk import runtime from . import _session_util from ..errors.already_exists_error import AlreadyExistsError from ..events.event import Event @@ -108,14 +109,14 @@ def _create_session_impl( session_id = ( session_id.strip() if session_id and session_id.strip() - else str(uuid.uuid4()) + else runtime.new_uuid() ) session = Session( app_name=app_name, user_id=user_id, id=session_id, state=session_state or {}, - last_update_time=time.time(), + last_update_time=runtime.get_time(), ) if app_name not in self.sessions: diff --git a/tests/unittests/test_runtime.py b/tests/unittests/test_runtime.py new file mode 100644 index 0000000000..cde013631b --- /dev/null +++ b/tests/unittests/test_runtime.py @@ -0,0 +1,56 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for the runtime module.""" + +import time +import uuid +import unittest +from unittest.mock import MagicMock, patch + +from google.adk import runtime + + +class TestRuntime(unittest.TestCase): + + def tearDown(self): + # Reset providers to default after each test + runtime.set_time_provider(time.time) + runtime.set_id_provider(lambda: str(uuid.uuid4())) + + def test_default_time_provider(self): + # Verify it returns a float that is close to now + now = time.time() + rt_time = runtime.get_time() + self.assertIsInstance(rt_time, float) + self.assertAlmostEqual(rt_time, now, delta=1.0) + + def test_default_id_provider(self): + # Verify it returns a string uuid + uid = runtime.new_uuid() + self.assertIsInstance(uid, str) + # Should be parseable as uuid + uuid.UUID(uid) + + def test_custom_time_provider(self): + # Test override + mock_time = 123456789.0 + runtime.set_time_provider(lambda: mock_time) + self.assertEqual(runtime.get_time(), mock_time) + + def test_custom_id_provider(self): + # Test override + mock_id = "test-id-123" + runtime.set_id_provider(lambda: mock_id) + self.assertEqual(runtime.new_uuid(), mock_id) From 57ddf8fb82ef1c35c2de010e4a8c4676417755ea Mon Sep 17 00:00:00 2001 From: Marcus Motill Date: Mon, 19 Jan 2026 02:34:46 +0000 Subject: [PATCH 2/5] chore: remove pr_description.md --- pr_description.md | 50 ----------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 pr_description.md diff --git a/pr_description.md b/pr_description.md deleted file mode 100644 index bf1c961c47..0000000000 --- a/pr_description.md +++ /dev/null @@ -1,50 +0,0 @@ -# Refactor: Temporal Integration Cleanup & Documentation - -## Summary -This PR refactors the `TemporalPlugin` to remove implicit default configurations and adds comprehensive documentation for the ADK-Temporal integration. The goal is to make the integration stricter, clearer, and easier to debug by forcing explicit configuration from the user. - -## Key Changes - -### 1. `TemporalPlugin` Refactor -* **Removed Implicit Defaults**: The plugin no longer contains hardcoded default timeouts (previously 60s) or retry policies. Users must now explicitly provide `activity_options` when initializing the plugin. -* **Removed Magic Model Resolution**: Removed the fallback logic that attempted to guess the model name from the agent context if missing. Failed requests will now raise a clear `ValueError` instead of behaving unpredictably. -* **Simplified `before_model_callback`**: The interception hook is now a pure passthrough to `workflow.execute_activity` without additional logic or conditional checks (`workflow.in_workflow()` check removed to fail fast if misused). - -### 2. Documentation (`README.md`) -* Added a new `src/google/adk/integrations/temporal/README.md`. -* **Internals Explained**: Documented the "Interception Flow" (how it short-circuits ADK) and "Dynamic Activities" (how `AgentName.generate_content` works without registration). -* **Clear Usage Examples**: provided distinct, copy-pasteable snippets for **Agent Setup** (Workflow side) and **Worker Setup**, clarifying the role of `AdkWorkerPlugin`. - -## Impact -* **Safety**: Impossible to accidentally run with unsafe defaults. -* **Debuggability**: Errors are now explicit (missing options, missing model). -* **Clarity**: Users understand exactly *why* they are adding plugins (serialization, runtime determinism, activity routing) via the new docs. - -## Usage Example - -### Agent Side -```python -# You MUST now provide explicit options -activity_options = { - "start_to_close_timeout": timedelta(minutes=1), - "retry_policy": RetryPolicy(maximum_attempts=3) -} - -agent = Agent( - model="gemini-2.5-pro", - plugins=[ - # Routes model calls to Temporal Activities - TemporalPlugin(activity_options=activity_options) - ] -) -``` - -### Worker Side -```python -worker = Worker( - client, - task_queue="my-queue", - # Configures ADK Runtime, Pydantic Support & Unsandboxed Runner - plugins=[AdkWorkerPlugin()] -) -``` From 08d099d963fd9f4666ce7a2b6afff428d9c7a666 Mon Sep 17 00:00:00 2001 From: Marcus Motill Date: Thu, 19 Feb 2026 00:36:29 +0000 Subject: [PATCH 3/5] address Bo comments, move runtime to platform --- src/google/adk/events/event.py | 7 ++-- .../adk/{runtime.py => platform/time.py} | 26 ++++-------- src/google/adk/platform/uuid.py | 42 +++++++++++++++++++ .../adk/sessions/in_memory_session_service.py | 7 ++-- .../test_time.py} | 34 ++++----------- tests/unittests/platform/test_uuid.py | 40 ++++++++++++++++++ 6 files changed, 107 insertions(+), 49 deletions(-) rename src/google/adk/{runtime.py => platform/time.py} (64%) create mode 100644 src/google/adk/platform/uuid.py rename tests/unittests/{test_runtime.py => platform/test_time.py} (50%) create mode 100644 tests/unittests/platform/test_uuid.py diff --git a/src/google/adk/events/event.py b/src/google/adk/events/event.py index cca65295cb..89e2bf05e3 100644 --- a/src/google/adk/events/event.py +++ b/src/google/adk/events/event.py @@ -18,7 +18,8 @@ from typing import Optional import uuid -from google.adk import runtime +from google.adk.platform import time as platform_time +from google.adk.platform import uuid as platform_uuid from google.genai import types from pydantic import alias_generators from pydantic import ConfigDict @@ -71,7 +72,7 @@ class Event(LlmResponse): # Do not assign the ID. It will be assigned by the session. id: str = '' """The unique identifier of the event.""" - timestamp: float = Field(default_factory=lambda: runtime.get_time()) + timestamp: float = Field(default_factory=lambda: platform_time.get_time()) """The timestamp of the event.""" def model_post_init(self, __context): @@ -126,4 +127,4 @@ def has_trailing_code_execution_result( @staticmethod def new_id(): - return runtime.new_uuid() + return platform_uuid.new_uuid() diff --git a/src/google/adk/runtime.py b/src/google/adk/platform/time.py similarity index 64% rename from src/google/adk/runtime.py rename to src/google/adk/platform/time.py index edd6be9045..e6b60aea90 100644 --- a/src/google/adk/runtime.py +++ b/src/google/adk/platform/time.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,14 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Runtime module for abstracting system primitives like time and UUIDs.""" +"""Platform module for abstracting system time generation.""" import time -import uuid from typing import Callable -_time_provider: Callable[[], float] = time.time -_id_provider: Callable[[], str] = lambda: str(uuid.uuid4()) +_default_time_provider: Callable[[], float] = time.time +_time_provider: Callable[[], float] = _default_time_provider def set_time_provider(provider: Callable[[], float]) -> None: @@ -33,21 +32,12 @@ def set_time_provider(provider: Callable[[], float]) -> None: _time_provider = provider -def set_id_provider(provider: Callable[[], str]) -> None: - """Sets the provider for generating unique IDs. - - Args: - provider: A callable that returns a unique ID string. - """ - global _id_provider - _id_provider = provider +def reset_time_provider() -> None: + """Resets the time provider to its default implementation.""" + global _time_provider + _time_provider = _default_time_provider def get_time() -> float: """Returns the current time in seconds since the epoch.""" return _time_provider() - - -def new_uuid() -> str: - """Returns a new unique ID.""" - return _id_provider() diff --git a/src/google/adk/platform/uuid.py b/src/google/adk/platform/uuid.py new file mode 100644 index 0000000000..ab6aedc6a0 --- /dev/null +++ b/src/google/adk/platform/uuid.py @@ -0,0 +1,42 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Platform module for abstracting unique ID generation.""" + +import uuid +from typing import Callable + +_default_id_provider: Callable[[], str] = lambda: str(uuid.uuid4()) +_id_provider: Callable[[], str] = _default_id_provider + + +def set_id_provider(provider: Callable[[], str]) -> None: + """Sets the provider for generating unique IDs. + + Args: + provider: A callable that returns a unique ID string. + """ + global _id_provider + _id_provider = provider + + +def reset_id_provider() -> None: + """Resets the ID provider to its default implementation.""" + global _id_provider + _id_provider = _default_id_provider + + +def new_uuid() -> str: + """Returns a new unique ID.""" + return _id_provider() diff --git a/src/google/adk/sessions/in_memory_session_service.py b/src/google/adk/sessions/in_memory_session_service.py index b856f3b584..c91a836fe3 100644 --- a/src/google/adk/sessions/in_memory_session_service.py +++ b/src/google/adk/sessions/in_memory_session_service.py @@ -22,7 +22,8 @@ from typing_extensions import override -from google.adk import runtime +from google.adk.platform import time as platform_time +from google.adk.platform import uuid as platform_uuid from . import _session_util from ..errors.already_exists_error import AlreadyExistsError from ..events.event import Event @@ -109,14 +110,14 @@ def _create_session_impl( session_id = ( session_id.strip() if session_id and session_id.strip() - else runtime.new_uuid() + else platform_uuid.new_uuid() ) session = Session( app_name=app_name, user_id=user_id, id=session_id, state=session_state or {}, - last_update_time=runtime.get_time(), + last_update_time=platform_time.get_time(), ) if app_name not in self.sessions: diff --git a/tests/unittests/test_runtime.py b/tests/unittests/platform/test_time.py similarity index 50% rename from tests/unittests/test_runtime.py rename to tests/unittests/platform/test_time.py index cde013631b..badff75f2a 100644 --- a/tests/unittests/test_runtime.py +++ b/tests/unittests/platform/test_time.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,45 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Unit tests for the runtime module.""" +"""Unit tests for the platform time module.""" import time -import uuid import unittest -from unittest.mock import MagicMock, patch -from google.adk import runtime +from google.adk.platform import time as platform_time -class TestRuntime(unittest.TestCase): +class TestTime(unittest.TestCase): def tearDown(self): - # Reset providers to default after each test - runtime.set_time_provider(time.time) - runtime.set_id_provider(lambda: str(uuid.uuid4())) + # Reset provider to default after each test + platform_time.reset_time_provider() def test_default_time_provider(self): # Verify it returns a float that is close to now now = time.time() - rt_time = runtime.get_time() + rt_time = platform_time.get_time() self.assertIsInstance(rt_time, float) self.assertAlmostEqual(rt_time, now, delta=1.0) - def test_default_id_provider(self): - # Verify it returns a string uuid - uid = runtime.new_uuid() - self.assertIsInstance(uid, str) - # Should be parseable as uuid - uuid.UUID(uid) - def test_custom_time_provider(self): # Test override mock_time = 123456789.0 - runtime.set_time_provider(lambda: mock_time) - self.assertEqual(runtime.get_time(), mock_time) - - def test_custom_id_provider(self): - # Test override - mock_id = "test-id-123" - runtime.set_id_provider(lambda: mock_id) - self.assertEqual(runtime.new_uuid(), mock_id) + platform_time.set_time_provider(lambda: mock_time) + self.assertEqual(platform_time.get_time(), mock_time) diff --git a/tests/unittests/platform/test_uuid.py b/tests/unittests/platform/test_uuid.py new file mode 100644 index 0000000000..afc8e378a7 --- /dev/null +++ b/tests/unittests/platform/test_uuid.py @@ -0,0 +1,40 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for the platform uuid module.""" + +import uuid +import unittest + +from google.adk.platform import uuid as platform_uuid + + +class TestUUID(unittest.TestCase): + + def tearDown(self): + # Reset provider to default after each test + platform_uuid.reset_id_provider() + + def test_default_id_provider(self): + # Verify it returns a string uuid + uid = platform_uuid.new_uuid() + self.assertIsInstance(uid, str) + # Should be parseable as uuid + uuid.UUID(uid) + + def test_custom_id_provider(self): + # Test override + mock_id = "test-id-123" + platform_uuid.set_id_provider(lambda: mock_id) + self.assertEqual(platform_uuid.new_uuid(), mock_id) From cc58fa596c889a24b28aea0a65561b9eb34368b3 Mon Sep 17 00:00:00 2001 From: Marcus Motill Date: Thu, 19 Feb 2026 02:57:09 +0000 Subject: [PATCH 4/5] updates for durable execution --- src/google/adk/a2a/converters/event_converter.py | 13 +++++++------ src/google/adk/agents/invocation_context.py | 5 +++-- src/google/adk/artifacts/base_artifact_service.py | 4 +++- src/google/adk/flows/llm_flows/functions.py | 4 ++-- src/google/adk/sessions/sqlite_session_service.py | 7 +++++-- .../a2a/converters/test_event_converter.py | 4 ++-- tests/unittests/artifacts/test_artifact_service.py | 12 ++++++------ 7 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/google/adk/a2a/converters/event_converter.py b/src/google/adk/a2a/converters/event_converter.py index 59bbefa1f0..d15b66bf9c 100644 --- a/src/google/adk/a2a/converters/event_converter.py +++ b/src/google/adk/a2a/converters/event_converter.py @@ -22,7 +22,8 @@ from typing import Dict from typing import List from typing import Optional -import uuid + +from google.adk.platform import uuid as platform_uuid from a2a.server.events import Event as A2AEvent from a2a.types import DataPart @@ -254,7 +255,7 @@ def convert_a2a_task_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else str(uuid.uuid4()) + else platform_uuid.new_uuid() ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -299,7 +300,7 @@ def convert_a2a_message_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else str(uuid.uuid4()) + else platform_uuid.new_uuid() ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -349,7 +350,7 @@ def convert_a2a_message_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else str(uuid.uuid4()) + else platform_uuid.new_uuid() ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -408,7 +409,7 @@ def convert_event_to_a2a_message( if output_parts: return Message( - message_id=str(uuid.uuid4()), role=role, parts=output_parts + message_id=platform_uuid.new_uuid(), role=role, parts=output_parts ) except Exception as e: @@ -449,7 +450,7 @@ def _create_error_status_event( status=TaskStatus( state=TaskState.failed, message=Message( - message_id=str(uuid.uuid4()), + message_id=platform_uuid.new_uuid(), role=Role.agent, parts=[TextPart(text=error_message)], metadata={ diff --git a/src/google/adk/agents/invocation_context.py b/src/google/adk/agents/invocation_context.py index 7a23a6cc8e..ee7f956571 100644 --- a/src/google/adk/agents/invocation_context.py +++ b/src/google/adk/agents/invocation_context.py @@ -16,7 +16,8 @@ from typing import Any from typing import Optional -import uuid + +from google.adk.platform import uuid as platform_uuid from google.genai import types from pydantic import BaseModel @@ -409,4 +410,4 @@ def _find_matching_function_call( def new_invocation_context_id() -> str: - return "e-" + str(uuid.uuid4()) + return "e-" + platform_uuid.new_uuid() diff --git a/src/google/adk/artifacts/base_artifact_service.py b/src/google/adk/artifacts/base_artifact_service.py index 1a265f8ad9..2421b6e2b1 100644 --- a/src/google/adk/artifacts/base_artifact_service.py +++ b/src/google/adk/artifacts/base_artifact_service.py @@ -19,6 +19,8 @@ from typing import Any from typing import Optional +from google.adk.platform import time as platform_time + from google.genai import types from pydantic import alias_generators from pydantic import BaseModel @@ -47,7 +49,7 @@ class ArtifactVersion(BaseModel): description="Optional user-supplied metadata stored with the artifact.", ) create_time: float = Field( - default_factory=lambda: datetime.now().timestamp(), + default_factory=lambda: platform_time.get_time(), description=( "Unix timestamp (seconds) when the version record was created." ), diff --git a/src/google/adk/flows/llm_flows/functions.py b/src/google/adk/flows/llm_flows/functions.py index 6f34e8fe75..8d6834e8c1 100644 --- a/src/google/adk/flows/llm_flows/functions.py +++ b/src/google/adk/flows/llm_flows/functions.py @@ -29,8 +29,8 @@ from typing import Dict from typing import Optional from typing import TYPE_CHECKING -import uuid +from google.adk.platform import uuid as platform_uuid from google.genai import types from ...agents.active_streaming_tool import ActiveStreamingTool @@ -175,7 +175,7 @@ def run_async_tool_in_new_loop(): def generate_client_function_call_id() -> str: - return f'{AF_FUNCTION_CALL_ID_PREFIX}{uuid.uuid4()}' + return f'{AF_FUNCTION_CALL_ID_PREFIX}{platform_uuid.new_uuid()}' def populate_client_function_call_id(model_response_event: Event) -> None: diff --git a/src/google/adk/sessions/sqlite_session_service.py b/src/google/adk/sessions/sqlite_session_service.py index d23c8278cf..33f3f71920 100644 --- a/src/google/adk/sessions/sqlite_session_service.py +++ b/src/google/adk/sessions/sqlite_session_service.py @@ -29,6 +29,9 @@ import aiosqlite from typing_extensions import override +from google.adk.platform import time as platform_time +from google.adk.platform import uuid as platform_uuid + from . import _session_util from ..errors.already_exists_error import AlreadyExistsError from ..events.event import Event @@ -165,8 +168,8 @@ async def create_session( if session_id: session_id = session_id.strip() if not session_id: - session_id = str(uuid.uuid4()) - now = time.time() + session_id = platform_uuid.new_uuid() + now = platform_time.get_time() async with self._get_db_connection() as db: # Check if session_id already exists diff --git a/tests/unittests/a2a/converters/test_event_converter.py b/tests/unittests/a2a/converters/test_event_converter.py index 47f0bbf435..286941a810 100644 --- a/tests/unittests/a2a/converters/test_event_converter.py +++ b/tests/unittests/a2a/converters/test_event_converter.py @@ -742,7 +742,7 @@ def test_convert_a2a_task_to_event_no_message(self): assert result.branch == "test-branch" assert result.invocation_id == "test-invocation-id" - @patch("google.adk.a2a.converters.event_converter.uuid.uuid4") + @patch("google.adk.a2a.converters.event_converter.platform_uuid.new_uuid") def test_convert_a2a_task_to_event_default_author(self, mock_uuid): """Test converting A2A task with default author and no invocation context.""" from google.adk.a2a.converters.event_converter import convert_a2a_task_to_event @@ -974,7 +974,7 @@ def test_convert_a2a_message_to_event_missing_tool_id(self): # Parts will be empty since conversion returned None assert len(result.content.parts) == 0 - @patch("google.adk.a2a.converters.event_converter.uuid.uuid4") + @patch("google.adk.a2a.converters.event_converter.platform_uuid.new_uuid") def test_convert_a2a_message_to_event_default_author(self, mock_uuid): """Test conversion with default author and no invocation context.""" from google.adk.a2a.converters.event_converter import convert_a2a_message_to_event diff --git a/tests/unittests/artifacts/test_artifact_service.py b/tests/unittests/artifacts/test_artifact_service.py index ec74f8abe3..f9790e9257 100644 --- a/tests/unittests/artifacts/test_artifact_service.py +++ b/tests/unittests/artifacts/test_artifact_service.py @@ -418,9 +418,9 @@ async def test_list_artifact_versions_and_get_artifact_version( ] with patch( - "google.adk.artifacts.base_artifact_service.datetime" - ) as mock_datetime: - mock_datetime.now.return_value = FIXED_DATETIME + "google.adk.artifacts.base_artifact_service.platform_time" + ) as mock_platform_time: + mock_platform_time.get_time.return_value = FIXED_DATETIME.timestamp() for i in range(4): custom_metadata = {"key": "value" + str(i)} @@ -505,9 +505,9 @@ async def test_list_artifact_versions_with_user_prefix( ] with patch( - "google.adk.artifacts.base_artifact_service.datetime" - ) as mock_datetime: - mock_datetime.now.return_value = FIXED_DATETIME + "google.adk.artifacts.base_artifact_service.platform_time" + ) as mock_platform_time: + mock_platform_time.get_time.return_value = FIXED_DATETIME.timestamp() for i in range(4): custom_metadata = {"key": "value" + str(i)} From 5abb5194a179ef57fe3a34a8ea98a7c66dbb663c Mon Sep 17 00:00:00 2001 From: Marcus Motill Date: Thu, 19 Feb 2026 03:00:09 +0000 Subject: [PATCH 5/5] back out a2a changes --- src/google/adk/a2a/converters/event_converter.py | 13 ++++++------- .../a2a/converters/test_event_converter.py | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/google/adk/a2a/converters/event_converter.py b/src/google/adk/a2a/converters/event_converter.py index d15b66bf9c..59bbefa1f0 100644 --- a/src/google/adk/a2a/converters/event_converter.py +++ b/src/google/adk/a2a/converters/event_converter.py @@ -22,8 +22,7 @@ from typing import Dict from typing import List from typing import Optional - -from google.adk.platform import uuid as platform_uuid +import uuid from a2a.server.events import Event as A2AEvent from a2a.types import DataPart @@ -255,7 +254,7 @@ def convert_a2a_task_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else platform_uuid.new_uuid() + else str(uuid.uuid4()) ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -300,7 +299,7 @@ def convert_a2a_message_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else platform_uuid.new_uuid() + else str(uuid.uuid4()) ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -350,7 +349,7 @@ def convert_a2a_message_to_event( invocation_id=( invocation_context.invocation_id if invocation_context - else platform_uuid.new_uuid() + else str(uuid.uuid4()) ), author=author or "a2a agent", branch=invocation_context.branch if invocation_context else None, @@ -409,7 +408,7 @@ def convert_event_to_a2a_message( if output_parts: return Message( - message_id=platform_uuid.new_uuid(), role=role, parts=output_parts + message_id=str(uuid.uuid4()), role=role, parts=output_parts ) except Exception as e: @@ -450,7 +449,7 @@ def _create_error_status_event( status=TaskStatus( state=TaskState.failed, message=Message( - message_id=platform_uuid.new_uuid(), + message_id=str(uuid.uuid4()), role=Role.agent, parts=[TextPart(text=error_message)], metadata={ diff --git a/tests/unittests/a2a/converters/test_event_converter.py b/tests/unittests/a2a/converters/test_event_converter.py index 286941a810..47f0bbf435 100644 --- a/tests/unittests/a2a/converters/test_event_converter.py +++ b/tests/unittests/a2a/converters/test_event_converter.py @@ -742,7 +742,7 @@ def test_convert_a2a_task_to_event_no_message(self): assert result.branch == "test-branch" assert result.invocation_id == "test-invocation-id" - @patch("google.adk.a2a.converters.event_converter.platform_uuid.new_uuid") + @patch("google.adk.a2a.converters.event_converter.uuid.uuid4") def test_convert_a2a_task_to_event_default_author(self, mock_uuid): """Test converting A2A task with default author and no invocation context.""" from google.adk.a2a.converters.event_converter import convert_a2a_task_to_event @@ -974,7 +974,7 @@ def test_convert_a2a_message_to_event_missing_tool_id(self): # Parts will be empty since conversion returned None assert len(result.content.parts) == 0 - @patch("google.adk.a2a.converters.event_converter.platform_uuid.new_uuid") + @patch("google.adk.a2a.converters.event_converter.uuid.uuid4") def test_convert_a2a_message_to_event_default_author(self, mock_uuid): """Test conversion with default author and no invocation context.""" from google.adk.a2a.converters.event_converter import convert_a2a_message_to_event