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
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
1"""LLM module for dependency injection."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any
7from lexigram.contracts.ai import LLMClientProtocol
8from lexigram.di.module import DynamicModule, Module, module
10if TYPE_CHECKING:
11 from lexigram.ai.llm.config import ClientConfig
12 from lexigram.ai.llm.routing.config import LLMConfig
15@module(is_global=True)
16class LLMModule(Module):
17 """LLM client and model-management integration.
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.
23 Call :meth:`configure` to register an :class:`~lexigram.contracts.ai.LLMClientProtocol`
24 implementation and optional model manager for injection.
26 Usage::
28 from lexigram.ai.llm.config import ClientConfig
30 @module(
31 imports=[
32 LLMModule.configure(
33 ClientConfig(provider="openai", model="gpt-4o")
34 )
35 ]
36 )
37 class AppModule(Module):
38 pass
40 Multi-provider routing::
42 from lexigram.ai.llm import LLMModule
44 @module(
45 imports=[LLMModule.configure(routing=LLMConfig())]
46 )
47 class AppModule(Module):
48 pass
49 """
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.
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``.
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
83 return DynamicModule(
84 module=cls,
85 providers=[LLMRoutingProvider(config=routing)],
86 exports=[LLMClientProtocol],
87 )
89 from lexigram.ai.llm.di.provider import LLMProvider
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 )
104 @classmethod
105 def stub(cls, config: ClientConfig | Any | None = None) -> DynamicModule:
106 """Create an LLMModule suitable for unit and integration testing.
108 Uses a no-op or stub LLM client with minimal external dependencies.
109 Streaming is disabled by default to simplify test assertions.
111 Args:
112 config: Optional :class:`~lexigram.ai.llm.config.ClientConfig` override.
113 Uses safe test defaults when ``None``.
115 Returns:
116 A :class:`~lexigram.di.module.DynamicModule` descriptor.
117 """
118 from lexigram.ai.llm.di.provider import LLMProvider
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 )
134__all__ = ["LLMModule"]