unrealon 1.1.5__py3-none-any.whl → 2.0.4__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 (146) hide show
  1. {unrealon-1.1.5.dist-info/licenses → unrealon-2.0.4.dist-info}/LICENSE +1 -1
  2. unrealon-2.0.4.dist-info/METADATA +491 -0
  3. unrealon-2.0.4.dist-info/RECORD +129 -0
  4. {unrealon-1.1.5.dist-info → unrealon-2.0.4.dist-info}/WHEEL +2 -1
  5. unrealon-2.0.4.dist-info/entry_points.txt +3 -0
  6. unrealon-2.0.4.dist-info/top_level.txt +3 -0
  7. unrealon_browser/__init__.py +5 -2
  8. unrealon_browser/cli/browser_cli.py +18 -9
  9. unrealon_browser/cli/interactive_mode.py +18 -7
  10. unrealon_browser/core/browser_manager.py +76 -13
  11. unrealon_browser/dto/__init__.py +21 -0
  12. unrealon_browser/dto/bot_detection.py +175 -0
  13. unrealon_browser/dto/models/config.py +14 -1
  14. unrealon_browser/managers/__init__.py +4 -1
  15. unrealon_browser/managers/logger_bridge.py +3 -6
  16. unrealon_browser/managers/page_wait_manager.py +198 -0
  17. unrealon_browser/stealth/__init__.py +27 -0
  18. unrealon_browser/stealth/bypass_techniques.pyc +0 -0
  19. unrealon_browser/stealth/manager.pyc +0 -0
  20. unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
  21. unrealon_browser/stealth/playwright_stealth.pyc +0 -0
  22. unrealon_browser/stealth/scanner_tester.pyc +0 -0
  23. unrealon_browser/stealth/undetected_chrome.pyc +0 -0
  24. unrealon_core/__init__.py +160 -0
  25. unrealon_core/config/__init__.py +16 -0
  26. unrealon_core/config/environment.py +98 -0
  27. unrealon_core/config/urls.py +93 -0
  28. unrealon_core/enums/__init__.py +24 -0
  29. unrealon_core/enums/status.py +216 -0
  30. unrealon_core/enums/types.py +240 -0
  31. unrealon_core/error_handling/__init__.py +45 -0
  32. unrealon_core/error_handling/circuit_breaker.py +292 -0
  33. unrealon_core/error_handling/error_context.py +324 -0
  34. unrealon_core/error_handling/recovery.py +371 -0
  35. unrealon_core/error_handling/retry.py +268 -0
  36. unrealon_core/exceptions/__init__.py +46 -0
  37. unrealon_core/exceptions/base.py +292 -0
  38. unrealon_core/exceptions/communication.py +22 -0
  39. unrealon_core/exceptions/driver.py +11 -0
  40. unrealon_core/exceptions/proxy.py +11 -0
  41. unrealon_core/exceptions/task.py +12 -0
  42. unrealon_core/exceptions/validation.py +17 -0
  43. unrealon_core/models/__init__.py +98 -0
  44. unrealon_core/models/arq_context.py +252 -0
  45. unrealon_core/models/arq_responses.py +125 -0
  46. unrealon_core/models/base.py +291 -0
  47. unrealon_core/models/bridge_stats.py +58 -0
  48. unrealon_core/models/communication.py +39 -0
  49. unrealon_core/models/config.py +47 -0
  50. unrealon_core/models/connection_stats.py +47 -0
  51. unrealon_core/models/driver.py +30 -0
  52. unrealon_core/models/driver_details.py +98 -0
  53. unrealon_core/models/logging.py +28 -0
  54. unrealon_core/models/task.py +21 -0
  55. unrealon_core/models/typed_responses.py +210 -0
  56. unrealon_core/models/websocket/__init__.py +91 -0
  57. unrealon_core/models/websocket/base.py +49 -0
  58. unrealon_core/models/websocket/config.py +200 -0
  59. unrealon_core/models/websocket/driver.py +215 -0
  60. unrealon_core/models/websocket/errors.py +138 -0
  61. unrealon_core/models/websocket/heartbeat.py +100 -0
  62. unrealon_core/models/websocket/logging.py +261 -0
  63. unrealon_core/models/websocket/proxy.py +496 -0
  64. unrealon_core/models/websocket/tasks.py +275 -0
  65. unrealon_core/models/websocket/utils.py +153 -0
  66. unrealon_core/models/websocket_session.py +144 -0
  67. unrealon_core/monitoring/__init__.py +43 -0
  68. unrealon_core/monitoring/alerts.py +398 -0
  69. unrealon_core/monitoring/dashboard.py +307 -0
  70. unrealon_core/monitoring/health_check.py +354 -0
  71. unrealon_core/monitoring/metrics.py +352 -0
  72. unrealon_core/utils/__init__.py +11 -0
  73. unrealon_core/utils/time.py +61 -0
  74. unrealon_core/version.py +219 -0
  75. unrealon_driver/__init__.py +88 -50
  76. unrealon_driver/core_module/__init__.py +34 -0
  77. unrealon_driver/core_module/base.py +184 -0
  78. unrealon_driver/core_module/config.py +30 -0
  79. unrealon_driver/core_module/event_manager.py +127 -0
  80. unrealon_driver/core_module/protocols.py +98 -0
  81. unrealon_driver/core_module/registry.py +146 -0
  82. unrealon_driver/decorators/__init__.py +15 -0
  83. unrealon_driver/decorators/retry.py +117 -0
  84. unrealon_driver/decorators/schedule.py +137 -0
  85. unrealon_driver/decorators/task.py +61 -0
  86. unrealon_driver/decorators/timing.py +132 -0
  87. unrealon_driver/driver/__init__.py +20 -0
  88. unrealon_driver/driver/communication/__init__.py +10 -0
  89. unrealon_driver/driver/communication/session.py +203 -0
  90. unrealon_driver/driver/communication/websocket_client.py +197 -0
  91. unrealon_driver/driver/core/__init__.py +10 -0
  92. unrealon_driver/driver/core/config.py +85 -0
  93. unrealon_driver/driver/core/driver.py +221 -0
  94. unrealon_driver/driver/factory/__init__.py +9 -0
  95. unrealon_driver/driver/factory/manager_factory.py +130 -0
  96. unrealon_driver/driver/lifecycle/__init__.py +11 -0
  97. unrealon_driver/driver/lifecycle/daemon.py +76 -0
  98. unrealon_driver/driver/lifecycle/initialization.py +97 -0
  99. unrealon_driver/driver/lifecycle/shutdown.py +48 -0
  100. unrealon_driver/driver/monitoring/__init__.py +9 -0
  101. unrealon_driver/driver/monitoring/health.py +63 -0
  102. unrealon_driver/driver/utilities/__init__.py +10 -0
  103. unrealon_driver/driver/utilities/logging.py +51 -0
  104. unrealon_driver/driver/utilities/serialization.py +61 -0
  105. unrealon_driver/managers/__init__.py +32 -0
  106. unrealon_driver/managers/base.py +174 -0
  107. unrealon_driver/managers/browser.py +98 -0
  108. unrealon_driver/managers/cache.py +116 -0
  109. unrealon_driver/managers/http.py +107 -0
  110. unrealon_driver/managers/logger.py +286 -0
  111. unrealon_driver/managers/proxy.py +99 -0
  112. unrealon_driver/managers/registry.py +87 -0
  113. unrealon_driver/managers/threading.py +54 -0
  114. unrealon_driver/managers/update.py +107 -0
  115. unrealon_driver/utils/__init__.py +9 -0
  116. unrealon_driver/utils/time.py +10 -0
  117. unrealon/__init__.py +0 -40
  118. unrealon-1.1.5.dist-info/METADATA +0 -621
  119. unrealon-1.1.5.dist-info/RECORD +0 -54
  120. unrealon-1.1.5.dist-info/entry_points.txt +0 -9
  121. unrealon_browser/managers/stealth.py +0 -388
  122. unrealon_driver/exceptions.py +0 -33
  123. unrealon_driver/html_analyzer/__init__.py +0 -32
  124. unrealon_driver/html_analyzer/cleaner.py +0 -657
  125. unrealon_driver/html_analyzer/config.py +0 -64
  126. unrealon_driver/html_analyzer/manager.py +0 -247
  127. unrealon_driver/html_analyzer/models.py +0 -115
  128. unrealon_driver/html_analyzer/websocket_analyzer.py +0 -157
  129. unrealon_driver/models/__init__.py +0 -31
  130. unrealon_driver/models/websocket.py +0 -98
  131. unrealon_driver/parser/__init__.py +0 -36
  132. unrealon_driver/parser/cli_manager.py +0 -142
  133. unrealon_driver/parser/daemon_manager.py +0 -403
  134. unrealon_driver/parser/managers/__init__.py +0 -25
  135. unrealon_driver/parser/managers/config.py +0 -293
  136. unrealon_driver/parser/managers/error.py +0 -412
  137. unrealon_driver/parser/managers/result.py +0 -321
  138. unrealon_driver/parser/parser_manager.py +0 -458
  139. unrealon_driver/smart_logging/__init__.py +0 -24
  140. unrealon_driver/smart_logging/models.py +0 -44
  141. unrealon_driver/smart_logging/smart_logger.py +0 -406
  142. unrealon_driver/smart_logging/unified_logger.py +0 -525
  143. unrealon_driver/websocket/__init__.py +0 -31
  144. unrealon_driver/websocket/client.py +0 -249
  145. unrealon_driver/websocket/config.py +0 -188
  146. unrealon_driver/websocket/manager.py +0 -90
