[add] basic plugin support
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Evan Reichard 2023-11-05 21:01:43 -05:00
parent 5afd2bb498
commit 34a602edc3
4 changed files with 113 additions and 1 deletions

View File

@ -3,6 +3,7 @@ import click
import signal
import sys
from importlib.metadata import version
from minyma.plugin import PluginLoader
from minyma.oai import OpenAIConnector
from minyma.vdb import ChromaDB
from flask import Flask
@ -15,7 +16,7 @@ def signal_handler(sig, frame):
def create_app():
global oai, vdb
global oai, vdb, pman
from minyma.config import Config
import minyma.api.common as api_common
@ -24,6 +25,7 @@ def create_app():
app = Flask(__name__)
vdb = ChromaDB(path.join(Config.DATA_PATH, "chroma"))
oai = OpenAIConnector(Config.OPENAI_API_KEY, vdb)
pman = PluginLoader()
app.register_blueprint(api_common.bp)
app.register_blueprint(api_v1.bp)

86
minyma/plugin.py Normal file
View File

@ -0,0 +1,86 @@
import inspect
import os
import importlib.util
TYPE_DEFS = {str:"string"}
class MinymaPlugin:
pass
class PluginLoader:
def __init__(self):
self.plugins = self.get_plugins()
self.definitions = self.derive_function_definitions()
print(self.definitions)
def derive_function_definitions(self):
"""Dynamically generate function definitions"""
function_definitions = []
for plugin in self.plugins:
plugin_name = plugin.name
for method_obj in plugin.functions:
method_name = method_obj.__name__
signature = inspect.signature(method_obj)
parameters = list(signature.parameters.values())
# Extract Parameter Information
params_info = {}
for param in parameters:
param_name = param.name
param_type = param.annotation
params_info[param_name] = {"type": TYPE_DEFS[param_type]}
# Store Function Information
method_info = {
"name": "%s_%s" %(plugin_name, method_name),
"description": inspect.getdoc(method_obj),
"parameters": {
"type": "object",
"properties": params_info
}
}
function_definitions.append(method_info)
return function_definitions
def get_plugins(self):
"""Dynamically load plugins"""
# Derive Plugin Folder
loader_dir = os.path.dirname(os.path.abspath(__file__))
plugin_folder = os.path.join(loader_dir, "plugins")
# Find Minyma Plugins
plugin_classes = []
for filename in os.listdir(plugin_folder):
# Exclude Files
if not filename.endswith(".py") or filename == "__init__.py":
continue
# Derive Module Path
module_name = os.path.splitext(filename)[0]
module_path = os.path.join(plugin_folder, filename)
# Load Module Dynamically
spec = importlib.util.spec_from_file_location(module_name, module_path)
if spec is None or spec.loader is None:
raise ImportError("Unable to dynamically load plugin - %s" % filename)
# Load & Exec Module
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Only Process MinymaPlugin SubClasses
for _, member in inspect.getmembers(module):
if inspect.isclass(member) and issubclass(member, MinymaPlugin) and member != MinymaPlugin:
plugin_classes.append(member)
# Instantiate Plugins
plugins = []
for cls in plugin_classes:
plugins.append(cls())
return plugins

3
minyma/plugins/README.md Normal file
View File

@ -0,0 +1,3 @@
# Plugins
These are plugins that provide OpenAI with functions. Each plugin can define multiple plugins. The plugin loader will automatically derive the function definition. Each function will have the plugin name prepended.

View File

@ -0,0 +1,21 @@
from minyma.plugin import MinymaPlugin
class DuckDuckGoPlugin(MinymaPlugin):
"""Search DuckDuckGo"""
def __init__(self):
self.name = "duck_duck_go"
self.functions = [self.search]
def search(self, query: str):
"""Search DuckDuckGo"""
"""
TODO:
1. API Query
2. Parse (BeautifulSoup?)
3. Return top 10 (Title & Blurb?)
ENDPOINT: https://html.duckduckgo.com/html/?q=%s
"""
pass