Refactor ServiceFactory and Dependencies (#1560)

* Update dependencies for OpenTelemetry

* Update service dependency logic and add first version of telemetry service

* Remove telemetry service and related code
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-03-25 10:00:39 -03:00 • committed by GitHub
commit e8630dac7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 246 additions and 133 deletions

36
poetry.lock generated
View file

@ -5262,6 +5262,21 @@ files = [
deprecated = ">=1.2.6" deprecated = ">=1.2.6"
importlib-metadata = ">=6.0,<7.0" importlib-metadata = ">=6.0,<7.0"
[[package]]
name = "opentelemetry-exporter-otlp"
version = "1.23.0"
description = "OpenTelemetry Collector Exporters"
optional = false
python-versions = ">=3.8"
files = [
{file = "opentelemetry_exporter_otlp-1.23.0-py3-none-any.whl", hash = "sha256:92371fdc8d7803465a45801fe30cd8c522ef355a385b0a1d5346d32f77511ea2"},
{file = "opentelemetry_exporter_otlp-1.23.0.tar.gz", hash = "sha256:4af8798f9bc3bddb92fcbb5b4aa9d0e955d962aa1d9bceaab08891c355a9f907"},
]
[package.dependencies]
opentelemetry-exporter-otlp-proto-grpc = "1.23.0"
opentelemetry-exporter-otlp-proto-http = "1.23.0"
[[package]] [[package]]
name = "opentelemetry-exporter-otlp-proto-common" name = "opentelemetry-exporter-otlp-proto-common"
version = "1.23.0" version = "1.23.0"
@ -5382,6 +5397,27 @@ opentelemetry-util-http = "0.44b0"
instruments = ["fastapi (>=0.58,<1.0)"] instruments = ["fastapi (>=0.58,<1.0)"]
test = ["httpx (>=0.22,<1.0)", "opentelemetry-instrumentation-fastapi[instruments]", "opentelemetry-test-utils (==0.44b0)", "requests (>=2.23,<3.0)"] test = ["httpx (>=0.22,<1.0)", "opentelemetry-instrumentation-fastapi[instruments]", "opentelemetry-test-utils (==0.44b0)", "requests (>=2.23,<3.0)"]
[[package]]
name = "opentelemetry-instrumentation-httpx"
version = "0.44b0"
description = "OpenTelemetry HTTPX Instrumentation"
optional = false
python-versions = ">=3.8"
files = [
{file = "opentelemetry_instrumentation_httpx-0.44b0-py3-none-any.whl", hash = "sha256:a4f1121b6212b018e719ef6a9a2f8317c329edd01a61452b7250f574f7d95a91"},
{file = "opentelemetry_instrumentation_httpx-0.44b0.tar.gz", hash = "sha256:6cc81c4182f54dfb0d15774e3e48bb90d3ed44e9ad8bf5eef2a64a7197f945d8"},
]
[package.dependencies]
opentelemetry-api = ">=1.12,<2.0"
opentelemetry-instrumentation = "0.44b0"
opentelemetry-semantic-conventions = "0.44b0"
opentelemetry-util-http = "0.44b0"
[package.extras]
instruments = ["httpx (>=0.18.0)"]
test = ["opentelemetry-instrumentation-httpx[instruments]", "opentelemetry-sdk (>=1.12,<2.0)", "opentelemetry-test-utils (==0.44b0)"]
[[package]] [[package]]
name = "opentelemetry-proto" name = "opentelemetry-proto"
version = "1.23.0" version = "1.23.0"

View file

@ -105,6 +105,12 @@ python-socketio = "^5.11.0"
llama-index = "^0.10.13" llama-index = "^0.10.13"
langchain-openai = "^0.0.5" langchain-openai = "^0.0.5"
unstructured = { extras = ["md"], version = "^0.12.4" } unstructured = { extras = ["md"], version = "^0.12.4" }
opentelemetry-api = "^1.23.0"
opentelemetry-sdk = "^1.23.0"
opentelemetry-exporter-otlp = "^1.23.0"
opentelemetry-instrumentation-fastapi = "^0.44b0"
opentelemetry-instrumentation-httpx = "^0.44b0"
opentelemetry-instrumentation-asgi = "^0.44b0"
dspy-ai = "^2.4.0" dspy-ai = "^2.4.0"
crewai = "^0.22.5" crewai = "^0.22.5"
langchain-anthropic = "^0.1.4" langchain-anthropic = "^0.1.4"

View file

@ -20,7 +20,9 @@ from langflow.utils.logger import configure
def get_lifespan(fix_migration=False, socketio_server=None): def get_lifespan(fix_migration=False, socketio_server=None):
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
initialize_services(fix_migration=fix_migration, socketio_server=socketio_server) initialize_services(
fix_migration=fix_migration, socketio_server=socketio_server
)
setup_llm_caching() setup_llm_caching()
LangfuseInstance.update() LangfuseInstance.update()
create_or_update_starter_projects() create_or_update_starter_projects()
@ -34,7 +36,9 @@ def create_app():
"""Create the FastAPI app and include the router.""" """Create the FastAPI app and include the router."""
configure() configure()
socketio_server = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*", logger=True) socketio_server = socketio.AsyncServer(
async_mode="asgi", cors_allowed_origins="*", logger=True
)
lifespan = get_lifespan(socketio_server=socketio_server) lifespan = get_lifespan(socketio_server=socketio_server)
app = FastAPI(lifespan=lifespan) app = FastAPI(lifespan=lifespan)
origins = ["*"] origins = ["*"]
@ -101,7 +105,9 @@ def get_static_files_dir():
return frontend_path / "frontend" return frontend_path / "frontend"
def setup_app(static_files_dir: Optional[Path] = None, backend_only: bool = False) -> FastAPI: def setup_app(
static_files_dir: Optional[Path] = None, backend_only: bool = False
) -> FastAPI:
"""Setup the FastAPI app.""" """Setup the FastAPI app."""
# get the directory of the current file # get the directory of the current file
if not static_files_dir: if not static_files_dir:

View file

@ -6,7 +6,7 @@ from typing import Optional
from langflow.services.base import Service from langflow.services.base import Service
class BaseCacheService(Service): class CacheService(Service):
""" """
Abstract base class for a cache. Abstract base class for a cache.
""" """

View file

@ -15,7 +15,7 @@ if TYPE_CHECKING:
class CacheServiceFactory(ServiceFactory): class CacheServiceFactory(ServiceFactory):
def __init__(self): def __init__(self):
super().__init__(BaseCacheService) super().__init__(CacheService)
def create(self, settings_service: "SettingsService"): def create(self, settings_service: "SettingsService"):
# Here you would have logic to create and configure a CacheService # Here you would have logic to create and configure a CacheService

View file

@ -68,7 +68,10 @@ class ThreadingInMemoryCache(BaseCacheService, Service):
Retrieve an item from the cache without acquiring the lock. Retrieve an item from the cache without acquiring the lock.
""" """
if item := self._cache.get(key): if item := self._cache.get(key):
if self.expiration_time is None or time.time() - item["time"] < self.expiration_time: if (
self.expiration_time is None
or time.time() - item["time"] < self.expiration_time
):
# Move the key to the end to make it recently used # Move the key to the end to make it recently used
self._cache.move_to_end(key) self._cache.move_to_end(key)
# Check if the value is pickled # Check if the value is pickled
@ -113,7 +116,11 @@ class ThreadingInMemoryCache(BaseCacheService, Service):
""" """
with lock or self._lock: with lock or self._lock:
existing_value = self._get_without_lock(key) existing_value = self._get_without_lock(key)
if existing_value is not None and isinstance(existing_value, dict) and isinstance(value, dict): if (
existing_value is not None
and isinstance(existing_value, dict)
and isinstance(value, dict)
):
existing_value.update(value) existing_value.update(value)
value = existing_value value = existing_value
@ -179,7 +186,7 @@ class ThreadingInMemoryCache(BaseCacheService, Service):
return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})" return f"InMemoryCache(max_size={self.max_size}, expiration_time={self.expiration_time})"
class RedisCache(BaseCacheService, Service): class RedisCache(CacheService):
""" """
A Redis-based cache implementation. A Redis-based cache implementation.
@ -202,7 +209,9 @@ class RedisCache(BaseCacheService, Service):
b = cache["b"] b = cache["b"]
""" """
def __init__(self, host="localhost", port=6379, db=0, url=None, expiration_time=60 * 60): def __init__(
self, host="localhost", port=6379, db=0, url=None, expiration_time=60 * 60
):
""" """
Initialize a new RedisCache instance. Initialize a new RedisCache instance.
@ -270,7 +279,9 @@ class RedisCache(BaseCacheService, Service):
if not result: if not result:
raise ValueError("RedisCache could not set the value.") raise ValueError("RedisCache could not set the value.")
except TypeError as exc: except TypeError as exc:
raise TypeError("RedisCache only accepts values that can be pickled. ") from exc raise TypeError(
"RedisCache only accepts values that can be pickled. "
) from exc
def upsert(self, key, value): def upsert(self, key, value):
""" """
@ -282,7 +293,11 @@ class RedisCache(BaseCacheService, Service):
value: The value to insert or update. value: The value to insert or update.
""" """
existing_value = self.get(key) existing_value = self.get(key)
if existing_value is not None and isinstance(existing_value, dict) and isinstance(value, dict): if (
existing_value is not None
and isinstance(existing_value, dict)
and isinstance(value, dict)
):
existing_value.update(value) existing_value.update(value)
value = existing_value value = existing_value

