The xpander.ai SDK provides a powerful tool execution system that enables agents to interact with external APIs, services, and local functions. This guide covers how to work with tools, execute tool calls, and implement local tools.

Tool Execution Flow

The typical flow for tool execution involves these steps:

1

Get available tools

Get the agent’s tools formatted for the LLM provider.

tools = agent.get_tools(llm_provider=LLMProvider.OPEN_AI)
2

Send tools to the LLM

Include the tools in the request to the LLM.

response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=agent.messages,
    tools=tools,
    tool_choice="auto"
)
3

Extract tool calls

Extract tool calls from the LLM response.

tool_calls = XpanderClient.extract_tool_calls(
    llm_response=response.model_dump(),
    llm_provider=LLMProvider.OPEN_AI
)
4

Execute tool calls

Run the tools and get results.

results = agent.run_tools(tool_calls=tool_calls)

Getting Tools

get_tools()

Returns the tools available to the agent in a format compatible with the specified LLM provider.

from xpander_sdk import LLMProvider

# Get tools formatted for OpenAI
openai_tools = agent.get_tools(llm_provider=LLMProvider.OPEN_AI)

# Print the first tool
if openai_tools:
    print(f"First tool: {openai_tools[0]['function']['name']}")
    print(f"Description: {openai_tools[0]['function']['description']}")

Parameters

ParameterTypeRequiredDescription
llm_providerLLMProviderNoThe LLM provider type (default: LLMProvider.OPEN_AI)

Returns

Returns a list of tool definitions in the format required by the specified LLM provider.

Executing Tools

run_tool()

Executes a single tool call.

from xpander_sdk import ToolCall, ToolCallType

# Create a tool call for web search
tool_call = ToolCall(
    name="web_search",
    type=ToolCallType.XPANDER,
    payload={
        "bodyParams": {
            "query": "AI startups in San Francisco"
        }
    },
    tool_call_id="call_1234abcd"
)

# Execute the tool
result = agent.run_tool(tool=tool_call)

# Print the result
print(f"Tool: {result.function_name}")
print(f"Success: {result.is_success}")
print(f"Result: {result.result[:100]}...")  # Print first 100 chars

Parameters

ParameterTypeRequiredDescription
toolToolCallYesThe tool call to execute
payload_extensionDictNoAdditional parameters to add to the call
is_multiplebooleanNoWhether this is part of a multi-tool call

Returns

Returns a ToolCallResult containing the execution results.

run_tools()

Executes multiple tool calls at once.

# Extract tool calls from LLM response
tool_calls = XpanderClient.extract_tool_calls(
    llm_response=response.model_dump(),
    llm_provider=LLMProvider.OPEN_AI
)

# Run all tool calls
results = agent.run_tools(tool_calls=tool_calls)

# Process results
for result in results:
    print(f"Tool: {result.function_name}")
    print(f"Success: {result.is_success}")
    if result.is_success:
        print(f"Result: {result.result[:50]}...")
    else:
        print(f"Error: {result.error}")
    print("-" * 40)

Parameters

ParameterTypeRequiredDescription
tool_callsList[ToolCall]YesList of tool calls to execute
payload_extensionDictNoAdditional parameters for all tool calls

Returns

Returns a list of ToolCallResult objects containing the execution results.

Working with Local Tools

Local tools are functions that you define and run in your local environment. They provide a way to extend the agent’s capabilities with custom functionality.

add_local_tools()

Adds local functions as tools that the agent can use.

from xpander_sdk import ToolCallType

# Define local tool functions
def calculate_mortgage(principal: float, rate: float, years: int) -> str:
    """Calculate monthly mortgage payment."""
    monthly_rate = rate / 100 / 12
    payments = years * 12
    payment = principal * (monthly_rate * (1 + monthly_rate) ** payments) / ((1 + monthly_rate) ** payments - 1)
    return f"Monthly payment: ${payment:.2f} for {years} years at {rate}% interest on ${principal:.2f} principal"

