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,489 +0,0 @@
1
- """
2
- Scheduler Service for UnrealOn Driver v3.0
3
-
4
- Full scheduler implementation with human-readable intervals and enterprise monitoring.
5
- Based on the proven architecture from v2.0 with modern enhancements.
6
-
7
- CRITICAL REQUIREMENTS COMPLIANCE:
8
- - ✅ Absolute imports only
9
- - ✅ Pydantic v2 models everywhere
10
- - ✅ Complete type annotations
11
- - ✅ Full unrealon_sdk integration
12
- """
13
-
14
- import asyncio
15
- import re
16
- import time
17
- from datetime import datetime, timedelta
18
- from typing import Any, Dict, List, Optional, Callable, Union
19
- from enum import Enum
20
-
21
- from pydantic import BaseModel, Field, ConfigDict, field_validator
22
-
23
- from unrealon_sdk.src.dto.task_scheduling import TaskSchedulerConfig, ScheduleInfo, TaskProgress
24
- from unrealon_sdk.src.enterprise.logging.development import get_development_logger
25
- from unrealon_sdk.src.dto.logging import SDKContext, SDKEventType
26
-
27
- from unrealon_driver.src.core.exceptions import SchedulingError
28
- from unrealon_driver.src.dto.services import SchedulerConfig
29
-
30
-
31
- class ScheduledTaskStatus(str, Enum):
32
- """Task status enumeration."""
33
-
34
- PENDING = "pending"
35
- RUNNING = "running"
36
- COMPLETED = "completed"
37
- FAILED = "failed"
38
- CANCELLED = "cancelled"
39
-
40
-
41
- class ScheduleExpression(BaseModel):
42
- """Pydantic model for schedule expression parsing."""
43
-
44
- model_config = ConfigDict(validate_assignment=True, extra="forbid")
45
-
46
- expression: str = Field(..., description="Human-readable schedule expression")
47
- interval_seconds: int = Field(..., ge=1, description="Parsed interval in seconds")
48
-
49
- @field_validator("expression")
50
- @classmethod
51
- def validate_expression(cls, v: str) -> str:
52
- """Validate schedule expression format."""
53
- v = v.lower().strip()
54
-
55
- # Allowed patterns
56
- patterns = [
57
- r"^\d+[smhd]$", # 30s, 5m, 1h, 2d
58
- r"^(minutely|hourly|daily|weekly|monthly)$", # Named intervals
59
- r"^(weekdays|weekends|business_hours)$", # Special intervals
60
- ]
61
-
62
- if not any(re.match(pattern, v) for pattern in patterns):
63
- raise ValueError(f"Invalid schedule expression: {v}")
64
-
65
- return v
66
-
67
- @classmethod
68
- def parse(cls, expression: str) -> "ScheduleExpression":
69
- """Parse human-readable expression to seconds."""
70
- expr = expression.lower().strip()
71
-
72
- # Parse time units (30s, 5m, 1h, 2d)
73
- if re.match(r"^\d+[smhd]$", expr):
74
- number = int(expr[:-1])
75
- unit = expr[-1]
76
-
77
- multipliers = {"s": 1, "m": 60, "h": 3600, "d": 86400}
78
-
79
- seconds = number * multipliers[unit]
80
- return cls(expression=expression, interval_seconds=seconds)
81
-
82
- # Named intervals
83
- named_intervals = {
84
- "minutely": 60,
85
- "hourly": 3600,
86
- "daily": 86400,
87
- "weekly": 604800,
88
- "monthly": 2592000, # 30 days
89
- "weekdays": 86400, # Daily but only weekdays
90
- "weekends": 86400, # Daily but only weekends
91
- "business_hours": 3600, # Hourly during business hours
92
- }
93
-
94
- if expr in named_intervals:
95
- return cls(expression=expression, interval_seconds=named_intervals[expr])
96
-
97
- raise ValueError(f"Unsupported schedule expression: {expression}")
98
-
99
-
100
- class ScheduledTask(BaseModel):
101
- """Pydantic model for scheduled task configuration."""
102
-
103
- model_config = ConfigDict(
104
- validate_assignment=True, extra="forbid", arbitrary_types_allowed=True
105
- )
106
-
107
- task_id: str = Field(..., description="Unique task identifier")
108
- name: str = Field(..., description="Human-readable task name")
109
- func: Callable = Field(..., description="Async function to execute")
110
- schedule: ScheduleExpression = Field(..., description="Schedule expression")
111
-
112
- # Optional configuration
113
- enabled: bool = Field(default=True, description="Whether task is enabled")
114
- max_runs: Optional[int] = Field(default=None, ge=1, description="Maximum number of runs")
115
- timeout: int = Field(default=300, ge=1, description="Task timeout in seconds")
116
- retry_attempts: int = Field(default=3, ge=0, description="Number of retry attempts")
117
- retry_delay: float = Field(default=1.0, ge=0, description="Delay between retries")
118
-
119
- # Runtime tracking
120
- status: ScheduledTaskStatus = Field(default=ScheduledTaskStatus.PENDING)
121
- runs_count: int = Field(default=0, ge=0, description="Number of completed runs")
122
- last_run: Optional[datetime] = Field(default=None, description="Last execution time")
123
- next_run: Optional[datetime] = Field(default=None, description="Next scheduled execution")
124
- last_duration: Optional[float] = Field(default=None, description="Last execution duration")
125
- last_error: Optional[str] = Field(default=None, description="Last error message")
126
-
127
-
128
- class SchedulerService:
129
- """
130
- 📅 Scheduler Service - Enterprise Task Scheduling
131
-
132
- Human-readable scheduling with monitoring and enterprise features:
133
- - Natural language intervals ("30m", "1h", "daily")
134
- - Smart load balancing with jitter
135
- - Error recovery and retries
136
- - Health monitoring and alerting
137
- - Production-ready reliability
138
- """
139
-
140
- def __init__(
141
- self, config: SchedulerConfig, logger: Optional[Any] = None, metrics: Optional[Any] = None
142
- ):
143
- """Initialize scheduler service."""
144
- self.config = config
145
- self.logger = logger
146
- self.metrics = metrics
147
-
148
- # ✅ DEVELOPMENT LOGGER INTEGRATION (CRITICAL REQUIREMENT)
149
- self.dev_logger = get_development_logger()
150
-
151
- # Scheduler state
152
- self._tasks: Dict[str, ScheduledTask] = {}
153
- self._running_tasks: Dict[str, asyncio.Task] = {}
154
- self._is_running = False
155
- self._shutdown_event = asyncio.Event()
156
-
157
- # Configuration - SchedulerConfig is a Pydantic object
158
- self.max_concurrent = config.max_concurrent_tasks
159
- self.enable_jitter = config.enable_jitter
160
- self.jitter_range = config.jitter_range
161
-
162
- # Log initialization with development logger
163
- if self.dev_logger:
164
- self.dev_logger.log_info(
165
- SDKEventType.COMPONENT_CREATED,
166
- "Scheduler service initialized",
167
- context=SDKContext(
168
- parser_id=self.config.parser_id,
169
- component_name="Scheduler",
170
- layer_name="UnrealOn_Driver",
171
- metadata={
172
- "max_concurrent": self.max_concurrent,
173
- "enable_jitter": self.enable_jitter,
174
- },
175
- ),
176
- )
177
-
178
- def add_task(
179
- self, task_id: str, name: str, func: Callable, every: str, **kwargs
180
- ) -> ScheduledTask:
181
- """
182
- Add a new scheduled task.
183
-
184
- Args:
185
- task_id: Unique task identifier
186
- name: Human-readable task name
187
- func: Async function to execute
188
- every: Schedule expression ("30m", "1h", "daily", etc.)
189
- **kwargs: Additional task configuration
190
-
191
- Returns:
192
- Created ScheduledTask
193
-
194
- Example:
195
- scheduler.add_task(
196
- "parse_news",
197
- "Parse News Data",
198
- my_parse_function,
199
- every="30m",
200
- max_runs=100,
201
- timeout=60
202
- )
203
- """
204
- try:
205
- # Parse schedule expression
206
- schedule = ScheduleExpression.parse(every)
207
-
208
- # Create task
209
- task = ScheduledTask(task_id=task_id, name=name, func=func, schedule=schedule, **kwargs)
210
-
211
- # Calculate next run time
212
- task.next_run = self._calculate_next_run(task)
213
-
214
- # Store task
215
- self._tasks[task_id] = task
216
-
217
- if self.logger:
218
- self.logger.info(f"📅 Added scheduled task: {name} (every {every})")
219
-
220
- return task
221
-
222
- except Exception as e:
223
- raise SchedulingError(f"Failed to add task {task_id}: {e}", schedule_expression=every)
224
-
225
- def remove_task(self, task_id: str) -> bool:
226
- """Remove a scheduled task."""
227
- if task_id in self._tasks:
228
- # Cancel running task if any
229
- if task_id in self._running_tasks:
230
- self._running_tasks[task_id].cancel()
231
- del self._running_tasks[task_id]
232
-
233
- # Remove from schedule
234
- del self._tasks[task_id]
235
-
236
- if self.logger:
237
- self.logger.info(f"📅 Removed scheduled task: {task_id}")
238
-
239
- return True
240
-
241
- return False
242
-
243
- async def start(self):
244
- """Start the scheduler."""
245
- if self._is_running:
246
- if self.logger:
247
- self.logger.warning("Scheduler already running")
248
- return
249
-
250
- self._is_running = True
251
- self._shutdown_event.clear()
252
-
253
- if self.logger:
254
- self.logger.info(f"📅 Starting scheduler with {len(self._tasks)} tasks")
255
-
256
- try:
257
- # Main scheduler loop
258
- while self._is_running:
259
- await self._scheduler_tick()
260
- await asyncio.sleep(1) # Check every second
261
-
262
- except asyncio.CancelledError:
263
- if self.logger:
264
- self.logger.info("📅 Scheduler cancelled")
265
- except Exception as e:
266
- if self.logger:
267
- self.logger.error(f"❌ Scheduler error: {e}")
268
- raise
269
- finally:
270
- await self._cleanup_running_tasks()
271
-
272
- async def stop(self):
273
- """Stop the scheduler gracefully."""
274
- if not self._is_running:
275
- return
276
-
277
- if self.logger:
278
- self.logger.info("📅 Stopping scheduler...")
279
-
280
- self._is_running = False
281
- self._shutdown_event.set()
282
-
283
- # Wait for running tasks to complete
284
- await self._cleanup_running_tasks()
285
-
286
- if self.logger:
287
- self.logger.info("✅ Scheduler stopped")
288
-
289
- async def _scheduler_tick(self):
290
- """Single scheduler tick - check and execute due tasks."""
291
- now = datetime.now()
292
-
293
- # Find tasks that are due
294
- due_tasks = [
295
- task
296
- for task in self._tasks.values()
297
- if (
298
- task.enabled
299
- and task.next_run
300
- and task.next_run <= now
301
- and task.task_id not in self._running_tasks
302
- and (task.max_runs is None or task.runs_count < task.max_runs)
303
- )
304
- ]
305
-
306
- # Check concurrent task limit
307
- available_slots = self.max_concurrent - len(self._running_tasks)
308
- due_tasks = due_tasks[:available_slots]
309
-
310
- # Execute due tasks
311
- for task in due_tasks:
312
- await self._execute_task(task)
313
-
314
- async def _execute_task(self, task: ScheduledTask):
315
- """Execute a scheduled task."""
316
- if self.logger:
317
- self.logger.info(f"🚀 Executing task: {task.name}")
318
-
319
- # Create execution coroutine
320
- execution_coro = self._run_task_with_monitoring(task)
321
-
322
- # Start task
323
- task_handle = asyncio.create_task(execution_coro)
324
- self._running_tasks[task.task_id] = task_handle
325
-
326
- # Update task status
327
- task.status = ScheduledTaskStatus.RUNNING
328
- task.last_run = datetime.now()
329
-
330
- async def _run_task_with_monitoring(self, task: ScheduledTask):
331
- """Run task with full monitoring and error handling."""
332
- start_time = time.time()
333
-
334
- try:
335
- # Execute with timeout
336
- await asyncio.wait_for(task.func(), timeout=task.timeout)
337
-
338
- # Task completed successfully
339
- duration = time.time() - start_time
340
- task.status = ScheduledTaskStatus.COMPLETED
341
- task.runs_count += 1
342
- task.last_duration = duration
343
- task.last_error = None
344
-
345
- # Calculate next run
346
- task.next_run = self._calculate_next_run(task)
347
-
348
- if self.logger:
349
- self.logger.info(f"✅ Task completed: {task.name} ({duration:.2f}s)")
350
-
351
- # Record metrics
352
- if self.metrics:
353
- self.metrics.record_operation(
354
- service="scheduler",
355
- operation="task_execution",
356
- duration=duration,
357
- result_count=1,
358
- )
359
-
360
- except asyncio.TimeoutError:
361
- duration = time.time() - start_time
362
- error_msg = f"Task timeout after {duration:.2f}s"
363
-
364
- task.status = ScheduledTaskStatus.FAILED
365
- task.last_duration = duration
366
- task.last_error = error_msg
367
-
368
- if self.logger:
369
- self.logger.error(f"⏰ Task timeout: {task.name}")
370
-
371
- await self._handle_task_failure(task, error_msg)
372
-
373
- except Exception as e:
374
- duration = time.time() - start_time
375
- error_msg = str(e)
376
-
377
- task.status = ScheduledTaskStatus.FAILED
378
- task.last_duration = duration
379
- task.last_error = error_msg
380
-
381
- if self.logger:
382
- self.logger.error(f"❌ Task failed: {task.name} - {error_msg}")
383
-
384
- await self._handle_task_failure(task, error_msg)
385
-
386
- finally:
387
- # Remove from running tasks
388
- if task.task_id in self._running_tasks:
389
- del self._running_tasks[task.task_id]
390
-
391
- async def _handle_task_failure(self, task: ScheduledTask, error_msg: str):
392
- """Handle task failure with retry logic."""
393
- # TODO: Implement retry logic if needed
394
- # Calculate next run even for failed tasks
395
- task.next_run = self._calculate_next_run(task)
396
-
397
- # Record error metrics
398
- if self.metrics:
399
- self.metrics.record_operation(
400
- service="scheduler",
401
- operation="task_execution",
402
- duration=task.last_duration or 0,
403
- result_count=0,
404
- error=error_msg,
405
- )
406
-
407
- def _calculate_next_run(self, task: ScheduledTask) -> datetime:
408
- """Calculate next run time for a task."""
409
- now = datetime.now()
410
- base_next = now + timedelta(seconds=task.schedule.interval_seconds)
411
-
412
- # Apply jitter if enabled
413
- if self.enable_jitter:
414
- jitter_seconds = task.schedule.interval_seconds * self.jitter_range
415
- import random
416
-
417
- jitter = random.uniform(-jitter_seconds, jitter_seconds)
418
- base_next += timedelta(seconds=jitter)
419
-
420
- return base_next
421
-
422
- async def _cleanup_running_tasks(self):
423
- """Clean up running tasks during shutdown."""
424
- if not self._running_tasks:
425
- return
426
-
427
- if self.logger:
428
- self.logger.info(f"🧹 Cleaning up {len(self._running_tasks)} running tasks...")
429
-
430
- # Cancel all running tasks
431
- for task_id, task_handle in self._running_tasks.items():
432
- task_handle.cancel()
433
-
434
- # Wait for cancellation
435
- if self._running_tasks:
436
- await asyncio.gather(*self._running_tasks.values(), return_exceptions=True)
437
-
438
- self._running_tasks.clear()
439
-
440
- # ==========================================
441
- # STATUS AND MONITORING
442
- # ==========================================
443
-
444
- def get_task_status(self, task_id: str) -> Optional[dict]:
445
- """Get status of a specific task."""
446
- if task_id not in self._tasks:
447
- return None
448
-
449
- task = self._tasks[task_id]
450
- return {
451
- "task_id": task.task_id,
452
- "name": task.name,
453
- "status": task.status.value,
454
- "enabled": task.enabled,
455
- "runs_count": task.runs_count,
456
- "last_run": task.last_run.isoformat() if task.last_run else None,
457
- "next_run": task.next_run.isoformat() if task.next_run else None,
458
- "last_duration": task.last_duration,
459
- "last_error": task.last_error,
460
- "schedule": task.schedule.expression,
461
- "interval_seconds": task.schedule.interval_seconds,
462
- }
463
-
464
- def get_all_tasks_status(self) -> dict:
465
- """Get status of all tasks."""
466
- return {task_id: self.get_task_status(task_id) for task_id in self._tasks}
467
-
468
- async def health_check(self) -> dict:
469
- """Check scheduler service health."""
470
- return {
471
- "status": "healthy" if self._is_running else "stopped",
472
- "is_running": self._is_running,
473
- "total_tasks": len(self._tasks),
474
- "enabled_tasks": sum(1 for t in self._tasks.values() if t.enabled),
475
- "running_tasks": len(self._running_tasks),
476
- "max_concurrent": self.max_concurrent,
477
- "tasks": self.get_all_tasks_status(),
478
- }
479
-
480
- async def cleanup(self):
481
- """Clean up scheduler resources."""
482
- await self.stop()
483
- self._tasks.clear()
484
-
485
- if self.logger:
486
- self.logger.info("Scheduler service cleanup completed")
487
-
488
- def __repr__(self) -> str:
489
- return f"<SchedulerService(running={self._is_running}, tasks={len(self._tasks)})>"