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:
|
def _build_request_log(req: LlmRequest) -> str:
|
||||||
function_decls: list[types.FunctionDeclaration] = cast(
|
# Find which tool contains function_declarations
|
||||||
list[types.FunctionDeclaration],
|
function_decls: list[types.FunctionDeclaration] = []
|
||||||
req.config.tools[0].function_declarations if req.config.tools else [],
|
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 = (
|
function_logs = (
|
||||||
[
|
[
|
||||||
_build_function_declaration_log(func_decl)
|
_build_function_declaration_log(func_decl)
|
||||||
@@ -375,12 +384,35 @@ def _build_request_log(req: LlmRequest) -> str:
|
|||||||
for content in req.contents
|
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"""
|
return f"""
|
||||||
LLM Request:
|
LLM Request:
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
System Instruction:
|
System Instruction:
|
||||||
{req.config.system_instruction}
|
{req.config.system_instruction}
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
Config:
|
||||||
|
{config_log}
|
||||||
|
-----------------------------------------------------------
|
||||||
Contents:
|
Contents:
|
||||||
{_NEW_LINE.join(contents_logs)}
|
{_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.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_ENV_VARIABLE_NAME
|
||||||
from google.adk.models.google_llm import _AGENT_ENGINE_TELEMETRY_TAG
|
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.google_llm import Gemini
|
||||||
from google.adk.models.llm_request import LlmRequest
|
from google.adk.models.llm_request import LlmRequest
|
||||||
from google.adk.models.llm_response import LlmResponse
|
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
|
# Verify cache metadata is preserved
|
||||||
assert second_arg.cache_name == cache_metadata.cache_name
|
assert second_arg.cache_name == cache_metadata.cache_name
|
||||||
assert second_arg.invocations_used == cache_metadata.invocations_used
|
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