Source code for mindroot.lib.providers.services

import os
import sys
import inspect
from typing import Type, TypeVar, get_type_hints
from . import ProviderManager

# Ensure singleton across different import paths (lib.providers vs mindroot.lib.providers)
_SINGLETON_KEY = 'mindroot.lib.providers.services._service_manager'

if _SINGLETON_KEY in dir(sys.modules.get('builtins', {})):
    # Retrieve existing singleton
    service_manager = getattr(sys.modules['builtins'], _SINGLETON_KEY)
else:
    # Create new singleton and store it
    service_manager = ProviderManager()
    setattr(sys.modules['builtins'], _SINGLETON_KEY, service_manager)

P = TypeVar('P')  # Protocol type variable

[docs] def service(*, flags=[]): def decorator(func): docstring = func.__doc__ name = func.__name__ signature = inspect.signature(func) module = inspect.getmodule(func) if module is None: raise ValueError("Cannot determine module of function") module_name = os.path.basename(os.path.dirname(module.__file__)) service_manager.register_function(name, module_name, func, signature, docstring, flags) return func return decorator
[docs] def service_class(protocol: Type[P], *, flags=[]): """Class decorator that registers all Protocol methods as services. This enables class-based service implementations with IDE autocomplete when inheriting from a Protocol. Args: protocol: The Protocol class being implemented. Only methods defined in this Protocol will be registered as services. flags: Optional flags to pass to each service registration. Example: from mindroot.services import service_class from mindroot.protocols import LLM @service_class(LLM) class DeepSeekLLM(LLM): async def stream_chat(self, model: str, messages: list = None, context = None, ...) -> AsyncIterator[str]: # IDE autocomplete works here! ... async def format_image_message(self, pil_image, context = None) -> dict: ... The decorator will: 1. Instantiate the class (must have no required __init__ args) 2. Find all methods that are defined in the Protocol 3. Register each matching method as a service """ def decorator(cls: Type) -> Type: # Get the protocol's method names (excluding dunder methods) protocol_methods = set() for name in dir(protocol): if not name.startswith('_'): attr = getattr(protocol, name, None) if callable(attr) or isinstance(inspect.getattr_static(protocol, name), (staticmethod, classmethod, property)) is False: # Check if it's actually a method in the protocol try: if callable(getattr(protocol, name)): protocol_methods.add(name) except: pass # Instantiate the class instance = cls() # Get module info from the class module = inspect.getmodule(cls) if module is None: raise ValueError(f"Cannot determine module of class {cls.__name__}") module_name = os.path.basename(os.path.dirname(module.__file__)) # Register each protocol method for method_name in protocol_methods: if hasattr(instance, method_name): method = getattr(instance, method_name) if callable(method): docstring = method.__doc__ signature = inspect.signature(method) service_manager.register_function( method_name, module_name, method, signature, docstring, flags ) return cls return decorator