LangChain with Caddey

Integrate Caddey tools into a LangChain Python agent.


Caddey exposes app features as tools, and LangChain agents can dynamically choose and call these tools. This guide shows two ways to integrate Caddey tools—preloading them into your agent or loading them dynamically at runtime.

Prerequisites

  • Caddey OAuth client (create an OAuth client and note the Client ID)
  • Python 3.x and a virtual environment
  • LangChain & helpers
pip install langchain openai requests python-dotenv requests-oauthlib
  • LLM API key (e.g. OPENAI_API_KEY for ChatOpenAI)
  • Caddey OAuth Client ID (from your Caddey OAuth client)

Security tip: Store secrets in environment variables or a .env file (never commit them).

OAuth 2.0 Setup

Before using Caddey tools, you need to implement OAuth 2.0 to obtain an access token:

import os
from requests_oauthlib import OAuth2Session

def get_caddey_access_token(client_id, redirect_uri="http://localhost:8080/callback"):
    # Create OAuth2 session
    oauth = OAuth2Session(
        client_id=client_id,
        redirect_uri=redirect_uri,
        scope=['openid', 'profile', 'email']
    )

    # Get authorization URL
    authorization_url, state = oauth.authorization_url(
        'https://auth.caddey.ai/realms/caddey/protocol/openid-connect/auth'
    )

    print(f"Please go to {authorization_url} and authorize access.")
    authorization_response = input('Paste the full redirect URL here: ')

    # Fetch access token
    token = oauth.fetch_token(
        'https://auth.caddey.ai/realms/caddey/protocol/openid-connect/token',
        authorization_response=authorization_response,
        client_secret=None  # Public client
    )

    return token['access_token']

# Get access token
CLIENT_ID = os.getenv("CADDEY_CLIENT_ID")
CADDEY_TOKEN = get_caddey_access_token(CLIENT_ID)

Now that you have an access token, you can use it with either approach below.

Approach A: Preload Tools into LangChain

Fetch tool definitions and convert them into LangChain Tool objects.

import requests
import json
from langchain.agents import Tool

# Use the token from OAuth flow above

# Fetch tool definitions
response = requests.get(
    "https://api.caddey.ai/tools/query",
    headers={"Authorization": f"Bearer {CADDEY_TOKEN}"}
)
tools_data = response.json().get("content", [])

# Convert to LangChain Tool objects
preloaded_tools = []
for tool in tools_data:
    def make_func(tool_id):
        def run_tool(input_str: str):
            payload = json.loads(input_str) if input_str else {}
            res = requests.post(
                f"https://api.caddey.ai/tools/{tool_id}/execute",
                headers={"Authorization": f"Bearer {CADDEY_TOKEN}"},
                json=payload
            )
            return json.dumps(res.json(), indent=2)
        return run_tool

    preloaded_tools.append(
        Tool(
            name=tool.get("name", ""),
            description=tool.get("description", ""),
            func=make_func(tool.get("id", ""))
        )
    )

Approach B: Dynamic Tool Loading

Provide two generic tools that let the model fetch and execute tools on the fly.

import requests
import json
from langchain.agents import Tool

# Use the token from OAuth flow above

def list_caddey_tools(input_json: str) -> str:
    """
    input_json (optional): JSON string with keys:
      - pageNumber (int)
      - pageSize (int)
      - searchTerm (str)  # free-text filter
    """
    params = {"pageNumber": 0, "pageSize": 20}
    if input_json:
        opts = json.loads(input_json)
        params["pageNumber"] = opts.get("pageNumber", params["pageNumber"])
        params["pageSize"]   = opts.get("pageSize", params["pageSize"])
        if "searchTerm" in opts:
            params["search"] = opts["searchTerm"]

    response = requests.get("https://api.caddey.ai/tools/query", headers={"Authorization": f"Bearer {CADDEY_TOKEN}"}, params=params)
    response.raise_for_status()
    return json.dumps(response.json(), indent=2)

def execute_caddey_tool(input_json: str) -> str:
    """
    input_json: JSON string with keys:
      - tool_id (str, UUID)
      - input (object)  # the payload to pass to the tool
    """
    payload = json.loads(input_json)
    tool_id = payload["tool_id"]
    params  = payload.get("input", {})
    response = requests.post(
        f"https://api.caddey.ai/tools/{tool_id}/execute",
        headers={"Authorization": f"Bearer {CADDEY_TOKEN}"},
        json={"input": params},
    )
    response.raise_for_status()
    return json.dumps(response.json(), indent=2)

list_tools_tool = Tool(
    name="List Caddey Tools",
    description=(
        "Fetch a paginated list of Caddey tools. "
        "Input JSON may specify pageNumber, pageSize, and searchTerm (free-text)."
    ),
    func=list_caddey_tools,
)

execute_tool = Tool(
    name="Execute Caddey Tool",
    description=(
        "Execute any Caddey tool by ID. "
        "Input JSON must include tool_id and an 'input' object for its parameters."
    ),
    func=execute_caddey_tool,
)

Initialize the Agent

After setting up your tool list (either preloaded_tools from Approach A or [list_tools_tool, execute_tool] from Approach B), initialize your LangChain agent.

from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
import os

llm = ChatOpenAI(
    temperature=0,
    openai_api_key=os.getenv("OPENAI_API_KEY")
)

# Use preloaded_tools OR dynamic tools
tools_list = preloaded_tools  # or [list_tools_tool, execute_tool]

agent = initialize_agent(
    tools=tools_list,
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

Using Your Agent

Simply prompt the agent in natural language, and it will pick the appropriate tool under the hood.

Examples:

  • Create a reminder to call Bob tomorrow at 3 PM.
  • Fetch the list of available tools.
  • Send an email to [email protected] with subject “Project Update” and body “The report is ready.”

The agent will automatically invoke the listing or execution of tools as needed.

For additional support, visit the Support Section.