mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
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:
committed by
Copybara-Service
parent
30212669ff
commit
f2bed14c4b
@@ -351,10 +351,19 @@ def _build_function_declaration_log(
|
||||
|
||||
|
||||
def _build_request_log(req: LlmRequest) -> str:
|
||||
function_decls: list[types.FunctionDeclaration] = cast(
|
||||
list[types.FunctionDeclaration],
|
||||
req.config.tools[0].function_declarations if req.config.tools else [],
|
||||
)
|
||||
# Find which tool contains function_declarations
|
||||
function_decls: list[types.FunctionDeclaration] = []
|
||||
function_decl_tool_index: Optional[int] = None
|
||||
|
||||
if req.config.tools:
|
||||
for idx, tool in enumerate(req.config.tools):
|
||||
if tool.function_declarations:
|
||||
function_decls = cast(
|
||||
list[types.FunctionDeclaration], tool.function_declarations
|
||||
)
|
||||
function_decl_tool_index = idx
|
||||
break
|
||||
|
||||
function_logs = (
|
||||
[
|
||||
_build_function_declaration_log(func_decl)
|
||||
@@ -375,12 +384,35 @@ def _build_request_log(req: LlmRequest) -> str:
|
||||
for content in req.contents
|
||||
]
|
||||
|
||||
# Build exclusion dict for config logging
|
||||
tools_exclusion = (
|
||||
{function_decl_tool_index: {'function_declarations'}}
|
||||
if function_decl_tool_index is not None
|
||||
else True
|
||||
)
|
||||
|
||||
try:
|
||||
config_log = str(
|
||||
req.config.model_dump(
|
||||
exclude_none=True,
|
||||
exclude={
|
||||
'system_instruction': True,
|
||||
'tools': tools_exclusion if req.config.tools else True,
|
||||
},
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
config_log = repr(req.config)
|
||||
|
||||
return f"""
|
||||
LLM Request:
|
||||
-----------------------------------------------------------
|
||||
System Instruction:
|
||||
{req.config.system_instruction}
|
||||
-----------------------------------------------------------
|
||||
Config:
|
||||
{config_log}
|
||||
-----------------------------------------------------------
|
||||
Contents:
|
||||
{_NEW_LINE.join(contents_logs)}
|
||||
-----------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user