From faa2a79c2bcedf2a4db364811223a88e74b8fcf3 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 2 Nov 2023 16:51:03 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20fix(getters.py):=20add=20import?= =?UTF-8?q?=20statement=20for=20PluginService=20in=20getters.py=20to=20fix?= =?UTF-8?q?=20import=20error=20=E2=9C=A8=20feat(plugins/base.py):=20add=20?= =?UTF-8?q?BasePlugin=20class=20with=20initialize,=20teardown,=20and=20get?= =?UTF-8?q?=20methods=20to=20serve=20as=20base=20class=20for=20plugins=20?= =?UTF-8?q?=E2=9C=A8=20feat(plugins/factory.py):=20add=20PluginServiceFact?= =?UTF-8?q?ory=20class=20to=20create=20PluginService=20instance=20and=20lo?= =?UTF-8?q?ad=20plugins=20=E2=9C=A8=20feat(plugins/langfuse.py):=20add=20L?= =?UTF-8?q?angfusePlugin=20class=20with=20initialize,=20teardown,=20and=20?= =?UTF-8?q?get=20methods=20to=20serve=20as=20a=20plugin=20for=20Langfuse?= =?UTF-8?q?=20=E2=9C=A8=20feat(plugins/service.py):=20add=20PluginService?= =?UTF-8?q?=20class=20to=20manage=20plugins,=20load=20plugins,=20register?= =?UTF-8?q?=20plugins,=20get=20plugins,=20and=20teardown=20plugins=20?= =?UTF-8?q?=E2=9C=A8=20feat(schema.py):=20add=20PLUGIN=5FSERVICE=20to=20Se?= =?UTF-8?q?rviceType=20enum=20to=20support=20plugin=20service=20?= =?UTF-8?q?=F0=9F=94=A7=20fix(settings/base.py):=20add=20PLUGIN=5FDIR=20op?= =?UTF-8?q?tional=20setting=20to=20support=20specifying=20plugin=20directo?= =?UTF-8?q?ry=20=F0=9F=94=A7=20fix(utils.py):=20add=20import=20statement?= =?UTF-8?q?=20for=20plugins=5Ffactory=20in=20utils.py=20to=20fix=20import?= =?UTF-8?q?=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/services/getters.py | 5 ++ src/backend/langflow/services/plugins/base.py | 12 +++++ .../langflow/services/plugins/factory.py | 16 ++++++ .../langflow/services/plugins/langfuse.py | 12 +++++ .../langflow/services/plugins/service.py | 50 +++++++++++++++++++ src/backend/langflow/services/schema.py | 1 + .../langflow/services/settings/base.py | 2 + src/backend/langflow/services/utils.py | 2 + 8 files changed, 100 insertions(+) create mode 100644 src/backend/langflow/services/plugins/base.py create mode 100644 src/backend/langflow/services/plugins/factory.py create mode 100644 src/backend/langflow/services/plugins/service.py diff --git a/src/backend/langflow/services/getters.py b/src/backend/langflow/services/getters.py index 1da9b8258..769336bbb 100644 --- a/src/backend/langflow/services/getters.py +++ b/src/backend/langflow/services/getters.py @@ -9,9 +9,14 @@ if TYPE_CHECKING: from langflow.services.session.service import SessionService from langflow.services.task.service import TaskService from langflow.services.chat.service import ChatService + from langflow.services.plugins.service import PluginService from sqlmodel import Session +def get_plugins_service() -> "PluginService": + return service_manager.get(ServiceType.PLUGIN_SERVICE) + + def get_settings_service() -> "SettingsService": try: return service_manager.get(ServiceType.SETTINGS_SERVICE) diff --git a/src/backend/langflow/services/plugins/base.py b/src/backend/langflow/services/plugins/base.py new file mode 100644 index 000000000..d5a457b66 --- /dev/null +++ b/src/backend/langflow/services/plugins/base.py @@ -0,0 +1,12 @@ +from typing import Any + + +class BasePlugin: + def initialize(self): + pass + + def teardown(self): + pass + + def get(self) -> Any: + pass diff --git a/src/backend/langflow/services/plugins/factory.py b/src/backend/langflow/services/plugins/factory.py new file mode 100644 index 000000000..2205786ed --- /dev/null +++ b/src/backend/langflow/services/plugins/factory.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING +from langflow.services.plugins.service import PluginService +from langflow.services.factory import ServiceFactory + +if TYPE_CHECKING: + from langflow.services.settings.service import SettingsService + + +class PluginServiceFactory(ServiceFactory): + def __init__(self): + super().__init__(PluginService) + + def create(self, settings_service: "SettingsService"): + service = PluginService(settings_service) + service.load_plugins() + return service diff --git a/src/backend/langflow/services/plugins/langfuse.py b/src/backend/langflow/services/plugins/langfuse.py index 7a1f60a48..08b9176a5 100644 --- a/src/backend/langflow/services/plugins/langfuse.py +++ b/src/backend/langflow/services/plugins/langfuse.py @@ -1,4 +1,5 @@ from langflow.services.getters import get_settings_service +from langflow.services.plugins.base import BasePlugin from langflow.utils.logger import logger ### Temporary implementation @@ -52,3 +53,14 @@ class LangfuseInstance: if cls._instance is not None: cls._instance.flush() cls._instance = None + + +class LangfusePlugin(BasePlugin): + def initialize(self): + LangfuseInstance.create() + + def teardown(self): + LangfuseInstance.teardown() + + def get(self): + return LangfuseInstance.get() diff --git a/src/backend/langflow/services/plugins/service.py b/src/backend/langflow/services/plugins/service.py new file mode 100644 index 000000000..187ec48b6 --- /dev/null +++ b/src/backend/langflow/services/plugins/service.py @@ -0,0 +1,50 @@ +import importlib +import inspect +import os +from langflow.services.base import Service +from langflow.services.plugins.base import BasePlugin +from typing import TYPE_CHECKING, Union + +if TYPE_CHECKING: + from langflow.services.settings.service import SettingsService + + +class PluginService(Service): + name = "plugin_service" + + def __init__(self, settings_service: "SettingsService"): + self.plugins = {} + plugin_dir = settings_service.settings.PLUGIN_DIR + self.plugin_dir = plugin_dir or os.path.dirname(__file__) + + def load_plugins(self): + base_files = ["base.py", "service.py", "factory.py", "__init__.py"] + for module in os.listdir(self.plugin_dir): + if module.endswith(".py") and module not in base_files: + plugin_name = module[:-3] + module_path = f"{self.plugin_dir}.{plugin_name}" + mod = importlib.import_module(module_path) + for attr_name in dir(mod): + attr = getattr(mod, attr_name) + if ( + inspect.isclass(attr) + and issubclass(attr, BasePlugin) + and attr is not BasePlugin + ): + self.register_plugin(plugin_name, attr()) + + def register_plugin(self, plugin_name, plugin_instance): + self.plugins[plugin_name] = plugin_instance + plugin_instance.initialize() + + def get_plugin(self, plugin_name) -> Union[BasePlugin, None]: + return self.plugins.get(plugin_name) + + def get(self, plugin_name): + if plugin := self.get_plugin(plugin_name): + return plugin.get() + return None + + def teardown(self): + for plugin in self.plugins.values(): + plugin.teardown() diff --git a/src/backend/langflow/services/schema.py b/src/backend/langflow/services/schema.py index 8b3b41fcb..8194c9125 100644 --- a/src/backend/langflow/services/schema.py +++ b/src/backend/langflow/services/schema.py @@ -14,3 +14,4 @@ class ServiceType(str, Enum): CHAT_SERVICE = "chat_service" SESSION_SERVICE = "session_service" TASK_SERVICE = "task_service" + PLUGIN_SERVICE = "plugin_service" diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 14e3f9928..779668a11 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -48,6 +48,8 @@ class Settings(BaseSettings): REDIS_DB: int = 0 REDIS_CACHE_EXPIRE: int = 3600 + PLUGIN_DIR: Optional[str] = None + LANGFUSE_SECRET_KEY: Optional[str] = None LANGFUSE_PUBLIC_KEY: Optional[str] = None LANGFUSE_HOST: Optional[str] = None diff --git a/src/backend/langflow/services/utils.py b/src/backend/langflow/services/utils.py index adbf072fe..d055a925e 100644 --- a/src/backend/langflow/services/utils.py +++ b/src/backend/langflow/services/utils.py @@ -19,6 +19,7 @@ def get_factories_and_deps(): from langflow.services.auth import factory as auth_factory from langflow.services.task import factory as task_factory from langflow.services.session import factory as session_service_factory # type: ignore + from langflow.services.plugins import factory as plugins_factory return [ (settings_factory.SettingsServiceFactory(), []), @@ -40,6 +41,7 @@ def get_factories_and_deps(): session_service_factory.SessionServiceFactory(), [ServiceType.CACHE_SERVICE], ), + (plugins_factory.PluginServiceFactory(), [ServiceType.SETTINGS_SERVICE]), ]