Protocol-Based Typed Service Access

MindRoot provides a Protocol-based system for typed service access, enabling IDE autocomplete and type checking when calling services.

Overview

MindRoot services are dynamically registered and called via service_manager. While flexible, this loses type information. The Protocols system restores type safety:

from mindroot.protocols import llm, image, tts

# Full IDE autocomplete!
stream = await llm.stream_chat('gpt-4', messages=[...], context=ctx)
result = await image.generate('a red dragon', width=1024, context=ctx)
audio = await tts.synthesize('Hello world', context=ctx)

Quick Start

Creating Your Own Typed Proxy

Alternatively, create a typed proxy explicitly:

from mindroot.protocols import LLM
from mindroot.services import service_manager

llm: LLM = service_manager.typed(LLM)
stream = await llm.stream_chat('gpt-4', messages=[...], context=ctx)

Implementing Services with Protocols

Function-Based Implementation (Legacy)

The traditional @service() decorator still works for standalone functions:

from mindroot.services import service

@service()
async def stream_chat(model, messages=[], context=None, ...):
    ...

This approach is fully backwards compatible but doesn’t provide IDE autocomplete for the function signature.

Available Protocols

Core MindRoot provides these common Protocol definitions:

LLM (Language Model)

class LLM(Protocol):
    async def stream_chat(self, model: str, messages: list = None,
                          context = None, temperature: float = 0.0,
                          max_tokens: int = 5000) -> AsyncIterator[str]: ...

    async def chat(self, model: str, messages: list,
                   context = None) -> str: ...

    async def format_image_message(self, pil_image, context = None) -> dict: ...

    async def get_service_models(self, context = None) -> dict: ...

Implemented by: ah_openai, ah_anthropic, ah_ollama, mr_deepseek, etc.

Image

class Image(Protocol):
    async def generate(self, prompt: str, width: int = 1024,
                      height: int = 1024, context = None) -> dict: ...

Implemented by: ah_flux, ah_sd, mr_imagen, etc.

TTS (Text-to-Speech)

class TTS(Protocol):
    async def synthesize(self, text: str, voice: str = 'default',
                        context = None) -> bytes: ...

    async def list_voices(self, context = None) -> list: ...

Implemented by: ah_tts, mr_eleven_stream, mr_f5_tts, etc.

STT (Speech-to-Text)

class STT(Protocol):
    async def transcribe(self, audio: bytes, context = None) -> str: ...

    async def transcribe_stream(self, audio_stream: AsyncIterator[bytes],
                               context = None) -> AsyncIterator[str]: ...

Implemented by: mr_deepgram, whisper plugins, etc.

WebSearch

class WebSearch(Protocol):
    async def search(self, query: str, num_results: int = 10,
                    context = None) -> list: ...

Plugin-Defined Protocols

Plugins can define their own Protocols without modifying core MindRoot:

Defining a Protocol

# In mr_sip/protocols.py
from typing import Protocol, runtime_checkable

@runtime_checkable
class SIP(Protocol):
    """SIP telephony service protocol."""

    async def dial_service(self, destination: str,
                          context = None) -> dict: ...

    async def end_call_service(self, context = None) -> dict: ...

    async def sip_audio_out_chunk(self, audio_chunk: bytes,
                                  context = None) -> None: ...

Implementing with service_class

# In mr_sip/mod.py
from mindroot.services import service_class
from .protocols import SIP

@service_class(SIP)
class MySIPProvider(SIP):
    async def dial_service(self, destination: str, context = None) -> dict:
        # Implementation with IDE autocomplete
        ...

Creating a Pre-instantiated Proxy

# In mr_sip/__init__.py or mr_sip/protocols.py
from mindroot.lib.providers.protocols.registry import create_lazy_proxy
from .protocols import SIP

# Create a lazy proxy for convenient access
sip: SIP = create_lazy_proxy(SIP)

