Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-observability/src/lexigram/ai/observability/module.py: 79%

24 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 07:19 +0800

1"""Observability module for dependency injection.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.contracts.observability.ai import AITracerProtocol 

8from lexigram.di.module import DynamicModule, Module, module 

9 

10 

11@module() 

12class ObservabilityModule(Module): 

13 """AI Observability module — registers ObservabilityProvider.""" 

14 

15 @classmethod 

16 def configure(cls, config: Any | None = None) -> DynamicModule: 

17 """Create an ObservabilityModule with explicit configuration.""" 

18 return DynamicModule( 

19 module=cls, 

20 providers=[cls._resolve_provider(config)], 

21 exports=[AITracerProtocol], 

22 ) 

23 

24 @classmethod 

25 def stub(cls, config: Any = None) -> DynamicModule: 

26 """Return a no-op ObservabilityModule for testing. 

27 

28 Honors *config* identically to :meth:`configure`; the provider 

29 registers no-op tracers and metrics when the features are disabled. 

30 

31 Args: 

32 config: Optional config override; dicts are coerced. 

33 

34 Returns: 

35 A DynamicModule with noop observability configuration. 

36 """ 

37 return cls.configure(config=config) 

38 

39 @staticmethod 

40 def _resolve_provider(config: Any) -> Any: 

41 """Resolve the observability provider honoring typed and dict configs. 

42 

43 Args: 

44 config: ``None``, an ``ObservabilityConfig``, or a plain dict 

45 of the same keys. 

46 

47 Returns: 

48 The configured provider instance. 

49 

50 Raises: 

51 TypeError: When *config* is neither ``None``, a dict, nor an 

52 ``ObservabilityConfig``. 

53 """ 

54 from lexigram.ai.observability.config import ObservabilityConfig 

55 from lexigram.ai.observability.di.provider import ObservabilityProvider 

56 

57 if config is None: 

58 return ObservabilityProvider() 

59 if isinstance(config, dict): 

60 return ObservabilityProvider(config=ObservabilityConfig(**config)) 

61 if isinstance(config, ObservabilityConfig): 

62 return ObservabilityProvider(config=config) 

63 raise TypeError( 

64 f"config must be ObservabilityConfig or dict, got {type(config).__name__}" 

65 ) 

66 

67 

68__all__ = ["ObservabilityModule"]