Use AWS Bedrock function calling with AI Proxy Advanced

Deployment Platform
Minimum Version
Kong Gateway - 3.10
TL;DR

Configure AI Proxy Advanced with the bedrock provider, llm_format: bedrock, and llm/v1/chat route type. Point a Boto3 client at the AI Gateway route. The model can request tool calls, and the client sends results back through the same route.

Prerequisites

This is a Konnect tutorial and requires a Konnect personal access token.

  1. Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.

  2. Export your token to an environment variable:

     export KONNECT_TOKEN='YOUR_KONNECT_PAT'
    
  3. Run the quickstart script to automatically provision a Control Plane and Data Plane, and configure your environment:

     curl -Ls https://get.konghq.com/quickstart | bash -s -- -k $KONNECT_TOKEN --deck-output
    

    This sets up a Konnect Control Plane named quickstart, provisions a local Data Plane, and prints out the following environment variable exports:

     export DECK_KONNECT_TOKEN=$KONNECT_TOKEN
     export DECK_KONNECT_CONTROL_PLANE_NAME=quickstart
     export KONNECT_CONTROL_PLANE_URL=https://us.api.konghq.com
     export KONNECT_PROXY_URL='http://localhost:8000'
    

    Copy and paste these into your terminal to configure your session.

This tutorial requires Kong Gateway Enterprise. If you don’t have Kong Gateway set up yet, you can use the quickstart script with an enterprise license to get an instance of Kong Gateway running almost instantly.

  1. Export your license to an environment variable:

     export KONG_LICENSE_DATA='LICENSE-CONTENTS-GO-HERE'
    
  2. Run the quickstart script:

    curl -Ls https://get.konghq.com/quickstart | bash -s -- -e KONG_LICENSE_DATA 
    

    Once Kong Gateway is ready, you will see the following message:

     Kong Gateway Ready
    

decK is a CLI tool for managing Kong Gateway declaratively with state files. To complete this tutorial, install decK version 1.43 or later.

This guide uses deck gateway apply, which directly applies entity configuration to your Gateway instance. We recommend upgrading your decK installation to take advantage of this tool.

You can check your current decK version with deck version.

For this tutorial, you’ll need Kong Gateway entities, like Gateway Services and Routes, pre-configured. These entities are essential for Kong Gateway to function but installing them isn’t the focus of this guide. Follow these steps to pre-configure them:

  1. Run the following command:

    echo '
    _format_version: "3.0"
    services:
      - name: ai-proxy
        url: https://api.openai.com
    routes:
      - name: openai-chat
        paths:
        - "/"
        service:
          name: ai-proxy
    ' | deck gateway apply -
    

To learn more about entities, you can read our entities documentation.

You must have AWS credentials with Bedrock permissions:

  • AWS Access Key ID: Your AWS access key
  • AWS Secret Access Key: Your AWS secret key
  • Region: AWS region where Bedrock is available (for example, us-west-2)
  1. Enable the Cohere Command R model in the AWS Bedrock console under Model Access. Navigate to Bedrock > Model access and request access to cohere.command-r-v1:0.

  2. Export the required values as environment variables:

    export DECK_AWS_ACCESS_KEY_ID="<your-access-key-id>"
    export DECK_AWS_SECRET_ACCESS_KEY="<your-secret-access-key>"
    export DECK_AWS_REGION="us-west-2"
    

Install Python 3 and the required libraries:

pip install boto3

Configure the plugin

Configure AI Proxy Advanced to proxy native AWS Bedrock Converse API requests. The llm_format: bedrock setting tells Kong to accept native Bedrock API payloads and forward them to the correct Bedrock endpoint.

echo '
_format_version: "3.0"
plugins:
  - name: ai-proxy-advanced
    config:
      llm_format: bedrock
      targets:
      - route_type: llm/v1/chat
        auth:
          allow_override: false
          aws_access_key_id: "${{ env "DECK_AWS_ACCESS_KEY_ID" }}"
          aws_secret_access_key: "${{ env "DECK_AWS_SECRET_ACCESS_KEY" }}"
        model:
          provider: bedrock
          name: cohere.command-r-v1:0
          options:
            bedrock:
              aws_region: "${{ env "DECK_AWS_REGION" }}"
' | deck gateway apply -

The config.llm_format: bedrock setting enables Kong to accept native AWS Bedrock API requests. Kong detects the Converse API request pattern and routes it to the Bedrock Runtime service.

Use AWS Bedrock function calling

The Bedrock Converse API supports function calling (tool use), which lets a model request execution of locally defined functions. The model doesn’t execute functions directly. Instead, it returns a tool_use stop reason with the function name and input arguments. Your application runs the function and sends the result back to the model for a final response.

The following script defines a top_song tool that returns the most popular song for a given radio station call sign. The model receives a user question, decides to call the tool, and then incorporates the tool result into its final answer.

Create the script:

cat > bedrock-tool-use-demo.py << 'EOF'
#!/usr/bin/env python3
"""Demonstrate AWS Bedrock function calling (tool use) through Kong's AI Gateway"""

import logging
import json
import boto3
from botocore.exceptions import ClientError

GATEWAY_URL = "http://localhost:8000"

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)


class StationNotFoundError(Exception):
    """Raised when a radio station isn't found."""
    pass


