Coverage for src/lexigram/ai/module.py: 100%

23 statements  

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

1"""AI orchestration module for dependency injection.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.contracts.ai import AIProviderProtocol 

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

9 

10 

11@module() 

12class AIModule(Module): 

13 """Core AI layer: LLM orchestration, RAG pipelines, and governance. 

14 

15 Call :meth:`configure` to configure the AI subsystem. Sub-modules such as 

16 :class:`~lexigram.ai.llm.LLMModule` and 

17 :class:`~lexigram.ai.rag.RAGModule` may be imported independently 

18 for more granular control. 

19 

20 Usage:: 

21 

22 from lexigram.ai.config import AIConfig 

23 from lexigram.ai.llm.config import ClientConfig 

24 

25 @module( 

26 imports=[ 

27 AIModule.configure( 

28 AIConfig(llm=ClientConfig(provider="openai", model="gpt-4o")) 

29 ) 

30 ] 

31 ) 

32 class AppModule(Module): 

33 pass 

34 """ 

35 

36 @classmethod 

37 def configure(cls, config: Any | None = None, **kwargs: Any) -> DynamicModule: 

38 """Create an AIModule with explicit configuration. 

39 

40 Args: 

41 config: :class:`~lexigram.ai.config.AIConfig` or ``None`` 

42 for framework defaults. 

43 **kwargs: Additional keyword arguments forwarded to 

44 :class:`~lexigram.ai.di.provider.AIProvider`. 

45 

46 Returns: 

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

48 """ 

49 from lexigram.ai.di.provider import AIProvider 

50 

51 return DynamicModule( 

52 module=cls, 

53 providers=[AIProvider(config=config, **kwargs)], 

54 exports=[AIProviderProtocol], 

55 ) 

56 

57 @classmethod 

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

59 """Return a no-op AIModule for testing. 

60 

61 Registers all AI sub-modules with their testing stubs: agents, 

62 LLM, RAG, memory, prompts, skills, sessions, feedback, governance, 

63 and guards with minimal side effects. 

64 

65 Returns: 

66 A DynamicModule with all AI sub-modules in testing configuration. 

67 """ 

68 from importlib.metadata import entry_points 

69 

70 from lexigram.di.module import DynamicModule 

71 

72 stub_imports: list[Any] = [] 

73 for ep in entry_points(group="lexigram.ai.modules"): 

74 try: 

75 module_cls = ep.load() 

76 stub_imports.append(module_cls.stub()) 

77 except (AttributeError, ImportError, RuntimeError): 

78 pass 

79 

80 return DynamicModule( 

81 module=cls, 

82 imports=stub_imports, 

83 exports=[AIProviderProtocol], 

84 ) 

85 

86 

87__all__ = ["AIModule"]