quantum-framework 0.18.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 (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -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 +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,228 @@
1
+ """
2
+ Invoke Executor - Execute q:invoke statements
3
+
4
+ Handles function, component, and HTTP invocations with caching.
5
+ """
6
+
7
+ from typing import Any, List, Dict, Type
8
+ import json
9
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
10
+ from quantum.core.features.invocation.src.ast_node import InvokeNode
11
+
12
+
13
+ class InvokeExecutor(BaseExecutor):
14
+ """
15
+ Executor for q:invoke statements.
16
+
17
+ Supports:
18
+ - Function invocation (local functions)
19
+ - Component invocation (other .q components)
20
+ - HTTP invocation (REST APIs)
21
+ - Response caching with TTL
22
+ """
23
+
24
+ @property
25
+ def handles(self) -> List[Type]:
26
+ return [InvokeNode]
27
+
28
+ def execute(self, node: InvokeNode, exec_context) -> Any:
29
+ """
30
+ Execute invocation.
31
+
32
+ Args:
33
+ node: InvokeNode with invocation configuration
34
+ exec_context: Execution context
35
+
36
+ Returns:
37
+ None (stores result in context)
38
+ """
39
+ try:
40
+ context = exec_context.get_all_variables()
41
+
42
+ invocation_type = node.get_invocation_type()
43
+
44
+ if invocation_type == "unknown":
45
+ raise ExecutorError(
46
+ f"Invoke '{node.name}' requires one of: function, component, url or service"
47
+ )
48
+
49
+ # Build parameters based on type
50
+ if invocation_type == "function":
51
+ params = self._build_function_params(node, context)
52
+ elif invocation_type == "component":
53
+ params = self._build_component_params(node, context)
54
+ elif invocation_type == "http":
55
+ params = self._build_http_params(node, context)
56
+ elif invocation_type == "service":
57
+ result = self._invoke_service(node, context)
58
+ self._store_result(node, result, exec_context)
59
+ params = None
60
+ else:
61
+ raise ExecutorError(f"Unsupported invocation type: {invocation_type}")
62
+
63
+ # Check cache
64
+ if params is None:
65
+ pass
66
+ elif node.cache:
67
+ cache_key = f"invoke_{node.name}_{hash(str(params))}"
68
+ cached = self.services.invocation.get_from_cache(cache_key)
69
+ if cached is not None:
70
+ self._store_result(node, cached, exec_context)
71
+ return
72
+
73
+ if params is not None:
74
+ # Execute invocation
75
+ result = self.services.invocation.invoke(
76
+ invocation_type,
77
+ params,
78
+ context=self.runtime
79
+ )
80
+
81
+ # Cache result
82
+ if node.cache and result.success:
83
+ cache_key = f"invoke_{node.name}_{hash(str(params))}"
84
+ self.services.invocation.put_in_cache(cache_key, result, node.ttl)
85
+
86
+ # Store result
87
+ self._store_result(node, result, exec_context)
88
+
89
+ except Exception as e:
90
+ raise ExecutorError(f"Invoke execution error in '{node.name}': {e}")
91
+
92
+ # INV-2: same rule as q:data and q:query — a failure stops the
93
+ # component, unless onerror="continue" says the program handles it.
94
+ if not result.success and getattr(node, 'on_error', 'fail') != 'continue':
95
+ erro = result.error
96
+ motivo = erro.get('message', 'unknown error') if isinstance(erro, dict) else str(erro)
97
+ raise ExecutorError(
98
+ f"q:invoke '{node.name}' failed: {motivo}. "
99
+ f"Add onerror=\"continue\" to handle it with {node.name}_result instead.")
100
+
101
+ def _invoke_service(self, node: InvokeNode, context: Dict[str, Any]):
102
+ """q:invoke service="name" (SVC-3): call a function declared with @service.
103
+
104
+ The q:param children are the keyword arguments, converted by their
105
+ type= like any other q:param. It was parsed since the first version
106
+ ("Service discovery (Phase 2)") and failed at runtime with
107
+ "Unsupported invocation type: service".
108
+ """
109
+ import time
110
+ from quantum import services
111
+ from quantum.core.features.invocation.src.runtime import InvocationResult
112
+ from quantum.runtime import param_validation
113
+
114
+ services.load(self.services.config.get('services') or [])
115
+ func = services.get(node.service)
116
+
117
+ args = self._param_args(node, context)
118
+ for param in node.params:
119
+ if param.name in args and getattr(param, 'type', None):
120
+ valor, erro = param_validation.coerce(param, args[param.name])
121
+ if erro:
122
+ raise ExecutorError(erro)
123
+ args[param.name] = valor
124
+
125
+ inicio = time.time()
126
+ try:
127
+ data = func(**args)
128
+ except TypeError as exc:
129
+ return InvocationResult(success=False, invocation_type='service',
130
+ error={'message': f"service '{node.service}': {exc}"})
131
+ except Exception as exc: # noqa: BLE001 — the service's own failure (INV-2)
132
+ return InvocationResult(success=False, invocation_type='service',
133
+ error={'message': f"service '{node.service}' failed: {exc}",
134
+ 'type': type(exc).__name__})
135
+ return InvocationResult(success=True, data=data, invocation_type='service',
136
+ execution_time=(time.time() - inicio) * 1000)
137
+
138
+ def _param_args(self, node: InvokeNode, context: Dict[str, Any]) -> Dict[str, Any]:
139
+ """Resolve <q:param> children into call arguments.
140
+
141
+ This used to read `param.default`, but the invoke parser stores the
142
+ value= attribute on `param.value` — `default` is only the fallback.
143
+ So every argument arrived as the empty string and
144
+ <q:param name="n" value="21" /> passed ''.
145
+ """
146
+ args = {}
147
+ for param in node.params:
148
+ raw = getattr(param, 'value', None)
149
+ if raw is None:
150
+ raw = param.default
151
+ args[param.name] = self.apply_databinding(raw if raw is not None else "", context)
152
+ return args
153
+
154
+ def _build_function_params(self, node: InvokeNode, context: Dict[str, Any]) -> Dict[str, Any]:
155
+ """Build parameters for function invocation"""
156
+ args = self._param_args(node, context)
157
+ return {
158
+ 'function': node.function,
159
+ 'args': args
160
+ }
161
+
162
+ def _build_component_params(self, node: InvokeNode, context: Dict[str, Any]) -> Dict[str, Any]:
163
+ """Build parameters for component invocation"""
164
+ args = self._param_args(node, context)
165
+ return {
166
+ 'component': node.component,
167
+ 'args': args
168
+ }
169
+
170
+ def _build_http_params(self, node: InvokeNode, context: Dict[str, Any]) -> Dict[str, Any]:
171
+ """Build parameters for HTTP invocation"""
172
+ url = self.apply_databinding(node.url, context)
173
+
174
+ headers = {}
175
+ for header_node in node.headers:
176
+ header_value = self.apply_databinding(header_node.value, context)
177
+ headers[header_node.name] = header_value
178
+
179
+ if 'Content-Type' not in headers and 'content-type' not in headers:
180
+ headers['Content-Type'] = node.content_type
181
+
182
+ # Same value=/default fallback as function and component calls: this
183
+ # read only `default`, so <q:param name="q" value="x"/> sent q=''.
184
+ query_params = self._param_args(node, context)
185
+
186
+ body = None
187
+ if node.body:
188
+ body = self.apply_databinding(node.body, context)
189
+ if isinstance(body, str) and 'json' in node.content_type.lower():
190
+ try:
191
+ body = json.loads(body)
192
+ except:
193
+ pass
194
+
195
+ return {
196
+ 'url': url,
197
+ 'method': node.method,
198
+ 'headers': headers,
199
+ 'params': query_params,
200
+ 'body': body,
201
+ 'auth_type': node.auth_type,
202
+ 'auth_token': self.apply_databinding(node.auth_token, context) if node.auth_token else None,
203
+ 'auth_header': node.auth_header,
204
+ 'auth_username': self.apply_databinding(node.auth_username, context) if node.auth_username else None,
205
+ 'auth_password': self.apply_databinding(node.auth_password, context) if node.auth_password else None,
206
+ 'timeout': node.timeout,
207
+ 'retry': node.retry,
208
+ 'retry_delay': node.retry_delay,
209
+ 'response_format': node.response_format
210
+ }
211
+
212
+ def _store_result(self, node: InvokeNode, result, exec_context):
213
+ """Store invocation result in context"""
214
+ exec_context.set_variable(node.name, result.data, scope="component")
215
+
216
+ result_dict = {
217
+ 'success': result.success,
218
+ 'data': result.data,
219
+ 'error': result.error,
220
+ 'executionTime': result.execution_time,
221
+ 'invocationType': result.invocation_type,
222
+ 'metadata': result.metadata
223
+ }
224
+
225
+ exec_context.set_variable(f"{node.name}_result", result_dict, scope="component")
226
+
227
+ if node.result:
228
+ exec_context.set_variable(node.result, result_dict, scope="component")
@@ -0,0 +1,290 @@
1
+ """
2
+ Query Executor - Execute q:query statements
3
+
4
+ Handles database queries with parameter validation, pagination, and Query of Queries.
5
+ """
6
+
7
+ from typing import Any, List, Dict, Type
8
+ import sqlite3
9
+ import time
10
+ import re
11
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
12
+ from quantum.core.ast_nodes import QueryNode
13
+
14
+
15
+ class QueryExecutor(BaseExecutor):
16
+ """
17
+ Executor for q:query statements.
18
+
19
+ Supports:
20
+ - Database queries with parameterized SQL
21
+ - Pagination with automatic COUNT
22
+ - Query of Queries (in-memory SQL on result sets)
23
+ - Knowledge base queries (virtual datasource)
24
+ """
25
+
26
+ @property
27
+ def handles(self) -> List[Type]:
28
+ return [QueryNode]
29
+
30
+ def execute(self, node: QueryNode, exec_context) -> Any:
31
+ """
32
+ Execute database query.
33
+
34
+ Args:
35
+ node: QueryNode with query configuration
36
+ exec_context: Execution context
37
+
38
+ Returns:
39
+ QueryResult with data and metadata
40
+ """
41
+ try:
42
+ context = exec_context.get_all_variables()
43
+
44
+ # Resolve and validate parameters
45
+ resolved_params = self._resolve_params(node, context)
46
+
47
+ # Check for knowledge base query
48
+ if node.datasource and node.datasource.startswith('knowledge:'):
49
+ return self._execute_knowledge_query(node, resolved_params, exec_context)
50
+
51
+ # Check for Query of Queries
52
+ if node.source:
53
+ return self._execute_query_of_queries(node, context, resolved_params, exec_context)
54
+
55
+ # Execute standard database query
56
+ return self._execute_database_query(node, resolved_params, exec_context)
57
+
58
+ except Exception as e:
59
+ raise ExecutorError(f"Query execution error in '{node.name}': {e}")
60
+
61
+ def _resolve_params(self, node: QueryNode, context: Dict[str, Any]) -> Dict[str, Any]:
62
+ """Resolve and validate query parameters"""
63
+ from quantum.runtime.query_validators import QueryValidator, QueryValidationError
64
+
65
+ resolved_params = {}
66
+ for param_node in node.params:
67
+ param_value = self.apply_databinding(param_node.value, context)
68
+
69
+ attributes = {
70
+ 'null': param_node.null,
71
+ 'max_length': param_node.max_length,
72
+ 'scale': param_node.scale
73
+ }
74
+
75
+ try:
76
+ validated_value = QueryValidator.validate_param(
77
+ param_value,
78
+ param_node.param_type,
79
+ attributes
80
+ )
81
+ resolved_params[param_node.name] = validated_value
82
+ except QueryValidationError as e:
83
+ raise ExecutorError(f"Parameter '{param_node.name}' validation failed: {e}")
84
+
85
+ return resolved_params
86
+
87
+ def _execute_database_query(self, node: QueryNode, params: Dict[str, Any], exec_context) -> Any:
88
+ """Execute standard database query"""
89
+ from quantum.runtime.query_validators import QueryValidator
90
+
91
+ # Sanitize SQL
92
+ QueryValidator.sanitize_sql(node.sql)
93
+
94
+ # Handle pagination
95
+ pagination_metadata = None
96
+ sql_to_execute = self._sorted_sql(node, params, exec_context) # UI-13
97
+
98
+ if node.paginate:
99
+ pagination_metadata, sql_to_execute = self._handle_pagination(
100
+ node, params, exec_context, base_sql=sql_to_execute)
101
+
102
+ # Execute query
103
+ result = self.services.database.execute_query(
104
+ node.datasource,
105
+ sql_to_execute,
106
+ params
107
+ )
108
+
109
+ # Store results
110
+ self._store_query_result(node, result, pagination_metadata, exec_context)
111
+
112
+ return result
113
+
114
+ def _sorted_sql(self, node: QueryNode, params: Dict[str, Any], exec_context) -> str:
115
+ """UI-13: a sortable query orders by the URL's ?sort= (and ?dir=asc|desc).
116
+
117
+ The column must be one the query returns — found with LIMIT 0, never
118
+ taken from the URL otherwise; an unknown one leaves the order as
119
+ written (a URL is not an error)."""
120
+ if not getattr(node, 'sortable', False):
121
+ return node.sql
122
+ try:
123
+ consulta = exec_context.get_variable('query') if exec_context is not None else None
124
+ except Exception:
125
+ consulta = None
126
+ consulta = consulta if isinstance(consulta, dict) else {}
127
+ coluna = str(consulta.get('sort') or '').strip()
128
+ direcao = 'DESC' if str(consulta.get('dir') or '').lower() == 'desc' else 'ASC'
129
+ if not coluna:
130
+ return node.sql
131
+ base = node.sql.strip().rstrip(';')
132
+ sonda = self.services.database.execute_query(
133
+ node.datasource, f'SELECT * FROM ({base}) AS quantum_ordenada LIMIT 0', params)
134
+ if coluna not in (sonda.column_list or []):
135
+ return node.sql
136
+ return f'SELECT * FROM ({base}) AS quantum_ordenada ORDER BY "{coluna}" {direcao}'
137
+
138
+ def _handle_pagination(self, node: QueryNode, params: Dict[str, Any], exec_context=None,
139
+ base_sql: str = None):
140
+ """Handle query pagination"""
141
+ count_sql = self._generate_count_query(node.sql)
142
+ count_result = self.services.database.execute_query(
143
+ node.datasource,
144
+ count_sql,
145
+ params
146
+ )
147
+
148
+ total_records = count_result.data[0]['count'] if count_result.data else 0
149
+
150
+ page = node.page if node.page is not None else 1
151
+ if getattr(node, 'page_expr', None):
152
+ # DB-9: from the URL, so never an error — ?page=abc or ?page=-3 is page 1.
153
+ variaveis = exec_context.get_all_variables() if exec_context is not None else None
154
+ bruto = self.apply_databinding(node.page_expr, variaveis)
155
+ try:
156
+ page = max(1, int(str(bruto).strip()))
157
+ except (TypeError, ValueError):
158
+ page = 1
159
+ # paginate="true" without a page size is a config error, not a
160
+ # reason to crash on None arithmetic further down.
161
+ page_size = node.page_size or 10
162
+ total_pages = (total_records + page_size - 1) // page_size
163
+
164
+ offset = (page - 1) * page_size
165
+ sql_to_execute = f"{base_sql or node.sql}\nLIMIT {page_size} OFFSET {offset}"
166
+
167
+ pagination_metadata = {
168
+ 'totalRecords': total_records,
169
+ 'totalPages': total_pages,
170
+ 'currentPage': page,
171
+ 'pageSize': page_size,
172
+ 'hasNextPage': page < total_pages,
173
+ 'hasPreviousPage': page > 1,
174
+ 'startRecord': offset + 1 if total_records > 0 else 0,
175
+ 'endRecord': min(offset + page_size, total_records)
176
+ }
177
+
178
+ return pagination_metadata, sql_to_execute
179
+
180
+ def _generate_count_query(self, original_sql: str) -> str:
181
+ """Generate COUNT(*) query from original SQL"""
182
+ sql = ' '.join(original_sql.split())
183
+ sql = re.sub(r'\s+ORDER\s+BY\s+[^;]+?(?=\s+(?:LIMIT|OFFSET|FOR\s+UPDATE)|$)', '', sql, flags=re.IGNORECASE)
184
+ sql = re.sub(r'\s+LIMIT\s+\d+', '', sql, flags=re.IGNORECASE)
185
+ sql = re.sub(r'\s+OFFSET\s+\d+', '', sql, flags=re.IGNORECASE)
186
+ return f"SELECT COUNT(*) as count FROM ({sql}) AS count_query"
187
+
188
+ def _execute_query_of_queries(self, node: QueryNode, context: Dict[str, Any],
189
+ params: Dict[str, Any], exec_context) -> Any:
190
+ """Execute Query of Queries - SQL on in-memory result sets"""
191
+ from quantum.runtime.database_service import QueryResult
192
+
193
+ source_name = node.source
194
+ source_data = context.get(source_name) or exec_context.get_variable(source_name)
195
+
196
+ if source_data is None:
197
+ raise ExecutorError(f"Source query '{source_name}' not found")
198
+
199
+ if not isinstance(source_data, list):
200
+ raise ExecutorError(f"Source '{source_name}' is not a query result")
201
+
202
+ if not source_data:
203
+ result = QueryResult(
204
+ success=True, data=[], record_count=0,
205
+ column_list=[], execution_time=0, sql=node.sql
206
+ )
207
+ self._store_query_result(node, result, None, exec_context)
208
+ return result
209
+
210
+ # Create in-memory SQLite database
211
+ conn = sqlite3.connect(':memory:')
212
+ conn.row_factory = sqlite3.Row
213
+ cursor = conn.cursor()
214
+
215
+ try:
216
+ first_row = source_data[0]
217
+ if not isinstance(first_row, dict):
218
+ raise ExecutorError("Source data must be list of dictionaries")
219
+
220
+ columns = list(first_row.keys())
221
+ column_defs = ', '.join([f'"{col}" TEXT' for col in columns])
222
+ cursor.execute(f'CREATE TABLE source_table ({column_defs})')
223
+
224
+ placeholders = ', '.join(['?' for _ in columns])
225
+ for row in source_data:
226
+ values = [row.get(col) for col in columns]
227
+ cursor.execute(f'INSERT INTO source_table VALUES ({placeholders})', values)
228
+
229
+ sql = node.sql.replace(f'FROM {source_name}', 'FROM source_table')
230
+ sql = sql.replace(f'FROM {{{source_name}}}', 'FROM source_table')
231
+
232
+ for param_name in params:
233
+ sql = sql.replace(f':{param_name}', '?')
234
+
235
+ start_time = time.time()
236
+ cursor.execute(sql, list(params.values()) if params else [])
237
+ result_rows = cursor.fetchall()
238
+ execution_time = (time.time() - start_time) * 1000
239
+
240
+ result_data = []
241
+ column_names = []
242
+ if result_rows:
243
+ column_names = [desc[0] for desc in cursor.description]
244
+ for row in result_rows:
245
+ result_data.append(dict(zip(column_names, row)))
246
+
247
+ conn.close()
248
+
249
+ result = QueryResult(
250
+ success=True,
251
+ data=result_data,
252
+ record_count=len(result_data),
253
+ column_list=column_names,
254
+ execution_time=int(execution_time),
255
+ sql=node.sql
256
+ )
257
+
258
+ self._store_query_result(node, result, None, exec_context)
259
+ return result
260
+
261
+ except sqlite3.Error as e:
262
+ conn.close()
263
+ raise ExecutorError(f"Query of Queries SQL error: {e}")
264
+
265
+ def _execute_knowledge_query(self, node: QueryNode, params: Dict[str, Any], exec_context) -> Any:
266
+ """Execute knowledge base query (RAG)"""
267
+ # Delegate to runtime's knowledge query handler
268
+ return self.runtime._execute_knowledge_query(node, params, exec_context)
269
+
270
+ def _store_query_result(self, node: QueryNode, result, pagination_metadata, exec_context):
271
+ """Store query result in context"""
272
+ result_dict = result.to_dict()
273
+
274
+ if pagination_metadata:
275
+ result_dict['pagination'] = pagination_metadata
276
+
277
+ # Store data as array
278
+ exec_context.set_variable(node.name, result.data, scope="component")
279
+
280
+ # Store full result with metadata
281
+ exec_context.set_variable(f"{node.name}_result", result_dict, scope="component")
282
+
283
+ # Single-row field exposure
284
+ if result.data and len(result.data) == 1 and isinstance(result.data[0], dict):
285
+ for field_name, field_value in result.data[0].items():
286
+ dotted_key = f"{node.name}.{field_name}"
287
+ exec_context.set_variable(dotted_key, field_value, scope="component")
288
+
289
+ if node.result:
290
+ exec_context.set_variable(node.result, result_dict, scope="component")
@@ -0,0 +1,91 @@
1
+ """
2
+ Transaction Executor - Execute q:transaction statements
3
+
4
+ Handles database transactions with atomic commit/rollback.
5
+ """
6
+
7
+ from typing import Any, List, Dict, Type
8
+ from quantum.runtime.executors.base import BaseExecutor, ExecutorError
9
+ from quantum.core.ast_nodes import TransactionNode, QueryNode
10
+
11
+
12
+ class TransactionExecutor(BaseExecutor):
13
+ """
14
+ Executor for q:transaction statements.
15
+
16
+ Supports:
17
+ - Atomic transaction execution
18
+ - Automatic rollback on failure
19
+ - Multiple isolation levels
20
+ - Nested statement execution
21
+ """
22
+
23
+ @property
24
+ def handles(self) -> List[Type]:
25
+ return [TransactionNode]
26
+
27
+ def execute(self, node: TransactionNode, exec_context) -> Any:
28
+ """
29
+ Execute database transaction.
30
+
31
+ Args:
32
+ node: TransactionNode with statements to execute
33
+ exec_context: Execution context
34
+
35
+ Returns:
36
+ Transaction result dict with success status
37
+ """
38
+ try:
39
+ # Determine datasource
40
+ datasource_name = self._get_datasource(node)
41
+
42
+ # Begin transaction
43
+ transaction_context = self.services.database.begin_transaction(datasource_name)
44
+
45
+ results = []
46
+ try:
47
+ # Execute all statements
48
+ for statement in node.statements:
49
+ result = self.execute_child(statement, exec_context)
50
+ results.append(result)
51
+
52
+ # Commit transaction
53
+ success = self.services.database.commit_transaction(transaction_context)
54
+
55
+ transaction_result = {
56
+ 'success': success,
57
+ 'committed': True,
58
+ 'isolation_level': node.isolation_level,
59
+ 'statement_count': len(node.statements),
60
+ 'results': results
61
+ }
62
+
63
+ except Exception as stmt_error:
64
+ # Rollback on failure
65
+ self.services.database.rollback_transaction(transaction_context)
66
+
67
+ transaction_result = {
68
+ 'success': False,
69
+ 'rolled_back': True,
70
+ 'error': str(stmt_error),
71
+ 'isolation_level': node.isolation_level
72
+ }
73
+
74
+ raise ExecutorError(f"Transaction failed and was rolled back: {stmt_error}")
75
+
76
+ # Store result
77
+ exec_context.set_variable('_transaction_result', transaction_result, scope="component")
78
+
79
+ return transaction_result
80
+
81
+ except ExecutorError:
82
+ raise
83
+ except Exception as e:
84
+ raise ExecutorError(f"Transaction execution error: {e}")
85
+
86
+ def _get_datasource(self, node: TransactionNode) -> str:
87
+ """Get datasource from first query in transaction"""
88
+ for stmt in node.statements:
89
+ if isinstance(stmt, QueryNode) and stmt.datasource:
90
+ return stmt.datasource
91
+ return "default"
@@ -0,0 +1,11 @@
1
+ """
2
+ Jobs Executors
3
+
4
+ Executors for job/scheduling operations: schedule, thread, job.
5
+ """
6
+
7
+ from .schedule_executor import ScheduleExecutor
8
+ from .thread_executor import ThreadExecutor
9
+ from .job_executor import JobExecutor
10
+
11
+ __all__ = ['ScheduleExecutor', 'ThreadExecutor', 'JobExecutor']