View file

@ -4,7 +4,9 @@ from typing import TYPE_CHECKING, Generator
from langflow.services import ServiceType, service_manager from langflow.services import ServiceType, service_manager
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.service import BaseCacheService from sqlmodel import Session
from langflow.services.cache.service import CacheService
from langflow.services.chat.service import ChatService from langflow.services.chat.service import ChatService
from langflow.services.credentials.service import CredentialService from langflow.services.credentials.service import CredentialService
from langflow.services.database.service import DatabaseService from langflow.services.database.service import DatabaseService
@ -16,11 +18,10 @@ if TYPE_CHECKING:
from langflow.services.storage.service import StorageService from langflow.services.storage.service import StorageService
from langflow.services.store.service import StoreService from langflow.services.store.service import StoreService
from langflow.services.task.service import TaskService from langflow.services.task.service import TaskService
from sqlmodel import Session
def get_socket_service() -> "SocketIOService": def get_socket_service() -> "SocketIOService":
return service_manager.get(ServiceType.SOCKET_IO_SERVICE) # type: ignore return service_manager.get(ServiceType.SOCKETIO_SERVICE) # type: ignore
def get_storage_service() -> "StorageService": def get_storage_service() -> "StorageService":

View file

@ -1,12 +1,91 @@
from typing import TYPE_CHECKING import importlib
import inspect
from typing import TYPE_CHECKING, Type, get_type_hints
from cachetools import LRUCache, cached
from loguru import logger
from langflow.services.schema import ServiceType
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.base import Service from langflow.services.base import Service
class ServiceFactory: class ServiceFactory:
def __init__(self, service_class): def __init__(
self,
service_class,
):
self.service_class = service_class self.service_class = service_class
self.dependencies = infer_service_types(self, import_all_services_into_a_dict())
def create(self, *args, **kwargs) -> "Service": def create(self, *args, **kwargs) -> "Service":
raise NotImplementedError raise self.service_class(*args, **kwargs)
def hash_factory(factory: ServiceFactory) -> str:
return factory.service_class.__name__
def hash_dict(d: dict) -> str:
return str(d)
def hash_infer_service_types_args(
factory_class: Type[ServiceFactory], available_services=None
) -> str:
factory_hash = hash_factory(factory_class)
services_hash = hash_dict(available_services)
return f"{factory_hash}_{services_hash}"
@cached(cache=LRUCache(maxsize=10), key=hash_infer_service_types_args)
def infer_service_types(
factory_class: Type[ServiceFactory], available_services=None
) -> "ServiceType":
create_method = factory_class.create
type_hints = get_type_hints(create_method, globalns=available_services)
service_types = []
for param_name, param_type in type_hints.items():
# Skip the return type if it's included in type hints
if param_name == "return":
continue
# Convert the type to the expected enum format directly without appending "_SERVICE"
type_name = param_type.__name__.upper().replace("SERVICE", "_SERVICE")
try:
# Attempt to find a matching enum value
service_type = ServiceType[type_name]
service_types.append(service_type)
except KeyError:
raise ValueError(
f"No matching ServiceType for parameter type: {param_type.__name__}"
)
return service_types
@cached(cache=LRUCache(maxsize=1))
def import_all_services_into_a_dict():
# Services are all in langflow.services.{service_name}.service
# and are subclass of Service
# We want to import all of them and put them in a dict
# to use as globals
from langflow.services.base import Service
services = {}
for service_type in ServiceType:
try:
service_name = ServiceType(service_type).value.replace("_service", "")
module_name = f"langflow.services.{service_name}.service"
module = importlib.import_module(module_name)
for name, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, Service) and obj is not Service:
services[name] = obj
break
except Exception as exc:
logger.exception(exc)
raise RuntimeError(
f"Could not initialize services. Please check your settings."
) from exc
return services

