quantum-framework 0.9.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 (262) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/commands/__init__.py +15 -0
  4. quantum/cli/commands/build.py +417 -0
  5. quantum/cli/commands/dev.py +185 -0
  6. quantum/cli/commands/docs.py +329 -0
  7. quantum/cli/commands/lint.py +523 -0
  8. quantum/cli/commands/migrate.py +527 -0
  9. quantum/cli/commands/new.py +622 -0
  10. quantum/cli/commands/serve.py +190 -0
  11. quantum/cli/commands/test.py +193 -0
  12. quantum/cli/deploy.py +810 -0
  13. quantum/cli/hot_reload.py +951 -0
  14. quantum/cli/jobs.py +356 -0
  15. quantum/cli/mq.py +582 -0
  16. quantum/cli/pkg.py +390 -0
  17. quantum/cli/runner.py +547 -0
  18. quantum/cli/server_process.py +159 -0
  19. quantum/cli/utils.py +334 -0
  20. quantum/compiler/__init__.py +30 -0
  21. quantum/compiler/base_generator.py +367 -0
  22. quantum/compiler/cli.py +295 -0
  23. quantum/compiler/expression_transformer.py +444 -0
  24. quantum/compiler/javascript/__init__.py +10 -0
  25. quantum/compiler/javascript/generator.py +659 -0
  26. quantum/compiler/optimizer.py +270 -0
  27. quantum/compiler/python/__init__.py +10 -0
  28. quantum/compiler/python/generator.py +883 -0
  29. quantum/compiler/python/runtime.py +863 -0
  30. quantum/compiler/transpiler.py +330 -0
  31. quantum/core/__init__.py +3 -0
  32. quantum/core/ast_nodes.py +2611 -0
  33. quantum/core/expression_diagnostics.py +87 -0
  34. quantum/core/expression_stdlib.py +148 -0
  35. quantum/core/expressions.py +591 -0
  36. quantum/core/features/agents/src/__init__.py +30 -0
  37. quantum/core/features/agents/src/ast_node.py +540 -0
  38. quantum/core/features/conditionals/src/__init__.py +8 -0
  39. quantum/core/features/conditionals/src/ast_node.py +68 -0
  40. quantum/core/features/data_fetching/src/__init__.py +21 -0
  41. quantum/core/features/data_fetching/src/ast_node.py +312 -0
  42. quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
  43. quantum/core/features/data_fetching/src/html_adapter.py +474 -0
  44. quantum/core/features/data_fetching/src/parser.py +225 -0
  45. quantum/core/features/data_import/src/__init__.py +0 -0
  46. quantum/core/features/data_import/src/ast_node.py +291 -0
  47. quantum/core/features/data_import/src/runtime.py +538 -0
  48. quantum/core/features/dump/src/__init__.py +12 -0
  49. quantum/core/features/dump/src/ast_node.py +106 -0
  50. quantum/core/features/dump/src/parser.py +61 -0
  51. quantum/core/features/dump/src/runtime.py +246 -0
  52. quantum/core/features/functions/src/__init__.py +8 -0
  53. quantum/core/features/functions/src/ast_node.py +149 -0
  54. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  55. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  56. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  57. quantum/core/features/invocation/src/__init__.py +0 -0
  58. quantum/core/features/invocation/src/ast_node.py +146 -0
  59. quantum/core/features/invocation/src/runtime.py +327 -0
  60. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  61. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  62. quantum/core/features/knowledge_base/src/parser.py +82 -0
  63. quantum/core/features/logging/src/__init__.py +12 -0
  64. quantum/core/features/logging/src/ast_node.py +111 -0
  65. quantum/core/features/logging/src/parser.py +50 -0
  66. quantum/core/features/logging/src/runtime.py +190 -0
  67. quantum/core/features/loops/src/__init__.py +8 -0
  68. quantum/core/features/loops/src/ast_node.py +60 -0
  69. quantum/core/features/query/src/__init__.py +0 -0
  70. quantum/core/features/query/src/database_service.py +322 -0
  71. quantum/core/features/query/src/query_validators.py +20 -0
  72. quantum/core/features/state_management/src/__init__.py +11 -0
  73. quantum/core/features/state_management/src/ast_node.py +228 -0
  74. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  75. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  76. quantum/core/features/terminal_engine/src/parser.py +361 -0
  77. quantum/core/features/testing_engine/src/__init__.py +41 -0
  78. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  79. quantum/core/features/testing_engine/src/parser.py +604 -0
  80. quantum/core/features/theming/src/__init__.py +48 -0
  81. quantum/core/features/theming/src/ast_node.py +137 -0
  82. quantum/core/features/theming/src/presets.py +405 -0
  83. quantum/core/features/ui_engine/src/__init__.py +20 -0
  84. quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
  85. quantum/core/features/ui_engine/src/parser.py +1106 -0
  86. quantum/core/features/websocket/src/__init__.py +24 -0
  87. quantum/core/features/websocket/src/ast_node.py +247 -0
  88. quantum/core/html_compat.py +299 -0
  89. quantum/core/parser.py +1235 -0
  90. quantum/core/parser_registry.py +213 -0
  91. quantum/core/parsers/__init__.py +76 -0
  92. quantum/core/parsers/ai/__init__.py +12 -0
  93. quantum/core/parsers/ai/agent_parser.py +106 -0
  94. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  95. quantum/core/parsers/ai/llm_parser.py +78 -0
  96. quantum/core/parsers/ai/team_parser.py +88 -0
  97. quantum/core/parsers/base.py +322 -0
  98. quantum/core/parsers/composition/__init__.py +10 -0
  99. quantum/core/parsers/composition/import_parser.py +50 -0
  100. quantum/core/parsers/composition/slot_parser.py +48 -0
  101. quantum/core/parsers/control_flow/__init__.py +11 -0
  102. quantum/core/parsers/control_flow/if_parser.py +68 -0
  103. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  104. quantum/core/parsers/control_flow/set_parser.py +89 -0
  105. quantum/core/parsers/data/__init__.py +12 -0
  106. quantum/core/parsers/data/data_parser.py +217 -0
  107. quantum/core/parsers/data/invoke_parser.py +116 -0
  108. quantum/core/parsers/data/query_parser.py +188 -0
  109. quantum/core/parsers/data/transaction_parser.py +77 -0
  110. quantum/core/parsers/events/__init__.py +9 -0
  111. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  112. quantum/core/parsers/forms/__init__.py +11 -0
  113. quantum/core/parsers/forms/action_parser.py +54 -0
  114. quantum/core/parsers/forms/flash_parser.py +34 -0
  115. quantum/core/parsers/forms/redirect_parser.py +33 -0
  116. quantum/core/parsers/functions/__init__.py +11 -0
  117. quantum/core/parsers/functions/function_parser.py +137 -0
  118. quantum/core/parsers/functions/param_parser.py +66 -0
  119. quantum/core/parsers/functions/return_parser.py +31 -0
  120. quantum/core/parsers/html/__init__.py +10 -0
  121. quantum/core/parsers/html/component_call_parser.py +130 -0
  122. quantum/core/parsers/html/html_parser.py +115 -0
  123. quantum/core/parsers/jobs/__init__.py +11 -0
  124. quantum/core/parsers/jobs/job_parser.py +71 -0
  125. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  126. quantum/core/parsers/jobs/thread_parser.py +57 -0
  127. quantum/core/parsers/messaging/__init__.py +17 -0
  128. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  130. quantum/core/parsers/messaging/message_parser.py +114 -0
  131. quantum/core/parsers/messaging/queue_parser.py +61 -0
  132. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  133. quantum/core/parsers/persistence/__init__.py +9 -0
  134. quantum/core/parsers/persistence/persist_parser.py +64 -0
  135. quantum/core/parsers/routing/__init__.py +9 -0
  136. quantum/core/parsers/routing/route_parser.py +41 -0
  137. quantum/core/parsers/scripting/__init__.py +12 -0
  138. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  139. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  140. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  141. quantum/core/parsers/scripting/python_parser.py +49 -0
  142. quantum/core/parsers/services/__init__.py +12 -0
  143. quantum/core/parsers/services/dump_parser.py +52 -0
  144. quantum/core/parsers/services/file_parser.py +48 -0
  145. quantum/core/parsers/services/log_parser.py +46 -0
  146. quantum/core/parsers/services/mail_parser.py +65 -0
  147. quantum/core/tiers.py +82 -0
  148. quantum/packages/__init__.py +28 -0
  149. quantum/packages/manager.py +413 -0
  150. quantum/packages/manifest.py +351 -0
  151. quantum/packages/registry.py +399 -0
  152. quantum/packages/resolver.py +336 -0
  153. quantum/plugins/__init__.py +33 -0
  154. quantum/plugins/hooks.py +329 -0
  155. quantum/plugins/loader.py +479 -0
  156. quantum/plugins/manifest.py +336 -0
  157. quantum/plugins/registry.py +371 -0
  158. quantum/runtime/__init__.py +28 -0
  159. quantum/runtime/action_handler.py +443 -0
  160. quantum/runtime/adapters/__init__.py +88 -0
  161. quantum/runtime/adapters/memory_adapter.py +690 -0
  162. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  163. quantum/runtime/adapters/redis_adapter.py +582 -0
  164. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  165. quantum/runtime/agent_service.py +1133 -0
  166. quantum/runtime/api_server.py +86 -0
  167. quantum/runtime/ast_cache.py +506 -0
  168. quantum/runtime/auth_service.py +267 -0
  169. quantum/runtime/component.py +990 -0
  170. quantum/runtime/component_composer.py +319 -0
  171. quantum/runtime/component_resolver.py +174 -0
  172. quantum/runtime/database_service.py +598 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +286 -0
  176. quantum/runtime/executor_registry.py +171 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +262 -0
  184. quantum/runtime/executors/control_flow/__init__.py +11 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +93 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +307 -0
  187. quantum/runtime/executors/control_flow/set_executor.py +412 -0
  188. quantum/runtime/executors/data/__init__.py +12 -0
  189. quantum/runtime/executors/data/data_executor.py +145 -0
  190. quantum/runtime/executors/data/invoke_executor.py +176 -0
  191. quantum/runtime/executors/data/query_executor.py +256 -0
  192. quantum/runtime/executors/data/transaction_executor.py +91 -0
  193. quantum/runtime/executors/jobs/__init__.py +11 -0
  194. quantum/runtime/executors/jobs/job_executor.py +190 -0
  195. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  196. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  197. quantum/runtime/executors/messaging/__init__.py +17 -0
  198. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  199. quantum/runtime/executors/messaging/message_executor.py +174 -0
  200. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  201. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  202. quantum/runtime/executors/scripting/__init__.py +11 -0
  203. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  204. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  205. quantum/runtime/executors/scripting/python_executor.py +249 -0
  206. quantum/runtime/executors/services/__init__.py +12 -0
  207. quantum/runtime/executors/services/dump_executor.py +72 -0
  208. quantum/runtime/executors/services/file_executor.py +89 -0
  209. quantum/runtime/executors/services/log_executor.py +77 -0
  210. quantum/runtime/executors/services/mail_executor.py +81 -0
  211. quantum/runtime/expression_cache.py +498 -0
  212. quantum/runtime/file_upload_service.py +326 -0
  213. quantum/runtime/function_registry.py +118 -0
  214. quantum/runtime/game_builder.py +166 -0
  215. quantum/runtime/game_code_generator.py +2371 -0
  216. quantum/runtime/game_templates.py +2006 -0
  217. quantum/runtime/godot_code_generator.py +4681 -0
  218. quantum/runtime/godot_templates.py +1449 -0
  219. quantum/runtime/job_executor.py +1599 -0
  220. quantum/runtime/knowledge_service.py +500 -0
  221. quantum/runtime/llm_cache.py +100 -0
  222. quantum/runtime/llm_providers.py +704 -0
  223. quantum/runtime/llm_service.py +287 -0
  224. quantum/runtime/logging_setup.py +140 -0
  225. quantum/runtime/message_broker.py +364 -0
  226. quantum/runtime/message_queue_service.py +571 -0
  227. quantum/runtime/param_validation.py +184 -0
  228. quantum/runtime/pypy_compat.py +315 -0
  229. quantum/runtime/python_bridge.py +698 -0
  230. quantum/runtime/query_validators.py +304 -0
  231. quantum/runtime/renderer.py +733 -0
  232. quantum/runtime/service_container.py +444 -0
  233. quantum/runtime/terminal_builder.py +76 -0
  234. quantum/runtime/terminal_code_generator.py +607 -0
  235. quantum/runtime/terminal_templates.py +243 -0
  236. quantum/runtime/testing_builder.py +77 -0
  237. quantum/runtime/testing_code_generator.py +833 -0
  238. quantum/runtime/testing_templates.py +85 -0
  239. quantum/runtime/ui_builder.py +188 -0
  240. quantum/runtime/ui_desktop_adapter.py +1730 -0
  241. quantum/runtime/ui_desktop_templates.py +307 -0
  242. quantum/runtime/ui_html_adapter.py +2691 -0
  243. quantum/runtime/ui_html_templates.py +2297 -0
  244. quantum/runtime/ui_mobile_adapter.py +1832 -0
  245. quantum/runtime/ui_mobile_templates.py +1003 -0
  246. quantum/runtime/ui_textual_adapter.py +1866 -0
  247. quantum/runtime/ui_textual_templates.py +45 -0
  248. quantum/runtime/ui_tokens.py +465 -0
  249. quantum/runtime/ui_validator.py +365 -0
  250. quantum/runtime/validators.py +256 -0
  251. quantum/runtime/web_server.py +1766 -0
  252. quantum/runtime/websocket_adapter.py +501 -0
  253. quantum/runtime/websocket_service.py +585 -0
  254. quantum/runtime/websocket_transport.py +289 -0
  255. quantum/runtime/wsgi.py +101 -0
  256. quantum/utils/__init__.py +1 -0
  257. quantum_framework-0.9.0.dist-info/METADATA +244 -0
  258. quantum_framework-0.9.0.dist-info/RECORD +262 -0
  259. quantum_framework-0.9.0.dist-info/WHEEL +5 -0
  260. quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
  261. quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
  262. quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
