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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/admin.py +131 -0
- quantum/cli/check.py +223 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +414 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +548 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +565 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2619 -0
- quantum/core/config_env.py +54 -0
- quantum/core/docs_links.py +8 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +208 -0
- quantum/core/expressions.py +677 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +292 -0
- quantum/core/features/data_import/src/runtime.py +533 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +147 -0
- quantum/core/features/invocation/src/runtime.py +332 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +121 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
- quantum/core/features/ui_engine/src/parser.py +1192 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/guards.py +53 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1551 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +71 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +93 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +229 -0
- quantum/core/parsers/data/invoke_parser.py +133 -0
- quantum/core/parsers/data/query_parser.py +213 -0
- quantum/core/parsers/data/transaction_parser.py +86 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +79 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +98 -0
- quantum/core/parsers/functions/param_parser.py +73 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +164 -0
- quantum/core/xml_lines.py +81 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +25 -0
- quantum/runtime/action_handler.py +532 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +1048 -0
- quantum/runtime/component_composer.py +133 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/conditions.py +54 -0
- quantum/runtime/database_service.py +595 -0
- quantum/runtime/db_schema.py +100 -0
- quantum/runtime/dev_panel.py +178 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +283 -0
- quantum/runtime/executor_registry.py +178 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +264 -0
- quantum/runtime/executors/control_flow/__init__.py +12 -0
- quantum/runtime/executors/control_flow/if_executor.py +98 -0
- quantum/runtime/executors/control_flow/loop_executor.py +308 -0
- quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
- quantum/runtime/executors/control_flow/set_executor.py +464 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +156 -0
- quantum/runtime/executors/data/invoke_executor.py +228 -0
- quantum/runtime/executors/data/query_executor.py +290 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +546 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/local_server.py +28 -0
- quantum/runtime/logging_setup.py +141 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +218 -0
- quantum/runtime/pypy_compat.py +317 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +734 -0
- quantum/runtime/service_container.py +470 -0
- quantum/runtime/table_params.py +163 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +222 -0
- quantum/runtime/ui_console.py +405 -0
- quantum/runtime/ui_desktop.py +56 -0
- quantum/runtime/ui_form_rules.py +193 -0
- quantum/runtime/ui_html_adapter.py +2567 -0
- quantum/runtime/ui_html_templates.py +2106 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_page_renderer.py +392 -0
- quantum/runtime/ui_pager.py +73 -0
- quantum/runtime/ui_table_edit.py +139 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/ui_view.py +221 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +2131 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/services.py +109 -0
- quantum/utils/__init__.py +1 -0
- quantum_admin/__init__.py +4 -0
- quantum_admin/assets/quantum-admin-connectors.js +123 -0
- quantum_admin/assets/quantum-admin.css +2058 -0
- quantum_admin/assets/quantum-source-highlight.js +106 -0
- quantum_admin/backend/__init__.py +4 -0
- quantum_admin/backend/audit_service.py +346 -0
- quantum_admin/backend/auth_service.py +394 -0
- quantum_admin/backend/component_discovery.py +766 -0
- quantum_admin/backend/config_generator.py +586 -0
- quantum_admin/backend/connector_service.py +1016 -0
- quantum_admin/backend/crud.py +557 -0
- quantum_admin/backend/database.py +226 -0
- quantum_admin/backend/db_setup_service.py +193 -0
- quantum_admin/backend/deploy_service.py +698 -0
- quantum_admin/backend/docker_service.py +422 -0
- quantum_admin/backend/environment_service.py +418 -0
- quantum_admin/backend/git_service.py +370 -0
- quantum_admin/backend/health_service.py +390 -0
- quantum_admin/backend/job_service.py +347 -0
- quantum_admin/backend/log_service.py +331 -0
- quantum_admin/backend/main.py +14426 -0
- quantum_admin/backend/models.py +986 -0
- quantum_admin/backend/pipeline_service.py +812 -0
- quantum_admin/backend/resource_manager.py +1118 -0
- quantum_admin/backend/schemas.py +419 -0
- quantum_admin/backend/secret_manager.py +277 -0
- quantum_admin/backend/settings_service.py +488 -0
- quantum_admin/backend/template_service.py +710 -0
- quantum_admin/backend/test_execution_service.py +364 -0
- quantum_admin/backend/test_generator.py +782 -0
- quantum_admin/backend/webhook_service.py +454 -0
- quantum_admin/backend/websocket_manager.py +360 -0
- quantum_admin/components/admin/_layout/AdminShell.q +121 -0
- quantum_admin/components/admin/agents.q +107 -0
- quantum_admin/components/admin/app/[name].q +558 -0
- quantum_admin/components/admin/applications.q +160 -0
- quantum_admin/components/admin/component/[...path].q +205 -0
- quantum_admin/components/admin/components.q +59 -0
- quantum_admin/components/admin/connectors.q +283 -0
- quantum_admin/components/admin/dashboard.q +86 -0
- quantum_admin/components/admin/database.q +95 -0
- quantum_admin/components/admin/features.q +69 -0
- quantum_admin/components/admin/index.q +12 -0
- quantum_admin/components/admin/jobs.q +75 -0
- quantum_admin/components/admin/login.q +65 -0
- quantum_admin/components/admin/logout.q +11 -0
- quantum_admin/components/admin/settings.q +174 -0
- quantum_admin/components/admin/source.q +45 -0
- quantum_admin/components/admin/tests.q +73 -0
- quantum_admin/run.py +13 -0
- quantum_admin/services/__init__.py +16 -0
- quantum_admin/services/_base.py +84 -0
- quantum_admin/services/_component_test_generator.py +205 -0
- quantum_admin/services/_yaml_editor.py +82 -0
- quantum_admin/services/apps.py +330 -0
- quantum_admin/services/auth.py +61 -0
- quantum_admin/services/components.py +210 -0
- quantum_admin/services/connectors.py +164 -0
- quantum_admin/services/projects.py +189 -0
- quantum_admin/services/repository.py +223 -0
- quantum_admin/services/settings.py +150 -0
- quantum_admin/services/yaml_import.py +118 -0
- quantum_framework-0.18.0.dist-info/METADATA +281 -0
- quantum_framework-0.18.0.dist-info/RECORD +333 -0
- quantum_framework-0.18.0.dist-info/WHEEL +5 -0
- quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agents Feature - AI Agents with Tool Use
|
|
3
|
+
|
|
4
|
+
This module provides AST nodes for the q:agent feature,
|
|
5
|
+
enabling autonomous AI agents that can use tools to complete tasks.
|
|
6
|
+
|
|
7
|
+
Multi-Agent Systems:
|
|
8
|
+
- AgentTeamNode: Team of collaborating agents
|
|
9
|
+
- AgentHandoffNode: Built-in handoff tool for agent delegation
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .ast_node import (
|
|
13
|
+
AgentNode,
|
|
14
|
+
AgentInstructionNode,
|
|
15
|
+
AgentToolNode,
|
|
16
|
+
AgentToolParamNode,
|
|
17
|
+
AgentExecuteNode,
|
|
18
|
+
AgentTeamNode,
|
|
19
|
+
AgentHandoffNode,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
'AgentNode',
|
|
24
|
+
'AgentInstructionNode',
|
|
25
|
+
'AgentToolNode',
|
|
26
|
+
'AgentToolParamNode',
|
|
27
|
+
'AgentExecuteNode',
|
|
28
|
+
'AgentTeamNode',
|
|
29
|
+
'AgentHandoffNode',
|
|
30
|
+
]
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent AST Nodes
|
|
3
|
+
|
|
4
|
+
Defines the AST structure for AI agents with tool use capabilities.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
AgentToolParamNode - Parameter definition for a tool
|
|
8
|
+
AgentToolNode - Tool available to the agent
|
|
9
|
+
AgentInstructionNode - System instruction for the agent
|
|
10
|
+
AgentExecuteNode - Execute the agent with a task
|
|
11
|
+
AgentNode - Main agent definition
|
|
12
|
+
AgentHandoffNode - Built-in handoff tool for agent delegation
|
|
13
|
+
AgentTeamNode - Team of collaborating agents
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import List, Optional, Any
|
|
18
|
+
|
|
19
|
+
# Import base class - handle both direct and package imports
|
|
20
|
+
try:
|
|
21
|
+
from quantum.core.ast_nodes import QuantumNode
|
|
22
|
+
except ImportError:
|
|
23
|
+
from ...ast_nodes import QuantumNode
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class AgentToolParamNode(QuantumNode):
|
|
28
|
+
"""
|
|
29
|
+
Parameter definition for an agent tool.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
name: Parameter name (required)
|
|
33
|
+
type: Parameter type (string, integer, number, boolean, array, object)
|
|
34
|
+
required: Whether the parameter is required
|
|
35
|
+
default: Default value if not provided
|
|
36
|
+
description: Human-readable description for the LLM
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
<q:param name="orderId" type="integer" required="true"
|
|
40
|
+
description="The order ID to look up" />
|
|
41
|
+
"""
|
|
42
|
+
name: str = ""
|
|
43
|
+
type: str = "string"
|
|
44
|
+
required: bool = False
|
|
45
|
+
default: Optional[Any] = None
|
|
46
|
+
description: str = ""
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> dict:
|
|
49
|
+
"""Convert to dictionary."""
|
|
50
|
+
return {
|
|
51
|
+
"name": self.name,
|
|
52
|
+
"type": self.type,
|
|
53
|
+
"required": self.required,
|
|
54
|
+
"default": self.default,
|
|
55
|
+
"description": self.description
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
def validate(self) -> List[str]:
|
|
59
|
+
"""Validate the parameter node."""
|
|
60
|
+
errors = []
|
|
61
|
+
if not self.name:
|
|
62
|
+
errors.append("AgentToolParamNode requires 'name' attribute")
|
|
63
|
+
valid_types = {"string", "integer", "number", "boolean", "array", "object", "date", "decimal"}
|
|
64
|
+
if self.type not in valid_types:
|
|
65
|
+
errors.append(f"Invalid param type '{self.type}'. Must be one of: {valid_types}")
|
|
66
|
+
return errors
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class AgentToolNode(QuantumNode):
|
|
71
|
+
"""
|
|
72
|
+
Tool available to the agent.
|
|
73
|
+
|
|
74
|
+
A tool is a function that the agent can call to perform actions
|
|
75
|
+
or retrieve information. Tools have parameters and a body that
|
|
76
|
+
defines what happens when the tool is called.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
name: Tool name (used by LLM to call it)
|
|
80
|
+
description: What the tool does (helps LLM decide when to use it)
|
|
81
|
+
params: List of parameter definitions
|
|
82
|
+
body: List of AST nodes to execute when tool is called
|
|
83
|
+
builtin: If True, this is a built-in tool (handoff, readShared, etc.)
|
|
84
|
+
|
|
85
|
+
Built-in Tools (use builtin="true"):
|
|
86
|
+
- handoff: Transfer to another agent in the team
|
|
87
|
+
- readShared: Read from shared team memory
|
|
88
|
+
- writeShared: Write to shared team memory
|
|
89
|
+
- listAgents: List available agents in the team
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
<q:tool name="getOrder" description="Get order details by ID">
|
|
93
|
+
<q:param name="orderId" type="integer" required="true" />
|
|
94
|
+
<q:function name="fetchOrder">
|
|
95
|
+
<q:query name="order" datasource="db">
|
|
96
|
+
SELECT * FROM orders WHERE id = :orderId
|
|
97
|
+
</q:query>
|
|
98
|
+
<q:return value="{order[0]}" />
|
|
99
|
+
</q:function>
|
|
100
|
+
</q:tool>
|
|
101
|
+
|
|
102
|
+
<!-- Built-in tool -->
|
|
103
|
+
<q:tool name="handoff" builtin="true" />
|
|
104
|
+
"""
|
|
105
|
+
name: str = ""
|
|
106
|
+
description: str = ""
|
|
107
|
+
params: List[AgentToolParamNode] = field(default_factory=list)
|
|
108
|
+
body: List[QuantumNode] = field(default_factory=list)
|
|
109
|
+
builtin: bool = False # True for built-in tools like handoff, readShared
|
|
110
|
+
|
|
111
|
+
def to_dict(self) -> dict:
|
|
112
|
+
"""Convert to dictionary."""
|
|
113
|
+
return {
|
|
114
|
+
"name": self.name,
|
|
115
|
+
"description": self.description,
|
|
116
|
+
"params": [p.to_dict() for p in self.params],
|
|
117
|
+
"body": [b.to_dict() if hasattr(b, 'to_dict') else str(b) for b in self.body],
|
|
118
|
+
"builtin": self.builtin
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def validate(self) -> List[str]:
|
|
122
|
+
"""Validate the tool node."""
|
|
123
|
+
errors = []
|
|
124
|
+
if not self.name:
|
|
125
|
+
errors.append("AgentToolNode requires 'name' attribute")
|
|
126
|
+
# Built-in tools get their description automatically
|
|
127
|
+
if not self.description and not self.builtin:
|
|
128
|
+
errors.append(f"Tool '{self.name}' should have a description for the LLM")
|
|
129
|
+
for param in self.params:
|
|
130
|
+
errors.extend(param.validate())
|
|
131
|
+
return errors
|
|
132
|
+
|
|
133
|
+
def is_builtin(self) -> bool:
|
|
134
|
+
"""Check if this is a built-in tool."""
|
|
135
|
+
return self.builtin
|
|
136
|
+
|
|
137
|
+
def get_schema(self) -> dict:
|
|
138
|
+
"""Get JSON Schema representation for LLM tool calling."""
|
|
139
|
+
properties = {}
|
|
140
|
+
required = []
|
|
141
|
+
|
|
142
|
+
for param in self.params:
|
|
143
|
+
prop = {"type": self._map_type(param.type)}
|
|
144
|
+
if param.description:
|
|
145
|
+
prop["description"] = param.description
|
|
146
|
+
if param.default is not None:
|
|
147
|
+
prop["default"] = param.default
|
|
148
|
+
properties[param.name] = prop
|
|
149
|
+
|
|
150
|
+
if param.required:
|
|
151
|
+
required.append(param.name)
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
"name": self.name,
|
|
155
|
+
"description": self.description,
|
|
156
|
+
"parameters": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"properties": properties,
|
|
159
|
+
"required": required
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def _map_type(self, quantum_type: str) -> str:
|
|
164
|
+
"""Map Quantum types to JSON Schema types."""
|
|
165
|
+
type_map = {
|
|
166
|
+
"string": "string",
|
|
167
|
+
"integer": "integer",
|
|
168
|
+
"number": "number",
|
|
169
|
+
"decimal": "number",
|
|
170
|
+
"boolean": "boolean",
|
|
171
|
+
"array": "array",
|
|
172
|
+
"object": "object",
|
|
173
|
+
"date": "string",
|
|
174
|
+
}
|
|
175
|
+
return type_map.get(quantum_type, "string")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass
|
|
179
|
+
class AgentInstructionNode(QuantumNode):
|
|
180
|
+
"""
|
|
181
|
+
System instruction for the agent.
|
|
182
|
+
|
|
183
|
+
Defines the agent's role, personality, and behavior guidelines.
|
|
184
|
+
This becomes part of the system prompt sent to the LLM.
|
|
185
|
+
|
|
186
|
+
Attributes:
|
|
187
|
+
content: The instruction text (supports databinding)
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
<q:instruction>
|
|
191
|
+
You are a helpful customer support agent.
|
|
192
|
+
Be polite, concise, and helpful.
|
|
193
|
+
Always verify information before taking action.
|
|
194
|
+
</q:instruction>
|
|
195
|
+
"""
|
|
196
|
+
content: str = ""
|
|
197
|
+
|
|
198
|
+
def to_dict(self) -> dict:
|
|
199
|
+
"""Convert to dictionary."""
|
|
200
|
+
return {"content": self.content}
|
|
201
|
+
|
|
202
|
+
def validate(self) -> List[str]:
|
|
203
|
+
"""Validate the instruction node."""
|
|
204
|
+
errors = []
|
|
205
|
+
if not self.content.strip():
|
|
206
|
+
errors.append("AgentInstructionNode should have content")
|
|
207
|
+
return errors
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class AgentExecuteNode(QuantumNode):
|
|
212
|
+
"""
|
|
213
|
+
Execute the agent with a specific task.
|
|
214
|
+
|
|
215
|
+
Triggers the agent's reasoning loop to complete the given task
|
|
216
|
+
using the available tools.
|
|
217
|
+
|
|
218
|
+
Attributes:
|
|
219
|
+
task: The task/question for the agent (supports databinding)
|
|
220
|
+
context: Additional context to provide (optional, supports databinding)
|
|
221
|
+
entry: Entry agent name for team execution (optional)
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
<q:execute task="Check status of order #1234 and update if needed" />
|
|
225
|
+
|
|
226
|
+
<q:execute task="{userMessage}" context="User ID: {userId}" />
|
|
227
|
+
|
|
228
|
+
<!-- For team execution with specific entry point -->
|
|
229
|
+
<q:execute task="{question}" entry="router" />
|
|
230
|
+
"""
|
|
231
|
+
task: str = ""
|
|
232
|
+
context: str = ""
|
|
233
|
+
entry: str = "" # Entry agent for team execution
|
|
234
|
+
|
|
235
|
+
def to_dict(self) -> dict:
|
|
236
|
+
"""Convert to dictionary."""
|
|
237
|
+
result = {"task": self.task, "context": self.context}
|
|
238
|
+
if self.entry:
|
|
239
|
+
result["entry"] = self.entry
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
def validate(self) -> List[str]:
|
|
243
|
+
"""Validate the execute node."""
|
|
244
|
+
errors = []
|
|
245
|
+
if not self.task:
|
|
246
|
+
errors.append("AgentExecuteNode requires 'task' attribute or content")
|
|
247
|
+
return errors
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@dataclass
|
|
251
|
+
class AgentNode(QuantumNode):
|
|
252
|
+
"""
|
|
253
|
+
AI Agent that can use tools to complete tasks.
|
|
254
|
+
|
|
255
|
+
An agent is an autonomous AI that can:
|
|
256
|
+
1. Receive a task from the user
|
|
257
|
+
2. Reason about which tools to use
|
|
258
|
+
3. Call tools to gather information or take actions
|
|
259
|
+
4. Synthesize results into a coherent response
|
|
260
|
+
|
|
261
|
+
Attributes:
|
|
262
|
+
name: Variable name to store result (required)
|
|
263
|
+
model: LLM model identifier (default: "phi3")
|
|
264
|
+
endpoint: Custom LLM API endpoint (optional)
|
|
265
|
+
provider: LLM provider (ollama, openai, anthropic, auto)
|
|
266
|
+
api_key: API key for cloud providers (optional)
|
|
267
|
+
max_iterations: Maximum tool calls allowed (default: 10)
|
|
268
|
+
timeout: Total timeout in milliseconds (default: 60000)
|
|
269
|
+
instruction: System instruction/prompt
|
|
270
|
+
tools: List of available tools
|
|
271
|
+
execute: Task to execute
|
|
272
|
+
|
|
273
|
+
Result Object:
|
|
274
|
+
{name}: The agent's final response (string)
|
|
275
|
+
{name}_result: Full result object with:
|
|
276
|
+
- success: bool
|
|
277
|
+
- result: str
|
|
278
|
+
- error: {message: str} or None
|
|
279
|
+
- executionTime: float (ms)
|
|
280
|
+
- iterations: int
|
|
281
|
+
- actionCount: int
|
|
282
|
+
- actions: [{tool, args, result}, ...]
|
|
283
|
+
- tokenUsage: {prompt, completion, total}
|
|
284
|
+
|
|
285
|
+
Supported Providers:
|
|
286
|
+
- ollama: Local Ollama server (default: http://localhost:11434)
|
|
287
|
+
- openai: OpenAI API or LM Studio (http://localhost:1234/v1)
|
|
288
|
+
- anthropic: Anthropic Claude API
|
|
289
|
+
- auto: Auto-detect from endpoint
|
|
290
|
+
|
|
291
|
+
Example:
|
|
292
|
+
<!-- Using Ollama (default) -->
|
|
293
|
+
<q:agent name="support" model="phi3" max_iterations="5">
|
|
294
|
+
...
|
|
295
|
+
</q:agent>
|
|
296
|
+
|
|
297
|
+
<!-- Using OpenAI -->
|
|
298
|
+
<q:agent name="helper" model="gpt-4" provider="openai"
|
|
299
|
+
api_key="{env.OPENAI_API_KEY}">
|
|
300
|
+
...
|
|
301
|
+
</q:agent>
|
|
302
|
+
|
|
303
|
+
<!-- Using LM Studio -->
|
|
304
|
+
<q:agent name="local" model="mistral"
|
|
305
|
+
endpoint="http://localhost:1234/v1">
|
|
306
|
+
...
|
|
307
|
+
</q:agent>
|
|
308
|
+
|
|
309
|
+
<!-- Using Claude -->
|
|
310
|
+
<q:agent name="claude" model="claude-3-haiku-20240307"
|
|
311
|
+
provider="anthropic" api_key="{env.ANTHROPIC_API_KEY}">
|
|
312
|
+
...
|
|
313
|
+
</q:agent>
|
|
314
|
+
"""
|
|
315
|
+
name: str = ""
|
|
316
|
+
model: str = "phi3"
|
|
317
|
+
endpoint: str = ""
|
|
318
|
+
provider: str = "auto" # ollama, openai, anthropic, auto
|
|
319
|
+
api_key: str = "" # For cloud providers
|
|
320
|
+
max_iterations: int = 10
|
|
321
|
+
timeout: int = 60000 # milliseconds
|
|
322
|
+
|
|
323
|
+
instruction: Optional[AgentInstructionNode] = None
|
|
324
|
+
tools: List[AgentToolNode] = field(default_factory=list)
|
|
325
|
+
execute: Optional[AgentExecuteNode] = None
|
|
326
|
+
|
|
327
|
+
# Set by the team parser for agents declared inside <q:team>. Those agents
|
|
328
|
+
# are driven by the team's own <q:execute>, so requiring one of their own
|
|
329
|
+
# would reject every valid team component.
|
|
330
|
+
in_team: bool = False
|
|
331
|
+
|
|
332
|
+
def to_dict(self) -> dict:
|
|
333
|
+
"""Convert to dictionary."""
|
|
334
|
+
return {
|
|
335
|
+
"name": self.name,
|
|
336
|
+
"model": self.model,
|
|
337
|
+
"endpoint": self.endpoint,
|
|
338
|
+
"provider": self.provider,
|
|
339
|
+
"api_key": self.api_key,
|
|
340
|
+
"max_iterations": self.max_iterations,
|
|
341
|
+
"timeout": self.timeout,
|
|
342
|
+
"instruction": self.instruction.to_dict() if self.instruction else None,
|
|
343
|
+
"tools": [t.to_dict() for t in self.tools],
|
|
344
|
+
"execute": self.execute.to_dict() if self.execute else None
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
def validate(self) -> List[str]:
|
|
348
|
+
"""Validate the agent node."""
|
|
349
|
+
errors = []
|
|
350
|
+
|
|
351
|
+
if not self.name:
|
|
352
|
+
errors.append("AgentNode requires 'name' attribute")
|
|
353
|
+
|
|
354
|
+
if not self.model:
|
|
355
|
+
errors.append("AgentNode requires 'model' attribute")
|
|
356
|
+
|
|
357
|
+
if self.max_iterations < 1:
|
|
358
|
+
errors.append("max_iterations must be at least 1")
|
|
359
|
+
|
|
360
|
+
if self.max_iterations > 100:
|
|
361
|
+
errors.append("max_iterations should not exceed 100 for safety")
|
|
362
|
+
|
|
363
|
+
if self.timeout < 1000:
|
|
364
|
+
errors.append("timeout should be at least 1000ms (1 second)")
|
|
365
|
+
|
|
366
|
+
if not self.tools:
|
|
367
|
+
errors.append("AgentNode should have at least one tool")
|
|
368
|
+
|
|
369
|
+
if not self.execute and not self.in_team:
|
|
370
|
+
errors.append("AgentNode requires <q:execute> to run")
|
|
371
|
+
|
|
372
|
+
# Validate children
|
|
373
|
+
if self.instruction:
|
|
374
|
+
errors.extend(self.instruction.validate())
|
|
375
|
+
|
|
376
|
+
for tool in self.tools:
|
|
377
|
+
errors.extend(tool.validate())
|
|
378
|
+
|
|
379
|
+
if self.execute:
|
|
380
|
+
errors.extend(self.execute.validate())
|
|
381
|
+
|
|
382
|
+
return errors
|
|
383
|
+
|
|
384
|
+
def get_tools_schema(self) -> List[dict]:
|
|
385
|
+
"""Get JSON Schema for all tools (for LLM function calling)."""
|
|
386
|
+
return [tool.get_schema() for tool in self.tools]
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@dataclass
|
|
390
|
+
class AgentHandoffNode(QuantumNode):
|
|
391
|
+
"""
|
|
392
|
+
Built-in handoff tool for agent delegation.
|
|
393
|
+
|
|
394
|
+
Allows an agent to transfer control to another agent in a team,
|
|
395
|
+
passing along context and the current task state.
|
|
396
|
+
|
|
397
|
+
Attributes:
|
|
398
|
+
target_agent: Name of the agent to hand off to
|
|
399
|
+
context: Additional context/message for the target agent
|
|
400
|
+
|
|
401
|
+
Example (used internally when agent calls handoff tool):
|
|
402
|
+
{"action": "handoff", "args": {"agent": "billing", "message": "Customer needs invoice help"}}
|
|
403
|
+
"""
|
|
404
|
+
target_agent: str = ""
|
|
405
|
+
context: str = ""
|
|
406
|
+
|
|
407
|
+
def to_dict(self) -> dict:
|
|
408
|
+
"""Convert to dictionary."""
|
|
409
|
+
return {
|
|
410
|
+
"target_agent": self.target_agent,
|
|
411
|
+
"context": self.context
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
def validate(self) -> List[str]:
|
|
415
|
+
"""Validate the handoff node."""
|
|
416
|
+
errors = []
|
|
417
|
+
if not self.target_agent:
|
|
418
|
+
errors.append("AgentHandoffNode requires 'target_agent'")
|
|
419
|
+
return errors
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@dataclass
|
|
423
|
+
class AgentTeamNode(QuantumNode):
|
|
424
|
+
"""
|
|
425
|
+
Team of collaborating agents that can hand off tasks to each other.
|
|
426
|
+
|
|
427
|
+
A team provides:
|
|
428
|
+
1. Shared context accessible by all agents
|
|
429
|
+
2. Agent discovery and handoff capabilities
|
|
430
|
+
3. Orchestration with entry point and handoff limits
|
|
431
|
+
|
|
432
|
+
Attributes:
|
|
433
|
+
name: Team name (used to store results)
|
|
434
|
+
supervisor: Entry agent name (default entry point)
|
|
435
|
+
shared: List of SetNode for shared state initialization
|
|
436
|
+
agents: List of AgentNode team members
|
|
437
|
+
execute: AgentExecuteNode with task and optional entry agent
|
|
438
|
+
max_handoffs: Maximum handoffs allowed (safety limit, default 10)
|
|
439
|
+
max_total_iterations: Sum of all agent iterations (default 50)
|
|
440
|
+
|
|
441
|
+
Result Object:
|
|
442
|
+
{name}: The final agent's response (string)
|
|
443
|
+
{name}_result: Full result object with:
|
|
444
|
+
- success: bool
|
|
445
|
+
- finalResponse: str
|
|
446
|
+
- finalAgent: str
|
|
447
|
+
- handoffs: [{fromAgent, toAgent, message, timestamp}, ...]
|
|
448
|
+
- totalIterations: int
|
|
449
|
+
- executionTime: float (ms)
|
|
450
|
+
- agentResults: {agentName: AgentResult, ...}
|
|
451
|
+
- sharedContext: {key: value, ...}
|
|
452
|
+
|
|
453
|
+
Example:
|
|
454
|
+
<q:team name="support" supervisor="router">
|
|
455
|
+
<q:shared>
|
|
456
|
+
<q:set name="customerName" value="John" />
|
|
457
|
+
</q:shared>
|
|
458
|
+
|
|
459
|
+
<q:agent name="router" model="gpt-4">
|
|
460
|
+
<q:instruction>Route to appropriate specialist</q:instruction>
|
|
461
|
+
<q:tool name="handoff" builtin="true" />
|
|
462
|
+
</q:agent>
|
|
463
|
+
|
|
464
|
+
<q:agent name="billing" model="gpt-4">
|
|
465
|
+
<q:instruction>Handle billing questions</q:instruction>
|
|
466
|
+
<q:tool name="readShared" builtin="true" />
|
|
467
|
+
</q:agent>
|
|
468
|
+
|
|
469
|
+
<q:execute task="{question}" entry="router" />
|
|
470
|
+
</q:team>
|
|
471
|
+
"""
|
|
472
|
+
name: str = ""
|
|
473
|
+
supervisor: str = "" # Entry agent (default)
|
|
474
|
+
shared: List[Any] = field(default_factory=list) # SetNode list
|
|
475
|
+
agents: List['AgentNode'] = field(default_factory=list)
|
|
476
|
+
execute: Optional[AgentExecuteNode] = None
|
|
477
|
+
max_handoffs: int = 10 # Safety limit for handoffs
|
|
478
|
+
max_total_iterations: int = 50 # Total iterations across all agents
|
|
479
|
+
|
|
480
|
+
def to_dict(self) -> dict:
|
|
481
|
+
"""Convert to dictionary."""
|
|
482
|
+
return {
|
|
483
|
+
"name": self.name,
|
|
484
|
+
"supervisor": self.supervisor,
|
|
485
|
+
"shared": [s.to_dict() if hasattr(s, 'to_dict') else str(s) for s in self.shared],
|
|
486
|
+
"agents": [a.to_dict() for a in self.agents],
|
|
487
|
+
"execute": self.execute.to_dict() if self.execute else None,
|
|
488
|
+
"max_handoffs": self.max_handoffs,
|
|
489
|
+
"max_total_iterations": self.max_total_iterations
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
def validate(self) -> List[str]:
|
|
493
|
+
"""Validate the team node."""
|
|
494
|
+
errors = []
|
|
495
|
+
|
|
496
|
+
if not self.name:
|
|
497
|
+
errors.append("AgentTeamNode requires 'name' attribute")
|
|
498
|
+
|
|
499
|
+
if not self.agents:
|
|
500
|
+
errors.append("AgentTeamNode requires at least one agent")
|
|
501
|
+
|
|
502
|
+
if not self.execute:
|
|
503
|
+
errors.append("AgentTeamNode requires <q:execute> to run")
|
|
504
|
+
|
|
505
|
+
# Validate supervisor exists in agents
|
|
506
|
+
agent_names = {a.name for a in self.agents}
|
|
507
|
+
if self.supervisor and self.supervisor not in agent_names:
|
|
508
|
+
errors.append(f"Supervisor '{self.supervisor}' not found in team agents: {agent_names}")
|
|
509
|
+
|
|
510
|
+
# Validate entry agent if specified in execute
|
|
511
|
+
if self.execute and hasattr(self.execute, 'entry'):
|
|
512
|
+
entry = getattr(self.execute, 'entry', None)
|
|
513
|
+
if entry and entry not in agent_names:
|
|
514
|
+
errors.append(f"Entry agent '{entry}' not found in team agents: {agent_names}")
|
|
515
|
+
|
|
516
|
+
if self.max_handoffs < 1:
|
|
517
|
+
errors.append("max_handoffs must be at least 1")
|
|
518
|
+
|
|
519
|
+
if self.max_handoffs > 100:
|
|
520
|
+
errors.append("max_handoffs should not exceed 100 for safety")
|
|
521
|
+
|
|
522
|
+
if self.max_total_iterations < 1:
|
|
523
|
+
errors.append("max_total_iterations must be at least 1")
|
|
524
|
+
|
|
525
|
+
# Validate child agents
|
|
526
|
+
for agent in self.agents:
|
|
527
|
+
errors.extend(agent.validate())
|
|
528
|
+
|
|
529
|
+
return errors
|
|
530
|
+
|
|
531
|
+
def get_agent(self, name: str) -> Optional['AgentNode']:
|
|
532
|
+
"""Get an agent by name."""
|
|
533
|
+
for agent in self.agents:
|
|
534
|
+
if agent.name == name:
|
|
535
|
+
return agent
|
|
536
|
+
return None
|
|
537
|
+
|
|
538
|
+
def get_agent_names(self) -> List[str]:
|
|
539
|
+
"""Get list of all agent names in the team."""
|
|
540
|
+
return [a.name for a in self.agents]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST Node for Conditionals (q:if/q:else/q:elseif)
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, List
|
|
6
|
+
|
|
7
|
+
# Import base class
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from quantum.core.ast_nodes import QuantumNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IfNode(QuantumNode):
|
|
14
|
+
"""Represents a <q:if> with elseif and else"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, condition: str):
|
|
17
|
+
self.condition = condition
|
|
18
|
+
self.if_body: List[QuantumNode] = []
|
|
19
|
+
|
|
20
|
+
# Support for elseif blocks
|
|
21
|
+
self.elseif_blocks: List[Dict[str, Any]] = [] # [{"condition": str, "body": List[QuantumNode]}]
|
|
22
|
+
|
|
23
|
+
# Support for else block
|
|
24
|
+
self.else_body: List[QuantumNode] = []
|
|
25
|
+
|
|
26
|
+
def add_if_statement(self, statement: QuantumNode):
|
|
27
|
+
"""Add statement to if body"""
|
|
28
|
+
self.if_body.append(statement)
|
|
29
|
+
|
|
30
|
+
def add_elseif_block(self, condition: str, body: List[QuantumNode] = None):
|
|
31
|
+
"""Add elseif block"""
|
|
32
|
+
self.elseif_blocks.append({
|
|
33
|
+
"condition": condition,
|
|
34
|
+
"body": body or []
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
def add_else_statement(self, statement: QuantumNode):
|
|
38
|
+
"""Add statement to else body"""
|
|
39
|
+
self.else_body.append(statement)
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
42
|
+
return {
|
|
43
|
+
"type": "if",
|
|
44
|
+
"condition": self.condition,
|
|
45
|
+
"if_body_count": len(self.if_body),
|
|
46
|
+
"elseif_count": len(self.elseif_blocks),
|
|
47
|
+
"has_else": len(self.else_body) > 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def validate(self) -> List[str]:
|
|
51
|
+
errors = []
|
|
52
|
+
if not self.condition:
|
|
53
|
+
errors.append("If condition is required")
|
|
54
|
+
|
|
55
|
+
# Validate all bodies
|
|
56
|
+
for statement in self.if_body:
|
|
57
|
+
errors.extend(statement.validate())
|
|
58
|
+
|
|
59
|
+
for elseif_block in self.elseif_blocks:
|
|
60
|
+
if not elseif_block["condition"]:
|
|
61
|
+
errors.append("Elseif condition is required")
|
|
62
|
+
for statement in elseif_block["body"]:
|
|
63
|
+
errors.extend(statement.validate())
|
|
64
|
+
|
|
65
|
+
for statement in self.else_body:
|
|
66
|
+
errors.extend(statement.validate())
|
|
67
|
+
|
|
68
|
+
return errors
|
|
File without changes
|