View file

@ -1,11 +1,11 @@
from typing import TYPE_CHECKING, Dict, List, Optional from typing import TYPE_CHECKING, Dict
from langflow.services.schema import ServiceType
from loguru import logger from loguru import logger
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.schema import ServiceType
class ServiceManager: class ServiceManager:
@ -16,23 +16,19 @@ class ServiceManager:
def __init__(self): def __init__(self):
self.services: Dict[str, "Service"] = {} self.services: Dict[str, "Service"] = {}
self.factories = {} self.factories = {}
self.dependencies = {}
def register_factory( def register_factory(
self, self,
service_factory: "ServiceFactory", service_factory: "ServiceFactory",
dependencies: Optional[List[ServiceType]] = None,
): ):
""" """
Registers a new factory with dependencies. Registers a new factory with dependencies.
""" """
if dependencies is None:
dependencies = []
service_name = service_factory.service_class.name service_name = service_factory.service_class.name
self.factories[service_name] = service_factory self.factories[service_name] = service_factory
self.dependencies[service_name] = dependencies
def get(self, service_name: ServiceType) -> "Service": def get(self, service_name: "ServiceType") -> "Service":
""" """
Get (or create) a service by its name. Get (or create) a service by its name.
""" """
@ -41,7 +37,7 @@ class ServiceManager:
return self.services[service_name] return self.services[service_name]
def _create_service(self, service_name: ServiceType): def _create_service(self, service_name: "ServiceType"):
""" """
Create a new service given its name, handling dependencies. Create a new service given its name, handling dependencies.
""" """
@ -49,25 +45,32 @@ class ServiceManager:
self._validate_service_creation(service_name) self._validate_service_creation(service_name)
# Create dependencies first # Create dependencies first
for dependency in self.dependencies.get(service_name, []): factory = self.factories.get(service_name)
for dependency in factory.dependencies:
if dependency not in self.services: if dependency not in self.services:
self._create_service(dependency) self._create_service(dependency)
# Collect the dependent services # Collect the dependent services
dependent_services = {dep.value: self.services[dep] for dep in self.dependencies.get(service_name, [])} dependent_services = {
dep.value: self.services[dep] for dep in factory.dependencies
}
# Create the actual service # Create the actual service
self.services[service_name] = self.factories[service_name].create(**dependent_services) self.services[service_name] = self.factories[service_name].create(
**dependent_services
)
self.services[service_name].set_ready() self.services[service_name].set_ready()
def _validate_service_creation(self, service_name: ServiceType): def _validate_service_creation(self, service_name: "ServiceType"):
""" """
Validate whether the service can be created. Validate whether the service can be created.
""" """
if service_name not in self.factories: if service_name not in self.factories:
raise ValueError(f"No factory registered for the service class '{service_name.name}'") raise ValueError(
f"No factory registered for the service class '{service_name.name}'"
)
def update(self, service_name: ServiceType): def update(self, service_name: "ServiceType"):
""" """
Update a service by its name. Update a service by its name.
""" """
@ -90,36 +93,11 @@ class ServiceManager:
logger.exception(exc) logger.exception(exc)
self.services = {} self.services = {}
self.factories = {} self.factories = {}
self.dependencies = {}
service_manager = ServiceManager() service_manager = ServiceManager()
def reinitialize_services():
"""
Reinitialize all the services needed.
"""
service_manager.update(ServiceType.SETTINGS_SERVICE)
service_manager.update(ServiceType.DATABASE_SERVICE)
service_manager.update(ServiceType.CACHE_SERVICE)
service_manager.update(ServiceType.CHAT_SERVICE)
service_manager.update(ServiceType.SESSION_SERVICE)
service_manager.update(ServiceType.AUTH_SERVICE)
service_manager.update(ServiceType.TASK_SERVICE)
# Test cache connection
service_manager.get(ServiceType.CACHE_SERVICE)
# Test database connection
service_manager.get(ServiceType.DATABASE_SERVICE)
# Test cache connection
service_manager.get(ServiceType.CACHE_SERVICE)
# Test database connection
service_manager.get(ServiceType.DATABASE_SERVICE)
def initialize_settings_service(): def initialize_settings_service():
""" """
Initialize the settings manager. Initialize the settings manager.
@ -134,13 +112,12 @@ def initialize_session_service():
Initialize the session manager. Initialize the session manager.
""" """
from langflow.services.cache import factory as cache_factory from langflow.services.cache import factory as cache_factory
from langflow.services.session import factory as session_service_factory # type: ignore from langflow.services.session import (
factory as session_service_factory,
) # type: ignore
initialize_settings_service() initialize_settings_service()
service_manager.register_factory(cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_SERVICE]) service_manager.register_factory(cache_factory.CacheServiceFactory())
service_manager.register_factory( service_manager.register_factory(session_service_factory.SessionServiceFactory())
session_service_factory.SessionServiceFactory(),
dependencies=[ServiceType.CACHE_SERVICE],
)

