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.
- {unrealon-1.1.5.dist-info/licenses → unrealon-2.0.4.dist-info}/LICENSE +1 -1
- unrealon-2.0.4.dist-info/METADATA +491 -0
- unrealon-2.0.4.dist-info/RECORD +129 -0
- {unrealon-1.1.5.dist-info → unrealon-2.0.4.dist-info}/WHEEL +2 -1
- unrealon-2.0.4.dist-info/entry_points.txt +3 -0
- unrealon-2.0.4.dist-info/top_level.txt +3 -0
- unrealon_browser/__init__.py +5 -2
- unrealon_browser/cli/browser_cli.py +18 -9
- unrealon_browser/cli/interactive_mode.py +18 -7
- unrealon_browser/core/browser_manager.py +76 -13
- unrealon_browser/dto/__init__.py +21 -0
- unrealon_browser/dto/bot_detection.py +175 -0
- unrealon_browser/dto/models/config.py +14 -1
- unrealon_browser/managers/__init__.py +4 -1
- unrealon_browser/managers/logger_bridge.py +3 -6
- unrealon_browser/managers/page_wait_manager.py +198 -0
- unrealon_browser/stealth/__init__.py +27 -0
- unrealon_browser/stealth/bypass_techniques.pyc +0 -0
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- unrealon_core/__init__.py +160 -0
- unrealon_core/config/__init__.py +16 -0
- unrealon_core/config/environment.py +98 -0
- unrealon_core/config/urls.py +93 -0
- unrealon_core/enums/__init__.py +24 -0
- unrealon_core/enums/status.py +216 -0
- unrealon_core/enums/types.py +240 -0
- unrealon_core/error_handling/__init__.py +45 -0
- unrealon_core/error_handling/circuit_breaker.py +292 -0
- unrealon_core/error_handling/error_context.py +324 -0
- unrealon_core/error_handling/recovery.py +371 -0
- unrealon_core/error_handling/retry.py +268 -0
- unrealon_core/exceptions/__init__.py +46 -0
- unrealon_core/exceptions/base.py +292 -0
- unrealon_core/exceptions/communication.py +22 -0
- unrealon_core/exceptions/driver.py +11 -0
- unrealon_core/exceptions/proxy.py +11 -0
- unrealon_core/exceptions/task.py +12 -0
- unrealon_core/exceptions/validation.py +17 -0
- unrealon_core/models/__init__.py +98 -0
- unrealon_core/models/arq_context.py +252 -0
- unrealon_core/models/arq_responses.py +125 -0
- unrealon_core/models/base.py +291 -0
- unrealon_core/models/bridge_stats.py +58 -0
- unrealon_core/models/communication.py +39 -0
- unrealon_core/models/config.py +47 -0
- unrealon_core/models/connection_stats.py +47 -0
- unrealon_core/models/driver.py +30 -0
- unrealon_core/models/driver_details.py +98 -0
- unrealon_core/models/logging.py +28 -0
- unrealon_core/models/task.py +21 -0
- unrealon_core/models/typed_responses.py +210 -0
- unrealon_core/models/websocket/__init__.py +91 -0
- unrealon_core/models/websocket/base.py +49 -0
- unrealon_core/models/websocket/config.py +200 -0
- unrealon_core/models/websocket/driver.py +215 -0
- unrealon_core/models/websocket/errors.py +138 -0
- unrealon_core/models/websocket/heartbeat.py +100 -0
- unrealon_core/models/websocket/logging.py +261 -0
- unrealon_core/models/websocket/proxy.py +496 -0
- unrealon_core/models/websocket/tasks.py +275 -0
- unrealon_core/models/websocket/utils.py +153 -0
- unrealon_core/models/websocket_session.py +144 -0
- unrealon_core/monitoring/__init__.py +43 -0
- unrealon_core/monitoring/alerts.py +398 -0
- unrealon_core/monitoring/dashboard.py +307 -0
- unrealon_core/monitoring/health_check.py +354 -0
- unrealon_core/monitoring/metrics.py +352 -0
- unrealon_core/utils/__init__.py +11 -0
- unrealon_core/utils/time.py +61 -0
- unrealon_core/version.py +219 -0
- unrealon_driver/__init__.py +88 -50
- unrealon_driver/core_module/__init__.py +34 -0
- unrealon_driver/core_module/base.py +184 -0
- unrealon_driver/core_module/config.py +30 -0
- unrealon_driver/core_module/event_manager.py +127 -0
- unrealon_driver/core_module/protocols.py +98 -0
- unrealon_driver/core_module/registry.py +146 -0
- unrealon_driver/decorators/__init__.py +15 -0
- unrealon_driver/decorators/retry.py +117 -0
- unrealon_driver/decorators/schedule.py +137 -0
- unrealon_driver/decorators/task.py +61 -0
- unrealon_driver/decorators/timing.py +132 -0
- unrealon_driver/driver/__init__.py +20 -0
- unrealon_driver/driver/communication/__init__.py +10 -0
- unrealon_driver/driver/communication/session.py +203 -0
- unrealon_driver/driver/communication/websocket_client.py +197 -0
- unrealon_driver/driver/core/__init__.py +10 -0
- unrealon_driver/driver/core/config.py +85 -0
- unrealon_driver/driver/core/driver.py +221 -0
- unrealon_driver/driver/factory/__init__.py +9 -0
- unrealon_driver/driver/factory/manager_factory.py +130 -0
- unrealon_driver/driver/lifecycle/__init__.py +11 -0
- unrealon_driver/driver/lifecycle/daemon.py +76 -0
- unrealon_driver/driver/lifecycle/initialization.py +97 -0
- unrealon_driver/driver/lifecycle/shutdown.py +48 -0
- unrealon_driver/driver/monitoring/__init__.py +9 -0
- unrealon_driver/driver/monitoring/health.py +63 -0
- unrealon_driver/driver/utilities/__init__.py +10 -0
- unrealon_driver/driver/utilities/logging.py +51 -0
- unrealon_driver/driver/utilities/serialization.py +61 -0
- unrealon_driver/managers/__init__.py +32 -0
- unrealon_driver/managers/base.py +174 -0
- unrealon_driver/managers/browser.py +98 -0
- unrealon_driver/managers/cache.py +116 -0
- unrealon_driver/managers/http.py +107 -0
- unrealon_driver/managers/logger.py +286 -0
- unrealon_driver/managers/proxy.py +99 -0
- unrealon_driver/managers/registry.py +87 -0
- unrealon_driver/managers/threading.py +54 -0
- unrealon_driver/managers/update.py +107 -0
- unrealon_driver/utils/__init__.py +9 -0
- unrealon_driver/utils/time.py +10 -0
- unrealon/__init__.py +0 -40
- unrealon-1.1.5.dist-info/METADATA +0 -621
- unrealon-1.1.5.dist-info/RECORD +0 -54
- unrealon-1.1.5.dist-info/entry_points.txt +0 -9
- unrealon_browser/managers/stealth.py +0 -388
- unrealon_driver/exceptions.py +0 -33
- unrealon_driver/html_analyzer/__init__.py +0 -32
- unrealon_driver/html_analyzer/cleaner.py +0 -657
- unrealon_driver/html_analyzer/config.py +0 -64
- unrealon_driver/html_analyzer/manager.py +0 -247
- unrealon_driver/html_analyzer/models.py +0 -115
- unrealon_driver/html_analyzer/websocket_analyzer.py +0 -157
- unrealon_driver/models/__init__.py +0 -31
- unrealon_driver/models/websocket.py +0 -98
- unrealon_driver/parser/__init__.py +0 -36
- unrealon_driver/parser/cli_manager.py +0 -142
- unrealon_driver/parser/daemon_manager.py +0 -403
- unrealon_driver/parser/managers/__init__.py +0 -25
- unrealon_driver/parser/managers/config.py +0 -293
- unrealon_driver/parser/managers/error.py +0 -412
- unrealon_driver/parser/managers/result.py +0 -321
- unrealon_driver/parser/parser_manager.py +0 -458
- unrealon_driver/smart_logging/__init__.py +0 -24
- unrealon_driver/smart_logging/models.py +0 -44
- unrealon_driver/smart_logging/smart_logger.py +0 -406
- unrealon_driver/smart_logging/unified_logger.py +0 -525
- unrealon_driver/websocket/__init__.py +0 -31
- unrealon_driver/websocket/client.py +0 -249
- unrealon_driver/websocket/config.py +0 -188
- 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
|
+
)
|