Skip to content

Conversation

@ks93
Copy link
Contributor

@ks93 ks93 commented Oct 1, 2025

🎯 Overview

Add ClientToolAction.from_function() method to automatically generate client tool actions from Python functions using type hints and docstrings. This significantly reduces boilerplate and improves developer experience.

🔗 Dependencies

⚠️ This PR is based on #2350 (feat/agents-actions-v2)

This PR should be merged after #2350 is merged into main.

✨ What's New

Feature: from_function() Class Method

Automatically generate action definitions from Python functions:

def add(a: float, b: float) -> float:
    """Add two numbers.
    
    Args:
        a: The first number.
        b: The second number.
    """
    return a + b

# Before: ~50 lines of manual JSON Schema definition
# After: One line!
action = ClientToolAction.from_function(add)

# Use in agent chat
response = client.agents.chat(
    agent_id="my_agent",
    messages=Message("What is 42 plus 58?"),
    actions=[action]
)

Supported Types

Primitives: int, float, str, bool
Lists: list[int], list[float], list[str], list[bool]
Optional parameters: Parameters with = None are marked optional
Custom names: Override action name via name parameter

Docstring Parsing

Supports Google-style docstrings:

  • Function description becomes action description
  • Parameter descriptions extracted from Args: section
  • Multi-line descriptions supported

🏗️ Architecture

Refactoring: Moved to Utils Module

Following SDK best practices, introspection logic is now in a dedicated utility module:

New Files:

  • cognite/client/utils/_function_introspection.py - Core introspection utilities
  • tests/tests_unit/test_utils/test_function_introspection.py - 25 unit tests

Benefits:

  • ✅ Reusable across SDK (not agent-specific)
  • ✅ Better separation of concerns
  • ✅ Easier to test and maintain
  • ✅ Follows patterns like _text.py, _validation.py

No Dependencies Added

Keeps SDK lean - uses only Python stdlib:

  • inspect - Function signature introspection
  • typing - Type hint processing
  • re - Docstring parsing

💡 Example Usage

Basic Example

def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

action = ClientToolAction.from_function(add)

With Optional Parameters

def greet(name: str, title: str | None = None) -> str:
    """Greet a person.
    
    Args:
        name: The person's name.
        title: Optional title (e.g., "Dr.", "Mr.").
    """
    if title:
        return f"Hello, {title} {name}!"
    return f"Hello, {name}!"

action = ClientToolAction.from_function(greet)
# name is required, title is optional

With Lists

def sum_numbers(numbers: list[float]) -> float:
    """Calculate sum of numbers.
    
    Args:
        numbers: List of numbers to sum.
    """
    return sum(numbers)

action = ClientToolAction.from_function(sum_numbers)

Custom Name

action = ClientToolAction.from_function(add, name="custom_add")

🔍 Design Decisions

  1. Optional Parameters: Detected via = None default value
  2. Missing Type Hints: Raises TypeError with clear message
  3. Missing Docstring: Warns and uses function name as description
  4. Unsupported Types: Raises TypeError with list of supported types
  5. Bare list: Raises TypeError suggesting list[type]

🚀 Future Extensibility

The utility module is designed for easy extension:

  • Add support for dict types
  • Support Pydantic models
  • Support other docstring formats (NumPy, Sphinx)
  • Support nested types

Add comprehensive support for client-side action execution in agent chat:
- Action base class with ClientToolAction and UnknownAction implementations
- ActionCall base class with ClientToolCall and UnknownActionCall for agent requests
- ActionResult message type for sending action execution results back to agent
- Updated AgentMessage to include actions field
- Updated chat() API to accept actions parameter and ActionResult messages
- Added action_calls property to AgentChatResponse for convenient access
- Following existing patterns from AgentTool and MessageContent implementations

This enables users to define custom client-side functions that the agent can
call during reasoning, receive the action calls, execute them, and send results
back to continue the conversation.
Make ActionResult follow the same polymorphic pattern as Action and ActionCall:
- ActionResult is now an abstract base class
- ClientToolResult is the concrete implementation for client tool execution results
- UnknownActionResult provides forward compatibility
- Updated API imports and documentation to use ClientToolResult
- Updated test script to use ClientToolResult

This ensures consistency across all action-related classes and allows future
action types to have their own result schemas.
Add 13 unit tests covering:
- ClientToolAction serialization/deserialization
- ClientToolCall with JSON argument parsing
- ClientToolResult creation and serialization
- Polymorphic dispatch for unknown action types
- Chat API with actions parameter
- Chat API with action result messages
- AgentChatResponse.action_calls property

All tests passing ✅
Removed 7 redundant tests that were already covered by integration tests:
- ClientToolAction dump/load (covered by chat integration tests)
- ClientToolCall load (covered by response parsing)
- ClientToolResult string content and dump (covered by chat tests)
- action_calls property edge cases (covered by main integration tests)

Kept 6 non-redundant tests:
- JSON argument serialization (unique coverage)
- Unknown action type polymorphism (unique coverage)
- MessageContent initialization (unique coverage)
- ClientToolResult deserialization (unique coverage)
- Chat with actions parameter (core integration)
- Chat with action result message (core integration)

13 tests → 6 tests (-54%) while maintaining full feature coverage.
…ospection

Implement automatic generation of client tool actions from Python functions using
type hints and docstrings, making it easier to define agent actions.

Key Features:
- Automatic JSON Schema generation from function signatures
- Google-style docstring parsing for descriptions
- Support for primitives (int, float, str, bool) and lists
- Optional parameter detection (default=None)
- Clear error messages for unsupported types

Implementation:
- Add function_introspection utility module in cognite/client/utils/
- Add ClientToolAction.from_function() classmethod
- Extract introspection logic to reusable utilities following SDK patterns

Benefits:
- Reduces boilerplate: ~50 lines of manual schema → 1 line from_function()
- Better DX: Type-safe, IDE-friendly function definitions
- Maintainable: Introspection logic separate from agent code
- Testable: 25 unit tests for utils, 15 integration tests for from_function()

Example:
    def add(a: float, b: float) -> float:
        '''Add two numbers.

        Args:
            a: First number.
            b: Second number.
        '''
        return a + b

    # Before: ~50 lines of manual JSON Schema
    # After:
    action = ClientToolAction.from_function(add)

    response = client.agents.chat(
        agent_id="my_agent",
        messages=Message("What is 42 + 58?"),
        actions=[action]
    )

Files:
- cognite/client/utils/_function_introspection.py: New utility module
- cognite/client/data_classes/agents/chat.py: Add from_function() method
- tests/tests_unit/test_utils/test_function_introspection.py: 25 unit tests
- tests/tests_unit/test_api/test_agents_actions.py: 15 integration tests

Tests: All 61 tests passing (25 new utils tests, 21 existing tests unchanged)
@ks93 ks93 requested review from a team as code owners October 1, 2025 05:07
@gemini-code-assist
Copy link
Contributor

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

@ks93 ks93 marked this pull request as draft October 1, 2025 05:10
Base automatically changed from feat/agents-actions-v2 to master October 15, 2025 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants