Coverage for src / lexigram / contracts / ai / rag.py: 0%

52 statements  

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

1"""RAG pipeline, retrieval, and reranking protocols.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass 

6from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable 

7 

8from lexigram.contracts.ai.exceptions import RAGError 

9 

10if TYPE_CHECKING: 

11 from lexigram.contracts.ai.vector import DocumentProtocol, SearchResultProtocol 

12 from lexigram.contracts.core.result import Result 

13 

14 

15# RAG Errors 

16class RetrievalError(RAGError): 

17 """Error raised during document retrieval.""" 

18 

19 _code = "LEX_ERR_RAG_002" 

20 

21 

22class SynthesisError(RAGError): 

23 """Error raised during response synthesis.""" 

24 

25 _code = "LEX_ERR_RAG_003" 

26 

27 

28class ChunkingError(RAGError): 

29 """Error raised during document chunking.""" 

30 

31 _code = "LEX_ERR_RAG_004" 

32 

33 

34@runtime_checkable 

35class ChunkProtocol(Protocol): 

36 """Structural protocol for document chunks.""" 

37 

38 @property 

39 def text(self) -> str: 

40 """Chunk text content.""" 

41 ... 

42 

43 @property 

44 def metadata(self) -> dict[str, Any]: 

45 """Chunk metadata.""" 

46 ... 

47 

48 @property 

49 def score(self) -> float | None: 

50 """Optional relevance score.""" 

51 ... 

52 

53 

54@dataclass(frozen=True) 

55class RAGContext: 

56 """Inputs to a RAG pipeline.""" 

57 

58 query: str 

59 config: dict[str, Any] | None = None 

60 filters: dict[str, Any] | None = None 

61 session_id: str | None = None 

62 

63 

64@dataclass(frozen=True) 

65class RAGResponse: 

66 """Outputs from a RAG pipeline.""" 

67 

68 answer: str 

69 sources: list[SearchResultProtocol] 

70 citations: list[Any] | None = None 

71 confidence: float | None = None 

72 

73 

74@runtime_checkable 

75class DocumentLoaderProtocol(Protocol): 

76 """Protocol for loading documents.""" 

77 

78 async def load(self, source: str, **kwargs: Any) -> list[DocumentProtocol]: 

79 """Load documents from a source.""" 

80 ... 

81 

82 

83@runtime_checkable 

84class SynthesizerProtocol(Protocol): 

85 """Protocol for synthesizing a final answer from context.""" 

86 

87 async def synthesize( 

88 self, 

89 query: str, 

90 context: list[SearchResultProtocol], 

91 **kwargs: Any, 

92 ) -> Result[RAGResponse, RAGError]: 

93 """Synthesize retrieved documents into an answer.""" 

94 ... 

95 

96 

97@runtime_checkable 

98class RAGPipelineProtocol(Protocol): 

99 """Protocol for RAG pipeline execution. 

100 

101 Orchestrates retrieval, synthesis, and quality stages for a 

102 given query context. 

103 """ 

104 

105 async def execute(self, context: RAGContext) -> Result[RAGResponse, RAGError]: 

106 """Execute the full RAG pipeline for the given context. 

107 

108 Args: 

109 context: Pipeline context containing query and config. 

110 

111 Returns: 

112 Updated context with retrieved and synthesised response. 

113 """ 

114 ... 

115 

116 

117@runtime_checkable 

118class RetrievalStrategyProtocol(Protocol): 

119 """Protocol for pluggable RAG retrieval and ranking strategies. 

120 

121 Implementations take a query and a set of candidate documents and 

122 return an ordered subset. 

123 """ 

124 

125 async def retrieve( 

126 self, 

127 query: str, 

128 candidates: list[SearchResultProtocol], 

129 *, 

130 top_k: int = 5, 

131 **kwargs: Any, 

132 ) -> list[SearchResultProtocol]: 

133 """Rank and return the top-k most relevant candidates. 

134 

135 Args: 

136 query: Query string. 

137 candidates: Retrieved candidate documents. 

138 top_k: Maximum number of results to return. 

139 **kwargs: Strategy-specific options. 

140 

141 Returns: 

142 Ordered list of the most relevant documents. 

143 """ 

144 ... 

145 

146 

147@runtime_checkable 

148class RerankingStrategyProtocol(Protocol): 

149 """Protocol for cross-encoder or LLM-based reranking strategies. 

150 

151 Applied after initial retrieval to reorder documents by relevance. 

152 """ 

153 

154 async def rerank( 

155 self, 

156 query: str, 

157 documents: list[SearchResultProtocol], 

158 *, 

159 top_k: int | None = None, 

160 ) -> list[SearchResultProtocol]: 

161 """Reorder documents by relevance to query. 

162 

163 Args: 

164 query: Query string. 

165 documents: Documents to rerank. 

166 top_k: If set, return only the top-k results. 

167 

168 Returns: 

169 Reranked (and optionally truncated) list of documents. 

170 """ 

171 ... 

172 

173 

174@runtime_checkable 

175class RAGEvaluatorProtocol(Protocol): 

176 """Protocol for evaluating RAG pipeline quality. 

177 

178 Implementations run metrics (faithfulness, relevance, etc.) on a 

179 completed RAG interaction and return a structured report. 

180 """ 

181 

182 async def evaluate( 

183 self, 

184 query: str, 

185 retrieved_docs: list[Any], 

186 generated_answer: str, 

187 **kwargs: Any, 

188 ) -> Any: 

189 """Evaluate a completed RAG interaction. 

190 

191 Args: 

192 query: The original user query. 

193 retrieved_docs: Documents retrieved by the pipeline. 

194 generated_answer: The synthesized answer produced by the pipeline. 

195 **kwargs: Additional evaluation parameters. 

196 

197 Returns: 

198 An evaluation report (``RAGEvaluationReport`` or similar). 

199 """ 

200 ... 

201 

202 

203@runtime_checkable 

204class PromptCompressorProtocol(Protocol): 

205 """Compresses text to fit within a token budget. 

206 

207 Implementations range from learned compression (LLMLingua-2) to 

208 heuristic truncation. The protocol guarantees that the returned text 

209 fits within target_token_count. 

210 

211 Placement note: Lives in ai/rag.py because its primary consumer is the 

212 RAG context compression stage. Also consumed by lexigram-ai-memory. 

213 """ 

214 

215 async def compress( 

216 self, 

217 text: str, 

218 target_token_count: int, 

219 force_tokens: list[str] | None = None, 

220 ) -> str: 

221 """Compress text to fit within target_token_count. 

222 

223 Args: 

224 text: The raw text to compress. 

225 target_token_count: Maximum tokens in the result. 

226 force_tokens: Tokens that must never be removed. 

227 

228 Returns: 

229 Compressed text fitting within the budget. 

230 """ 

231 ... 

232 

233 

234__all__ = [ 

235 "ChunkProtocol", 

236 "ChunkingError", 

237 "DocumentLoaderProtocol", 

238 "PromptCompressorProtocol", 

239 "RAGContext", 

240 "RAGError", 

241 "RAGEvaluatorProtocol", 

242 "RAGPipelineProtocol", 

243 "RAGResponse", 

244 "RerankingStrategyProtocol", 

245 "RetrievalError", 

246 "RetrievalStrategyProtocol", 

247 "SynthesisError", 

248 "SynthesizerProtocol", 

249]