Skip to content

Commit

Permalink
feat(core): implement plugins
Browse files Browse the repository at this point in the history
- add plugins feature using importlib
- edit .gitignore
- change enabled state of AUTOGGUF_SERVER to "enabled" from "true" for consistency
  • Loading branch information
leafspark committed Aug 23, 2024
1 parent 53ab6a6 commit d4be39a
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/*
Expand Down
13 changes: 13 additions & 0 deletions plugins/example.py
Original file line number Diff line number Diff line change
@@ -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",
}
80 changes: 80 additions & 0 deletions src/AutoGGUF.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import re
import shutil
import importlib

from functools import partial
from datetime import datetime
Expand Down Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions src/Localizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
20 changes: 19 additions & 1 deletion src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down

0 comments on commit d4be39a

Please sign in to comment.