unrealon 1.0.8__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.8.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.8.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.8.dist-info/METADATA +0 -803
  71. unrealon-1.0.8.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
unrealon_bridge/cli.py ADDED
@@ -0,0 +1,316 @@
1
+ """
2
+ CLI commands for unrealon_bridge.
3
+
4
+ Provides command-line interfaces for running parser bridge servers.
5
+ """
6
+
7
+ import asyncio
8
+ import sys
9
+ import os
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Optional
13
+ from watchdog.observers import Observer
14
+ from watchdog.events import FileSystemEventHandler
15
+ import websockets
16
+ import click
17
+
18
+ from unrealon_bridge import ParserBridgeServer
19
+ from unrealon_bridge.configs import load_bridge_config, create_sample_config, save_bridge_config
20
+ from unrealon_rpc.logging import get_logger, setup_logging_with_clear, clear_specific_log_file, resolve_log_path
21
+ from unrealon_rpc.logging.models import LogConfig
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ async def run_parser_bridge_server_async(
27
+ redis_url: str = "redis://localhost:6379/0",
28
+ rpc_channel: str = "parser_rpc",
29
+ pubsub_prefix: str = "parser",
30
+ websocket_host: str = "localhost",
31
+ websocket_port: int = 8001,
32
+ clear_logs: bool = True
33
+ ) -> None:
34
+ """
35
+ Run Parser Bridge Server asynchronously.
36
+
37
+ Args:
38
+ redis_url: Redis connection URL
39
+ rpc_channel: RPC channel name
40
+ pubsub_prefix: PubSub channel prefix
41
+ websocket_host: WebSocket host
42
+ websocket_port: WebSocket port
43
+ """
44
+ # Setup logging with file output and clear only this server's log
45
+ log_file_path = resolve_log_path("logs/parser_bridge.log")
46
+ log_config = LogConfig(
47
+ file_enabled=True,
48
+ file_path=log_file_path,
49
+ log_level="INFO"
50
+ )
51
+ if clear_logs:
52
+ clear_specific_log_file(log_file_path)
53
+ setup_logging_with_clear(log_config, clear_logs=False)
54
+
55
+ logger.info("Starting Parser Bridge Server")
56
+ logger.info(f"Redis URL: {redis_url}")
57
+ logger.info(f"RPC Channel: {rpc_channel}")
58
+ logger.info(f"PubSub Prefix: {pubsub_prefix}")
59
+ logger.info(f"WebSocket: {websocket_host}:{websocket_port}")
60
+
61
+ server = ParserBridgeServer(
62
+ redis_url=redis_url,
63
+ rpc_channel=rpc_channel,
64
+ pubsub_prefix=pubsub_prefix
65
+ )
66
+
67
+ # Register example command handlers
68
+ async def example_scrape_handler(command):
69
+ """Example handler for scrape commands."""
70
+ logger.info(f"Processing scrape command: {command.command_id}")
71
+ return {
72
+ "success": "true",
73
+ "message": f"Scrape completed for command {command.command_id}",
74
+ "items_found": "42",
75
+ "timestamp": command.created_at.isoformat()
76
+ }
77
+
78
+ async def example_parse_handler(command):
79
+ """Example handler for parse commands."""
80
+ logger.info(f"Processing parse command: {command.command_id}")
81
+ return {
82
+ "success": "true",
83
+ "message": f"Parse completed for command {command.command_id}",
84
+ "parsed_items": "15",
85
+ "timestamp": command.created_at.isoformat()
86
+ }
87
+
88
+ async def health_handler(command):
89
+ """Handler for health commands."""
90
+ logger.info(f"Processing health command: {command.command_id} for parser {command.parser_id}")
91
+ return {
92
+ "command_type": "health",
93
+ "status": "healthy",
94
+ "parser_ready": "true",
95
+ "bridge_connected": "true",
96
+ "timestamp": command.created_at.isoformat()
97
+ }
98
+
99
+ async def status_handler(command):
100
+ """Handler for status commands."""
101
+ logger.info(f"Processing status command: {command.command_id} for parser {command.parser_id}")
102
+ return {
103
+ "command_type": "status",
104
+ "running": "true",
105
+ "uptime_seconds": "60",
106
+ "total_runs": "0",
107
+ "successful_runs": "0",
108
+ "failed_runs": "0",
109
+ "timestamp": command.created_at.isoformat()
110
+ }
111
+
112
+ # Register handlers
113
+ server.register_command_handler("scrape", example_scrape_handler)
114
+ server.register_command_handler("parse", example_parse_handler)
115
+ server.register_command_handler("test_command", example_scrape_handler)
116
+ server.register_command_handler("benchmark_command", example_scrape_handler)
117
+ server.register_command_handler("throughput_test", example_parse_handler)
118
+ server.register_command_handler("concurrent_test", example_scrape_handler)
119
+ server.register_command_handler("health", health_handler)
120
+ server.register_command_handler("status", status_handler)
121
+
122
+ # Start server
123
+ await server.start()
124
+ logger.info("Parser Bridge Server started successfully")
125
+
126
+ # WebSocket handler
127
+ async def websocket_handler(websocket):
128
+ """Handle WebSocket connections."""
129
+ logger.info(f"New WebSocket connection from {websocket.remote_address}")
130
+ try:
131
+ await server.bridge.handle_websocket(websocket)
132
+ except Exception as e:
133
+ logger.error(f"WebSocket connection error: {e}")
134
+ finally:
135
+ logger.info("WebSocket connection closed")
136
+
137
+ try:
138
+ logger.info(f"Starting WebSocket server on {websocket_host}:{websocket_port}")
139
+
140
+ # Start WebSocket server
141
+ async with websockets.serve(websocket_handler, websocket_host, websocket_port):
142
+ logger.info(f"WebSocket server started on {websocket_host}:{websocket_port}")
143
+
144
+ # Keep server running
145
+ while True:
146
+ await asyncio.sleep(1)
147
+
148
+ except KeyboardInterrupt:
149
+ logger.info("Received shutdown signal")
150
+ except Exception as e:
151
+ logger.error(f"Server error: {e}")
152
+ raise
153
+ finally:
154
+ logger.info("Stopping Parser Bridge Server...")
155
+ await server.stop()
156
+ logger.info("Parser Bridge Server stopped")
157
+
158
+
159
+ @click.command()
160
+ @click.option("--redis-url", default="redis://localhost:6379/0", help="Redis connection URL")
161
+ @click.option("--rpc-channel", default="parser_rpc", help="RPC channel name")
162
+ @click.option("--pubsub-prefix", default="parser", help="PubSub channel prefix")
163
+ @click.option("--websocket-host", default="localhost", help="WebSocket host")
164
+ @click.option("--websocket-port", type=int, default=8001, help="WebSocket port")
165
+ def run_parser_bridge_server(redis_url: str, rpc_channel: str, pubsub_prefix: str, websocket_host: str, websocket_port: int) -> None:
166
+ """Run unrealon_bridge Parser Bridge Server."""
167
+ try:
168
+ asyncio.run(run_parser_bridge_server_async(
169
+ redis_url=redis_url,
170
+ rpc_channel=rpc_channel,
171
+ pubsub_prefix=pubsub_prefix,
172
+ websocket_host=websocket_host,
173
+ websocket_port=websocket_port
174
+ ))
175
+ except KeyboardInterrupt:
176
+ logger.info("Server shutdown completed")
177
+ sys.exit(0)
178
+ except Exception as e:
179
+ logger.error(f"Failed to start server: {e}")
180
+ sys.exit(1)
181
+
182
+
183
+ def _run_parser_bridge_server_with_reload_impl(
184
+ redis_url: str = "redis://localhost:6379/0",
185
+ rpc_channel: str = "parser_rpc",
186
+ pubsub_prefix: str = "parser",
187
+ websocket_host: str = "localhost",
188
+ websocket_port: int = 8002,
189
+ watch_dirs: Optional[list] = None
190
+ ) -> None:
191
+ """
192
+ Run Parser Bridge Server with auto-reload on file changes.
193
+ """
194
+
195
+ if watch_dirs is None:
196
+ # Watch current directory and src directory
197
+ current_dir = Path.cwd()
198
+ src_dir = current_dir / "src"
199
+ watch_dirs = [str(current_dir), str(src_dir)] if src_dir.exists() else [str(current_dir)]
200
+
201
+ class ReloadHandler(FileSystemEventHandler):
202
+ def __init__(self):
203
+ self.last_reload = 0
204
+ self.reload_delay = 1.0 # Delay to avoid multiple reloads
205
+
206
+ def on_modified(self, event):
207
+ if event.is_directory:
208
+ return
209
+
210
+ # Only reload for Python files
211
+ if not event.src_path.endswith('.py'):
212
+ return
213
+
214
+ # Avoid too frequent reloads
215
+ current_time = time.time()
216
+ if current_time - self.last_reload < self.reload_delay:
217
+ return
218
+
219
+ self.last_reload = current_time
220
+ logger.info(f"File changed: {event.src_path}")
221
+ logger.info("Restarting Parser Bridge Server...")
222
+
223
+ # Restart the process
224
+ os.execv(sys.executable, [sys.executable] + sys.argv)
225
+
226
+ # Setup file watcher
227
+ event_handler = ReloadHandler()
228
+ observer = Observer()
229
+
230
+ for watch_dir in watch_dirs:
231
+ if os.path.exists(watch_dir):
232
+ observer.schedule(event_handler, watch_dir, recursive=True)
233
+ logger.info(f"Watching directory for changes: {watch_dir}")
234
+
235
+ observer.start()
236
+
237
+ try:
238
+ logger.info("🔄 Auto-reload enabled - server will restart on file changes")
239
+ # Don't clear logs on auto-reload, only on first start
240
+ clear_logs_on_start = not os.environ.get('PARSER_BRIDGE_RELOADING', False)
241
+ os.environ['PARSER_BRIDGE_RELOADING'] = 'true'
242
+ asyncio.run(run_parser_bridge_server_async(
243
+ redis_url, rpc_channel, pubsub_prefix, websocket_host, websocket_port, clear_logs_on_start
244
+ ))
245
+ except KeyboardInterrupt:
246
+ logger.info("Server shutdown completed")
247
+ finally:
248
+ observer.stop()
249
+ observer.join()
250
+
251
+
252
+ @click.command()
253
+ @click.option("--redis-url", default="redis://localhost:6379/0", help="Redis connection URL")
254
+ @click.option("--rpc-channel", default="parser_rpc", help="RPC channel name")
255
+ @click.option("--pubsub-prefix", default="parser", help="PubSub channel prefix")
256
+ @click.option("--websocket-host", default="localhost", help="WebSocket host")
257
+ @click.option("--websocket-port", type=int, default=8002, help="WebSocket port (dev mode)")
258
+ @click.option("--watch-dirs", multiple=True, help="Additional directories to watch for changes")
259
+ def run_parser_bridge_server_with_reload(redis_url: str, rpc_channel: str, pubsub_prefix: str, websocket_host: str, websocket_port: int, watch_dirs: tuple) -> None:
260
+ """Run unrealon_bridge Parser Bridge Server with auto-reload (development mode)."""
261
+ try:
262
+ _run_parser_bridge_server_with_reload_impl(
263
+ redis_url=redis_url,
264
+ rpc_channel=rpc_channel,
265
+ pubsub_prefix=pubsub_prefix,
266
+ websocket_host=websocket_host,
267
+ websocket_port=websocket_port,
268
+ watch_dirs=list(watch_dirs) if watch_dirs else None
269
+ )
270
+ except Exception as e:
271
+ logger.error(f"Failed to start server: {e}")
272
+ sys.exit(1)
273
+
274
+
275
+ @click.command()
276
+ @click.option('--output', '-o', default='bridge_config.yaml', help='Output config file path')
277
+ @click.option('--force', is_flag=True, help='Overwrite existing config file')
278
+ def create_config(output: str, force: bool):
279
+ """Create a sample bridge configuration file."""
280
+ config_path = Path(output)
281
+
282
+ if config_path.exists() and not force:
283
+ click.echo(f"❌ Config file already exists: {config_path}")
284
+ click.echo("Use --force to overwrite")
285
+ sys.exit(1)
286
+
287
+ try:
288
+ # Create sample configuration
289
+ config = create_sample_config()
290
+ saved_path = save_bridge_config(config, config_path)
291
+
292
+ click.echo(f"✅ Created sample config: {saved_path}")
293
+ click.echo("\n📋 Configuration includes:")
294
+ click.echo(" • Test API keys for debugging")
295
+ click.echo(" • Development environment settings")
296
+ click.echo(" • WebSocket on port 8002")
297
+ click.echo(" • Redis connection settings")
298
+ click.echo("\n🔧 Edit the config file to customize settings")
299
+
300
+ except Exception as e:
301
+ click.echo(f"❌ Failed to create config: {e}")
302
+ sys.exit(1)
303
+
304
+
305
+ @click.group()
306
+ def cli():
307
+ """Unrealon Bridge CLI tools."""
308
+ pass
309
+
310
+
311
+ cli.add_command(run_parser_bridge_server, name="server")
312
+ cli.add_command(create_config, name="create-config")
313
+
314
+
315
+ if __name__ == "__main__":
316
+ cli()
@@ -0,0 +1,93 @@
1
+ """
2
+ Parser Bridge Client - Modular implementation with composition.
3
+
4
+ Clean architecture without multiple inheritance or hasattr checks.
5
+ """
6
+
7
+ from typing import Optional, List
8
+ from unrealon_rpc.logging import get_logger
9
+
10
+ from .base import ParserBridgeClientBase
11
+ from .connection import ConnectionMixin
12
+ from .session import SessionMixin
13
+ from .commands import CommandsMixin
14
+ from .proxy import ProxyMixin
15
+ from .events import EventsMixin
16
+ from .health import HealthMixin
17
+ from .logging import LoggingMixin
18
+ from .html_parser import HTMLParserMixin
19
+ from .scheduler import SchedulerMixin
20
+
21
+ from ..models import (
22
+ ParserInfo, ParserHealth, ParserStats, CommandResult, ProxyInfo
23
+ )
24
+
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ class ParserBridgeClient(
29
+ ParserBridgeClientBase,
30
+ ConnectionMixin,
31
+ SessionMixin,
32
+ CommandsMixin,
33
+ ProxyMixin,
34
+ EventsMixin,
35
+ HealthMixin,
36
+ LoggingMixin,
37
+ HTMLParserMixin,
38
+ SchedulerMixin
39
+ ):
40
+ """
41
+ Complete Parser Bridge Client with all functionality.
42
+
43
+ Combines all mixins to provide full parser client capabilities:
44
+ - Connection and registration management
45
+ - Session lifecycle
46
+ - Command execution
47
+ - Proxy management
48
+ - Event logging and heartbeat
49
+ - Health monitoring
50
+ - Parser logging to Django
51
+ - HTML parsing via AI/LLM
52
+ - Task scheduling and management
53
+ """
54
+
55
+ def __init__(self, websocket_url: str, parser_type: str, parser_version: str = "1.0.0", capabilities: List[str] = None, api_key: str = None, **kwargs):
56
+ """
57
+ Initialize complete parser bridge client.
58
+
59
+ Args:
60
+ websocket_url: WebSocket server URL
61
+ parser_type: Type of parser (encar, autotrader, etc.)
62
+ parser_version: Parser version
63
+ capabilities: List of parser capabilities
64
+ api_key: API key for authentication
65
+ **kwargs: Additional arguments for BridgeClient
66
+ """
67
+ super().__init__(websocket_url, parser_type, parser_version, capabilities, api_key, **kwargs)
68
+
69
+ async def disconnect(self) -> None:
70
+ """Enhanced disconnect with proper session cleanup."""
71
+ if self.session_id:
72
+ await self.end_session()
73
+
74
+ # Call parent disconnect
75
+ await super().disconnect()
76
+
77
+ async def _log_command_event(self, event_type: str, message: str, level: str = "info", data: Optional[dict[str, str]] = None) -> None:
78
+ """Override to provide actual event logging."""
79
+ await self.log_event(event_type=event_type, message=message, level=level, data=data)
80
+
81
+ def set_command_handler(self, handler) -> None:
82
+ """
83
+ Set command handler for daemon compatibility.
84
+
85
+ Args:
86
+ handler: Command handler function
87
+ """
88
+ # For now, just store the handler - can be extended later for actual command processing
89
+ self._command_handler = handler
90
+ logger.info("Command handler set for daemon compatibility")
91
+
92
+
93
+ __all__ = ["ParserBridgeClient"]
@@ -0,0 +1,78 @@
1
+ """
2
+ Base Parser Bridge Client.
3
+
4
+ Core client functionality and state management.
5
+ """
6
+
7
+ from typing import Optional, List
8
+ from unrealon_rpc.bridge import BridgeClient
9
+ from unrealon_rpc.logging import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class ParserBridgeClientBase:
15
+ """
16
+ Base parser bridge client with core functionality.
17
+
18
+ Manages connection state and provides foundation for specialized components.
19
+ """
20
+
21
+ def __init__(self, websocket_url: str, parser_type: str, parser_version: str = "1.0.0", capabilities: List[str] = None, api_key: str = None, **kwargs):
22
+ """
23
+ Initialize parser bridge client.
24
+
25
+ Args:
26
+ websocket_url: WebSocket server URL
27
+ parser_type: Type of parser (encar, autotrader, etc.)
28
+ parser_version: Parser version
29
+ capabilities: List of parser capabilities
30
+ api_key: API key for authentication
31
+ **kwargs: Additional arguments for BridgeClient
32
+ """
33
+ self.parser_type = parser_type
34
+ self.parser_version = parser_version
35
+ self.capabilities = capabilities or []
36
+ self.api_key = api_key
37
+
38
+ # Initialize generic bridge client
39
+ self.bridge_client = BridgeClient(
40
+ websocket_url=websocket_url,
41
+ client_type=f"parser_{parser_type}",
42
+ client_version=parser_version,
43
+ api_key=api_key,
44
+ **kwargs
45
+ )
46
+
47
+ # Parser state
48
+ self.parser_id: Optional[str] = None
49
+ self.session_id: Optional[str] = None
50
+ self.registered = False
51
+
52
+ @property
53
+ def is_connected(self) -> bool:
54
+ """Check if client is connected."""
55
+ return self.bridge_client.is_connected
56
+
57
+ @property
58
+ def is_registered(self) -> bool:
59
+ """Check if parser is registered."""
60
+ return self.registered
61
+
62
+ async def send_message(self, message: dict) -> None:
63
+ """Send message through WebSocket bridge."""
64
+ await self.bridge_client._send_message(message)
65
+
66
+ def add_message_handler(self, message_type: str, handler) -> None:
67
+ """Add message handler for specific message type."""
68
+ self.bridge_client.message_handlers[message_type] = handler
69
+
70
+ def _ensure_registered(self) -> None:
71
+ """Ensure parser is registered, raise error if not."""
72
+ if not self.registered:
73
+ raise RuntimeError("Parser not registered")
74
+
75
+ def _ensure_connected(self) -> None:
76
+ """Ensure client is connected, raise error if not."""
77
+ if not self.is_connected:
78
+ raise RuntimeError("Client not connected")
@@ -0,0 +1,89 @@
1
+ """
2
+ Command execution for Parser Bridge Client.
3
+ """
4
+
5
+ import uuid
6
+ from typing import Optional
7
+ from datetime import datetime
8
+ from unrealon_rpc.logging import get_logger
9
+
10
+ from ..models import (
11
+ CommandResult, CommandExecuteRequest, CommandExecuteResponse
12
+ )
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class CommandsMixin:
18
+ """Mixin for command execution functionality."""
19
+
20
+ async def execute_command(self, command_type: str, parameters: Optional[dict[str, str]] = None, timeout: int = 300) -> CommandResult:
21
+ """
22
+ Execute a parser command.
23
+
24
+ Args:
25
+ command_type: Type of command to execute
26
+ parameters: Command parameters
27
+ timeout: Command timeout in seconds
28
+
29
+ Returns:
30
+ Command execution result
31
+ """
32
+ self._ensure_registered()
33
+
34
+ request = CommandExecuteRequest(
35
+ command_type=command_type,
36
+ parser_id=self.parser_id,
37
+ parameters=parameters or {},
38
+ timeout=timeout
39
+ )
40
+
41
+ started_at = datetime.now()
42
+
43
+ try:
44
+ result = await self.bridge_client.call_rpc(
45
+ method="parser.execute_command",
46
+ params=request.model_dump(),
47
+ timeout=timeout
48
+ )
49
+
50
+ response = CommandExecuteResponse.model_validate(result)
51
+
52
+ if response.success and response.result:
53
+ execution_time = (datetime.now() - started_at).total_seconds()
54
+
55
+ # Log command completion
56
+ await self._log_command_event(
57
+ event_type="command_completed",
58
+ message=f"Command {command_type} completed",
59
+ data={"command_type": command_type, "execution_time": str(execution_time)}
60
+ )
61
+
62
+ return response.result
63
+ else:
64
+ raise RuntimeError(f"Command execution failed: {response.error}")
65
+
66
+ except Exception as e:
67
+ execution_time = (datetime.now() - started_at).total_seconds()
68
+
69
+ # Create error result
70
+ command_result = CommandResult(
71
+ command_id=str(uuid.uuid4()),
72
+ success=False,
73
+ error_message=str(e),
74
+ execution_time=execution_time
75
+ )
76
+
77
+ # Log command error
78
+ await self._log_command_event(
79
+ event_type="command_failed",
80
+ level="error",
81
+ message=f"Command {command_type} failed: {str(e)}"
82
+ )
83
+
84
+ return command_result
85
+
86
+ async def _log_command_event(self, event_type: str, message: str, level: str = "info", data: Optional[dict[str, str]] = None) -> None:
87
+ """Log command-related event."""
88
+ # Event logging will be handled by the main client
89
+ pass
@@ -0,0 +1,90 @@
1
+ """
2
+ Connection and registration management for Parser Bridge Client.
3
+ """
4
+
5
+ import uuid
6
+ from typing import Optional
7
+ from unrealon_rpc.logging import get_logger
8
+
9
+ from ..models import (
10
+ ParserInfo, ParserRegisterRequest, ParserRegisterResponse
11
+ )
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class ConnectionMixin:
17
+ """Mixin for connection and registration functionality."""
18
+
19
+ async def connect(self) -> None:
20
+ """Connect to bridge and register parser."""
21
+ await self.bridge_client.connect()
22
+ # Auto-register parser after connection
23
+ await self.register_parser()
24
+
25
+ async def disconnect(self) -> None:
26
+ """Disconnect from bridge."""
27
+ if self.session_id:
28
+ # Session cleanup will be handled by the main client
29
+ pass
30
+
31
+ await self.bridge_client.disconnect()
32
+
33
+ async def register_parser(self, metadata: Optional[dict[str, str]] = None) -> ParserInfo:
34
+ """
35
+ Register parser with the system.
36
+
37
+ Args:
38
+ metadata: Additional parser metadata
39
+
40
+ Returns:
41
+ Parser registration information
42
+ """
43
+ request = ParserRegisterRequest(
44
+ parser_id=str(uuid.uuid4()),
45
+ parser_type=self.parser_type,
46
+ version=self.parser_version,
47
+ capabilities=self.capabilities,
48
+ metadata=metadata
49
+ )
50
+
51
+ # Prepare params with API key
52
+ params = request.model_dump()
53
+ if hasattr(self, 'api_key') and self.api_key:
54
+ params["api_key"] = self.api_key
55
+ logger.info(f"🔑 Adding API key to params: {self.api_key[:8]}...")
56
+ else:
57
+ logger.warning(f"⚠️ No API key available! hasattr: {hasattr(self, 'api_key')}, api_key: {getattr(self, 'api_key', 'NOT_FOUND')}")
58
+
59
+ result = await self.bridge_client.call_rpc(
60
+ method="parser.register",
61
+ params=params
62
+ )
63
+
64
+ # Parse response using typed model
65
+ response = ParserRegisterResponse.model_validate(result)
66
+
67
+ if response.success:
68
+ self.parser_id = response.parser_id or request.parser_id
69
+ self.registered = True
70
+
71
+ logger.info(f"Parser registered: {self.parser_id} ({self.parser_type})")
72
+
73
+ return ParserInfo(
74
+ parser_id=self.parser_id,
75
+ parser_type=self.parser_type,
76
+ version=self.parser_version,
77
+ capabilities=self.capabilities,
78
+ metadata=metadata or {}
79
+ )
80
+ else:
81
+ raise RuntimeError(f"Parser registration failed: {response.error}")
82
+
83
+ async def __aenter__(self):
84
+ """Async context manager entry."""
85
+ await self.connect()
86
+ return self
87
+
88
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
89
+ """Async context manager exit."""
90
+ await self.disconnect()