View file

@ -1,5 +1,6 @@
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.monitor.service import MonitorService from langflow.services.monitor.service import MonitorService
from langflow.services.schema import ServiceType
class MonitorServiceFactory(ServiceFactory): class MonitorServiceFactory(ServiceFactory):

View file

@ -14,9 +14,9 @@ class ServiceType(str, Enum):
CHAT_SERVICE = "chat_service" CHAT_SERVICE = "chat_service"
SESSION_SERVICE = "session_service" SESSION_SERVICE = "session_service"
TASK_SERVICE = "task_service" TASK_SERVICE = "task_service"
PLUGIN_SERVICE = "plugin_service" PLUGINS_SERVICE = "plugins_service"
STORE_SERVICE = "store_service" STORE_SERVICE = "store_service"
CREDENTIAL_SERVICE = "credential_service" CREDENTIALS_SERVICE = "credentials_service"
STORAGE_SERVICE = "storage_service" STORAGE_SERVICE = "storage_service"
MONITOR_SERVICE = "monitor_service" MONITOR_SERVICE = "monitor_service"
SOCKET_IO_SERVICE = "socket_io_service" SOCKETIO_SERVICE = "socket_service"

View file

@ -1,14 +1,15 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.services.session.service import SessionService
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.session.service import SessionService
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.service import BaseCacheService from langflow.services.cache.service import CacheService
class SessionServiceFactory(ServiceFactory): class SessionServiceFactory(ServiceFactory):
def __init__(self): def __init__(self):
super().__init__(SessionService) super().__init__(SessionService)
def create(self, cache_service: "BaseCacheService"): def create(self, cache_service: "CacheService"):
return SessionService(cache_service) return SessionService(cache_service)