def format_date(date: str, format: str) -> str:
    """Format a date string."""
    from datetime import datetime
    try:
        dt = datetime.fromisoformat(date.replace('Z', '+00:00'))
        if format == "MM/DD/YYYY":
            return dt.strftime("%m/%d/%Y")
        elif format == "DD/MM/YYYY":
            return dt.strftime("%d/%m/%Y")
        else:
            return dt.strftime(format)
    except Exception as e:
        return f"Error: {str(e)}"

# Create tool declarations
local_tools = [
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "calculate_mortgage",
                "description": "Calculate monthly mortgage payment",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "principal": {
                            "type": "number",
                            "description": "Loan principal amount"
                        },
                        "rate": {
                            "type": "number",
                            "description": "Annual interest rate (percentage)"
                        },
                        "years": {
                            "type": "integer",
                            "description": "Loan term in years"
                        }
                    },
                    "required": ["principal", "rate", "years"]
                }
            }
        },
        "fn": calculate_mortgage
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "format_date",
                "description": "Format a date string",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "date": {
                            "type": "string",
                            "description": "ISO format date string"
                        },
                        "format": {
                            "type": "string",
                            "description": "Output format (MM/DD/YYYY, DD/MM/YYYY, or custom format)"
                        }
                    },
                    "required": ["date", "format"]
                }
            }
        },
        "fn": format_date
    }
]

# Add local tools to the agent
agent.add_local_tools(tools=local_tools)

Executing Local Tools

To execute local tools, you need to extract the local tool calls and run them manually:

# Extract all tool calls from LLM response
all_tool_calls = XpanderClient.extract_tool_calls(
    llm_response=response.model_dump(),
    llm_provider=LLMProvider.OPEN_AI
)

# Execute remote tools (handled by xpander.ai)
remote_results = agent.run_tools(tool_calls=all_tool_calls)

# Extract local tool calls
local_tool_calls = XpanderClient.retrieve_pending_local_tool_calls(
    tool_calls=all_tool_calls
)

# Helper for looking up local functions
local_tools_by_name = {tool['declaration']['function']['name']: tool['fn'] 
                    for tool in local_tools}

# Execute local tool calls
local_results = []
for tc in local_tool_calls:
    # Create result object
    result = ToolCallResult(
        function_name=tc.name,
        tool_call_id=tc.tool_call_id,
        payload=tc.payload
    )
    
    try:
        if tc.name in local_tools_by_name:
            # Execute the local function
            result.is_success = True
            result.result = local_tools_by_name[tc.name](**tc.payload)
        else:
            result.is_success = False
            result.error = f"Tool '{tc.name}' not found"
    except Exception as e:
        result.is_success = False
        result.error = str(e)
    
    local_results.append(result)

# Add local results to memory (so the agent knows what happened)
agent.memory.add_tool_call_results(tool_call_results=local_results)

# Combine results for processing
all_results = remote_results + local_results
print(f"Executed {len(all_results)} tools ({len(local_results)} local)")

Example: Calculator Agent with Local Tools

This example shows a complete implementation of an agent that uses local tools for calculations:

from xpander_sdk import XpanderClient, ToolCall, ToolCallType, ToolCallResult, LLMProvider
from openai import OpenAI
from dotenv import load_dotenv
import os
import math

