Coverage for src / lexigram / contracts / ai / exceptions.py: 9%

65 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-19 05:41 +0800

1"""Domain errors for AI subsystem. 

2 

3This module defines the base exception classes for AI-related errors. 

4All AI subsystem errors are expected, recoverable failures that clients 

5should handle gracefully. 

6 

7Specific implementations (LLM, RAG, agents, memory, skills) are defined 

8in their respective extension packages and extend these base classes. 

9""" 

10 

11from __future__ import annotations 

12 

13from enum import StrEnum 

14from typing import Any 

15 

16from lexigram.contracts.exceptions.domain import DomainError 

17 

18 

19class AIError(DomainError): 

20 """Base exception for all AI-domain errors. 

21 

22 This is the catch-all for AI operations that fail in expected, 

23 recoverable ways (e.g., LLM response generation, RAG retrieval, 

24 skill execution). 

25 """ 

26 

27 _code = "LEX_ERR_AI_001" 

28 

29 def __init__(self, message: str = "AI error", **kwargs: Any) -> None: 

30 super().__init__(message, **kwargs) 

31 

32 

33class LLMError(AIError): 

34 """Base for LLM client errors. 

35 

36 Extended in lexigram-ai-llm with specific failures like 

37 rate limiting, content filtering, invalid tokens, etc. 

38 """ 

39 

40 _code = "LEX_ERR_LLM_001" 

41 

42 def __init__(self, message: str = "LLM error", **kwargs: Any) -> None: 

43 super().__init__(message, **kwargs) 

44 

45 

46class RAGError(AIError): 

47 """Base for RAG pipeline errors. 

48 

49 Extended in lexigram-ai-rag with specific failures like 

50 retrieval, preprocessing, synthesis, etc. 

51 """ 

52 

53 _code = "LEX_ERR_RAG_001" 

54 

55 def __init__(self, message: str = "RAG error", **kwargs: Any) -> None: 

56 super().__init__(message, **kwargs) 

57 

58 

59class RetrieverError(AIError): 

60 """Base for retriever errors. 

61 

62 Raised when document retrieval fails in an expected, recoverable way 

63 (e.g., query parsing, backend unavailable, timeout). 

64 """ 

65 

66 _code = "LEX_ERR_RET_001" 

67 

68 def __init__(self, message: str = "Retriever error", **kwargs: Any) -> None: 

69 super().__init__(message, **kwargs) 

70 

71 

72class AIMemoryError(AIError): 

73 """Base for AI memory system errors. 

74 

75 Extended in lexigram-ai-memory with specific failures like 

76 consolidation, retrieval, storage issues, etc. 

77 """ 

78 

79 _code = "LEX_ERR_MEM_001" 

80 

81 def __init__(self, message: str = "Memory error", **kwargs: Any) -> None: 

82 super().__init__(message, **kwargs) 

83 

84 

85class SkillError(AIError): 

86 """Base for skill execution errors. 

87 

88 Extended in lexigram-ai-skills with specific failures like 

89 skill not found, execution failure, parameter validation, etc. 

90 """ 

91 

92 _code = "LEX_ERR_SKILL_001" 

93 

94 def __init__(self, message: str = "Skill error", **kwargs: Any) -> None: 

95 super().__init__(message, **kwargs) 

96 

97 

98class GuardError(AIError): 

99 """Base for AI guard/policy enforcement errors. 

100 

101 Extended in the AI guard layer with specific failures like 

102 input/output policy violations, content filtering, etc. 

103 """ 

104 

105 _code = "LEX_ERR_GUARD_001" 

106 

107 def __init__(self, message: str = "Guard error", **kwargs: Any) -> None: 

108 super().__init__(message, **kwargs) 

109 

110 

111class ExtractionError(AIError): 

112 """Base for structured extraction errors. 

113 

114 Used in ``StructuredExtractorProtocol.extract()`` return type. 

115 Extended in lexigram-ai-llm with specific failures like parse errors, 

116 validation failures, and max retry exhaustion. 

117 """ 

118 

119 _code = "LEX_ERR_AI_002" 

120 

121 def __init__(self, message: str = "Extraction error", **kwargs: Any) -> None: 

122 super().__init__(message, **kwargs) 

123 

124 

125class RelayErrorCode(StrEnum): 