View file

@ -5,14 +5,14 @@ from langflow.services.base import Service
from langflow.services.session.utils import compute_dict_hash, session_id_generator from langflow.services.session.utils import compute_dict_hash, session_id_generator
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.base import BaseCacheService from langflow.services.cache.base import CacheService
class SessionService(Service): class SessionService(Service):
name = "session_service" name = "session_service"
def __init__(self, cache_service): def __init__(self, cache_service):
self.cache_service: "BaseCacheService" = cache_service self.cache_service: "CacheService" = cache_service
async def load_session(self, key, flow_id: str, data_graph: Optional[dict] = None): async def load_session(self, key, flow_id: str, data_graph: Optional[dict] = None):
# Check if the data is cached # Check if the data is cached

View file

@ -1,15 +1,18 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.schema import ServiceType
from langflow.services.socket.service import SocketIOService from langflow.services.socket.service import SocketIOService
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.service import BaseCacheService from langflow.services.cache.service import CacheService
class SocketIOFactory(ServiceFactory): class SocketIOFactory(ServiceFactory):
def __init__(self): def __init__(self):
super().__init__(service_class=SocketIOService) super().__init__(
service_class=SocketIOService,
)
def create(self, cache_service: "BaseCacheService"): def create(self, cache_service: "CacheService"):
return SocketIOService(cache_service) return SocketIOService(cache_service)

