Coverage for src / lexigram / ai / relay / di / provider.py: 96%

24 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-08 23:08 +0800

1"""RelayProvider — registers the relay registry and conversion engine.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING 

6 

7from lexigram.ai.relay.engine import RelayConverterEngine 

8from lexigram.ai.relay.registry import RelayConverterRegistry 

9from lexigram.contracts.ai.relay.protocols import ( 

10 RelayConverterProtocol, 

11 RelayRegistryProtocol, 

12) 

13from lexigram.contracts.core.health import HealthCheckResult, HealthStatus 

14from lexigram.contracts.core.provider import ProviderPriority 

15from lexigram.di.provider import Provider 

16from lexigram.logging import get_logger 

17 

18if TYPE_CHECKING: 

19 from lexigram.contracts.core.di import ( 

20 BootContainerProtocol, 

21 ContainerRegistrarProtocol, 

22 ) 

23 

24logger = get_logger(__name__) 

25 

26 

27class RelayProvider(Provider): 

28 """Registers the caller-owned relay registry and the public engine. 

29 

30 The provider binds a default :class:`RelayConverterRegistry` when the 

31 caller did not already register their own ``RelayRegistryProtocol``; 

32 it never overrides an existing caller-owned registry. The engine is 

33 registered as its class so the container constructs it against 

34 whichever registry binding wins. 

35 

36 Registers: 

37 - ``RelayRegistryProtocol`` — the default registry (only when absent) 

38 - ``RelayConverterProtocol`` — the public conversion engine 

39 

40 The provider requires no LLM API credentials and never creates an 

41 HTTP client. 

42 """ 

43 

44 name = "ai-relay" 

45 priority = ProviderPriority.DOMAIN 

46 

47 async def register(self, container: ContainerRegistrarProtocol) -> None: 

48 """Register the relay registry and engine.""" 

49 if not container.has(RelayRegistryProtocol): 

50 container.singleton( 

51 RelayRegistryProtocol, 

52 RelayConverterRegistry.with_defaults(), 

53 ) 

54 container.singleton(RelayConverterProtocol, RelayConverterEngine) 

55 logger.info("relay_provider_registered") 

56 

57 async def boot(self, container: BootContainerProtocol) -> None: 

58 """No-op boot; the engine needs no runtime wiring.""" 

59 logger.info("relay_provider_booted") 

60 

61 async def shutdown(self) -> None: 

62 """No-op shutdown.""" 

63 logger.info("relay_provider_shutdown") 

64 

65 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult: 

66 """Report the relay engine as healthy. 

67 

68 Args: 

69 timeout: Ignored; the engine performs no I/O. 

70 

71 Returns: 

72 A healthy check result. 

73 """ 

74 return HealthCheckResult( 

75 component=self.name, 

76 status=HealthStatus.HEALTHY, 

77 )