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
File without changes
@@ -0,0 +1,147 @@
1
+ """
2
+ Invocation Feature - AST Nodes
3
+ Defines InvokeNode and InvokeHeaderNode for q:invoke
4
+ """
5
+
6
+ from typing import List, Dict, Any
7
+ from quantum.core.ast_nodes import QuantumNode, QuantumParam
8
+
9
+
10
+ class InvokeNode(QuantumNode):
11
+ """Represents a <q:invoke> - Universal invocation for functions, components, HTTP, etc."""
12
+
13
+ def __init__(self, name: str):
14
+ self.name = name # Variable name for result
15
+
16
+ # Invocation target (determines type)
17
+ self.function = None # Local function name
18
+ self.component = None # Local component name
19
+ self.url = None # HTTP/REST endpoint
20
+ self.endpoint = None # GraphQL/SOAP endpoint
21
+ self.service = None # Service name (for service discovery)
22
+
23
+ # HTTP-specific
24
+ self.method = "GET" # GET, POST, PUT, DELETE, PATCH
25
+ self.headers: List['InvokeHeaderNode'] = []
26
+ self.body = None # Request body (for POST/PUT/PATCH)
27
+ self.content_type = "application/json"
28
+
29
+ # Authentication
30
+ self.auth_type = None # bearer, apikey, basic, oauth2
31
+ self.auth_token = None # Bearer token or API key
32
+ self.auth_header = None # Header name for API key (default: Authorization)
33
+ self.auth_username = None # Basic auth username
34
+ self.auth_password = None # Basic auth password
35
+
36
+ # Parameters (query params for HTTP, args for functions/components)
37
+ self.params: List[QuantumParam] = []
38
+
39
+ # Timeouts and retries
40
+ self.timeout = 30000 # Default 30s
41
+ self.retry = 0 # Number of retries
42
+ self.retry_delay = 1000 # Delay between retries in ms
43
+
44
+ # Response handling
45
+ self.response_format = "auto" # auto, json, text, xml, binary
46
+ self.transform = None # Transformation expression
47
+ self.cache = False
48
+ self.ttl = None
49
+ self.result = None # Variable name for metadata
50
+ self.on_error = 'fail' # INV-2: 'fail' or 'continue'
51
+
52
+ def add_header(self, header: 'InvokeHeaderNode'):
53
+ """Add HTTP header"""
54
+ self.headers.append(header)
55
+
56
+ def add_param(self, param: QuantumParam):
57
+ """Add parameter"""
58
+ self.params.append(param)
59
+
60
+ def get_invocation_type(self) -> str:
61
+ """Determine invocation type based on attributes"""
62
+ if self.function:
63
+ return "function"
64
+ elif self.component:
65
+ return "component"
66
+ elif self.url:
67
+ return "http"
68
+ elif self.endpoint:
69
+ # Could be GraphQL, SOAP, etc. (Phase 2)
70
+ return "endpoint"
71
+ elif self.service:
72
+ # Service discovery (Phase 2)
73
+ return "service"
74
+ else:
75
+ return "unknown"
76
+
77
+ def to_dict(self) -> Dict[str, Any]:
78
+ return {
79
+ "type": "invoke",
80
+ "name": self.name,
81
+ "invocation_type": self.get_invocation_type(),
82
+ "function": self.function,
83
+ "component": self.component,
84
+ "url": self.url,
85
+ "method": self.method,
86
+ "headers": [h.to_dict() for h in self.headers],
87
+ "params": [p.__dict__ for p in self.params],
88
+ "auth_type": self.auth_type,
89
+ "timeout": self.timeout,
90
+ "retry": self.retry,
91
+ "cache": self.cache
92
+ }
93
+
94
+ def validate(self) -> List[str]:
95
+ errors = []
96
+ # name is optional: fire-and-forget invocation has no result to bind.
97
+ # What actually matters is that there is something to invoke.
98
+
99
+ # Validate that at least one target is specified
100
+ targets = [self.function, self.component, self.url, self.endpoint, self.service]
101
+ if not any(targets):
102
+ errors.append("Invoke requires one of: function, component, url, endpoint, or service")
103
+
104
+ # Validate that only one target is specified
105
+ if sum(1 for t in targets if t is not None) > 1:
106
+ errors.append("Invoke can only specify one target (function, component, url, endpoint, or service)")
107
+
108
+ # Validate HTTP method
109
+ if self.method not in ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']:
110
+ errors.append(f"Invalid HTTP method: {self.method}")
111
+
112
+ # Validate auth_type
113
+ if self.auth_type and self.auth_type not in ['bearer', 'apikey', 'basic', 'oauth2']:
114
+ errors.append(f"Invalid auth_type: {self.auth_type}. Must be bearer, apikey, basic, or oauth2")
115
+
116
+ # Validate headers
117
+ for header in self.headers:
118
+ errors.extend(header.validate())
119
+
120
+ # Validate params
121
+ for param in self.params:
122
+ errors.extend(param.validate())
123
+
124
+ return errors
125
+
126
+
127
+ class InvokeHeaderNode(QuantumNode):
128
+ """Represents a header in <q:invoke>"""
129
+
130
+ def __init__(self, name: str, value: str):
131
+ self.name = name
132
+ self.value = value
133
+
134
+ def to_dict(self) -> Dict[str, Any]:
135
+ return {
136
+ "type": "invokeHeader",
137
+ "name": self.name,
138
+ "value": self.value[:50] + "..." if len(str(self.value)) > 50 else self.value
139
+ }
140
+
141
+ def validate(self) -> List[str]:
142
+ errors = []
143
+ if not self.name:
144
+ errors.append("Header name is required")
145
+ if self.value is None:
146
+ errors.append(f"Header '{self.name}' value is required")
147
+ return errors
@@ -0,0 +1,332 @@
1
+ """
2
+ Invocation Feature - Runtime
3
+ Handles execution of all invocation types (functions, components, HTTP, etc.)
4
+ """
5
+
6
+ import time
7
+ import json
8
+ import requests
9
+ from typing import Dict, Any, Optional, List
10
+ from dataclasses import dataclass
11
+ import base64
12
+
13
+
14
+ @dataclass
15
+ class InvocationResult:
16
+ """Result object for all invocations"""
17
+ success: bool
18
+ data: Any = None
19
+ error: Optional[Dict[str, Any]] = None
20
+ execution_time: float = 0.0
21
+ invocation_type: str = ""
22
+ metadata: Optional[Dict[str, Any]] = None
23
+
24
+
25
+ class InvocationService:
26
+ """Service to handle all types of invocations"""
27
+
28
+ def __init__(self):
29
+ self.cache: Dict[str, Any] = {} # Simple in-memory cache
30
+
31
+ def invoke(
32
+ self,
33
+ invocation_type: str,
34
+ params: Dict[str, Any],
35
+ context: Any = None
36
+ ) -> InvocationResult:
37
+ """Main invocation dispatcher"""
38
+ start_time = time.time()
39
+
40
+ try:
41
+ if invocation_type == "function":
42
+ result = self._invoke_function(params, context)
43
+ elif invocation_type == "component":
44
+ result = self._invoke_component(params, context)
45
+ elif invocation_type == "http":
46
+ result = self._invoke_http(params)
47
+ else:
48
+ return InvocationResult(
49
+ success=False,
50
+ error={"message": f"Unknown invocation type: {invocation_type}"},
51
+ invocation_type=invocation_type
52
+ )
53
+
54
+ execution_time = (time.time() - start_time) * 1000 # Convert to ms
55
+ result.execution_time = execution_time
56
+ result.invocation_type = invocation_type
57
+ return result
58
+
59
+ except Exception as e:
60
+ execution_time = (time.time() - start_time) * 1000
61
+ return InvocationResult(
62
+ success=False,
63
+ error={
64
+ "message": str(e),
65
+ "type": type(e).__name__
66
+ },
67
+ execution_time=execution_time,
68
+ invocation_type=invocation_type
69
+ )
70
+
71
+ def _invoke_function(self, params: Dict[str, Any], context: Any) -> InvocationResult:
72
+ """Invoke local function"""
73
+ function_name = params.get('function')
74
+ function_args = params.get('args', {})
75
+
76
+ if not context:
77
+ return InvocationResult(
78
+ success=False,
79
+ error={"message": "Context required for function invocation"}
80
+ )
81
+
82
+ # Get function from context (ComponentRuntime will handle this)
83
+ if not hasattr(context, 'get_function'):
84
+ return InvocationResult(
85
+ success=False,
86
+ error={"message": "Context does not support function invocation"}
87
+ )
88
+
89
+ func = context.get_function(function_name)
90
+ if not func:
91
+ return InvocationResult(
92
+ success=False,
93
+ error={"message": f"Function '{function_name}' not found"}
94
+ )
95
+
96
+ # Execute function
97
+ result = context.execute_function(function_name, function_args)
98
+
99
+ return InvocationResult(
100
+ success=result.get('success', False),
101
+ data=result.get('data'),
102
+ error=result.get('error'),
103
+ metadata=result
104
+ )
105
+
106
+ def _invoke_component(self, params: Dict[str, Any], context: Any) -> InvocationResult:
107
+ """Invoke local component"""
108
+ component_name = params.get('component')
109
+ component_args = params.get('args', {})
110
+
111
+ if not context:
112
+ return InvocationResult(
113
+ success=False,
114
+ error={"message": "Context required for component invocation"}
115
+ )
116
+
117
+ # Get component from context (ComponentRuntime will handle this)
118
+ if not hasattr(context, 'execute_component'):
119
+ return InvocationResult(
120
+ success=False,
121
+ error={"message": "Context does not support component invocation"}
122
+ )
123
+
124
+ result = context.execute_component(component_name, component_args)
125
+
126
+ return InvocationResult(
127
+ success=result.get('success', False),
128
+ data=result.get('data'),
129
+ error=result.get('error'),
130
+ metadata=result
131
+ )
132
+
133
+ def _invoke_http(self, params: Dict[str, Any]) -> InvocationResult:
134
+ """Invoke HTTP/REST endpoint"""
135
+ url = params.get('url')
136
+ method = params.get('method', 'GET').upper()
137
+ headers = params.get('headers', {})
138
+ query_params = params.get('params', {})
139
+ body = params.get('body')
140
+ auth_type = params.get('auth_type')
141
+ # `or`, not a get() default: the executor always passes these keys,
142
+ # with None when the attribute is absent — so get('timeout', 30000)
143
+ # returned None and `None / 1000` raised TypeError before any request
144
+ # was made. Every q:invoke url= without an explicit timeout failed
145
+ # with "unsupported operand type(s) for /: 'NoneType' and 'int'".
146
+ timeout_ms = params.get('timeout') or 30000
147
+ retry = params.get('retry') or 0
148
+ retry_delay = params.get('retry_delay') or 1000
149
+ response_format = params.get('response_format', 'auto')
150
+
151
+ if not url:
152
+ return InvocationResult(
153
+ success=False,
154
+ error={"message": "URL required for HTTP invocation"}
155
+ )
156
+
157
+ # Build authentication
158
+ headers = self._build_auth_headers(params, headers)
159
+
160
+ # Convert timeout to seconds for requests library
161
+ timeout_sec = timeout_ms / 1000
162
+
163
+ # Retry logic
164
+ last_error = None
165
+ for attempt in range(retry + 1):
166
+ try:
167
+ # Make HTTP request
168
+ response = requests.request(
169
+ method=method,
170
+ url=url,
171
+ headers=headers,
172
+ params=query_params,
173
+ json=body if method in ['POST', 'PUT', 'PATCH'] and isinstance(body, dict) else None,
174
+ data=body if method in ['POST', 'PUT', 'PATCH'] and isinstance(body, str) else None,
175
+ timeout=timeout_sec
176
+ )
177
+
178
+ # Parse response based on format
179
+ data = self._parse_http_response(response, response_format)
180
+
181
+ # Build metadata
182
+ metadata = {
183
+ "status_code": response.status_code,
184
+ "status_text": response.reason,
185
+ "headers": dict(response.headers),
186
+ "url": response.url,
187
+ "attempts": attempt + 1
188
+ }
189
+
190
+ # Check if response is successful
191
+ success = 200 <= response.status_code < 300
192
+
193
+ if not success:
194
+ return InvocationResult(
195
+ success=False,
196
+ data=data,
197
+ error={
198
+ "message": f"HTTP {response.status_code}: {response.reason}",
199
+ "status_code": response.status_code
200
+ },
201
+ metadata=metadata
202
+ )
203
+
204
+ return InvocationResult(
205
+ success=True,
206
+ data=data,
207
+ metadata=metadata
208
+ )
209
+
210
+ except requests.exceptions.Timeout as e:
211
+ last_error = InvocationResult(
212
+ success=False,
213
+ error={
214
+ "message": f"Request timeout after {timeout_ms}ms",
215
+ "type": "TimeoutError"
216
+ },
217
+ metadata={"attempts": attempt + 1}
218
+ )
219
+
220
+ except requests.exceptions.ConnectionError as e:
221
+ last_error = InvocationResult(
222
+ success=False,
223
+ error={
224
+ "message": f"Connection error: {str(e)}",
225
+ "type": "ConnectionError"
226
+ },
227
+ metadata={"attempts": attempt + 1}
228
+ )
229
+
230
+ except Exception as e:
231
+ last_error = InvocationResult(
232
+ success=False,
233
+ error={
234
+ "message": str(e),
235
+ "type": type(e).__name__
236
+ },
237
+ metadata={"attempts": attempt + 1}
238
+ )
239
+
240
+ # If we have more retries, wait before retrying
241
+ if attempt < retry:
242
+ time.sleep(retry_delay / 1000) # Convert ms to seconds
243
+
244
+ # All retries exhausted, return last error
245
+ return last_error
246
+
247
+ def _build_auth_headers(self, params: Dict[str, Any], headers: Dict[str, str]) -> Dict[str, str]:
248
+ """Build authentication headers"""
249
+ auth_type = params.get('auth_type')
250
+ headers = headers.copy() # Don't mutate original
251
+
252
+ if auth_type == 'bearer':
253
+ token = params.get('auth_token')
254
+ if token:
255
+ headers['Authorization'] = f'Bearer {token}'
256
+
257
+ elif auth_type == 'apikey':
258
+ token = params.get('auth_token')
259
+ header_name = params.get('auth_header', 'Authorization')
260
+ if token:
261
+ headers[header_name] = token
262
+
263
+ elif auth_type == 'basic':
264
+ username = params.get('auth_username')
265
+ password = params.get('auth_password')
266
+ if username and password:
267
+ credentials = f"{username}:{password}".encode('utf-8')
268
+ encoded = base64.b64encode(credentials).decode('utf-8')
269
+ headers['Authorization'] = f'Basic {encoded}'
270
+
271
+ return headers
272
+
273
+ def _parse_http_response(self, response: requests.Response, response_format: str) -> Any:
274
+ """Parse HTTP response based on format"""
275
+ if response_format == 'auto':
276
+ # Try to detect format from Content-Type header
277
+ content_type = response.headers.get('Content-Type', '').lower()
278
+
279
+ if 'application/json' in content_type:
280
+ try:
281
+ return response.json()
282
+ except:
283
+ return response.text
284
+
285
+ elif 'text/' in content_type or 'application/xml' in content_type:
286
+ return response.text
287
+
288
+ elif 'application/octet-stream' in content_type or 'image/' in content_type:
289
+ return {
290
+ "binary": True,
291
+ "content_type": content_type,
292
+ "size": len(response.content)
293
+ }
294
+
295
+ else:
296
+ # Default to text
297
+ return response.text
298
+
299
+ elif response_format == 'json':
300
+ try:
301
+ return response.json()
302
+ except:
303
+ return {"error": "Failed to parse JSON response", "text": response.text}
304
+
305
+ elif response_format == 'text':
306
+ return response.text
307
+
308
+ elif response_format == 'xml':
309
+ return response.text
310
+
311
+ elif response_format == 'binary':
312
+ return {
313
+ "binary": True,
314
+ "content_type": response.headers.get('Content-Type'),
315
+ "size": len(response.content)
316
+ }
317
+
318
+ else:
319
+ return response.text
320
+
321
+ def get_from_cache(self, cache_key: str) -> Optional[Any]:
322
+ """Get value from cache"""
323
+ return self.cache.get(cache_key)
324
+
325
+ def put_in_cache(self, cache_key: str, value: Any, ttl: Optional[int] = None):
326
+ """Put value in cache"""
327
+ # Simple implementation - in production you'd use TTL properly
328
+ self.cache[cache_key] = value
329
+
330
+ def clear_cache(self):
331
+ """Clear all cache"""
332
+ self.cache.clear()
@@ -0,0 +1,6 @@
1
+ """Knowledge Base feature - RAG with virtual knowledge bases."""
2
+
3
+ from .ast_node import KnowledgeNode, KnowledgeSourceNode
4
+ from .parser import parse_knowledge
5
+
6
+ __all__ = ['KnowledgeNode', 'KnowledgeSourceNode', 'parse_knowledge']
@@ -0,0 +1,113 @@
1
+ """AST nodes for q:knowledge and q:source."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Dict, Any, Optional
5
+ from quantum.core.ast_nodes import QuantumNode
6
+
7
+
8
+ @dataclass
9
+ class KnowledgeSourceNode(QuantumNode):
10
+ """
11
+ Represents <q:source> inside <q:knowledge>.
12
+
13
+ Examples:
14
+ <q:source type="text">Quantum Framework allows...</q:source>
15
+ <q:source type="file" path="./docs/guide.md" />
16
+ <q:source type="directory" path="./docs/" pattern="*.md" />
17
+ <q:source type="query" datasource="mydb">
18
+ SELECT title, content FROM articles
19
+ </q:source>
20
+ """
21
+ source_type: str = "text" # text, file, directory, url, query
22
+ content: Optional[str] = None # Inline text content
23
+ path: Optional[str] = None # File or directory path
24
+ pattern: Optional[str] = None # Glob pattern for directory type
25
+ url: Optional[str] = None # URL for url type
26
+ datasource: Optional[str] = None # Datasource for query type
27
+ sql: Optional[str] = None # SQL for query type
28
+ chunk_size: Optional[int] = None # Override parent chunk_size
29
+ chunk_overlap: Optional[int] = None # Override parent chunk_overlap
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ return {
33
+ "type": "knowledge_source",
34
+ "source_type": self.source_type,
35
+ "content": self.content[:50] + "..." if self.content and len(self.content) > 50 else self.content,
36
+ "path": self.path,
37
+ "pattern": self.pattern,
38
+ "url": self.url,
39
+ "datasource": self.datasource,
40
+ }
41
+
42
+ def validate(self) -> List[str]:
43
+ errors = []
44
+ valid_types = ['text', 'file', 'directory', 'url', 'query']
45
+ if self.source_type not in valid_types:
46
+ errors.append(f"Invalid source type: {self.source_type}. Must be one of {valid_types}")
47
+
48
+ if self.source_type == 'text' and not self.content:
49
+ errors.append("Text source requires content")
50
+ if self.source_type == 'file' and not self.path:
51
+ errors.append("File source requires 'path' attribute")
52
+ if self.source_type == 'directory' and not self.path:
53
+ errors.append("Directory source requires 'path' attribute")
54
+ if self.source_type == 'url' and not self.url:
55
+ errors.append("URL source requires 'url' attribute")
56
+ if self.source_type == 'query' and not self.datasource:
57
+ errors.append("Query source requires 'datasource' attribute")
58
+
59
+ return errors
60
+
61
+ def __repr__(self):
62
+ return f'<KnowledgeSourceNode type={self.source_type}>'
63
+
64
+
65
+ @dataclass
66
+ class KnowledgeNode(QuantumNode):
67
+ """
68
+ Represents <q:knowledge> - defines a virtual knowledge base.
69
+
70
+ Examples:
71
+ <q:knowledge name="docs" model="phi3" embedModel="nomic-embed-text">
72
+ <q:source type="text">Quantum Framework allows...</q:source>
73
+ <q:source type="file" path="./docs/guide.md" />
74
+ </q:knowledge>
75
+ """
76
+ name: str = ""
77
+ model: Optional[str] = None # LLM model for RAG
78
+ embed_model: str = "nomic-embed-text" # Embedding model
79
+ chunk_size: int = 500 # Characters per chunk
80
+ chunk_overlap: int = 50 # Overlap between chunks
81
+ sources: List[KnowledgeSourceNode] = field(default_factory=list)
82
+ persist: bool = False # Persist ChromaDB to disk
83
+ persist_path: Optional[str] = None # ChromaDB persistence path
84
+ rebuild: bool = False # Force reindex
85
+
86
+ def add_source(self, source: KnowledgeSourceNode):
87
+ self.sources.append(source)
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ return {
91
+ "type": "knowledge",
92
+ "name": self.name,
93
+ "model": self.model,
94
+ "embed_model": self.embed_model,
95
+ "chunk_size": self.chunk_size,
96
+ "chunk_overlap": self.chunk_overlap,
97
+ "sources": [s.to_dict() for s in self.sources],
98
+ "persist": self.persist,
99
+ "rebuild": self.rebuild,
100
+ }
101
+
102
+ def validate(self) -> List[str]:
103
+ errors = []
104
+ if not self.name:
105
+ errors.append("Knowledge base name is required")
106
+ if not self.sources:
107
+ errors.append("Knowledge base requires at least one <q:source>")
108
+ for source in self.sources:
109
+ errors.extend(source.validate())
110
+ return errors
111
+
112
+ def __repr__(self):
113
+ return f'<KnowledgeNode name={self.name} sources={len(self.sources)}>'
@@ -0,0 +1,82 @@
1
+ """Parser for q:knowledge and q:source elements."""
2
+
3
+ from xml.etree import ElementTree as ET
4
+ from .ast_node import KnowledgeNode, KnowledgeSourceNode
5
+
6
+
7
+ def parse_knowledge(element: ET.Element) -> KnowledgeNode:
8
+ """
9
+ Parse <q:knowledge> element into KnowledgeNode.
10
+
11
+ Args:
12
+ element: XML element representing <q:knowledge>
13
+
14
+ Returns:
15
+ KnowledgeNode with parsed sources
16
+ """
17
+ name = element.get('name', '')
18
+ node = KnowledgeNode(name=name)
19
+
20
+ # Parse attributes
21
+ node.model = element.get('model')
22
+ node.embed_model = element.get('embedModel', 'nomic-embed-text')
23
+
24
+ chunk_size = element.get('chunkSize')
25
+ if chunk_size:
26
+ try:
27
+ node.chunk_size = int(chunk_size)
28
+ except ValueError:
29
+ pass
30
+
31
+ chunk_overlap = element.get('chunkOverlap')
32
+ if chunk_overlap:
33
+ try:
34
+ node.chunk_overlap = int(chunk_overlap)
35
+ except ValueError:
36
+ pass
37
+
38
+ node.persist = element.get('persist', 'false').lower() in ('true', '1', 'yes')
39
+ node.persist_path = element.get('persistPath')
40
+ node.rebuild = element.get('rebuild', 'false').lower() in ('true', '1', 'yes')
41
+
42
+ # Parse <q:source> children
43
+ for child in element:
44
+ local_name = child.tag.split('}')[-1] if '}' in child.tag else child.tag.split(':')[-1]
45
+ if local_name == 'source':
46
+ source = _parse_source(child)
47
+ node.add_source(source)
48
+
49
+ return node
50
+
51
+
52
+ def _parse_source(element: ET.Element) -> KnowledgeSourceNode:
53
+ """Parse <q:source> element into KnowledgeSourceNode."""
54
+ source = KnowledgeSourceNode()
55
+ source.source_type = element.get('type', 'text')
56
+ source.path = element.get('path')
57
+ source.pattern = element.get('pattern', '*.md')
58
+ source.url = element.get('url')
59
+ source.datasource = element.get('datasource')
60
+
61
+ chunk_size = element.get('chunkSize')
62
+ if chunk_size:
63
+ try:
64
+ source.chunk_size = int(chunk_size)
65
+ except ValueError:
66
+ pass
67
+
68
+ chunk_overlap = element.get('chunkOverlap')
69
+ if chunk_overlap:
70
+ try:
71
+ source.chunk_overlap = int(chunk_overlap)
72
+ except ValueError:
73
+ pass
74
+
75
+ # Text content (inline or SQL for query type)
76
+ text = (element.text or '').strip()
77
+ if source.source_type == 'query':
78
+ source.sql = text
79
+ elif source.source_type == 'text':
80
+ source.content = text
81
+
82
+ return source