ec2b666284
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
164 lines
4.8 KiB
Python
164 lines
4.8 KiB
Python
# 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.
|
|
|
|
"""Tests for graph_serialization edge handling with routing maps."""
|
|
|
|
import json
|
|
|
|
from google.adk.agents import LlmAgent
|
|
from google.adk.cli.utils.graph_serialization import serialize_agent
|
|
from google.adk.models.lite_llm import LiteLlm
|
|
from google.adk.tools.base_toolset import BaseToolset
|
|
from google.adk.workflow import START
|
|
from google.adk.workflow import Workflow
|
|
|
|
from tests.unittests.workflow.workflow_testing_utils import TestingNode
|
|
|
|
|
|
def test_serialize_edges_with_routing_map() -> None:
|
|
"""Tests that routing map dicts in edges are serialized without error."""
|
|
node_a = TestingNode(name='NodeA')
|
|
node_b = TestingNode(name='NodeB')
|
|
node_c = TestingNode(name='NodeC')
|
|
|
|
agent = Workflow(
|
|
name='test_workflow',
|
|
edges=[
|
|
(START, node_a),
|
|
(node_a, {'route_b': node_b, 'route_c': node_c}),
|
|
],
|
|
)
|
|
|
|
result = serialize_agent(agent)
|
|
|
|
serialized_edges = result['edges']
|
|
assert len(serialized_edges) == 2
|
|
|
|
# First edge: (START, node_a) — serialized as a 2-element list.
|
|
assert len(serialized_edges[0]) == 2
|
|
|
|
# Second edge: (node_a, {route: node}) — serialized as a 2-element list
|
|
# where the second element is a dict with string keys.
|
|
routing_map_edge = serialized_edges[1]
|
|
assert len(routing_map_edge) == 2
|
|
assert isinstance(routing_map_edge[1], dict)
|
|
assert 'route_b' in routing_map_edge[1]
|
|
assert 'route_c' in routing_map_edge[1]
|
|
|
|
|
|
def test_serialize_edges_with_routing_map_int_keys() -> None:
|
|
"""Tests that integer routing map keys are serialized as strings."""
|
|
node_a = TestingNode(name='NodeA')
|
|
node_b = TestingNode(name='NodeB')
|
|
|
|
agent = Workflow(
|
|
name='test_workflow',
|
|
edges=[
|
|
(START, node_a),
|
|
(node_a, {1: node_b}),
|
|
],
|
|
)
|
|
|
|
result = serialize_agent(agent)
|
|
|
|
routing_map_edge = result['edges'][1]
|
|
# Integer keys become string keys in the serialized output.
|
|
assert '1' in routing_map_edge[1]
|
|
|
|
|
|
def test_serialize_edges_mixed_formats() -> None:
|
|
"""Tests serialization of edges mixing tuples, Edge objects, and routing maps."""
|
|
from google.adk.workflow import Edge
|
|
|
|
node_a = TestingNode(name='NodeA')
|
|
node_b = TestingNode(name='NodeB')
|
|
node_c = TestingNode(name='NodeC')
|
|
node_d = TestingNode(name='NodeD')
|
|
|
|
agent = Workflow(
|
|
name='test_workflow',
|
|
edges=[
|
|
(START, node_a),
|
|
(node_a, {'route_b': node_b, 'route_c': node_c}),
|
|
(node_b, node_d),
|
|
Edge(from_node=node_c, to_node=node_d),
|
|
],
|
|
)
|
|
|
|
result = serialize_agent(agent)
|
|
|
|
serialized_edges = result['edges']
|
|
assert len(serialized_edges) == 4
|
|
|
|
# Tuple edges are lists, Edge objects are dicts with from_node/to_node.
|
|
assert isinstance(serialized_edges[0], list) # (START, node_a)
|
|
assert isinstance(serialized_edges[1], list) # routing map
|
|
assert isinstance(serialized_edges[2], list) # (node_b, node_d)
|
|
assert isinstance(serialized_edges[3], dict) # Edge object
|
|
assert 'from_node' in serialized_edges[3]
|
|
|
|
|
|
def test_serialize_agent_with_toolset() -> None:
|
|
"""Tests that toolsets are serialized using their class name."""
|
|
|
|
class MockToolset(BaseToolset):
|
|
|
|
async def get_tools(self, readonly_context=None):
|
|
return []
|
|
|
|
class FakeAgent:
|
|
model_fields = {'tools': None}
|
|
|
|
def __init__(self):
|
|
self.tools = [MockToolset()]
|
|
|
|
agent = FakeAgent()
|
|
result = serialize_agent(agent) # type: ignore
|
|
|
|
assert 'tools' in result
|
|
assert len(result['tools']) == 1
|
|
assert result['tools'][0]['name'] == 'MockToolset'
|
|
assert result['tools'][0]['type'] == 'tool'
|
|
|
|
|
|
def test_serialize_agent_with_litellm_model_is_json_safe() -> None:
|
|
agent = LlmAgent(
|
|
name='repro',
|
|
model=LiteLlm(model='ollama_chat/llama3'),
|
|
)
|
|
|
|
result = serialize_agent(agent)
|
|
|
|
assert result['model'] == 'ollama_chat/llama3'
|
|
json.dumps(result)
|
|
|
|
|
|
def test_serialize_agent_skips_excluded_fields() -> None:
|
|
"""Fields marked Field(exclude=True) are omitted from serialization."""
|
|
from typing import Any
|
|
|
|
from google.adk.agents.base_agent import BaseAgent
|
|
from pydantic import ConfigDict
|
|
from pydantic import Field
|
|
|
|
class _Agent(BaseAgent):
|
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
secret: Any = Field(default=None, exclude=True)
|
|
|
|
agent = _Agent(name='a', secret=lambda: None)
|
|
result = serialize_agent(agent)
|
|
|
|
assert 'secret' not in result
|
|
assert result['name'] == 'a'
|