Using Plugin Protocols

# Users can then import and use:
from mr_sip.protocols import sip

result = await sip.dial_service('555-1234', context=ctx)
await sip.end_call_service(context=ctx)

Protocol Registry

Plugins can optionally register their Protocols for discovery:

Registering a Protocol

from mindroot.protocols import register_protocol
from .protocols import SIP

register_protocol('sip', SIP)

Discovering Protocols

from mindroot.protocols import get_protocol, list_protocols

# List all registered protocols
protocols = list_protocols()  # {'llm': LLM, 'image': Image, ...}

# Get a specific protocol
SIP = get_protocol('sip')
if SIP:
    from mindroot.services import service_manager
    sip = service_manager.typed(SIP)

Method-to-Service Mapping

When a Protocol method name differs from the service name, use explicit mapping:

from mindroot.protocols import map_method_to_service, Image

# Map Image.generate() to the 'image' service
map_method_to_service(Image, 'generate', 'image')

Backwards Compatibility

The Protocol system is fully backwards compatible. All existing code continues to work:

# This still works exactly as before
from mindroot.services import service_manager

stream = await service_manager.stream_chat('gpt-4', messages=[...], context=ctx)
result = await service_manager.image('a red dragon', context=ctx)

# Legacy imports also work
from lib.providers.services import service_manager
from lib.providers.protocols import llm

Import Paths

MindRoot supports both short and full import paths:

# Short paths (recommended)
from mindroot.services import service, service_class, service_manager
from mindroot.protocols import LLM, Image, TTS, llm, image, tts

# Full paths (also work)
from mindroot.lib.providers.services import service, service_class, service_manager
from mindroot.lib.providers.protocols import LLM, Image, TTS, llm, image, tts

API Reference

@service_class(protocol)

Class decorator that registers all Protocol methods as services.

param protocol:

The Protocol class being implemented

param flags:

Optional flags to pass to each service registration

from mindroot.services import service_class
from mindroot.protocols import LLM

@service_class(LLM)
class MyLLM(LLM):
    async def stream_chat(self, model: str, ...) -> AsyncIterator[str]:
        ...

@service()

Function decorator for registering individual service functions.

param flags:

Optional flags for the service

from mindroot.services import service

@service()
async def my_function(arg1, context=None):
    ...

service_manager.typed(protocol)

Get a typed proxy for a service protocol.

param protocol:

A Protocol class defining the service interface

returns:

A proxy object typed as the Protocol

from mindroot.protocols import LLM
from mindroot.services import service_manager

llm: LLM = service_manager.typed(LLM)

service_manager.get_protocol(name)

Get a registered Protocol by name.

param name:

The protocol name (e.g., ‘llm’, ‘sip’)

returns:

The Protocol class, or None if not found

SIP = service_manager.get_protocol('sip')

service_manager.list_protocols()

List all registered Protocols.

returns:

Dict mapping protocol names to Protocol classes

protocols = service_manager.list_protocols()
# {'llm': LLM, 'image': Image, 'tts': TTS, ...}

create_lazy_proxy(protocol)

Create a lazy typed proxy for a Protocol. Useful for plugins.

param protocol:

The Protocol class

returns:

A LazyTypedProxy typed as the Protocol

from mindroot.lib.providers.protocols.registry import create_lazy_proxy

sip: SIP = create_lazy_proxy(SIP)

register_protocol(name, protocol_class)

Register a Protocol class for discovery.

param name:

A unique name for the protocol

param protocol_class:

The Protocol class

from mindroot.protocols import register_protocol

register_protocol('sip', SIP)

map_method_to_service(protocol, method_name, service_name)

Create an explicit mapping from a Protocol method to a service name.

param protocol:

The Protocol class

param method_name:

The method name in the Protocol

param service_name:

The actual service name to call

from mindroot.protocols import map_method_to_service, Image

map_method_to_service(Image, 'generate', 'image')