unrealon 1.0.9__py3-none-any.whl → 1.1.0__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 (302) hide show
  1. unrealon/__init__.py +23 -21
  2. unrealon-1.1.0.dist-info/METADATA +164 -0
  3. unrealon-1.1.0.dist-info/RECORD +82 -0
  4. {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info}/WHEEL +1 -1
  5. unrealon-1.1.0.dist-info/entry_points.txt +9 -0
  6. {unrealon-1.0.9.dist-info → unrealon-1.1.0.dist-info/licenses}/LICENSE +1 -1
  7. unrealon_bridge/__init__.py +114 -0
  8. unrealon_bridge/cli.py +316 -0
  9. unrealon_bridge/client/__init__.py +93 -0
  10. unrealon_bridge/client/base.py +78 -0
  11. unrealon_bridge/client/commands.py +89 -0
  12. unrealon_bridge/client/connection.py +90 -0
  13. unrealon_bridge/client/events.py +65 -0
  14. unrealon_bridge/client/health.py +38 -0
  15. unrealon_bridge/client/html_parser.py +146 -0
  16. unrealon_bridge/client/logging.py +139 -0
  17. unrealon_bridge/client/proxy.py +70 -0
  18. unrealon_bridge/client/scheduler.py +450 -0
  19. unrealon_bridge/client/session.py +70 -0
  20. unrealon_bridge/configs/__init__.py +14 -0
  21. unrealon_bridge/configs/bridge_config.py +212 -0
  22. unrealon_bridge/configs/bridge_config.yaml +39 -0
  23. unrealon_bridge/models/__init__.py +138 -0
  24. unrealon_bridge/models/base.py +28 -0
  25. unrealon_bridge/models/command.py +41 -0
  26. unrealon_bridge/models/events.py +40 -0
  27. unrealon_bridge/models/html_parser.py +79 -0
  28. unrealon_bridge/models/logging.py +55 -0
  29. unrealon_bridge/models/parser.py +63 -0
  30. unrealon_bridge/models/proxy.py +41 -0
  31. unrealon_bridge/models/requests.py +95 -0
  32. unrealon_bridge/models/responses.py +88 -0
  33. unrealon_bridge/models/scheduler.py +592 -0
  34. unrealon_bridge/models/session.py +28 -0
  35. unrealon_bridge/server/__init__.py +91 -0
  36. unrealon_bridge/server/base.py +171 -0
  37. unrealon_bridge/server/handlers/__init__.py +23 -0
  38. unrealon_bridge/server/handlers/command.py +110 -0
  39. unrealon_bridge/server/handlers/html_parser.py +139 -0
  40. unrealon_bridge/server/handlers/logging.py +95 -0
  41. unrealon_bridge/server/handlers/parser.py +95 -0
  42. unrealon_bridge/server/handlers/proxy.py +75 -0
  43. unrealon_bridge/server/handlers/scheduler.py +545 -0
  44. unrealon_bridge/server/handlers/session.py +66 -0
  45. unrealon_browser/__init__.py +61 -18
  46. unrealon_browser/{src/cli → cli}/browser_cli.py +6 -13
  47. unrealon_browser/{src/cli → cli}/cookies_cli.py +5 -1
  48. unrealon_browser/{src/core → core}/browser_manager.py +2 -2
  49. unrealon_browser/{src/managers → managers}/captcha.py +1 -1
  50. unrealon_browser/{src/managers → managers}/cookies.py +1 -1
  51. unrealon_browser/managers/logger_bridge.py +231 -0
  52. unrealon_browser/{src/managers → managers}/profile.py +1 -1
  53. unrealon_driver/__init__.py +73 -19
  54. unrealon_driver/browser/__init__.py +8 -0
  55. unrealon_driver/browser/config.py +74 -0
  56. unrealon_driver/browser/manager.py +416 -0
  57. unrealon_driver/exceptions.py +28 -0
  58. unrealon_driver/parser/__init__.py +55 -0
  59. unrealon_driver/parser/cli_manager.py +141 -0
  60. unrealon_driver/parser/daemon_manager.py +227 -0
  61. unrealon_driver/parser/managers/__init__.py +46 -0
  62. unrealon_driver/parser/managers/browser.py +51 -0
  63. unrealon_driver/parser/managers/config.py +281 -0
  64. unrealon_driver/parser/managers/error.py +412 -0
  65. unrealon_driver/parser/managers/html.py +732 -0
  66. unrealon_driver/parser/managers/logging.py +609 -0
  67. unrealon_driver/parser/managers/result.py +321 -0
  68. unrealon_driver/parser/parser_manager.py +628 -0
  69. unrealon/sdk_config.py +0 -88
  70. unrealon-1.0.9.dist-info/METADATA +0 -810
  71. unrealon-1.0.9.dist-info/RECORD +0 -246
  72. unrealon_browser/pyproject.toml +0 -182
  73. unrealon_browser/src/__init__.py +0 -62
  74. unrealon_browser/src/managers/logger_bridge.py +0 -395
  75. unrealon_driver/README.md +0 -204
  76. unrealon_driver/pyproject.toml +0 -187
  77. unrealon_driver/src/__init__.py +0 -90
  78. unrealon_driver/src/cli/__init__.py +0 -10
  79. unrealon_driver/src/cli/main.py +0 -66
  80. unrealon_driver/src/cli/simple.py +0 -510
  81. unrealon_driver/src/config/__init__.py +0 -11
  82. unrealon_driver/src/config/auto_config.py +0 -478
  83. unrealon_driver/src/core/__init__.py +0 -18
  84. unrealon_driver/src/core/exceptions.py +0 -289
  85. unrealon_driver/src/core/parser.py +0 -638
  86. unrealon_driver/src/dto/__init__.py +0 -66
  87. unrealon_driver/src/dto/cli.py +0 -119
  88. unrealon_driver/src/dto/config.py +0 -18
  89. unrealon_driver/src/dto/events.py +0 -237
  90. unrealon_driver/src/dto/execution.py +0 -313
  91. unrealon_driver/src/dto/services.py +0 -311
  92. unrealon_driver/src/execution/__init__.py +0 -23
  93. unrealon_driver/src/execution/daemon_mode.py +0 -317
  94. unrealon_driver/src/execution/interactive_mode.py +0 -88
  95. unrealon_driver/src/execution/modes.py +0 -45
  96. unrealon_driver/src/execution/scheduled_mode.py +0 -209
  97. unrealon_driver/src/execution/test_mode.py +0 -250
  98. unrealon_driver/src/logging/__init__.py +0 -24
  99. unrealon_driver/src/logging/driver_logger.py +0 -512
  100. unrealon_driver/src/services/__init__.py +0 -24
  101. unrealon_driver/src/services/browser_service.py +0 -726
  102. unrealon_driver/src/services/llm/__init__.py +0 -15
  103. unrealon_driver/src/services/llm/browser_llm_service.py +0 -363
  104. unrealon_driver/src/services/llm/llm.py +0 -195
  105. unrealon_driver/src/services/logger_service.py +0 -232
  106. unrealon_driver/src/services/metrics_service.py +0 -185
  107. unrealon_driver/src/services/scheduler_service.py +0 -489
  108. unrealon_driver/src/services/websocket_service.py +0 -362
  109. unrealon_driver/src/utils/__init__.py +0 -16
  110. unrealon_driver/src/utils/service_factory.py +0 -317
  111. unrealon_driver/src/utils/time_formatter.py +0 -338
  112. unrealon_llm/README.md +0 -44
  113. unrealon_llm/__init__.py +0 -26
  114. unrealon_llm/pyproject.toml +0 -154
  115. unrealon_llm/src/__init__.py +0 -228
  116. unrealon_llm/src/cli/__init__.py +0 -0
  117. unrealon_llm/src/core/__init__.py +0 -11
  118. unrealon_llm/src/core/smart_client.py +0 -438
  119. unrealon_llm/src/dto/__init__.py +0 -155
  120. unrealon_llm/src/dto/models/__init__.py +0 -0
  121. unrealon_llm/src/dto/models/config.py +0 -343
  122. unrealon_llm/src/dto/models/core.py +0 -328
  123. unrealon_llm/src/dto/models/enums.py +0 -123
  124. unrealon_llm/src/dto/models/html_analysis.py +0 -345
  125. unrealon_llm/src/dto/models/statistics.py +0 -473
  126. unrealon_llm/src/dto/models/translation.py +0 -383
  127. unrealon_llm/src/dto/models/type_conversion.py +0 -462
  128. unrealon_llm/src/dto/schemas/__init__.py +0 -0
  129. unrealon_llm/src/exceptions.py +0 -392
  130. unrealon_llm/src/llm_config/__init__.py +0 -20
  131. unrealon_llm/src/llm_config/logging_config.py +0 -178
  132. unrealon_llm/src/llm_logging/__init__.py +0 -42
  133. unrealon_llm/src/llm_logging/llm_events.py +0 -107
  134. unrealon_llm/src/llm_logging/llm_logger.py +0 -466
  135. unrealon_llm/src/managers/__init__.py +0 -15
  136. unrealon_llm/src/managers/cache_manager.py +0 -67
  137. unrealon_llm/src/managers/cost_manager.py +0 -107
  138. unrealon_llm/src/managers/request_manager.py +0 -298
  139. unrealon_llm/src/modules/__init__.py +0 -0
  140. unrealon_llm/src/modules/html_processor/__init__.py +0 -25
  141. unrealon_llm/src/modules/html_processor/base_processor.py +0 -415
  142. unrealon_llm/src/modules/html_processor/details_processor.py +0 -85
  143. unrealon_llm/src/modules/html_processor/listing_processor.py +0 -91
  144. unrealon_llm/src/modules/html_processor/models/__init__.py +0 -20
  145. unrealon_llm/src/modules/html_processor/models/processing_models.py +0 -40
  146. unrealon_llm/src/modules/html_processor/models/universal_model.py +0 -56
  147. unrealon_llm/src/modules/html_processor/processor.py +0 -102
  148. unrealon_llm/src/modules/llm/__init__.py +0 -0
  149. unrealon_llm/src/modules/translator/__init__.py +0 -0
  150. unrealon_llm/src/provider.py +0 -116
  151. unrealon_llm/src/utils/__init__.py +0 -95
  152. unrealon_llm/src/utils/common.py +0 -64
  153. unrealon_llm/src/utils/data_extractor.py +0 -188
  154. unrealon_llm/src/utils/html_cleaner.py +0 -767
  155. unrealon_llm/src/utils/language_detector.py +0 -308
  156. unrealon_llm/src/utils/models_cache.py +0 -592
  157. unrealon_llm/src/utils/smart_counter.py +0 -229
  158. unrealon_llm/src/utils/token_counter.py +0 -189
  159. unrealon_sdk/README.md +0 -25
  160. unrealon_sdk/__init__.py +0 -30
  161. unrealon_sdk/pyproject.toml +0 -231
  162. unrealon_sdk/src/__init__.py +0 -150
  163. unrealon_sdk/src/cli/__init__.py +0 -12
  164. unrealon_sdk/src/cli/commands/__init__.py +0 -22
  165. unrealon_sdk/src/cli/commands/benchmark.py +0 -42
  166. unrealon_sdk/src/cli/commands/diagnostics.py +0 -573
  167. unrealon_sdk/src/cli/commands/health.py +0 -46
  168. unrealon_sdk/src/cli/commands/integration.py +0 -498
  169. unrealon_sdk/src/cli/commands/reports.py +0 -43
  170. unrealon_sdk/src/cli/commands/security.py +0 -36
  171. unrealon_sdk/src/cli/commands/server.py +0 -483
  172. unrealon_sdk/src/cli/commands/servers.py +0 -56
  173. unrealon_sdk/src/cli/commands/tests.py +0 -55
  174. unrealon_sdk/src/cli/main.py +0 -126
  175. unrealon_sdk/src/cli/utils/reporter.py +0 -519
  176. unrealon_sdk/src/clients/openapi.yaml +0 -3347
  177. unrealon_sdk/src/clients/python_http/__init__.py +0 -3
  178. unrealon_sdk/src/clients/python_http/api_config.py +0 -228
  179. unrealon_sdk/src/clients/python_http/models/BaseModel.py +0 -12
  180. unrealon_sdk/src/clients/python_http/models/BroadcastDeliveryStats.py +0 -33
  181. unrealon_sdk/src/clients/python_http/models/BroadcastMessage.py +0 -17
  182. unrealon_sdk/src/clients/python_http/models/BroadcastMessageRequest.py +0 -35
  183. unrealon_sdk/src/clients/python_http/models/BroadcastPriority.py +0 -10
  184. unrealon_sdk/src/clients/python_http/models/BroadcastResponse.py +0 -21
  185. unrealon_sdk/src/clients/python_http/models/BroadcastResultResponse.py +0 -33
  186. unrealon_sdk/src/clients/python_http/models/BroadcastTarget.py +0 -11
  187. unrealon_sdk/src/clients/python_http/models/ConnectionStats.py +0 -27
  188. unrealon_sdk/src/clients/python_http/models/ConnectionsResponse.py +0 -21
  189. unrealon_sdk/src/clients/python_http/models/DeveloperMessageResponse.py +0 -23
  190. unrealon_sdk/src/clients/python_http/models/ErrorResponse.py +0 -25
  191. unrealon_sdk/src/clients/python_http/models/HTTPValidationError.py +0 -16
  192. unrealon_sdk/src/clients/python_http/models/HealthResponse.py +0 -23
  193. unrealon_sdk/src/clients/python_http/models/HealthStatus.py +0 -33
  194. unrealon_sdk/src/clients/python_http/models/LogLevel.py +0 -10
  195. unrealon_sdk/src/clients/python_http/models/LoggingRequest.py +0 -27
  196. unrealon_sdk/src/clients/python_http/models/LoggingResponse.py +0 -23
  197. unrealon_sdk/src/clients/python_http/models/MaintenanceMode.py +0 -9
  198. unrealon_sdk/src/clients/python_http/models/MaintenanceModeRequest.py +0 -33
  199. unrealon_sdk/src/clients/python_http/models/MaintenanceStatusResponse.py +0 -39
  200. unrealon_sdk/src/clients/python_http/models/ParserCommandRequest.py +0 -25
  201. unrealon_sdk/src/clients/python_http/models/ParserMessageResponse.py +0 -21
  202. unrealon_sdk/src/clients/python_http/models/ParserRegistrationRequest.py +0 -28
  203. unrealon_sdk/src/clients/python_http/models/ParserRegistrationResponse.py +0 -25
  204. unrealon_sdk/src/clients/python_http/models/ParserType.py +0 -10
  205. unrealon_sdk/src/clients/python_http/models/ProxyBlockRequest.py +0 -19
  206. unrealon_sdk/src/clients/python_http/models/ProxyEndpointResponse.py +0 -20
  207. unrealon_sdk/src/clients/python_http/models/ProxyListResponse.py +0 -19
  208. unrealon_sdk/src/clients/python_http/models/ProxyProvider.py +0 -10
  209. unrealon_sdk/src/clients/python_http/models/ProxyPurchaseRequest.py +0 -25
  210. unrealon_sdk/src/clients/python_http/models/ProxyResponse.py +0 -47
  211. unrealon_sdk/src/clients/python_http/models/ProxyRotationRequest.py +0 -23
  212. unrealon_sdk/src/clients/python_http/models/ProxyStatus.py +0 -10
  213. unrealon_sdk/src/clients/python_http/models/ProxyUsageRequest.py +0 -19
  214. unrealon_sdk/src/clients/python_http/models/ProxyUsageStatsResponse.py +0 -26
  215. unrealon_sdk/src/clients/python_http/models/ServiceRegistrationDto.py +0 -23
  216. unrealon_sdk/src/clients/python_http/models/ServiceStatsResponse.py +0 -31
  217. unrealon_sdk/src/clients/python_http/models/SessionStartRequest.py +0 -23
  218. unrealon_sdk/src/clients/python_http/models/SuccessResponse.py +0 -25
  219. unrealon_sdk/src/clients/python_http/models/SystemNotificationResponse.py +0 -23
  220. unrealon_sdk/src/clients/python_http/models/ValidationError.py +0 -18
  221. unrealon_sdk/src/clients/python_http/models/ValidationErrorResponse.py +0 -21
  222. unrealon_sdk/src/clients/python_http/models/WebSocketMetrics.py +0 -21
  223. unrealon_sdk/src/clients/python_http/models/__init__.py +0 -44
  224. unrealon_sdk/src/clients/python_http/services/None_service.py +0 -35
  225. unrealon_sdk/src/clients/python_http/services/ParserManagement_service.py +0 -190
  226. unrealon_sdk/src/clients/python_http/services/ProxyManagement_service.py +0 -289
  227. unrealon_sdk/src/clients/python_http/services/SocketLogging_service.py +0 -187
  228. unrealon_sdk/src/clients/python_http/services/SystemHealth_service.py +0 -119
  229. unrealon_sdk/src/clients/python_http/services/WebSocketAPI_service.py +0 -198
  230. unrealon_sdk/src/clients/python_http/services/__init__.py +0 -0
  231. unrealon_sdk/src/clients/python_http/services/admin_service.py +0 -125
  232. unrealon_sdk/src/clients/python_http/services/async_None_service.py +0 -35
  233. unrealon_sdk/src/clients/python_http/services/async_ParserManagement_service.py +0 -190
  234. unrealon_sdk/src/clients/python_http/services/async_ProxyManagement_service.py +0 -289
  235. unrealon_sdk/src/clients/python_http/services/async_SocketLogging_service.py +0 -189
  236. unrealon_sdk/src/clients/python_http/services/async_SystemHealth_service.py +0 -123
  237. unrealon_sdk/src/clients/python_http/services/async_WebSocketAPI_service.py +0 -200
  238. unrealon_sdk/src/clients/python_http/services/async_admin_service.py +0 -125
  239. unrealon_sdk/src/clients/python_websocket/__init__.py +0 -28
  240. unrealon_sdk/src/clients/python_websocket/client.py +0 -490
  241. unrealon_sdk/src/clients/python_websocket/events.py +0 -732
  242. unrealon_sdk/src/clients/python_websocket/example.py +0 -136
  243. unrealon_sdk/src/clients/python_websocket/types.py +0 -871
  244. unrealon_sdk/src/core/__init__.py +0 -64
  245. unrealon_sdk/src/core/client.py +0 -556
  246. unrealon_sdk/src/core/config.py +0 -465
  247. unrealon_sdk/src/core/exceptions.py +0 -239
  248. unrealon_sdk/src/core/metadata.py +0 -191
  249. unrealon_sdk/src/core/models.py +0 -142
  250. unrealon_sdk/src/core/types.py +0 -68
  251. unrealon_sdk/src/dto/__init__.py +0 -268
  252. unrealon_sdk/src/dto/authentication.py +0 -108
  253. unrealon_sdk/src/dto/cache.py +0 -208
  254. unrealon_sdk/src/dto/common.py +0 -19
  255. unrealon_sdk/src/dto/concurrency.py +0 -393
  256. unrealon_sdk/src/dto/events.py +0 -108
  257. unrealon_sdk/src/dto/health.py +0 -339
  258. unrealon_sdk/src/dto/load_balancing.py +0 -336
  259. unrealon_sdk/src/dto/logging.py +0 -230
  260. unrealon_sdk/src/dto/performance.py +0 -165
  261. unrealon_sdk/src/dto/rate_limiting.py +0 -295
  262. unrealon_sdk/src/dto/resource_pooling.py +0 -128
  263. unrealon_sdk/src/dto/structured_logging.py +0 -112
  264. unrealon_sdk/src/dto/task_scheduling.py +0 -121
  265. unrealon_sdk/src/dto/websocket.py +0 -55
  266. unrealon_sdk/src/enterprise/__init__.py +0 -59
  267. unrealon_sdk/src/enterprise/authentication.py +0 -401
  268. unrealon_sdk/src/enterprise/cache_manager.py +0 -578
  269. unrealon_sdk/src/enterprise/error_recovery.py +0 -494
  270. unrealon_sdk/src/enterprise/event_system.py +0 -549
  271. unrealon_sdk/src/enterprise/health_monitor.py +0 -747
  272. unrealon_sdk/src/enterprise/load_balancer.py +0 -964
  273. unrealon_sdk/src/enterprise/logging/__init__.py +0 -68
  274. unrealon_sdk/src/enterprise/logging/cleanup.py +0 -156
  275. unrealon_sdk/src/enterprise/logging/development.py +0 -744
  276. unrealon_sdk/src/enterprise/logging/service.py +0 -410
  277. unrealon_sdk/src/enterprise/multithreading_manager.py +0 -853
  278. unrealon_sdk/src/enterprise/performance_monitor.py +0 -539
  279. unrealon_sdk/src/enterprise/proxy_manager.py +0 -696
  280. unrealon_sdk/src/enterprise/rate_limiter.py +0 -652
  281. unrealon_sdk/src/enterprise/resource_pool.py +0 -763
  282. unrealon_sdk/src/enterprise/task_scheduler.py +0 -709
  283. unrealon_sdk/src/internal/__init__.py +0 -10
  284. unrealon_sdk/src/internal/command_router.py +0 -497
  285. unrealon_sdk/src/internal/connection_manager.py +0 -397
  286. unrealon_sdk/src/internal/http_client.py +0 -446
  287. unrealon_sdk/src/internal/websocket_client.py +0 -420
  288. unrealon_sdk/src/provider.py +0 -471
  289. unrealon_sdk/src/utils.py +0 -234
  290. /unrealon_browser/{src/cli → cli}/__init__.py +0 -0
  291. /unrealon_browser/{src/cli → cli}/interactive_mode.py +0 -0
  292. /unrealon_browser/{src/cli → cli}/main.py +0 -0
  293. /unrealon_browser/{src/core → core}/__init__.py +0 -0
  294. /unrealon_browser/{src/dto → dto}/__init__.py +0 -0
  295. /unrealon_browser/{src/dto → dto}/models/config.py +0 -0
  296. /unrealon_browser/{src/dto → dto}/models/core.py +0 -0
  297. /unrealon_browser/{src/dto → dto}/models/dataclasses.py +0 -0
  298. /unrealon_browser/{src/dto → dto}/models/detection.py +0 -0
  299. /unrealon_browser/{src/dto → dto}/models/enums.py +0 -0
  300. /unrealon_browser/{src/dto → dto}/models/statistics.py +0 -0
  301. /unrealon_browser/{src/managers → managers}/__init__.py +0 -0
  302. /unrealon_browser/{src/managers → managers}/stealth.py +0 -0