View file

@ -8,13 +8,13 @@ from langflow.services.deps import get_chat_service
from langflow.services.socket.utils import build_vertex, get_vertices from langflow.services.socket.utils import build_vertex, get_vertices
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.cache.service import BaseCacheService from langflow.services.cache.service import CacheService
class SocketIOService(Service): class SocketIOService(Service):
name = "socket_io_service" name = "socket_service"
def __init__(self, cache_service: "BaseCacheService"): def __init__(self, cache_service: "CacheService"):
self.cache_service = cache_service self.cache_service = cache_service
def init(self, sio: socketio.AsyncServer): def init(self, sio: socketio.AsyncServer):

View file

@ -1,6 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
from langflow.services.schema import ServiceType
from langflow.services.storage.service import StorageService from langflow.services.storage.service import StorageService
from loguru import logger from loguru import logger
@ -11,9 +12,13 @@ if TYPE_CHECKING:
class StorageServiceFactory(ServiceFactory): class StorageServiceFactory(ServiceFactory):
def __init__(self): def __init__(self):
super().__init__(StorageService) super().__init__(
StorageService,
)
def create(self, session_service: "SessionService", settings_service: "SettingsService"): def create(
self, session_service: "SessionService", settings_service: "SettingsService"
):
storage_type = settings_service.settings.STORAGE_TYPE storage_type = settings_service.settings.STORAGE_TYPE
if storage_type.lower() == "local": if storage_type.lower() == "local":
from .local import LocalStorageService from .local import LocalStorageService
@ -24,7 +29,9 @@ class StorageServiceFactory(ServiceFactory):
return S3StorageService(session_service, settings_service) return S3StorageService(session_service, settings_service)
else: else:
logger.warning(f"Storage type {storage_type} not supported. Using local storage.") logger.warning(
f"Storage type {storage_type} not supported. Using local storage."
)
from .local import LocalStorageService from .local import LocalStorageService
return LocalStorageService(session_service, settings_service) return LocalStorageService(session_service, settings_service)

View file

