audex 1.0.7a3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. audex/__init__.py +9 -0
  2. audex/__main__.py +7 -0
  3. audex/cli/__init__.py +189 -0
  4. audex/cli/apis/__init__.py +12 -0
  5. audex/cli/apis/init/__init__.py +34 -0
  6. audex/cli/apis/init/gencfg.py +130 -0
  7. audex/cli/apis/init/setup.py +330 -0
  8. audex/cli/apis/init/vprgroup.py +125 -0
  9. audex/cli/apis/serve.py +141 -0
  10. audex/cli/args.py +356 -0
  11. audex/cli/exceptions.py +44 -0
  12. audex/cli/helper/__init__.py +0 -0
  13. audex/cli/helper/ansi.py +193 -0
  14. audex/cli/helper/display.py +288 -0
  15. audex/config/__init__.py +64 -0
  16. audex/config/core/__init__.py +30 -0
  17. audex/config/core/app.py +29 -0
  18. audex/config/core/audio.py +45 -0
  19. audex/config/core/logging.py +163 -0
  20. audex/config/core/session.py +11 -0
  21. audex/config/helper/__init__.py +1 -0
  22. audex/config/helper/client/__init__.py +1 -0
  23. audex/config/helper/client/http.py +28 -0
  24. audex/config/helper/client/websocket.py +21 -0
  25. audex/config/helper/provider/__init__.py +1 -0
  26. audex/config/helper/provider/dashscope.py +13 -0
  27. audex/config/helper/provider/unisound.py +18 -0
  28. audex/config/helper/provider/xfyun.py +23 -0
  29. audex/config/infrastructure/__init__.py +31 -0
  30. audex/config/infrastructure/cache.py +51 -0
  31. audex/config/infrastructure/database.py +48 -0
  32. audex/config/infrastructure/recorder.py +32 -0
  33. audex/config/infrastructure/store.py +19 -0
  34. audex/config/provider/__init__.py +18 -0
  35. audex/config/provider/transcription.py +109 -0
  36. audex/config/provider/vpr.py +99 -0
  37. audex/container.py +40 -0
  38. audex/entity/__init__.py +468 -0
  39. audex/entity/doctor.py +109 -0
  40. audex/entity/doctor.pyi +51 -0
  41. audex/entity/fields.py +401 -0
  42. audex/entity/segment.py +115 -0
  43. audex/entity/segment.pyi +38 -0
  44. audex/entity/session.py +133 -0
  45. audex/entity/session.pyi +47 -0
  46. audex/entity/utterance.py +142 -0
  47. audex/entity/utterance.pyi +48 -0
  48. audex/entity/vp.py +68 -0
  49. audex/entity/vp.pyi +35 -0
  50. audex/exceptions.py +157 -0
  51. audex/filters/__init__.py +692 -0
  52. audex/filters/generated/__init__.py +21 -0
  53. audex/filters/generated/doctor.py +987 -0
  54. audex/filters/generated/segment.py +723 -0
  55. audex/filters/generated/session.py +978 -0
  56. audex/filters/generated/utterance.py +939 -0
  57. audex/filters/generated/vp.py +815 -0
  58. audex/helper/__init__.py +1 -0
  59. audex/helper/hash.py +33 -0
  60. audex/helper/mixin.py +65 -0
  61. audex/helper/net.py +19 -0
  62. audex/helper/settings/__init__.py +830 -0
  63. audex/helper/settings/fields.py +317 -0
  64. audex/helper/stream.py +153 -0
  65. audex/injectors/__init__.py +1 -0
  66. audex/injectors/config.py +12 -0
  67. audex/injectors/lifespan.py +7 -0
  68. audex/lib/__init__.py +1 -0
  69. audex/lib/cache/__init__.py +383 -0
  70. audex/lib/cache/inmemory.py +513 -0
  71. audex/lib/database/__init__.py +83 -0
  72. audex/lib/database/sqlite.py +406 -0
  73. audex/lib/exporter.py +189 -0
  74. audex/lib/injectors/__init__.py +1 -0
  75. audex/lib/injectors/cache.py +25 -0
  76. audex/lib/injectors/container.py +47 -0
  77. audex/lib/injectors/exporter.py +26 -0
  78. audex/lib/injectors/recorder.py +33 -0
  79. audex/lib/injectors/server.py +17 -0
  80. audex/lib/injectors/session.py +18 -0
  81. audex/lib/injectors/sqlite.py +24 -0
  82. audex/lib/injectors/store.py +13 -0
  83. audex/lib/injectors/transcription.py +42 -0
  84. audex/lib/injectors/usb.py +12 -0
  85. audex/lib/injectors/vpr.py +65 -0
  86. audex/lib/injectors/wifi.py +7 -0
  87. audex/lib/recorder.py +844 -0
  88. audex/lib/repos/__init__.py +149 -0
  89. audex/lib/repos/container.py +23 -0
  90. audex/lib/repos/database/__init__.py +1 -0
  91. audex/lib/repos/database/sqlite.py +672 -0
  92. audex/lib/repos/decorators.py +74 -0
  93. audex/lib/repos/doctor.py +286 -0
  94. audex/lib/repos/segment.py +302 -0
  95. audex/lib/repos/session.py +285 -0
  96. audex/lib/repos/tables/__init__.py +70 -0
  97. audex/lib/repos/tables/doctor.py +137 -0
  98. audex/lib/repos/tables/segment.py +113 -0
  99. audex/lib/repos/tables/session.py +140 -0
  100. audex/lib/repos/tables/utterance.py +131 -0
  101. audex/lib/repos/tables/vp.py +102 -0
  102. audex/lib/repos/utterance.py +288 -0
  103. audex/lib/repos/vp.py +286 -0
  104. audex/lib/restful.py +251 -0
  105. audex/lib/server/__init__.py +97 -0
  106. audex/lib/server/auth.py +98 -0
  107. audex/lib/server/handlers.py +248 -0
  108. audex/lib/server/templates/index.html.j2 +226 -0
  109. audex/lib/server/templates/login.html.j2 +111 -0
  110. audex/lib/server/templates/static/script.js +68 -0
  111. audex/lib/server/templates/static/style.css +579 -0
  112. audex/lib/server/types.py +123 -0
  113. audex/lib/session.py +503 -0
  114. audex/lib/store/__init__.py +238 -0
  115. audex/lib/store/localfile.py +411 -0
  116. audex/lib/transcription/__init__.py +33 -0
  117. audex/lib/transcription/dashscope.py +525 -0
  118. audex/lib/transcription/events.py +62 -0
  119. audex/lib/usb.py +554 -0
  120. audex/lib/vpr/__init__.py +38 -0
  121. audex/lib/vpr/unisound/__init__.py +185 -0
  122. audex/lib/vpr/unisound/types.py +469 -0
  123. audex/lib/vpr/xfyun/__init__.py +483 -0
  124. audex/lib/vpr/xfyun/types.py +679 -0
  125. audex/lib/websocket/__init__.py +8 -0
  126. audex/lib/websocket/connection.py +485 -0
  127. audex/lib/websocket/pool.py +991 -0
  128. audex/lib/wifi.py +1146 -0
  129. audex/lifespan.py +75 -0
  130. audex/service/__init__.py +27 -0
  131. audex/service/decorators.py +73 -0
  132. audex/service/doctor/__init__.py +652 -0
  133. audex/service/doctor/const.py +36 -0
  134. audex/service/doctor/exceptions.py +96 -0
  135. audex/service/doctor/types.py +54 -0
  136. audex/service/export/__init__.py +236 -0
  137. audex/service/export/const.py +17 -0
  138. audex/service/export/exceptions.py +34 -0
  139. audex/service/export/types.py +21 -0
  140. audex/service/injectors/__init__.py +1 -0
  141. audex/service/injectors/container.py +53 -0
  142. audex/service/injectors/doctor.py +34 -0
  143. audex/service/injectors/export.py +27 -0
  144. audex/service/injectors/session.py +49 -0
  145. audex/service/session/__init__.py +754 -0
  146. audex/service/session/const.py +34 -0
  147. audex/service/session/exceptions.py +67 -0
  148. audex/service/session/types.py +91 -0
  149. audex/types.py +39 -0
  150. audex/utils.py +287 -0
  151. audex/valueobj/__init__.py +81 -0
  152. audex/valueobj/common/__init__.py +1 -0
  153. audex/valueobj/common/auth.py +84 -0
  154. audex/valueobj/common/email.py +16 -0
  155. audex/valueobj/common/ops.py +22 -0
  156. audex/valueobj/common/phone.py +84 -0
  157. audex/valueobj/common/version.py +72 -0
  158. audex/valueobj/session.py +19 -0
  159. audex/valueobj/utterance.py +15 -0
  160. audex/view/__init__.py +51 -0
  161. audex/view/container.py +17 -0
  162. audex/view/decorators.py +303 -0
  163. audex/view/pages/__init__.py +1 -0
  164. audex/view/pages/dashboard/__init__.py +286 -0
  165. audex/view/pages/dashboard/wifi.py +407 -0
  166. audex/view/pages/login.py +110 -0
  167. audex/view/pages/recording.py +348 -0
  168. audex/view/pages/register.py +202 -0
  169. audex/view/pages/sessions/__init__.py +196 -0
  170. audex/view/pages/sessions/details.py +224 -0
  171. audex/view/pages/sessions/export.py +443 -0
  172. audex/view/pages/settings.py +374 -0
  173. audex/view/pages/voiceprint/__init__.py +1 -0
  174. audex/view/pages/voiceprint/enroll.py +195 -0
  175. audex/view/pages/voiceprint/update.py +195 -0
  176. audex/view/static/css/dashboard.css +452 -0
  177. audex/view/static/css/glass.css +22 -0
  178. audex/view/static/css/global.css +541 -0
  179. audex/view/static/css/login.css +386 -0
  180. audex/view/static/css/recording.css +439 -0
  181. audex/view/static/css/register.css +293 -0
  182. audex/view/static/css/sessions/styles.css +501 -0
  183. audex/view/static/css/settings.css +186 -0
  184. audex/view/static/css/voiceprint/enroll.css +43 -0
  185. audex/view/static/css/voiceprint/styles.css +209 -0
  186. audex/view/static/css/voiceprint/update.css +44 -0
  187. audex/view/static/images/logo.svg +95 -0
  188. audex/view/static/js/recording.js +42 -0
  189. audex-1.0.7a3.dist-info/METADATA +361 -0
  190. audex-1.0.7a3.dist-info/RECORD +192 -0
  191. audex-1.0.7a3.dist-info/WHEEL +4 -0
  192. audex-1.0.7a3.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,47 @@