@@ -1,10 +0,0 @@
1
- """
2
- Internal SDK components for UnrealOn SDK v1.0
3
-
4
- This module contains internal implementation details that are not part
5
- of the public API. These components handle low-level communication,
6
- command routing, and connection management.
7
- """
8
-
9
- # Internal components are not exported in public API
10
- # They are only used internally by the SDK
@@ -1,497 +0,0 @@
1
- """
2
- Command routing and handling for UnrealOn SDK v1.0
3
-
4
- Provides intelligent command routing with:
5
- - Type-safe command dispatch using Pydantic v2 models
6
- - Priority handling with configurable strategies
7
- - Timeout management with automatic cancellation
8
- - Performance monitoring and metrics collection
9
- - Concurrent execution control with resource management
10
- """
11
-
12
- import asyncio
13
- import logging
14
- from typing import Dict, Callable, Optional, Awaitable, Any, Union
15
- from datetime import datetime, timedelta
16
- from dataclasses import dataclass
17
- from enum import Enum
18
-
19
- # Use auto-generated models only - no custom models!
20
- from unrealon_sdk.src.clients.python_websocket.types import ParserCommandEvent, CommandStatus
21
- from unrealon_sdk.src.clients.python_http.models import SuccessResponse, ErrorResponse
22
-
23
- # SDK metadata models
24
- from unrealon_sdk.src.core.metadata import ExecutionInfo, RouterStatistics
25
- from unrealon_sdk.src.core.exceptions import CommandError, TimeoutError as SDKTimeoutError
26
- from unrealon_sdk.src.utils import generate_correlation_id
27
-
28
-
29
- class CommandPriority(str, Enum):
30
- """Command priority levels for queue ordering."""
31
-
32
- LOW = "low"
33
- NORMAL = "normal"
34
- HIGH = "high"
35
- URGENT = "urgent"
36
-
37
-
38
- @dataclass
39
- class CommandExecution:
40
- """Tracks command execution state with comprehensive monitoring."""
41
-
42
- command: ParserCommandEvent
43
- handler: Callable
44
- started_at: datetime
45
- correlation_id: str
46
- task: Optional[asyncio.Task] = None
47
- timeout_task: Optional[asyncio.Task] = None
48
- priority: CommandPriority = CommandPriority.NORMAL
49
-
50
- @property
51
- def execution_time_ms(self) -> float:
52
- """Get current execution time in milliseconds."""
53
- return (datetime.utcnow() - self.started_at).total_seconds() * 1000
54
-
55
- @property
56
- def is_running(self) -> bool:
57
- """Check if command is currently running."""
58
- return self.task is not None and not self.task.done()
59
-
60
- @property
61
- def is_timed_out(self) -> bool:
62
- """Check if command has timed out."""
63
- return (
64
- self.timeout_task is not None
65
- and self.timeout_task.done()
66
- and not self.timeout_task.cancelled()
67
- )
68
-
69
-
70
- class CommandRouter:
71
- """
72
- Routes and manages command execution with enterprise-grade capabilities.
73
-
74
- Features:
75
- - Priority-based command queuing with multiple strategies
76
- - Concurrent execution control with resource management
77
- - Comprehensive timeout handling with cancellation
78
- - Performance metrics collection and analysis
79
- - Error correlation and recovery recommendations
80
- - Circuit breaker pattern for failed handlers
81
-
82
- Follows Layer 2 requirements from development checklist:
83
- - Type-safe command dispatch
84
- - Automatic retry with exponential backoff
85
- - Resource monitoring and optimization
86
- - Error recovery and circuit breaker patterns
87
- """
88
-
89
- def __init__(self, max_concurrent_commands: int = 10, default_timeout: int = 300):
90
- """
91
- Initialize command router.
92
-
93
- Args:
94
- max_concurrent_commands: Maximum concurrent command executions
95
- default_timeout: Default command timeout in seconds
96
- """
97
- self.logger = logging.getLogger("unrealon_sdk.command_router")
98
-
99
- # Command handlers registry
100
- self._handlers: Dict[str, Callable] = {}
101
-
102
- # Execution tracking
103
- self._active_executions: Dict[str, CommandExecution] = {}
104
- self._command_queue: asyncio.PriorityQueue = asyncio.PriorityQueue()
105
-
106
- # Configuration
107
- self._max_concurrent_commands = max_concurrent_commands
108
- self._default_timeout = default_timeout
109
-
110
- # Statistics and monitoring
111
- self._total_commands = 0
112
- self._successful_commands = 0
113
- self._failed_commands = 0
114
- self._timeout_commands = 0
115
- self._cancelled_commands = 0
116
-
117
- # Performance tracking
118
- self._total_execution_time = 0.0
119
- self._min_execution_time = float("inf")
120
- self._max_execution_time = 0.0
121
-
122
- # Circuit breaker for handlers
123
- self._handler_failures: Dict[str, int] = {}
124
- self._handler_last_failure: Dict[str, datetime] = {}
125
- self._circuit_breaker_threshold = 5
126
- self._circuit_breaker_timeout = timedelta(minutes=5)
127
-
128
- self.logger.debug(
129
- f"CommandRouter initialized with max_concurrent={max_concurrent_commands}"
130
- )
131
-
132
- def register_handler(
133
- self,
134
- command_type: str,
135
- handler: Callable[[ParserCommandEvent], Awaitable[Union[SuccessResponse, ErrorResponse]]],
136
- ) -> None:
137
- """
138
- Register a command handler.
139
-
140
- Args:
141
- command_type: Type of command to handle
142
- handler: Async function to handle the command
143
- """
144
- self._handlers[command_type] = handler
145
-
146
- # Reset circuit breaker for this handler
147
- self._handler_failures[command_type] = 0
148
- if command_type in self._handler_last_failure:
149
- del self._handler_last_failure[command_type]
150
-
151
- self.logger.debug(f"Registered handler for command type: {command_type}")
152
-
153
- def unregister_handler(self, command_type: str) -> None:
154
- """
155
- Unregister a command handler.
156
-
157
- Args:
158
- command_type: Type of command to unregister
159
- """
160
- if command_type in self._handlers:
161
- del self._handlers[command_type]
162
- self.logger.debug(f"Unregistered handler for command type: {command_type}")
163
-
164
- async def route_command(
165
- self, command: ParserCommandEvent
166
- ) -> Union[SuccessResponse, ErrorResponse]:
167
- """
168
- Route and execute a command with comprehensive error handling.
169
-
170
- Args:
171
- command: Command to execute
172
-
173
- Returns:
174
- Command response
175
-
176
- Raises:
177
- CommandError: If no handler exists for command type or execution fails
178
- """
179
- self._total_commands += 1
180
- start_time = datetime.utcnow()
181
- correlation_id = generate_correlation_id()
182
-
183
- self.logger.info(
184
- f"Routing command {command.command_id} of type {command.command_type}",
185
- extra={"correlation_id": correlation_id},
186
- )
187
-
188
- try:
189
- # Validate command and handler
190
- await self._validate_command(command)
191
-
192
- # Check circuit breaker for handler
193
- if self._is_circuit_breaker_open(command.command_type):
194
- raise CommandError(
195
- f"Circuit breaker open for command type: {command.command_type}",
196
- error_code="CIRCUIT_BREAKER_OPEN",
197
- )
198
-
199
- # Check concurrent execution limit
200
- if len(self._active_executions) >= self._max_concurrent_commands:
201
- raise CommandError(
202
- f"Maximum concurrent commands limit reached ({self._max_concurrent_commands})",
203
- error_code="CONCURRENT_LIMIT_EXCEEDED",
204
- )
205
-
206
- # Execute command
207
- response = await self._execute_command(command, correlation_id, start_time)
208
-
209
- # Update statistics for successful execution
210
- self._update_success_statistics(start_time)
211
-
212
- return response
213
-
214
- except Exception as e:
215
- # Update statistics for failed execution
216
- self._update_failure_statistics(command.command_type, start_time)
217
-
218
- # Create error response
219
- error_response = self._create_error_response(command, e, correlation_id, start_time)
220
-
221
- self.logger.error(
222
- f"Command {command.command_id} failed: {e}",
223
- extra={"correlation_id": correlation_id},
224
- )
225
-
226
- return error_response
227
-
228
- async def _validate_command(self, command: ParserCommandEvent) -> None:
229
- """Validate command before execution."""
230
- if command.command_type not in self._handlers:
231
- raise CommandError(
232
- f"No handler registered for command type: {command.command_type}",
233
- error_code="HANDLER_NOT_FOUND",
234
- )
235
-
236
- # Validate command structure using Pydantic (already validated during parsing)
237
- # Additional business logic validation can be added here
238
-
239
- def _is_circuit_breaker_open(self, command_type: str) -> bool:
240
- """Check if circuit breaker is open for a command type."""
241
- failure_count = self._handler_failures.get(command_type, 0)
242
- last_failure = self._handler_last_failure.get(command_type)
243
-
244
- if failure_count < self._circuit_breaker_threshold:
245
- return False
246
-
247
- if last_failure is None:
248
- return False
249
-
250
- # Check if timeout has passed
251
- if datetime.utcnow() - last_failure > self._circuit_breaker_timeout:
252
- # Reset circuit breaker
253
- self._handler_failures[command_type] = 0
254
- del self._handler_last_failure[command_type]
255
- return False
256
-
257
- return True
258
-
259
- async def _execute_command(
260
- self, command: ParserCommandEvent, correlation_id: str, start_time: datetime
261
- ) -> Union[SuccessResponse, ErrorResponse]:
262
- """Execute command with timeout and cancellation support."""
263
-
264
- handler = self._handlers[command.command_type]
265
- execution = CommandExecution(
266
- command=command, handler=handler, started_at=start_time, correlation_id=correlation_id
267
- )
268
-
269
- # Add to active executions
270
- self._active_executions[command.command_id] = execution
271
-
272
- try:
273
- # Create execution task
274
- execution.task = asyncio.create_task(handler(command))
275
-
276
- # Create timeout task
277
- timeout_seconds = command.timeout_seconds or self._default_timeout
278
- execution.timeout_task = asyncio.create_task(
279
- self._handle_timeout(command, timeout_seconds)
280
- )
281
-
282
- # Wait for either completion or timeout
283
- done, pending = await asyncio.wait(
284
- [execution.task, execution.timeout_task], return_when=asyncio.FIRST_COMPLETED
285
- )
286
-
287
- # Cancel pending tasks
288
- for task in pending:
289
- task.cancel()
290
- try:
291
- await task
292
- except asyncio.CancelledError:
293
- pass
294
-
295
- # Check if command completed or timed out
296
- if execution.task in done:
297
- # Command completed successfully
298
- result_data = await execution.task
299
- execution_time = execution.execution_time_ms
300
-
301
- self._successful_commands += 1
302
-
303
- response = SuccessResponse(
304
- success=True,
305
- message="Command completed successfully",
306
- data=result_data,
307
- timestamp=datetime.utcnow().isoformat(),
308
- )
309
-
310
- self.logger.info(
311
- f"Command {command.command_id} completed in {execution_time:.2f}ms",
312
- extra={"correlation_id": correlation_id},
313
- )
314
-
315
- return response
316
-
317
- else:
318
- # Command timed out
319
- execution_time = execution.execution_time_ms
320
- self._timeout_commands += 1
321
-
322
- response = ErrorResponse(
323
- success=False,
324
- message=f"Command timed out after {timeout_seconds} seconds",
325
- error_code="COMMAND_TIMEOUT",
326
- timestamp=datetime.utcnow().isoformat(),
327
- )
328
-
329
- self.logger.warning(
330
- f"Command {command.command_id} timed out after {execution_time:.2f}ms",
331
- extra={"correlation_id": correlation_id},
332
- )
333
-
334
- return response
335
-
336
- finally:
337
- # Cleanup execution tracking
338
- if command.command_id in self._active_executions:
339
- del self._active_executions[command.command_id]
340
-
341
- async def _handle_timeout(self, command: ParserCommandEvent, timeout_seconds: int) -> None:
342
- """Handle command timeout."""
343
- await asyncio.sleep(timeout_seconds)
344
- self.logger.warning(
345
- f"Command {command.command_id} exceeded timeout of {timeout_seconds} seconds"
346
- )
347
-
348
- def _update_success_statistics(self, start_time: datetime) -> None:
349
- """Update statistics for successful command execution."""
350
- execution_time = (datetime.utcnow() - start_time).total_seconds() * 1000
351
-
352
- self._total_execution_time += execution_time
353
- self._min_execution_time = min(self._min_execution_time, execution_time)
354
- self._max_execution_time = max(self._max_execution_time, execution_time)
355
-
356
- def _update_failure_statistics(self, command_type: str, start_time: datetime) -> None:
357
- """Update statistics for failed command execution."""
358
- execution_time = (datetime.utcnow() - start_time).total_seconds() * 1000
359
-
360
- self._failed_commands += 1
361
- self._total_execution_time += execution_time
362
-
363
- # Update circuit breaker
364
- self._handler_failures[command_type] = self._handler_failures.get(command_type, 0) + 1
365
- self._handler_last_failure[command_type] = datetime.utcnow()
366
-
367
- def _create_error_response(
368
- self,
369
- command: ParserCommandEvent,
370
- error: Exception,
371
- correlation_id: str,
372
- start_time: datetime,
373
- ) -> ErrorResponse:
374
- """Create structured error response."""
375
- execution_time = (datetime.utcnow() - start_time).total_seconds() * 1000
376
-
377
- # Determine error details
378
- if isinstance(error, CommandError):
379
- error_message = str(error)
380
- error_code = getattr(error, "error_code", "COMMAND_ERROR")
381
- elif isinstance(error, TimeoutError):
382
- error_message = str(error)
383
- error_code = "TIMEOUT_ERROR"
384
- else:
385
- error_message = f"Unexpected error: {str(error)}"
386
- error_code = "INTERNAL_ERROR"
387
-
388
- return ErrorResponse(
389
- success=False,
390
- message=error_message,
391
- error_code=error_code,
392
- timestamp=datetime.utcnow().isoformat(),
393
- )
394
-
395
- def get_registered_handlers(self) -> Dict[str, str]:
396
- """Get information about registered handlers."""
397
- return {
398
- cmd_type: handler.__name__ if hasattr(handler, "__name__") else str(handler)
399
- for cmd_type, handler in self._handlers.items()
400
- }
401
-
402
- def get_active_executions(self) -> Dict[str, ExecutionInfo]:
403
- """Get information about currently executing commands."""
404
- return {
405
- cmd_id: ExecutionInfo(
406
- command_id=cmd_id,
407
- command_type=execution.command.command_type,
408
- status=(
409
- "running"
410
- if execution.is_running
411
- else ("timeout" if execution.is_timed_out else "completed")
412
- ),
413
- start_time=execution.started_at,
414
- duration_ms=execution.execution_time_ms,
415
- )
416
- for cmd_id, execution in self._active_executions.items()
417
- }
418
-
419
- def get_statistics(self) -> RouterStatistics:
420
- """Get comprehensive command execution statistics."""
421
- avg_execution_time = (
422
- self._total_execution_time / self._total_commands if self._total_commands > 0 else 0
423
- )
424
-
425
- success_rate = (
426
- self._successful_commands / self._total_commands * 100
427
- if self._total_commands > 0
428
- else 0
429
- )
430
-
431
- return RouterStatistics(
432
- total_commands=self._total_commands,
433
- successful_commands=self._successful_commands,
434
- failed_commands=self._failed_commands,
435
- average_execution_time_ms=avg_execution_time,
436
- active_executions=len(self._active_executions),
437
- success_rate=success_rate,
438
- )
439
-
440
- async def cancel_command(self, command_id: str) -> bool:
441
- """
442
- Cancel an active command execution.
443
-
444
- Args:
445
- command_id: ID of command to cancel
446
-
447
- Returns:
448
- True if command was cancelled, False if not found
449
- """
450
- if command_id not in self._active_executions:
451
- return False
452
-
453
- execution = self._active_executions[command_id]
454
-
455
- # Cancel tasks
456
- if execution.task and not execution.task.done():
457
- execution.task.cancel()
458
-
459
- if execution.timeout_task and not execution.timeout_task.done():
460
- execution.timeout_task.cancel()
461
-
462
- # Update statistics
463
- self._cancelled_commands += 1
464
-
465
- # Remove from tracking
466
- del self._active_executions[command_id]
467
-
468
- self.logger.info(f"Cancelled command {command_id}")
469
- return True
470
-
471
- async def shutdown(self) -> None:
472
- """Shutdown the command router and cancel all active executions."""
473
- self.logger.info("Shutting down command router...")
474
-
475
- # Cancel all active executions
476
- active_commands = list(self._active_executions.keys())
477
- for command_id in active_commands:
478
- await self.cancel_command(command_id)
479
-
480
- self.logger.info(
481
- f"Command router shutdown complete. Cancelled {len(active_commands)} commands"
482
- )
483
-
484
- def reset_statistics(self) -> None:
485
- """Reset all statistics (useful for testing)."""
486
- self._total_commands = 0
487
- self._successful_commands = 0
488
- self._failed_commands = 0
489
- self._timeout_commands = 0
490
- self._cancelled_commands = 0
491
- self._total_execution_time = 0.0
492
- self._min_execution_time = float("inf")
493
- self._max_execution_time = 0.0
494
- self._handler_failures.clear()
495
- self._handler_last_failure.clear()
496
-
497
- self.logger.debug("Command router statistics reset")