chore: Adjust the LLM Request logging

1. function declarations is not necessary in the first tool
2. log the config

PiperOrigin-RevId: 816547534
This commit is contained in:
Xiang (Sean) Zhou
2025-10-07 23:07:18 -07:00
committed by Copybara-Service
parent 30212669ff
commit f2bed14c4b
2 changed files with 168 additions and 4 deletions
+132
View File
@@ -24,6 +24,7 @@ from google.adk.models.cache_metadata import CacheMetadata
from google.adk.models.gemini_llm_connection import GeminiLlmConnection
from google.adk.models.google_llm import _AGENT_ENGINE_TELEMETRY_ENV_VARIABLE_NAME
from google.adk.models.google_llm import _AGENT_ENGINE_TELEMETRY_TAG
from google.adk.models.google_llm import _build_request_log
from google.adk.models.google_llm import Gemini
from google.adk.models.llm_request import LlmRequest
from google.adk.models.llm_response import LlmResponse
@@ -1726,3 +1727,134 @@ async def test_generate_content_async_with_cache_metadata_integration(
# Verify cache metadata is preserved
assert second_arg.cache_name == cache_metadata.cache_name
assert second_arg.invocations_used == cache_metadata.invocations_used
def test_build_request_log_with_config_multiple_tool_types():
"""Test that _build_request_log includes config with multiple tool types."""
func_decl = types.FunctionDeclaration(
name="test_function",
description="A test function",
parameters={"type": "object", "properties": {}},
)
tool = types.Tool(
function_declarations=[func_decl],
google_search=types.GoogleSearch(),
code_execution=types.ToolCodeExecution(),
)
llm_request = LlmRequest(
model="gemini-1.5-flash",
contents=[Content(role="user", parts=[Part.from_text(text="Hello")])],
config=types.GenerateContentConfig(
temperature=0.7,
max_output_tokens=500,
system_instruction="You are a helpful assistant",
tools=[tool],
),
)
log_output = _build_request_log(llm_request)
# Verify config section exists
assert "Config:" in log_output
# Verify config contains expected fields (using Python dict format with single quotes)
assert "'temperature': 0.7" in log_output
assert "'max_output_tokens': 500" in log_output
# Verify config contains other tool types (not function_declarations)
assert "'google_search'" in log_output
assert "'code_execution'" in log_output
# Verify function_declarations is NOT in config section
# (it should only be in the Functions section)
config_section = log_output.split("Functions:")[0]
assert "'function_declarations'" not in config_section
# Verify function is in Functions section
assert "Functions:" in log_output
assert "test_function" in log_output
# Verify system instruction is NOT in config section
assert (
"'system_instruction'"
not in log_output.split("Contents:")[0].split("Config:")[1]
)
def test_build_request_log_function_declarations_in_second_tool():
"""Test that function_declarations in non-first tool are handled correctly."""
func_decl = types.FunctionDeclaration(
name="my_function",
description="A test function",
parameters={"type": "object", "properties": {}},
)
# First tool has only google_search
tool1 = types.Tool(google_search=types.GoogleSearch())
# Second tool has function_declarations
tool2 = types.Tool(
function_declarations=[func_decl],
code_execution=types.ToolCodeExecution(),
)
llm_request = LlmRequest(
model="gemini-1.5-flash",
contents=[Content(role="user", parts=[Part.from_text(text="Hello")])],
config=types.GenerateContentConfig(
temperature=0.5,
system_instruction="You are a helpful assistant",
tools=[tool1, tool2],
),
)
log_output = _build_request_log(llm_request)
# Verify function is in Functions section
assert "Functions:" in log_output
assert "my_function" in log_output
# Verify function_declarations is NOT in config section
config_section = log_output.split("Functions:")[0]
assert "'function_declarations'" not in config_section
# Verify both tools are in config but without function_declarations (Python dict format)
assert "'google_search'" in log_output
assert "'code_execution'" in log_output
# Verify config has the expected structure without parsing
config_section = log_output.split("Config:")[1].split("---")[0]
# Should have 2 tools (two dict entries in the tools list)
assert config_section.count("'google_search'") == 1
assert config_section.count("'code_execution'") == 1
# Function declarations should NOT be in config section
assert "'function_declarations'" not in config_section
def test_build_request_log_fallback_to_repr_on_all_failures(monkeypatch):
"""Test that _build_request_log falls back to repr() if model_dump fails."""
llm_request = LlmRequest(
model="gemini-1.5-flash",
contents=[Content(role="user", parts=[Part.from_text(text="Hello")])],
config=types.GenerateContentConfig(
temperature=0.7,
system_instruction="You are a helpful assistant",
),
)
# Mock model_dump at class level to raise exception
def mock_model_dump(*args, **kwargs):
raise Exception("dump failed")
monkeypatch.setattr(
types.GenerateContentConfig, "model_dump", mock_model_dump
)
log_output = _build_request_log(llm_request)
# Should still succeed using repr()
assert "Config:" in log_output
assert "GenerateContentConfig" in log_output