@@ -0,0 +1,49 @@
1
+ """
2
+ Base WebSocket Models.
3
+
4
+ Core message types and base classes for WebSocket communication.
5
+ Strictly typed without raw dictionaries.
6
+
7
+ Phase 2: Core Systems - WebSocket Bridge
8
+ """
9
+
10
+ from typing import Optional
11
+ from datetime import datetime
12
+
13
+ from pydantic import Field, ConfigDict, field_serializer
14
+ from ..base import UnrealOnBaseModel, TimestampedModel, IdentifiedModel
15
+ from ...enums.types import MessageType
16
+ from ...utils.time import datetime_to_iso
17
+
18
+
19
+ class WebSocketMessage(IdentifiedModel, TimestampedModel):
20
+ """
21
+ Base WebSocket message model.
22
+
23
+ All WebSocket messages inherit from this base class for consistency.
24
+ Provides message ID, timestamp, and optional correlation ID.
25
+
26
+ CRITICAL: No raw Dict[str, Any] - all data must be typed!
27
+ """
28
+
29
+ model_config = ConfigDict(
30
+ validate_assignment=True,
31
+ extra="allow",
32
+ use_enum_values=True
33
+ )
34
+
35
+ type: MessageType = Field(
36
+ description="Type of the WebSocket message"
37
+ )
38
+
39
+ correlation_id: Optional[str] = Field(
40
+ default=None,
41
+ description="ID of the message this is responding to"
42
+ )
43
+
44
+ @field_serializer('created_at', 'updated_at', when_used='json')
45
+ def serialize_datetime_fields(self, value: Optional[datetime]) -> Optional[str]:
46
+ """Serialize datetime fields to ISO format."""
47
+ return datetime_to_iso(value)
48
+
49
+
@@ -0,0 +1,200 @@
1
+ """
2
+ Configuration WebSocket Models.
3
+
4
+ Strictly typed models for configuration updates.
5
+ No raw dictionaries - all data structures are Pydantic models.
6
+
7
+ Phase 2: Core Systems - WebSocket Bridge
8
+ """
9
+
10
+ from typing import Optional, List
11
+ from pydantic import Field, ConfigDict
12
+
13
+ from ..base import UnrealOnBaseModel
14
+ from .base import WebSocketMessage, MessageType
15
+
16
+
17
+ class LoggingConfiguration(UnrealOnBaseModel):
18
+ """Logging configuration - strictly typed."""
19
+
20
+ model_config = ConfigDict(
21
+ validate_assignment=True,
22
+ extra="forbid"
23
+ )
24
+
25
+ level: str = Field(
26
+ default="INFO",
27
+ pattern=r"^(DEBUG|INFO|WARNING|ERROR|CRITICAL)$",
28
+ description="Logging level"
29
+ )
30
+
31
+ batch_size: int = Field(
32
+ default=10,
33
+ ge=1,
34
+ le=100,
35
+ description="Log batch size"
36
+ )
37
+
38
+ send_interval: float = Field(
39
+ default=1.0,
40
+ ge=0.1,
41
+ le=60.0,
42
+ description="Log send interval in seconds"
43
+ )
44
+
45
+ local_logging: bool = Field(
46
+ default=True,
47
+ description="Enable local logging"
48
+ )
49
+
50
+
51
+ class TaskConfiguration(UnrealOnBaseModel):
52
+ """Task configuration - strictly typed."""
53
+
54
+ model_config = ConfigDict(
55
+ validate_assignment=True,
56
+ extra="forbid"
57
+ )
58
+
59
+ max_concurrent_tasks: int = Field(
60
+ default=1,
61
+ ge=1,
62
+ le=100,
63
+ description="Maximum concurrent tasks"
64
+ )
65
+
66
+ default_timeout: float = Field(
67
+ default=300.0,
68
+ ge=30.0,
69
+ le=3600.0,
70
+ description="Default task timeout in seconds"
71
+ )
72
+
73
+ max_retries: int = Field(
74
+ default=3,
75
+ ge=0,
76
+ le=10,
77
+ description="Maximum task retries"
78
+ )
79
+
80
+ retry_delay: float = Field(
81
+ default=1.0,
82
+ ge=0.1,
83
+ le=60.0,
84
+ description="Retry delay in seconds"
85
+ )
86
+
87
+
88
+ class ProxyConfiguration(UnrealOnBaseModel):
89
+ """Proxy configuration - strictly typed."""
90
+
91
+ model_config = ConfigDict(
92
+ validate_assignment=True,
93
+ extra="forbid"
94
+ )
95
+
96
+ enabled: bool = Field(
97
+ default=False,
98
+ description="Enable proxy usage"
99
+ )
100
+
101
+ rotation_enabled: bool = Field(
102
+ default=True,
103
+ description="Enable proxy rotation"
104
+ )
105
+
106
+ rotation_interval: int = Field(
107
+ default=10,
108
+ ge=1,
109
+ le=1000,
110
+ description="Proxy rotation interval (requests)"
111
+ )
112
+
113
+ health_check_interval: float = Field(
114
+ default=60.0,
115
+ ge=10.0,
116
+ le=3600.0,
117
+ description="Proxy health check interval in seconds"
118
+ )
119
+
120
+
121
+ class DriverConfiguration(UnrealOnBaseModel):
122
+ """Complete driver configuration - strictly typed."""
123
+
124
+ model_config = ConfigDict(
125
+ validate_assignment=True,
126
+ extra="forbid"
127
+ )
128
+
129
+ heartbeat_interval: int = Field(
130
+ default=30,
131
+ ge=5,
132
+ le=300,
133
+ description="Heartbeat interval in seconds"
134
+ )
135
+
136
+ logging: LoggingConfiguration = Field(
137
+ default_factory=LoggingConfiguration,
138
+ description="Logging configuration"
139
+ )
140
+
141
+ tasks: TaskConfiguration = Field(
142
+ default_factory=TaskConfiguration,
143
+ description="Task configuration"
144
+ )
145
+
146
+ proxy: ProxyConfiguration = Field(
147
+ default_factory=ProxyConfiguration,
148
+ description="Proxy configuration"
149
+ )
150
+
151
+ enabled_managers: List[str] = Field(
152
+ default_factory=lambda: ["logger", "http", "proxy"],
153
+ description="List of enabled managers"
154
+ )
155
+
156
+
157
+ class ConfigurationUpdateData(UnrealOnBaseModel):
158
+ """Configuration update data payload - strictly typed."""
159
+
160
+ model_config = ConfigDict(
161
+ validate_assignment=True,
162
+ extra="forbid"
163
+ )
164
+
165
+ configuration: DriverConfiguration = Field(
166
+ description="Updated configuration parameters"
167
+ )
168
+
169
+ apply_immediately: bool = Field(
170
+ default=True,
171
+ description="Whether to apply configuration immediately"
172
+ )
173
+
174
+ restart_required: bool = Field(
175
+ default=False,
176
+ description="Whether driver restart is required"
177
+ )
178
+
179
+ config_version: Optional[str] = Field(
180
+ default=None,
181
+ description="Configuration version"
182
+ )
183
+
184
+
185
+ class ConfigurationUpdateMessage(WebSocketMessage):
186
+ """Configuration update message (Server → Driver)."""
187
+
188
+ model_config = ConfigDict(
189
+ validate_assignment=True,
190
+ extra="forbid"
191
+ )
192
+
193
+ type: MessageType = Field(
194
+ default=MessageType.COMMAND_CONFIG_UPDATE,
195
+ frozen=True
196
+ )
197
+
198
+ data: ConfigurationUpdateData = Field(
199
+ description="Configuration update data"
200
+ )
@@ -0,0 +1,215 @@
1
+ """
2
+ Driver WebSocket Models.
3
+
4
+ Strictly typed models for driver registration and responses.
5
+ No raw dictionaries - all data structures are Pydantic models.
6
+
7
+ Phase 2: Core Systems - WebSocket Bridge
8
+ """
9
+
10
+ from typing import List, Optional
11
+ from pydantic import Field, ConfigDict, field_validator
12
+
13
+ from ..base import UnrealOnBaseModel
14
+ from .base import WebSocketMessage, MessageType
15
+ from ...version import get_driver_version
16
+
17
+
18
+ class DriverMetadata(UnrealOnBaseModel):
19
+ """Driver metadata with strict typing."""
20
+
21
+ model_config = ConfigDict(
22
+ validate_assignment=True,
23
+ extra="forbid"
24
+ )
25
+
26
+ region: Optional[str] = Field(
27
+ default=None,
28
+ description="Driver deployment region"
29
+ )
30
+
31
+ environment: Optional[str] = Field(
32
+ default="production",
33
+ description="Environment (development, staging, production)"
34
+ )
35
+
36
+ max_concurrent_tasks: Optional[int] = Field(
37
+ default=1,
38
+ ge=1,
39
+ le=100,
40
+ description="Maximum concurrent tasks"
41
+ )
42
+
43
+ tags: List[str] = Field(
44
+ default_factory=list,
45
+ description="Driver tags for filtering"
46
+ )
47
+
48
+
49
+ class DriverRegistrationData(UnrealOnBaseModel):
50
+ """Driver registration data payload - strictly typed."""
51
+
52
+ model_config = ConfigDict(
53
+ validate_assignment=True,
54
+ extra="forbid"
55
+ )
56
+
57
+ driver_id: str = Field(
58
+ min_length=1,
59
+ max_length=100,
60
+ description="Unique driver identifier"
61
+ )
62
+
63
+ driver_name: str = Field(
64
+ min_length=1,
65
+ max_length=200,
66
+ description="Human-readable driver name"
67
+ )
68
+
69
+ driver_type: str = Field(
70
+ min_length=1,
71
+ max_length=50,
72
+ description="Type of driver (universal, ecommerce, etc.)"
73
+ )
74
+
75
+ capabilities: List[str] = Field(
76
+ default_factory=list,
77
+ description="List of supported task types"
78
+ )
79
+
80
+ version: str = Field(
81
+ default_factory=get_driver_version,
82
+ pattern=r"^\d+\.\d+\.\d+$",
83
+ description="Driver version (semver)"
84
+ )
85
+
86
+ metadata: DriverMetadata = Field(
87
+ default_factory=DriverMetadata,
88
+ description="Driver metadata"
89
+ )
90
+
91
+ @field_validator('capabilities')
92
+ @classmethod
93
+ def validate_capabilities(cls, v: List[str]) -> List[str]:
94
+ """Validate capabilities list."""
95
+ if not v:
96
+ raise ValueError("Driver must have at least one capability")
97
+
98
+ # Remove duplicates while preserving order
99
+ seen = set()
100
+ unique_caps = []
101
+ for cap in v:
102
+ if cap not in seen:
103
+ seen.add(cap)
104
+ unique_caps.append(cap)
105
+
106
+ return unique_caps
107
+
108
+
109
+ class DriverRegistrationMessage(WebSocketMessage):
110
+ """Driver registration message (Driver → Server)."""
111
+
112
+ model_config = ConfigDict(
113
+ validate_assignment=True,
114
+ extra="forbid"
115
+ )
116
+
117
+ type: MessageType = Field(
118
+ default=MessageType.DRIVER_REGISTER,
119
+ frozen=True
120
+ )
121
+
122
+ data: DriverRegistrationData = Field(
123
+ description="Driver registration data"
124
+ )
125
+
126
+
127
+ class DriverConfiguration(UnrealOnBaseModel):
128
+ """Driver configuration from server - strictly typed."""
129
+
130
+ model_config = ConfigDict(
131
+ validate_assignment=True,
132
+ extra="forbid"
133
+ )
134
+
135
+ heartbeat_interval: int = Field(
136
+ default=30,
137
+ ge=5,
138
+ le=300,
139
+ description="Heartbeat interval in seconds"
140
+ )
141
+
142
+ log_level: str = Field(
143
+ default="INFO",
144
+ pattern=r"^(DEBUG|INFO|WARNING|ERROR|CRITICAL)$",
145
+ description="Logging level"
146
+ )
147
+
148
+ batch_size: int = Field(
149
+ default=10,
150
+ ge=1,
151
+ le=100,
152
+ description="Log batch size"
153
+ )
154
+
155
+ max_retries: int = Field(
156
+ default=3,
157
+ ge=0,
158
+ le=10,
159
+ description="Maximum task retries"
160
+ )
161
+
162
+ timeout_seconds: int = Field(
163
+ default=300,
164
+ ge=30,
165
+ le=3600,
166
+ description="Default task timeout"
167
+ )
168
+
169
+
170
+ class RegistrationResponseData(UnrealOnBaseModel):
171
+ """Registration response data payload - strictly typed."""
172
+
173
+ model_config = ConfigDict(
174
+ validate_assignment=True,
175
+ extra="forbid"
176
+ )
177
+
178
+ success: bool = Field(
179
+ description="Whether registration was successful"
180
+ )
181
+
182
+ driver_id: str = Field(
183
+ description="Confirmed driver ID"
184
+ )
185
+
186
+ session_id: str = Field(
187
+ description="WebSocket session ID"
188
+ )
189
+
190
+ message: str = Field(
191
+ description="Registration status message"
192
+ )
193
+
194
+ configuration: DriverConfiguration = Field(
195
+ default_factory=DriverConfiguration,
196
+ description="Driver configuration from server"
197
+ )
198
+
199
+
200
+ class RegistrationResponseMessage(WebSocketMessage):
201
+ """Registration response message (Server → Driver)."""
202
+
203
+ model_config = ConfigDict(
204
+ validate_assignment=True,
205
+ extra="forbid"
206
+ )
207
+
208
+ type: MessageType = Field(
209
+ default=MessageType.DRIVER_REGISTER_RESPONSE,
210
+ frozen=True
211
+ )
212
+
213
+ data: RegistrationResponseData = Field(
214
+ description="Registration response data"
215
+ )
@@ -0,0 +1,138 @@
1
+ """
2
+ Error and Acknowledgment WebSocket Models.
3
+
4
+ Strictly typed models for error messages and acknowledgments.
5
+ No raw dictionaries - all data structures are Pydantic models.
6
+
7
+ Phase 2: Core Systems - WebSocket Bridge
8
+ """
9
+
10
+ from typing import Optional, List
11
+ from pydantic import Field, ConfigDict
12
+
13
+ from ..base import UnrealOnBaseModel
14
+ from .base import WebSocketMessage, MessageType
15
+
16
+
17
+ class ErrorDetails(UnrealOnBaseModel):
18
+ """Error details - strictly typed."""
19
+
20
+ model_config = ConfigDict(
21
+ validate_assignment=True,
22
+ extra="forbid"
23
+ )
24
+
25
+ field_errors: List[str] = Field(
26
+ default_factory=list,
27
+ description="Field validation errors"
28
+ )
29
+
30
+ stack_trace: Optional[str] = Field(
31
+ default=None,
32
+ description="Stack trace (if available)"
33
+ )
34
+
35
+ request_id: Optional[str] = Field(
36
+ default=None,
37
+ description="Request ID that caused the error"
38
+ )
39
+
40
+ timestamp: Optional[str] = Field(
41
+ default=None,
42
+ description="Error timestamp"
43
+ )
44
+
45
+
46
+ class ErrorData(UnrealOnBaseModel):
47
+ """Error data payload - strictly typed."""
48
+
49
+ model_config = ConfigDict(
50
+ validate_assignment=True,
51
+ extra="forbid"
52
+ )
53
+
54
+ error_code: str = Field(
55
+ min_length=1,
56
+ max_length=100,
57
+ description="Error code identifier"
58
+ )
59
+
60
+ error_message: str = Field(
61
+ min_length=1,
62
+ max_length=1000,
63
+ description="Human-readable error message"
64
+ )
65
+
66
+ details: Optional[ErrorDetails] = Field(
67
+ default=None,
68
+ description="Additional error details"
69
+ )
70
+
71
+ retry_after: Optional[int] = Field(
72
+ default=None,
73
+ ge=0,
74
+ le=3600,
75
+ description="Retry after seconds (if retryable)"
76
+ )
77
+
78
+
79
+ class ErrorMessage(WebSocketMessage):
80
+ """Error message (Bidirectional)."""
81
+
82
+ model_config = ConfigDict(
83
+ validate_assignment=True,
84
+ extra="forbid"
85
+ )
86
+
87
+ type: MessageType = Field(
88
+ default=MessageType.ERROR,
89
+ frozen=True
90
+ )
91
+
92
+ data: ErrorData = Field(
93
+ description="Error data"
94
+ )
95
+
96
+
97
+ class AckData(UnrealOnBaseModel):
98
+ """Acknowledgment data payload - strictly typed."""
99
+
100
+ model_config = ConfigDict(
101
+ validate_assignment=True,
102
+ extra="forbid"
103
+ )
104
+
105
+ acknowledged: bool = Field(
106
+ default=True,
107
+ description="Acknowledgment status"
108
+ )
109
+
110
+ message: Optional[str] = Field(
111
+ default=None,
112
+ max_length=500,
113
+ description="Optional acknowledgment message"
114
+ )
115
+
116
+ processing_time_ms: Optional[float] = Field(
117
+ default=None,
118
+ ge=0.0,
119
+ description="Processing time in milliseconds"
120
+ )
121
+
122
+
123
+ class AckMessage(WebSocketMessage):
124
+ """Acknowledgment message (Bidirectional)."""
125
+
126
+ model_config = ConfigDict(
127
+ validate_assignment=True,
128
+ extra="forbid"
129
+ )
130
+
131
+ type: MessageType = Field(
132
+ default=MessageType.ACK,
133
+ frozen=True
134
+ )
135
+
136
+ data: AckData = Field(
137
+ description="Acknowledgment data"
138
+ )
@@ -0,0 +1,100 @@
1
+ """
2
+ Heartbeat WebSocket Models.
3
+
4
+ Strictly typed models for driver heartbeat messages.
5
+ No raw dictionaries - all data structures are Pydantic models.
6
+
7
+ Phase 2: Core Systems - WebSocket Bridge
8
+ """
9
+
10
+ from typing import Optional
11
+ from pydantic import Field, ConfigDict
12
+
13
+ from ..base import UnrealOnBaseModel
14
+ from .base import WebSocketMessage, MessageType
15
+
16
+
17
+ class HeartbeatData(UnrealOnBaseModel):
18
+ """Heartbeat data payload - strictly typed."""
19
+
20
+ model_config = ConfigDict(
21
+ validate_assignment=True,
22
+ extra="forbid"
23
+ )
24
+
25
+ driver_status: str = Field(
26
+ default="ready",
27
+ pattern=r"^(ready|busy|idle|error|maintenance)$",
28
+ description="Current driver status"
29
+ )
30
+
31
+ active_tasks: int = Field(
32
+ default=0,
33
+ ge=0,
34
+ le=1000,
35
+ description="Number of active tasks"
36
+ )
37
+
38
+ completed_tasks: int = Field(
39
+ default=0,
40
+ ge=0,
41
+ description="Total completed tasks"
42
+ )
43
+
44
+ failed_tasks: int = Field(
45
+ default=0,
46
+ ge=0,
47
+ description="Total failed tasks"
48
+ )
49
+
50
+ uptime_seconds: float = Field(
51
+ default=0.0,
52
+ ge=0.0,
53
+ description="Driver uptime in seconds"
54
+ )
55
+
56
+ memory_usage_mb: Optional[float] = Field(
57
+ default=None,
58
+ ge=0.0,
59
+ le=100000.0, # 100GB max
60
+ description="Memory usage in MB"
61
+ )
62
+
63
+ cpu_usage_percent: Optional[float] = Field(
64
+ default=None,
65
+ ge=0.0,
66
+ le=100.0,
67
+ description="CPU usage percentage"
68
+ )
69
+
70
+ def get_success_rate(self) -> float:
71
+ """Calculate task success rate."""
72
+ total_tasks = self.completed_tasks + self.failed_tasks
73
+ if total_tasks == 0:
74
+ return 1.0
75
+ return self.completed_tasks / total_tasks
76
+
77
+ def is_healthy(self) -> bool:
78
+ """Check if driver is healthy."""
79
+ return (
80
+ self.driver_status in ["ready", "busy", "idle"] and
81
+ self.get_success_rate() >= 0.8 # 80% success rate threshold
82
+ )
83
+
84
+
85
+ class HeartbeatMessage(WebSocketMessage):
86
+ """Heartbeat message (Driver → Server)."""
87
+
88
+ model_config = ConfigDict(
89
+ validate_assignment=True,
90
+ extra="forbid"
91
+ )
92
+
93
+ type: MessageType = Field(
94
+ default=MessageType.DRIVER_HEARTBEAT,
95
+ frozen=True
96
+ )
97
+
98
+ data: HeartbeatData = Field(
99
+ description="Heartbeat data"
100
+ )