"""Build a Pipeline from Settings. This is the single place that knows which concrete class each config name maps to, so adding a backend = one line here.""" from __future__ import annotations from .config import Settings from .monitor import Monitor from .pipeline import Pipeline def build(settings: Settings, monitor: Monitor | None = None) -> Pipeline: pipe = Pipeline( source=_source(settings), vision=_vision(settings), brain=_brain(settings), stt=_stt(settings), tts=_tts(settings), text_channel=_text(settings), monitor=monitor, ) if monitor is not None: monitor.set_components( { "source": settings.source or "none", "vision": settings.vision or "none", "stt": settings.stt or "none", "brain": settings.brain, "tts": settings.tts or "none", "text": settings.text or "none", } ) return pipe def _source(s: Settings): if s.source in (None, "none"): return None if s.source == "mss": from .backends.capture_mss import MSSFrameSource return MSSFrameSource(interval=s.capture_interval) from .backends.mock import MockFrameSource return MockFrameSource(interval=s.capture_interval) def _vision(s: Settings): if s.vision in (None, "none"): return None if s.vision == "claude": from .backends.claude import ClaudeVision return ClaudeVision(model=s.anthropic_model) from .backends.mock import MockVision return MockVision() def _brain(s: Settings): if s.brain == "claude": from .backends.claude import ClaudeBrain return ClaudeBrain(model=s.anthropic_model) from .backends.mock import MockBrain return MockBrain() def _stt(s: Settings): if s.stt in (None, "none"): return None from .backends.mock import MockSTT return MockSTT() def _tts(s: Settings): if s.tts in (None, "none"): return None from .backends.mock import MockTTS return MockTTS() def _text(s: Settings): if s.text in (None, "none"): return None raise NotImplementedError("discord text channel backend not implemented yet")