# Function Calling

{% hint style="info" %}
Function calling lets models use APIs or functions to perform tasks, get real-time data, and respond intelligently to user input.
{% endhint %}

Function or tool calling enables models to connect to predefined functions and APIs, allowing them to carry out tasks, retrieve real-time data, and interact with various systems. It’s useful for building more capable assistants, chat applications, and agentic systems that need to take action based on user input.

Before looking at an example, let's define some **core concepts:**

## Core Concepts

### Tools

Tools or functions provide a model with access to specific capabilities or data sources. Each tool has a name, description, and parameter schema. When included in a request, the model can choose to when use them.

### Tool Calls

A tool call is a response from the model that occurs when it has access to a set of tools and determines that using one of them is necessary to correctly address the question or instructions in the prompt. Here are key parameters to pass when calling the model related to function/tool calling:&#x20;

* `tools`  Defines the functions the model can call, including their names, descriptions, and parameter schemas.
* `tool_choice` Controls whether the model should decide when to call a tool. The supported value is `auto`, which lets the model choose based on the prompt

### Tool Call Outputs

This is the result of running the tool or function with the arguments the model provided. These are provided to the model, and the model uses it to generate the final answer.

* `tool_calls`Contains the list of tool calls returned by the model. Each entry includes the tool name, the arguments the model generated, and an ID used to link your tool output back to the call.
* `function.name` The name of the tool or function the model selected.
* `function.arguments` A JSON string with the arguments the model generated for the tool.
* `id` A unique identifier for the tool call, useful when handling multiple calls/requests or in multi-turn settings.

***

## Example: Function Calling with Custom Python Functions

This example demonstrates how to use function calling with an OpenAI-compatible model to **fetch real-time financial data** using the `yfinance` library. The model can retrieve stock prices, CEO names, and business summaries for publicly traded companies by invoking custom Python functions.

Before running the example, make sure the following Python packages are installed:

```python
pip install -qU httpx[http2] yfinance openai
```

* `httpx` is an HTTP client for Python that provides asynchronous support.
* The `[http2]` extra enables HTTP/2 support, which improves efficiency in communication with APIs.

<details>

<summary>Expand for Example Code</summary>

```python
import json
import yfinance as yf
from openai import OpenAI
import httpx

# Initialize client
endpoint = "https://api.arcee.ai/api/v1"
model = "trinity-nano-6b"
api_key = "<YOUR API KEY HERE>"

client = OpenAI(
    base_url=endpoint,
    api_key=api_key,
    http_client=httpx.Client(http2=True)
)

# Define stock price function
def get_stock_price(company_name: str, stock_symbol: str) -> dict:
    """Get the last closing price of a company's stock"""
    try:
        stock = yf.Ticker(stock_symbol)
        hist = stock.history(period="1d")

        if hist.empty:
            return {
                "error": f"No price data found for {company_name} ({stock_symbol})."
            }

        price = float(hist["Close"].iloc[-1])
        return {"price": price}

    except Exception as e:
        return {"error": str(e)}

# Define CEO name function
def get_ceo_name(company_name: str, stock_symbol: str) -> dict:
    """Get the CEO of the company"""
    try:
        info = yf.Ticker(stock_symbol).info

        officers = info.get("companyOfficers", [])
        if not officers:
            return {"error": "No officer data available."}

        ceo = officers[0]
        return {
            "ceo": ceo.get("name", "Unknown"),
            "title": ceo.get("title", "Unknown")
        }

    except Exception as e:
        return {"error": str(e)}

# Define company summary function
def get_company_summary(company_name: str, stock_symbol: str) -> dict:
    """Get a company's business summary"""
    try:
        info = yf.Ticker(stock_symbol).info
        summary = info.get("longBusinessSummary")
        if not summary:
            return {"error": "No summary available."}

        return {"summary": summary}

    except Exception as e:
        return {"error": str(e)}

# Define function tools
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "Get the last closing price of a company's stock",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {
                        "type": "string",
                        "description": "Company name, e.g.: Apple, Microsoft, Chipotle"
                    },
                    "stock_symbol": {
                        "type": "string",
                        "description": "Stock ticker symbol, e.g.: AAPL, MSFT, CMG"
                    }
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ceo_name",
            "description": "Get the CEO of the company",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {
                        "type": "string",
                        "description": "Company name, e.g.: Apple, Microsoft, Chipotle"
                    },
                    "stock_symbol": {
                        "type": "string",
                        "description": "Stock ticker symbol, e.g.: AAPL, MSFT, CMG"
                    }
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_company_summary",
            "description": "Get a company's business summary",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {
                        "type": "string",
                        "description": "Company name, e.g.: Apple, Microsoft, Chipotle"
                    },
                    "stock_symbol": {
                        "type": "string",
                        "description": "Stock ticker symbol, e.g.: AAPL, MSFT, CMG"
                    }
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    }
]

# Example Query
user_prompt = "What's the last closing price of Chipotle stock?"

response = client.chat.completions.create(
    model=model,
    messages=[
        {"role": "user", "content": user_prompt}
    ],
    tools=tools,
    tool_choice="auto"
)

# Handle function calls
message = response.choices[0].message
messages = [{"role": "user", "content": user_prompt}]
messages.append(message)

if message.tool_calls:
    for tool_call in message.tool_calls:
        # Parse parameters
        args = json.loads(tool_call.function.arguments)
        if isinstance(args, str):
            args = json.loads(args)

        print(f"Calling {tool_call.function.name} with arguments: {args}")

        # Execute the Python functions
        if tool_call.function.name == "get_stock_price":
            result = get_stock_price(args.get("company_name"), args.get("stock_symbol"))
        elif tool_call.function.name == "get_ceo_name":
            result = get_ceo_name(args.get("company_name"), args.get("stock_symbol"))
        elif tool_call.function.name == "get_company_summary":
            result = get_company_summary(args.get("company_name"), args.get("stock_symbol"))
        else:
            result = {"error": f"Unknown function: {tool_call.function.name}"}

        print(f"Tool result: {result}")

        # Return function result to model
        messages.append({
            "role": "tool",
            "content": json.dumps(result, ensure_ascii=False),
            "tool_call_id": tool_call.id
        })

    # Final model call
    final_response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=tools,
        tool_choice="none"
    )

    print("Final output:")
    print(final_response.choices[0].message.content)

else:
    if message.content:
        print(message.content)
    else:
        print("No content in response")



```

