Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added apicaller tool to make calls using OpenAPI specification #1508

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions cookbook/tools/apicaller_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from phi.agent import Agent
from phi.tools.apicaller import ApiCaller

"""
Use ApiCaller to generate functions from an OpenAPI spec and call APIs directly.

Prerequisites:
1. OpenAPI spec in JSON/YAML format (URL or local file path).
2. Swagger client for indirect API calls (optional):
a. Set generate_swagger=True to auto-generate using generator3.swagger.io and save it to `swagger_clients` folder.
b. Or, manually generate via editor.swagger.io and copy the swagger_client package to `swagger_clients` folder.
"""

# Petstore example
# Use any OpenAPI spec file
OPENAPI_SPEC = 'https://petstore3.swagger.io/api/v3/openapi.json'
# Generate swagger client and copy the client package to the current directory
CLIENT_PACKAGE = 'swagger_client'
agent = Agent(tools=[ApiCaller(CLIENT_PACKAGE, OPENAPI_SPEC,
generate_swagger=True,
configuration={'host': 'https://petstore3.swagger.io/api/v3'})],
show_tool_calls=True)
agent.print_response("Get pet id 1", markdown=True)

# Spotify example

# # Use any OpenAPI spec file
# OPENAPI_SPEC = 'https://raw.githubusercontent.com/sonallux/spotify-web-api/refs/heads/main/official-spotify-open-api.yml'
# # Generate swagger client and copy the client package to the current directory
# CLIENT_PACKAGE = 'swagger_client'
# Sptofy access token
# ACCESS_TOKEN = 'ACCESS_TOKEN'
#swagger_caller = SwaggerCaller(CLIENT_PACKAGE, OPENAPI_SPEC, configuration={'access_token': ACCESS_TOKEN})

# agent = Agent(tools=[ApiCaller(CLIENT_PACKAGE, OPENAPI_SPEC,
# generate_swagger=True,
# configuration={'access_token': ACCESS_TOKEN}],
# show_tool_calls=True, debug_mode=True)
# agent.print_response("make me a playlist with the first song from kind of blue. call it machine blues.", markdown=True)
48 changes: 48 additions & 0 deletions phi/tools/apicaller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json

from pydantic.alias_generators import to_snake

from phi.tools import Toolkit, Function

try:
from apicaller import SwaggerCaller
except ImportError as e:
raise ImportError("`apicaller` not installed. Please install using `pip install six urllib3 pyapicaller`")

def to_dict(obj):
if isinstance(obj, list):
return [to_dict(item) for item in obj]
if hasattr(obj, 'to_dict'):
return obj.to_dict()
return obj

class ApiCaller(Toolkit):
def __init__(
self,
swagger_client: str,
openapi: str,
path: str = 'swagger_clients',
configuration: dict = None,
generate_swagger = False
):
super().__init__(name="apicaller")
self._caller = SwaggerCaller(swagger_client, openapi, path=path, configuration=configuration)
if generate_swagger:
self._caller.generate()
self.register_all()


def register_all(self):
def create_callable(function_name):
def api_function(**kwargs):
parameters = {to_snake(k): v for k, v in kwargs['parameters'].items()} if kwargs else {}
response = self._caller.call_api(function_name, **parameters)
return json.dumps(to_dict(response))
return api_function

for f_dict in self._caller.get_tools():
f = Function(name=f_dict['function']['name'],
description=f_dict['function']['description'],
parameters=f_dict['function']['parameters'],
entrypoint=create_callable(f_dict['function']['name']))
self.functions[f.name] = f