@ -1,8 +1,12 @@
import importlib
import inspect
from loguru import logger from loguru import logger
from sqlmodel import Session, select from sqlmodel import Session, select
from langflow.services.auth.utils import create_super_user, verify_password from langflow.services.auth.utils import create_super_user, verify_password
from langflow.services.database.utils import initialize_database from langflow.services.database.utils import initialize_database
from langflow.services.factory import ServiceFactory
from langflow.services.manager import service_manager from langflow.services.manager import service_manager
from langflow.services.schema import ServiceType from langflow.services.schema import ServiceType
from langflow.services.settings.constants import ( from langflow.services.settings.constants import (
@ -14,56 +18,32 @@ from langflow.services.socket.utils import set_socketio_server
from .deps import get_db_service, get_session, get_settings_service from .deps import get_db_service, get_session, get_settings_service
def get_factories_and_deps(): def get_factories():
from langflow.services.auth import factory as auth_factory service_names = [
from langflow.services.cache import factory as cache_factory ServiceType(service_type).value.replace("_service", "")
from langflow.services.chat import factory as chat_factory for service_type in ServiceType
from langflow.services.credentials import factory as credentials_factory
from langflow.services.database import factory as database_factory
from langflow.services.monitor import factory as monitor_factory
from langflow.services.plugins import factory as plugins_factory
from langflow.services.session import (
factory as session_service_factory,
) # type: ignore
from langflow.services.settings import factory as settings_factory
from langflow.services.socket import factory as socket_factory
from langflow.services.storage import factory as storage_factory
from langflow.services.store import factory as store_factory
from langflow.services.task import factory as task_factory
return [
(settings_factory.SettingsServiceFactory(), []),
(
auth_factory.AuthServiceFactory(),
[ServiceType.SETTINGS_SERVICE],
),
(
database_factory.DatabaseServiceFactory(),
[ServiceType.SETTINGS_SERVICE],
),
(
cache_factory.CacheServiceFactory(),
[ServiceType.SETTINGS_SERVICE],
),
(chat_factory.ChatServiceFactory(), []),
(task_factory.TaskServiceFactory(), []),
(
session_service_factory.SessionServiceFactory(),
[ServiceType.CACHE_SERVICE],
),
(plugins_factory.PluginServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
(store_factory.StoreServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
(
credentials_factory.CredentialServiceFactory(),
[ServiceType.SETTINGS_SERVICE],
),
(
storage_factory.StorageServiceFactory(),
[ServiceType.SESSION_SERVICE, ServiceType.SETTINGS_SERVICE],
),
(monitor_factory.MonitorServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
(socket_factory.SocketIOFactory(), [ServiceType.CACHE_SERVICE]),
] ]
base_module = "langflow.services"
factories = []
for name in service_names:
try:
module_name = f"{base_module}.{name}.factory"
module = importlib.import_module(module_name)
# Find all classes in the module that are subclasses of ServiceFactory
for name, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, ServiceFactory) and obj is not ServiceFactory:
factories.append(obj())
break
except Exception as exc:
logger.exception(exc)
raise RuntimeError(
f"Could not initialize services. Please check your settings."
) from exc
return factories
def get_or_create_super_user(session: Session, username, password, is_default): def get_or_create_super_user(session: Session, username, password, is_default):
@ -198,11 +178,12 @@ def initialize_session_service():
initialize_settings_service() initialize_settings_service()
service_manager.register_factory(cache_factory.CacheServiceFactory(), dependencies=[ServiceType.SETTINGS_SERVICE]) service_manager.register_factory(
cache_factory.CacheServiceFactory(),
)
service_manager.register_factory( service_manager.register_factory(
session_service_factory.SessionServiceFactory(), session_service_factory.SessionServiceFactory(),
dependencies=[ServiceType.CACHE_SERVICE],
) )
@ -210,9 +191,9 @@ def initialize_services(fix_migration: bool = False, socketio_server=None):
""" """
Initialize all the services needed. Initialize all the services needed.
""" """
for factory, dependencies in get_factories_and_deps(): for factory in get_factories():
try: try:
service_manager.register_factory(factory, dependencies=dependencies) service_manager.register_factory(factory)
except Exception as exc: except Exception as exc:
logger.exception(exc) logger.exception(exc)
raise RuntimeError("Could not initialize services. Please check your settings.") from exc raise RuntimeError("Could not initialize services. Please check your settings.") from exc