feat(core): implement plugins

- add plugins feature using importlib
- edit .gitignore
- change enabled state of AUTOGGUF_SERVER to "enabled" from "true" for consistency
This commit is contained in:
BuildTools 2024-08-22 20:08:02 -07:00
parent 53ab6a688e
commit d4be39a22c
No known key found for this signature in database
GPG Key ID: 3270C066C15D530B
5 changed files with 129 additions and 1 deletions

5
.gitignore vendored
View File

@ -26,6 +26,11 @@ src/*
docs/*
!docs/*.py
# Allow plugins folder and its .py files
!plugins/
plugins/*
!plugins/*.py
# Allow assets folder, but only .svg, .png, .rc, .css, .iss and .ico files
!assets/
assets/*

13
plugins/example.py Normal file
View File

@ -0,0 +1,13 @@
class ExamplePlugin:
def init(self, autogguf_instance):
# This gets called after the plugin is loaded
print("Plugin initialized")
def __data__(self):
return {
"name": "ExamplePlugin",
"description": "This is an example plugin.",
"compatible_versions": ["*"],
"author": "leafspark",
"version": "v1.0.0",
}

View File

@ -1,6 +1,7 @@
import json
import re
import shutil
import importlib
from functools import partial
from datetime import datetime
@ -777,8 +778,87 @@ def __init__(self, args):
# Load models
self.load_models()
# Load plugins
self.plugins = self.load_plugins()
self.apply_plugins()
self.logger.info(AUTOGGUF_INITIALIZATION_COMPLETE)
def load_plugins(self):
plugins = {}
plugin_dir = "plugins"
if not os.path.exists(plugin_dir):
self.logger.info(PLUGINS_DIR_NOT_EXIST.format(plugin_dir))
return plugins
if not os.path.isdir(plugin_dir):
self.logger.warning(PLUGINS_DIR_NOT_DIRECTORY.format(plugin_dir))
return plugins
for file in os.listdir(plugin_dir):
if file.endswith(".py") and not file.endswith(".disabled.py"):
name = file[:-3]
path = os.path.join(plugin_dir, file)
try:
spec = importlib.util.spec_from_file_location(name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for item_name in dir(module):
item = getattr(module, item_name)
if isinstance(item, type) and hasattr(item, "__data__"):
plugin_instance = item()
plugin_data = plugin_instance.__data__()
compatible_versions = plugin_data.get(
"compatible_versions", []
)
if (
"*" in compatible_versions
or AUTOGGUF_VERSION in compatible_versions
):
plugins[name] = {
"instance": plugin_instance,
"data": plugin_data,
}
self.logger.info(
PLUGIN_LOADED.format(
plugin_data["name"], plugin_data["version"]
)
)
else:
self.logger.warning(
PLUGIN_INCOMPATIBLE.format(
plugin_data["name"],
plugin_data["version"],
AUTOGGUF_VERSION,
", ".join(compatible_versions),
)
)
break
except Exception as e:
self.logger.error(PLUGIN_LOAD_FAILED.format(name, str(e)))
return plugins
def apply_plugins(self):
if not self.plugins:
self.logger.info(NO_PLUGINS_LOADED)
return
for plugin_name, plugin_info in self.plugins.items():
plugin_instance = plugin_info["instance"]
for attr_name in dir(plugin_instance):
if not attr_name.startswith("__") and attr_name != "init":
attr_value = getattr(plugin_instance, attr_name)
setattr(self, attr_name, attr_value)
if hasattr(plugin_instance, "init") and callable(plugin_instance.init):
plugin_instance.init(self)
def check_for_updates(self):
try:
response = requests.get(

View File

@ -43,6 +43,18 @@ def __init__(self):
"Found {} concatenated file parts. Please concat the files first."
)
# Plugins
self.PLUGINS_DIR_NOT_EXIST = (
"Plugins directory '{}' does not exist. No plugins will be loaded."
)
self.PLUGINS_DIR_NOT_DIRECTORY = (
"'{}' exists but is not a directory. No plugins will be loaded."
)
self.PLUGIN_LOADED = "Loaded plugin: {} {}"
self.PLUGIN_INCOMPATIBLE = "Plugin {} {} is not compatible with AutoGGUF version {}. Supported versions: {}"
self.PLUGIN_LOAD_FAILED = "Failed to load plugin {}: {}"
self.NO_PLUGINS_LOADED = "No plugins loaded."
# GPU Monitoring
self.GPU_USAGE = "GPU Usage:"
self.GPU_USAGE_FORMAT = "GPU: {:.1f}% | VRAM: {:.1f}% ({} MB / {} MB)"

View File

@ -39,8 +39,26 @@ def get_backends():
)
return jsonify({"backends": backends})
@server.route("/v1/plugins", methods=["GET"])
def get_plugins():
if window:
return jsonify(
{
"plugins": [
{
"name": plugin_data["data"]["name"],
"version": plugin_data["data"]["version"],
"description": plugin_data["data"]["description"],
"author": plugin_data["data"]["author"],
}
for plugin_data in window.plugins.values()
]
}
)
return jsonify({"plugins": []})
def run_flask():
if os.environ.get("AUTOGGUF_SERVER", "").lower() == "true":
if os.environ.get("AUTOGGUF_SERVER", "").lower() == "enabled":
server.run(
host="0.0.0.0",
port=int(os.environ.get("AUTOGGUF_SERVER_PORT", 5000)),