unrealon 1.0.9__py3-none-any.whl → 1.1.1__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.1.dist-info/METADATA +722 -0
  3. unrealon-1.1.1.dist-info/RECORD +82 -0
  4. {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info}/WHEEL +1 -1
  5. unrealon-1.1.1.dist-info/entry_points.txt +9 -0
  6. {unrealon-1.0.9.dist-info → unrealon-1.1.1.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
@@ -0,0 +1,412 @@
1
+ """
2
+ Error Manager - Smart error handling and retry logic with Pydantic v2
3
+
4
+ Strict compliance with CRITICAL_REQUIREMENTS.md:
5
+ - No Dict[str, Any] usage
6
+ - Complete type annotations
7
+ - Custom exception hierarchy
8
+ - No bare except clauses
9
+ """
10
+
11
+ import asyncio
12
+ import random
13
+ from datetime import datetime, timedelta
14
+ from typing import Callable, Optional, List, Type, TypeVar, Any
15
+ from pydantic import BaseModel, Field, ConfigDict, field_validator
16
+ from enum import Enum
17
+ import functools
18
+ import logging
19
+
20
+
21
+ F = TypeVar('F', bound=Callable[..., Any])
22
+
23
+
24
+ class ErrorSeverity(str, Enum):
25
+ """Error severity levels"""
26
+ LOW = "low"
27
+ MEDIUM = "medium"
28
+ HIGH = "high"
29
+ CRITICAL = "critical"
30
+
31
+
32
+ class ErrorInfo(BaseModel):
33
+ """Error information with strict typing"""
34
+ model_config = ConfigDict(
35
+ validate_assignment=True,
36
+ extra="forbid"
37
+ )
38
+
39
+ exception_type: str = Field(
40
+ ...,
41
+ description="Exception type name"
42
+ )
43
+ exception_message: str = Field(
44
+ ...,
45
+ description="Exception message"
46
+ )
47
+ severity: ErrorSeverity = Field(
48
+ ...,
49
+ description="Error severity level"
50
+ )
51
+ timestamp: datetime = Field(
52
+ default_factory=datetime.now,
53
+ description="Error occurrence timestamp"
54
+ )
55
+ operation: str = Field(
56
+ ...,
57
+ min_length=1,
58
+ description="Operation where error occurred"
59
+ )
60
+ retry_count: int = Field(
61
+ default=0,
62
+ ge=0,
63
+ description="Number of retry attempts"
64
+ )
65
+ recoverable: bool = Field(
66
+ default=True,
67
+ description="Whether error is recoverable"
68
+ )
69
+ context: dict[str, str] = Field(
70
+ default_factory=dict,
71
+ description="Additional error context"
72
+ )
73
+
74
+
75
+ class RetryConfig(BaseModel):
76
+ """Retry configuration with validation"""
77
+ model_config = ConfigDict(
78
+ validate_assignment=True,
79
+ extra="forbid"
80
+ )
81
+
82
+ max_attempts: int = Field(
83
+ default=3,
84
+ ge=1,
85
+ le=10,
86
+ description="Maximum retry attempts"
87
+ )
88
+ base_delay: float = Field(
89
+ default=1.0,
90
+ ge=0.1,
91
+ le=60.0,
92
+ description="Base delay between retries in seconds"
93
+ )
94
+ max_delay: float = Field(
95
+ default=60.0,
96
+ ge=1.0,
97
+ le=300.0,
98
+ description="Maximum delay between retries in seconds"
99
+ )
100
+ exponential_base: float = Field(
101
+ default=2.0,
102
+ ge=1.1,
103
+ le=10.0,
104
+ description="Exponential backoff base"
105
+ )
106
+ jitter: bool = Field(
107
+ default=True,
108
+ description="Add random jitter to delays"
109
+ )
110
+ retry_on_exceptions: List[str] = Field(
111
+ default_factory=lambda: ["Exception"],
112
+ description="Exception types to retry on"
113
+ )
114
+
115
+ @field_validator('max_delay')
116
+ @classmethod
117
+ def validate_max_delay(cls, v: float, info) -> float:
118
+ """Validate max_delay is greater than base_delay"""
119
+ if 'base_delay' in info.data and v < info.data['base_delay']:
120
+ raise ValueError("max_delay must be greater than base_delay")
121
+ return v
122
+
123
+
124
+ class CircuitBreakerState(BaseModel):
125
+ """Circuit breaker state tracking"""
126
+ model_config = ConfigDict(
127
+ validate_assignment=True,
128
+ extra="forbid"
129
+ )
130
+
131
+ total_requests: int = Field(default=0, ge=0)
132
+ failures: int = Field(default=0, ge=0)
133
+ failure_rate: float = Field(default=0.0, ge=0.0, le=1.0)
134
+ last_failure: Optional[datetime] = Field(default=None)
135
+ is_open: bool = Field(default=False)
136
+
137
+
138
+ class ErrorManagerError(Exception):
139
+ """Base exception for error manager"""
140
+ def __init__(self, message: str, operation: str, details: Optional[dict[str, str]] = None):
141
+ self.message = message
142
+ self.operation = operation
143
+ self.details = details or {}
144
+ super().__init__(message)
145
+
146
+
147
+ class RetryExhaustedException(ErrorManagerError):
148
+ """Raised when all retry attempts are exhausted"""
149
+ pass
150
+
151
+
152
+ class CircuitBreakerOpenError(ErrorManagerError):
153
+ """Raised when circuit breaker is open"""
154
+ pass
155
+
156
+
157
+ class ErrorManager:
158
+ """
159
+ 🛡️ Error Manager - Smart error handling and retry logic
160
+
161
+ Features:
162
+ - Automatic retry with exponential backoff
163
+ - Error classification and severity
164
+ - Circuit breaker pattern
165
+ - Error pattern detection
166
+ - Type-safe error handling
167
+ """
168
+
169
+ def __init__(self, logger: Optional[logging.Logger] = None):
170
+ self.logger: logging.Logger = logger or logging.getLogger(__name__)
171
+ self._error_history: List[ErrorInfo] = []
172
+ self._retry_configs: dict[str, RetryConfig] = {}
173
+ self._circuit_breaker_states: dict[str, CircuitBreakerState] = {}
174
+
175
+ def register_retry_config(self, operation: str, config: RetryConfig) -> None:
176
+ """Register retry configuration for an operation"""
177
+ if not operation.strip():
178
+ raise ValueError("Operation name cannot be empty")
179
+
180
+ self._retry_configs[operation] = config
181
+
182
+ def classify_error(self, exception: Exception, operation: str) -> ErrorSeverity:
183
+ """Classify error severity based on exception type"""
184
+ exception_type = type(exception).__name__
185
+
186
+ # Network/connection errors - usually recoverable
187
+ if exception_type in ['ConnectionError', 'TimeoutError', 'ConnectTimeout']:
188
+ return ErrorSeverity.MEDIUM
189
+
190
+ # Browser/automation errors - might be recoverable
191
+ if 'playwright' in exception_type.lower() or 'selenium' in exception_type.lower():
192
+ return ErrorSeverity.MEDIUM
193
+
194
+ # Parse/data errors - usually low severity
195
+ if exception_type in ['ValueError', 'KeyError', 'AttributeError', 'TypeError']:
196
+ return ErrorSeverity.LOW
197
+
198
+ # Memory/system errors - critical
199
+ if exception_type in ['MemoryError', 'OSError', 'SystemError']:
200
+ return ErrorSeverity.CRITICAL
201
+
202
+ # Permission errors - high severity
203
+ if exception_type in ['PermissionError', 'FileNotFoundError']:
204
+ return ErrorSeverity.HIGH
205
+
206
+ # Default to medium
207
+ return ErrorSeverity.MEDIUM
208
+
209
+ def should_retry(self, error_info: ErrorInfo, config: RetryConfig) -> bool:
210
+ """Determine if operation should be retried"""
211
+ # Check max attempts
212
+ if error_info.retry_count >= config.max_attempts:
213
+ return False
214
+
215
+ # Check if exception type is retryable
216
+ if error_info.exception_type not in config.retry_on_exceptions:
217
+ # Check if "Exception" is in the list (catch-all)
218
+ if "Exception" not in config.retry_on_exceptions:
219
+ return False
220
+
221
+ # Check circuit breaker
222
+ if self._is_circuit_open(error_info.operation):
223
+ return False
224
+
225
+ # Critical errors shouldn't be retried
226
+ if error_info.severity == ErrorSeverity.CRITICAL:
227
+ return False
228
+
229
+ return error_info.recoverable
230
+
231
+ def calculate_delay(self, retry_count: int, config: RetryConfig) -> float:
232
+ """Calculate retry delay with exponential backoff"""
233
+ delay = config.base_delay * (config.exponential_base ** retry_count)
234
+ delay = min(delay, config.max_delay)
235
+
236
+ if config.jitter:
237
+ # Add random jitter (±25%)
238
+ jitter = delay * 0.25 * (2 * random.random() - 1)
239
+ delay += jitter
240
+
241
+ return max(0.1, delay) # Minimum 0.1 second delay
242
+
243
+ def record_error(
244
+ self,
245
+ exception: Exception,
246
+ operation: str,
247
+ context: Optional[dict[str, str]] = None
248
+ ) -> ErrorInfo:
249
+ """Record an error occurrence"""
250
+ error_info = ErrorInfo(
251
+ exception_type=type(exception).__name__,
252
+ exception_message=str(exception),
253
+ severity=self.classify_error(exception, operation),
254
+ operation=operation,
255
+ recoverable=self._is_recoverable(exception),
256
+ context=context or {}
257
+ )
258
+
259
+ self._error_history.append(error_info)
260
+ self._update_circuit_breaker(operation, success=False)
261
+
262
+ return error_info
263
+
264
+ def record_success(self, operation: str) -> None:
265
+ """Record a successful operation"""
266
+ self._update_circuit_breaker(operation, success=True)
267
+
268
+ def with_retry(
269
+ self,
270
+ operation: str,
271
+ config: Optional[RetryConfig] = None
272
+ ) -> Callable[[F], F]:
273
+ """Decorator for automatic retry logic"""
274
+ def decorator(func: F) -> F:
275
+ @functools.wraps(func)
276
+ async def wrapper(*args, **kwargs):
277
+ retry_config = config or self._retry_configs.get(operation, RetryConfig())
278
+ last_error: Optional[Exception] = None
279
+
280
+ for attempt in range(retry_config.max_attempts):
281
+ try:
282
+ result = await func(*args, **kwargs)
283
+ self.record_success(operation)
284
+ return result
285
+
286
+ except Exception as e:
287
+ error_info = self.record_error(e, operation)
288
+ error_info.retry_count = attempt
289
+ last_error = e
290
+
291
+ if not self.should_retry(error_info, retry_config):
292
+ break
293
+
294
+ if attempt < retry_config.max_attempts - 1:
295
+ delay = self.calculate_delay(attempt, retry_config)
296
+ self.logger.warning(
297
+ f"Operation '{operation}' failed (attempt {attempt + 1}), "
298
+ f"retrying in {delay:.1f}s: {e}"
299
+ )
300
+ await asyncio.sleep(delay)
301
+
302
+ # All retries exhausted
303
+ self.logger.error(f"Operation '{operation}' failed after {retry_config.max_attempts} attempts")
304
+ if last_error:
305
+ raise RetryExhaustedException(
306
+ message=f"Operation failed after {retry_config.max_attempts} attempts",
307
+ operation=operation,
308
+ details={"last_error": str(last_error)}
309
+ ) from last_error
310
+ else:
311
+ raise RetryExhaustedException(
312
+ message=f"Operation failed after {retry_config.max_attempts} attempts",
313
+ operation=operation
314
+ )
315
+
316
+ return wrapper # type: ignore
317
+ return decorator
318
+
319
+ def _is_recoverable(self, exception: Exception) -> bool:
320
+ """Determine if an error is recoverable"""
321
+ exception_type = type(exception).__name__
322
+
323
+ # System errors are usually not recoverable
324
+ if exception_type in ['MemoryError', 'OSError', 'KeyboardInterrupt', 'SystemExit']:
325
+ return False
326
+
327
+ # Network errors are usually recoverable
328
+ if exception_type in ['ConnectionError', 'TimeoutError', 'ConnectTimeout']:
329
+ return True
330
+
331
+ # Most other errors are recoverable
332
+ return True
333
+
334
+ def _is_circuit_open(self, operation: str) -> bool:
335
+ """Check if circuit breaker is open for operation"""
336
+ state = self._circuit_breaker_states.get(operation)
337
+ if not state:
338
+ return False
339
+
340
+ # Circuit is open if failure rate is too high
341
+ if state.failure_rate > 0.5 and state.total_requests > 10:
342
+ # Check if cooldown period has passed
343
+ if state.last_failure:
344
+ cooldown_period = timedelta(minutes=5)
345
+ if datetime.now() - state.last_failure < cooldown_period:
346
+ return True
347
+
348
+ return False
349
+
350
+ def _update_circuit_breaker(self, operation: str, success: bool) -> None:
351
+ """Update circuit breaker state"""
352
+ if operation not in self._circuit_breaker_states:
353
+ self._circuit_breaker_states[operation] = CircuitBreakerState()
354
+
355
+ state = self._circuit_breaker_states[operation]
356
+ state.total_requests += 1
357
+
358
+ if not success:
359
+ state.failures += 1
360
+ state.last_failure = datetime.now()
361
+
362
+ state.failure_rate = state.failures / state.total_requests
363
+ state.is_open = self._is_circuit_open(operation)
364
+
365
+ def get_error_stats(self) -> dict[str, float]:
366
+ """Get error statistics"""
367
+ if not self._error_history:
368
+ return {
369
+ "total_errors": 0.0,
370
+ "recent_errors_24h": 0.0,
371
+ "critical_errors": 0.0,
372
+ "high_errors": 0.0,
373
+ "medium_errors": 0.0,
374
+ "low_errors": 0.0
375
+ }
376
+
377
+ recent_errors = [
378
+ e for e in self._error_history
379
+ if e.timestamp > datetime.now() - timedelta(hours=24)
380
+ ]
381
+
382
+ severity_counts = {
383
+ "critical": len([e for e in recent_errors if e.severity == ErrorSeverity.CRITICAL]),
384
+ "high": len([e for e in recent_errors if e.severity == ErrorSeverity.HIGH]),
385
+ "medium": len([e for e in recent_errors if e.severity == ErrorSeverity.MEDIUM]),
386
+ "low": len([e for e in recent_errors if e.severity == ErrorSeverity.LOW])
387
+ }
388
+
389
+ return {
390
+ "total_errors": float(len(self._error_history)),
391
+ "recent_errors_24h": float(len(recent_errors)),
392
+ "critical_errors": float(severity_counts["critical"]),
393
+ "high_errors": float(severity_counts["high"]),
394
+ "medium_errors": float(severity_counts["medium"]),
395
+ "low_errors": float(severity_counts["low"])
396
+ }
397
+
398
+ def get_circuit_breaker_states(self) -> dict[str, CircuitBreakerState]:
399
+ """Get circuit breaker states"""
400
+ return {
401
+ operation: CircuitBreakerState.model_validate(state.model_dump())
402
+ for operation, state in self._circuit_breaker_states.items()
403
+ }
404
+
405
+ def reset_circuit_breaker(self, operation: str) -> None:
406
+ """Reset circuit breaker for operation"""
407
+ if operation in self._circuit_breaker_states:
408
+ self._circuit_breaker_states[operation] = CircuitBreakerState()
409
+
410
+ def clear_error_history(self) -> None:
411
+ """Clear error history"""
412
+ self._error_history.clear()