quantum/cli/mq.py ADDED
@@ -0,0 +1,582 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Quantum CLI - Message Queue Commands
5
+
6
+ Commands for managing message queues, publishing messages, and running consumers.
7
+ """
8
+
9
+ import argparse
10
+ import sys
11
+ import json
12
+ from pathlib import Path
13
+ from datetime import datetime
14
+ from typing import Optional
15
+
16
+ # Add parent to path for imports
17
+
18
+
19
+ def create_mq_parser(subparsers):
20
+ """Add mq subcommand to CLI parser"""
21
+ mq_parser = subparsers.add_parser(
22
+ 'mq',
23
+ help='Message queue operations',
24
+ description='Commands for managing message queues, topics, and consumers'
25
+ )
26
+
27
+ mq_sub = mq_parser.add_subparsers(dest='mq_action', help='MQ action')
28
+
29
+ # Queues management
30
+ queues_parser = mq_sub.add_parser('queues', help='Manage message queues')
31
+ queues_sub = queues_parser.add_subparsers(dest='queues_action', help='Queue action')
32
+
33
+ list_queues = queues_sub.add_parser('list', help='List all queues')
34
+ list_queues.add_argument('--format', choices=['table', 'json'], default='table')
35
+
36
+ create_queue = queues_sub.add_parser('create', help='Create a queue')
37
+ create_queue.add_argument('name', help='Queue name')
38
+ create_queue.add_argument('--durable', action='store_true', default=True, help='Durable queue')
39
+ create_queue.add_argument('--dlq', help='Dead letter queue name')
40
+ create_queue.add_argument('--ttl', type=int, help='Message TTL in milliseconds')
41
+
42
+ delete_queue = queues_sub.add_parser('delete', help='Delete a queue')
43
+ delete_queue.add_argument('name', help='Queue name')
44
+ delete_queue.add_argument('--force', action='store_true', help='Force delete even if not empty')
45
+
46
+ purge_queue = queues_sub.add_parser('purge', help='Purge all messages from a queue')
47
+ purge_queue.add_argument('name', help='Queue name')
48
+
49
+ peek_queue = queues_sub.add_parser('peek', help='Peek messages without consuming')
50
+ peek_queue.add_argument('name', help='Queue name')
51
+ peek_queue.add_argument('--limit', type=int, default=10, help='Number of messages to peek')
52
+ peek_queue.add_argument('--format', choices=['table', 'json'], default='table')
53
+
54
+ # Topics management
55
+ topics_parser = mq_sub.add_parser('topics', help='Manage topics')
56
+ topics_sub = topics_parser.add_subparsers(dest='topics_action', help='Topic action')
57
+
58
+ list_topics = topics_sub.add_parser('list', help='List all topics')
59
+ list_topics.add_argument('--format', choices=['table', 'json'], default='table')
60
+
61
+ # Publish message
62
+ publish_parser = mq_sub.add_parser('publish', help='Publish a message to a topic')
63
+ publish_parser.add_argument('topic', help='Topic name')
64
+ publish_parser.add_argument('message', nargs='?', help='Message body (JSON string or plain text)')
65
+ publish_parser.add_argument('--file', help='Read message from file')
66
+ publish_parser.add_argument('--header', action='append', dest='headers',
67
+ help='Add header (format: key=value)')
68
+
69
+ # Send to queue
70
+ send_parser = mq_sub.add_parser('send', help='Send a message to a queue')
71
+ send_parser.add_argument('queue', help='Queue name')
72
+ send_parser.add_argument('message', nargs='?', help='Message body (JSON string or plain text)')
73
+ send_parser.add_argument('--file', help='Read message from file')
74
+ send_parser.add_argument('--header', action='append', dest='headers',
75
+ help='Add header (format: key=value)')
76
+ send_parser.add_argument('--delay', type=int, help='Delay in seconds before delivery')
77
+
78
+ # Consume messages
79
+ consume_parser = mq_sub.add_parser('consume', help='Consume messages from a queue')
80
+ consume_parser.add_argument('queue', help='Queue name')
81
+ consume_parser.add_argument('--limit', type=int, default=1, help='Number of messages to consume')
82
+ consume_parser.add_argument('--timeout', type=int, default=5, help='Timeout in seconds')
83
+ consume_parser.add_argument('--ack', action='store_true', help='Auto-acknowledge messages')
84
+ consume_parser.add_argument('--format', choices=['table', 'json'], default='json')
85
+
86
+ # Subscribe to topic
87
+ subscribe_parser = mq_sub.add_parser('subscribe', help='Subscribe to a topic (real-time)')
88
+ subscribe_parser.add_argument('topic', help='Topic pattern (supports wildcards)')
89
+ subscribe_parser.add_argument('--format', choices=['raw', 'json'], default='json')
90
+
91
+ # Worker mode
92
+ worker_parser = mq_sub.add_parser('worker', help='Start a message worker')
93
+ worker_parser.add_argument('--queues', required=True, help='Queues to consume (comma-separated)')
94
+ worker_parser.add_argument('--handler', help='Handler script (.q file)')
95
+ worker_parser.add_argument('--concurrency', type=int, default=4, help='Number of concurrent consumers')
96
+ worker_parser.add_argument('--prefetch', type=int, default=1, help='Prefetch count')
97
+
98
+ # Stats
99
+ stats_parser = mq_sub.add_parser('stats', help='Show message broker statistics')
100
+ stats_parser.add_argument('--format', choices=['table', 'json'], default='table')
101
+
102
+ # Connection info
103
+ info_parser = mq_sub.add_parser('info', help='Show connection information')
104
+
105
+ return mq_parser
106
+
107
+
108
+ def handle_mq(args) -> int:
109
+ """Handle mq command"""
110
+ from quantum.runtime.message_broker import MessageBroker, Message
111
+ from quantum.runtime.adapters.memory_adapter import InMemoryAdapter
112
+
113
+ if not args.mq_action:
114
+ print("Usage: quantum mq <action>")
115
+ print("Actions: queues, topics, publish, send, consume, subscribe, worker, stats, info")
116
+ return 1
117
+
118
+ try:
119
+ # Initialize broker (use in-memory by default, can be configured)
120
+ broker = _get_broker()
121
+
122
+ # Actions that only make sense ACROSS processes. With the default
123
+ # in-memory broker they silently do nothing, so say so.
124
+ if args.mq_action in ('publish', 'send', 'consume', 'subscribe',
125
+ 'worker', 'stats'):
126
+ _warn_if_ephemeral(broker, f"mq {args.mq_action}")
127
+
128
+ if args.mq_action == 'queues':
129
+ return _handle_mq_queues(broker, args)
130
+ elif args.mq_action == 'topics':
131
+ return _handle_mq_topics(broker, args)
132
+ elif args.mq_action == 'publish':
133
+ return _handle_mq_publish(broker, args)
134
+ elif args.mq_action == 'send':
135
+ return _handle_mq_send(broker, args)
136
+ elif args.mq_action == 'consume':
137
+ return _handle_mq_consume(broker, args)
138
+ elif args.mq_action == 'subscribe':
139
+ return _handle_mq_subscribe(broker, args)
140
+ elif args.mq_action == 'worker':
141
+ return _handle_mq_worker(broker, args)
142
+ elif args.mq_action == 'stats':
143
+ return _handle_mq_stats(broker, args)
144
+ elif args.mq_action == 'info':
145
+ return _handle_mq_info(broker, args)
146
+ else:
147
+ print(f"Unknown action: {args.mq_action}")
148
+ return 1
149
+
150
+ except Exception as e:
151
+ print(f"[ERROR] {e}")
152
+ import traceback
153
+ traceback.print_exc()
154
+ return 1
155
+
156
+
157
+ def _get_broker():
158
+ """Get message broker instance based on configuration"""
159
+ import os
160
+
161
+ broker_type = os.environ.get('QUANTUM_MQ_BROKER', 'memory')
162
+
163
+ if broker_type == 'rabbitmq':
164
+ try:
165
+ from quantum.runtime.adapters.rabbitmq_adapter import RabbitMQAdapter
166
+ broker = RabbitMQAdapter()
167
+ broker.connect({
168
+ 'host': os.environ.get('RABBITMQ_HOST', 'localhost'),
169
+ 'port': int(os.environ.get('RABBITMQ_PORT', 5672)),
170
+ 'username': os.environ.get('RABBITMQ_USER', 'guest'),
171
+ 'password': os.environ.get('RABBITMQ_PASS', 'guest'),
172
+ 'vhost': os.environ.get('RABBITMQ_VHOST', '/'),
173
+ })
174
+ return broker
175
+ except ImportError:
176
+ print("[WARN] RabbitMQ adapter not available, using in-memory")
177
+
178
+ elif broker_type == 'redis':
179
+ try:
180
+ from quantum.runtime.adapters.redis_adapter import RedisAdapter
181
+ broker = RedisAdapter()
182
+ broker.connect({
183
+ 'host': os.environ.get('REDIS_HOST', 'localhost'),
184
+ 'port': int(os.environ.get('REDIS_PORT', 6379)),
185
+ 'db': int(os.environ.get('REDIS_DB', 0)),
186
+ 'password': os.environ.get('REDIS_PASSWORD'),
187
+ })
188
+ return broker
189
+ except ImportError:
190
+ print("[WARN] Redis adapter not available, using in-memory")
191
+
192
+ if broker_type == 'sqlite':
193
+ from quantum.runtime.adapters.sqlite_adapter import SqliteAdapter
194
+ broker = SqliteAdapter()
195
+ broker.connect({'path': os.environ.get('QUANTUM_MQ_PATH', 'quantum_messages.db')})
196
+ return broker
197
+
198
+ # Default: in-memory.
199
+ #
200
+ # This broker lives inside THIS PROCESS and dies with it. Every `quantum
201
+ # mq` invocation is its own process, so `mq publish` wrote into a broker
202
+ # that was discarded a moment later and `mq consume` created a fresh empty
203
+ # one — every message silently lost, with the CLI reporting success both
204
+ # times. Nothing said the messages were going nowhere.
205
+ #
206
+ # It still exists because it is genuinely useful in-process (a .q file
207
+ # that publishes and subscribes in one run), so this warns rather than
208
+ # refusing; the caller decides whether the operation is cross-process.
209
+ from quantum.runtime.adapters.memory_adapter import InMemoryAdapter
210
+ broker = InMemoryAdapter()
211
+ broker.connect({})
212
+ broker._is_ephemeral_cli_broker = True
213
+ return broker
214
+
215
+
216
+ def _warn_if_ephemeral(broker, action: str) -> None:
217
+ """Say plainly that a cross-process mq action cannot work in memory."""
218
+ if not getattr(broker, '_is_ephemeral_cli_broker', False):
219
+ return
220
+ print(
221
+ f"[WARN] QUANTUM_MQ_BROKER is 'memory' (the default). That broker lives\n"
222
+ f" inside this process and is discarded when it exits, so "
223
+ f"`{action}` cannot\n"
224
+ f" reach any other command. Messages published this way are lost.\n"
225
+ f" Set QUANTUM_MQ_BROKER=rabbitmq or =redis for anything that has to\n"
226
+ f" cross process boundaries."
227
+ )
228
+
229
+
230
+ def _parse_headers(headers_list):
231
+ """Parse headers from command line format"""
232
+ if not headers_list:
233
+ return {}
234
+
235
+ result = {}
236
+ for h in headers_list:
237
+ if '=' in h:
238
+ key, value = h.split('=', 1)
239
+ result[key.strip()] = value.strip()
240
+ return result
241
+
242
+
243
+ def _handle_mq_queues(broker, args) -> int:
244
+ """Handle queue management commands"""
245
+ if not args.queues_action:
246
+ print("Usage: quantum mq queues <list|create|delete|purge|peek>")
247
+ return 1
248
+
249
+ if args.queues_action == 'list':
250
+ queues = broker.list_queues() if hasattr(broker, 'list_queues') else []
251
+
252
+ if args.format == 'json':
253
+ print(json.dumps(queues, indent=2))
254
+ else:
255
+ if not queues:
256
+ print("No queues found.")
257
+ return 0
258
+
259
+ print(f"{'Queue':<30} {'Messages':<10} {'Consumers':<10}")
260
+ print("-" * 55)
261
+ for q in queues:
262
+ name = q if isinstance(q, str) else q.get('name', 'unknown')
263
+ messages = q.get('messages', 0) if isinstance(q, dict) else 0
264
+ consumers = q.get('consumers', 0) if isinstance(q, dict) else 0
265
+ print(f"{name:<30} {messages:<10} {consumers:<10}")
266
+
267
+ return 0
268
+
269
+ elif args.queues_action == 'create':
270
+ options = {
271
+ 'durable': args.durable,
272
+ }
273
+ if args.dlq:
274
+ options['dead_letter_queue'] = args.dlq
275
+ if args.ttl:
276
+ options['ttl'] = args.ttl
277
+
278
+ broker.declare_queue(args.name, **options)
279
+ print(f"Queue created: {args.name}")
280
+ return 0
281
+
282
+ elif args.queues_action == 'delete':
283
+ if hasattr(broker, 'delete_queue'):
284
+ broker.delete_queue(args.name, force=args.force)
285
+ print(f"Queue deleted: {args.name}")
286
+ else:
287
+ print("Delete queue not supported by this broker")
288
+ return 1
289
+ return 0
290
+
291
+ elif args.queues_action == 'purge':
292
+ if hasattr(broker, 'purge_queue'):
293
+ count = broker.purge_queue(args.name)
294
+ print(f"Purged {count} message(s) from queue: {args.name}")
295
+ else:
296
+ print("Purge queue not supported by this broker")
297
+ return 1
298
+ return 0
299
+
300
+ elif args.queues_action == 'peek':
301
+ if hasattr(broker, 'peek_queue'):
302
+ messages = broker.peek_queue(args.name, limit=args.limit)
303
+ else:
304
+ print("Peek not supported by this broker")
305
+ return 1
306
+
307
+ if args.format == 'json':
308
+ print(json.dumps([m.__dict__ for m in messages], indent=2, default=str))
309
+ else:
310
+ if not messages:
311
+ print("No messages in queue.")
312
+ return 0
313
+
314
+ print(f"{'ID':<36} {'Body (truncated)':<50}")
315
+ print("-" * 90)
316
+ for msg in messages:
317
+ body_str = str(msg.body)[:50]
318
+ print(f"{msg.id:<36} {body_str:<50}")
319
+
320
+ return 0
321
+
322
+ return 1
323
+
324
+
325
+ def _handle_mq_topics(broker, args) -> int:
326
+ """Handle topics management"""
327
+ if not args.topics_action:
328
+ print("Usage: quantum mq topics <list>")
329
+ return 1
330
+
331
+ if args.topics_action == 'list':
332
+ topics = broker.list_topics() if hasattr(broker, 'list_topics') else []
333
+
334
+ if args.format == 'json':
335
+ print(json.dumps(topics, indent=2))
336
+ else:
337
+ if not topics:
338
+ print("No topics found.")
339
+ return 0
340
+
341
+ print("Topics:")
342
+ for t in topics:
343
+ print(f" - {t}")
344
+
345
+ return 0
346
+
347
+ return 1
348
+
349
+
350
+ def _handle_mq_publish(broker, args) -> int:
351
+ """Publish message to topic"""
352
+ from quantum.runtime.message_broker import Message
353
+
354
+ # Get message body
355
+ if args.file:
356
+ with open(args.file, 'r') as f:
357
+ body = f.read()
358
+ elif args.message:
359
+ body = args.message
360
+ else:
361
+ print("Error: Message body required (use positional argument or --file)")
362
+ return 1
363
+
364
+ # Try to parse as JSON
365
+ try:
366
+ body = json.loads(body)
367
+ except json.JSONDecodeError:
368
+ pass # Keep as string
369
+
370
+ # Create message
371
+ message = Message(
372
+ topic=args.topic,
373
+ body=body,
374
+ headers=_parse_headers(args.headers)
375
+ )
376
+
377
+ # Publish
378
+ broker.publish(args.topic, message)
379
+ print(f"Published to topic: {args.topic}")
380
+ print(f"Message ID: {message.id}")
381
+
382
+ return 0
383
+
384
+
385
+ def _handle_mq_send(broker, args) -> int:
386
+ """Send message to queue"""
387
+ from quantum.runtime.message_broker import Message
388
+
389
+ # Get message body
390
+ if args.file:
391
+ with open(args.file, 'r') as f:
392
+ body = f.read()
393
+ elif args.message:
394
+ body = args.message
395
+ else:
396
+ print("Error: Message body required (use positional argument or --file)")
397
+ return 1
398
+
399
+ # Try to parse as JSON
400
+ try:
401
+ body = json.loads(body)
402
+ except json.JSONDecodeError:
403
+ pass # Keep as string
404
+
405
+ # Create message
406
+ message = Message(
407
+ queue=args.queue,
408
+ body=body,
409
+ headers=_parse_headers(args.headers)
410
+ )
411
+
412
+ # Send (with delay if specified)
413
+ if args.delay:
414
+ if hasattr(broker, 'send_delayed'):
415
+ broker.send_delayed(args.queue, message, delay=args.delay)
416
+ else:
417
+ print("[WARN] Delayed send not supported, sending immediately")
418
+ broker.send(args.queue, message)
419
+ else:
420
+ broker.send(args.queue, message)
421
+
422
+ print(f"Sent to queue: {args.queue}")
423
+ print(f"Message ID: {message.id}")
424
+
425
+ return 0
426
+
427
+
428
+ def _handle_mq_consume(broker, args) -> int:
429
+ """Consume messages from queue"""
430
+ import time
431
+
432
+ messages = []
433
+ start_time = time.time()
434
+
435
+ def handler(msg):
436
+ messages.append(msg)
437
+ if args.ack:
438
+ broker.ack(msg)
439
+
440
+ # Start consuming
441
+ consumer_id = broker.consume(args.queue, handler)
442
+
443
+ # Wait for messages
444
+ while len(messages) < args.limit and (time.time() - start_time) < args.timeout:
445
+ time.sleep(0.1)
446
+
447
+ # Stop consumer if supported
448
+ if hasattr(broker, 'cancel_consumer'):
449
+ broker.cancel_consumer(consumer_id)
450
+
451
+ # Output
452
+ if args.format == 'json':
453
+ print(json.dumps([{
454
+ 'id': m.id,
455
+ 'body': m.body,
456
+ 'headers': m.headers,
457
+ 'timestamp': m.timestamp
458
+ } for m in messages], indent=2, default=str))
459
+ else:
460
+ for msg in messages:
461
+ print(f"--- Message {msg.id} ---")
462
+ print(json.dumps(msg.body, indent=2) if isinstance(msg.body, dict) else msg.body)
463
+ print()
464
+
465
+ if not messages:
466
+ print("No messages received within timeout.")
467
+
468
+ return 0
469
+
470
+
471
+ def _handle_mq_subscribe(broker, args) -> int:
472
+ """Subscribe to topic (real-time)"""
473
+ print(f"Subscribing to topic: {args.topic}")
474
+ print("Press Ctrl+C to stop.\n")
475
+
476
+ def handler(msg):
477
+ if args.format == 'json':
478
+ print(json.dumps({
479
+ 'topic': msg.topic,
480
+ 'body': msg.body,
481
+ 'headers': msg.headers,
482
+ 'timestamp': msg.timestamp
483
+ }, indent=2, default=str))
484
+ else:
485
+ print(f"[{msg.topic}] {msg.body}")
486
+
487
+ try:
488
+ consumer_id = broker.subscribe(args.topic, handler)
489
+
490
+ # Keep running
491
+ import time
492
+ while True:
493
+ time.sleep(1)
494
+
495
+ except KeyboardInterrupt:
496
+ print("\nUnsubscribing...")
497
+ if hasattr(broker, 'unsubscribe'):
498
+ broker.unsubscribe(consumer_id)
499
+
500
+ return 0
501
+
502
+
503
+ def _handle_mq_worker(broker, args) -> int:
504
+ """Start message worker"""
505
+ queues = [q.strip() for q in args.queues.split(',')]
506
+
507
+ print(f"Starting message worker...")
508
+ print(f" Queues: {', '.join(queues)}")
509
+ print(f" Concurrency: {args.concurrency}")
510
+ print(f" Prefetch: {args.prefetch}")
511
+ if args.handler:
512
+ print(f" Handler: {args.handler}")
513
+ print("")
514
+ print("Press Ctrl+C to stop.\n")
515
+
516
+ # Load handler if specified
517
+ handler_func = None
518
+ if args.handler:
519
+ from quantum.core.parser import QuantumParser
520
+ from quantum.runtime.component import ComponentRuntime
521
+
522
+ parser = QuantumParser()
523
+ runtime = ComponentRuntime()
524
+ ast = parser.parse_file(args.handler)
525
+
526
+ def handler_func(msg):
527
+ context = {'message': msg}
528
+ runtime.execute(ast, context)
529
+ else:
530
+ def handler_func(msg):
531
+ print(f"[{msg.queue}] {msg.id}: {msg.body}")
532
+
533
+ consumers = []
534
+
535
+ try:
536
+ for queue in queues:
537
+ for i in range(args.concurrency):
538
+ consumer_id = broker.consume(queue, handler_func)
539
+ consumers.append(consumer_id)
540
+ print(f"Started consumer {i+1} for queue: {queue}")
541
+
542
+ # Keep running
543
+ import time
544
+ while True:
545
+ time.sleep(1)
546
+
547
+ except KeyboardInterrupt:
548
+ print("\nStopping workers...")
549
+ for consumer_id in consumers:
550
+ if hasattr(broker, 'cancel_consumer'):
551
+ broker.cancel_consumer(consumer_id)
552
+ print("Workers stopped.")
553
+
554
+ return 0
555
+
556
+
557
+ def _handle_mq_stats(broker, args) -> int:
558
+ """Show broker statistics"""
559
+ stats = broker.get_stats() if hasattr(broker, 'get_stats') else {}
560
+
561
+ if args.format == 'json':
562
+ print(json.dumps(stats, indent=2, default=str))
563
+ else:
564
+ print("Message Broker Statistics")
565
+ print("-" * 40)
566
+ for key, value in stats.items():
567
+ print(f" {key}: {value}")
568
+
569
+ return 0
570
+
571
+
572
+ def _handle_mq_info(broker, args) -> int:
573
+ """Show connection info"""
574
+ info = broker.get_info() if hasattr(broker, 'get_info') else {}
575
+
576
+ print("Message Broker Connection")
577
+ print("-" * 40)
578
+ print(f" Type: {type(broker).__name__}")
579
+ for key, value in info.items():
580
+ print(f" {key}: {value}")
581
+
582
+ return 0