unrealon 1.1.6__py3-none-any.whl → 2.0.5__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.6.dist-info/licenses → unrealon-2.0.5.dist-info}/LICENSE +1 -1
- unrealon-2.0.5.dist-info/METADATA +491 -0
- unrealon-2.0.5.dist-info/RECORD +128 -0
- {unrealon-1.1.6.dist-info → unrealon-2.0.5.dist-info}/WHEEL +2 -1
- unrealon-2.0.5.dist-info/entry_points.txt +3 -0
- unrealon-2.0.5.dist-info/top_level.txt +3 -0
- unrealon_browser/__init__.py +5 -6
- unrealon_browser/cli/browser_cli.py +18 -9
- unrealon_browser/cli/interactive_mode.py +13 -4
- unrealon_browser/core/browser_manager.py +29 -16
- unrealon_browser/dto/__init__.py +21 -0
- unrealon_browser/dto/bot_detection.py +175 -0
- unrealon_browser/dto/models/config.py +9 -3
- unrealon_browser/managers/__init__.py +1 -1
- unrealon_browser/managers/logger_bridge.py +1 -4
- 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 +172 -0
- unrealon_core/config/__init__.py +16 -0
- unrealon_core/config/environment.py +151 -0
- unrealon_core/config/urls.py +94 -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 +79 -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/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 +90 -51
- 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 +205 -0
- unrealon_driver/driver/core/__init__.py +10 -0
- unrealon_driver/driver/core/config.py +175 -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-1.1.6.dist-info/METADATA +0 -625
- unrealon-1.1.6.dist-info/RECORD +0 -55
- unrealon-1.1.6.dist-info/entry_points.txt +0 -9
- unrealon_browser/managers/stealth.py +0 -388
- unrealon_driver/README.md +0 -0
- 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,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
|
+
)
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging WebSocket Models.
|
|
3
|
+
|
|
4
|
+
Strictly typed models for log entries and batches.
|
|
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
|
+
|
|
16
|
+
|
|
17
|
+
class LogContext(UnrealOnBaseModel):
|
|
18
|
+
"""Log context data - strictly typed for centralized logging."""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(
|
|
21
|
+
validate_assignment=True,
|
|
22
|
+
extra="forbid"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Task context
|
|
26
|
+
task_id: Optional[str] = Field(
|
|
27
|
+
default=None,
|
|
28
|
+
max_length=100,
|
|
29
|
+
description="Associated task ID"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
url: Optional[str] = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
max_length=2000,
|
|
35
|
+
description="Associated URL"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Performance metrics
|
|
39
|
+
duration_ms: Optional[float] = Field(
|
|
40
|
+
default=None,
|
|
41
|
+
ge=0.0,
|
|
42
|
+
description="Operation duration in milliseconds"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
memory_usage_mb: Optional[float] = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
ge=0.0,
|
|
48
|
+
description="Memory usage in MB"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
cpu_usage_percent: Optional[float] = Field(
|
|
52
|
+
default=None,
|
|
53
|
+
ge=0.0,
|
|
54
|
+
le=100.0,
|
|
55
|
+
description="CPU usage percentage"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Error context
|
|
59
|
+
error_code: Optional[str] = Field(
|
|
60
|
+
default=None,
|
|
61
|
+
max_length=50,
|
|
62
|
+
description="Error code if applicable"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
stack_trace: Optional[str] = Field(
|
|
66
|
+
default=None,
|
|
67
|
+
max_length=5000,
|
|
68
|
+
description="Stack trace for errors"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Network context
|
|
72
|
+
user_agent: Optional[str] = Field(
|
|
73
|
+
default=None,
|
|
74
|
+
max_length=500,
|
|
75
|
+
description="User agent used"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
proxy_id: Optional[str] = Field(
|
|
79
|
+
default=None,
|
|
80
|
+
max_length=100,
|
|
81
|
+
description="Proxy ID used"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
response_code: Optional[int] = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
ge=100,
|
|
87
|
+
le=599,
|
|
88
|
+
description="HTTP response code"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Driver context
|
|
92
|
+
driver_version: Optional[str] = Field(
|
|
93
|
+
default=None,
|
|
94
|
+
max_length=50,
|
|
95
|
+
description="Driver version"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
session_id: Optional[str] = Field(
|
|
99
|
+
default=None,
|
|
100
|
+
max_length=100,
|
|
101
|
+
description="Driver session ID"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class LogEntryData(UnrealOnBaseModel):
|
|
106
|
+
"""Log entry data payload - strictly typed for centralized logging."""
|
|
107
|
+
|
|
108
|
+
model_config = ConfigDict(
|
|
109
|
+
validate_assignment=True,
|
|
110
|
+
extra="forbid"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Core log fields
|
|
114
|
+
level: str = Field(
|
|
115
|
+
pattern=r"^(DEBUG|INFO|WARNING|ERROR|CRITICAL)$",
|
|
116
|
+
description="Log level"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
message: str = Field(
|
|
120
|
+
min_length=1,
|
|
121
|
+
max_length=2000,
|
|
122
|
+
description="Log message"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
timestamp: str = Field(
|
|
126
|
+
description="Log timestamp in ISO format"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Source identification
|
|
130
|
+
logger_name: str = Field(
|
|
131
|
+
default="driver",
|
|
132
|
+
min_length=1,
|
|
133
|
+
max_length=100,
|
|
134
|
+
description="Logger name"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
module: Optional[str] = Field(
|
|
138
|
+
default=None,
|
|
139
|
+
max_length=200,
|
|
140
|
+
description="Module that generated the log"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
function_name: Optional[str] = Field(
|
|
144
|
+
default=None,
|
|
145
|
+
max_length=100,
|
|
146
|
+
description="Function that generated the log"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
line_number: Optional[int] = Field(
|
|
150
|
+
default=None,
|
|
151
|
+
ge=1,
|
|
152
|
+
description="Line number where log was generated"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Driver identification
|
|
156
|
+
driver_id: str = Field(
|
|
157
|
+
min_length=1,
|
|
158
|
+
max_length=100,
|
|
159
|
+
description="Unique driver identifier"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
driver_type: str = Field(
|
|
163
|
+
default="unrealon_driver",
|
|
164
|
+
max_length=50,
|
|
165
|
+
description="Type of driver"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Environment
|
|
169
|
+
environment: str = Field(
|
|
170
|
+
default="production",
|
|
171
|
+
pattern=r"^(development|staging|production)$",
|
|
172
|
+
description="Environment where log was generated"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Rich context
|
|
176
|
+
context: LogContext = Field(
|
|
177
|
+
default_factory=LogContext,
|
|
178
|
+
description="Additional log context"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@field_validator('timestamp')
|
|
182
|
+
@classmethod
|
|
183
|
+
def validate_timestamp(cls, v: str) -> str:
|
|
184
|
+
"""Validate ISO timestamp format."""
|
|
185
|
+
from datetime import datetime
|
|
186
|
+
try:
|
|
187
|
+
datetime.fromisoformat(v.replace('Z', '+00:00'))
|
|
188
|
+
return v
|
|
189
|
+
except ValueError:
|
|
190
|
+
raise ValueError("timestamp must be valid ISO format")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class LogEntryMessage(WebSocketMessage):
|
|
194
|
+
"""Log entry message (Driver → Server)."""
|
|
195
|
+
|
|
196
|
+
model_config = ConfigDict(
|
|
197
|
+
validate_assignment=True,
|
|
198
|
+
extra="forbid"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
type: MessageType = Field(
|
|
202
|
+
default=MessageType.LOG_MESSAGE,
|
|
203
|
+
frozen=True
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
data: LogEntryData = Field(
|
|
207
|
+
description="Log entry data"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class LogBatchData(UnrealOnBaseModel):
|
|
212
|
+
"""Log batch data payload - strictly typed."""
|
|
213
|
+
|
|
214
|
+
model_config = ConfigDict(
|
|
215
|
+
validate_assignment=True,
|
|
216
|
+
extra="forbid"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
logs: List[LogEntryData] = Field(
|
|
220
|
+
min_length=1,
|
|
221
|
+
max_length=100,
|
|
222
|
+
description="Batch of log entries"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
driver_id: str = Field(
|
|
226
|
+
min_length=1,
|
|
227
|
+
description="Driver ID for the batch"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
batch_timestamp: str = Field(
|
|
231
|
+
description="Timestamp when batch was created (ISO format)"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
@field_validator('batch_timestamp')
|
|
235
|
+
@classmethod
|
|
236
|
+
def validate_timestamp(cls, v: str) -> str:
|
|
237
|
+
"""Validate ISO timestamp format."""
|
|
238
|
+
from datetime import datetime
|
|
239
|
+
try:
|
|
240
|
+
datetime.fromisoformat(v.replace('Z', '+00:00'))
|
|
241
|
+
return v
|
|
242
|
+
except ValueError:
|
|
243
|
+
raise ValueError("batch_timestamp must be valid ISO format")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class LogBatchMessage(WebSocketMessage):
|
|
247
|
+
"""Log batch message (Driver → Server)."""
|
|
248
|
+
|
|
249
|
+
model_config = ConfigDict(
|
|
250
|
+
validate_assignment=True,
|
|
251
|
+
extra="forbid"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
type: MessageType = Field(
|
|
255
|
+
default=MessageType.LOG_BATCH,
|
|
256
|
+
frozen=True
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
data: LogBatchData = Field(
|
|
260
|
+
description="Log batch data"
|
|
261
|
+
)
|