1
+ # This file is auto-generated by PrototypeX stub generator.
2
+ # Do not edit manually - changes will be overwritten.
3
+ # Regenerate using: python -m scripts.genstubs gen
4
+
5
+ from __future__ import annotations
6
+
7
+ import datetime
8
+
9
+ from audex.entity import BaseEntity
10
+ from audex.valueobj.session import SessionStatus
11
+
12
+ class Session(BaseEntity):
13
+ id: str
14
+ created_at: datetime.datetime
15
+ updated_at: datetime.datetime | None
16
+ doctor_id: str
17
+ patient_name: str | None
18
+ clinic_number: str | None
19
+ medical_record_number: str | None
20
+ diagnosis: str | None
21
+ status: SessionStatus
22
+ started_at: datetime.datetime | None
23
+ ended_at: datetime.datetime | None
24
+ notes: str | None
25
+ def __init__(
26
+ self,
27
+ *,
28
+ id: str = ...,
29
+ created_at: datetime.datetime = ...,
30
+ updated_at: datetime.datetime | None = None,
31
+ doctor_id: str,
32
+ patient_name: str | None = None,
33
+ clinic_number: str | None = None,
34
+ medical_record_number: str | None = None,
35
+ diagnosis: str | None = None,
36
+ status: SessionStatus = ...,
37
+ started_at: datetime.datetime | None = None,
38
+ ended_at: datetime.datetime | None = None,
39
+ notes: str | None = None,
40
+ ) -> None: ...
41
+ @property
42
+ def is_active(self) -> bool: ...
43
+ @property
44
+ def is_finished(self) -> bool: ...
45
+ def cancel(self) -> None: ...
46
+ def complete(self) -> None: ...
47
+ def start(self) -> None: ...
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+
5
+ from audex import utils
6
+ from audex.entity import BaseEntity
7
+ from audex.entity import touch_after
8
+ from audex.entity.fields import DateTimeField
9
+ from audex.entity.fields import FloatField
10
+ from audex.entity.fields import IntegerField
11
+ from audex.entity.fields import StringBackedField
12
+ from audex.entity.fields import StringField
13
+ from audex.valueobj.utterance import Speaker
14
+
15
+
16
+ class Utterance(BaseEntity):
17
+ """Utterance entity representing a single speech utterance in
18
+ conversation.
19
+
20
+ Represents one continuous speech segment from either the doctor or patient,
21
+ as recognized by ASR (Automatic Speech Recognition) and speaker verification.
22
+ Each utterance contains the transcribed text, speaker identification,
23
+ timing information, and confidence scores.
24
+
25
+ Attributes:
26
+ id: The unique identifier of the utterance. Auto-generated with
27
+ "utterance-" prefix.
28
+ session_id: The ID of the session this utterance belongs to. Foreign
29
+ key reference to Session entity.
30
+ segment_id: The ID of the audio segment containing this utterance.
31
+ Foreign key reference to Segment entity.
32
+ sequence: The sequence number of this utterance within the session.
33
+ Starts from 1 and increments for each new utterance.
34
+ speaker: The identified speaker (DOCTOR or PATIENT). Determined by
35
+ voiceprint verification against doctor's registered voiceprint.
36
+ text: The transcribed text content from ASR.
37
+ confidence: ASR confidence score, typically between 0.0 and 1.0.
38
+ Higher values indicate more confident transcription.
39
+ start_time_ms: Start time of the utterance within the audio segment,
40
+ in milliseconds from segment start.
41
+ end_time_ms: End time of the utterance within the audio segment,
42
+ in milliseconds from segment start.
43
+ timestamp: Absolute timestamp when the utterance occurred in the
44
+ session timeline.
45
+
46
+ Inherited Attributes:
47
+ created_at: Timestamp when the utterance was created.
48
+ updated_at: Timestamp when the utterance was last updated.
49
+
50
+ Example:
51
+ ```python
52
+ # Create doctor utterance
53
+ utterance = Utterance(
54
+ session_id="session-xyz789",
55
+ segment_id="segment-abc123",
56
+ sequence=1,
57
+ speaker=Speaker.DOCTOR,
58
+ text="您好,今天哪里不舒服?",
59
+ confidence=0.95,
60
+ start_time_ms=1000,
61
+ end_time_ms=3500,
62
+ timestamp=utils.utcnow(),
63
+ )
64
+
65
+ # Create patient utterance
66
+ utterance2 = Utterance(
67
+ session_id="session-xyz789",
68
+ segment_id="segment-abc123",
69
+ sequence=2,
70
+ speaker=Speaker.PATIENT,
71
+ text="医生,我最近总是头疼。",
72
+ confidence=0.88,
73
+ start_time_ms=4000,
74
+ end_time_ms=6800,
75
+ timestamp=utils.utcnow(),
76
+ )
77
+
78
+ # Check speaker
79
+ if utterance.is_doctor:
80
+ print("This is doctor speaking")
81
+ if utterance2.is_patient:
82
+ print("This is patient speaking")
83
+ ```
84
+ """
85
+
86
+ id: str = StringField(default_factory=lambda: utils.gen_id(prefix="utterance-"))
87
+ session_id: str = StringField()
88
+ segment_id: str = StringField()
89
+ sequence: int = IntegerField()
90
+ speaker: Speaker = StringBackedField(Speaker)
91
+ text: str = StringField()
92
+ confidence: float | None = FloatField(nullable=True)
93
+ start_time_ms: int = IntegerField()
94
+ end_time_ms: int = IntegerField()
95
+ timestamp: datetime.datetime = DateTimeField(default_factory=utils.utcnow)
96
+
97
+ @property
98
+ def duration_ms(self) -> int:
99
+ """Calculate the duration of this utterance in milliseconds.
100
+
101
+ Returns:
102
+ Duration in milliseconds.
103
+ """
104
+ return self.end_time_ms - self.start_time_ms
105
+
106
+ @property
107
+ def is_doctor(self) -> bool:
108
+ """Check if the speaker is the doctor.
109
+
110
+ Returns:
111
+ True if speaker is DOCTOR, False otherwise.
112
+ """
113
+ return self.speaker == Speaker.DOCTOR
114
+
115
+ @property
116
+ def is_patient(self) -> bool:
117
+ """Check if the speaker is the patient.
118
+
119
+ Returns:
120
+ True if speaker is PATIENT, False otherwise.
121
+ """
122
+ return self.speaker == Speaker.PATIENT
123
+
124
+ @touch_after
125
+ def incr(self) -> None:
126
+ """Increment the sequence number of this utterance by 1.
127
+
128
+ Note:
129
+ The updated_at timestamp is automatically updated.
130
+ """
131
+ self.sequence += 1
132
+
133
+ @touch_after
134
+ def decr(self) -> None:
135
+ """Decrement the sequence number of this utterance by 1.
136
+
137
+ Note:
138
+ The updated_at timestamp is automatically updated.
139
+ """
140
+ if self.sequence > 1:
141
+ self.sequence -= 1
142
+ raise ValueError("Sequence number cannot be less than 1.")
@@ -0,0 +1,48 @@
1
+ # This file is auto-generated by PrototypeX stub generator.
2
+ # Do not edit manually - changes will be overwritten.
3
+ # Regenerate using: python -m scripts.genstubs gen
4
+
5
+ from __future__ import annotations
6
+
7
+ import datetime
8
+
9
+ from audex.entity import BaseEntity
10
+ from audex.valueobj.utterance import Speaker
11
+
12
+ class Utterance(BaseEntity):
13
+ id: str
14
+ created_at: datetime.datetime
15
+ updated_at: datetime.datetime | None
16
+ session_id: str
17
+ segment_id: str
18
+ sequence: int
19
+ speaker: Speaker
20
+ text: str
21
+ confidence: float | None
22
+ start_time_ms: int
23
+ end_time_ms: int
24
+ timestamp: datetime.datetime
25
+ def __init__(
26
+ self,
27
+ *,
28
+ id: str = ...,
29
+ created_at: datetime.datetime = ...,
30
+ updated_at: datetime.datetime | None = None,
31
+ session_id: str,
32
+ segment_id: str,
33
+ sequence: int,
34
+ speaker: Speaker,
35
+ text: str,
36
+ confidence: float | None = None,
37
+ start_time_ms: int,
38
+ end_time_ms: int,
39
+ timestamp: datetime.datetime = ...,
40
+ ) -> None: ...
41
+ @property
42
+ def duration_ms(self) -> int: ...
43
+ @property
44
+ def is_doctor(self) -> bool: ...
45
+ @property
46
+ def is_patient(self) -> bool: ...
47
+ def decr(self) -> None: ...
48
+ def incr(self) -> None: ...
audex/entity/vp.py ADDED
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from audex import utils
4
+ from audex.entity import BaseEntity
5
+ from audex.entity.fields import BoolField
6
+ from audex.entity.fields import IntegerField
7
+ from audex.entity.fields import StringField
8
+
9
+
10
+ class VP(BaseEntity):
11
+ """Voiceprint registration record entity.
12
+
13
+ Represents a voiceprint registration in the VPR system. Links a doctor
14
+ to their registered voiceprint in the external VPR service, storing
15
+ both the local audio reference and remote VPR identifiers.
16
+
17
+ This entity allows for VPR system migration without requiring doctors
18
+ to re-register their voiceprints - the local audio can be re-uploaded
19
+ to a new VPR system.
20
+
21
+ Attributes:
22
+ id: The unique identifier of the registration. Auto-generated with
23
+ "vp_reg-" prefix.
24
+ doctor_id: The ID of the doctor who owns this voiceprint. Foreign
25
+ key reference to Doctor entity.
26
+ vpr_uid: The unique identifier in the VPR system for this voiceprint.
27
+ Used for 1:1 speaker verification.
28
+ vpr_group_id: The group ID in the VPR system. Optional for organizing
29
+ voiceprints (e.g., by hospital or department).
30
+ audio_key: Local storage key for the voiceprint audio file. Preserved
31
+ for potential re-registration with different VPR systems.
32
+ text_content: The text that was read during voiceprint registration.
33
+ Stored for potential re-registration requirements.
34
+ sample_rate: Audio sample rate used for registration (8000 or 16000 Hz).
35
+ is_active: Whether this registration is currently active. Only one
36
+ active registration per doctor should exist.
37
+
38
+ Inherited Attributes:
39
+ created_at: Timestamp when the registration was created.
40
+ updated_at: Timestamp when the registration was last updated.
41
+
42
+ Example:
43
+ ```python
44
+ # Create voiceprint registration after VPR API call
45
+ registration = VPRegistration(
46
+ doctor_id="doctor-abc123",
47
+ vpr_uid="vpr_user_xyz789",
48
+ vpr_group_id="hospital_001",
49
+ audio_key="vp/doctor-abc123/registration.wav",
50
+ text_content="请朗读以下文本进行声纹注册...",
51
+ sample_rate=16000,
52
+ is_active=True,
53
+ )
54
+
55
+ # Check if active
56
+ if registration.is_active:
57
+ print(f"Active VPR UID: {registration.vpr_uid}")
58
+ ```
59
+ """
60
+
61
+ id: str = StringField(default_factory=lambda: utils.gen_id(prefix="vp_reg-"))
62
+ doctor_id: str = StringField()
63
+ vpr_uid: str = StringField()
64
+ vpr_group_id: str = StringField()
65
+ audio_key: str = StringField()
66
+ text_content: str = StringField()
67
+ sample_rate: int = IntegerField()
68
+ is_active: bool = BoolField(default=True)
audex/entity/vp.pyi ADDED
@@ -0,0 +1,35 @@
1
+ # This file is auto-generated by PrototypeX stub generator.
2
+ # Do not edit manually - changes will be overwritten.
3
+ # Regenerate using: python -m scripts.genstubs gen
4
+
5
+ from __future__ import annotations
6
+
7
+ import datetime
8
+
9
+ from audex.entity import BaseEntity
10
+
11
+ class VP(BaseEntity):
12
+ id: str
13
+ created_at: datetime.datetime
14
+ updated_at: datetime.datetime | None
15
+ doctor_id: str
16
+ vpr_uid: str
17
+ vpr_group_id: str
18
+ audio_key: str
19
+ text_content: str
20
+ sample_rate: int
21
+ is_active: bool
22
+ def __init__(
23
+ self,
24
+ *,
25
+ id: str = ...,
26
+ created_at: datetime.datetime = ...,
27
+ updated_at: datetime.datetime | None = None,
28
+ doctor_id: str,
29
+ vpr_uid: str,
30
+ vpr_group_id: str,
31
+ audio_key: str,
32
+ text_content: str,
33
+ sample_rate: int,
34
+ is_active: bool = True,
35
+ ) -> None: ...
audex/exceptions.py ADDED
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ import typing as t
4
+
5
+ import pydantic as pyd
6
+
7
+
8
+ class AudexError(Exception):
9
+ """Base exception for all Audex-related errors.
10
+
11
+ Attributes:
12
+ default_message: Default error message for this exception type.
13
+ code: Unique error code identifying this exception type.
14
+ message: The actual error message for this instance.
15
+ """
16
+
17
+ __slots__ = ("message",)
18
+
19
+ default_message: t.ClassVar[str] = "An error occurred in Audex."
20
+ code: t.ClassVar[int] = 0x01
21
+
22
+ def __init__(self, message: str | None = None) -> None:
23
+ """Initialize the exception.
24
+
25
+ Args:
26
+ message: Custom error message. If None, uses default_message.
27
+ """
28
+ self.message = message or self.default_message
29
+ super().__init__(self.message)
30
+
31
+ def __str__(self) -> str:
32
+ """Return formatted error string with code."""
33
+ return f"[Error {self.code}] {self.message}"
34
+
35
+ def __repr__(self) -> str:
36
+ """Return detailed representation of the exception."""
37
+ return f"{self.__class__.__name__}(code={self.code}, message={self.message!r})"
38
+
39
+ def as_dict(self) -> dict[str, t.Any]:
40
+ """Convert exception to dictionary with all slots."""
41
+ data: dict[str, t.Any] = {}
42
+ for slot in getattr(self, "__slots__", []):
43
+ data[slot] = getattr(self, slot, None)
44
+ return data
45
+
46
+
47
+ class InternalError(AudexError):
48
+ """Base exception for internal system errors.
49
+
50
+ These errors should NOT expose technical details to users. Frontend
51
+ should display a generic error message and ask users to contact
52
+ support with the error code.
53
+ """
54
+
55
+ __slots__ = ("details", "message")
56
+
57
+ default_message = "Internal system error occurred"
58
+ code: t.ClassVar[int] = 0x10
59
+
60
+ def __init__(self, message: str | None = None, **details: t.Any) -> None:
61
+ """Initialize the exception.
62
+
63
+ Args:
64
+ message: Custom error message. If None, uses default_message.
65
+ **details: Additional technical details for logging/debugging.
66
+ """
67
+ self.details = details
68
+ full_message = message or self.default_message
69
+ super().__init__(full_message)
70
+
71
+
72
+ class RequiredModuleNotFoundError(InternalError):
73
+ """Exception raised when a required module is not found."""
74
+
75
+ __slots__ = ("details", "message", "module_name")
76
+
77
+ default_message = "Required module not found"
78
+ code: t.ClassVar[int] = 0x11
79
+
80
+ def __init__(self, *module_name: str, message: str | None = None, **details: t.Any) -> None:
81
+ """Initialize the exception.
82
+
83
+ Args:
84
+ *module_name: One or more names of missing modules.
85
+ message: Custom error message. If None, uses formatted
86
+ default_message.
87
+ **details: Additional technical details for logging/debugging.
88
+ """
89
+ self.module_name = module_name
90
+ full_message = message or f"{self.default_message}: {', '.join(module_name)}"
91
+ super().__init__(full_message, **details)
92
+
93
+
94
+ class ConfigurationError(InternalError):
95
+ """Exception raised for configuration errors."""
96
+
97
+ __slots__ = ("config_key", "details", "message")
98
+
99
+ default_message = "Configuration error occurred"
100
+ code: t.ClassVar[int] = 0x13
101
+
102
+ def __init__(
103
+ self, message: str | None = None, *, config_key: str, reason: str, **details: t.Any
104
+ ) -> None:
105
+ """Initialize the exception.
106
+
107
+ Args:
108
+ message: Custom error message. If None, uses formatted
109
+ default_message.
110
+ config_key: The configuration key that caused the error.
111
+ reason: Description of the configuration error.
112
+ **details: Additional technical details for logging/debugging.
113
+ """
114
+ self.config_key = config_key
115
+ self.reason = reason
116
+ full_message = message or f"{self.default_message}: [{config_key}] {reason}"
117
+ super().__init__(full_message, **details)
118
+
119
+
120
+ class ValidationError(AudexError):
121
+ """Exception raised for validation errors."""
122
+
123
+ __slots__ = ("message", "reason")
124
+
125
+ default_message = "Validation failed"
126
+ code: t.ClassVar[int] = 0x12
127
+
128
+ def __init__(self, message: str | None = None, *, reason: str) -> None:
129
+ """Initialize the exception.
130
+
131
+ Args:
132
+ reason: Description of what failed validation.
133
+ message: Custom error message. If None, uses formatted default_message.
134
+ """
135
+ self.reason = reason
136
+ full_message = message or f"{self.default_message}: {reason}"
137
+ super().__init__(full_message)
138
+
139
+ @classmethod
140
+ def from_pydantic_validation_err(cls, err: pyd.ValidationError) -> t.Self:
141
+ """Create ValidationError from a Pydantic ValidationError."""
142
+ reason = "; ".join(f"{e['loc']}: {e['msg']}" for e in err.errors())
143
+ return cls(reason=reason)
144
+
145
+
146
+ class NoActiveSessionError(AudexError):
147
+ """Exception raised when there is no active session."""
148
+
149
+ default_message = "No active session found. Please re-login to continue."
150
+ code: t.ClassVar[int] = 0x20
151
+
152
+
153
+ class PermissionDeniedError(AudexError):
154
+ """Exception raised for permission denied errors."""
155
+
156
+ default_message = "Permission denied. You do not have access to this resource."
157
+ code: t.ClassVar[int] = 0x21