Files
google--adk-python/tests/unittests/plugins/test_global_instruction_plugin.py
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:25:13 +08:00

209 lines
6.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.
from unittest.mock import Mock
from google.adk.agents.callback_context import CallbackContext
from google.adk.agents.invocation_context import InvocationContext
from google.adk.agents.llm_agent import Agent
from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.models.llm_request import LlmRequest
from google.adk.plugins.global_instruction_plugin import GlobalInstructionPlugin
from google.adk.sessions.session import Session
from google.genai import types
import pytest
@pytest.mark.asyncio
async def test_global_instruction_plugin_with_string():
"""Test GlobalInstructionPlugin with a string global instruction."""
plugin = GlobalInstructionPlugin(
global_instruction=(
"You are a helpful assistant with a friendly personality."
)
)
# Create mock objects
mock_session = Session(
app_name="test_app", user_id="test_user", id="test_session", state={}
)
mock_invocation_context = Mock(spec=InvocationContext)
mock_invocation_context.session = mock_session
mock_callback_context = Mock(spec=CallbackContext)
mock_callback_context._invocation_context = mock_invocation_context
llm_request = LlmRequest(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(system_instruction=""),
)
# Execute the plugin's before_model_callback
result = await plugin.before_model_callback(
callback_context=mock_callback_context, llm_request=llm_request
)
# Plugin should return None to allow normal processing
assert result is None
# System instruction should now contain the global instruction
assert (
"You are a helpful assistant with a friendly personality."
in llm_request.config.system_instruction
)
@pytest.mark.asyncio
async def test_global_instruction_plugin_with_instruction_provider():
"""Test GlobalInstructionPlugin with an InstructionProvider function."""
async def build_global_instruction(readonly_context: ReadonlyContext) -> str:
return f"You are assistant for user {readonly_context.session.user_id}."
plugin = GlobalInstructionPlugin(global_instruction=build_global_instruction)
# Create mock objects
mock_session = Session(
app_name="test_app", user_id="alice", id="test_session", state={}
)
mock_invocation_context = Mock(spec=InvocationContext)
mock_callback_context = Mock(spec=CallbackContext)
mock_callback_context._invocation_context = mock_invocation_context
mock_callback_context.session = mock_session
llm_request = LlmRequest(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(system_instruction=""),
)
# Execute the plugin's before_model_callback
result = await plugin.before_model_callback(
callback_context=mock_callback_context, llm_request=llm_request
)
# Plugin should return None to allow normal processing
assert result is None
# System instruction should contain the dynamically generated instruction
assert (
"You are assistant for user alice."
in llm_request.config.system_instruction
)
@pytest.mark.asyncio
async def test_global_instruction_plugin_empty_instruction():
"""Test GlobalInstructionPlugin with empty global instruction."""
plugin = GlobalInstructionPlugin(global_instruction="")
# Create mock objects
mock_session = Session(
app_name="test_app", user_id="test_user", id="test_session", state={}
)
mock_invocation_context = Mock(spec=InvocationContext)
mock_invocation_context.session = mock_session
mock_callback_context = Mock(spec=CallbackContext)
mock_callback_context._invocation_context = mock_invocation_context
llm_request = LlmRequest(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction="Original instruction"
),
)
# Execute the plugin's before_model_callback
result = await plugin.before_model_callback(
callback_context=mock_callback_context, llm_request=llm_request
)
# Plugin should return None to allow normal processing
assert result is None
# System instruction should remain unchanged
assert llm_request.config.system_instruction == "Original instruction"
@pytest.mark.asyncio
async def test_global_instruction_plugin_leads_existing():
"""Test that GlobalInstructionPlugin prepends global instructions."""
plugin = GlobalInstructionPlugin(
global_instruction="You are a helpful assistant."
)
# Create mock objects
mock_session = Session(
app_name="test_app", user_id="test_user", id="test_session", state={}
)
mock_invocation_context = Mock(spec=InvocationContext)
mock_invocation_context.session = mock_session
mock_callback_context = Mock(spec=CallbackContext)
mock_callback_context._invocation_context = mock_invocation_context
llm_request = LlmRequest(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction="Existing instructions."
),
)
# Execute the plugin's before_model_callback
result = await plugin.before_model_callback(
callback_context=mock_callback_context, llm_request=llm_request
)
# Plugin should return None to allow normal processing
assert result is None
# System instruction should contain global instruction before existing ones
expected = "You are a helpful assistant.\n\nExisting instructions."
assert llm_request.config.system_instruction == expected
@pytest.mark.asyncio
async def test_global_instruction_plugin_prepends_to_list():
"""Test GlobalInstructionPlugin prepends to a list of instructions."""
plugin = GlobalInstructionPlugin(global_instruction="Global instruction.")
mock_session = Session(
app_name="test_app", user_id="test_user", id="test_session", state={}
)
mock_invocation_context = Mock(spec=InvocationContext)
mock_invocation_context.session = mock_session
mock_callback_context = Mock(spec=CallbackContext)
mock_callback_context._invocation_context = mock_invocation_context
llm_request = LlmRequest(
model="gemini-2.5-flash",
config=types.GenerateContentConfig(
system_instruction=["Existing instruction."]
),
)
await plugin.before_model_callback(
callback_context=mock_callback_context, llm_request=llm_request
)
expected = ["Global instruction.", "Existing instruction."]
assert llm_request.config.system_instruction == expected