# Load environment variables
load_dotenv()
XPANDER_API_KEY = os.environ["XPANDER_API_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
AGENT_ID = os.environ["AGENT_ID"]

# Initialize clients
xpander_client = XpanderClient(api_key=XPANDER_API_KEY)
openai_client = OpenAI(api_key=OPENAI_API_KEY)

# Get the agent
agent = xpander_client.agents.get(agent_id=AGENT_ID)
print(f"Agent: {agent.name} ({agent.id})")

# Define local calculator functions
def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

def subtract(a: float, b: float) -> float:
    """Subtract b from a."""
    return a - b

def multiply(a: float, b: float) -> float:
    """Multiply two numbers."""
    return a * b

def divide(a: float, b: float) -> float:
    """Divide a by b."""
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

def square_root(a: float) -> float:
    """Calculate square root of a number."""
    if a < 0:
        raise ValueError("Cannot calculate square root of negative number")
    return math.sqrt(a)

def power(base: float, exponent: float) -> float:
    """Calculate base raised to exponent."""
    return math.pow(base, exponent)

# Define tool declarations
local_tools = [
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "add",
                "description": "Add two numbers",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "a": {"type": "number", "description": "First number"},
                        "b": {"type": "number", "description": "Second number"}
                    },
                    "required": ["a", "b"]
                }
            }
        },
        "fn": add
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "subtract",
                "description": "Subtract b from a",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "a": {"type": "number", "description": "First number"},
                        "b": {"type": "number", "description": "Second number"}
                    },
                    "required": ["a", "b"]
                }
            }
        },
        "fn": subtract
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "multiply",
                "description": "Multiply two numbers",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "a": {"type": "number", "description": "First number"},
                        "b": {"type": "number", "description": "Second number"}
                    },
                    "required": ["a", "b"]
                }
            }
        },
        "fn": multiply
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "divide",
                "description": "Divide a by b",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "a": {"type": "number", "description": "First number"},
                        "b": {"type": "number", "description": "Second number"}
                    },
                    "required": ["a", "b"]
                }
            }
        },
        "fn": divide
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "square_root",
                "description": "Calculate square root of a number",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "a": {"type": "number", "description": "Number to calculate square root of"}
                    },
                    "required": ["a"]
                }
            }
        },
        "fn": square_root
    },
    {
        "declaration": {
            "type": "function",
            "function": {
                "name": "power",
                "description": "Calculate base raised to exponent",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "base": {"type": "number", "description": "Base number"},
                        "exponent": {"type": "number", "description": "Exponent to raise base to"}
                    },
                    "required": ["base", "exponent"]
                }
            }
        },
        "fn": power
    }
]

# Add local tools to the agent
agent.add_local_tools(tools=local_tools)

# Add a task
agent.add_task(input="Calculate the square root of 16 multiplied by 4")
print(f"Added task: {agent.execution.input_message}")

# Initialize memory
agent.memory.init_messages(
    input=agent.execution.input_message,
    instructions=agent.instructions,
    llm_provider=LLMProvider.OPEN_AI
)

# Run the agent
while not agent.is_finished():
    # Get next action from LLM
    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=agent.messages,
        tools=agent.get_tools(),
        tool_choice="auto",
        temperature=0.0
    )
    
    # Add LLM response to memory
    agent.memory.add_messages(messages=response.model_dump())
    
    # Extract tool calls
    all_tool_calls = XpanderClient.extract_tool_calls(
        llm_response=response.model_dump(),
        llm_provider=LLMProvider.OPEN_AI
    )
    
    if all_tool_calls:
        # Execute remote tools (if any)
        remote_results = agent.run_tools(tool_calls=all_tool_calls)
        
        # Extract and execute local tool calls
        local_tool_calls = XpanderClient.retrieve_pending_local_tool_calls(
            tool_calls=all_tool_calls
        )
        
        if local_tool_calls:
            # Helper for looking up local functions
            local_tools_by_name = {tool['declaration']['function']['name']: tool['fn'] 
                                for tool in local_tools}
            
            # Execute local tool calls
            local_results = []
            for tc in local_tool_calls:
                result = ToolCallResult(
                    function_name=tc.name,
                    tool_call_id=tc.tool_call_id,
                    payload=tc.payload
                )
                
                try:
                    if tc.name in local_tools_by_name:
                        result.is_success = True
                        result.result = str(local_tools_by_name[tc.name](**tc.payload))
                    else:
                        result.is_success = False
                        result.error = f"Tool '{tc.name}' not found"
                except Exception as e:
                    result.is_success = False
                    result.error = str(e)
                
                local_results.append(result)
            
            # Add local results to memory
            agent.memory.add_tool_call_results(tool_call_results=local_results)
            print(f"Executed {len(local_results)} local tools")

# Get final result
result = agent.retrieve_execution_result()
print("\nRESULT:")
print(result.result)