def get_top_song(call_sign):
    """Returns the most popular song for the given radio station call sign."""
    if call_sign == "WZPZ":
        return "Elemental Hotel", "8 Storey Hike"
    raise StationNotFoundError(f"Station {call_sign} not found.")


def generate_text(bedrock_client, model_id, tool_config, input_text):
    """Sends a message to Bedrock and handles tool use if the model requests it."""

    logger.info("Sending request to model %s", model_id)

    messages = [{"role": "user", "content": [{"text": input_text}]}]

    response = bedrock_client.converse(
        modelId=model_id, messages=messages, toolConfig=tool_config
    )

    output_message = response["output"]["message"]
    messages.append(output_message)
    stop_reason = response["stopReason"]

    if stop_reason == "tool_use":
        tool_requests = output_message["content"]
        for tool_request in tool_requests:
            if "toolUse" not in tool_request:
                continue

            tool = tool_request["toolUse"]
            logger.info(
                "Model requested tool: %s (ID: %s)", tool["name"], tool["toolUseId"]
            )

            if tool["name"] == "top_song":
                try:
                    song, artist = get_top_song(tool["input"]["sign"])
                    tool_result = {
                        "toolUseId": tool["toolUseId"],
                        "content": [{"json": {"song": song, "artist": artist}}],
                    }
                except StationNotFoundError as err:
                    tool_result = {
                        "toolUseId": tool["toolUseId"],
                        "content": [{"text": err.args[0]}],
                        "status": "error",
                    }

                messages.append(
                    {"role": "user", "content": [{"toolResult": tool_result}]}
                )

                response = bedrock_client.converse(
                    modelId=model_id, messages=messages, toolConfig=tool_config
                )
                output_message = response["output"]["message"]

    for content in output_message["content"]:
        print(json.dumps(content, indent=4))


def main():
    model_id = "cohere.command-r-v1:0"
    input_text = "What is the most popular song on WZPZ?"

    tool_config = {
        "tools": [
            {
                "toolSpec": {
                    "name": "top_song",
                    "description": "Get the most popular song played on a radio station.",
                    "inputSchema": {
                        "json": {
                            "type": "object",
                            "properties": {
                                "sign": {
                                    "type": "string",
                                    "description": "The call sign for the radio station for which you want the most popular song. Example call signs are WZPZ and WKRP.",
                                }
                            },
                            "required": ["sign"],
                        }
                    },
                }
            }
        ]
    }

    bedrock_client = boto3.client(
        "bedrock-runtime",
        endpoint_url=GATEWAY_URL,
        region_name="us-west-2",
        aws_access_key_id="dummy",
        aws_secret_access_key="dummy",
    )

    try:
        print(f"Question: {input_text}")
        generate_text(bedrock_client, model_id, tool_config, input_text)
    except ClientError as err:
        message = err.response["Error"]["Message"]
        logger.error("A client error occurred: %s", message)
        print(f"A client error occurred: {message}")
    else:
        print(f"Finished generating text with model {model_id}.")


if __name__ == "__main__":
    main()
EOF

The script creates a Boto3 client pointed at the AI Gateway endpoint (http://localhost:8000) instead of directly at AWS. AI Gateway handles AWS authentication, so the client uses dummy credentials. The allow_override: false setting in the plugin configuration ensures that Kong always uses its own credentials, regardless of what the client sends.

The conversation flow works as follows:

  1. The client sends the user question and tool definition to the model through Kong.
  2. The model responds with a tool_use stop reason and the top_song function call with {"sign": "WZPZ"}.
  3. The client executes get_top_song("WZPZ") locally and sends the result back to the model through Kong.
  4. The model generates a final text response that incorporates the tool result.

Validate the configuration

Run the script:

python3 bedrock-tool-use-demo.py

Expected output:

INFO: Sending request to model cohere.command-r-v1:0
INFO: Model requested tool: top_song (ID: tooluse_abc123)
Question: What is the most popular song on WZPZ?
{
    "text": "The most popular song on WZPZ is \"Elemental Hotel\" by 8 Storey Hike."
}
Finished generating text with model cohere.command-r-v1:0.

The output confirms that AI Gateway correctly proxied both the initial Converse API request and the follow-up tool result message to AWS Bedrock. The model received the top_song tool output and generated a natural language response that includes the song title and artist.

If the request fails with authentication errors, verify that the aws_access_key_id and aws_secret_access_key in your Kong plugin configuration are valid and that the Cohere Command R model is enabled in your AWS Bedrock console.

Cleanup

If you created a new control plane and want to conserve your free trial credits or avoid unnecessary charges, delete the new control plane used in this tutorial.

curl -Ls https://get.konghq.com/quickstart | bash -s -- -d

FAQs

Function calling (also called tool use) allows a model to request external function execution during a conversation. The model returns a tool_use stop reason along with the function name and arguments. Your application executes the function locally and sends the result back to the model, which then generates a final response that incorporates the function output.

Cohere Command R and Command R+, Anthropic Claude 3 and later, and Amazon Titan models support function calling through the Converse API. Check the AWS documentation for the full compatibility matrix.

AI Gateway handles authentication with AWS Bedrock on behalf of the client (auth.allow_override: false). The Boto3 client still requires credentials to sign HTTP requests, but AI Gateway replaces them before forwarding to Bedrock. The dummy credentials never reach AWS.

Something wrong?

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!