Skip to content

Commit d4be39a

Browse files
committed
feat(core): implement plugins
- add plugins feature using importlib - edit .gitignore - change enabled state of AUTOGGUF_SERVER to "enabled" from "true" for consistency
1 parent 53ab6a6 commit d4be39a

File tree

5 files changed

+129
-1
lines changed

5 files changed

+129
-1
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ src/*
2626
docs/*
2727
!docs/*.py
2828

29+
# Allow plugins folder and its .py files
30+
!plugins/
31+
plugins/*
32+
!plugins/*.py
33+
2934
# Allow assets folder, but only .svg, .png, .rc, .css, .iss and .ico files
3035
!assets/
3136
assets/*

plugins/example.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class ExamplePlugin:
2+
def init(self, autogguf_instance):
3+
# This gets called after the plugin is loaded
4+
print("Plugin initialized")
5+
6+
def __data__(self):
7+
return {
8+
"name": "ExamplePlugin",
9+
"description": "This is an example plugin.",
10+
"compatible_versions": ["*"],
11+
"author": "leafspark",
12+
"version": "v1.0.0",
13+
}

src/AutoGGUF.py

+80
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import re
33
import shutil
4+
import importlib
45

56
from functools import partial
67
from datetime import datetime
@@ -777,8 +778,87 @@ def __init__(self, args):
777778

778779
# Load models
779780
self.load_models()
781+
782+
# Load plugins
783+
self.plugins = self.load_plugins()
784+
self.apply_plugins()
785+
780786
self.logger.info(AUTOGGUF_INITIALIZATION_COMPLETE)
781787

788+
def load_plugins(self):
789+
plugins = {}
790+
plugin_dir = "plugins"
791+
792+
if not os.path.exists(plugin_dir):
793+
self.logger.info(PLUGINS_DIR_NOT_EXIST.format(plugin_dir))
794+
return plugins
795+
796+
if not os.path.isdir(plugin_dir):
797+
self.logger.warning(PLUGINS_DIR_NOT_DIRECTORY.format(plugin_dir))
798+
return plugins
799+
800+
for file in os.listdir(plugin_dir):
801+
if file.endswith(".py") and not file.endswith(".disabled.py"):
802+
name = file[:-3]
803+
path = os.path.join(plugin_dir, file)
804+
805+
try:
806+
spec = importlib.util.spec_from_file_location(name, path)
807+
module = importlib.util.module_from_spec(spec)
808+
spec.loader.exec_module(module)
809+
810+
for item_name in dir(module):
811+
item = getattr(module, item_name)
812+
if isinstance(item, type) and hasattr(item, "__data__"):
813+
plugin_instance = item()
814+
plugin_data = plugin_instance.__data__()
815+
816+
compatible_versions = plugin_data.get(
817+
"compatible_versions", []
818+
)
819+
if (
820+
"*" in compatible_versions
821+
or AUTOGGUF_VERSION in compatible_versions
822+
):
823+
plugins[name] = {
824+
"instance": plugin_instance,
825+
"data": plugin_data,
826+
}
827+
self.logger.info(
828+
PLUGIN_LOADED.format(
829+
plugin_data["name"], plugin_data["version"]
830+
)
831+
)
832+
else:
833+
self.logger.warning(
834+
PLUGIN_INCOMPATIBLE.format(
835+
plugin_data["name"],
836+
plugin_data["version"],
837+
AUTOGGUF_VERSION,
838+
", ".join(compatible_versions),
839+
)
840+
)
841+
break
842+
except Exception as e:
843+
self.logger.error(PLUGIN_LOAD_FAILED.format(name, str(e)))
844+
845+
return plugins
846+
847+
def apply_plugins(self):
848+
if not self.plugins:
849+
self.logger.info(NO_PLUGINS_LOADED)
850+
return
851+
852+
for plugin_name, plugin_info in self.plugins.items():
853+
plugin_instance = plugin_info["instance"]
854+
for attr_name in dir(plugin_instance):
855+
if not attr_name.startswith("__") and attr_name != "init":
856+
attr_value = getattr(plugin_instance, attr_name)
857+
setattr(self, attr_name, attr_value)
858+
859+
if hasattr(plugin_instance, "init") and callable(plugin_instance.init):
860+
plugin_instance.init(self)
861+
782862
def check_for_updates(self):
783863
try:
784864
response = requests.get(

src/Localizations.py

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ def __init__(self):
4343
"Found {} concatenated file parts. Please concat the files first."
4444
)
4545

46+
# Plugins
47+
self.PLUGINS_DIR_NOT_EXIST = (
48+
"Plugins directory '{}' does not exist. No plugins will be loaded."
49+
)
50+
self.PLUGINS_DIR_NOT_DIRECTORY = (
51+
"'{}' exists but is not a directory. No plugins will be loaded."
52+
)
53+
self.PLUGIN_LOADED = "Loaded plugin: {} {}"
54+
self.PLUGIN_INCOMPATIBLE = "Plugin {} {} is not compatible with AutoGGUF version {}. Supported versions: {}"
55+
self.PLUGIN_LOAD_FAILED = "Failed to load plugin {}: {}"
56+
self.NO_PLUGINS_LOADED = "No plugins loaded."
57+
4658
# GPU Monitoring
4759
self.GPU_USAGE = "GPU Usage:"
4860
self.GPU_USAGE_FORMAT = "GPU: {:.1f}% | VRAM: {:.1f}% ({} MB / {} MB)"

src/main.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,26 @@ def get_backends():
3939
)
4040
return jsonify({"backends": backends})
4141

42+
@server.route("/v1/plugins", methods=["GET"])
43+
def get_plugins():
44+
if window:
45+
return jsonify(
46+
{
47+
"plugins": [
48+
{
49+
"name": plugin_data["data"]["name"],
50+
"version": plugin_data["data"]["version"],
51+
"description": plugin_data["data"]["description"],
52+
"author": plugin_data["data"]["author"],
53+
}
54+
for plugin_data in window.plugins.values()
55+
]
56+
}
57+
)
58+
return jsonify({"plugins": []})
59+
4260
def run_flask():
43-
if os.environ.get("AUTOGGUF_SERVER", "").lower() == "true":
61+
if os.environ.get("AUTOGGUF_SERVER", "").lower() == "enabled":
4462
server.run(
4563
host="0.0.0.0",
4664
port=int(os.environ.get("AUTOGGUF_SERVER_PORT", 5000)),

0 commit comments

Comments
 (0)