feat(tools): support per-parameter descriptions via Annotated[T, Field(description=...)]#4962
feat(tools): support per-parameter descriptions via Annotated[T, Field(description=...)]#4962ecanlar wants to merge 2 commits intogoogle:mainfrom
Conversation
…d(description=...)] This change implements Option A from issue google#4552, adding support for per-parameter descriptions in FunctionTool using the Annotated type hint with pydantic.Field(description=...). Changes: - Add _extract_field_info_from_annotated() to extract FieldInfo from Annotated - Add _extract_base_type_from_annotated() to unwrap base types from Annotated - Update _get_fields_dict() to use descriptions from Annotated[T, Field(...)] - Add comprehensive tests for the new functionality This enables developers to provide contextual guidance for LLM parameter selection without embedding all information in the tool docstring: from typing import Annotated from pydantic import Field async def create_task( repository: Annotated[str, Field( description='Repository URL. MUST be obtained from get_repository_info.' )], ) -> dict: ... Closes google#4552
|
Does this handle nested pydantic objects? I noticed the original PR didn't. If it helps, I can add code for that. |
|
Hi @edpowers! You're absolutely right - I just tested the implementation and confirmed that nested Pydantic objects are NOT handled correctly. The issue is that Pydantic generates JSON schemas with Thank you for catching this! |
…ptions This commit adds _resolve_pydantic_refs() to inline nested BaseModel properties and their Field descriptions, ensuring that LLMs receive complete parameter documentation even for complex nested structures. Key changes: - Add _resolve_pydantic_refs() to resolve $ref pointers from Pydantic schemas - Integrate reference resolution into _get_pydantic_schema() - Handle allOf wrappers (Pydantic v2 pattern) - Support multi-level nesting with circular reference protection - Preserve parameter-level descriptions over model docstrings - Add 8 comprehensive tests for nested models including: * Single-level nested models * Multi-level nested models (e.g., Person -> ContactInfo -> email) * List[BaseModel] support * Optional[BaseModel] support * Mixed simple and nested parameters * Circular reference handling This addresses the limitation identified by @edpowers in PR google#4962 where nested Pydantic BaseModel Field descriptions were not accessible to LLMs because they remained in $defs instead of being inlined.
|
Hi @edpowers! 👋 You're absolutely right - I just verified the implementation and nested Pydantic objects were NOT handled correctly in the original PR. Thanks for catching this! The ProblemPydantic v2 generates JSON schemas with {
"properties": {
"user": {
"allOf": [{"$ref": "#/$defs/Person"}],
"description": "User info"
}
},
"$defs": {
"Person": {
"properties": {
"name": {"description": "Person's full name"},
"age": {"description": "Person's age in years"}
}
}
}
}The LLM/API would not see the nested The SolutionI've just pushed a commit that adds full support for nested Pydantic models! What I implemented:
Example - Now this works correctly:class ContactInfo(BaseModel):
email: str = Field(description="Email in format [email protected]")
phone: str = Field(description="Phone with country code")
class Person(BaseModel):
name: str = Field(description="Person's full name")
contact: ContactInfo = Field(description="Contact information")
def create_user(
person: Annotated[Person, Field(description="User personal information")],
) -> dict:
"""Create a new user."""
passThe generated
InspirationThe implementation is based on ADK's existing Let me know if you'd like me to add any additional test cases or edge cases! |
Summary
This PR implements Option A from issue #4552, adding support for per-parameter descriptions in
FunctionToolusing theAnnotatedtype hint withpydantic.Field(description=...).Annotated[T, Field(description=...)]syntaxAnnotated[T, ...]for proper model creationMotivation
Per-parameter descriptions enable contextual prompting at the tool level without polluting agent system prompts. This allows developers to:
PROJECT-123)Example Usage
Changes
Modified Files
src/google/adk/tools/_automatic_function_calling_util.py:_extract_field_info_from_annotated()helper function_extract_base_type_from_annotated()helper function_get_fields_dict()to extract and use parameter descriptionsNew Files
tests/unittests/tools/test_annotated_parameter_descriptions.py: Comprehensive tests for the new functionalityTesting
All new and existing tests pass:
Closes #4552