Coverage for src / lexigram / ai / relay / errors.py: 100%

29 statements  

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

1"""Stable ``RelayError`` factories for the relay conversion engine. 

2 

3Mappers and the engine construct errors through these factories so every 

4failure carries one stable machine-readable code from 

5:class:`lexigram.contracts.ai.exceptions.RelayErrorCode`. Callers branch 

6on ``error.code``, never on message text. 

7""" 

8 

9from __future__ import annotations 

10 

11from lexigram.contracts.ai.exceptions import RelayError, RelayErrorCode 

12 

13__all__ = [ 

14 "duplicate_registration", 

15 "malformed_payload", 

16 "media_resolution_required", 

17 "missing_required_option", 

18 "serialization_error", 

19 "stream_already_finalized", 

20 "stream_state_invalid", 

21 "translate", 

22 "unsupported_feature", 

23 "unsupported_format", 

24 "unsupported_route", 

25] 

26 

27 

28def malformed_payload(detail: str) -> RelayError: 

29 """The wire payload did not match the expected shape. 

30 

31 Args: 

32 detail: Human-readable description of the malformed field. 

33 

34 Returns: 

35 A ``RelayError`` with code ``malformed_payload``. 

36 """ 

37 return RelayError(detail, code=RelayErrorCode.MALFORMED_PAYLOAD) 

38 

39 

40def missing_required_option(detail: str) -> RelayError: 

41 """A required field or host option is absent. 

42 

43 Args: 

44 detail: Which required option is missing. 

45 

46 Returns: 

47 A ``RelayError`` with code ``missing_required_option``. 

48 """ 

49 return RelayError(detail, code=RelayErrorCode.MISSING_REQUIRED_OPTION) 

50 

51 

52def unsupported_feature(detail: str) -> RelayError: 

53 """The source feature cannot be converted to the target. 

54 

55 Args: 

56 detail: The feature that could not be converted. 

57 

58 Returns: 

59 A ``RelayError`` with code ``unsupported_feature``. 

60 """ 

61 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_FEATURE) 

62 

63 

64def unsupported_format(detail: str) -> RelayError: 

65 """The payload does not belong to this mapper's wire format. 

66 

67 Args: 

68 detail: The expected and actual payload shapes. 

69 

70 Returns: 

71 A ``RelayError`` with code ``unsupported_format``. 

72 """ 

73 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_FORMAT) 

74 

75 

76def unsupported_route(detail: str) -> RelayError: 

77 """No mapper exists for the requested source/target route. 

78 

79 Args: 

80 detail: The route that cannot be converted. 

81 

82 Returns: 

83 A ``RelayError`` with code ``unsupported_route``. 

84 """ 

85 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_ROUTE) 

86 

87 

88def duplicate_registration(detail: str) -> RelayError: 

89 """A mapper was registered twice for one wire format. 

90 

91 Args: 

92 detail: The format that was registered twice. 

93 

94 Returns: 

95 A ``RelayError`` with code ``duplicate_registration``. 

96 """ 

97 return RelayError(detail, code=RelayErrorCode.DUPLICATE_REGISTRATION) 

98 

99 

100def media_resolution_required(detail: str) -> RelayError: 

101 """URL media requires a resolver the host did not supply. 

102 

103 Args: 

104 detail: The media URL that cannot be resolved. 

105 

106 Returns: 

107 A ``RelayError`` with code ``media_resolution_required``. 

108 """ 

109 return RelayError(detail, code=RelayErrorCode.MEDIA_RESOLUTION_REQUIRED) 

110 

111 

112def serialization_error(detail: str) -> RelayError: 

113 """The payload cannot be serialized or deserialized. 

114 

115 Args: 

116 detail: Human-readable description of the serialization failure. 

117 

118 Returns: 

119 A ``RelayError`` with code ``serialization_error``. 

120 """ 

121 return RelayError(detail, code=RelayErrorCode.SERIALIZATION_ERROR) 

122 

123 

124def stream_state_invalid(detail: str) -> RelayError: 

125 """A stream event is out of order or from the wrong source format. 

126 

127 Args: 

128 detail: The ordering or format violation. 

129 

130 Returns: 

131 A ``RelayError`` with code ``stream_state_invalid``. 

132 """ 

133 return RelayError(detail, code=RelayErrorCode.STREAM_STATE_INVALID) 

134 

135 

136def stream_already_finalized(detail: str) -> RelayError: 

137 """An event was accepted after the stream was finalized. 

138 

139 Args: 

140 detail: What was accepted after finalization. 

141 

142 Returns: 

143 A ``RelayError`` with code ``stream_already_finalized``. 

144 """ 

145 return RelayError(detail, code=RelayErrorCode.STREAM_ALREADY_FINALIZED) 

146 

147 

148def translate(exc: Exception, *, detail: str) -> RelayError: 

149 """Translate an unexpected exception into a stable error category. 

150 

151 ``RelayError`` passes through unchanged. DTO parsing failures 

152 (``ValueError``, ``TypeError``, ``KeyError``) become 

153 ``malformed_payload``; any other exception becomes 

154 ``serialization_error``. 

155 

156 Args: 

157 exc: The exception raised inside a mapper. 

158 detail: Context describing what was being translated. 

159 

160 Returns: 

161 A stable ``RelayError``. 

162 """ 

163 if isinstance(exc, RelayError): 

164 return exc 

165 if isinstance(exc, (ValueError, TypeError, KeyError)): 

166 return malformed_payload(detail) 

167 return RelayError(detail, code=RelayErrorCode.SERIALIZATION_ERROR)