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

18 statements  

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

1"""LLM module for dependency injection.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING, Any 

6 

7from lexigram.contracts.ai import LLMClientProtocol 

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

9 

10if TYPE_CHECKING: 

11 from lexigram.ai.llm.config import ClientConfig 

12 from lexigram.ai.llm.routing.config import LLMConfig 

13 

14 

15@module(is_global=True) 

16class LLMModule(Module): 

17 """LLM client and model-management integration. 

18 

19 Declared global so its exports are visible to every module in the graph 

20 without explicit import — the LLM client is cross-cutting infrastructure 

21 consumed by agents, RAG, verification, and embedding pipelines. 

22 

23 Call :meth:`configure` to register an :class:`~lexigram.contracts.ai.LLMClientProtocol` 

24 implementation and optional model manager for injection. 

25 

26 Usage:: 

27 

28 from lexigram.ai.llm.config import ClientConfig 

29 

30 @module( 

31 imports=[ 

32 LLMModule.configure( 

33 ClientConfig(provider="openai", model="gpt-4o") 

34 ) 

35 ] 

36 ) 

37 class AppModule(Module): 

38 pass 

39 

40 Multi-provider routing:: 

41 

42 from lexigram.ai.llm import LLMModule 

43 

44 @module( 

45 imports=[LLMModule.configure(routing=LLMConfig())] 

46 ) 

47 class AppModule(Module): 

48 pass 

49 """ 

50 

51 @classmethod 

52 def configure( 

53 cls, 

54 config: ClientConfig | Any | None = None, 

55 *, 

56 routing: LLMConfig | Any | None = None, 

57 enable_model_manager: bool = False, 

58 enable_streaming: bool = True, 

59 audit_calls: bool = False, 

60 ) -> DynamicModule: 

61 """Create an LLMModule with a single configured provider. 

62 

63 Args: 

64 config: :class:`~lexigram.ai.llm.config.ClientConfig` or ``None`` 

65 to read configuration from environment variables. 

66 routing: Optional :class:`~lexigram.ai.llm.routing.config.LLMConfig` 

67 enabling the multi-provider routing layer instead of the 

68 single-provider client. 

69 enable_model_manager: Register :class:`~lexigram.ai.llm.model_manager.LLMModelManager` 

70 for local model lifecycle control. 

71 enable_streaming: Enable streaming response support. Defaults to 

72 ``True``; set to ``False`` to restrict to non-streaming clients only. 

73 audit_calls: Emit an ``AuditEntry`` per LLM completion via 

74 :class:`~lexigram.ai.llm.audit_bridge.LLMAuditBridge`. Requires 

75 ``AuditLoggerProtocol`` in the container. Default ``False``. 

76 

77 Returns: 

78 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

79 """ 

80 if routing is not None: 

81 from lexigram.ai.llm.di.routing_provider import LLMRoutingProvider 

82 

83 return DynamicModule( 

84 module=cls, 

85 providers=[LLMRoutingProvider(config=routing)], 

86 exports=[LLMClientProtocol], 

87 ) 

88 

89 from lexigram.ai.llm.di.provider import LLMProvider 

90 

91 return DynamicModule( 

92 module=cls, 

93 providers=[ 

94 LLMProvider( 

95 config=config, 

96 enable_model_manager=enable_model_manager, 

97 enable_streaming=enable_streaming, 

98 audit_calls=audit_calls, 

99 ) 

100 ], 

101 exports=[LLMClientProtocol], 

102 ) 

103 

104 @classmethod 

105 def stub(cls, config: ClientConfig | Any | None = None) -> DynamicModule: 

106 """Create an LLMModule suitable for unit and integration testing. 

107 

108 Uses a no-op or stub LLM client with minimal external dependencies. 

109 Streaming is disabled by default to simplify test assertions. 

110 

111 Args: 

112 config: Optional :class:`~lexigram.ai.llm.config.ClientConfig` override. 

113 Uses safe test defaults when ``None``. 

114 

115 Returns: 

116 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

117 """ 

118 from lexigram.ai.llm.di.provider import LLMProvider 

119 

120 return DynamicModule( 

121 module=cls, 

122 providers=[ 

123 LLMProvider( 

124 config=config, 

125 enable_model_manager=False, 

126 enable_streaming=False, 

127 stub_mode=True, 

128 ) 

129 ], 

130 exports=[LLMClientProtocol], 

131 ) 

132 

133 

134__all__ = ["LLMModule"]