├── tests
└── __init__.py
├── plugnplai
├── verify_plugins
│ ├── verify_langchain.py
│ ├── __init__.py
│ ├── verify_plugnplai.py
│ └── verify_for_plugnplai.csv
├── __init__.py
├── prompt_templates.py
├── api
│ └── retrieve.py
├── api_call.py
├── embeddings.py
└── utils.py
├── docs
├── get-started
│ └── quickstart.md
├── requirements.txt
├── reference
│ ├── embeddings.rst
│ └── plugins.rst
├── modules
│ ├── utility-functions.md
│ ├── embeddings.md
│ └── plugins.md
├── examples
│ ├── get-list-of-plugins.md
│ ├── apply_plugins_three_steps.ipynb
│ ├── create_prompt_plugins.ipynb
│ └── plugin_retriever_with_langchain_agent.ipynb
├── Makefile
├── .readthedocs.yaml
├── make.bat
├── index.rst
├── README.md
└── conf.py
├── pyproject.toml
├── Makefile
├── LICENSE
├── examples
├── oauth_example
│ ├── oauth_server.py
│ └── run_plugin_with_auth.py
├── langchain_chatgpt_example.ipynb
├── retrieve_plugins_for_langchain_agent.ipynb
├── apply_plugins_three_steps.ipynb
├── create_prompt_plugins.ipynb
└── retrieve_plugins_api.ipynb
├── .gitignore
├── CONTRIBUTING.md
└── README.md
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plugnplai/verify_plugins/verify_langchain.py:
--------------------------------------------------------------------------------
1 | def test_one_plugin_langchain():
2 |
3 | return True
4 |
5 | def test_plugins_langchain():
6 |
7 | return True
--------------------------------------------------------------------------------
/docs/get-started/quickstart.md:
--------------------------------------------------------------------------------
1 | # Quickstart
2 |
3 | ## Installation
4 |
5 | You can install Plug and PlAI using pip:
6 |
7 | ```python
8 | pip install plugnplai
9 | ```
10 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx>=4.3.0
2 | furo>=2023.3.27
3 | docutils<0.17
4 | myst-parser
5 | myst-nb
6 | sphinx-autobuild
7 | sphinx_rtd_theme
8 | nbsphinx
9 | jsonref
10 | langchain
11 | tiktoken
--------------------------------------------------------------------------------
/docs/reference/embeddings.rst:
--------------------------------------------------------------------------------
1 | Embeddings
2 | ==============================
3 |
4 | .. automodule:: plugnplai.embeddings
5 | :members:
6 | :inherited-members:
7 | :undoc-members:
8 | :show-inheritance:
--------------------------------------------------------------------------------
/plugnplai/verify_plugins/__init__.py:
--------------------------------------------------------------------------------
1 | from plugnplai.verify_plugins.verify_plugnplai import test_one_plugin_plugnplai, test_plugins_plugnplai
2 | from plugnplai.verify_plugins.verify_langchain import test_one_plugin_langchain
3 |
4 | __all__ = [
5 | "test_one_plugin_plugnplai",
6 | "test_plugins_plugnplai",
7 | "test_one_plugin_langchain",
8 | "test_plugins_langchain",
9 | ]
--------------------------------------------------------------------------------
/docs/reference/plugins.rst:
--------------------------------------------------------------------------------
1 | Plugins
2 | ==============================
3 |
4 | .. autoclass:: plugnplai.plugins.Plugins
5 | :members:
6 | :inherited-members:
7 | :undoc-members:
8 | :show-inheritance:
9 |
10 | PluginObject
11 | ------------
12 |
13 | .. autoclass:: plugnplai.plugins.PluginObject
14 | :members:
15 | :inherited-members:
16 | :undoc-members:
17 | :show-inheritance:
18 |
19 | count_tokens
20 | ------------
21 | .. autofunction:: plugnplai.plugins.count_tokens
22 |
23 | build_request_body
24 | ------------
25 | .. autofunction:: plugnplai.plugins.build_request_body
26 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "plugnplai"
3 | version = "0.0.24"
4 | description = "Connect plugins to AI"
5 | authors = []
6 | license = "MIT"
7 | readme = "README.md"
8 |
9 | [tool.poetry.dependencies]
10 | python = "^3.10"
11 | requests = "^2.28.2"
12 | pyyaml = "^6.0"
13 | pydantic = "^1.10.7"
14 | jsonref = "^1.1.0"
15 | langchain = "^0.0.160"
16 | tiktoken = "^0.3.3"
17 | faiss-cpu = "^1.7.4"
18 | openai = "^0.27.6"
19 |
20 | [tool.poetry.group.dev.dependencies]
21 | black = "^23.3.0"
22 | ipykernel = "^6.23.2"
23 |
24 | [build-system]
25 | requires = ["poetry-core"]
26 | build-backend = "poetry.core.masonry.api"
--------------------------------------------------------------------------------
/docs/modules/utility-functions.md:
--------------------------------------------------------------------------------
1 | # Utility Functions
2 |
3 | ### Utility Functions
4 |
5 | The following utility functions are available in the library:
6 |
7 | * `get_plugins(endpoint)`: Get a list of available plugins from a [plugins repository](https://www.plugplai.com/).
8 | * `get_plugin_manifest(url)`: Get the AI plugin manifest from the specified plugin URL.
9 | * `get_openapi_url(url, manifest)`: Get the OpenAPI URL from the plugin manifest.
10 | * `get_openapi_spec(openapi_url)`: Get the OpenAPI specification from the specified OpenAPI URL.
11 | * `spec_from_url(url)`: Returns the Manifest and OpenAPI specification from the plugin URL.
12 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | autoformat:
2 | set -e
3 | isort .
4 | black --config pyproject.toml .
5 | flake8
6 |
7 | lint:
8 | set -e
9 | isort -c .
10 | black --check --config pyproject.toml .
11 | flake8
12 |
13 | dev-lint:
14 | pip install --upgrade black==22.8.0 coverage isort flake8 flake8-bugbear flake8-comprehensions pre-commit pooch
15 |
16 | build-docs:
17 | set -e
18 | mkdir -p docs/source/_static
19 | rm -rf docs/build
20 | rm -rf docs/source/generated
21 | cd docs && make html
22 |
23 | all: autoformat build-docs
24 | # Docs
25 | watch-docs: ## Build and watch documentation
26 | sphinx-autobuild docs/ docs/_build/html --open-browser --watch $(GIT_ROOT)/plugnplai
--------------------------------------------------------------------------------
/docs/examples/get-list-of-plugins.md:
--------------------------------------------------------------------------------
1 | # Get list of plugins
2 |
3 | Here is an example of how to get a list of plugins from the [plugnplai.com](https://plugnplai.com) API:
4 |
5 | ```python
6 | import plugnplai
7 |
8 | # Get all plugins from plugnplai.com
9 | urls = plugnplai.get_plugins()
10 |
11 | # Get ChatGPT plugins - only ChatGPT verified plugins
12 | urls = plugnplai.get_plugins(filter = 'ChatGPT')
13 |
14 | # Get working plugins - only tested plugins (in progress)
15 | urls = plugnplai.get_plugins(filter = 'working')
16 |
17 |
18 | # Get the Manifest and the OpenAPI specification from the plugin URL
19 | manifest, openapi_spec = plugnplai.spec_from_url(urls[0])
20 | ```
21 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 | livehtml:
17 | sphinx-autobuild -a -b html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(0) --watch ../sqlite_utils
18 | # Catch-all target: route all unknown targets to Sphinx using the new
19 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
20 | %: Makefile
21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
22 |
--------------------------------------------------------------------------------
/docs/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Set the version of Python and other tools you might need
9 | build:
10 | os: ubuntu-22.04
11 | tools:
12 | python: "3.9"
13 | # You can also specify other tool versions:
14 | # nodejs: "19"
15 | # rust: "1.64"
16 | # golang: "1.19"
17 |
18 | # Build documentation in the docs/ directory with Sphinx
19 | sphinx:
20 | configuration: docs/conf.py
21 |
22 | # If using Sphinx, optionally build your docs in additional formats such as PDF
23 | # formats:
24 | # - pdf
25 |
26 | # Optionally declare the Python requirements required to build your docs
27 | python:
28 | install:
29 | - requirements: docs/requirements.txt
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/modules/embeddings.md:
--------------------------------------------------------------------------------
1 | # PluginRetriever
2 |
3 | This module provides a `PluginRetriever` class that retrieves plugin information based on queries using embeddings and vector stores.
4 |
5 | ## Classes
6 |
7 | ### PluginRetriever
8 |
9 | `PluginRetriever` retrieves plugin information based on queries using embeddings and vector stores.
10 |
11 | #### Parameters
12 |
13 | - `manifests` (list): List of manifest objects.
14 | - `returnList` (list, optional): List of objects to be returned. Can be a list of URLs or a list of objects like `LangChain` `AIPlugin` object. Defaults to `None`.
15 |
16 | #### Methods
17 |
18 | - `__init__(manifests, returnList=None)`: Initializes the `PluginRetriever`.
19 | - `from_urls(urls)`: Creates a `PluginRetriever` object from a list of URLs.
20 | - `from_directory(provider='plugnplai')`: Creates a `PluginRetriever` object from a directory.
21 | - `retrieve_names(query)`: Retrieves plugin names based on a query.
22 | - `retrieve_urls(query)`: Retrieves plugin URLs based on a query.
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Eduardo Reis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/plugnplai/__init__.py:
--------------------------------------------------------------------------------
1 | from plugnplai.utils import (
2 | get_openapi_spec,
3 | get_openapi_url,
4 | get_plugin_manifest,
5 | get_plugins,
6 | spec_from_url,
7 | get_category_names,
8 | parse_llm_response
9 | )
10 | from plugnplai.embeddings import PluginRetriever
11 | from plugnplai.plugins import PluginObject, Plugins, build_request_body, count_tokens
12 | from plugnplai.api.retrieve import retrieve
13 |
14 | from importlib import metadata
15 | # use the method from LangChain (Chase, H. (2022). LangChain [Computer software]. https://github.com/hwchase17/langchain) to get the version of the package
16 | try:
17 | __version__ = metadata.version(__package__)
18 | except metadata.PackageNotFoundError:
19 | # Case where package metadata is not available.
20 | __version__ = ""
21 | del metadata # optional, avoids polluting the results of dir(__package__)
22 |
23 | __all__ = [
24 | "PluginObject",
25 | "Plugins",
26 | "PluginRetriever",
27 | "get_plugins",
28 | "get_plugin_manifest",
29 | "get_openapi_url",
30 | "get_openapi_spec",
31 | "get_category_names",
32 | "spec_from_url",
33 | "parse_llm_response",
34 | "build_request_body",
35 | "count_tokens",
36 | "retrieve"
37 | ]
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to plugnplai
2 | =====================================
3 |
4 | Overview
5 | ^^^^^^^^
6 |
7 | Plug and Plai is an open source library aiming to simplify the integration of AI plugins into open-source language models (LLMs).
8 |
9 | It provides utility functions to get a list of active plugins from https://plugnplai.com/ directory, get plugin manifests, and extract OpenAPI specifications and load plugins.
10 |
11 |
12 | .. toctree::
13 | :maxdepth: 1
14 | :caption: Getting Started
15 | :hidden:
16 |
17 | get-started/quickstart.md
18 |
19 |
20 |
21 |
22 | .. toctree::
23 | :caption: Examples
24 | :hidden:
25 |
26 | examples/get-list-of-plugins.md
27 | examples/apply_plugins_three_steps.ipynb
28 | examples/plugins_step_by_step.ipynb
29 | examples/create_prompt_plugins.ipynb
30 | examples/plugin_retriever_with_langchain_agent_clean_version.ipynb
31 | examples/plugin_retriever_with_langchain_agent.ipynb
32 |
33 |
34 |
35 | .. toctree::
36 | :maxdepth: 2
37 | :caption: Modules
38 | :hidden:
39 |
40 | modules/utility-functions.md
41 | modules/plugins.md
42 | modules/embeddings.md
43 |
44 |
45 | .. toctree::
46 | :maxdepth: 2
47 | :caption: Reference
48 | :hidden:
49 |
50 |
51 | reference/plugins.rst
52 | reference/embeddings.rst
53 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Plugnplai Documentation
2 |
3 | ## A guide for docs contributors
4 |
5 | The `docs` directory contains the sphinx source text for Plugnplai's documentation.
6 |
7 | This guide is made for anyone who's interested in running the docs locally, or
8 | contributing to the docs.
9 |
10 | ## Build Docs
11 |
12 | If you haven't already, clone the Plugnplai Github repo to a local directory:
13 |
14 |
15 | Install all dependencies required for building docs (mainly `sphinx` and its extension):
16 |
17 | ```bash
18 | pip install -r docs/requirements.txt
19 | ```
20 |
21 | Build the sphinx docs:
22 |
23 | ```bash
24 | cd docs
25 | make html
26 | ```
27 |
28 | The docs HTML files are now generated under `docs/_build/html` directory, you can preview
29 | it locally with the following command:
30 |
31 | ```bash
32 | python -m http.server 8000 -d _build/html
33 | ```
34 |
35 | And open your browser at http://0.0.0.0:8000/ to view the generated docs.
36 |
37 |
38 | ##### Watch Docs
39 |
40 | During development, it is advisable to utilize sphinx-autobuild, which offers a live-reloading server. This server automatically rebuilds the documentation and refreshes any open pages whenever changes are saved. This feature facilitates a shorter feedback loop, thereby enhancing productivity when writing documentation.
41 |
42 | Simply run the following command from Plugnplai project's root directory:
43 | ```bash
44 | make watch-docs
45 | ```
--------------------------------------------------------------------------------
/plugnplai/prompt_templates.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | today_date = datetime.today().date()
3 |
4 | # Template to be filled with the plugins description,
5 | # this template muste have a {{plugins}} variable
6 | template_gpt4 = '''
7 | # SYSTEM MESSAGE
8 | You are a large language model trained to assist humans.
9 | Knowledge Cutoff: 2021-09
10 | Current date: DATE_TODAY
11 | Below is a list of available APIs that you can utilize to fulfill user requests.
12 | When using an API, please follow the specified format to make the API call.
13 | Don't ask follow-up questions and aim to complete the task with the information provided by the user.
14 |
15 | To make an API call, use the following format (using JSON double quotes for the API call parameters):
16 |
17 | namespace.operationId({"parameter_name": "parameter_value", ...})
18 |
19 | For example, to call an API operation with the operation ID "productsUsingGET" in the "KlarnaProducts" namespace,
20 | and provide the required parameters "q" and "size", the format would be as follows:
21 |
22 | KlarnaProducts.productsUsingGET({"q": "t-shirt", "size": 3})
23 |
24 | Please ensure that you use the correct namespace and operation ID, and provide the necessary parameters for each API call.
25 | After requesting the API, refrain from writing anything else and wait for the API response, which will be delivered in a new message.
26 |
27 | ## Plugins description ('*' are required parameters):
28 |
29 | {{plugins}}
30 | # USER MESSAGE
31 | '''.replace('DATE_TODAY', str(today_date))
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 | import os
9 | import sys
10 | sys.path.insert(0, os.path.abspath('../'))
11 | print(os.path.abspath('.'))
12 | project = '🎸 plugnplai'
13 | copyright = '2023, Eduardo Reis'
14 | author = 'Eduardo Reis'
15 | release = '0.0.1'
16 |
17 | # -- General configuration ---------------------------------------------------
18 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
19 |
20 | extensions = [
21 | 'sphinx.ext.autodoc',
22 | "sphinx.ext.coverage",
23 | "sphinx.ext.autodoc.typehints",
24 | "sphinx.ext.autosummary",
25 | "sphinx.ext.napoleon",
26 | "sphinx_rtd_theme",
27 | "sphinx.ext.mathjax",
28 | "myst_nb",
29 | ]
30 |
31 | myst_heading_anchors = 4
32 | # TODO: Fix the non-consecutive header level in our docs, until then
33 | # disable the sphinx/myst warnings
34 | suppress_warnings = ["myst.header"]
35 |
36 | templates_path = ["_templates"]
37 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
38 |
39 |
40 | # -- Options for HTML output -------------------------------------------------
41 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
42 |
43 | html_theme = "furo"
44 | html_title = project + " " + "0.0.1"
45 | # html_static_path = ["_static"]
46 |
47 | html_css_files = [
48 | "css/custom.css",
49 | ]
50 | nb_execution_mode = "off"
--------------------------------------------------------------------------------
/plugnplai/api/retrieve.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | def make_request_get(url: str):
4 | """Helper function to make a GET request.
5 |
6 | Args:
7 | url (str): URL to make the GET request to.
8 |
9 | Returns:
10 | Response: HTTP response object.
11 | """
12 | response = requests.get(url)
13 | return response
14 |
15 | def retrieve(text: str, available_plugins: list):
16 | """Retrieve top plugins based on the provided text query.
17 |
18 | Args:
19 | text (str): Text query to find the top plugins.
20 | available_plugins (list): List of available plugin names.
21 |
22 | Returns:
23 | list/dict/str: Returns a list of top plugins if successful, dictionary of error info if unsuccessful, or error message if exception is caught.
24 | """
25 | base_url = "https://www.plugnplai.com/_functions"
26 | # Construct the endpoint URL based on the text query
27 | url = f'{base_url.strip("/")}/retrieve?text={text}'
28 | # Make the HTTP GET request
29 | try:
30 | response = make_request_get(url)
31 | # Check if the response status code is successful (200 OK)
32 | if response.status_code == 200:
33 | # Parse the JSON response
34 | plugins = response.json()
35 | # Filter plugins to only include ones that are in the list of available plugins
36 | filtered_plugins = [plugin for plugin in plugins if plugin in available_plugins]
37 | return filtered_plugins
38 | elif response.status_code in [400, 500]:
39 | # Handle unsuccessful responses
40 | return response.json() # Assuming the API returns error info in JSON format
41 | else:
42 | # Handle other potential status codes
43 | return f"An error occurred: {response.status_code} {response.reason}"
44 | except requests.exceptions.RequestException as e:
45 | return str(e)
46 |
--------------------------------------------------------------------------------
/examples/oauth_example/oauth_server.py:
--------------------------------------------------------------------------------
1 | # This file is example for the handling the login with oauth authentication.
2 | # it has two endpoints
3 | # 1. /login : it is used for redirecting the user to a aunthentication_url(you can get this deatils from the mantifest file) of particular plugin.
4 | # 2. /callback : it is called automatically on success or failure response of the /login endpoint.
5 | # requirements.
6 | # pip install requests_oauthlib
7 | # run this file first.
8 |
9 |
10 | import json
11 | from urllib.parse import urljoin
12 |
13 | from requests_oauthlib import OAuth2Session
14 | from flask import Flask, request, session, redirect
15 | import ssl
16 |
17 | redirect_uri = 'https://localhost:5000/callback'
18 | app = Flask(__name__)
19 | app.secret_key = "qazwsx"
20 | # Update the SSL context with the certificate and key file paths
21 |
22 | context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
23 | # add the certificate and key from the pyopenssl or generate it.
24 | context.load_cert_chain(certfile='certificate.crt', keyfile='private.key')
25 | client_id = "client_id"
26 | client_secret = "client_secret"
27 | client_url = "client_url"
28 | token_url = "authorization_token_url"
29 |
30 | @app.route('/login', methods=["GET"])
31 | def login():
32 | try:
33 | global oauth
34 | oauth = OAuth2Session(client_id, redirect_uri=redirect_uri)
35 | authorization_url, state = oauth.authorization_url(client_url)
36 | session['oauth_state'] = state
37 | return redirect(authorization_url)
38 | except Exception as e:
39 | print(e)
40 |
41 |
42 | @app.route('/callback')
43 | def callback():
44 | if 'error' in request.args:
45 | return 'Error: ' + request.args['error']
46 |
47 | token = oauth.fetch_token(
48 | token_url,
49 | client_secret=client_secret,
50 | code=request.args.get("code", "")
51 | )
52 |
53 | return token
54 |
55 |
56 | if __name__ == '__main__':
57 | app.run(debug=True, ssl_context=context)
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 | debug.py
54 | debug.ipynb
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 | local_settings.py
63 | db.sqlite3
64 | db.sqlite3-journal
65 |
66 | # Flask stuff:
67 | instance/
68 | .webassets-cache
69 |
70 | # Scrapy stuff:
71 | .scrapy
72 |
73 | # Sphinx documentation
74 | docs/_build/
75 |
76 | # PyBuilder
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 | dev_jupyter.ipynb
82 |
83 | # IPython
84 | profile_default/
85 | ipython_config.py
86 |
87 | # pyenv
88 | .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 | s.py
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to plugnplai
2 |
3 | We welcome contributions from the community. Here are the guidelines for contributing to this project:
4 |
5 | ## Reporting Issues
6 |
7 | If you encounter any bugs or issues, please file an issue on the [issue tracker](https://github.com/edreisMD/plugnplai/issues). When filing an issue, please provide:
8 |
9 | - A clear and descriptive title
10 | - A summary of the issue
11 | - Steps to reproduce the issue
12 | - The expected behavior
13 | - Screenshots if applicable
14 | - Your system information (OS, Python version, etc.)
15 |
16 | ## Contributing Code
17 |
18 | We welcome pull requests to fix bugs, add new features, and improve the codebase. Here are the steps to contribute code:
19 |
20 | 1. Fork the repository and clone your fork
21 | 2. Create a branch for your changes: `git checkout -b my-feature-branch`
22 | 3. Make your changes and commit them: `git commit -am 'Add new feature'`
23 | 4. Push the branch to your fork: `git push origin my-feature-branch`
24 | 5. Create a pull request from your branch against the `main` branch
25 | 6. Your PR will be reviewed by the maintainers, who may ask you to make some changes before merging
26 |
27 | Please make sure to follow the code style and conventions used in the project. We use [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) and [Google's Python Style Guide](https://google.github.io/styleguide/pyguide.html).
28 |
29 | ## Testing
30 |
31 | We welcome contributions that add or improve tests. Please ensure that any code changes are accompanied by tests.
32 |
33 | To run the tests, install the test dependencies and run:
34 |
35 | ```bash
36 | pip install -r requirements-dev.txt
37 | pytest
38 | ```
39 |
40 | ## Style Guide
41 |
42 | We follow [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) and [Google's Python Style Guide](https://google.github.io/styleguide/pyguide.html). Please ensure your contributions follow these style guides.
43 |
44 | To run plugnplai locally with Poetry:
45 |
46 | 1. Install Poetry if you haven't already:
47 |
48 | ```bash
49 | pip install poetry
50 | ```
51 |
52 | 2. Clone the repository:
53 |
54 | ```bash
55 | git clone https://github.com/edreisMD/plugnplai.git
56 | cd plugnplai
57 | ```
58 |
59 | 3. Install dependencies:
60 |
61 | ```bash
62 | poetry install
63 | ```
64 |
65 | 4. Activate the Poetry shell:
66 |
67 | ```bash
68 | poetry shell
69 | ```
70 |
71 | 5. Make your changes and run the tests:
72 |
73 | ```bash
74 | pytest
75 | ```
76 |
77 | 6. Commit your changes and make a pull request!
78 |
--------------------------------------------------------------------------------
/docs/modules/plugins.md:
--------------------------------------------------------------------------------
1 | ## count_tokens(text: str, model_name: str = "gpt-4") -> int
2 |
3 | Count the number of tokens in a text.
4 |
5 | ### Parameters
6 | - `text` (str): The input text.
7 | - `model_name` (str): The name of the GPT model. Defaults to "gpt-4".
8 |
9 | ### Returns
10 | - `int`: The number of tokens in the text.
11 |
12 |
13 | ## build_request_body(schema: Dict[str, Any], parameters: Dict[str, Any]) -> Any
14 |
15 | Build the request body for an API call.
16 |
17 | ### Parameters
18 | - `schema` (Dict[str, Any]): The schema for the request body.
19 | - `parameters` (Dict[str, Any]): The parameters to pass to the API call.
20 |
21 | ### Returns
22 | - `Any`: The request body.
23 |
24 |
25 | ## PluginObject
26 |
27 | Represents an AI plugin object.
28 |
29 | ### Attributes
30 | - `openapi` (dict): The OpenAPI specification for the plugin.
31 | - `info` (dict): The info object from the OpenAPI spec.
32 | - `paths` (dict): The paths object from the OpenAPI spec.
33 | - `servers` (list): The servers list from the OpenAPI spec.
34 | - `manifest` (dict): The plugin manifest.
35 | - `url` (str): The plugin URL.
36 | - `name_for_model` (str): The plugin name.
37 | - `description_for_model` (str): The plugin description.
38 | - `operation_details_dict` (dict): A dictionary containing details for each operation in the plugin.
39 | - `description_prompt` (str): A prompt describing the plugin operations.
40 | - `tokens` (int): The number of tokens in the description_prompt.
41 |
42 | ### Methods
43 | - `__init__(self, url: str, spec: Dict[str, Any], manifest: Dict[str, Any])`: Initialize the PluginObject.
44 | - `get_operation_details(self)`: Get the details for each operation in the plugin.
45 | - `call_operation(self, operation_id: str, parameters: Dict[str, Any])`: Call an operation in the plugin.
46 | - `describe_api(self)`: Generate a prompt describing the plugin operations.
47 |
48 |
49 | ## call_operation(operation_id: str, parameters: Dict[str, Any]) -> Optional[requests.Response]
50 |
51 | Call an operation in the plugin.
52 |
53 | ### Parameters
54 | - `operation_id` (str): The ID of the operation to call.
55 | - `parameters` (dict): The parameters to pass to the operation.
56 |
57 | ### Returns
58 | - `requests.Response` or `None`: The response from the API call, or `None` if unsuccessful.
59 |
60 |
61 | ## describe_api() -> str
62 |
63 | Generate a prompt describing the plugin operations.
64 |
65 | ### Returns
66 | - `str`: The generated prompt.
67 |
68 |
69 | ## Plugins
70 |
71 | Manages installed and active plugins.
72 |
73 | ### Attributes
74 | - `installed_plugins` (dict): A dictionary of installed PluginObject instances, keyed by plugin name.
75 | - `active_plugins` (dict): A dictionary of active PluginObject instances, keyed by plugin name.
76 | - `template` (str): The prompt template to use.
77 | - `prompt` (str): The generated prompt with descriptions of active plugins.
78 | - `tokens` (int): The number of tokens in the prompt.
79 | - `max_plugins` (int): The maximum number of plugins that can be active at once.
80 |
81 | ### Methods
82 | - `__init__(self, urls: List[str], template: str = None)`: Initialize the Plugins class.
83 | - `install_and_activate(cls, urls: Union[str, List[str]], template: Optional[str] = None)`: Install plugins from URLs and activate them.
84 | - `list_installed(self) -> List[str]`: Get a list of installed plugin names.
85 | - `list_active(self) -> List[str]`: Get a list of active plugin names. (Max 3 active plugins)
86 |
--------------------------------------------------------------------------------
/plugnplai/verify_plugins/verify_plugnplai.py:
--------------------------------------------------------------------------------
1 | from ..utils import get_plugin_manifest, get_openapi_url, get_openapi_spec
2 | from ..plugins import PluginObject, Plugins, count_tokens
3 | import json
4 |
5 | def test_one_plugin_plugnplai(url):
6 | # test get manifest (from {url}/.well-known/ai-plugin.json)
7 | try:
8 | manifest = get_plugin_manifest(url)
9 | except Exception as e:
10 | raise Exception("Error getting manifest from {url}: {e}".format(url=url, e=e))
11 |
12 | # test get openapi url (from manifest)
13 | try:
14 | openapi_url = get_openapi_url(url, manifest)
15 | except Exception as e:
16 | raise Exception("Error getting openapi url from manifest: {e}".format(e=e))
17 |
18 | # test get openapi spec (from {openapi_url})
19 | try:
20 | openapi_spec = get_openapi_spec(openapi_url)
21 | except Exception as e:
22 | raise Exception("Error getting openapi spec from {openapi_url}: {e}".format(openapi_url=openapi_url, e=e))
23 |
24 | # test PluginObject
25 | try:
26 | plugin = PluginObject(url, openapi_spec, manifest)
27 | except Exception as e:
28 | raise Exception("Error creating PluginObject: {e}".format(e=e))
29 |
30 | # Test install Plugins
31 | try:
32 | plugins = Plugins([url])
33 | except Exception as e:
34 | raise Exception("Error installing Plugins: {e}".format(e=e))
35 |
36 | # Get the names of installed plugins
37 | try:
38 | names = plugins.list_installed()
39 | except Exception as e:
40 | raise Exception("Error retrieving list of active plugins: {e}".format(e=e))
41 |
42 | # Activate the plugin
43 | try:
44 | plugins.activate(names[0])
45 | error_activating = False
46 | except Exception as e:
47 | error_activating = True
48 | pass
49 |
50 | if error_activating:
51 | # test activate manually
52 | try:
53 | plugin_name = list(plugins.installed_plugins.keys())[0]
54 | plugin = plugins.installed_plugins.get(plugin_name)
55 | plugins.active_plugins[plugin_name] = plugin
56 | except Exception as e:
57 | raise Exception("Error activating plugin: {e}".format(e=e))
58 |
59 | # test fill_prompt()
60 | try:
61 | prompt = plugins.fill_prompt(plugins.template)
62 | except Exception as e:
63 | raise Exception("Error filling prompt: {e}".format(e=e))
64 |
65 | # test conunt tokens
66 | try:
67 | count = count_tokens(prompt)
68 | except Exception as e:
69 | raise Exception("Error counting tokens: {e}".format(e=e))
70 |
71 | # test build_functions()
72 | try:
73 | functions = plugins.build_functions()
74 | except Exception as e:
75 | raise Exception("Error building functions: {e}".format(e=e))
76 |
77 | # test loading functions with json library
78 | try:
79 | dict_functions = json.loads(functions)
80 | except Exception as e:
81 | raise Exception("Error loading functions with json library: {e}".format(e=e))
82 |
83 | return True
84 |
85 | def test_plugins_plugnplai(urls):
86 | list_pass = []
87 | list_fail = []
88 | list_errors = []
89 | for url in urls:
90 | try:
91 | r = test_one_plugin_plugnplai(url)
92 | if r:
93 | list_pass.append(url)
94 | except Exception as e:
95 | list_fail.append(url)
96 | list_errors.append(e)
97 | pass
98 | return list_pass, list_fail, list_errors
--------------------------------------------------------------------------------
/plugnplai/api_call.py:
--------------------------------------------------------------------------------
1 | '''
2 | The aim is to build utilitarian functions here to call the different types of API auth
3 | '''
4 |
5 | # def make_request_get(url: str, timeout=5):
6 | # """Make an HTTP GET request.
7 |
8 | # Args:
9 | # url (str): URL to make request to.
10 | # timeout (int, optional): Timeout in seconds. Defaults to 5.
11 |
12 | # Returns:
13 | # requests.Response: Response from request.
14 | # """
15 | # try:
16 | # response = requests.get(url, timeout=timeout)
17 | # response.raise_for_status() # Raises stored HTTPError, if one occurred.
18 | # except requests.exceptions.HTTPError as errh:
19 | # print ("Http Error:",errh)
20 | # except requests.exceptions.ConnectionError as errc:
21 | # print ("Error Connecting:",errc)
22 | # except requests.exceptions.Timeout as errt:
23 | # print ("Timeout Error:",errt)
24 | # except requests.exceptions.RequestException as err:
25 | # print ("Something went wrong",err)
26 | # return None
27 | # return response
28 |
29 |
30 |
31 | # def call_operation(self, operation_id: str, parameters: Dict[str, Any]) -> Optional[requests.Response]:
32 | # """Call an operation in the plugin.
33 |
34 | # Parameters
35 | # ----------
36 | # operation_id : str
37 | # The ID of the operation to call.
38 | # parameters : dict
39 | # The parameters to pass to the operation.
40 |
41 | # Returns
42 | # -------
43 | # requests.Response or None
44 | # The response from the API call, or None if unsuccessful.
45 | # """
46 | # # Get the operation details from the operation_details_dict attribute
47 | # operation_details = self.operation_details_dict.get(operation_id)
48 | # if not operation_details:
49 | # print(f'Operation {operation_id} not found')
50 | # return None
51 |
52 | # # Separate parameters by type
53 | # path_parameters = {}
54 | # query_parameters = {}
55 | # header_parameters = {}
56 | # cookie_parameters = {}
57 | # body = None
58 |
59 | # for parameter in operation_details['parameters']:
60 | # if parameter['in'] == 'path':
61 | # path_parameters[parameter['name']] = parameters.get(parameter['name'])
62 | # elif parameter['in'] == 'query':
63 | # query_parameters[parameter['name']] = parameters.get(parameter['name'])
64 | # elif parameter['in'] == 'header':
65 | # header_parameters[parameter['name']] = parameters.get(parameter['name'])
66 | # elif parameter['in'] == 'cookie':
67 | # cookie_parameters[parameter['name']] = parameters.get(parameter['name'])
68 |
69 | # if operation_details['requestBody']:
70 | # body_schema = operation_details['requestBody']['content']['application/json']['schema']
71 | # body = build_request_body(body_schema, parameters)
72 |
73 | # # Replace path parameters in the URL
74 | # url = operation_details['url']
75 | # for name, value in path_parameters.items():
76 | # url = url.replace('{' + name + '}', str(value))
77 |
78 | # # Make the API call
79 | # method = operation_details['method']
80 | # if method.lower() == 'get':
81 | # response = requests.get(url, params=query_parameters, headers=header_parameters, cookies=cookie_parameters)
82 | # elif method.lower() == 'post':
83 | # headers = {'Content-Type': 'application/json'}
84 | # response = requests.post(url, params=query_parameters, headers=headers, cookies=cookie_parameters, json=body)
85 |
86 | # return response
--------------------------------------------------------------------------------
/examples/oauth_example/run_plugin_with_auth.py:
--------------------------------------------------------------------------------
1 | # run server.py first
2 | # this file is the example for the retry mechanism with asana plugin.
3 | # enter the access_token when your plugin is using the oauth authentication.
4 |
5 | import guidance
6 |
7 | guidance.llm = guidance.llms.OpenAI("gpt-4", token="openai_api_key")
8 | import plugnplai
9 |
10 |
11 | def send_single_user_request(system_prompt, user_message):
12 | send_single_user_request = guidance(
13 | '''
14 | {{#system~}}
15 | {{system_prompt}}
16 | {{~/system}}
17 |
18 | {{#user~}}
19 | {{user_message}}
20 | {{~/user}}
21 |
22 | {{#assistant~}}
23 | {{gen 'assistant_response' temperature=0 max_tokens=300}}
24 | {{~/assistant}}'''
25 | )
26 | return send_single_user_request(system_prompt=system_prompt, user_message=user_message)
27 |
28 |
29 | def api_return_message(user_message, llm_first_response, api_response):
30 | create_api_return_message = guidance(
31 | '''
32 | {{#user~}}
33 | {{user_message}}
34 | {{~/user}}
35 |
36 | {{#system~}}
37 | Assistant is a large language model with access to plugins.
38 |
39 | Assistant called a plugin in response to previous user message.
40 | # API REQUEST SUMMARY
41 | {{llm_first_response}}
42 |
43 | # API RESPONSE
44 | {{api_response}}
45 | {{~/system}}
46 |
47 | {{#assistant~}}
48 | {{gen 'assistant_response' temperature=0 max_tokens=300}}
49 | {{~/assistant}}'''
50 | )
51 | return create_api_return_message(
52 | user_message=user_message, llm_first_response=llm_first_response, api_response=api_response
53 | )
54 |
55 |
56 | if __name__ == '__main__':
57 |
58 | user_inputs_query = "Create a task in Asana with the title 'Prepare presentation' and due date '2023-05-25' in workspace id 1"
59 | # Use the correct URL
60 | url_of_plugin_to_test = "https://app.asana.com"
61 |
62 | api_key = input("input your api key: ")
63 |
64 | print('\n----\n1 -> Entered Process\n')
65 |
66 | urls = [url_of_plugin_to_test]
67 | # urls.append(url_of_plugin_to_test)
68 | plugins = plugnplai.Plugins.install_and_activate(urls)
69 |
70 | print('\n----\n2 -> Plugin Prompt\n')
71 |
72 | print(plugins.prompt)
73 |
74 | print('\n----\n3 -> User Query\n')
75 | user_first_message = user_inputs_query
76 | print(user_first_message)
77 |
78 | assistant_first_response = send_single_user_request(system_prompt=plugins.prompt, user_message=user_first_message)
79 |
80 | llm_first_response = assistant_first_response['assistant_response']
81 |
82 | print('\n----\n4 -> Assistant First Response\n')
83 | print(llm_first_response)
84 |
85 | print('\n----\n5 -> API Call Dict\n')
86 | call_dict = plugnplai.parse_llm_response(llm_first_response)
87 | print(call_dict)
88 |
89 |
90 | # pass the access_token as api_key incase of the oauth technique.
91 | api_response = plugins.call_api(
92 | plugin_name=call_dict['plugin_name'], operation_id=call_dict['operation_id'], parameters=call_dict['parameters'],
93 | url=url_of_plugin_to_test,
94 | api_key=api_key
95 | )
96 |
97 | # ---------------------------------RETRY MECHANISM-----------------------------------------------------#
98 | # retry mechanism when access_token is expired.
99 | if "token has expired" in response.text:
100 |
101 | # ------------------------------REPLACE WITH ORIGINAL VALUES---------------------------------------#
102 | client_id = "client_id"
103 | client_secret = "client_secret"
104 | req_url = "authorization_token_url"
105 | refresh_token = "refresh_token"
106 |
107 | # make a request to a sever ( to a authorization_url ) with a grant type as a refresh_token.
108 | try:
109 | res = requests.post(req_url, params={"client_id":client_id, "client_secret":client_secret, "refresh_token":refresh_token, "grant_type": "refresh_token"})
110 | except Exception as e:
111 | print(f"something went wrong {e}")
112 | return "Something went wrong please relogin"
113 | token_obj = res.json()
114 | print(token_obj)
115 | api_key = token_obj["access_token"]
116 | api_response = plugins.call_api(
117 | plugin_name=call_dict['plugin_name'], operation_id=call_dict['operation_id'], parameters=call_dict['parameters'],
118 | url=url_of_plugin_to_test,
119 | api_key=api_key
120 | )
121 |
122 |
123 | print('\n----\n6 -> API Response\n')
124 | print(api_response)
125 |
--------------------------------------------------------------------------------
/plugnplai/embeddings.py:
--------------------------------------------------------------------------------
1 | """
2 | Embeddings for LangChain.
3 |
4 | This module provides a PluginRetriever class that retrieves plugin
5 | information based on queries using embeddings and vector stores.
6 |
7 | Classes
8 | -------
9 | PluginRetriever
10 |
11 | """
12 | from langchain.embeddings import OpenAIEmbeddings
13 | from langchain.schema import Document
14 | from langchain.vectorstores import FAISS
15 | from plugnplai.utils import get_plugin_manifest, get_plugins
16 |
17 | class PluginRetriever:
18 | """PluginRetriever retrieves plugin information based on queries using embeddings and vector stores.
19 |
20 | Parameters
21 | ----------
22 | manifests : list
23 | List of manifest objects.
24 | returnList : list, optional
25 | List of objects to be returned. Can be a list of URLs or a list of
26 | objects like LangChain AIPluging object. Defaults to None.
27 |
28 | Methods
29 | -------
30 | __init__(manifests, returnList=None)
31 | from_urls(urls)
32 | from_directory(provider='plugnplai')
33 | retrieve_names(query)
34 | retrieve_urls(query)
35 | """
36 |
37 | def __init__(self, manifests, returnList=None):
38 | """Initialize the PluginRetriever.
39 |
40 | Parameters
41 | ----------
42 | manifests : list
43 | List of manifest objects.
44 | returnList : list, optional
45 | List of objects to be returned. Can be a list of URLs or a list of
46 | objects like LangChain AIPluging object. Defaults to None.
47 | """
48 | self.returnList = returnList
49 | if self.returnList:
50 | # add urls to manifests
51 | for i in range(len(manifests)):
52 | manifests[i]["plugin_object"] = self.returnList[i]
53 |
54 | self.docs = [
55 | Document(
56 | page_content=manifest["description_for_model"],
57 | metadata={
58 | "plugin_name": manifest["name_for_model"],
59 | "plugin_object": manifest.get("plugin_object", None),
60 | },
61 | )
62 | for manifest in manifests
63 | ]
64 |
65 | # Initialize embeddings
66 | self.embeddings = OpenAIEmbeddings()
67 |
68 | # Create vector store from documents
69 | self.vector_store = FAISS.from_documents(self.docs, self.embeddings)
70 |
71 | # Create a retriever
72 | self.retriever = self.vector_store.as_retriever()
73 |
74 | @classmethod
75 | def from_urls(cls, urls):
76 | """Create a PluginRetriever object from a list of URLs.
77 |
78 | Parameters
79 | ----------
80 | urls : list
81 | List of URLs.
82 |
83 | Returns
84 | -------
85 | PluginRetriever
86 | Initialized PluginRetriever object.
87 | """
88 | manifests = [get_plugin_manifest(url) for url in urls]
89 | return cls(manifests, urls)
90 |
91 | @classmethod
92 | def from_directory(cls, provider='plugnplai'):
93 | """Create a PluginRetriever object from a directory.
94 |
95 | Parameters
96 | ----------
97 | provider : str, optional
98 | Provider name. Defaults to 'plugnplai'.
99 |
100 | Returns
101 | -------
102 | PluginRetriever
103 | Initialized PluginRetriever object.
104 | """
105 |
106 | urls = get_plugins(filter = 'working', provider = provider)
107 | manifests = [get_plugin_manifest(url) for url in urls]
108 | return cls(manifests)
109 |
110 | def retrieve_names(self, query):
111 | """Retrieve plugin names based on a query.
112 |
113 | Parameters
114 | ----------
115 | query :
116 | Query string.
117 |
118 | Returns
119 | -------
120 | list
121 | List of plugin names.
122 | """
123 | # Get relevant documents based on query
124 | docs = self.retriever.get_relevant_documents(query)
125 |
126 | # Get toolkits based on relevant documents
127 | plugin_names = [d.metadata["plugin_name"] for d in docs]
128 |
129 | return plugin_names
130 |
131 | def retrieve_urls(self, query):
132 | """Retrieve plugin URLs based on a query.
133 |
134 | Parameters
135 | ----------
136 | query :
137 | Query string.
138 |
139 | Returns
140 | -------
141 | list
142 | List of plugin URLs.
143 | """
144 | if not self.urls:
145 | raise Exception("No urls provided in constructor.")
146 |
147 | # Get relevant documents based on query
148 | docs = self.retriever.get_relevant_documents(query)
149 |
150 | # Get toolkits based on relevant documents
151 | plugin_urls = [d.metadata["plugin_object"] for d in docs]
152 |
153 | return plugin_urls
154 |
--------------------------------------------------------------------------------
/examples/langchain_chatgpt_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# Appying plugins to your LangChain OpenAI GPT model (without agents)\n",
9 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/langchain_chatgpt_example.ipynb)\n",
10 | "\n",
11 | "This notebook displays how to use Plug and Plai to retrieve and implement plugins to a LangChain LLM model, without needing to convert the plugins to tools and attach them to an agent.\n",
12 | "## Preform necessary imports, and install plugnplai."
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": null,
18 | "metadata": {},
19 | "outputs": [],
20 | "source": [
21 | "pip install plugnplai -q"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 62,
27 | "metadata": {},
28 | "outputs": [],
29 | "source": [
30 | "import plugnplai\n",
31 | "from langchain.chat_models import ChatOpenAI\n",
32 | "from langchain.schema import HumanMessage, SystemMessage\n",
33 | "from IPython.display import display, Markdown"
34 | ]
35 | },
36 | {
37 | "attachments": {},
38 | "cell_type": "markdown",
39 | "metadata": {},
40 | "source": [
41 | "## Set up LLM"
42 | ]
43 | },
44 | {
45 | "cell_type": "code",
46 | "execution_count": 30,
47 | "metadata": {},
48 | "outputs": [],
49 | "source": [
50 | "# Change model to \"gpt-3.5-turbo\" or \"gpt-3.5\" if you do not have access to GPT-4\n",
51 | "chat = ChatOpenAI(temperature=0, model=\"gpt-4\", openai_api_key=\"YOUR_API_KEY\")"
52 | ]
53 | },
54 | {
55 | "attachments": {},
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "## Retrieve and store plugins\n",
60 | "Retrieve all plugins, filter by category or framework, or enter specific urls. For this example, we will be using Klarna.com and Trip.com as our plugins."
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": 58,
66 | "metadata": {},
67 | "outputs": [
68 | {
69 | "name": "stdout",
70 | "output_type": "stream",
71 | "text": [
72 | "Our chosen Plugins: ['https://klarna.com', 'https://trip.com']\n"
73 | ]
74 | }
75 | ],
76 | "source": [
77 | "# Get all plugins from plugnplai.com\n",
78 | "urls = plugnplai.get_plugins()\n",
79 | "\n",
80 | "# Get ChatGPT plugins - only ChatGPT verified plugins\n",
81 | "urls = plugnplai.get_plugins(filter = 'ChatGPT')\n",
82 | "\n",
83 | "# Get plugins verified for specific frameworks\n",
84 | "urls = plugnplai.get_plugins(verified_for = 'langchain')\n",
85 | "\n",
86 | "# Get plugins from direct url\n",
87 | "urls = ['https://klarna.com', 'https://trip.com']\n",
88 | "\n",
89 | "print(f'Our chosen Plugins: {urls}')"
90 | ]
91 | },
92 | {
93 | "attachments": {},
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "## Install and activate plugins"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 64,
103 | "metadata": {},
104 | "outputs": [],
105 | "source": [
106 | "from plugnplai import Plugins\n",
107 | "\n",
108 | "plugins = Plugins.install_and_activate(urls)"
109 | ]
110 | },
111 | {
112 | "attachments": {},
113 | "cell_type": "markdown",
114 | "metadata": {},
115 | "source": [
116 | "## Apply plugins to LLM\n",
117 | "Use `@plugins.apply_plugins` decorator to easily apply plugins to your LLM function\n",
118 | "\n",
119 | "The LLM function MUST get a string as the user input as first variable and output the string with the response"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": 60,
125 | "metadata": {},
126 | "outputs": [],
127 | "source": [
128 | "@plugins.apply_plugins\n",
129 | "def call_llm(user_message):\n",
130 | " messages = [\n",
131 | " SystemMessage(content=\"\"),\n",
132 | " HumanMessage(content=user_message)\n",
133 | " ]\n",
134 | "\n",
135 | " res = chat(messages)\n",
136 | "\n",
137 | " llm_first_response = res.content\n",
138 | "\n",
139 | " return llm_first_response\n"
140 | ]
141 | },
142 | {
143 | "attachments": {},
144 | "cell_type": "markdown",
145 | "metadata": {},
146 | "source": [
147 | "## Test LLM\n",
148 | "\n",
149 | "Let's test both plugins now."
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": 33,
155 | "metadata": {},
156 | "outputs": [
157 | {
158 | "name": "stdout",
159 | "output_type": "stream",
160 | "text": [
161 | "Using KlarnaProducts\n"
162 | ]
163 | },
164 | {
165 | "data": {
166 | "text/markdown": [
167 | "Here are some t-shirts available on Klarna:\n",
168 | "\n",
169 | "1. [Patagonia Women's P-6 Logo Responsibili-Tee - T-shirt](https://www.klarna.com/us/shopping/pl/cl10001/3201782768/Clothing/Patagonia-Women-s-P-6-Logo-Responsibili-Tee-T-shirt/?utm_source=openai&ref-site=openai_plugin) - $23.84\n",
170 | " - Material: Polyester, Recycled Fabric, Cotton\n",
171 | " - Target Group: Woman\n",
172 | " - Color: Gray, Pink, White, Blue\n",
173 | "\n",
174 | "2. [Moschino T-shirt - White](https://www.klarna.com/us/shopping/pl/cl10001/3203506327/Clothing/Moschino-T-shirt-White/?utm_source=openai&ref-site=openai_plugin) - $121.00\n",
175 | " - Material: Cotton\n",
176 | " - Target Group: Man\n",
177 | " - Color: White\n",
178 | "\n",
179 | "3. [Polo Ralph Lauren Slim Fit Cotton T-shirt 3-pack](https://www.klarna.com/us/shopping/pl/cl10001/3201838628/Clothing/Polo-Ralph-Lauren-Slim-Fit-Cotton-T-shirt-3-pack/?utm_source=openai&ref-site=openai_plugin) - $28.90\n",
180 | " - Material: Cotton\n",
181 | " - Target Group: Man\n",
182 | " - Color: Gray, White, Blue, Multicolor, Black\n",
183 | "\n",
184 | "Please note that prices and availability are subject to change."
185 | ],
186 | "text/plain": [
187 | ""
188 | ]
189 | },
190 | "metadata": {},
191 | "output_type": "display_data"
192 | }
193 | ],
194 | "source": [
195 | "# Message that triggers the use of plugin 1\n",
196 | "response = call_llm(\"What are some t shirts available on Klarna?\")\n",
197 | "# Display in markdown format\n",
198 | "display(Markdown(response))"
199 | ]
200 | },
201 | {
202 | "cell_type": "code",
203 | "execution_count": 61,
204 | "metadata": {},
205 | "outputs": [
206 | {
207 | "name": "stdout",
208 | "output_type": "stream",
209 | "text": [
210 | "Using Trip\n"
211 | ]
212 | },
213 | {
214 | "data": {
215 | "text/markdown": [
216 | "I found 3 top hotels in Paris for your stay from June 3 to June 8, 2023:\n",
217 | "\n",
218 | "1. [Pullman Paris Centre - Bercy](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2107175&checkin=2023-06-03&checkout=2023-06-08&curr=USD&allianceid=3842389&sid=22086800)\n",
219 | " - Address: 1 Rue de Libourne\n",
220 | " - Price: $270 per night\n",
221 | " - Rating: 4.5/5.0 (42 reviews)\n",
222 | " - 4-star hotel\n",
223 | " - Features: Swimming pool, fitness center\n",
224 | " - Opened in 2000, renovated in 2014\n",
225 | "\n",
226 | "2. [Shangri-La Paris](https://us.trip.com/hotels/detail/?cityId=192&hotelId=730333&checkin=2023-06-03&checkout=2023-06-08&curr=USD&allianceid=3842389&sid=22086800)\n",
227 | " - Address: 10 Av. d'Iéna\n",
228 | " - Price: $2117 per night\n",
229 | " - Rating: 4.6/5.0 (6 reviews)\n",
230 | " - 5-star hotel\n",
231 | " - Features: Sauna, swimming pool\n",
232 | " - Opened and renovated in 2010\n",
233 | "\n",
234 | "3. [Pullman Paris Tour Eiffel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2081163&checkin=2023-06-03&checkout=2023-06-08&curr=USD&allianceid=3842389&sid=22086800)\n",
235 | " - Address: 18 Avenue De Suffren, 22 Rue Jean Rey Entrée Au\n",
236 | " - Price: $1056 per night\n",
237 | " - Rating: 4.2/5.0 (113 reviews)\n",
238 | " - 4-star hotel\n",
239 | " - Features: Fitness center, multi-function hall\n",
240 | " - Opened in 1964, renovated in 2019\n",
241 | "\n",
242 | "Please note that prices and availability are subject to change. Click on the hotel links to get more information and book your stay."
243 | ],
244 | "text/plain": [
245 | ""
246 | ]
247 | },
248 | "metadata": {},
249 | "output_type": "display_data"
250 | }
251 | ],
252 | "source": [
253 | "# Message that triggers the use of plugin 2\n",
254 | "response = call_llm(\"Book a hotel in Paris for June 3-8?\")\n",
255 | "display(Markdown(response))"
256 | ]
257 | }
258 | ],
259 | "metadata": {
260 | "kernelspec": {
261 | "display_name": "Python 3",
262 | "language": "python",
263 | "name": "python3"
264 | },
265 | "language_info": {
266 | "codemirror_mode": {
267 | "name": "ipython",
268 | "version": 3
269 | },
270 | "file_extension": ".py",
271 | "mimetype": "text/x-python",
272 | "name": "python",
273 | "nbconvert_exporter": "python",
274 | "pygments_lexer": "ipython3",
275 | "version": "3.11.3"
276 | },
277 | "orig_nbformat": 4
278 | },
279 | "nbformat": 4,
280 | "nbformat_minor": 2
281 | }
282 |
--------------------------------------------------------------------------------
/examples/retrieve_plugins_for_langchain_agent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# Retrieving Plugins for LangChain Agent Using Plug and Plai\n",
9 | "\n",
10 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/retrieve_plugins_for_langchain_agent.ipynb)\n",
11 | "\n",
12 | "This example shows how to retrieve compatible plugins using Plug and Plai within LangChain abstractions.\n",
13 | "\n",
14 | "### 1. Install Plug and Plai and necessary LangChain tools"
15 | ]
16 | },
17 | {
18 | "cell_type": "code",
19 | "execution_count": null,
20 | "metadata": {},
21 | "outputs": [],
22 | "source": [
23 | "pip install plugnplai -q"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": 3,
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "from langchain.chat_models import ChatOpenAI\n",
33 | "from langchain.agents import load_tools, initialize_agent\n",
34 | "from langchain.agents import AgentType\n",
35 | "from langchain.tools import AIPluginTool"
36 | ]
37 | },
38 | {
39 | "attachments": {},
40 | "cell_type": "markdown",
41 | "metadata": {},
42 | "source": [
43 | "### 2. Retrieve compatible plugins\n",
44 | "Use the 'get_plugins' function to retrieve compatible plugins for a given agent. You can filter your search by specifying the LLM or framework you will be working with."
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": 4,
50 | "metadata": {},
51 | "outputs": [
52 | {
53 | "name": "stdout",
54 | "output_type": "stream",
55 | "text": [
56 | "https://api.speak.com\n",
57 | "https://wolframcloud.com\n",
58 | "https://wolframalpha.com\n",
59 | "https://klarna.com\n",
60 | "https://nla.zapier.com\n",
61 | "https://server.shop.app\n",
62 | "https://joinmilo.com\n",
63 | "https://portfoliopilot.com\n",
64 | "https://api.tasty.co\n",
65 | "https://remoteambition.com\n",
66 | "https://openai.creaticode.com\n",
67 | "https://yabblezone.net\n"
68 | ]
69 | }
70 | ],
71 | "source": [
72 | "import plugnplai\n",
73 | "\n",
74 | "# Get all plugins from plugnplai.com\n",
75 | "urls = plugnplai.get_plugins()\n",
76 | "\n",
77 | "# Or get ChatGPT plugins - only ChatGPT verified plugins\n",
78 | "urls = plugnplai.get_plugins(filter = 'ChatGPT')\n",
79 | "for url in urls:\n",
80 | " print(url)\n",
81 | "\n",
82 | "# Or get plugins that are verified for working with LangChain\n",
83 | "urls = plugnplai.get_plugins(verified_for = 'langchain')"
84 | ]
85 | },
86 | {
87 | "attachments": {},
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "### 3. Convert to LangChain tool\n",
92 | "You will need to add \"/.well-known/ai-plugin.json\" to the end of the plugin URL to convert it to a LangChain tool."
93 | ]
94 | },
95 | {
96 | "cell_type": "code",
97 | "execution_count": 8,
98 | "metadata": {},
99 | "outputs": [],
100 | "source": [
101 | "tool = AIPluginTool.from_plugin_url(\"https://www.klarna.com/.well-known/ai-plugin.json\")\n",
102 | "\n",
103 | "tools = [tool]"
104 | ]
105 | },
106 | {
107 | "attachments": {},
108 | "cell_type": "markdown",
109 | "metadata": {},
110 | "source": [
111 | "### 4. Initialize LangChain agent\n",
112 | "Replace `\"YOUR_API_KEY\"` with your OpenAI API key and `\"gpt-4\"` with \"gpt-3.5\" or \"gpt-3.5-turbo\" if you do not have access to GPT-4."
113 | ]
114 | },
115 | {
116 | "cell_type": "code",
117 | "execution_count": null,
118 | "metadata": {},
119 | "outputs": [],
120 | "source": [
121 | "llm = ChatOpenAI(\n",
122 | " temperature=0,\n",
123 | " model=\"gpt-4\",\n",
124 | " openai_api_key=\"YOUR_API_KEY\",\n",
125 | " )\n",
126 | "\n",
127 | "agent_chain = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)\n",
128 | "agent_chain.run(\"what t shirts are available in klarna?\")"
129 | ]
130 | },
131 | {
132 | "attachments": {},
133 | "cell_type": "markdown",
134 | "metadata": {},
135 | "source": [
136 | "**> Entering new AgentExecutor chain...**\n",
137 | "I need to check the Klarna Shopping API to see if it has information on available t shirts.\n",
138 | "**Action: KlarnaProducts**\n",
139 | "Action Input: None\n",
140 | "\n",
141 | "Observation: Usage Guide: Use the Klarna plugin to get relevant product suggestions for any shopping or researching purpose. The query to be sent should not include stopwords like articles, prepositions and determinants. The api works best when searching for words that are related to products, like their name, brand, model or category. Links will always be returned and should be shown to the user.\n",
142 | "\n",
143 | "OpenAPI Spec: {'openapi': '3.0.1', 'info': {'version': 'v0', 'title': 'Open AI Klarna product Api'}, 'servers': [{'url': 'https://www.klarna.com/us/shopping'}], 'tags': [{'name': 'open-ai-product-endpoint', 'description': 'Open AI Product Endpoint. Query for products.'}], 'paths': {'/public/openai/v0/products': {'get': {'tags': ['open-ai-product-endpoint'], 'summary': 'API for fetching Klarna product information', 'operationId': 'productsUsingGET', 'parameters': [{'name': 'q', 'in': 'query', 'description': 'query, must be between 2 and 100 characters', 'required': True, 'schema': {'type': 'string'}}, {'name': 'size', 'in': 'query', 'description': 'number of products returned', 'required': False, 'schema': {'type': 'integer'}}, {'name': 'budget', 'in': 'query', 'description': 'maximum price of the matching product in local currency, filters results', 'required': False, 'schema': {'type': 'integer'}}], 'responses': {'200': {'description': 'Products found', 'content': {'application/json': {'schema': {'$ref': '#/components/schemas/ProductResponse'}}}}, '503': {'description': 'one or more services are unavailable'}}, 'deprecated': False}}}, 'components': {'schemas': {'Product': {'type': 'object', 'properties': {'attributes': {'type': 'array', 'items': {'type': 'string'}}, 'name': {'type': 'string'}, 'price': {'type': 'string'}, 'url': {'type': 'string'}}, 'title': 'Product'}, 'ProductResponse': {'type': 'object', 'properties': {'products': {'type': 'array', 'items': {'$ref': '#/components/schemas/Product'}}}, 'title': 'ProductResponse'}}}}\n",
144 | "**Thought:**I need to use the Klarna Shopping API to search for t shirts.\n",
145 | "Action: requests_get\n",
146 | "Action Input: https://www.klarna.com/us/shopping/public/openai/v0/products?q=t%20shirts\n",
147 | "\n",
148 | "Observation: {\"products\":[{\"name\":\"Lacoste Men's Pack of Plain T-Shirts\",\"url\":\"https://www.klarna.com/us/shopping/pl/cl10001/3202043025/Clothing/Lacoste-Men-s-Pack-of-Plain-T-Shirts/?utm_source=openai\",\"price\":\"$26.60\",\"attributes\":[\"Material:Cotton\",\"Target Group:Man\",\"Color:White,Black\"]},{\"name\":\"Hanes Men's Ultimate 6pk. Crewneck T-Shirts\",\"url\":\"https://www.klarna.com/us/shopping/pl/cl10001/3201808270/Clothing/Hanes-Men-s-Ultimate-6pk.-Crewneck-T-Shirts/?utm_source=openai\",\"price\":\"$13.82\",\"attributes\":[\"Material:Cotton\",\"Target Group:Man\",\"Color:White\"]},{\"name\":\"Nike Boy's Jordan Stretch T-shirts\",\"url\":\"https://www.klarna.com/us/shopping/pl/cl359/3201863202/Children-s-Clothing/Nike-Boy-s-Jordan-Stretch-T-shirts/?utm_source=openai\",\"price\":\"$14.99\",\"attributes\":[\"Material:Cotton\",\"Color:White,Green\",\"Model:Boy\",\"Size (Small-Large):S,XL,L,M\"]},{\"name\":\"Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack\",\"url\":\"https://www.klarna.com/us/shopping/pl/cl10001/3203028500/Clothing/Polo-Classic-Fit-Cotton-V-Neck-T-Shirts-3-Pack/?utm_source=openai\",\"price\":\"$29.95\",\"attributes\":[\"Material:Cotton\",\"Target Group:Man\",\"Color:White,Blue,Black\"]},{\"name\":\"adidas Comfort T-shirts Men's 3-pack\",\"url\":\"https://www.klarna.com/us/shopping/pl/cl10001/3202640533/Clothing/adidas-Comfort-T-shirts-Men-s-3-pack/?utm_source=openai\",\"price\":\"$14.99\",\"attributes\":[\"Material:Cotton\",\"Target Group:Man\",\"Color:White,Black\",\"Neckline:Round\"]}]}\n",
149 | "**Thought:**The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack.\n",
150 | "Final Answer: The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack.\n",
151 | "\n",
152 | "**> Finished chain.**"
153 | ]
154 | },
155 | {
156 | "attachments": {},
157 | "cell_type": "markdown",
158 | "metadata": {},
159 | "source": [
160 | "\"The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack.\""
161 | ]
162 | }
163 | ],
164 | "metadata": {
165 | "kernelspec": {
166 | "display_name": "Python 3",
167 | "language": "python",
168 | "name": "python3"
169 | },
170 | "language_info": {
171 | "codemirror_mode": {
172 | "name": "ipython",
173 | "version": 3
174 | },
175 | "file_extension": ".py",
176 | "mimetype": "text/x-python",
177 | "name": "python",
178 | "nbconvert_exporter": "python",
179 | "pygments_lexer": "ipython3",
180 | "version": "3.11.3"
181 | },
182 | "orig_nbformat": 4
183 | },
184 | "nbformat": 4,
185 | "nbformat_minor": 2
186 | }
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🎸 Plug and Plai
2 |
3 | Plug and Plai is an open source library aiming to simplify the integration of AI plugins into open-source language models (LLMs).
4 |
5 | It provides utility functions to get a list of active plugins from [plugnplai.com](https://plugnplai.com/) directory, get plugin manifests, and extract OpenAPI specifications and load plugins.
6 |
7 | ## Installation
8 |
9 | You can install Plug and Plai using pip:
10 |
11 | ```python
12 | pip install plugnplai
13 | ```
14 |
15 | ## Quick Start Example
16 |
17 | **Plugins Retrieval API - https://www.plugnplai.com/_functions/retrieve?text={user_message_here}:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/retrieve_plugins_api.ipynb)
18 |
19 | **Use an Specific Plugin - Step by Step:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/plugins_step_by_step.ipynb)
20 |
21 |
22 | ## More Examples
23 |
24 | **Generate Prompt with Plugins Description:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/create_prompt_plugins.ipynb)
25 |
26 | **Plugins Retrieval:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/plugin_retriever_with_langchain_agent.ipynb)
27 |
28 | **OAuth Plugins** Example for setting up plugins with OAuth: [examples/oauth_example](https://github.com/edreisMD/plugnplai/tree/master/examples/oauth_example)
29 |
30 | ## Usage
31 |
32 | ### Get a list of plugins
33 |
34 | - `urls = get_plugins()`: Get a list of available plugins from a [plugins repository](https://www.plugplai.com/).
35 |
36 | - `urls = get_plugins(filter = 'ChatGPT', category='dev')`: Use 'filter' or 'category' variables to query specific plugins
37 |
38 | Example:
39 |
40 | ```python
41 | import plugnplai
42 |
43 | # Get all plugins from plugnplai.com
44 | urls = plugnplai.get_plugins()
45 |
46 | # Get ChatGPT plugins - only ChatGPT verified plugins
47 | urls = plugnplai.get_plugins(filter = 'ChatGPT')
48 |
49 | # Get working plugins - only tested plugins (in progress)
50 | urls = plugnplai.get_plugins(filter = 'working')
51 |
52 | # Get plugins by category - only tested plugins (in progress)
53 | urls = plugnplai.get_plugins(category = 'travel')
54 |
55 | # Get the names list of categories
56 | urls = plugnplai.get_category_names()
57 | ```
58 |
59 | ### Utility Functions
60 |
61 | Help to load the plugins manifest and OpenAPI specification
62 |
63 | - `manifest = get_plugin_manifest(url)`: Get the AI plugin manifest from the specified plugin URL.
64 | - `specUrl = get_openapi_url(url, manifest)`: Get the OpenAPI URL from the plugin manifest.
65 | - `spec = get_openapi_spec(openapi_url)`: Get the OpenAPI specification from the specified OpenAPI URL.
66 | - `manifest, spec = spec_from_url(url)`: Returns the Manifest and OpenAPI specification from the plugin URL.
67 |
68 | Example:
69 |
70 | ```python
71 | import plugnplai
72 |
73 | # Get the Manifest and the OpenAPI specification from the plugin URL
74 | manifest, openapi_spec = plugnplai.spec_from_url(urls[0])
75 | ```
76 |
77 | ### Load Plugins
78 | **Example:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/plugins_step_by_step.ipynb)
79 |
80 | ```python
81 | from plugnplai import Plugins
82 |
83 | ###### ACTIVATE A MAX OF 3 PLUGINS ######
84 | # Context length limits the number of plugins you can activate,
85 | # you need to make sure the prompt fits in your context lenght,
86 | # still leaving space for the user message
87 |
88 | # Initialize 'Plugins' by passing a list of urls, this function will
89 | # load the plugins and build a default description to be used as prefix prompt
90 | plugins = Plugins.install_and_activate(urls)
91 |
92 | # Print the deafult prompt for the activated plugins
93 | print(plugins.prompt)
94 |
95 | # Print the number of tokens of the prefix prompt
96 | print(plugins.tokens)
97 | ```
98 |
99 | Example on installing (loading) all plugins, and activating a few later:
100 |
101 | ```python
102 | from plugnplai import Plugins
103 |
104 | # If you just want to load the plugins, but activate only
105 | # some of them later use Plugins(urls) instead
106 | plugins = Plugins(urls)
107 |
108 | # Print the names of installed plugins
109 | print(plugins.list_installed)
110 |
111 | # Activate the plugins you want
112 | plugins.activate(name1)
113 | plugins.activate(name2)
114 | plugins.activate(name3)
115 |
116 | # Deactivate the last plugin
117 | plugins.deactivate(name3)
118 | ```
119 |
120 | ### Prompt and Tokens Counting
121 |
122 | The `plugins.prompt` attribute contains a prompt with descriptions of the active plugins.
123 | The `plugins.tokens` attribute contains the number of tokens in the prompt.
124 |
125 | For example:
126 | ```python
127 | plugins = Plugins.install_and_activate(urls)
128 | print(plugins.prompt)
129 | print(plugins.tokens)
130 | ```
131 | This will print the prompt with plugin descriptions and the number of tokens.
132 |
133 |
134 | ### Parse LLM Response for API Tag
135 |
136 | The `parse_llm_response()` function parses an LLM response searching for API calls. It looks for the `` pattern defined in the `plugins.prompt` and extracts the plugin name, operation ID, and parameters.
137 |
138 |
139 | ### Call API
140 |
141 | The `call_api()` function calls an operation in an active plugin. It takes the plugin name, operation ID, and parameters extracted by `parse_llm_response()` and makes a request to the plugin API.
142 |
143 | **Plugin Authentication:** Plugins with Oauth, user or service level authentication pass api_key(string) as parameter. For oauth, use the access_token as the api_key parameter. For more detail about oauth authentication please see [oauth_example/run_plugin_with_oauth.py](https://github.com/edreisMD/plugnplai/tree/master/examples/oauth_example/run_plugin_with_auth.py) and [oauth_example/oauth_server.py](https://github.com/edreisMD/plugnplai/tree/master/examples/oauth_example/oauth_server.py) files.
144 |
145 |
146 | ### Apply Plugins
147 |
148 | The `@plugins.apply_plugins` decorator can be used to easily apply active plugins to an LLM function.
149 |
150 | This method is suboptimal for GPT chat models because it doesn't make use of the different available roles on the chat api (system, user, or assistant roles). But it may be useful for other LLM providers or open-source models.
151 |
152 | 1. Import the Plugins class and decorator:
153 |
154 | ```python
155 | from plugnplai import Plugins, plugins.apply_plugins
156 | ```
157 |
158 | 2. Define your LLM function, that necessarily takes a string (the user input) as the first argument and returns a string (the response):
159 |
160 | ```python
161 | @plugins.apply_plugins
162 | def call_llm(user_input):
163 | ...
164 | return response
165 | ```
166 |
167 | 3. The decorator will handle the following:
168 |
169 | - Prepending the prompt (with plugin descriptions) to the user input
170 | - Checking the LLM response for API calls (the ... pattern)
171 | - Calling the specified plugins
172 | - Summarizing the API calls in the LLM response
173 | - Calling the LLM function again with the summary to get a final response
174 |
175 | 4. If no API calls are detected, the original LLM response is returned.
176 |
177 | To more details on the implementation of these steps, see example "Step by Step": [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/plugins_step_by_step.ipynb)
178 |
179 | ### Plugins Retrieval
180 |
181 | **PlugnPlai Retrieval API - https://www.plugnplai.com/_functions/retrieve?text={user_message_here} :** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/retrieve_plugins_api.ipynb)
182 |
183 | **Build our own Plugins Vector Database:** [](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/plugin_retriever_with_langchain_agent.ipynb)
184 |
185 |
186 | ```python
187 | from plugnplai import PluginRetriever
188 |
189 | # Initialize the plugins retriever vector database and index the plugins descriptions.
190 | # Loading the plugins from plugnplai.com directory
191 | plugin_retriever = PluginRetriever.from_directory()
192 |
193 | # Retrieve the names of the plugins given a user's message
194 | plugin_retriever.retrieve_names("what shirts can i buy?")
195 | ```
196 |
197 |
198 | ## Contributing
199 |
200 | Plug and Plai is an open source library, and we welcome contributions from the entire community. If you're interested in contributing to the project, please feel free to fork, submit pull requests, report issues, or suggest new features.
201 |
202 | #### To dos
203 | - [x] [Load] Define a default object to read plugins - use LangChain standard? (for now using only manifest and specs jsons)
204 | - [ ] [Load] Fix breaking on reading certain plugins specs
205 | - [x] [Load] Accept different specs methods and versions (param, query, body)
206 | - [x] [Prompt] Build a utility function to return a default prompts for a plugin
207 | - [x] [Prompt] Fix prompt building for body (e.g. "Speak")
208 | - [x] [Prompt] Build a utility function to return a default prompts for a set of plugins
209 | - [x] [Prompt] Build a utility function to count tokens of the plugins prompt
210 | - [ ] [Prompt] Use the prompt tokens number to short or expand a plugin prompt, use LLM to summarize the prefix prompt
211 | - [x] [CallAPI] Build a function to call API given a dictionary of parameters
212 | - [x] [CallAPI] Add example for calling API
213 | - [ ] [Embeddings] Add filter option (e.g. "working", "ChatGPT") to "PluginRetriever.from_directory()"
214 | - [x] [Docs] Add Sphynx docs
215 | - [ ] [Verification] Build automated tests to verify new plugins
216 | - [x] [Verification] Build automated monitoring for working plugins for plugnplai
217 | - [ ] [Verification] Build automated monitoring for working plugins for langchain
218 | - [ ] [Website] Build an open-source website
219 | - [x] [Authentication] Add support for OAuth, user and service level authentication
220 | - [x] [Functions] Automaticly create OpenAI function dictionary for plugins (plugins.functions)
221 | - [ ] [Model] Create a dataset for finetuning open-source models to call Plugins / APIs. Use the descriptions on plugins.prompt (eventually plugins.functions) as input
222 | - [ ] [Model] Finetune an open-source model to call Plugins / APIs
223 |
224 | #### Project Roadmap
225 | 1. Build auxiliary functions that helps everyone to use plugins as defined by [OpenAI](https://platform.openai.com/docs/plugins/introduction)
226 | 2. Build in compatibility with different open-source formats (e.g. LangChain, BabyAGI, etc)
227 | 3. Find a best prompt format for plugins, optimizing for token number and description completness
228 | 4. Help with authentication
229 | 5. Build a dataset to finetune open-source models to call plugins
230 | 6. Finetune an open-source model to call plugins
231 | 7. Etc.
232 |
233 | ## Links
234 | - Plugins directory: [https://plugnplai.com/](https://plugnplai.com/)
235 | - API reference: [https://plugnplai.github.io/](https://plugnplai.github.io/)
236 |
--------------------------------------------------------------------------------
/plugnplai/utils.py:
--------------------------------------------------------------------------------
1 | import json
2 | import ast
3 | import os
4 |
5 | import jsonref
6 | import requests
7 | import yaml
8 | import re
9 |
10 | def make_request_get(url: str, timeout=5):
11 | """Make an HTTP GET request.
12 |
13 | Args:
14 | url (str): URL to make request to.
15 | timeout (int, optional): Timeout in seconds. Defaults to 5.
16 |
17 | Returns:
18 | requests.Response: Response from request.
19 | """
20 | try:
21 | response = requests.get(url, timeout=timeout)
22 | response.raise_for_status() # Raises stored HTTPError, if one occurred.
23 | except requests.exceptions.HTTPError as errh:
24 | print ("Http Error:",errh)
25 | except requests.exceptions.ConnectionError as errc:
26 | print ("Error Connecting:",errc)
27 | except requests.exceptions.Timeout as errt:
28 | print ("Timeout Error:",errt)
29 | except requests.exceptions.RequestException as err:
30 | print ("Something went wrong",err)
31 | return None
32 | return response
33 |
34 | def get_plugins(filter: str = None, verified_for = None, category: str = None, provider: str = "plugnplai"):
35 | """Get list of plugin URLs from a provider.
36 |
37 | Args:
38 | filter (str, optional): Filter to apply. Options are "working" or "ChatGPT". Defaults to None.
39 | verified_for (str, optional): Filter to plugins verified for a framework. Options are "langchain" or "plugnplai". Defaults to None.
40 | category (str, optional): Category to filter for. Defaults to None.
41 | provider (str, optional): Provider to get plugins from. Options are "plugnplai" or "pluginso". Defaults to "plugnplai".
42 |
43 | Returns:
44 | list: List of plugin URLs.
45 | """
46 | if provider == "plugnplai":
47 | base_url = "https://www.plugnplai.com/_functions"
48 | # Construct the endpoint URL based on the filter and category arguments
49 | if filter in ["working", "ChatGPT"]:
50 | url = f'{base_url.strip("/")}/getUrls/{filter}'
51 | elif verified_for in ["langchain", "plugnplai"]:
52 | url = f'{base_url.strip("/")}/getUrls/{verified_for}'
53 | elif category is not None:
54 | url = f'{base_url.strip("/")}/getCategoryUrls/{category}'
55 | else:
56 | url = f'{base_url.strip("/")}/getUrls'
57 | # Make the HTTP GET request
58 | response = make_request_get(url)
59 | # Check if the response status code is successful (200 OK)
60 | if response.status_code == 200:
61 | # Parse the JSON response and return the result
62 | return response.json()
63 | else:
64 | # Handle unsuccessful responses
65 | return f"An error occurred: {response.status_code} {response.reason}"
66 | elif provider == "pluginso":
67 | url = "https://plugin.so/api/plugins/list"
68 | response = make_request_get(url)
69 | if response.status_code == 200:
70 | # Parse the JSON response and return the result
71 | return [f"https://{entry['domain']}" for entry in response.json()]
72 | else:
73 | # Handle unsuccessful responses
74 | return f"An error occurred: {response.status_code} {response.reason}"
75 |
76 | def get_category_names(provider: str = "plugnplai"):
77 | """Get list of category names from a provider.
78 |
79 | Args:
80 | provider (str, optional): Provider to get category names from. Options are "plugnplai" or "pluginso". Defaults to "plugnplai".
81 |
82 | Returns:
83 | list: List of category names.
84 | """
85 | if provider == "plugnplai":
86 | base_url = "https://www.plugnplai.com/_functions"
87 | url = f'{base_url.strip("/")}/categoryNames'
88 | # Make the HTTP GET request
89 | response = make_request_get(url)
90 | # Check if the response status code is successful (200 OK)
91 | if response.status_code == 200:
92 | # Parse the JSON response and return the result
93 | return response.json()
94 | else:
95 | # Handle unsuccessful responses
96 | return f"An error occurred: {response.status_code} {response.reason}"
97 | else:
98 | return "Provider not supported for this operation."
99 |
100 | # given a plugin url, get the ai-plugin.json manifest, in "/.well-known/ai-plugin.json"
101 | def get_plugin_manifest(url: str):
102 | """Get plugin manifest from URL.
103 |
104 | Args:
105 | url (str): Plugin URL.
106 |
107 | Returns:
108 | dict: Plugin manifest.
109 | """
110 | urlJson = os.path.join(url, ".well-known/ai-plugin.json")
111 | response = make_request_get(urlJson)
112 | return response.json()
113 |
114 | def _is_partial_url(url, openapi_url):
115 | """Check if OpenAPI URL is partial.
116 |
117 | Args:
118 | url (str): Base URL.
119 | openapi_url (str): OpenAPI URL.
120 |
121 | Returns:
122 | str: Full OpenAPI URL.
123 | """
124 | if openapi_url.startswith("/"):
125 | # remove slash in the end of url if present
126 | url = url.strip("/")
127 | openapi_url = url + openapi_url
128 | elif "localhost" in openapi_url:
129 | openapi_url = "/"+openapi_url.split("/")[-1]
130 | return _is_partial_url(url, openapi_url)
131 | return openapi_url
132 |
133 | def get_openapi_url(url, manifest):
134 | """Get full OpenAPI URL from plugin URL and manifest.
135 |
136 | Args:
137 | url (str): Plugin URL.
138 | manifest (dict): Plugin manifest.
139 |
140 | Returns:
141 | str: Full OpenAPI URL.
142 | """
143 | openapi_url = manifest["api"]["url"]
144 | return _is_partial_url(url, openapi_url)
145 |
146 | # This code uses the following source: https://github.com/hwchase17/langchain/blob/master/langchain/tools/plugin.py
147 | def marshal_spec(txt: str) -> dict:
148 | """Convert YAML or JSON serialized spec to dict.
149 |
150 | Args:
151 | txt (str): YAML or JSON serialized spec.
152 |
153 | Returns:
154 | dict: Spec as a dict.
155 | """
156 | try:
157 | return json.loads(txt)
158 | except json.JSONDecodeError:
159 | return yaml.safe_load(txt)
160 |
161 |
162 | def get_openapi_spec(openapi_url):
163 | """Get OpenAPI spec from URL.
164 |
165 | Args:
166 | openapi_url (str): OpenAPI URL.
167 |
168 | Returns:
169 | dict: OpenAPI spec.
170 | """
171 | openapi_spec_str = make_request_get(openapi_url, timeout=20).text
172 | openapi_spec = marshal_spec(openapi_spec_str)
173 | # Use jsonref to resolve references
174 | resolved_openapi_spec = jsonref.JsonRef.replace_refs(openapi_spec)
175 | return resolved_openapi_spec
176 |
177 |
178 | def spec_from_url(url):
179 | """Get plugin manifest and OpenAPI spec from URL.
180 |
181 | Args:
182 | url (str): Plugin URL.
183 |
184 | Returns:
185 | dict: Plugin manifest.
186 | dict: OpenAPI spec.
187 | """
188 | manifest = get_plugin_manifest(url)
189 | openapi_url = get_openapi_url(url, manifest)
190 | openapi_spec = get_openapi_spec(openapi_url)
191 | return manifest, openapi_spec
192 |
193 |
194 | def extract_parameters(openapi_spec, path, method):
195 | """Extract parameters from OpenAPI spec for a path and method.
196 |
197 | Args:
198 | openapi_spec (dict): OpenAPI spec.
199 | path (str): Path.
200 | method (str): Method.
201 |
202 | Returns:
203 | dict: Parameters.
204 | """
205 | parameters = {}
206 |
207 | # Extract path parameters and query parameters
208 | if "parameters" in openapi_spec["paths"][path][method]:
209 | for param in openapi_spec["paths"][path][method]["parameters"]:
210 | param_name = param["name"]
211 | param_type = param["in"] # e.g., 'path', 'query', 'header'
212 | parameters[param_name] = {"type": param_type, "schema": param["schema"]}
213 |
214 | # Extract request body properties
215 | if "requestBody" in openapi_spec["paths"][path][method]:
216 | content = openapi_spec["paths"][path][method]["requestBody"]["content"]
217 | if "application/json" in content:
218 | json_schema = content["application/json"]["schema"]
219 | if "properties" in json_schema:
220 | for prop_name, prop_schema in json_schema["properties"].items():
221 | parameters[prop_name] = {"type": "body", "schema": prop_schema}
222 |
223 | return parameters
224 |
225 |
226 | def extract_all_parameters(openapi_spec):
227 | """Extract all parameters from OpenAPI spec.
228 |
229 | Args:
230 | openapi_spec (dict): OpenAPI spec.
231 |
232 | Returns:
233 | dict: All parameters.
234 | """
235 | all_parameters = {}
236 |
237 | # Mapping of long type names to short names
238 | type_shorteners = {
239 | "string": "str",
240 | "integer": "int",
241 | "boolean": "bool",
242 | "number": "num",
243 | "array": "arr",
244 | "object": "obj",
245 | }
246 |
247 | # Iterate over all paths in the specification
248 | for path, path_item in openapi_spec["paths"].items():
249 | # Iterate over all methods (e.g., 'get', 'post', 'put') in the path item
250 | for method, operation in path_item.items():
251 | # Skip non-method keys such as 'parameters' that can be present in the path item
252 | if method not in [
253 | "get",
254 | "post",
255 | "put",
256 | "delete",
257 | "patch",
258 | "options",
259 | "head",
260 | "trace",
261 | ]:
262 | continue
263 |
264 | # Extract the operation ID
265 | operation_id = operation.get("operationId", f"{method}_{path}")
266 |
267 | # Extract the summary, or use an empty string if it doesn't exist
268 | summary = operation.get("summary", "")
269 |
270 | # Extract parameters for the current operation
271 | parameters = extract_parameters(openapi_spec, path, method)
272 |
273 | # Shorten the types in the parameters dictionary
274 | for param_info in parameters.values():
275 | param_type = param_info["schema"].get("type")
276 | if param_type in type_shorteners:
277 | param_info["schema"]["type"] = type_shorteners[param_type]
278 |
279 | # Add the extracted information to the dictionary with the operation ID as the key
280 | all_parameters[operation_id] = {
281 | "summary": summary,
282 | "path": path,
283 | "method": method,
284 | "parameters": parameters,
285 | }
286 |
287 | return all_parameters
288 |
289 | def parse_llm_response(response: str) -> dict:
290 | """Parse LLM response to extract API call information.
291 |
292 | Args:
293 | response (str): LLM response.
294 |
295 | Returns:
296 | dict: API call information.
297 | """
298 | pattern = r'\s*(.*?)\s*\((.*?)\)\s*'
299 | match = re.search(pattern, response, re.DOTALL)
300 |
301 | if not match:
302 | return {}
303 |
304 | api = match.group(1)
305 | params_str = match.group(2)
306 |
307 | try:
308 | # Try parsing as JSON first
309 | params = json.loads(params_str)
310 | except json.JSONDecodeError:
311 | try:
312 | # If that fails, try parsing as a Python literal expression
313 | params = ast.literal_eval(params_str)
314 | except (ValueError, SyntaxError):
315 | params = {}
316 |
317 | return {
318 | 'plugin_name': api.split('.')[0],
319 | 'operation_id': api.split('.')[1],
320 | 'parameters': params
321 | }
322 |
--------------------------------------------------------------------------------
/examples/apply_plugins_three_steps.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "xXnd1cCu1TkJ"
7 | },
8 | "source": [
9 | "# Add Plugins in Three Steps\n",
10 | "\n",
11 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/apply_plugins_three_steps.ipynb)\n",
12 | "\n",
13 | "1. Load the plugins from a plugins directory (E.g. [plugnplai.com](https://plugnplai.com))\n",
14 | "2. Install and activate the plugins with `plugins = Plugins.install_and_activate(urls)`\n",
15 | "3. Add the plugins to your LLM function (you can use the decorator `@plugins.apply_plugins`)"
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "id": "OZWQil8RrAKV"
22 | },
23 | "source": [
24 | "# Install and import necessary packages"
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": 3,
30 | "metadata": {
31 | "colab": {
32 | "base_uri": "https://localhost:8080/"
33 | },
34 | "id": "7RUcr3tLEK7M",
35 | "outputId": "2aa31e20-4106-4048-c34c-b468f10fff56"
36 | },
37 | "outputs": [],
38 | "source": [
39 | "pip install plugnplai -q"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": 8,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "# You will need to first define your API key\n",
49 | "import os\n",
50 | "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_OPENAI_KEY\""
51 | ]
52 | },
53 | {
54 | "cell_type": "code",
55 | "execution_count": 13,
56 | "metadata": {},
57 | "outputs": [],
58 | "source": [
59 | "import plugnplai as pl\n",
60 | "from langchain.chat_models import ChatOpenAI\n",
61 | "from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate\n",
62 | "from langchain.schema import AIMessage, HumanMessage, SystemMessage\n",
63 | "from IPython.display import display, Markdown"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {
69 | "id": "7fYY9i4Z3sBO"
70 | },
71 | "source": [
72 | "# 1. Load plugins from [plugnplai.com](https://plugnplai.com)"
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {
78 | "id": "nlC-9wyp2vtF"
79 | },
80 | "source": [
81 | "Lets find one plugin for each category, using PlugnPlai categories (see [API reference](https://plugnplai.github.io/))\n",
82 | "- travel\n",
83 | "- shopping \n",
84 | "- weather"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": 5,
90 | "metadata": {
91 | "colab": {
92 | "base_uri": "https://localhost:8080/"
93 | },
94 | "id": "MmqqHyg5Eee0",
95 | "outputId": "33e028f9-003f-41c1-f9d0-acfcc4fd1fdd"
96 | },
97 | "outputs": [
98 | {
99 | "name": "stdout",
100 | "output_type": "stream",
101 | "text": [
102 | "Our chosen Plugins: ['https://trip.com', 'https://klarna.com', 'https://api.speak.com']\n"
103 | ]
104 | }
105 | ],
106 | "source": [
107 | "# Get working plugins - only tested plugins (in progress)\n",
108 | "urlsTravel = pl.get_plugins()\n",
109 | "\n",
110 | "# Lets pick Trip, Klarna and Speak\n",
111 | "urls = [plugin for plugin in urlsTravel if any(word in plugin for word in ('trip.com', 'klarna', 'speak'))]\n",
112 | "\n",
113 | "print(f'Our chosen Plugins: {urls}')"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {
119 | "id": "hj5MtCaPyk9p"
120 | },
121 | "source": [
122 | "# 2. Install and activate the plugins"
123 | ]
124 | },
125 | {
126 | "cell_type": "code",
127 | "execution_count": 6,
128 | "metadata": {
129 | "id": "JMHi1UTpy8LV"
130 | },
131 | "outputs": [],
132 | "source": [
133 | "from plugnplai import Plugins\n",
134 | "\n",
135 | "plugins = Plugins.install_and_activate(urls)"
136 | ]
137 | },
138 | {
139 | "cell_type": "markdown",
140 | "metadata": {
141 | "id": "2-alHvKpyk9q"
142 | },
143 | "source": [
144 | "# 3. Apply the plugins \n",
145 | "\n",
146 | "Use `@plugins.apply_plugins` decorator to easily apply plugins to your LLM function\n",
147 | "\n",
148 | "The LLM function MUST get a string as the user input as first variable and output the string with the response"
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": 9,
154 | "metadata": {
155 | "colab": {
156 | "base_uri": "https://localhost:8080/"
157 | },
158 | "id": "g770AOGZ0vwH",
159 | "outputId": "739138b3-e6f7-460d-9d5a-0837fc2f3594"
160 | },
161 | "outputs": [],
162 | "source": [
163 | "chat = ChatOpenAI(temperature=0, model=\"gpt-4\")\n",
164 | "\n",
165 | "@plugins.apply_plugins\n",
166 | "def call_llm(user_message):\n",
167 | " messages = [\n",
168 | " SystemMessage(content=\"\"),\n",
169 | " HumanMessage(content=user_message)\n",
170 | " ]\n",
171 | "\n",
172 | " res = chat(messages)\n",
173 | "\n",
174 | " llm_first_response = res.content\n",
175 | "\n",
176 | " return llm_first_response"
177 | ]
178 | },
179 | {
180 | "cell_type": "markdown",
181 | "metadata": {},
182 | "source": [
183 | "# Enjoy - Let's try it\n",
184 | "\n",
185 | "Now your function should call a plugin when the LLM decides to use a plugin \n",
186 | "or just returns the normal message if not."
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": 16,
192 | "metadata": {},
193 | "outputs": [
194 | {
195 | "name": "stdout",
196 | "output_type": "stream",
197 | "text": [
198 | "Using KlarnaProducts\n"
199 | ]
200 | },
201 | {
202 | "data": {
203 | "text/markdown": [
204 | "I found a few t-shirts for you:\n",
205 | "\n",
206 | "1. [T-shirt](https://www.klarna.com/us/shopping/pl/cl10001/3203506327/Clothing/T-shirt/?utm_source=openai&ref-site=openai_plugin) - $20.99\n",
207 | " - Material: Cotton\n",
208 | " - Target Group: Man\n",
209 | " - Color: Gray, White, Blue, Black, Orange\n",
210 | "\n",
211 | "2. [Polo Ralph Lauren Slim Fit Cotton T-shirt 3-pack](https://www.klarna.com/us/shopping/pl/cl10001/3201838628/Clothing/Polo-Ralph-Lauren-Slim-Fit-Cotton-T-shirt-3-pack/?utm_source=openai&ref-site=openai_plugin) - $42.50\n",
212 | " - Material: Cotton\n",
213 | " - Target Group: Man\n",
214 | " - Color: Gray, White, Blue, Multicolor, Black\n",
215 | "\n",
216 | "3. [Psycho Bunny Mens Copa Gradient Logo Graphic Tee](https://www.klarna.com/us/shopping/pl/cl10001/3203663222/Clothing/Psycho-Bunny-Mens-Copa-Gradient-Logo-Graphic-Tee/?utm_source=openai&ref-site=openai_plugin) - $49.00\n",
217 | " - Material: Cotton\n",
218 | " - Target Group: Man\n",
219 | " - Color: White, Blue, Black, Orange\n",
220 | "\n",
221 | "You can click on the links to view more details and make a purchase."
222 | ],
223 | "text/plain": [
224 | ""
225 | ]
226 | },
227 | "metadata": {},
228 | "output_type": "display_data"
229 | }
230 | ],
231 | "source": [
232 | "# Message that triggers the use of plugin 1\n",
233 | "response = call_llm(\"Buy a tshirt\")\n",
234 | "# Display in markdown format\n",
235 | "display(Markdown(response))"
236 | ]
237 | },
238 | {
239 | "cell_type": "code",
240 | "execution_count": 17,
241 | "metadata": {},
242 | "outputs": [
243 | {
244 | "name": "stdout",
245 | "output_type": "stream",
246 | "text": [
247 | "Using Trip\n"
248 | ]
249 | },
250 | {
251 | "data": {
252 | "text/markdown": [
253 | "I have found the top 5 hotels in Paris for your stay from December 3rd to December 8th, 2023. Here are the details:\n",
254 | "\n",
255 | "1. [Le Tsuba Hotel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=6597288&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
256 | " - Address: 45 Rue des Acacias\n",
257 | " - Price: $295 USD per night\n",
258 | " - Star rating: 4 stars\n",
259 | " - Score: 4.6/5.0 (36 reviews)\n",
260 | " - Features: Sauna, gym\n",
261 | "\n",
262 | "2. [Pullman Paris Centre - Bercy](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2107175&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
263 | " - Address: 1 Rue de Libourne\n",
264 | " - Price: $236 USD per night\n",
265 | " - Star rating: 4 stars\n",
266 | " - Score: 4.5/5.0 (42 reviews)\n",
267 | " - Features: Swimming pool, children's playground\n",
268 | "\n",
269 | "3. [Pullman Paris Tour Eiffel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2081163&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
270 | " - Address: 18 Avenue De Suffren, 22 Rue Jean Rey Entrée Au\n",
271 | " - Price: $298 USD per night\n",
272 | " - Star rating: 4 stars\n",
273 | " - Score: 4.2/5.0 (112 reviews)\n",
274 | " - Features: Gym, multi-function hall\n",
275 | "\n",
276 | "4. [Hotel de Castiglione Paris](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2157992&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
277 | " - Address: 38-40 Rue du Faubourg Saint-Honoré\n",
278 | " - Price: $221 USD per night\n",
279 | " - Star rating: 4 stars\n",
280 | " - Score: 3.9/5.0 (49 reviews)\n",
281 | " - Features: Tea room, conference hall\n",
282 | "\n",
283 | "5. [Hotel de Crillon A Rosewood Hotel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=1619850&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
284 | " - Address: 10 Pl. de la Concorde\n",
285 | " - Price: $1673 USD per night\n",
286 | " - Star rating: 5 stars\n",
287 | " - Score: 4.7/5.0 (7 reviews)\n",
288 | " - Features: Sunbathing area, sauna\n",
289 | "\n",
290 | "Please note that prices and availability are subject to change. Make sure to book your preferred hotel as soon as possible to secure your reservation."
291 | ],
292 | "text/plain": [
293 | ""
294 | ]
295 | },
296 | "metadata": {},
297 | "output_type": "display_data"
298 | }
299 | ],
300 | "source": [
301 | "# Message that triggers the use of plugin 2\n",
302 | "response = call_llm(\"Book a hotel in paris for Dec.3-8\")\n",
303 | "display(Markdown(response))"
304 | ]
305 | },
306 | {
307 | "cell_type": "code",
308 | "execution_count": 18,
309 | "metadata": {},
310 | "outputs": [
311 | {
312 | "name": "stdout",
313 | "output_type": "stream",
314 | "text": [
315 | "Using speak\n"
316 | ]
317 | },
318 | {
319 | "data": {
320 | "text/markdown": [
321 | "In Portuguese, you can say \"I love you\" as:\n",
322 | "\n",
323 | "**Eu te amo**\n",
324 | "\n",
325 | "Here are some alternative ways to express love in Portuguese:\n",
326 | "\n",
327 | "1. \"Amo-te\" (More formal, less commonly used, but still appropriate in romantic settings)\n",
328 | "2. \"Adoro-te\" (Similar to \"I adore you\", commonly used in romantic relationships)\n",
329 | "3. \"Te quero\" (Less intense than \"I love you\", also appropriate to use with friends, family, or significant others)\n",
330 | "\n",
331 | "Here's an example conversation in Portuguese:\n",
332 | "\n",
333 | "*Context: Maria and João are a young couple who are deeply in love and are on a date at the beach at sunset.*\n",
334 | "\n",
335 | "* João: \"Maria, eu te amo tanto, não consigo imaginar minha vida sem você.\"\n",
336 | "* Maria: \"Eu também te amo, João. Você é o melhor namorado que eu poderia pedir.\"\n",
337 | "\n",
338 | "[Report an issue or leave feedback](https://speak.com/chatgpt?rid=zgoen4wd4xgoc65ndmbzx4og)"
339 | ],
340 | "text/plain": [
341 | ""
342 | ]
343 | },
344 | "metadata": {},
345 | "output_type": "display_data"
346 | }
347 | ],
348 | "source": [
349 | "# Message that triggers the use of plugin 3\n",
350 | "response = call_llm(\"How to say I love you in Portuguese?\")\n",
351 | "display(Markdown(response))"
352 | ]
353 | },
354 | {
355 | "cell_type": "markdown",
356 | "metadata": {},
357 | "source": [
358 | "### Making a question that doesn't trigger the use of a plugin"
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": 15,
364 | "metadata": {},
365 | "outputs": [
366 | {
367 | "data": {
368 | "text/plain": [
369 | "'🤖 The AI revolution is transforming the world! 🌍 From enhancing productivity to personalizing experiences, artificial intelligence is unlocking new possibilities and reshaping industries. Embrace the change and join the #AIRevolution! 💡 #TechTrends #Innovation #FutureIsNow'"
370 | ]
371 | },
372 | "execution_count": 15,
373 | "metadata": {},
374 | "output_type": "execute_result"
375 | }
376 | ],
377 | "source": [
378 | "call_llm(\"Write tweet about the AI revolution.\")"
379 | ]
380 | },
381 | {
382 | "cell_type": "code",
383 | "execution_count": null,
384 | "metadata": {},
385 | "outputs": [],
386 | "source": []
387 | }
388 | ],
389 | "metadata": {
390 | "colab": {
391 | "provenance": []
392 | },
393 | "kernelspec": {
394 | "display_name": "Python 3.10.6 ('rainbow')",
395 | "language": "python",
396 | "name": "python3"
397 | },
398 | "language_info": {
399 | "codemirror_mode": {
400 | "name": "ipython",
401 | "version": 3
402 | },
403 | "file_extension": ".py",
404 | "mimetype": "text/x-python",
405 | "name": "python",
406 | "nbconvert_exporter": "python",
407 | "pygments_lexer": "ipython3",
408 | "version": "3.10.6"
409 | },
410 | "vscode": {
411 | "interpreter": {
412 | "hash": "3ccef4e08d87aa1eeb90f63e0f071292ccb2e9c42e70f74ab2bf6f5493ca7bbc"
413 | }
414 | }
415 | },
416 | "nbformat": 4,
417 | "nbformat_minor": 0
418 | }
419 |
--------------------------------------------------------------------------------
/docs/examples/apply_plugins_three_steps.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "xXnd1cCu1TkJ"
7 | },
8 | "source": [
9 | "# Add Plugins in Three Steps\n",
10 | "\n",
11 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/apply_plugins_three_steps.ipynb)\n",
12 | "\n",
13 | "1. Load the plugins from a plugins directory (E.g. [plugnplai.com](https://plugnplai.com))\n",
14 | "2. Install and activate the plugins with `plugins = Plugins.install_and_activate(urls)`\n",
15 | "3. Add the plugins to your LLM function (you can use the decorator `@plugins.apply_plugins`)"
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {
21 | "id": "OZWQil8RrAKV"
22 | },
23 | "source": [
24 | "## Install and import necessary packages"
25 | ]
26 | },
27 | {
28 | "cell_type": "code",
29 | "execution_count": 3,
30 | "metadata": {
31 | "colab": {
32 | "base_uri": "https://localhost:8080/"
33 | },
34 | "id": "7RUcr3tLEK7M",
35 | "outputId": "2aa31e20-4106-4048-c34c-b468f10fff56"
36 | },
37 | "outputs": [],
38 | "source": [
39 | "pip install plugnplai -q"
40 | ]
41 | },
42 | {
43 | "cell_type": "code",
44 | "execution_count": 8,
45 | "metadata": {},
46 | "outputs": [],
47 | "source": [
48 | "# You will need to first define your API key\n",
49 | "import os\n",
50 | "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_OPENAI_KEY\""
51 | ]
52 | },
53 | {
54 | "cell_type": "code",
55 | "execution_count": 13,
56 | "metadata": {},
57 | "outputs": [],
58 | "source": [
59 | "import plugnplai as pl\n",
60 | "from langchain.chat_models import ChatOpenAI\n",
61 | "from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate\n",
62 | "from langchain.schema import AIMessage, HumanMessage, SystemMessage\n",
63 | "from IPython.display import display, Markdown"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {
69 | "id": "7fYY9i4Z3sBO"
70 | },
71 | "source": [
72 | "## 1. Load plugins from [plugnplai.com](https://plugnplai.com)"
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {
78 | "id": "nlC-9wyp2vtF"
79 | },
80 | "source": [
81 | "Lets find one plugin for each category, using PlugnPlai categories (see [API reference](https://plugnplai.github.io/))\n",
82 | "- travel\n",
83 | "- shopping \n",
84 | "- weather"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": 5,
90 | "metadata": {
91 | "colab": {
92 | "base_uri": "https://localhost:8080/"
93 | },
94 | "id": "MmqqHyg5Eee0",
95 | "outputId": "33e028f9-003f-41c1-f9d0-acfcc4fd1fdd"
96 | },
97 | "outputs": [
98 | {
99 | "name": "stdout",
100 | "output_type": "stream",
101 | "text": [
102 | "Our chosen Plugins: ['https://trip.com', 'https://klarna.com', 'https://api.speak.com']\n"
103 | ]
104 | }
105 | ],
106 | "source": [
107 | "# Get working plugins - only tested plugins (in progress)\n",
108 | "urlsTravel = pl.get_plugins()\n",
109 | "\n",
110 | "# Lets pick Trip, Klarna and Speak\n",
111 | "urls = [plugin for plugin in urlsTravel if any(word in plugin for word in ('trip.com', 'klarna', 'speak'))]\n",
112 | "\n",
113 | "print(f'Our chosen Plugins: {urls}')"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {
119 | "id": "hj5MtCaPyk9p"
120 | },
121 | "source": [
122 | "## 2. Install and activate the plugins"
123 | ]
124 | },
125 | {
126 | "cell_type": "code",
127 | "execution_count": 6,
128 | "metadata": {
129 | "id": "JMHi1UTpy8LV"
130 | },
131 | "outputs": [],
132 | "source": [
133 | "from plugnplai import Plugins\n",
134 | "\n",
135 | "plugins = Plugins.install_and_activate(urls)"
136 | ]
137 | },
138 | {
139 | "cell_type": "markdown",
140 | "metadata": {
141 | "id": "2-alHvKpyk9q"
142 | },
143 | "source": [
144 | "## 3. Apply the plugins \n",
145 | "\n",
146 | "Use `@plugins.apply_plugins` decorator to easily apply plugins to your LLM function\n",
147 | "\n",
148 | "The LLM function MUST get a string as the user input as first variable and output the string with the response"
149 | ]
150 | },
151 | {
152 | "cell_type": "code",
153 | "execution_count": 9,
154 | "metadata": {
155 | "colab": {
156 | "base_uri": "https://localhost:8080/"
157 | },
158 | "id": "g770AOGZ0vwH",
159 | "outputId": "739138b3-e6f7-460d-9d5a-0837fc2f3594"
160 | },
161 | "outputs": [],
162 | "source": [
163 | "chat = ChatOpenAI(temperature=0, model=\"gpt-4\")\n",
164 | "\n",
165 | "@plugins.apply_plugins\n",
166 | "def call_llm(user_message):\n",
167 | " messages = [\n",
168 | " SystemMessage(content=\"\"),\n",
169 | " HumanMessage(content=user_message)\n",
170 | " ]\n",
171 | "\n",
172 | " res = chat(messages)\n",
173 | "\n",
174 | " llm_first_response = res.content\n",
175 | "\n",
176 | " return llm_first_response"
177 | ]
178 | },
179 | {
180 | "cell_type": "markdown",
181 | "metadata": {},
182 | "source": [
183 | "## Enjoy - Let's try it\n",
184 | "\n",
185 | "Now your function should call a plugin when the LLM decides to use a plugin \n",
186 | "or just returns the normal message if not."
187 | ]
188 | },
189 | {
190 | "cell_type": "code",
191 | "execution_count": 16,
192 | "metadata": {},
193 | "outputs": [
194 | {
195 | "name": "stdout",
196 | "output_type": "stream",
197 | "text": [
198 | "Using KlarnaProducts\n"
199 | ]
200 | },
201 | {
202 | "data": {
203 | "text/markdown": [
204 | "I found a few t-shirts for you:\n",
205 | "\n",
206 | "1. [T-shirt](https://www.klarna.com/us/shopping/pl/cl10001/3203506327/Clothing/T-shirt/?utm_source=openai&ref-site=openai_plugin) - $20.99\n",
207 | " - Material: Cotton\n",
208 | " - Target Group: Man\n",
209 | " - Color: Gray, White, Blue, Black, Orange\n",
210 | "\n",
211 | "2. [Polo Ralph Lauren Slim Fit Cotton T-shirt 3-pack](https://www.klarna.com/us/shopping/pl/cl10001/3201838628/Clothing/Polo-Ralph-Lauren-Slim-Fit-Cotton-T-shirt-3-pack/?utm_source=openai&ref-site=openai_plugin) - $42.50\n",
212 | " - Material: Cotton\n",
213 | " - Target Group: Man\n",
214 | " - Color: Gray, White, Blue, Multicolor, Black\n",
215 | "\n",
216 | "3. [Psycho Bunny Mens Copa Gradient Logo Graphic Tee](https://www.klarna.com/us/shopping/pl/cl10001/3203663222/Clothing/Psycho-Bunny-Mens-Copa-Gradient-Logo-Graphic-Tee/?utm_source=openai&ref-site=openai_plugin) - $49.00\n",
217 | " - Material: Cotton\n",
218 | " - Target Group: Man\n",
219 | " - Color: White, Blue, Black, Orange\n",
220 | "\n",
221 | "You can click on the links to view more details and make a purchase."
222 | ],
223 | "text/plain": [
224 | ""
225 | ]
226 | },
227 | "metadata": {},
228 | "output_type": "display_data"
229 | }
230 | ],
231 | "source": [
232 | "# Message that triggers the use of plugin 1\n",
233 | "response = call_llm(\"Buy a tshirt\")\n",
234 | "# Display in markdown format\n",
235 | "display(Markdown(response))"
236 | ]
237 | },
238 | {
239 | "cell_type": "code",
240 | "execution_count": 17,
241 | "metadata": {},
242 | "outputs": [
243 | {
244 | "name": "stdout",
245 | "output_type": "stream",
246 | "text": [
247 | "Using Trip\n"
248 | ]
249 | },
250 | {
251 | "data": {
252 | "text/markdown": [
253 | "I have found the top 5 hotels in Paris for your stay from December 3rd to December 8th, 2023. Here are the details:\n",
254 | "\n",
255 | "1. [Le Tsuba Hotel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=6597288&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
256 | " - Address: 45 Rue des Acacias\n",
257 | " - Price: $295 USD per night\n",
258 | " - Star rating: 4 stars\n",
259 | " - Score: 4.6/5.0 (36 reviews)\n",
260 | " - Features: Sauna, gym\n",
261 | "\n",
262 | "2. [Pullman Paris Centre - Bercy](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2107175&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
263 | " - Address: 1 Rue de Libourne\n",
264 | " - Price: $236 USD per night\n",
265 | " - Star rating: 4 stars\n",
266 | " - Score: 4.5/5.0 (42 reviews)\n",
267 | " - Features: Swimming pool, children's playground\n",
268 | "\n",
269 | "3. [Pullman Paris Tour Eiffel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2081163&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
270 | " - Address: 18 Avenue De Suffren, 22 Rue Jean Rey Entrée Au\n",
271 | " - Price: $298 USD per night\n",
272 | " - Star rating: 4 stars\n",
273 | " - Score: 4.2/5.0 (112 reviews)\n",
274 | " - Features: Gym, multi-function hall\n",
275 | "\n",
276 | "4. [Hotel de Castiglione Paris](https://us.trip.com/hotels/detail/?cityId=192&hotelId=2157992&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
277 | " - Address: 38-40 Rue du Faubourg Saint-Honoré\n",
278 | " - Price: $221 USD per night\n",
279 | " - Star rating: 4 stars\n",
280 | " - Score: 3.9/5.0 (49 reviews)\n",
281 | " - Features: Tea room, conference hall\n",
282 | "\n",
283 | "5. [Hotel de Crillon A Rosewood Hotel](https://us.trip.com/hotels/detail/?cityId=192&hotelId=1619850&checkin=2023-12-03&checkout=2023-12-08&curr=USD)\n",
284 | " - Address: 10 Pl. de la Concorde\n",
285 | " - Price: $1673 USD per night\n",
286 | " - Star rating: 5 stars\n",
287 | " - Score: 4.7/5.0 (7 reviews)\n",
288 | " - Features: Sunbathing area, sauna\n",
289 | "\n",
290 | "Please note that prices and availability are subject to change. Make sure to book your preferred hotel as soon as possible to secure your reservation."
291 | ],
292 | "text/plain": [
293 | ""
294 | ]
295 | },
296 | "metadata": {},
297 | "output_type": "display_data"
298 | }
299 | ],
300 | "source": [
301 | "# Message that triggers the use of plugin 2\n",
302 | "response = call_llm(\"Book a hotel in paris for Dec.3-8\")\n",
303 | "display(Markdown(response))"
304 | ]
305 | },
306 | {
307 | "cell_type": "code",
308 | "execution_count": 18,
309 | "metadata": {},
310 | "outputs": [
311 | {
312 | "name": "stdout",
313 | "output_type": "stream",
314 | "text": [
315 | "Using speak\n"
316 | ]
317 | },
318 | {
319 | "data": {
320 | "text/markdown": [
321 | "In Portuguese, you can say \"I love you\" as:\n",
322 | "\n",
323 | "**Eu te amo**\n",
324 | "\n",
325 | "Here are some alternative ways to express love in Portuguese:\n",
326 | "\n",
327 | "1. \"Amo-te\" (More formal, less commonly used, but still appropriate in romantic settings)\n",
328 | "2. \"Adoro-te\" (Similar to \"I adore you\", commonly used in romantic relationships)\n",
329 | "3. \"Te quero\" (Less intense than \"I love you\", also appropriate to use with friends, family, or significant others)\n",
330 | "\n",
331 | "Here's an example conversation in Portuguese:\n",
332 | "\n",
333 | "*Context: Maria and João are a young couple who are deeply in love and are on a date at the beach at sunset.*\n",
334 | "\n",
335 | "* João: \"Maria, eu te amo tanto, não consigo imaginar minha vida sem você.\"\n",
336 | "* Maria: \"Eu também te amo, João. Você é o melhor namorado que eu poderia pedir.\"\n",
337 | "\n",
338 | "[Report an issue or leave feedback](https://speak.com/chatgpt?rid=zgoen4wd4xgoc65ndmbzx4og)"
339 | ],
340 | "text/plain": [
341 | ""
342 | ]
343 | },
344 | "metadata": {},
345 | "output_type": "display_data"
346 | }
347 | ],
348 | "source": [
349 | "# Message that triggers the use of plugin 3\n",
350 | "response = call_llm(\"How to say I love you in Portuguese?\")\n",
351 | "display(Markdown(response))"
352 | ]
353 | },
354 | {
355 | "cell_type": "markdown",
356 | "metadata": {},
357 | "source": [
358 | "### Making a question that doesn't trigger the use of a plugin"
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": 15,
364 | "metadata": {},
365 | "outputs": [
366 | {
367 | "data": {
368 | "text/plain": [
369 | "'🤖 The AI revolution is transforming the world! 🌍 From enhancing productivity to personalizing experiences, artificial intelligence is unlocking new possibilities and reshaping industries. Embrace the change and join the #AIRevolution! 💡 #TechTrends #Innovation #FutureIsNow'"
370 | ]
371 | },
372 | "execution_count": 15,
373 | "metadata": {},
374 | "output_type": "execute_result"
375 | }
376 | ],
377 | "source": [
378 | "call_llm(\"Write tweet about the AI revolution.\")"
379 | ]
380 | },
381 | {
382 | "cell_type": "code",
383 | "execution_count": null,
384 | "metadata": {},
385 | "outputs": [],
386 | "source": []
387 | }
388 | ],
389 | "metadata": {
390 | "colab": {
391 | "provenance": []
392 | },
393 | "kernelspec": {
394 | "display_name": "Python 3.10.8 ('env': venv)",
395 | "language": "python",
396 | "name": "python3"
397 | },
398 | "language_info": {
399 | "codemirror_mode": {
400 | "name": "ipython",
401 | "version": 3
402 | },
403 | "file_extension": ".py",
404 | "mimetype": "text/x-python",
405 | "name": "python",
406 | "nbconvert_exporter": "python",
407 | "pygments_lexer": "ipython3",
408 | "version": "3.10.8"
409 | },
410 | "vscode": {
411 | "interpreter": {
412 | "hash": "db088be0df81f10a0d149836483f30eb6911268c99c8cd2461b5be70fec9cf57"
413 | }
414 | }
415 | },
416 | "nbformat": 4,
417 | "nbformat_minor": 0
418 | }
419 |
--------------------------------------------------------------------------------
/examples/create_prompt_plugins.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "xXnd1cCu1TkJ"
7 | },
8 | "source": [
9 | "# Create Prompt with Plugins Descriptions\n",
10 | "\n",
11 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/create_prompt_plugins.ipynb)\n",
12 | "\n",
13 | "The goal of this example is to load plugins specifications\n",
14 | "and create a prefix prompt describing the API to the LLM"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {
20 | "id": "OZWQil8RrAKV"
21 | },
22 | "source": [
23 | "# Install"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": 1,
29 | "metadata": {
30 | "colab": {
31 | "base_uri": "https://localhost:8080/"
32 | },
33 | "id": "7RUcr3tLEK7M",
34 | "outputId": "595c3751-91c9-4bb6-8326-9fbcca19fcff"
35 | },
36 | "outputs": [
37 | {
38 | "name": "stdout",
39 | "output_type": "stream",
40 | "text": [
41 | "Note: you may need to restart the kernel to use updated packages.\n"
42 | ]
43 | }
44 | ],
45 | "source": [
46 | "pip install plugnplai -q"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {
52 | "id": "7fYY9i4Z3sBO"
53 | },
54 | "source": [
55 | "# Get the plugins from the directory"
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {
61 | "id": "nlC-9wyp2vtF"
62 | },
63 | "source": [
64 | "We want to install three plugins at maximum to fit the description on the context length\n",
65 | "\n",
66 | "Lets find one plugin for each category:\n",
67 | "1. travel\n",
68 | "2. shopping \n",
69 | "3. weather\n",
70 | "\n",
71 | "We can use PlugnPlai categories (see [API reference](https://plugnplai.github.io/))"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": 2,
77 | "metadata": {
78 | "colab": {
79 | "base_uri": "https://localhost:8080/"
80 | },
81 | "id": "MmqqHyg5Eee0",
82 | "outputId": "2e002204-ddfb-4bb4-8a29-bf0a1bed2a25"
83 | },
84 | "outputs": [
85 | {
86 | "name": "stdout",
87 | "output_type": "stream",
88 | "text": [
89 | "Travel plugins: ['https://gogaffl.com', 'https://trip.com', 'https://api.yelp.com', 'https://gps-telecom.com']\n",
90 | "Shopping plugins: ['https://pricerunner.com', 'https://server.shop.app', 'https://klarna.com']\n",
91 | "Weather plugins: ['https://api.tomorrow.io']\n"
92 | ]
93 | }
94 | ],
95 | "source": [
96 | "import plugnplai as pl\n",
97 | "\n",
98 | "# Get working plugins - only tested plugins (in progress)\n",
99 | "urlsTravel = pl.get_plugins(category='travel')\n",
100 | "print(f'Travel plugins: {urlsTravel}')\n",
101 | "\n",
102 | "urlsShopping = pl.get_plugins(category='shopping')\n",
103 | "print(f'Shopping plugins: {urlsShopping}')\n",
104 | "\n",
105 | "urlsWeather = pl.get_plugins(category='weather')\n",
106 | "print(f'Weather plugins: {urlsWeather}')"
107 | ]
108 | },
109 | {
110 | "cell_type": "code",
111 | "execution_count": 3,
112 | "metadata": {
113 | "colab": {
114 | "base_uri": "https://localhost:8080/"
115 | },
116 | "id": "81MK5G3aJ6pA",
117 | "outputId": "4d4e72fa-ab73-45e3-d1f4-b478d0b5aa1e"
118 | },
119 | "outputs": [
120 | {
121 | "name": "stdout",
122 | "output_type": "stream",
123 | "text": [
124 | "Our chosen Plugins: ['https://trip.com', 'https://klarna.com', 'https://api.tomorrow.io']\n"
125 | ]
126 | }
127 | ],
128 | "source": [
129 | "# Lets pick one of each list and add to our url list\n",
130 | "\n",
131 | "urls = []\n",
132 | "\n",
133 | "# Trip (list index 1)\n",
134 | "urls.append([plugin for plugin in urlsTravel if 'trip' in plugin][0])\n",
135 | "\n",
136 | "# Klarna (list index 2)\n",
137 | "urls.append([plugin for plugin in urlsShopping if 'klarna' in plugin][0])\n",
138 | "\n",
139 | "# Speak (list index 0)\n",
140 | "urls.append([plugin for plugin in urlsWeather if 'tomorrow' in plugin][0])\n",
141 | "\n",
142 | "print(f'Our chosen Plugins: {urls}')"
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {},
148 | "source": [
149 | "# Load and activate the plugins"
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": 4,
155 | "metadata": {
156 | "id": "JMHi1UTpy8LV"
157 | },
158 | "outputs": [],
159 | "source": [
160 | "from plugnplai import Plugins\n",
161 | "\n",
162 | "plugins = Plugins.install_and_activate(urls)"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "metadata": {},
168 | "source": [
169 | "## Print the default prompt for the active plugins"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 5,
175 | "metadata": {
176 | "colab": {
177 | "base_uri": "https://localhost:8080/"
178 | },
179 | "id": "g770AOGZ0vwH",
180 | "outputId": "e83aeca5-d65f-4367-a653-2a36523045a5"
181 | },
182 | "outputs": [
183 | {
184 | "name": "stdout",
185 | "output_type": "stream",
186 | "text": [
187 | "\n",
188 | "# SYSTEM MESSAGE\n",
189 | "You are a large language model trained to assist humans.\n",
190 | "Knowledge Cutoff: 2021-09\n",
191 | "Current date: 2023-05-12\n",
192 | "Below is a list of available APIs that you can utilize to fulfill user requests. \n",
193 | "When using an API, please follow the specified format to make the API call. \n",
194 | "If possible, avoid asking follow-up questions and aim to complete the task with the information provided by the user.\n",
195 | "\n",
196 | "To make an API call, use the following format:\n",
197 | "\n",
198 | "[API]namespace.operationId[/API]\n",
199 | "[PARAMS]{ \n",
200 | " \"parameter_name\": \"parameter_value\",\n",
201 | " ...\n",
202 | "}[/PARAMS]\n",
203 | "\n",
204 | "For example, to call an API operation with the operation ID \"productsUsingGET\" in the \"KlarnaProducts\" namespace, \n",
205 | "and provide the required parameters \"q\" and \"size\", the format would be as follows:\n",
206 | "\n",
207 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
208 | "[PARAMS]{\n",
209 | " \"q\": \"t-shirt\", \n",
210 | " \"size\": 3\n",
211 | "}[/PARAMS]\n",
212 | "\n",
213 | "Please ensure that you use the correct namespace and operation ID, and provide the necessary parameters for each API call. \n",
214 | "After requesting the API, refrain from writing anything else and wait for the API response, which will be delivered in a new message.\n",
215 | "\n",
216 | "## Plugins description\n",
217 | "\n",
218 | "### Plugin 1\n",
219 | "// Plugin for users to effortlessly get customized travel product recommendation and itinerary planning including hotels and flights using chatGPT.\n",
220 | "namespace Trip {\n",
221 | "\n",
222 | "operationId search_flight_ticket = (_: {'originCityCode'*: 'str', 'destinationCityCode'*: 'str', 'departureDate'*: 'str', 'returnDate'*: 'str', 'locale'*: 'str', 'oneWayOrRoundTrip'*: 'str'}) => any\n",
223 | "\n",
224 | "operationId search_hotel = (_: {'cityName'*: 'str', 'topHotel'*: 'int', 'locale'*: 'str', 'checkIn'*: 'any', 'checkOut'*: 'any'}) => any}\n",
225 | "\n",
226 | "### Plugin 2\n",
227 | "// Assistant uses the Klarna plugin to get relevant product suggestions for any shopping or product discovery purpose. Assistant will reply with the following 3 paragraphs 1) Search Results 2) Product Comparison of the Search Results 3) Followup Questions. The first paragraph contains a list of the products with their attributes listed clearly and concisely as bullet points under the product, together with a link to the product and an explanation. Links will always be returned and should be shown to the user. The second paragraph compares the results returned in a summary sentence starting with \"In summary\". Assistant comparisons consider only the most important features of the products that will help them fit the users request, and each product mention is brief, short and concise. In the third paragraph assistant always asks helpful follow-up questions and end with a question mark. When assistant is asking a follow-up question, it uses it's product expertise to provide information pertaining to the subject of the user's request that may guide them in their search for the right product.\n",
228 | "namespace KlarnaProducts {\n",
229 | "\n",
230 | "operationId productsUsingGET = (_: {'q'*: 'str', 'size': 'int', 'min_price': 'int', 'max_price': 'int'}) => any}\n",
231 | "\n",
232 | "### Plugin 3\n",
233 | "// Plugin that answers questions about the weather to help users predict, plan, and adapt their day to day to the weather forecast via contextualized chat-based insights.\n",
234 | "namespace weather {\n",
235 | "\n",
236 | "operationId handleWeatherQuestion = (_: {'question'*: 'str'}) => any}\n",
237 | "\n",
238 | "\n",
239 | "# USER MESSAGE\n",
240 | "\n"
241 | ]
242 | }
243 | ],
244 | "source": [
245 | "print(plugins.prompt)"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "## Lets look at the length of the prompt\n",
253 | "\n",
254 | "Get the number of tokens of the prompt by just calling 'plugins.tokens' "
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": 6,
260 | "metadata": {
261 | "colab": {
262 | "base_uri": "https://localhost:8080/"
263 | },
264 | "id": "jnFld3xszdrx",
265 | "outputId": "fd6efdf5-6aca-45dc-bdac-f95c5a9c8d96"
266 | },
267 | "outputs": [
268 | {
269 | "name": "stdout",
270 | "output_type": "stream",
271 | "text": [
272 | "731\n"
273 | ]
274 | }
275 | ],
276 | "source": [
277 | "print(plugins.tokens)"
278 | ]
279 | },
280 | {
281 | "cell_type": "markdown",
282 | "metadata": {},
283 | "source": [
284 | "## Customize the prompt template\n",
285 | "\n",
286 | "You can customize the template by just passing 'template' as a variable\n",
287 | "\n",
288 | "Lets adapt this awesome and funny template from this [LangChain example](https://python.langchain.com/en/latest/use_cases/agents/custom_agent_with_plugin_retrieval.html)\n",
289 | "\n",
290 | "The template must have a {{plugins}} variable, to be filled with the descriptions. "
291 | ]
292 | },
293 | {
294 | "cell_type": "code",
295 | "execution_count": 7,
296 | "metadata": {},
297 | "outputs": [],
298 | "source": [
299 | "custom_template = '''\n",
300 | "Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following APIs:\n",
301 | "\n",
302 | "{{plugins}}\n",
303 | "To call any api write the name, operation Id and parameters in the following format:\n",
304 | "[API]namespace.operationId[/API]\n",
305 | "[PARAMS]{ \"parameter_name\": \"parameter_value\", ...}[/PARAMS]\n",
306 | "\n",
307 | "Like in this example to shop a t-shirt: \n",
308 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
309 | "[PARAMS]{\"q\": \"t-shirt\", \"size\": 3}[/PARAMS]\n",
310 | "\n",
311 | "Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n",
312 | "\n",
313 | "Question:\n",
314 | "'''"
315 | ]
316 | },
317 | {
318 | "cell_type": "code",
319 | "execution_count": 8,
320 | "metadata": {},
321 | "outputs": [
322 | {
323 | "name": "stdout",
324 | "output_type": "stream",
325 | "text": [
326 | "\n",
327 | "Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following APIs:\n",
328 | "\n",
329 | "### Plugin 1\n",
330 | "// Plugin for users to effortlessly get customized travel product recommendation and itinerary planning including hotels and flights using chatGPT.\n",
331 | "namespace Trip {\n",
332 | "\n",
333 | "operationId search_flight_ticket = (_: {'originCityCode'*: 'str', 'destinationCityCode'*: 'str', 'departureDate'*: 'str', 'returnDate'*: 'str', 'locale'*: 'str', 'oneWayOrRoundTrip'*: 'str'}) => any\n",
334 | "\n",
335 | "operationId search_hotel = (_: {'cityName'*: 'str', 'topHotel'*: 'int', 'locale'*: 'str', 'checkIn'*: 'any', 'checkOut'*: 'any'}) => any}\n",
336 | "\n",
337 | "### Plugin 2\n",
338 | "// Assistant uses the Klarna plugin to get relevant product suggestions for any shopping or product discovery purpose. Assistant will reply with the following 3 paragraphs 1) Search Results 2) Product Comparison of the Search Results 3) Followup Questions. The first paragraph contains a list of the products with their attributes listed clearly and concisely as bullet points under the product, together with a link to the product and an explanation. Links will always be returned and should be shown to the user. The second paragraph compares the results returned in a summary sentence starting with \"In summary\". Assistant comparisons consider only the most important features of the products that will help them fit the users request, and each product mention is brief, short and concise. In the third paragraph assistant always asks helpful follow-up questions and end with a question mark. When assistant is asking a follow-up question, it uses it's product expertise to provide information pertaining to the subject of the user's request that may guide them in their search for the right product.\n",
339 | "namespace KlarnaProducts {\n",
340 | "\n",
341 | "operationId productsUsingGET = (_: {'q'*: 'str', 'size': 'int', 'min_price': 'int', 'max_price': 'int'}) => any}\n",
342 | "\n",
343 | "### Plugin 3\n",
344 | "// Plugin that answers questions about the weather to help users predict, plan, and adapt their day to day to the weather forecast via contextualized chat-based insights.\n",
345 | "namespace weather {\n",
346 | "\n",
347 | "operationId handleWeatherQuestion = (_: {'question'*: 'str'}) => any}\n",
348 | "\n",
349 | "\n",
350 | "To call any api write the name, operation Id and parameters in the following format:\n",
351 | "[API]namespace.operationId[/API]\n",
352 | "[PARAMS]{ \"parameter_name\": \"parameter_value\", ...}[/PARAMS]\n",
353 | "\n",
354 | "Like in this example to shop a t-shirt: \n",
355 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
356 | "[PARAMS]{\"q\": \"t-shirt\", \"size\": 3}[/PARAMS]\n",
357 | "\n",
358 | "Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n",
359 | "\n",
360 | "Question:\n",
361 | "\n",
362 | "Number of tokens: 592\n"
363 | ]
364 | }
365 | ],
366 | "source": [
367 | "plugins = Plugins.install_and_activate(urls, template = custom_template)\n",
368 | "print(plugins.prompt)\n",
369 | "print(f'Number of tokens: {plugins.tokens}')"
370 | ]
371 | }
372 | ],
373 | "metadata": {
374 | "colab": {
375 | "provenance": []
376 | },
377 | "kernelspec": {
378 | "display_name": "Python 3.10.6 ('rainbow')",
379 | "language": "python",
380 | "name": "python3"
381 | },
382 | "language_info": {
383 | "codemirror_mode": {
384 | "name": "ipython",
385 | "version": 3
386 | },
387 | "file_extension": ".py",
388 | "mimetype": "text/x-python",
389 | "name": "python",
390 | "nbconvert_exporter": "python",
391 | "pygments_lexer": "ipython3",
392 | "version": "3.10.6"
393 | },
394 | "vscode": {
395 | "interpreter": {
396 | "hash": "3ccef4e08d87aa1eeb90f63e0f071292ccb2e9c42e70f74ab2bf6f5493ca7bbc"
397 | }
398 | }
399 | },
400 | "nbformat": 4,
401 | "nbformat_minor": 0
402 | }
403 |
--------------------------------------------------------------------------------
/docs/examples/create_prompt_plugins.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "xXnd1cCu1TkJ"
7 | },
8 | "source": [
9 | "# Create Prompt with Plugins Descriptions\n",
10 | "\n",
11 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/create_prompt_plugins.ipynb)\n",
12 | "\n",
13 | "The goal of this example is to load plugins specifications\n",
14 | "and create a prefix prompt describing the API to the LLM"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {
20 | "id": "OZWQil8RrAKV"
21 | },
22 | "source": [
23 | "## Install"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": 1,
29 | "metadata": {
30 | "colab": {
31 | "base_uri": "https://localhost:8080/"
32 | },
33 | "id": "7RUcr3tLEK7M",
34 | "outputId": "595c3751-91c9-4bb6-8326-9fbcca19fcff"
35 | },
36 | "outputs": [
37 | {
38 | "name": "stdout",
39 | "output_type": "stream",
40 | "text": [
41 | "Note: you may need to restart the kernel to use updated packages.\n"
42 | ]
43 | }
44 | ],
45 | "source": [
46 | "pip install plugnplai -q"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {
52 | "id": "7fYY9i4Z3sBO"
53 | },
54 | "source": [
55 | "## Get the plugins from the directory"
56 | ]
57 | },
58 | {
59 | "cell_type": "markdown",
60 | "metadata": {
61 | "id": "nlC-9wyp2vtF"
62 | },
63 | "source": [
64 | "We want to install three plugins at maximum to fit the description on the context length\n",
65 | "\n",
66 | "Lets find one plugin for each category:\n",
67 | "1. travel\n",
68 | "2. shopping \n",
69 | "3. weather\n",
70 | "\n",
71 | "We can use PlugnPlai categories (see [API reference](https://plugnplai.github.io/))"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": 2,
77 | "metadata": {
78 | "colab": {
79 | "base_uri": "https://localhost:8080/"
80 | },
81 | "id": "MmqqHyg5Eee0",
82 | "outputId": "2e002204-ddfb-4bb4-8a29-bf0a1bed2a25"
83 | },
84 | "outputs": [
85 | {
86 | "name": "stdout",
87 | "output_type": "stream",
88 | "text": [
89 | "Travel plugins: ['https://gogaffl.com', 'https://trip.com', 'https://api.yelp.com', 'https://gps-telecom.com']\n",
90 | "Shopping plugins: ['https://pricerunner.com', 'https://server.shop.app', 'https://klarna.com']\n",
91 | "Weather plugins: ['https://api.tomorrow.io']\n"
92 | ]
93 | }
94 | ],
95 | "source": [
96 | "import plugnplai as pl\n",
97 | "\n",
98 | "# Get working plugins - only tested plugins (in progress)\n",
99 | "urlsTravel = pl.get_plugins(category='travel')\n",
100 | "print(f'Travel plugins: {urlsTravel}')\n",
101 | "\n",
102 | "urlsShopping = pl.get_plugins(category='shopping')\n",
103 | "print(f'Shopping plugins: {urlsShopping}')\n",
104 | "\n",
105 | "urlsWeather = pl.get_plugins(category='weather')\n",
106 | "print(f'Weather plugins: {urlsWeather}')"
107 | ]
108 | },
109 | {
110 | "cell_type": "code",
111 | "execution_count": 3,
112 | "metadata": {
113 | "colab": {
114 | "base_uri": "https://localhost:8080/"
115 | },
116 | "id": "81MK5G3aJ6pA",
117 | "outputId": "4d4e72fa-ab73-45e3-d1f4-b478d0b5aa1e"
118 | },
119 | "outputs": [
120 | {
121 | "name": "stdout",
122 | "output_type": "stream",
123 | "text": [
124 | "Our chosen Plugins: ['https://trip.com', 'https://klarna.com', 'https://api.tomorrow.io']\n"
125 | ]
126 | }
127 | ],
128 | "source": [
129 | "# Lets pick one of each list and add to our url list\n",
130 | "\n",
131 | "urls = []\n",
132 | "\n",
133 | "# Trip (list index 1)\n",
134 | "urls.append([plugin for plugin in urlsTravel if 'trip' in plugin][0])\n",
135 | "\n",
136 | "# Klarna (list index 2)\n",
137 | "urls.append([plugin for plugin in urlsShopping if 'klarna' in plugin][0])\n",
138 | "\n",
139 | "# Speak (list index 0)\n",
140 | "urls.append([plugin for plugin in urlsWeather if 'tomorrow' in plugin][0])\n",
141 | "\n",
142 | "print(f'Our chosen Plugins: {urls}')"
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {},
148 | "source": [
149 | "## Load and activate the plugins"
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": 4,
155 | "metadata": {
156 | "id": "JMHi1UTpy8LV"
157 | },
158 | "outputs": [],
159 | "source": [
160 | "from plugnplai import Plugins\n",
161 | "\n",
162 | "plugins = Plugins.install_and_activate(urls)"
163 | ]
164 | },
165 | {
166 | "cell_type": "markdown",
167 | "metadata": {},
168 | "source": [
169 | "## Print the default prompt for the active plugins"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 5,
175 | "metadata": {
176 | "colab": {
177 | "base_uri": "https://localhost:8080/"
178 | },
179 | "id": "g770AOGZ0vwH",
180 | "outputId": "e83aeca5-d65f-4367-a653-2a36523045a5"
181 | },
182 | "outputs": [
183 | {
184 | "name": "stdout",
185 | "output_type": "stream",
186 | "text": [
187 | "\n",
188 | "# SYSTEM MESSAGE\n",
189 | "You are a large language model trained to assist humans.\n",
190 | "Knowledge Cutoff: 2021-09\n",
191 | "Current date: 2023-05-12\n",
192 | "Below is a list of available APIs that you can utilize to fulfill user requests. \n",
193 | "When using an API, please follow the specified format to make the API call. \n",
194 | "If possible, avoid asking follow-up questions and aim to complete the task with the information provided by the user.\n",
195 | "\n",
196 | "To make an API call, use the following format:\n",
197 | "\n",
198 | "[API]namespace.operationId[/API]\n",
199 | "[PARAMS]{ \n",
200 | " \"parameter_name\": \"parameter_value\",\n",
201 | " ...\n",
202 | "}[/PARAMS]\n",
203 | "\n",
204 | "For example, to call an API operation with the operation ID \"productsUsingGET\" in the \"KlarnaProducts\" namespace, \n",
205 | "and provide the required parameters \"q\" and \"size\", the format would be as follows:\n",
206 | "\n",
207 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
208 | "[PARAMS]{\n",
209 | " \"q\": \"t-shirt\", \n",
210 | " \"size\": 3\n",
211 | "}[/PARAMS]\n",
212 | "\n",
213 | "Please ensure that you use the correct namespace and operation ID, and provide the necessary parameters for each API call. \n",
214 | "After requesting the API, refrain from writing anything else and wait for the API response, which will be delivered in a new message.\n",
215 | "\n",
216 | "## Plugins description\n",
217 | "\n",
218 | "### Plugin 1\n",
219 | "// Plugin for users to effortlessly get customized travel product recommendation and itinerary planning including hotels and flights using chatGPT.\n",
220 | "namespace Trip {\n",
221 | "\n",
222 | "operationId search_flight_ticket = (_: {'originCityCode'*: 'str', 'destinationCityCode'*: 'str', 'departureDate'*: 'str', 'returnDate'*: 'str', 'locale'*: 'str', 'oneWayOrRoundTrip'*: 'str'}) => any\n",
223 | "\n",
224 | "operationId search_hotel = (_: {'cityName'*: 'str', 'topHotel'*: 'int', 'locale'*: 'str', 'checkIn'*: 'any', 'checkOut'*: 'any'}) => any}\n",
225 | "\n",
226 | "### Plugin 2\n",
227 | "// Assistant uses the Klarna plugin to get relevant product suggestions for any shopping or product discovery purpose. Assistant will reply with the following 3 paragraphs 1) Search Results 2) Product Comparison of the Search Results 3) Followup Questions. The first paragraph contains a list of the products with their attributes listed clearly and concisely as bullet points under the product, together with a link to the product and an explanation. Links will always be returned and should be shown to the user. The second paragraph compares the results returned in a summary sentence starting with \"In summary\". Assistant comparisons consider only the most important features of the products that will help them fit the users request, and each product mention is brief, short and concise. In the third paragraph assistant always asks helpful follow-up questions and end with a question mark. When assistant is asking a follow-up question, it uses it's product expertise to provide information pertaining to the subject of the user's request that may guide them in their search for the right product.\n",
228 | "namespace KlarnaProducts {\n",
229 | "\n",
230 | "operationId productsUsingGET = (_: {'q'*: 'str', 'size': 'int', 'min_price': 'int', 'max_price': 'int'}) => any}\n",
231 | "\n",
232 | "### Plugin 3\n",
233 | "// Plugin that answers questions about the weather to help users predict, plan, and adapt their day to day to the weather forecast via contextualized chat-based insights.\n",
234 | "namespace weather {\n",
235 | "\n",
236 | "operationId handleWeatherQuestion = (_: {'question'*: 'str'}) => any}\n",
237 | "\n",
238 | "\n",
239 | "# USER MESSAGE\n",
240 | "\n"
241 | ]
242 | }
243 | ],
244 | "source": [
245 | "print(plugins.prompt)"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "## Lets look at the length of the prompt\n",
253 | "\n",
254 | "Get the number of tokens of the prompt by just calling 'plugins.tokens' "
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": 6,
260 | "metadata": {
261 | "colab": {
262 | "base_uri": "https://localhost:8080/"
263 | },
264 | "id": "jnFld3xszdrx",
265 | "outputId": "fd6efdf5-6aca-45dc-bdac-f95c5a9c8d96"
266 | },
267 | "outputs": [
268 | {
269 | "name": "stdout",
270 | "output_type": "stream",
271 | "text": [
272 | "731\n"
273 | ]
274 | }
275 | ],
276 | "source": [
277 | "print(plugins.tokens)"
278 | ]
279 | },
280 | {
281 | "cell_type": "markdown",
282 | "metadata": {},
283 | "source": [
284 | "## Customize the prompt template\n",
285 | "\n",
286 | "You can customize the template by just passing 'template' as a variable\n",
287 | "\n",
288 | "Lets adapt this awesome and funny template from this [LangChain example](https://python.langchain.com/en/latest/use_cases/agents/custom_agent_with_plugin_retrieval.html)\n",
289 | "\n",
290 | "The template must have a {{plugins}} variable, to be filled with the descriptions. "
291 | ]
292 | },
293 | {
294 | "cell_type": "code",
295 | "execution_count": 7,
296 | "metadata": {},
297 | "outputs": [],
298 | "source": [
299 | "custom_template = '''\n",
300 | "Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following APIs:\n",
301 | "\n",
302 | "{{plugins}}\n",
303 | "To call any api write the name, operation Id and parameters in the following format:\n",
304 | "[API]namespace.operationId[/API]\n",
305 | "[PARAMS]{ \"parameter_name\": \"parameter_value\", ...}[/PARAMS]\n",
306 | "\n",
307 | "Like in this example to shop a t-shirt: \n",
308 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
309 | "[PARAMS]{\"q\": \"t-shirt\", \"size\": 3}[/PARAMS]\n",
310 | "\n",
311 | "Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n",
312 | "\n",
313 | "Question:\n",
314 | "'''"
315 | ]
316 | },
317 | {
318 | "cell_type": "code",
319 | "execution_count": 8,
320 | "metadata": {},
321 | "outputs": [
322 | {
323 | "name": "stdout",
324 | "output_type": "stream",
325 | "text": [
326 | "\n",
327 | "Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following APIs:\n",
328 | "\n",
329 | "### Plugin 1\n",
330 | "// Plugin for users to effortlessly get customized travel product recommendation and itinerary planning including hotels and flights using chatGPT.\n",
331 | "namespace Trip {\n",
332 | "\n",
333 | "operationId search_flight_ticket = (_: {'originCityCode'*: 'str', 'destinationCityCode'*: 'str', 'departureDate'*: 'str', 'returnDate'*: 'str', 'locale'*: 'str', 'oneWayOrRoundTrip'*: 'str'}) => any\n",
334 | "\n",
335 | "operationId search_hotel = (_: {'cityName'*: 'str', 'topHotel'*: 'int', 'locale'*: 'str', 'checkIn'*: 'any', 'checkOut'*: 'any'}) => any}\n",
336 | "\n",
337 | "### Plugin 2\n",
338 | "// Assistant uses the Klarna plugin to get relevant product suggestions for any shopping or product discovery purpose. Assistant will reply with the following 3 paragraphs 1) Search Results 2) Product Comparison of the Search Results 3) Followup Questions. The first paragraph contains a list of the products with their attributes listed clearly and concisely as bullet points under the product, together with a link to the product and an explanation. Links will always be returned and should be shown to the user. The second paragraph compares the results returned in a summary sentence starting with \"In summary\". Assistant comparisons consider only the most important features of the products that will help them fit the users request, and each product mention is brief, short and concise. In the third paragraph assistant always asks helpful follow-up questions and end with a question mark. When assistant is asking a follow-up question, it uses it's product expertise to provide information pertaining to the subject of the user's request that may guide them in their search for the right product.\n",
339 | "namespace KlarnaProducts {\n",
340 | "\n",
341 | "operationId productsUsingGET = (_: {'q'*: 'str', 'size': 'int', 'min_price': 'int', 'max_price': 'int'}) => any}\n",
342 | "\n",
343 | "### Plugin 3\n",
344 | "// Plugin that answers questions about the weather to help users predict, plan, and adapt their day to day to the weather forecast via contextualized chat-based insights.\n",
345 | "namespace weather {\n",
346 | "\n",
347 | "operationId handleWeatherQuestion = (_: {'question'*: 'str'}) => any}\n",
348 | "\n",
349 | "\n",
350 | "To call any api write the name, operation Id and parameters in the following format:\n",
351 | "[API]namespace.operationId[/API]\n",
352 | "[PARAMS]{ \"parameter_name\": \"parameter_value\", ...}[/PARAMS]\n",
353 | "\n",
354 | "Like in this example to shop a t-shirt: \n",
355 | "[API]KlarnaProducts.productsUsingGET[/API]\n",
356 | "[PARAMS]{\"q\": \"t-shirt\", \"size\": 3}[/PARAMS]\n",
357 | "\n",
358 | "Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n",
359 | "\n",
360 | "Question:\n",
361 | "\n",
362 | "Number of tokens: 592\n"
363 | ]
364 | }
365 | ],
366 | "source": [
367 | "plugins = Plugins.install_and_activate(urls, template = custom_template)\n",
368 | "print(plugins.prompt)\n",
369 | "print(f'Number of tokens: {plugins.tokens}')"
370 | ]
371 | }
372 | ],
373 | "metadata": {
374 | "colab": {
375 | "provenance": []
376 | },
377 | "kernelspec": {
378 | "display_name": "Python 3.8.15 ('envv': venv)",
379 | "language": "python",
380 | "name": "python3"
381 | },
382 | "language_info": {
383 | "codemirror_mode": {
384 | "name": "ipython",
385 | "version": 3
386 | },
387 | "file_extension": ".py",
388 | "mimetype": "text/x-python",
389 | "name": "python",
390 | "nbconvert_exporter": "python",
391 | "pygments_lexer": "ipython3",
392 | "version": "3.8.15"
393 | },
394 | "vscode": {
395 | "interpreter": {
396 | "hash": "7eed2ceb37f2e6de236eac810ddd55539a1a94fcc3b18d3286d083ea6ee73195"
397 | }
398 | }
399 | },
400 | "nbformat": 4,
401 | "nbformat_minor": 0
402 | }
403 |
--------------------------------------------------------------------------------
/plugnplai/verify_plugins/verify_for_plugnplai.csv:
--------------------------------------------------------------------------------
1 | ,user_msg,api_call,example
2 | https://api.speak.com,How do I say 'Where is the nearest pharmacy?' in Italian?,"speak.translate({'phrase_to_translate': 'Where is the nearest pharmacy?', 'learning_language': 'Italian', 'native_language': 'English', 'additional_context': '', 'full_query': 'How do I say \""Where is the nearest pharmacy?\"" in Italian?'})",
3 | https://wolframcloud.com,What is the integral of x * cos^3(x)?,WolframAlpha.getShortAnswer({'i': 'integral of x * cos^3(x)'}),
4 | https://wolframalpha.com,What is the distance between Earth and Mars?,Wolfram.getWolframAlphaResults({'input': 'distance between Earth and Mars'}),
5 | https://klarna.com,Find me 3 laptops under $1000,"KlarnaProducts.productsUsingGET({'q': 'laptop', 'size': 3, 'max_price': 1000})",
6 | https://nla.zapier.com,Preview a Zap that sends an email when a new lead is added to my CRM,"Zapier.preview_a_zap({""description_of_zap"": ""sends an email when a new lead is added to my CRM""})",
7 | https://server.shop.app,I'm looking for a pair of running shoes under $150.,"Shop.search({'query': 'running shoes', 'price_max': 150})",
8 | https://joinmilo.com,What's a magic moment I can create with my family today?,Milo.askMilo({'query': 'magic moment for family today'}),
9 | https://api.getguru.com,Find information about employee benefits in our company wiki,"guru.get_querySearch({'searchTerms': 'employee benefits', 'maxResults': 5})",
10 | https://biztoc.com,Find me the latest news about Tesla.,biztoc.getNews({'query': 'Tesla'}),
11 | https://chat-calculator-plugin.supportmirage.repl.co,Calculate the square root of 81,calculator.sqrt({'a': 81}),
12 | https://datasette.io,What is the total number of rows in the 'employees' table?,"datasette_datasette_io_3c330f.query({'sql': 'SELECT COUNT(*) FROM employees', '_shape': 'array'})",
13 | https://greenyroad.com,Please parse the content of these two URLs: https://example.com/article1 and https://example.com/article2,"url_reader.parseURLs({'urls': 'https://example.com/article1,https://example.com/article2'})",
14 | https://domainsg.pt,Check the availability of the domain 'examplestore.com' and compare prices for the '.com' TLD across registrars.,"owd.domainCheckUsingPOST({'domains': ['examplestore.com'], 'registrar': 'all'})owd.domainCompareUsingGET({'tld': 'com'})",
15 | https://quickchart.io,,,"{""user_msg"": ""Generate a QR code for the website https://www.example.com"", ""api_call"": ""quickchart.qrCode({""text"": ""https://www.example.com"", ""size"": 200})""}"
16 | https://urlbox.io,Take a screenshot of the website https://example.com in PNG format with a width of 1024 and a height of 768.,"screenshot.renderSync({'format': 'png', 'url': 'https://example.com', 'width': 1024, 'height': 768})",
17 | https://portfoliopilot.com,What are the top 3 ETFs with a focus on technology and a low expense ratio?,"portfoliopilot.getTopETFs({'philosophy': 'technology', 'is_diversified': false, 'max_expense_ratio': 0.1})",
18 | https://api.triplewhale.com,"What are the average benchmarks for Facebook ads in the electronics category with a high ad spend and high average order value between January 1, 2021, and December 31, 2021?","triple_whale_benchmarks.bi-avg-get({'dailyagg': False, 'platforms': 'Facebook', 'category': 'Electronics', 'aov_segment': 'High', 'spend_segment': 'High', 'start': '2021-01-01', 'end': '2021-12-31'})",
19 | https://api.tasty.co,Find me a delicious pasta recipe.,recipe_retrieval.food_query({'queries': ['delicious pasta']}),
20 | https://semgrep.dev,Please list the recent issues for deployment_slug 'example-deployment' and project_name 'example-project',"Semgrep.semgrep_app.saas.handlers.issue.openapi_list_recent_issues({'deployment_slug': 'example-deployment', 'project_name': 'example-project'})",
21 | https://woxo.tech,Create a video about the history of computers in English.,"woxo.createVideo({'topic': 'history of computers', 'languageCode': 'en'})",
22 | https://textbelt.com,,,"{""user_msg"": ""Send an SMS to 123-456-7890 with the message 'Hello, this is a test message from the LLM.'"", ""api_call"": ""textbelt.send({""phone"": ""123-456-7890"", ""message"": ""Hello, this is a test message from the LLM.""})""}"
23 | https://scenex.jina.ai,Please explain the content of this image: https://example.com/image.jpg,"SceneXplain.explainImage({'image': 'https://example.com/image.jpg', 'languages': ['en'], 'features': ['objects', 'colors', 'text']})",
24 | https://transvribe.com,What does the speaker say about the benefits of meditation at 5 minutes into this video? https://www.youtube.com/watch?v=example123,"Transvribe.ask({'url': 'https://www.youtube.com/watch?v=example123', 's': '5:00'})",
25 | https://websearch.plugsugar.com,Search for the best pizza places in New York City,web_search.searchGoogle({'query': 'best pizza places in New York City'}),
26 | https://sparkpost.com,"Send a message to channelId ABC123 and workspaceId DEF456 with the text 'Hello, World!'","messagebird.createChannelMessage({'channelId': 'ABC123', 'workspaceId': 'DEF456', 'body': {'type': 'text', 'content': 'Hello, World!'}})",
27 | https://appypie.com,Generate a text message using the AppyPieAIAppBuilder plugin.,"AppyPieAIAppBuilder.getText({'text': 'Hello, this is a test message.'})",
28 | https://app.asana.com,Create a task in Asana with the title 'Prepare presentation' and due date '2023-05-25',"Asana.createTask({'data': {'name': 'Prepare presentation', 'due_on': '2023-05-25'}})",
29 | https://mixerbox.com,Find me a playlist for working out,"MixerBox_OnePlayer_music.getPlaylistByType({'locale': 'en_US', 'type': 'workout'})",
30 | https://lu.ma,Get a list of upcoming events on Lu.ma with a limit of 5 events.,"luma_events.homeGetEvents({'pagination_cursor': '', 'pagination_limit': 5, 'period': 'upcoming'})",
31 | https://kalendar.ai,Show me my sales stats,KalendarAI.Stats({}),
32 | https://gh-plugin.teammait.com,Fetch the contents of the README.md file from the repository 'octocat/Hello-World',"github_repo_interaction.callOctokitMethod({""octokitMethod"": ""repos.getContent"", ""args"": {""owner"": ""octocat"", ""repo"": ""Hello-World"", ""path"": ""README.md""}})",
33 | https://datamuse.com,Find a word that means 'happy' and starts with the letter 'j',"onelook_thesaurus.getWords({'max': 100, 'ml': 'happy', 'sp': 'j*'})",
34 | https://remoteambition.com,"I'm looking for software engineering jobs near San Francisco, California.","Ambition.search({'query': 'software engineer', 'latitude': 37.7749, 'longitude': -122.4194, 'num_results': 5})",
35 | https://slack.com,Search for messages containing the word 'deadline' in Slack.,Slack.ai_alpha_search_messages({'query': 'deadline'}),
36 | https://stingray-app-9pild.ondigitalocean.app,Generate a persona based on the comments of the video with ID 'abc123xyz',yt_persona_generator.getVideoComments({'video_id': 'abc123xyz'}),
37 | https://yabblezone.net,I want to create a survey about electric cars and their impact on the environment.,"yabble.createSurveyAndFetchQuestions({'survey_topic': 'electric cars', 'additional_information': 'impact on the environment'})",
38 | https://polygon.io,What is the stock price of Apple Inc. (AAPL)?,polygon.SnapshotSummary({'ticker.any_of': 'AAPL'}),
39 | https://gpt.collov.com,I want to redesign my living room in a modern style. Here's the image URL: https://example.com/room_image.jpg,"Collov_Ai_Design.Collov Ai Design({'roomType': 'living room', 'style': 'modern', 'uploadUrl': 'https://example.com/room_image.jpg'})",
40 | https://api.yelp.com,Find me some Italian restaurants in New York City.,"yelp_business_search.v3_business_search({'term': 'Italian', 'location': 'New York City'})",
41 | https://coupert.com,Find me some coupon codes for Amazon.com,Coupert.storeUsingPost({'domain': 'amazon.com'}),
42 | https://oneword.domains,Check the availability of the domain 'examplestore.com' and compare prices for the '.com' TLD across registrars.,"owd.domainCheckUsingPOST({'domains': ['examplestore.com'], 'registrar': 'all'})owd.domainCompareUsingGET({'tld': 'com'})",
43 | https://telnyx.com,"Send a text message to +1234567890 from +0987654321 with the content 'Hello, this is a test message.'","TelnyxAPI._send_message_v2_openai_requests_v2_messages_post({'from': '+0987654321', 'to': '+1234567890', 'text': 'Hello, this is a test message.'})",
44 | https://trip.com,"Find me a round trip flight from New York City (JFK) to Los Angeles (LAX) departing on June 10th, 2023 and returning on June 20th, 2023.","Trip.search_flight_ticket({'originCityCode': 'JFK', 'destinationCityCode': 'LAX', 'departureDate': '2023-06-10', 'returnDate': '2023-06-20', 'locale': 'en', 'oneWayOrRoundTrip': 'RoundTrip'})",
45 | https://plugin.so,Find information about the domain example.com,"Plugin.so.query.search.getDomain({'domain': 'example.com', 'refresh': 'false'})",
46 | https://public-api.wordpress.com,Create a new blog post on my website with the title 'My Summer Vacation' and content 'I had an amazing time during my summer vacation. The beach was beautiful and the weather was perfect.',"a8c_wpcom.createPost({'site_id': 12345, 'title': 'My Summer Vacation', 'content': 'I had an amazing time during my summer vacation. The beach was beautiful and the weather was perfect.'})",
47 | https://baltimorehomecleaning.com,I would like to book a home cleaning service for May 25th at 2 PM.,"BHCHSC.bookHomeCleaningService({""date"": ""2023-05-25"", ""time"": ""14:00""})",
48 | https://twtdata.com,What are the stats for the Twitter account with the username 'elonmusk'?,twtData.com.getStats({'username': 'elonmusk'}),
49 | https://docsbot.ai,Find the top 3 documents related to machine learning in my library.,"DocsBot.Semantic_Search_teams__team_id__bots__bot_id__search_post({'team_id': '12345', 'bot_id': '67890', 'query': 'machine learning', 'top_k': 3})",
50 | https://savvytrader.com,Get the current stock price for Apple and Tesla.,"savvy_trader_ai.getQuotes({'stocks': ['AAPL', 'TSLA']})",
51 | https://pandia.pro,Obtenez les dernières actualités sur l'intelligence artificielle en français.,pandia.getLatestPosts({}),
52 | https://plugin.autoinfra.ai,Please run the 'top' command and show me the output.,AutoInfra1.runCommand({'command': 'top -b -n 1'}),
53 | https://infobot.ai,Create a wiki page about the ongoing Mars exploration mission.,infobot.createTopic({'topic_content': 'Mars exploration mission'}),
54 | https://gochitchat.ai,Can you please summarize the content of this link: https://example.com/article,linkReader.getContent({'url': 'https://example.com/article'}),
55 | https://polarr.co,I want a filter that can give my photo a vintage look.,polarr.get_gpt_plugin_search_ml_adjustment_assets_gpt_plugin_search_get({'prompt': 'vintage filter'}),
56 | https://dev.to,Find me some articles about machine learning on DEV Community.,"dev.getArticles({'q': 'machine learning', 'page': 1, 'per_page': 5, 'top': 'false'})",
57 | https://preview.techspecs.io,What are the specifications of the iPhone 13?,tech_specs.getTechSpecs({'query': 'iPhone 13'}),
58 | https://smyth.seo.app,"Please rewrite the content of this URL for better SEO using the keyphrase 'organic gardening tips', language 'en', country 'US', and location 'California': https://example.com/organic-gardening","SEO.rewriteForSEOFromURL({'keyphrase': 'organic gardening tips', 'lang': 'en', 'country': 'US', 'location': 'California', 'url': 'https://example.com/organic-gardening', '_context': 'Rewrite the content for better SEO'})",
59 | https://chat.noteable.io,Create a new notebook called 'My Example Notebook' in project '12345678-1234-5678-1234-567812345678',"noteable.create_notebook({'project_id': '12345678-1234-5678-1234-567812345678', 'notebook_name': 'My Example Notebook'})",
60 | https://decisionjournalapp.com,I want to create a decision about whether to buy a new car or not.,"decision_journal.create_decision({""data"": {""title"": ""Buy a new car"", ""description"": ""Deciding whether to buy a new car or not"", ""status"": ""open"", ""due_date"": ""2023-06-01""}})",
61 | https://app.reportdash.com,,,"{""user_msg"": ""Generate a report for my digital marketing channels from January 1, 2023, to May 20, 2023."", ""api_call"": ""ReportDash.generateReport({""start_date"": ""2023-01-01"", ""end_date"": ""2023-05-20""})""}"
62 | https://gogaffl.com,Find me some local guides in Paris,trips.getLocals({'location': 'Paris'}),
63 | https://haulingbuddies.com,"Find animal transporters near 123 Main Street, Springfield","haulingbuddies.searchTransportersByAddress({'address': '123 Main Street, Springfield'})",
64 | https://api.tomorrow.io,What's the weather like in New York today?,weather.handleWeatherQuestion({'question': 'What's the weather like in New York today?'}),
65 | https://wahi.com,"Find real estate listings in Toronto with 3 bedrooms, 2 bathrooms, and a maximum price of $800,000.","wahi.searchListings({'minBeds': 3, 'minBaths': 2, 'maxPrice': 800000, 'searchString': 'Toronto'})",
66 | https://pybullet.org,Add a new todo item: Buy groceries,"todo.addTodo({'username': 'example_user', 'todo': 'Buy groceries'})",
67 | https://audd.io,Can you identify the song in this video? Here's the URL: https://www.example.com/video and my API token is 1234567890abcdef.,"audd_song_id.identifyMusic({'api_token': '1234567890abcdef', 'url': 'https://www.example.com/video'})",
68 | https://avian.io,Retrieve my Google Analytics data for the last 30 days.,"avian.v1_google_analytics_four_data_retrieve({""start_date"": ""2023-04-21"", ""end_date"": ""2023-05-21""})",
69 | https://inkitt.com,Find me some stories about time travel,Inkitt.searchTitles({'q': 'time travel'}),
70 | https://lexi-shopping-assistant-chatgpt-plugin.iamnazzty.repl.co,I'm looking for a good book on machine learning. Can you recommend one?,"product_recommendation.getProductRecommendation({'userMessage': 'I\'m looking for a good book on machine learning. Can you recommend one?', 'searchPhrase': 'machine learning book', 'embeddingId': '12345', 'language': 'en', 'country': 'US'})",
71 | https://keyplays.malcsilberman.repl.co,Find the standings for the English Premier League,"keyplays_football.fetchStandings({'league_search': 'English Premier League', 'timezone': 'UTC'})",
72 | https://ndricks.com,Tell me about the latest news for the Los Angeles Lakers.,ndricks_sports_api.getTeamInfo({'team': 'Los Angeles Lakers'}),
73 | https://opentrivia.drengskapur.workers.dev,Give me 5 trivia questions with medium difficulty.,"opentrivia.getTriviaQuestions({'amount': 5, 'difficulty': 'medium'})",
74 | https://stage.glowing.ai,"I want to schedule daily motivational quotes to be sent to my phone at 8 am from May 22nd to June 1st. My name is John, my phone number is +1234567890, and my time zone is UTC-4.","Glowing.createSchedule({'phone_number': '+1234567890', 'name': 'John', 'local_time': '08:00', 'start_date': '2023-05-22', 'end_date': '2023-06-01', 'time_zone': 'UTC-4', 'messages': ['Daily motivational quote']})",
75 | https://plugin.speechki.org,"Convert this text to speech using speaker 1: 'Hello, how are you today?'","speechki_tts_plugin.tts({'speaker_id': 1, 'text': 'Hello, how are you today?'})",
76 | https://searchweb.keymate.ai,What are the latest advancements in electric vehicle technology?,internetSearch.searchGet({'q': 'latest advancements in electric vehicle technology'}),
77 | https://chatgpt.vipmanor.com,"Find me properties for sale in Los Angeles, CA and New York, NY with a minimum of 3 bedrooms and a maximum price of $1,000,000.","Manorlead.searchListings({'city_state_list': ['Los Angeles, CA', 'New York, NY'], 'lease_or_sale': 'sale', 'min_bed': 3, 'max_price': 1000000})",
78 | https://word-sneak.jeevnayak.repl.co,Let's play a game of Word Sneak!,word_sneak.get_secret_words({}),
79 | https://chatwithpdf.sdan.io,Load this PDF and tell me the main points: https://example.com/sample.pdf,chatwithpdf.loadPdf({'pdf_url': 'https://example.com/sample.pdf'}),
80 | https://comicfinder.fly.dev,Find me a comic related to programming.,comic_finder.findcomic({'request': 'programming'}),
81 | https://api.getchange.io,Find nonprofits focused on environmental conservation in California.,"nonprofits.getNonprofits({'category': 'environmental conservation', 'state': 'California', 'limit': 3, 'page': 1})",
82 | https://jettel.de,Can you provide the full transcript of this YouTube video? The video ID is 12345abcde.,video_insights.GetFullTranscriptOperationYoutube({'video-id': '12345abcde'}),
83 | https://mbplayer.com,Find me a playlist for working out,"MixerBox_OnePlayer_music.getPlaylistByType({'locale': 'en_US', 'type': 'workout'})",
84 | https://metar.pluginai.ai,Get METAR weather data for JFK airport.,metar.getMetar({'icao': 'KJFK'}),
85 | https://xyz-prompt-perfect.uc.r.appspot.com,perfect How can I make my writing better?,"rephrase.rephrasePrompt({'conversation_id': '12345', 'text': 'How can I make my writing better?'})",
86 | https://chatgpt-plugin.2u.com,Find me a course on machine learning.,edX.searchCourses({'query': 'machine learning'}),
87 | https://scholar-ai.net,Find me a paper about the effects of caffeine on cognitive function.,"scholarai.searchAbstracts({'subject': 'caffeine', 'phrase': ['cognitive function']})",
88 | https://chatgpt-plugin.prod.golden.dev,What is the current population of New York City?,"golden_data_plugin.semantic_disambiguate_api_semantic_disambiguate_post({'name': 'New York City', 'context': 'population', 'constraints': [], 'n_results': 1, 'allow_null_is_a': False})",
89 | https://bardeen.ai,Use the QueryMagicBox operation to find the weather in New York City.,Bardeen.QueryMagicBox({'query': 'weather in New York City'}),
90 | https://progressier.com,Create a PWA for my Webflow website.,progressier.newPwa({'platform': 'Webflow'}),
91 | https://labs.cactiml.com,What is the recommended treatment for type 2 diabetes?,MedQA.plugin({'query': 'recommended treatment for type 2 diabetes'}),
92 | https://screenshotone.com,Take a screenshot of the website https://example.com and save it as a PNG image.,"screenshots.takeScreenshot({'url': 'https://example.com', 'html': '', 'markdown': '', 'cache': False, 'response_type': 'image', 'format': 'png', 'dark_mode': False, 'viewport_width': 1280, 'viewport_height': 800, 'device_scale_factor': 1, 'block_cookie_banners': False, 'block_chats': False, 'block_ads': False, 'delay': 0})",
93 | https://portfolioslab.com,Please provide me with the historical performance of the stock with the symbol AAPL.,PortfoliosLab.getSymbol({'symbol': 'AAPL'}),
94 | https://retriever.openindex.ai,What are the main points of the Sarbanes-Oxley Act?,"retrieval.query_query_post({'queries': ['main points of the Sarbanes-Oxley Act'], 'namespace': 'financial_documents'})",
95 | https://buywisely.com.au,I'm looking for a laptop with good performance and battery life.,buywisely.getProductsFromOpenAI({'keyword': 'laptop'}),
96 | https://agones.gr,Find the latest match result between Manchester United and Chelsea,"Agones.getAgonesResults({'team1': 'Manchester United', 'team2': 'Chelsea', 'date_type': 'latest'})",
97 | https://aitoolhunt.com,Find AI tools for video editing,aitoolhunt.searchQuery({'search': 'video editing'}),
98 | https://chatspot.ai,Tell me about the company with the domain example.com,chatspot.chat({'message': 'Tell me about the company with the domain example.com'}),
99 | https://amazingtalker.co.kr,I want to find a Spanish teacher who can also speak English and is located in Spain.,"find_teachers.findTeachers({'teach_subject': 'Spanish', 'auxiliary_language': 'English', 'teacher_location': 'Spain'})",
100 | https://travelmyth.com,"Find me a romantic and beachfront hotel in Miami for 2 adults from June 10th, 2023 to June 15th, 2023.","travelmyth.fetchHotels({'destination': 'Miami', 'checkinDay': 10, 'checkinMonth': 6, 'checkinYear': 2023, 'checkoutDay': 15, 'checkoutMonth': 6, 'checkoutYear': 2023, 'adults': 2, 'children': 0, 'rooms': 1, 'categories': 'romantic,beachfront'})",
101 | https://tym.world,"Please get me a list of products with a parent product ID of 5, skipping the first 3 products, limiting the results to 10, ordered by name in ascending order, and with the culture set to 'en-US'.","tym.getProducts({'parent': 5, 'skip': 3, 'limit': 10, 'orderBy': 'name', 'order': 'asc', 'culture': 'en-US'})",
102 | https://alphavantage.co,use alpha_vantage to get the stock price of Microsoft,alpha_vantage.getStockPrice({'symbol': 'MSFT'}),
103 |
--------------------------------------------------------------------------------
/examples/retrieve_plugins_api.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# Retrieving Plugins using Plug and Plai API\n",
9 | "\n",
10 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/examples/retrieve_plugins_api.ipynb)\n",
11 | "\n",
12 | "This example shows how to retrieve compatible plugins using Plug and Plai within LangChain abstractions.\n",
13 | "\n",
14 | "### Install Plug and Plai and necessary LangChain tools"
15 | ]
16 | },
17 | {
18 | "cell_type": "code",
19 | "execution_count": 1,
20 | "metadata": {},
21 | "outputs": [
22 | {
23 | "name": "stdout",
24 | "output_type": "stream",
25 | "text": [
26 | "\n",
27 | "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.1.2\u001b[0m\n",
28 | "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
29 | "Note: you may need to restart the kernel to use updated packages.\n"
30 | ]
31 | }
32 | ],
33 | "source": [
34 | "pip install plugnplai -q"
35 | ]
36 | },
37 | {
38 | "attachments": {},
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "### Store all plugins in a dictionary\n",
43 | "1. Use the 'get_plugins' function to get all plugins from plugnplai.com\n",
44 | "2. Load the specifications and store in a dictionary of key: \"name_for_model\", value: PluginObject (from plugin.PluginObject - it contains all information to describe and call the plugin later)"
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": 3,
50 | "metadata": {},
51 | "outputs": [
52 | {
53 | "name": "stdout",
54 | "output_type": "stream",
55 | "text": [
56 | "Http Error: 403 Client Error: Forbidden for url: https://ramp.com/.well-known/ai-plugin.json\n",
57 | "Error Connecting: HTTPSConnectionPool(host='theansible.ai', port=443): Max retries exceeded with url: /.well-known/ai-plugin.json (Caused by NewConnectionError(': Failed to establish a new connection: [Errno -5] No address associated with hostname'))\n",
58 | "Http Error: 401 Client Error: for url: https://staging.valispace.com/rest/?format=openapi\n",
59 | "Http Error: 500 Server Error: Internal Server Error for url: https://api.kyuda.io/v1/sdk/user/openapi.json\n",
60 | "Error Connecting: HTTPSConnectionPool(host='docketalarm.com', port=443): Max retries exceeded with url: /.well-known/ai-plugin.json (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 101] Network is unreachable'))\n"
61 | ]
62 | }
63 | ],
64 | "source": [
65 | "import plugnplai as pl\n",
66 | "\n",
67 | "# Get all plugins from plugnplai.com\n",
68 | "urls = pl.get_plugins()\n",
69 | "\n",
70 | "dict_plugins = {}\n",
71 | "\n",
72 | "# Lets load 5 plugins as an example \n",
73 | "# (loading all plugins may take a while, we are loading only 50, but feel free to try loading more)\n",
74 | "max_line_length = 100\n",
75 | "for url in urls[:50]: # 50 plugins - remove [:50] to load all plugins\n",
76 | " try:\n",
77 | " manifest, specification = pl.spec_from_url(url)\n",
78 | " plugin = pl.PluginObject(url, specification, manifest)\n",
79 | " dict_plugins[plugin.name_for_model] = plugin\n",
80 | " # print(f\"Plugin {plugin.name_for_model} successfully loaded.\", end=\"\", flush=True)\n",
81 | " except Exception as e:\n",
82 | " # print(f\"Error loading plugin {url}: {e}\", end=\"\", flush=True)\n",
83 | " pass"
84 | ]
85 | },
86 | {
87 | "cell_type": "code",
88 | "execution_count": 11,
89 | "metadata": {},
90 | "outputs": [
91 | {
92 | "name": "stdout",
93 | "output_type": "stream",
94 | "text": [
95 | "Number of available plugins: 42\n"
96 | ]
97 | }
98 | ],
99 | "source": [
100 | "# print number of plugins in dict_plugins\n",
101 | "print(f\"Number of available plugins: {len(dict_plugins)}\")"
102 | ]
103 | },
104 | {
105 | "attachments": {},
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "### Retrieve the best plugin for the task\n",
110 | "Use pl.retrieve to call PlugnPlai retrieve api based on the user message - https://www.plugnplai.com/_functions/retrieve?text={user_message_here}"
111 | ]
112 | },
113 | {
114 | "cell_type": "code",
115 | "execution_count": 12,
116 | "metadata": {},
117 | "outputs": [
118 | {
119 | "data": {
120 | "text/plain": [
121 | "dict_keys(['speak', 'WolframAlpha', 'Wolfram', 'KlarnaProducts', 'Zapier', 'Shop', 'Milo', 'guru', 'biztoc', 'calculator', 'datasette_datasette_io_3c330f', 'url_reader', 'owd', 'quickchart', 'screenshot', 'portfoliopilot', 'triple_whale_benchmarks', 'recipe_retrieval', 'Semgrep', 'woxo', 'textbelt', 'mrkter', 'kittycad', 'SceneXplain', 'Transvribe', 'web_search', 'messagebird', 'AppyPieAIAppBuilder', 'Asana', 'dalle_plugin', 'MixerBox_OnePlayer_music', 'luma_events', 'safari', 'seoanalysis', 'doctors', 'KalendarAI', 'code_repo_interaction', 'onelook_thesaurus', 'Ambition', 'CensysGPT', 'Slack', 'yt_persona_generator'])"
122 | ]
123 | },
124 | "execution_count": 12,
125 | "metadata": {},
126 | "output_type": "execute_result"
127 | }
128 | ],
129 | "source": [
130 | "dict_plugins.keys()"
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "execution_count": 13,
136 | "metadata": {},
137 | "outputs": [
138 | {
139 | "name": "stdout",
140 | "output_type": "stream",
141 | "text": [
142 | "Top plugins: ['KlarnaProducts']\n"
143 | ]
144 | }
145 | ],
146 | "source": [
147 | "def top_plugins(user_message):\n",
148 | "\n",
149 | " top_plugins_names = pl.retrieve(text = user_message, available_plugins = dict_plugins.keys())\n",
150 | "\n",
151 | " # Convert the top_plugins (list of strings) in a list of PluginObject\n",
152 | " top_plugins = [dict_plugins[plugin] for plugin in top_plugins_names]\n",
153 | "\n",
154 | " plugins = pl.Plugins(top_plugins)\n",
155 | "\n",
156 | " # activate the plugins\n",
157 | " for name in top_plugins_names:\n",
158 | " plugins.activate(name)\n",
159 | "\n",
160 | " return top_plugins_names, plugins\n",
161 | "\n",
162 | "names, plugins = top_plugins(\"What t shirts are available in klarna?\")\n",
163 | "print(f\"Top plugins: {names}\")"
164 | ]
165 | },
166 | {
167 | "attachments": {},
168 | "cell_type": "markdown",
169 | "metadata": {},
170 | "source": [
171 | "# Load the top plugin into the prompt using Plugins class "
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": 14,
177 | "metadata": {},
178 | "outputs": [
179 | {
180 | "name": "stdout",
181 | "output_type": "stream",
182 | "text": [
183 | "\n",
184 | "# SYSTEM MESSAGE\n",
185 | "You are a large language model trained to assist humans.\n",
186 | "Knowledge Cutoff: 2021-09\n",
187 | "Current date: 2023-06-24\n",
188 | "Below is a list of available APIs that you can utilize to fulfill user requests. \n",
189 | "When using an API, please follow the specified format to make the API call. \n",
190 | "Don't ask follow-up questions and aim to complete the task with the information provided by the user.\n",
191 | "\n",
192 | "To make an API call, use the following format (using JSON double quotes for the API call parameters):\n",
193 | "\n",
194 | "namespace.operationId({\"parameter_name\": \"parameter_value\", ...})\n",
195 | "\n",
196 | "For example, to call an API operation with the operation ID \"productsUsingGET\" in the \"KlarnaProducts\" namespace, \n",
197 | "and provide the required parameters \"q\" and \"size\", the format would be as follows:\n",
198 | "\n",
199 | "KlarnaProducts.productsUsingGET({\"q\": \"t-shirt\", \"size\": 3})\n",
200 | "\n",
201 | "Please ensure that you use the correct namespace and operation ID, and provide the necessary parameters for each API call. \n",
202 | "After requesting the API, refrain from writing anything else and wait for the API response, which will be delivered in a new message.\n",
203 | "\n",
204 | "## Plugins description ('*' are required parameters):\n",
205 | "\n",
206 | "### Plugin 1\n",
207 | "// Assistant uses the Klarna plugin to get relevant product suggestions for any shopping or product discovery purpose. Assistant will reply with the following 3 paragraphs 1) Search Results 2) Product Comparison of the Search Results 3) Followup Questions. The first paragraph contains a list of the products with their attributes listed clearly and concisely as bullet points under the product, together with a link to the product and an explanation. Links will always be returned and should be shown to the user. The second paragraph compares the results returned in a summary sentence starting with \"In summary\". Assistant comparisons consider only the most important features of the products that will help them fit the users request, and each product mention is brief, short and concise. In the third paragraph assistant always asks helpful follow-up questions and end with a question mark. When assistant is asking a follow-up question, it uses it's product expertise to provide information pertaining to the subject of the user's request that may guide them in their search for the right product.\n",
208 | "namespace KlarnaProducts {\n",
209 | "\n",
210 | "operationId productsUsingGET = (_: {'countryCode'*: 'str', 'q'*: 'str', 'size': 'int', 'min_price': 'int', 'max_price': 'int'}) => any}\n",
211 | "\n",
212 | "\n",
213 | "# USER MESSAGE\n",
214 | "\n"
215 | ]
216 | }
217 | ],
218 | "source": [
219 | "print(plugins.prompt)"
220 | ]
221 | },
222 | {
223 | "attachments": {},
224 | "cell_type": "markdown",
225 | "metadata": {},
226 | "source": [
227 | "# Now lets just follow the example in plugins_step_by_step.ipynb"
228 | ]
229 | },
230 | {
231 | "attachments": {},
232 | "cell_type": "markdown",
233 | "metadata": {},
234 | "source": [
235 | "### Call the LLM using LangChain"
236 | ]
237 | },
238 | {
239 | "cell_type": "code",
240 | "execution_count": 42,
241 | "metadata": {},
242 | "outputs": [],
243 | "source": [
244 | "# You will need to first define your API key\n",
245 | "import os\n",
246 | "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_API_KEY\""
247 | ]
248 | },
249 | {
250 | "cell_type": "code",
251 | "execution_count": 16,
252 | "metadata": {},
253 | "outputs": [],
254 | "source": [
255 | "from langchain.chat_models import ChatOpenAI\n",
256 | "from langchain.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate\n",
257 | "from langchain.schema import AIMessage, HumanMessage, SystemMessage\n",
258 | "from IPython.display import display, Markdown"
259 | ]
260 | },
261 | {
262 | "attachments": {},
263 | "cell_type": "markdown",
264 | "metadata": {},
265 | "source": [
266 | "#### Uncomment or modify the message to test different plugins"
267 | ]
268 | },
269 | {
270 | "cell_type": "code",
271 | "execution_count": 30,
272 | "metadata": {},
273 | "outputs": [],
274 | "source": [
275 | "# Test Klarna Plugin\n",
276 | "HUMAN_MESSAGE = \"What t shirts are available in klarna?\"\n",
277 | "\n",
278 | "# Test Trip Plugin\n",
279 | "# HUMAN_MESSAGE = \"I need a hotel in Paris between Dec.3-8\"\n",
280 | "\n",
281 | "# Test Speak Plugin\n",
282 | "# HUMAN_MESSAGE = \"How to say I love you in Portuguese?\""
283 | ]
284 | },
285 | {
286 | "attachments": {},
287 | "cell_type": "markdown",
288 | "metadata": {},
289 | "source": [
290 | "### Retrieve plugins"
291 | ]
292 | },
293 | {
294 | "cell_type": "code",
295 | "execution_count": 37,
296 | "metadata": {},
297 | "outputs": [],
298 | "source": [
299 | "names, plugins = top_plugins(HUMAN_MESSAGE)"
300 | ]
301 | },
302 | {
303 | "attachments": {},
304 | "cell_type": "markdown",
305 | "metadata": {},
306 | "source": [
307 | "#### Call LLM"
308 | ]
309 | },
310 | {
311 | "cell_type": "code",
312 | "execution_count": 38,
313 | "metadata": {},
314 | "outputs": [
315 | {
316 | "name": "stdout",
317 | "output_type": "stream",
318 | "text": [
319 | "KlarnaProducts.productsUsingGET({\"countryCode\": \"us\", \"q\": \"t-shirt\", \"size\": 5})\n"
320 | ]
321 | }
322 | ],
323 | "source": [
324 | "chat = ChatOpenAI(temperature=0, model=\"gpt-4-0613\")\n",
325 | "\n",
326 | "messages = [\n",
327 | " SystemMessage(content=plugins.prompt),\n",
328 | " HumanMessage(content=HUMAN_MESSAGE)\n",
329 | "]\n",
330 | "\n",
331 | "res = chat(messages)\n",
332 | "\n",
333 | "llm_first_response = res.content\n",
334 | "\n",
335 | "print(llm_first_response)"
336 | ]
337 | },
338 | {
339 | "attachments": {},
340 | "cell_type": "markdown",
341 | "metadata": {},
342 | "source": [
343 | "## Parse the LLM response"
344 | ]
345 | },
346 | {
347 | "cell_type": "code",
348 | "execution_count": 39,
349 | "metadata": {},
350 | "outputs": [
351 | {
352 | "name": "stdout",
353 | "output_type": "stream",
354 | "text": [
355 | "{'plugin_name': 'KlarnaProducts', 'operation_id': 'productsUsingGET', 'parameters': {'countryCode': 'us', 'q': 't-shirt', 'size': 5}}\n"
356 | ]
357 | }
358 | ],
359 | "source": [
360 | "# import the parser function\n",
361 | "from plugnplai import parse_llm_response\n",
362 | "\n",
363 | "# Parse the LLM response importing '\n",
364 | "call_dict = parse_llm_response(llm_first_response)\n",
365 | "print(call_dict)"
366 | ]
367 | },
368 | {
369 | "attachments": {},
370 | "cell_type": "markdown",
371 | "metadata": {},
372 | "source": [
373 | "## Call Plugin"
374 | ]
375 | },
376 | {
377 | "cell_type": "code",
378 | "execution_count": 40,
379 | "metadata": {},
380 | "outputs": [
381 | {
382 | "name": "stdout",
383 | "output_type": "stream",
384 | "text": [
385 | "200\n"
386 | ]
387 | },
388 | {
389 | "data": {
390 | "text/plain": [
391 | "{'products': [{'name': 'Polo Ralph Lauren Slim Fit Cotton T-shirt 3-pack',\n",
392 | " 'url': 'https://www.klarna.com/us/shopping/pl/cl10001/3201838628/Clothing/Polo-Ralph-Lauren-Slim-Fit-Cotton-T-shirt-3-pack/?utm_source=openai&ref-site=openai_plugin',\n",
393 | " 'price': '$27.65',\n",
394 | " 'attributes': ['Material:Cotton',\n",
395 | " 'Target Group:Man',\n",
396 | " 'Color:Gray,White,Blue,Multicolor,Black',\n",
397 | " 'Size:S,XL,XS,L,M,XXL']},\n",
398 | " {'name': 'Palm Angels Bear T-shirt - White',\n",
399 | " 'url': 'https://www.klarna.com/us/shopping/pl/cl10001/3200094728/Clothing/Palm-Angels-Bear-T-shirt-White/?utm_source=openai&ref-site=openai_plugin',\n",
400 | " 'price': '$218.00',\n",
401 | " 'attributes': ['Size (US):12,14,8,10',\n",
402 | " 'Material:Cotton',\n",
403 | " 'Target Group:Man',\n",
404 | " 'Color:White',\n",
405 | " 'Size:S,XL,3XL,XS,L,M,XXL']},\n",
406 | " {'name': 'adidas Adicolor Classics Trefoil T-shirt - Black/White',\n",
407 | " 'url': 'https://www.klarna.com/us/shopping/pl/cl10001/3201095924/Clothing/adidas-Adicolor-Classics-Trefoil-T-shirt-Black-White/?utm_source=openai&ref-site=openai_plugin',\n",
408 | " 'price': '$11.22',\n",
409 | " 'attributes': ['Material:Cotton',\n",
410 | " 'Target Group:Man',\n",
411 | " 'Color:Black',\n",
412 | " 'Size:S,XL,XS,L,M,XXL']},\n",
413 | " {'name': 'Psycho Bunny Mens Copa Gradient Logo Graphic Tee',\n",
414 | " 'url': 'https://www.klarna.com/us/shopping/pl/cl10001/3203663222/Clothing/Psycho-Bunny-Mens-Copa-Gradient-Logo-Graphic-Tee/?utm_source=openai&ref-site=openai_plugin',\n",
415 | " 'price': '$49.00',\n",
416 | " 'attributes': ['Material:Cotton',\n",
417 | " 'Target Group:Man',\n",
418 | " 'Color:White,Blue,Black,Orange',\n",
419 | " 'Size:XXS,S,XL,3XL,XS,L,M,XXL']},\n",
420 | " {'name': \"Armani Exchange Men's Script Logo T-shirt\",\n",
421 | " 'url': 'https://www.klarna.com/us/shopping/pl/cl10001/3202187360/Clothing/Armani-Exchange-Men-s-Script-Logo-T-shirt/?utm_source=openai&ref-site=openai_plugin',\n",
422 | " 'price': '$36.95',\n",
423 | " 'attributes': ['Material:Cotton',\n",
424 | " 'Target Group:Man',\n",
425 | " 'Color:Blue,Black',\n",
426 | " 'Size:S,XL,XS,L,M,XXL']}]}"
427 | ]
428 | },
429 | "execution_count": 40,
430 | "metadata": {},
431 | "output_type": "execute_result"
432 | }
433 | ],
434 | "source": [
435 | "r = plugins.call_api(plugin_name = call_dict['plugin_name'],\n",
436 | " operation_id = call_dict['operation_id'],\n",
437 | " parameters = call_dict['parameters']\n",
438 | " )\n",
439 | "\n",
440 | "print(r.status_code)\n",
441 | "api_response = r.json()\n",
442 | "r.json()"
443 | ]
444 | },
445 | {
446 | "attachments": {},
447 | "cell_type": "markdown",
448 | "metadata": {},
449 | "source": [
450 | "## LLM responds using the API data"
451 | ]
452 | },
453 | {
454 | "cell_type": "code",
455 | "execution_count": 41,
456 | "metadata": {},
457 | "outputs": [
458 | {
459 | "data": {
460 | "text/markdown": [
461 | "Here are some t-shirts available on Klarna:\n",
462 | "\n",
463 | "1. [Polo Ralph Lauren Slim Fit Cotton T-shirt 3-pack](https://www.klarna.com/us/shopping/pl/cl10001/3201838628/Clothing/Polo-Ralph-Lauren-Slim-Fit-Cotton-T-shirt-3-pack/?utm_source=openai&ref-site=openai_plugin) - $27.65\n",
464 | " - Material: Cotton\n",
465 | " - Target Group: Man\n",
466 | " - Color: Gray, White, Blue, Multicolor, Black\n",
467 | " - Size: S, XL, XS, L, M, XXL\n",
468 | "\n",
469 | "2. [Palm Angels Bear T-shirt - White](https://www.klarna.com/us/shopping/pl/cl10001/3200094728/Clothing/Palm-Angels-Bear-T-shirt-White/?utm_source=openai&ref-site=openai_plugin) - $218.00\n",
470 | " - Size (US): 12, 14, 8, 10\n",
471 | " - Material: Cotton\n",
472 | " - Target Group: Man\n",
473 | " - Color: White\n",
474 | " - Size: S, XL, 3XL, XS, L, M, XXL\n",
475 | "\n",
476 | "3. [adidas Adicolor Classics Trefoil T-shirt - Black/White](https://www.klarna.com/us/shopping/pl/cl10001/3201095924/Clothing/adidas-Adicolor-Classics-Trefoil-T-shirt-Black-White/?utm_source=openai&ref-site=openai_plugin) - $11.22\n",
477 | " - Material: Cotton\n",
478 | " - Target Group: Man\n",
479 | " - Color: Black\n",
480 | " - Size: S, XL, XS, L, M, XXL\n",
481 | "\n",
482 | "4. [Psycho Bunny Mens Copa Gradient Logo Graphic Tee](https://www.klarna.com/us/shopping/pl/cl10001/3203663222/Clothing/Psycho-Bunny-Mens-Copa-Gradient-Logo-Graphic-Tee/?utm_source=openai&ref-site=openai_plugin) - $49.00\n",
483 | " - Material: Cotton\n",
484 | " - Target Group: Man\n",
485 | " - Color: White, Blue, Black, Orange\n",
486 | " - Size: XXS, S, XL, 3XL, XS, L, M, XXL\n",
487 | "\n",
488 | "5. [Armani Exchange Men's Script Logo T-shirt](https://www.klarna.com/us/shopping/pl/cl10001/3202187360/Clothing/Armani-Exchange-Men-s-Script-Logo-T-shirt/?utm_source=openai&ref-site=openai_plugin) - $36.95\n",
489 | " - Material: Cotton\n",
490 | " - Target Group: Man\n",
491 | " - Color: Blue, Black\n",
492 | " - Size: S, XL, XS, L, M, XXL\n",
493 | "\n",
494 | "Please note that prices and availability are subject to change."
495 | ],
496 | "text/plain": [
497 | ""
498 | ]
499 | },
500 | "metadata": {},
501 | "output_type": "display_data"
502 | }
503 | ],
504 | "source": [
505 | "from plugnplai import Plugins\n",
506 | "\n",
507 | "api_return_prompt = f\"\"\"\n",
508 | "Assistant is a large language model with access to plugins.\n",
509 | "\n",
510 | "Assistant called a plugin in response to this human message:\n",
511 | "# HUMAN MESSAGE\n",
512 | "{HUMAN_MESSAGE}\n",
513 | "\n",
514 | "# API REQUEST SUMMARY\n",
515 | "{llm_first_response}\n",
516 | "\n",
517 | "# API RESPONSE\n",
518 | "{api_response}\n",
519 | "\"\"\"\n",
520 | "\n",
521 | "# # Install the plugins ewith the original template\n",
522 | "# plugins = Plugins.install_and_activate(urls)\n",
523 | "\n",
524 | "chat = ChatOpenAI(temperature=0, model=\"gpt-4-0613\")\n",
525 | "# chat = ChatOpenAI(temperature=0, model=\"gpt-3.5-turbo\")\n",
526 | "\n",
527 | "messages = [\n",
528 | " SystemMessage(content=api_return_prompt),\n",
529 | " HumanMessage(content=\"HUMAN_MESSAGE\")\n",
530 | "]\n",
531 | "\n",
532 | "res = chat(messages)\n",
533 | "\n",
534 | "display(Markdown(res.content))"
535 | ]
536 | }
537 | ],
538 | "metadata": {
539 | "kernelspec": {
540 | "display_name": "Python 3",
541 | "language": "python",
542 | "name": "python3"
543 | },
544 | "language_info": {
545 | "codemirror_mode": {
546 | "name": "ipython",
547 | "version": 3
548 | },
549 | "file_extension": ".py",
550 | "mimetype": "text/x-python",
551 | "name": "python",
552 | "nbconvert_exporter": "python",
553 | "pygments_lexer": "ipython3",
554 | "version": "3.9.13"
555 | },
556 | "orig_nbformat": 4
557 | },
558 | "nbformat": 4,
559 | "nbformat_minor": 2
560 | }
561 |
--------------------------------------------------------------------------------
/docs/examples/plugin_retriever_with_langchain_agent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "ba5f8741",
6 | "metadata": {
7 | "id": "ba5f8741"
8 | },
9 | "source": [
10 | "# Plugin Retriever - Plug and Plai\n",
11 | "### Retrieve the optimal plugin to use with LLMs based on the user's message\n",
12 | "\n",
13 | "[](https://colab.research.google.com/github/edreisMD/plugnplai/blob/main/docs/examples/plugin_retriever_with_langchain_agent.ipynb)\n"
14 | ]
15 | },
16 | {
17 | "cell_type": "markdown",
18 | "id": "rPtygjemCQHE",
19 | "metadata": {
20 | "id": "rPtygjemCQHE"
21 | },
22 | "source": [
23 | "This notebook builds upon the idea of LangChain [tool retrieval](custom_agent_with_plugin_retrieval.html), but pulls all tools from `plugnplai` - a directory of AI Plugins. And abstracts the the instantiation of the vector database (plugin_retriever)."
24 | ]
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "id": "fea4812c",
29 | "metadata": {
30 | "id": "fea4812c"
31 | },
32 | "source": [
33 | "## Set up environment"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "aca08be8",
39 | "metadata": {
40 | "id": "aca08be8"
41 | },
42 | "source": [
43 | "Install plugnplai lib to get a list of active plugins from https://plugplai.com directory"
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": 22,
49 | "id": "OKr4jRvDv4IJ",
50 | "metadata": {
51 | "id": "OKr4jRvDv4IJ"
52 | },
53 | "outputs": [],
54 | "source": [
55 | "!pip install plugnplai -q"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": 23,
61 | "id": "EiqnqramMcth",
62 | "metadata": {
63 | "id": "EiqnqramMcth"
64 | },
65 | "outputs": [],
66 | "source": [
67 | "import os\n",
68 | "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_OPENAI_KEY\" # Copy your OpenAI Key from https://platform.openai.com/account/api-keys"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "id": "6df0253f",
74 | "metadata": {
75 | "id": "6df0253f"
76 | },
77 | "source": [
78 | "## Set up plugins\n",
79 | "\n",
80 | "Load and index plugins"
81 | ]
82 | },
83 | {
84 | "cell_type": "code",
85 | "execution_count": 24,
86 | "id": "9e0f7882",
87 | "metadata": {
88 | "id": "9e0f7882"
89 | },
90 | "outputs": [],
91 | "source": [
92 | "import plugnplai \n",
93 | "\n",
94 | "# Get all plugins from plugnplai.com\n",
95 | "urls = plugnplai.get_plugins()\n",
96 | "\n",
97 | "# Or get ChatGPT plugins - only ChatGPT verified plugins\n",
98 | "urls = plugnplai.get_plugins(filter = 'ChatGPT')\n",
99 | "\n",
100 | "# Or get working plugins - only tested plugins (in progress)\n",
101 | "urls = plugnplai.get_plugins(filter = 'working')"
102 | ]
103 | },
104 | {
105 | "cell_type": "markdown",
106 | "id": "17362717",
107 | "metadata": {
108 | "id": "17362717"
109 | },
110 | "source": [
111 | "## Plugin Retriever\n",
112 | "\n",
113 | "We will use a vectorstore to create embeddings for each tool description. Then, for an incoming query we can create embeddings for that query and do a similarity search for relevant tools."
114 | ]
115 | },
116 | {
117 | "cell_type": "code",
118 | "execution_count": 25,
119 | "id": "EqHhHrPbrMMj",
120 | "metadata": {
121 | "id": "EqHhHrPbrMMj"
122 | },
123 | "outputs": [],
124 | "source": [
125 | "'''\n",
126 | "Initialize the PluginRetriever - Index the manifests in the vector database\n",
127 | "'''\n",
128 | "\n",
129 | "# Import PluginRetriever from plugnplai lib\n",
130 | "from plugnplai import PluginRetriever\n",
131 | "\n",
132 | "\n",
133 | "# Initialize directly from the provider website (https://plugnplai.com)\n",
134 | "# In this case it wouldn't be necessary to use any previous functions\n",
135 | "plugin_retriever = PluginRetriever.from_directory()\n",
136 | "\n",
137 | "# Or initialize from the manifests\n",
138 | "manifests = [plugnplai.spec_from_url(url)[0] for url in urls]\n",
139 | "plugin_retriever = PluginRetriever(manifests)\n",
140 | "\n",
141 | "# Or initialize directly from the ulrs, lets use this option to be consistent with the next steps\n",
142 | "plugin_retriever = PluginRetriever.from_urls(urls)"
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "id": "FAA45e2Z6JZI",
148 | "metadata": {
149 | "id": "FAA45e2Z6JZI"
150 | },
151 | "source": [
152 | "## Define LangChain Agent with PluginsRetriever"
153 | ]
154 | },
155 | {
156 | "cell_type": "markdown",
157 | "id": "V0SK-Pk4y-w2",
158 | "metadata": {
159 | "id": "V0SK-Pk4y-w2"
160 | },
161 | "source": [
162 | "Add plugin_retriever to LangChain Tools to call the custom retrieved APIs"
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": 26,
168 | "id": "5yVgfYRETCPG",
169 | "metadata": {
170 | "colab": {
171 | "base_uri": "https://localhost:8080/"
172 | },
173 | "id": "5yVgfYRETCPG",
174 | "outputId": "e34f54b2-a64c-4d1a-d288-dab3c35b8e11"
175 | },
176 | "outputs": [
177 | {
178 | "name": "stderr",
179 | "output_type": "stream",
180 | "text": [
181 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load a Swagger 2.0 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
182 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
183 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
184 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.2 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
185 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
186 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
187 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
188 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.2 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
189 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
190 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n",
191 | "WARNING:langchain.tools.openapi.utils.openapi_utils:Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n"
192 | ]
193 | }
194 | ],
195 | "source": [
196 | "from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser\n",
197 | "from langchain.prompts import StringPromptTemplate\n",
198 | "from langchain import OpenAI, SerpAPIWrapper, LLMChain\n",
199 | "from typing import List, Union\n",
200 | "from langchain.schema import AgentAction, AgentFinish\n",
201 | "from langchain.agents.agent_toolkits import NLAToolkit\n",
202 | "from langchain.tools.plugin import AIPlugin\n",
203 | "import re\n",
204 | "\n",
205 | "# Load LangChain AIPlugin Objects \n",
206 | "AI_PLUGINS = [AIPlugin.from_url(url + \"/.well-known/ai-plugin.json\") for url in urls]\n",
207 | "\n",
208 | "# Define llm\n",
209 | "llm = OpenAI(temperature=0)\n",
210 | "\n",
211 | "toolkits_dict = {plugin.name_for_model: \n",
212 | " NLAToolkit.from_llm_and_ai_plugin(llm, plugin)\n",
213 | " for plugin in AI_PLUGINS}\n",
214 | "\n",
215 | "def get_tools(query):\n",
216 | " # Get documents, which contain the Plugins to use\n",
217 | " docs = plugin_retriever.retrieve_names(query)\n",
218 | " # Get the toolkits, one for each plugin\n",
219 | " tool_kits = [toolkits_dict[d] for d in docs]\n",
220 | " # Get the tools: a separate NLAChain for each endpoint\n",
221 | " tools = []\n",
222 | " for tk in tool_kits:\n",
223 | " tools.extend(tk.nla_tools)\n",
224 | " return tools"
225 | ]
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "id": "7699afd7",
230 | "metadata": {
231 | "id": "7699afd7"
232 | },
233 | "source": [
234 | "\n",
235 | "We can now test this retriever to see if it seems to work."
236 | ]
237 | },
238 | {
239 | "cell_type": "code",
240 | "execution_count": 27,
241 | "id": "425f2886",
242 | "metadata": {
243 | "colab": {
244 | "base_uri": "https://localhost:8080/"
245 | },
246 | "id": "425f2886",
247 | "outputId": "3cb5fc3c-4eb8-4669-c086-ad1310ea02f4"
248 | },
249 | "outputs": [
250 | {
251 | "data": {
252 | "text/plain": [
253 | "['Milo.askMilo',\n",
254 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.search_all_actions',\n",
255 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.preview_a_zap',\n",
256 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_configuration_link',\n",
257 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.list_exposed_actions',\n",
258 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_execution_log_endpoint',\n",
259 | " 'SchoolDigger_API_V2.0.Autocomplete_GetSchools',\n",
260 | " 'SchoolDigger_API_V2.0.Districts_GetAllDistricts2',\n",
261 | " 'SchoolDigger_API_V2.0.Districts_GetDistrict2',\n",
262 | " 'SchoolDigger_API_V2.0.Rankings_GetSchoolRank2',\n",
263 | " 'SchoolDigger_API_V2.0.Rankings_GetRank_District',\n",
264 | " 'SchoolDigger_API_V2.0.Schools_GetAllSchools20',\n",
265 | " 'SchoolDigger_API_V2.0.Schools_GetSchool20',\n",
266 | " 'Open_AI_Klarna_product_Api.productsUsingGET']"
267 | ]
268 | },
269 | "execution_count": 27,
270 | "metadata": {},
271 | "output_type": "execute_result"
272 | }
273 | ],
274 | "source": [
275 | "tools = get_tools(\"what could I do today with my kiddo\")\n",
276 | "[t.name for t in tools]"
277 | ]
278 | },
279 | {
280 | "cell_type": "code",
281 | "execution_count": 28,
282 | "id": "3aa88768",
283 | "metadata": {
284 | "colab": {
285 | "base_uri": "https://localhost:8080/"
286 | },
287 | "id": "3aa88768",
288 | "outputId": "e4e69afd-1fdc-4421-a10a-eca22255a26c"
289 | },
290 | "outputs": [
291 | {
292 | "data": {
293 | "text/plain": [
294 | "['Open_AI_Klarna_product_Api.productsUsingGET',\n",
295 | " 'Milo.askMilo',\n",
296 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.search_all_actions',\n",
297 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.preview_a_zap',\n",
298 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_configuration_link',\n",
299 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.list_exposed_actions',\n",
300 | " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_execution_log_endpoint',\n",
301 | " 'SchoolDigger_API_V2.0.Autocomplete_GetSchools',\n",
302 | " 'SchoolDigger_API_V2.0.Districts_GetAllDistricts2',\n",
303 | " 'SchoolDigger_API_V2.0.Districts_GetDistrict2',\n",
304 | " 'SchoolDigger_API_V2.0.Rankings_GetSchoolRank2',\n",
305 | " 'SchoolDigger_API_V2.0.Rankings_GetRank_District',\n",
306 | " 'SchoolDigger_API_V2.0.Schools_GetAllSchools20',\n",
307 | " 'SchoolDigger_API_V2.0.Schools_GetSchool20']"
308 | ]
309 | },
310 | "execution_count": 28,
311 | "metadata": {},
312 | "output_type": "execute_result"
313 | }
314 | ],
315 | "source": [
316 | "tools = get_tools(\"what shirts can i buy?\")\n",
317 | "[t.name for t in tools]"
318 | ]
319 | },
320 | {
321 | "cell_type": "markdown",
322 | "id": "2e7a075c",
323 | "metadata": {
324 | "id": "2e7a075c"
325 | },
326 | "source": [
327 | "## Prompt Template\n",
328 | "\n",
329 | "The prompt template is pretty standard, because we're not actually changing that much logic in the actual prompt template, but rather we are just changing how retrieval is done."
330 | ]
331 | },
332 | {
333 | "cell_type": "code",
334 | "execution_count": 29,
335 | "id": "339b1bb8",
336 | "metadata": {
337 | "id": "339b1bb8"
338 | },
339 | "outputs": [],
340 | "source": [
341 | "# Set up the base template\n",
342 | "template = \"\"\"Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:\n",
343 | "\n",
344 | "{tools}\n",
345 | "\n",
346 | "Use the following format:\n",
347 | "\n",
348 | "Question: the input question you must answer\n",
349 | "Thought: you should always think about what to do\n",
350 | "Action: the action to take, should be one of [{tool_names}]\n",
351 | "Action Input: the input to the action\n",
352 | "Observation: the result of the action\n",
353 | "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
354 | "Thought: I now know the final answer\n",
355 | "Final Answer: the final answer to the original input question\n",
356 | "\n",
357 | "Begin! Remember to speak as a pirate when giving your final answer. Use lots of \"Arg\"s\n",
358 | "\n",
359 | "Question: {input}\n",
360 | "{agent_scratchpad}\"\"\""
361 | ]
362 | },
363 | {
364 | "cell_type": "markdown",
365 | "id": "1583acdc",
366 | "metadata": {
367 | "id": "1583acdc"
368 | },
369 | "source": [
370 | "The custom prompt template now has the concept of a tools_getter, which we call on the input to select the tools to use"
371 | ]
372 | },
373 | {
374 | "cell_type": "code",
375 | "execution_count": 30,
376 | "id": "fd969d31",
377 | "metadata": {
378 | "id": "fd969d31"
379 | },
380 | "outputs": [],
381 | "source": [
382 | "from typing import Callable\n",
383 | "# Set up a prompt template\n",
384 | "class CustomPromptTemplate(StringPromptTemplate):\n",
385 | " # The template to use\n",
386 | " template: str\n",
387 | " ############## NEW ######################\n",
388 | " # The list of tools available\n",
389 | " tools_getter: Callable\n",
390 | " \n",
391 | " def format(self, **kwargs) -> str:\n",
392 | " # Get the intermediate steps (AgentAction, Observation tuples)\n",
393 | " # Format them in a particular way\n",
394 | " intermediate_steps = kwargs.pop(\"intermediate_steps\")\n",
395 | " thoughts = \"\"\n",
396 | " for action, observation in intermediate_steps:\n",
397 | " thoughts += action.log\n",
398 | " thoughts += f\"\\nObservation: {observation}\\nThought: \"\n",
399 | " # Set the agent_scratchpad variable to that value\n",
400 | " kwargs[\"agent_scratchpad\"] = thoughts\n",
401 | " ############## NEW ######################\n",
402 | " tools = self.tools_getter(kwargs[\"input\"])\n",
403 | " # Create a tools variable from the list of tools provided\n",
404 | " kwargs[\"tools\"] = \"\\n\".join([f\"{tool.name}: {tool.description}\" for tool in tools])\n",
405 | " # Create a list of tool names for the tools provided\n",
406 | " kwargs[\"tool_names\"] = \", \".join([tool.name for tool in tools])\n",
407 | " return self.template.format(**kwargs)"
408 | ]
409 | },
410 | {
411 | "cell_type": "code",
412 | "execution_count": 31,
413 | "id": "798ef9fb",
414 | "metadata": {
415 | "id": "798ef9fb"
416 | },
417 | "outputs": [],
418 | "source": [
419 | "prompt = CustomPromptTemplate(\n",
420 | " template=template,\n",
421 | " tools_getter=get_tools,\n",
422 | " # This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically\n",
423 | " # This includes the `intermediate_steps` variable because that is needed\n",
424 | " input_variables=[\"input\", \"intermediate_steps\"]\n",
425 | ")"
426 | ]
427 | },
428 | {
429 | "cell_type": "markdown",
430 | "id": "ef3a1af3",
431 | "metadata": {
432 | "id": "ef3a1af3"
433 | },
434 | "source": [
435 | "## Output Parser\n",
436 | "\n",
437 | "The output parser is unchanged from the previous notebook, since we are not changing anything about the output format."
438 | ]
439 | },
440 | {
441 | "cell_type": "code",
442 | "execution_count": 32,
443 | "id": "7c6fe0d3",
444 | "metadata": {
445 | "id": "7c6fe0d3"
446 | },
447 | "outputs": [],
448 | "source": [
449 | "class CustomOutputParser(AgentOutputParser):\n",
450 | " \n",
451 | " def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:\n",
452 | " # Check if agent should finish\n",
453 | " if \"Final Answer:\" in llm_output:\n",
454 | " return AgentFinish(\n",
455 | " # Return values is generally always a dictionary with a single `output` key\n",
456 | " # It is not recommended to try anything else at the moment :)\n",
457 | " return_values={\"output\": llm_output.split(\"Final Answer:\")[-1].strip()},\n",
458 | " log=llm_output,\n",
459 | " )\n",
460 | " # Parse out the action and action input\n",
461 | " regex = r\"Action\\s*\\d*\\s*:(.*?)\\nAction\\s*\\d*\\s*Input\\s*\\d*\\s*:[\\s]*(.*)\"\n",
462 | " match = re.search(regex, llm_output, re.DOTALL)\n",
463 | " if not match:\n",
464 | " raise ValueError(f\"Could not parse LLM output: `{llm_output}`\")\n",
465 | " action = match.group(1).strip()\n",
466 | " action_input = match.group(2)\n",
467 | " # Return the action and action input\n",
468 | " return AgentAction(tool=action, tool_input=action_input.strip(\" \").strip('\"'), log=llm_output)"
469 | ]
470 | },
471 | {
472 | "cell_type": "code",
473 | "execution_count": 33,
474 | "id": "d278706a",
475 | "metadata": {
476 | "id": "d278706a"
477 | },
478 | "outputs": [],
479 | "source": [
480 | "output_parser = CustomOutputParser()"
481 | ]
482 | },
483 | {
484 | "cell_type": "markdown",
485 | "id": "170587b1",
486 | "metadata": {
487 | "id": "170587b1"
488 | },
489 | "source": [
490 | "## Set up LLM, stop sequence, and the agent\n",
491 | "\n",
492 | "Also the same as the previous notebook"
493 | ]
494 | },
495 | {
496 | "cell_type": "code",
497 | "execution_count": 34,
498 | "id": "f9d4c374",
499 | "metadata": {
500 | "id": "f9d4c374"
501 | },
502 | "outputs": [],
503 | "source": [
504 | "llm = OpenAI(temperature=0)"
505 | ]
506 | },
507 | {
508 | "cell_type": "code",
509 | "execution_count": 35,
510 | "id": "9b1cc2a2",
511 | "metadata": {
512 | "id": "9b1cc2a2"
513 | },
514 | "outputs": [],
515 | "source": [
516 | "# LLM chain consisting of the LLM and a prompt\n",
517 | "llm_chain = LLMChain(llm=llm, prompt=prompt)"
518 | ]
519 | },
520 | {
521 | "cell_type": "code",
522 | "execution_count": 36,
523 | "id": "e4f5092f",
524 | "metadata": {
525 | "id": "e4f5092f"
526 | },
527 | "outputs": [],
528 | "source": [
529 | "tool_names = [tool.name for tool in tools]\n",
530 | "agent = LLMSingleActionAgent(\n",
531 | " llm_chain=llm_chain, \n",
532 | " output_parser=output_parser,\n",
533 | " stop=[\"\\nObservation:\"], \n",
534 | " allowed_tools=tool_names\n",
535 | ")"
536 | ]
537 | },
538 | {
539 | "cell_type": "markdown",
540 | "id": "aa8a5326",
541 | "metadata": {
542 | "id": "aa8a5326"
543 | },
544 | "source": [
545 | "## Use the Agent\n",
546 | "\n",
547 | "Now we can use it!"
548 | ]
549 | },
550 | {
551 | "cell_type": "code",
552 | "execution_count": 37,
553 | "id": "490604e9",
554 | "metadata": {
555 | "id": "490604e9"
556 | },
557 | "outputs": [],
558 | "source": [
559 | "agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)"
560 | ]
561 | },
562 | {
563 | "cell_type": "code",
564 | "execution_count": 38,
565 | "id": "653b1617",
566 | "metadata": {
567 | "colab": {
568 | "base_uri": "https://localhost:8080/",
569 | "height": 288
570 | },
571 | "id": "653b1617",
572 | "outputId": "3d870dbd-9fba-465b-a2f3-e507735a182f"
573 | },
574 | "outputs": [
575 | {
576 | "name": "stdout",
577 | "output_type": "stream",
578 | "text": [
579 | "\n",
580 | "\n",
581 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
582 | "\u001b[32;1m\u001b[1;3mThought: I need to find a way to get product information\n",
583 | "Action: Open_AI_Klarna_product_Api.productsUsingGET\n",
584 | "Action Input: shirts\u001b[0m\n",
585 | "\n",
586 | "Observation:\u001b[36;1m\u001b[1;3mI found 10 shirts from the API response. They range in price from $13.94 to $491.00 and come in a variety of materials, sizes, colors, and styles.\u001b[0m\n",
587 | "\u001b[32;1m\u001b[1;3m I now know the final answer\n",
588 | "Final Answer: Arg, ye can buy any of these 10 shirts from the API response. They range in price from $13.94 to $491.00 and come in a variety of materials, sizes, colors, and styles.\u001b[0m\n",
589 | "\n",
590 | "\u001b[1m> Finished chain.\u001b[0m\n"
591 | ]
592 | },
593 | {
594 | "data": {
595 | "application/vnd.google.colaboratory.intrinsic+json": {
596 | "type": "string"
597 | },
598 | "text/plain": [
599 | "'Arg, ye can buy any of these 10 shirts from the API response. They range in price from $13.94 to $491.00 and come in a variety of materials, sizes, colors, and styles.'"
600 | ]
601 | },
602 | "execution_count": 38,
603 | "metadata": {},
604 | "output_type": "execute_result"
605 | }
606 | ],
607 | "source": [
608 | "agent_executor.run(\"what shirts can i buy?\")"
609 | ]
610 | },
611 | {
612 | "cell_type": "code",
613 | "execution_count": null,
614 | "id": "dvcVS5dLJQYn",
615 | "metadata": {
616 | "id": "dvcVS5dLJQYn"
617 | },
618 | "outputs": [],
619 | "source": []
620 | }
621 | ],
622 | "metadata": {
623 | "colab": {
624 | "provenance": []
625 | },
626 | "kernelspec": {
627 | "display_name": "Python 3.10.6 64-bit",
628 | "language": "python",
629 | "name": "python3"
630 | },
631 | "language_info": {
632 | "codemirror_mode": {
633 | "name": "ipython",
634 | "version": 3
635 | },
636 | "file_extension": ".py",
637 | "mimetype": "text/x-python",
638 | "name": "python",
639 | "nbconvert_exporter": "python",
640 | "pygments_lexer": "ipython3",
641 | "version": "3.10.6"
642 | },
643 | "vscode": {
644 | "interpreter": {
645 | "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
646 | }
647 | }
648 | },
649 | "nbformat": 4,
650 | "nbformat_minor": 5
651 | }
652 |
--------------------------------------------------------------------------------