|
15 | 15 | import asyncio |
16 | 16 | from contextlib import aclosing |
17 | 17 | import importlib |
| 18 | +import logging |
18 | 19 | from pathlib import Path |
19 | 20 | import sys |
20 | 21 | import textwrap |
21 | 22 | from typing import AsyncGenerator |
22 | 23 | from typing import Optional |
23 | 24 | from unittest.mock import AsyncMock |
24 | 25 |
|
| 26 | +from google.adk import runners |
25 | 27 | from google.adk.agents.base_agent import BaseAgent |
26 | 28 | from google.adk.agents.context_cache_config import ContextCacheConfig |
27 | 29 | from google.adk.agents.invocation_context import InvocationContext |
@@ -1537,6 +1539,76 @@ def test_runner_realistic_cache_config_scenario(self): |
1537 | 1539 | assert str(runner.context_cache_config) == expected_str |
1538 | 1540 |
|
1539 | 1541 |
|
| 1542 | +class TestRunnerUncachedTransferWarning: |
| 1543 | + """Tests for the warning about agent transfer without a context cache.""" |
| 1544 | + |
| 1545 | + def setup_method(self): |
| 1546 | + """Set up test fixtures.""" |
| 1547 | + self.session_service = InMemorySessionService() |
| 1548 | + runners._UNCACHED_TRANSFER_APPS.clear() |
| 1549 | + |
| 1550 | + def teardown_method(self): |
| 1551 | + runners._UNCACHED_TRANSFER_APPS.clear() |
| 1552 | + |
| 1553 | + def _multi_agent(self) -> LlmAgent: |
| 1554 | + return LlmAgent( |
| 1555 | + name="root_agent", |
| 1556 | + model="gemini-1.5-pro", |
| 1557 | + sub_agents=[MockLlmAgent("sub_agent")], |
| 1558 | + ) |
| 1559 | + |
| 1560 | + def _warnings(self, caplog) -> list[str]: |
| 1561 | + return [ |
| 1562 | + record.getMessage() |
| 1563 | + for record in caplog.records |
| 1564 | + if record.levelno == logging.WARNING |
| 1565 | + and "context_cache_config" in record.getMessage() |
| 1566 | + ] |
| 1567 | + |
| 1568 | + def test_warns_for_multi_agent_app_without_cache_config(self, caplog): |
| 1569 | + """Transfer is possible and no cache is configured, so warn.""" |
| 1570 | + app = App(name="multi_agent_app", root_agent=self._multi_agent()) |
| 1571 | + |
| 1572 | + with caplog.at_level(logging.WARNING): |
| 1573 | + Runner(app=app, session_service=self.session_service) |
| 1574 | + |
| 1575 | + messages = self._warnings(caplog) |
| 1576 | + assert len(messages) == 1 |
| 1577 | + assert "multi_agent_app" in messages[0] |
| 1578 | + |
| 1579 | + def test_no_warning_when_cache_config_present(self, caplog): |
| 1580 | + """An app that configures a context cache is not warned.""" |
| 1581 | + app = App( |
| 1582 | + name="cached_app", |
| 1583 | + root_agent=self._multi_agent(), |
| 1584 | + context_cache_config=ContextCacheConfig(), |
| 1585 | + ) |
| 1586 | + |
| 1587 | + with caplog.at_level(logging.WARNING): |
| 1588 | + Runner(app=app, session_service=self.session_service) |
| 1589 | + |
| 1590 | + assert not self._warnings(caplog) |
| 1591 | + |
| 1592 | + def test_no_warning_without_transfer_targets(self, caplog): |
| 1593 | + """A single-agent app cannot transfer, so nothing is lost.""" |
| 1594 | + app = App(name="single_agent_app", root_agent=MockLlmAgent("root_agent")) |
| 1595 | + |
| 1596 | + with caplog.at_level(logging.WARNING): |
| 1597 | + Runner(app=app, session_service=self.session_service) |
| 1598 | + |
| 1599 | + assert not self._warnings(caplog) |
| 1600 | + |
| 1601 | + def test_warns_only_once_per_app(self, caplog): |
| 1602 | + """Rebuilding the runner for the same app does not warn again.""" |
| 1603 | + app = App(name="multi_agent_app", root_agent=self._multi_agent()) |
| 1604 | + |
| 1605 | + with caplog.at_level(logging.WARNING): |
| 1606 | + for _ in range(3): |
| 1607 | + Runner(app=app, session_service=self.session_service) |
| 1608 | + |
| 1609 | + assert len(self._warnings(caplog)) == 1 |
| 1610 | + |
| 1611 | + |
1540 | 1612 | class TestRunnerResolveApp: |
1541 | 1613 | """Tests for Runner._resolve_app and node support.""" |
1542 | 1614 |
|
|
0 commit comments