</details>

### Initializing the Client - Optimizing with Httpx

```python
endpoint = "https://api.arcee.ai/api/v1"
model = "trinity-26b"
api_key="YOUR API KEY GOES HERE"
client = OpenAI(
    base_url=endpoint,
    api_key=api_key,
    http_client=httpx.Client(http2=True)
)
```

* **`http_client=httpx.Client(http2=True)`**:
  * Configures the HTTP client with HTTP/2 support for faster, more efficient communication.

### Custom functions for stock research

```python
def get_stock_price(company_name: str, stock_symbol: str) -> dict:
    try:
        stock = yf.Ticker(stock_symbol)
        hist = stock.history(period="1d")
        if hist.empty:
            return {"error": f"No price data found for {company_name} ({stock_symbol})."}
        return {"price": float(hist["Close"].iloc[-1])}
    except Exception as e:
        return {"error": str(e)}
```

```python
def get_ceo_name(company_name: str, stock_symbol: str) -> dict:
    try:
        info = yf.Ticker(stock_symbol).info
        officers = info.get("companyOfficers", [])
        if not officers:
            return {"error": "No officer data available."}
        ceo = officers[0]
        return {
            "ceo": ceo.get("name", "Unknown"),
            "title": ceo.get("title", "Unknown")
        }
    except Exception as e:
        return {"error": str(e)}
```

```python
def get_company_summary(company_name: str, stock_symbol: str) -> dict:
    try:
        info = yf.Ticker(stock_symbol).info
        summary = info.get("longBusinessSummary")
        if not summary:
            return {"error": "No summary available."}
        return {"summary": summary}
    except Exception as e:
        return {"error": str(e)}
```

<figure><img src="https://2302384909-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOUJInHjoAOfzq3bL12Af%2Fuploads%2F1VfOMQqL11onGmmwljEv%2FScreenshot%202025-02-02%20at%2011.49.02%E2%80%AFPM.png?alt=media&#x26;token=5dc6d225-4de1-437e-bf64-15dbef8b6bd9" alt=""><figcaption></figcaption></figure>

### Register the functions as tools

```python
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_stock_price",
            "description": "Get the last closing price of a company's stock",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {"type": "string"},
                    "stock_symbol": {"type": "string"}
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ceo_name",
            "description": "Get the CEO of the company",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {"type": "string"},
                    "stock_symbol": {"type": "string"}
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_company_summary",
            "description": "Get a company's business summary",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {"type": "string"},
                    "stock_symbol": {"type": "string"}
                },
                "required": ["company_name", "stock_symbol"]
            }
        }
    }
]

```

### Step 5: Creating the Initial Model Call (Function Calling)

By setting `tool_choice="auto"`, the model will decide whether to call a tool and which to call, or answer directly.

```python
user_prompt = "What's the last closing price of Chipotle stock?"

response = client.chat.completions.create(
    model=model,
    messages=[
        {"role": "user", "content": user_prompt}
    ],
    tools=tools,
    tool_choice="auto"
)
```

At this stage, the model responds either with:

* a function/tool call including arguments, or
* a normal text response (if no function is needed).

### Handling Tool Calls and Returning a Response

After the user prompt is sent, the model may respond with one or more tool calls. This step processes those tool calls, executes the corresponding functions locally, and sends the results back to the model to complete the conversation.

```python

# Prepare message history
message = response.choices[0].message
messages = [{"role": "user", "content": user_prompt}, message]

# Execute tool calls if present
if message.tool_calls:
    for tool_call in message.tool_calls:
        args = json.loads(tool_call.function.arguments)
        if isinstance(args, str):  # Handle double-encoding
            args = json.loads(args)

        # Dispatch the correct tool
        if tool_call.function.name == "get_stock_price":
            result = get_stock_price(args.get("company_name"), args.get("stock_symbol"))
        elif tool_call.function.name == "get_ceo_name":
            result = get_ceo_name(args.get("company_name"), args.get("stock_symbol"))
        elif tool_call.function.name == "get_company_summary":
            result = get_company_summary(args.get("company_name"), args.get("stock_symbol"))
        else:
            result = {"error": f"Unknown function: {tool_call.function.name}"}

        messages.append({
            "role": "tool",
            "content": json.dumps(result, ensure_ascii=False),
            "tool_call_id": tool_call.id
        })

    # Final completion with tool results
    final_response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=tools,
        tool_choice="auto"
    )

    print(final_response.choices[0].message.content)

else:
    # If no tools were called, return model response directly
    if message.content:
        print(message.content)
    else:
        print("No content in response")
```

### Example Output

```python
Calling get_stock_price with arguments: {'company_name': 'Chipotle', 'stock_symbol': 'CMG'}
Tool result: {'price': 31.0}
Final output: The last closing price of Chipotle stock was **$31.0**.
```
