Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
78b1c4e
feat: extract durable runtime support changes
marcusmotill Jan 19, 2026
57ddf8f
chore: remove pr_description.md
marcusmotill Jan 19, 2026
08d099d
address Bo comments, move runtime to platform
marcusmotill Feb 19, 2026
cc58fa5
updates for durable execution
marcusmotill Feb 19, 2026
5abb519
back out a2a changes
marcusmotill Feb 19, 2026
544af2d
update for concurrency and review feedback
marcusmotill Feb 25, 2026
f518ee6
cover more bases with regards to durable utils
marcusmotill Feb 26, 2026
0c695cc
revert rounding
marcusmotill Feb 26, 2026
2856d2d
remove unused imports
marcusmotill Mar 2, 2026
48a56ba
Merge upstream main and resolve conflicts
marcusmotill Mar 2, 2026
ae66ac9
Merge branch 'main' into motill/durable-support
marcusmotill Mar 2, 2026
31700cb
Merge branch 'main' into motill/durable-support
sasha-gitg Mar 2, 2026
61798e2
chore: run formatting and linting (isort/pyink)
marcusmotill Mar 3, 2026
8863af6
Merge branch 'main' into motill/durable-support
marcusmotill Mar 3, 2026
5f003fb
chore: add __future__.annotations to platform modules
marcusmotill Mar 3, 2026
448a351
fix: cast platform_uuid.new_uuid to str for mypy strictness
marcusmotill Mar 3, 2026
0791508
Merge branch 'main' into motill/durable-support
marcusmotill Mar 3, 2026
06eeca8
Merge upstream/main and resolve conflict
marcusmotill Mar 4, 2026
9673095
fix(typing): explicitly cast platform uuid to str using typing.cast t…
marcusmotill Mar 4, 2026
118bc91
Merge remote-tracking branch 'origin/motill/durable-support' into mot…
marcusmotill Mar 4, 2026
8082764
fix(typing): add missing annotations and typing.cast to event.py and …
marcusmotill Mar 4, 2026
b491999
revert(typing): remove unnecessary model_post_init typing
marcusmotill Mar 4, 2026
6225046
Merge branch 'main' into motill/durable-support
marcusmotill Mar 4, 2026
79f8a48
Merge branch 'main' into motill/durable-support
sasha-gitg Mar 5, 2026
b317e57
import order and double casting
marcusmotill Mar 5, 2026
6244b3b
Merge branch 'main' into motill/durable-support
sasha-gitg Mar 5, 2026
8234af3
Merge branch 'main' into motill/durable-support
marcusmotill Mar 5, 2026
345e478
Merge branch 'main' into motill/durable-support
marcusmotill Mar 6, 2026
61d0e31
add license header
marcusmotill Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/google/adk/agents/invocation_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Comment thread
marcusmotill marked this conversation as resolved.
Outdated
4 changes: 3 additions & 1 deletion src/google/adk/artifacts/base_artifact_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."
),
Expand Down
6 changes: 4 additions & 2 deletions src/google/adk/events/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from typing import Optional
import uuid
Comment thread
marcusmotill marked this conversation as resolved.
Outdated

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
Expand Down Expand Up @@ -70,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: datetime.now().timestamp())
timestamp: float = Field(default_factory=lambda: platform_time.get_time())
"""The timestamp of the event."""

def model_post_init(self, __context):
Expand Down Expand Up @@ -125,4 +127,4 @@ def has_trailing_code_execution_result(

@staticmethod
def new_id():
return str(uuid.uuid4())
return platform_uuid.new_uuid()
4 changes: 2 additions & 2 deletions src/google/adk/flows/llm_flows/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
43 changes: 43 additions & 0 deletions src/google/adk/platform/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright 2026 Google LLC
Comment thread
sasha-gitg marked this conversation as resolved.
#
# 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 system time generation."""

import time
from typing import Callable

_default_time_provider: Callable[[], float] = time.time
Comment thread
sasha-gitg marked this conversation as resolved.
_time_provider: Callable[[], float] = _default_time_provider


def set_time_provider(provider: Callable[[], float]) -> None:
Comment thread
marcusmotill marked this conversation as resolved.
"""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 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()
42 changes: 42 additions & 0 deletions src/google/adk/platform/uuid.py
Original file line number Diff line number Diff line change
@@ -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()
6 changes: 4 additions & 2 deletions src/google/adk/sessions/in_memory_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

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
Expand Down Expand Up @@ -108,14 +110,14 @@ def _create_session_impl(
session_id = (
session_id.strip()
if session_id and session_id.strip()
else str(uuid.uuid4())
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=time.time(),
last_update_time=platform_time.get_time(),
)

if app_name not in self.sessions:
Expand Down
7 changes: 5 additions & 2 deletions src/google/adk/sessions/sqlite_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions tests/unittests/artifacts/test_artifact_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
Expand Down Expand Up @@ -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)}
Expand Down
40 changes: 40 additions & 0 deletions tests/unittests/platform/test_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2026 Google LLC
Comment thread
marcusmotill marked this conversation as resolved.
#
# 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 time module."""

import time
import unittest

from google.adk.platform import time as platform_time


class TestTime(unittest.TestCase):

def tearDown(self):
# 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 = platform_time.get_time()
self.assertIsInstance(rt_time, float)
self.assertAlmostEqual(rt_time, now, delta=1.0)

def test_custom_time_provider(self):
# Test override
mock_time = 123456789.0
platform_time.set_time_provider(lambda: mock_time)
self.assertEqual(platform_time.get_time(), mock_time)
40 changes: 40 additions & 0 deletions tests/unittests/platform/test_uuid.py
Original file line number Diff line number Diff line change
@@ -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)