126 """Stable machine-readable codes carried by :class:`RelayError`. 

127 

128 Attributes: 

129 UNSUPPORTED_FORMAT: Unknown or unimplemented wire format. 

130 UNSUPPORTED_ROUTE: No mapper exists for the requested route. 

131 MALFORMED_PAYLOAD: Wire payload does not match the expected shape. 

132 UNSUPPORTED_FEATURE: The source feature cannot be converted. 

133 MISSING_REQUIRED_OPTION: A required field or host option is absent. 

134 MEDIA_RESOLUTION_REQUIRED: URL media needs a resolver the host did 

135 not supply. 

136 STREAM_STATE_INVALID: Stream event out of order or from the wrong 

137 source format. 

138 STREAM_ALREADY_FINALIZED: Event accepted after finalization. 

139 SERIALIZATION_ERROR: Payload cannot be serialized or deserialized. 

140 DUPLICATE_REGISTRATION: A mapper was registered twice for one format. 

141 """ 

142 

143 UNSUPPORTED_FORMAT = "unsupported_format" 

144 UNSUPPORTED_ROUTE = "unsupported_route" 

145 MALFORMED_PAYLOAD = "malformed_payload" 

146 UNSUPPORTED_FEATURE = "unsupported_feature" 

147 MISSING_REQUIRED_OPTION = "missing_required_option" 

148 MEDIA_RESOLUTION_REQUIRED = "media_resolution_required" 

149 STREAM_STATE_INVALID = "stream_state_invalid" 

150 STREAM_ALREADY_FINALIZED = "stream_already_finalized" 

151 SERIALIZATION_ERROR = "serialization_error" 

152 DUPLICATE_REGISTRATION = "duplicate_registration" 

153 

154 

155class RelayError(AIError): 

156 """Base for relay conversion and gateway errors. 

157 

158 Extended in lexigram-ai-llm with specific conversion failures (e.g. 

159 unsupported protocol, malformed wire payload). Live alongside 

160 ``LLMError`` and ``RAGError`` as an AI sub-domain base. 

161 

162 The ``code`` attribute carries a stable machine-readable value from 

163 :class:`RelayErrorCode` (or a relaykit-compatible string) so callers 

164 can branch without string matching on messages. 

165 """ 

166 

167 _code = "LEX_ERR_AI_003" 

168 

169 def __init__( 

170 self, 

171 message: str = "Relay error", 

172 code: str | RelayErrorCode = "relay_error", 

173 **kwargs: Any, 

174 ) -> None: 

175 super().__init__(message, **kwargs) 

176 self.code = str(code) 

177 

178 

179class WorkflowError(AIError): 

180 """Base for workflow execution errors. 

181 

182 Raised when a workflow graph execution fails in an expected, 

183 recoverable way (e.g., node failure, max iterations exceeded, 

184 invalid workflow configuration). 

185 """ 

186 

187 _code = "LEX_ERR_WF_001" 

188 

189 def __init__(self, message: str = "Workflow error", **kwargs: Any) -> None: 

190 super().__init__(message, **kwargs) 

191 

192 

193class RunnableError(AIError): 

194 """Recoverable composition failure (input shape, parser error, retry budget exceeded).""" 

195 

196 _code = "LEX_ERR_RUN_001" 

197 

198 def __init__(self, message: str = "Runnable error", **kwargs: Any) -> None: 

199 super().__init__(message, **kwargs) 

200 

201 

202class EvaluationError(AIError): 

203 """Base for evaluation system errors. 

204 

205 Extended in the AI evaluation layer with specific failures like 

206 evaluator not found, dataset parsing, metric computation, etc. 

207 """ 

208 

209 _code = "LEX_ERR_EVAL_001" 

210 

211 def __init__(self, message: str = "Evaluation error", **kwargs: Any) -> None: 

212 super().__init__(message, **kwargs) 

213 

214 

215__all__ = [ 

216 "AIError", 

217 "AIMemoryError", 

218 "EvaluationError", 

219 "ExtractionError", 

220 "GuardError", 

221 "LLMError", 

222 "RAGError", 

223 "RelayError", 

224 "RelayErrorCode", 

225 "RetrieverError", 

226 "RunnableError", 

227 "SkillError", 

228 "WorkflowError", 

229]