quantum-framework 0.18.0__tar.gz → 0.20.0__tar.gz
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_framework-0.18.0 → quantum_framework-0.20.0}/PKG-INFO +2 -2
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/README.md +1 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/pyproject.toml +1 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/runner.py +15 -1
- quantum_framework-0.20.0/quantum/cli/schema_plan.py +273 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/ast_nodes.py +5 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/expression_stdlib.py +7 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/agents/src/ast_node.py +2 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/knowledge_base/src/ast_node.py +1 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/ui_engine/src/ast_nodes.py +34 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/ui_engine/src/parser.py +26 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parser.py +28 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/agent_parser.py +11 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/knowledge_parser.py +10 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/llm_parser.py +15 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/query_parser.py +6 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/action_handler.py +3 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/agent_service.py +8 -2
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/component.py +4 -19
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/database_service.py +12 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/ai/agent_executor.py +11 -5
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/ai/knowledge_executor.py +6 -4
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/ai/llm_executor.py +98 -4
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/data/query_executor.py +7 -0
- quantum_framework-0.20.0/quantum/runtime/history.py +203 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/knowledge_service.py +40 -40
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/llm_providers.py +60 -7
- quantum_framework-0.20.0/quantum/runtime/llm_stream.py +103 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_console.py +30 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_page_renderer.py +70 -2
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_view.py +18 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/web_server.py +24 -5
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/PKG-INFO +2 -2
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/SOURCES.txt +3 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_agent.py +1 -1
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/LICENSE +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/admin.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/check.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/build.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/dev.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/docs.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/lint.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/migrate.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/new.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/serve.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/commands/test.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/deploy.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/hot_reload.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/jobs.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/mq.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/pkg.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/server_process.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/cli/utils.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/base_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/cli.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/expression_transformer.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/javascript/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/javascript/generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/optimizer.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/python/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/python/generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/python/runtime.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/compiler/transpiler.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/config_env.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/docs_links.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/expression_diagnostics.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/expressions.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/agents/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/conditionals/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/conditionals/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/data_import/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/data_import/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/data_import/src/runtime.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/dump/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/dump/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/dump/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/dump/src/runtime.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/functions/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/functions/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/game_engine_2d/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/game_engine_2d/src/ast_nodes.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/game_engine_2d/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/invocation/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/invocation/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/invocation/src/runtime.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/knowledge_base/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/knowledge_base/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/logging/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/logging/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/logging/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/logging/src/runtime.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/loops/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/loops/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/query/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/query/src/query_validators.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/state_management/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/state_management/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/terminal_engine/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/terminal_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/terminal_engine/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/testing_engine/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/testing_engine/src/ast_nodes.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/testing_engine/src/parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/theming/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/theming/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/theming/src/presets.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/ui_engine/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/websocket/src/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/websocket/src/ast_node.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/guards.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/html_compat.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parser_registry.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/team_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/base.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/composition/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/composition/import_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/composition/slot_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/control_flow/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/control_flow/if_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/control_flow/loop_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/control_flow/set_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/data_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/invoke_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/transaction_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/events/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/events/dispatch_event_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/forms/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/forms/action_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/forms/flash_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/forms/redirect_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/functions/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/functions/function_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/functions/param_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/functions/return_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/html/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/html/component_call_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/html/html_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/jobs/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/jobs/job_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/jobs/schedule_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/jobs/thread_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/message_ack_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/message_nack_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/message_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/queue_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/messaging/websocket_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/routing/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/routing/route_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/scripting/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/scripting/pyclass_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/scripting/pydecorator_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/scripting/pyimport_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/scripting/python_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/services/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/services/dump_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/services/file_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/services/log_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/services/mail_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/tiers.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/xml_lines.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/packages/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/packages/manager.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/packages/manifest.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/packages/registry.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/packages/resolver.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/plugins/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/plugins/hooks.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/plugins/loader.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/plugins/manifest.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/plugins/registry.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/adapters/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/adapters/memory_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/adapters/rabbitmq_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/adapters/redis_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/adapters/sqlite_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ast_cache.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/auth_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/component_composer.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/component_resolver.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/conditions.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/db_schema.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/dev_panel.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/email_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/error_handler.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/execution_context.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executor_registry.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/ai/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/ai/team_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/base.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/control_flow/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/control_flow/if_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/control_flow/loop_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/control_flow/redirect_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/control_flow/set_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/data/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/data/data_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/data/invoke_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/data/transaction_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/jobs/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/jobs/job_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/jobs/schedule_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/jobs/thread_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/messaging/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/messaging/message_ack_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/messaging/message_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/messaging/queue_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/messaging/websocket_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/scripting/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/scripting/pyclass_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/scripting/pyimport_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/scripting/python_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/services/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/services/dump_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/services/file_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/services/log_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/executors/services/mail_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/expression_cache.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/file_upload_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/function_registry.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/game_builder.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/game_code_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/game_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/godot_code_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/godot_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/job_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/llm_cache.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/llm_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/local_server.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/logging_setup.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/message_broker.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/message_queue_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/param_validation.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/pypy_compat.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/python_bridge.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/query_validators.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/renderer.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/service_container.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/table_params.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/terminal_builder.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/terminal_code_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/terminal_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/testing_builder.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/testing_code_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/testing_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_builder.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_desktop.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_form_rules.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_html_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_html_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_mobile_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_mobile_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_pager.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_table_edit.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_textual_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_textual_templates.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_tokens.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/ui_validator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/validators.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/websocket_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/websocket_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/websocket_transport.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/runtime/wsgi.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/services.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/utils/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/assets/quantum-admin-connectors.js +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/assets/quantum-admin.css +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/assets/quantum-source-highlight.js +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/audit_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/auth_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/component_discovery.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/config_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/connector_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/crud.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/database.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/db_setup_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/deploy_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/docker_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/environment_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/git_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/health_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/job_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/log_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/main.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/models.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/pipeline_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/resource_manager.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/schemas.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/secret_manager.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/settings_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/template_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/test_execution_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/test_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/webhook_service.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/backend/websocket_manager.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/_layout/AdminShell.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/agents.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/app/[name].q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/applications.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/component/[...path].q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/components.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/connectors.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/dashboard.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/database.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/features.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/index.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/jobs.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/login.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/logout.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/settings.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/source.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/components/admin/tests.q +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/run.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/__init__.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/_base.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/_component_test_generator.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/_yaml_editor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/apps.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/auth.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/components.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/connectors.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/projects.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/repository.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/settings.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_admin/services/yaml_import.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/dependency_links.txt +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/entry_points.txt +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/requires.txt +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum_framework.egg-info/top_level.txt +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/setup.cfg +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_animations.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_ast_cache.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_cli_commands.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_component_library.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_expression_cache.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_form_validation.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_game_codegen.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_game_e2e.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_game_engine.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_game_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_godot_codegen.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_job_executor.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_llm_providers.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_message_broker.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_message_queue.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_mobile_target.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_multi_agent.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_packages.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_plugins.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_pypy_compat.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_python_scripting.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_terminal_codegen.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_terminal_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_testing_codegen.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_testing_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_theming.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_ui_html_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_ui_parity.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_ui_parser.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_ui_textual_adapter.py +0 -0
- {quantum_framework-0.18.0 → quantum_framework-0.20.0}/tests/test_websocket.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantum-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: Full-stack declarative framework for building web, desktop, and mobile applications
|
|
5
5
|
Author-email: Daniel Gregorio <danielgregorio2611@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -94,7 +94,7 @@ and asks the model — in the markup. There is no Python file behind it.
|
|
|
94
94
|
An agent with its own tools, same idea:
|
|
95
95
|
|
|
96
96
|
```xml
|
|
97
|
-
<q:agent name="calc" model="phi3"
|
|
97
|
+
<q:agent name="calc" model="phi3" maxIterations="4">
|
|
98
98
|
<q:instruction>Use the add tool, then answer.</q:instruction>
|
|
99
99
|
|
|
100
100
|
<q:tool name="add" description="Add two numbers">
|
|
@@ -44,7 +44,7 @@ and asks the model — in the markup. There is no Python file behind it.
|
|
|
44
44
|
An agent with its own tools, same idea:
|
|
45
45
|
|
|
46
46
|
```xml
|
|
47
|
-
<q:agent name="calc" model="phi3"
|
|
47
|
+
<q:agent name="calc" model="phi3" maxIterations="4">
|
|
48
48
|
<q:instruction>Use the add tool, then answer.</q:instruction>
|
|
49
49
|
|
|
50
50
|
<q:tool name="add" description="Add two numbers">
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantum-framework"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.20.0"
|
|
8
8
|
description = "Full-stack declarative framework for building web, desktop, and mobile applications"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -399,6 +399,15 @@ Examples:
|
|
|
399
399
|
migrate_create_parser = migrate_subparsers.add_parser('create', help='Create a new migration')
|
|
400
400
|
migrate_create_parser.add_argument('name', help='Migration name')
|
|
401
401
|
|
|
402
|
+
# M6 (DB-10): schema.sql as the source of truth
|
|
403
|
+
migrate_plan_parser = migrate_subparsers.add_parser(
|
|
404
|
+
'plan', help='Compare schema.sql with the migrations and plan the migration')
|
|
405
|
+
migrate_plan_parser.add_argument('--schema', default='schema.sql', help='Schema file (default: schema.sql)')
|
|
406
|
+
migrate_plan_parser.add_argument('--write', metavar='NAME', help='Save the plan as migration V00N_NAME')
|
|
407
|
+
migrate_plan_parser.add_argument('--yes', action='store_true', help='Do not ask before writing')
|
|
408
|
+
migrate_plan_parser.add_argument('--allow-data-loss', action='store_true',
|
|
409
|
+
help='Write a plan that drops tables or columns')
|
|
410
|
+
|
|
402
411
|
# Parse arguments
|
|
403
412
|
args = parser.parse_args()
|
|
404
413
|
|
|
@@ -541,13 +550,18 @@ Examples:
|
|
|
541
550
|
if failed:
|
|
542
551
|
sys.exit(1)
|
|
543
552
|
|
|
553
|
+
elif args.migrate_command == 'plan':
|
|
554
|
+
from quantum.cli.schema_plan import run_plan
|
|
555
|
+
sys.exit(run_plan(Path.cwd(), args.schema, args.write, args.yes, args.allow_data_loss,
|
|
556
|
+
interactive=sys.stdin.isatty()))
|
|
557
|
+
|
|
544
558
|
elif args.migrate_command == 'create':
|
|
545
559
|
filepath = runner.create(args.name)
|
|
546
560
|
print(f"Created migration: {filepath}")
|
|
547
561
|
print(f"Also created rollback: {filepath.with_suffix('.down.sql')}")
|
|
548
562
|
|
|
549
563
|
else:
|
|
550
|
-
print("Usage: quantum migrate <status|up|down|create> [options]")
|
|
564
|
+
print("Usage: quantum migrate <status|up|down|create|plan> [options]")
|
|
551
565
|
sys.exit(1)
|
|
552
566
|
|
|
553
567
|
sys.exit(0)
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""`quantum migrate plan`: a declarative schema (M6, DB-10).
|
|
2
|
+
|
|
3
|
+
You edit `schema.sql` — the tables as they should be. `quantum migrate plan`
|
|
4
|
+
compares it with the schema the migrations produce, shows what changes, marks
|
|
5
|
+
what loses data, and `--write` saves the migration (and its rollback) after
|
|
6
|
+
confirmation. `quantum migrate up` applies it as any other.
|
|
7
|
+
|
|
8
|
+
Both sides are built in in-memory SQLite databases — the migrations applied in
|
|
9
|
+
order, and schema.sql — and compared with the schema reader of `quantum check`
|
|
10
|
+
(quantum/runtime/db_schema.py). The development database is not read: the
|
|
11
|
+
plan is the same on any machine and in CI.
|
|
12
|
+
|
|
13
|
+
SQLite can add a column but not change one, so any other change to a table
|
|
14
|
+
rebuilds it: create the new table, copy the columns both have, drop the old,
|
|
15
|
+
rename. Before a plan is written it is checked: applied to the migrations'
|
|
16
|
+
schema it must produce exactly schema.sql's.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import re
|
|
20
|
+
import sqlite3
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Dict, List, Optional, Tuple
|
|
24
|
+
|
|
25
|
+
from quantum.runtime.db_schema import Column, read_schema
|
|
26
|
+
|
|
27
|
+
# Tables that belong to Quantum, not to the app's schema.
|
|
28
|
+
INTERNAL_TABLES = {'_migrations', 'quantum_history'}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SchemaPlanError(Exception):
|
|
32
|
+
"""The plan cannot be made (no schema.sql, invalid SQL, a change it cannot express)."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Step:
|
|
37
|
+
kind: str # add-table, drop-table, add-column, rebuild, add-index, drop-index
|
|
38
|
+
target: str # "posts" or "posts.summary"
|
|
39
|
+
detail: str = ''
|
|
40
|
+
sql: List[str] = field(default_factory=list)
|
|
41
|
+
loses_data: bool = False
|
|
42
|
+
warning: str = ''
|
|
43
|
+
|
|
44
|
+
def line(self) -> str:
|
|
45
|
+
mark = {'add-table': '+', 'add-column': '+', 'add-index': '+', 'drop-table': '-',
|
|
46
|
+
'drop-index': '-', 'rebuild': '~'}.get(self.kind, '?')
|
|
47
|
+
text = f'{mark} {self.kind.replace("-", " ")} {self.target}'
|
|
48
|
+
if self.detail:
|
|
49
|
+
text += f' ({self.detail})'
|
|
50
|
+
if self.loses_data:
|
|
51
|
+
text += ' !! loses data'
|
|
52
|
+
if self.warning:
|
|
53
|
+
text += f' !! {self.warning}'
|
|
54
|
+
return text
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _database(scripts: List[Tuple[str, str]]) -> sqlite3.Connection:
|
|
58
|
+
conn = sqlite3.connect(':memory:')
|
|
59
|
+
for origin, sql in scripts:
|
|
60
|
+
try:
|
|
61
|
+
conn.executescript(sql)
|
|
62
|
+
except sqlite3.Error as exc:
|
|
63
|
+
raise SchemaPlanError(f'{origin}: {exc}') from exc
|
|
64
|
+
return conn
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _migration_scripts(migrations_dir: Path) -> List[Tuple[str, str]]:
|
|
68
|
+
if not migrations_dir.exists():
|
|
69
|
+
return []
|
|
70
|
+
files = [f for f in sorted(migrations_dir.glob('*.sql')) if not f.name.endswith('.down.sql')]
|
|
71
|
+
return [(f.name, f.read_text(encoding='utf-8')) for f in files if '_' in f.stem]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _tables(conn) -> Dict[str, object]:
|
|
75
|
+
return {name: t for name, t in read_schema(conn).items() if name not in INTERNAL_TABLES}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _create_sql(conn, table: str) -> str:
|
|
79
|
+
return conn.execute("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
|
|
80
|
+
(table,)).fetchone()[0]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _indexes(conn) -> Dict[str, Tuple[str, str]]:
|
|
84
|
+
"""Explicit indexes: name -> (table, sql)."""
|
|
85
|
+
return {name: (table, sql) for name, table, sql in conn.execute(
|
|
86
|
+
"SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index' AND sql IS NOT NULL")
|
|
87
|
+
if table not in INTERNAL_TABLES}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _column_sql(column: Column) -> str:
|
|
91
|
+
parts = [f'"{column.name}"']
|
|
92
|
+
if column.type:
|
|
93
|
+
parts.append(column.type)
|
|
94
|
+
if column.not_null:
|
|
95
|
+
parts.append('NOT NULL')
|
|
96
|
+
if column.default is not None:
|
|
97
|
+
parts.append(f'DEFAULT {column.default}')
|
|
98
|
+
if column.options:
|
|
99
|
+
options = ', '.join("'" + o.replace("'", "''") + "'" for o in column.options)
|
|
100
|
+
parts.append(f'CHECK ("{column.name}" IN ({options}))')
|
|
101
|
+
if column.references:
|
|
102
|
+
parts.append(f'REFERENCES "{column.references[0]}"("{column.references[1]}")')
|
|
103
|
+
return ' '.join(parts)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _differences(a: Column, b: Column) -> List[str]:
|
|
107
|
+
changed = []
|
|
108
|
+
for label, x, y in (('type', (a.type or '').upper(), (b.type or '').upper()),
|
|
109
|
+
('NOT NULL', a.not_null, b.not_null), ('default', a.default, b.default),
|
|
110
|
+
('primary key', a.primary_key, b.primary_key), ('CHECK', a.options, b.options),
|
|
111
|
+
('references', a.references, b.references)):
|
|
112
|
+
if x != y:
|
|
113
|
+
changed.append(label)
|
|
114
|
+
return changed
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def diff(current_db, desired_db) -> List[Step]:
|
|
118
|
+
"""The steps that turn the first database's schema into the second's."""
|
|
119
|
+
current, desired = _tables(current_db), _tables(desired_db)
|
|
120
|
+
steps: List[Step] = []
|
|
121
|
+
rebuilt = set()
|
|
122
|
+
|
|
123
|
+
for name in sorted(set(desired) - set(current)):
|
|
124
|
+
steps.append(Step('add-table', name, sql=[_create_sql(desired_db, name) + ';']))
|
|
125
|
+
|
|
126
|
+
for name in sorted(set(current) & set(desired)):
|
|
127
|
+
before, after = current[name].columns, desired[name].columns
|
|
128
|
+
added = [c for c in after if c not in before]
|
|
129
|
+
removed = [c for c in before if c not in after]
|
|
130
|
+
changed = {c: _differences(before[c], after[c]) for c in before
|
|
131
|
+
if c in after and _differences(before[c], after[c])}
|
|
132
|
+
addable = all(not after[c].primary_key and (not after[c].not_null or after[c].default is not None)
|
|
133
|
+
for c in added)
|
|
134
|
+
if not removed and not changed and addable:
|
|
135
|
+
for c in added:
|
|
136
|
+
steps.append(Step('add-column', f'{name}.{c}', after[c].type or '',
|
|
137
|
+
[f'ALTER TABLE "{name}" ADD COLUMN {_column_sql(after[c])};']))
|
|
138
|
+
continue
|
|
139
|
+
if not (added or removed or changed):
|
|
140
|
+
continue
|
|
141
|
+
common = ', '.join(f'"{c}"' for c in after if c in before)
|
|
142
|
+
create = re.sub(r'^\s*CREATE\s+TABLE\s+("?)' + re.escape(name) + r'\1',
|
|
143
|
+
f'CREATE TABLE "{name}__new"', _create_sql(desired_db, name), count=1, flags=re.I)
|
|
144
|
+
details = [f'+ {c}' for c in added] + [f'- {c}' for c in removed] + \
|
|
145
|
+
[f'{c}: {", ".join(d)}' for c, d in changed.items()]
|
|
146
|
+
warning = ''
|
|
147
|
+
if any('NOT NULL' in d and after[c].not_null for c, d in changed.items()) or \
|
|
148
|
+
any(after[c].not_null and after[c].default is None for c in added):
|
|
149
|
+
warning = 'fails if existing rows have no value for a NOT NULL column'
|
|
150
|
+
steps.append(Step('rebuild', name, '; '.join(details), [
|
|
151
|
+
create + ';',
|
|
152
|
+
f'INSERT INTO "{name}__new" ({common}) SELECT {common} FROM "{name}";',
|
|
153
|
+
f'DROP TABLE "{name}";',
|
|
154
|
+
f'ALTER TABLE "{name}__new" RENAME TO "{name}";',
|
|
155
|
+
], loses_data=bool(removed) or any('type' in d for d in changed.values()), warning=warning))
|
|
156
|
+
rebuilt.add(name)
|
|
157
|
+
|
|
158
|
+
for name in sorted(set(current) - set(desired)):
|
|
159
|
+
steps.append(Step('drop-table', name, sql=[f'DROP TABLE "{name}";'], loses_data=True))
|
|
160
|
+
|
|
161
|
+
# Indexes: a rebuilt table lost its own, so they are created again.
|
|
162
|
+
before_idx, after_idx = _indexes(current_db), _indexes(desired_db)
|
|
163
|
+
for name, (table, sql) in sorted(before_idx.items()):
|
|
164
|
+
if table in desired and table not in rebuilt and after_idx.get(name, (None, None))[1] != sql:
|
|
165
|
+
steps.append(Step('drop-index', name, sql=[f'DROP INDEX "{name}";']))
|
|
166
|
+
for name, (table, sql) in sorted(after_idx.items()):
|
|
167
|
+
if before_idx.get(name, (None, None))[1] != sql or table in rebuilt:
|
|
168
|
+
steps.append(Step('add-index', name, sql=[sql + ';']))
|
|
169
|
+
return steps
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _schema_only(conn) -> str:
|
|
173
|
+
"""The CREATE statements of a database, tables before indexes, without its rows."""
|
|
174
|
+
rows = conn.execute("SELECT type, sql FROM sqlite_master WHERE sql IS NOT NULL "
|
|
175
|
+
"AND name NOT LIKE 'sqlite_%' ORDER BY type = 'index', rowid").fetchall()
|
|
176
|
+
return '\n'.join(sql + ';' for _type, sql in rows)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _signature(conn) -> tuple:
|
|
180
|
+
tables = _tables(conn)
|
|
181
|
+
return (tuple(sorted((n, tuple(sorted((c.name, (c.type or '').upper(), c.not_null, c.default, c.primary_key,
|
|
182
|
+
tuple(c.options or ()), c.references)
|
|
183
|
+
for c in t.columns.values()))) for n, t in tables.items())),
|
|
184
|
+
tuple(sorted((n, s) for n, (_t, s) in _indexes(conn).items())))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class Plan:
|
|
189
|
+
steps: List[Step]
|
|
190
|
+
up_sql: str
|
|
191
|
+
down_sql: str
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def loses_data(self) -> bool:
|
|
195
|
+
return any(s.loses_data for s in self.steps)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def make_plan(schema_file: Path, migrations_dir: Path) -> Plan:
|
|
199
|
+
if not schema_file.exists():
|
|
200
|
+
raise SchemaPlanError(f'{schema_file} does not exist: write the tables as they should be, '
|
|
201
|
+
f'e.g. CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT NOT NULL);')
|
|
202
|
+
migrations = _migration_scripts(migrations_dir)
|
|
203
|
+
current = _database(migrations)
|
|
204
|
+
desired = _database([(schema_file.name, schema_file.read_text(encoding='utf-8'))])
|
|
205
|
+
steps = diff(current, desired)
|
|
206
|
+
up = '\n'.join(s for step in steps for s in step.sql)
|
|
207
|
+
down = '\n'.join(s for step in diff(desired, current) for s in step.sql)
|
|
208
|
+
if steps:
|
|
209
|
+
# The plan must do what it says: applied, it yields schema.sql's schema.
|
|
210
|
+
# Checked on the structure only — what existing rows may break is the
|
|
211
|
+
# step's warning, shown in the plan, not a reason to hide the plan.
|
|
212
|
+
check = _database([('migrations (schema only)', _schema_only(current)), ('plan', up)])
|
|
213
|
+
if _signature(check) != _signature(desired):
|
|
214
|
+
raise SchemaPlanError('the generated migration does not reproduce schema.sql — '
|
|
215
|
+
'write this change as a migration by hand (quantum migrate create)')
|
|
216
|
+
return Plan(steps, up, down)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def next_version(migrations_dir: Path) -> str:
|
|
220
|
+
numbers = [int(m.group(1)) for f in migrations_dir.glob('V*.sql')
|
|
221
|
+
for m in [re.match(r'V(\d+)_', f.name)] if m]
|
|
222
|
+
return f'V{(max(numbers) + 1 if numbers else 1):03d}'
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def write_plan(plan: Plan, migrations_dir: Path, name: str) -> Path:
|
|
226
|
+
migrations_dir.mkdir(parents=True, exist_ok=True)
|
|
227
|
+
slug = re.sub(r'[^a-z0-9_]+', '_', name.lower()).strip('_') or 'schema'
|
|
228
|
+
version = next_version(migrations_dir)
|
|
229
|
+
path = migrations_dir / f'{version}_{slug}.sql'
|
|
230
|
+
header = '-- Generated by `quantum migrate plan` from schema.sql (DB-10)\n'
|
|
231
|
+
summary = '\n'.join(f'-- {s.line()}' for s in plan.steps)
|
|
232
|
+
path.write_text(f'{header}{summary}\n\n{plan.up_sql}\n', encoding='utf-8')
|
|
233
|
+
(migrations_dir / f'{version}_{slug}.down.sql').write_text(
|
|
234
|
+
f'{header}-- rollback\n\n{plan.down_sql}\n', encoding='utf-8')
|
|
235
|
+
return path
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def run_plan(project: Path, schema: Optional[str], write: Optional[str], yes: bool,
|
|
239
|
+
allow_data_loss: bool, ask=input, interactive: bool = True) -> int:
|
|
240
|
+
"""The `quantum migrate plan` command. Returns the exit code."""
|
|
241
|
+
from quantum.cli.commands.migrate import _paths_migrations
|
|
242
|
+
migrations_dir = project / _paths_migrations(project)
|
|
243
|
+
schema_file = project / (schema or 'schema.sql')
|
|
244
|
+
try:
|
|
245
|
+
plan = make_plan(schema_file, migrations_dir)
|
|
246
|
+
except SchemaPlanError as exc:
|
|
247
|
+
print(f'[ERROR] {exc}')
|
|
248
|
+
return 1
|
|
249
|
+
if not plan.steps:
|
|
250
|
+
print(f'Nothing to do: the migrations already produce {schema_file.name}.')
|
|
251
|
+
return 0
|
|
252
|
+
print(f'Plan: {schema_file.name} vs. the migrations in {migrations_dir.name}/\n')
|
|
253
|
+
for step in plan.steps:
|
|
254
|
+
print(' ' + step.line())
|
|
255
|
+
print('\nSQL:\n' + plan.up_sql)
|
|
256
|
+
if not write:
|
|
257
|
+
print('\nNothing written. `quantum migrate plan --write <name>` saves it as a migration.')
|
|
258
|
+
return 0
|
|
259
|
+
if plan.loses_data and not allow_data_loss:
|
|
260
|
+
print('\n[REFUSED] This plan loses data (marked !!). Check it, then run again with '
|
|
261
|
+
'--allow-data-loss.')
|
|
262
|
+
return 1
|
|
263
|
+
if not yes:
|
|
264
|
+
if not interactive:
|
|
265
|
+
print('\n[REFUSED] Confirm with --yes (not a terminal: nobody to ask).')
|
|
266
|
+
return 1
|
|
267
|
+
answer = ask(f'\nWrite migration {next_version(migrations_dir)}_{write}? [y/N] ').strip().lower()
|
|
268
|
+
if answer not in ('y', 'yes'):
|
|
269
|
+
print('Nothing written.')
|
|
270
|
+
return 1
|
|
271
|
+
path = write_plan(plan, migrations_dir, write)
|
|
272
|
+
print(f'Written: {path} (and {path.stem}.down.sql). Apply with `quantum migrate up`.')
|
|
273
|
+
return 0
|
|
@@ -699,6 +699,7 @@ class QueryNode(QuantumNode):
|
|
|
699
699
|
self.paginate = False # Enable automatic pagination
|
|
700
700
|
self.page = None
|
|
701
701
|
self.page_expr = None # DB-9: page as an expression (default {query.page})
|
|
702
|
+
self.on_error = 'fail' # IA-5: 'continue' hands a failure to <name>_result
|
|
702
703
|
self.sortable = False # UI-13: ORDER BY the URL's ?sort=/&dir=
|
|
703
704
|
self.page_size = 20
|
|
704
705
|
self.timeout = None
|
|
@@ -1478,6 +1479,10 @@ class LLMNode(QuantumNode):
|
|
|
1478
1479
|
self.prompt = None # Prompt text (with databinding)
|
|
1479
1480
|
self.system = None # System prompt (optional)
|
|
1480
1481
|
self.messages: List[LLMMessageNode] = [] # Chat mode messages
|
|
1482
|
+
self.on_error = 'fail' # IA-5: 'continue' hands a failure to <name>_result
|
|
1483
|
+
self.knowledge = None # IA-6: answer from this knowledge base, citing sources
|
|
1484
|
+
self.top = 4 # IA-6: how many sources to retrieve
|
|
1485
|
+
self.stream = False # IA-7: stream the answer to <ui:stream> (web requests)
|
|
1481
1486
|
self.temperature = None # 0.0-2.0
|
|
1482
1487
|
self.max_tokens = None # Token limit
|
|
1483
1488
|
self.response_format = None # "text" or "json"
|
|
@@ -172,6 +172,12 @@ def verify_password(password: Any, hashed: Any) -> bool:
|
|
|
172
172
|
return False
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
def _urlencode(v) -> str:
|
|
176
|
+
"""EXPR-12: a value made safe to put in a URL — `q:redirect url="/?q={urlencode(q)}"`."""
|
|
177
|
+
from urllib.parse import quote_plus
|
|
178
|
+
return quote_plus('' if v is None else str(v))
|
|
179
|
+
|
|
180
|
+
|
|
175
181
|
STDLIB = {
|
|
176
182
|
# passwords
|
|
177
183
|
'hashPassword': hash_password,
|
|
@@ -189,6 +195,7 @@ STDLIB = {
|
|
|
189
195
|
'split': _split,
|
|
190
196
|
'contains': _contains,
|
|
191
197
|
'slugify': _slugify,
|
|
198
|
+
'urlencode': _urlencode,
|
|
192
199
|
# collections
|
|
193
200
|
'len': _len,
|
|
194
201
|
'first': _first,
|
{quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/agents/src/ast_node.py
RENAMED
|
@@ -290,7 +290,7 @@ class AgentNode(QuantumNode):
|
|
|
290
290
|
|
|
291
291
|
Example:
|
|
292
292
|
<!-- Using Ollama (default) -->
|
|
293
|
-
<q:agent name="support" model="phi3"
|
|
293
|
+
<q:agent name="support" model="phi3" maxIterations="5">
|
|
294
294
|
...
|
|
295
295
|
</q:agent>
|
|
296
296
|
|
|
@@ -319,6 +319,7 @@ class AgentNode(QuantumNode):
|
|
|
319
319
|
api_key: str = "" # For cloud providers
|
|
320
320
|
max_iterations: int = 10
|
|
321
321
|
timeout: int = 60000 # milliseconds
|
|
322
|
+
on_error: str = "fail" # IA-5: "continue" hands a failure to the page
|
|
322
323
|
|
|
323
324
|
instruction: Optional[AgentInstructionNode] = None
|
|
324
325
|
tools: List[AgentToolNode] = field(default_factory=list)
|
|
@@ -82,6 +82,7 @@ class KnowledgeNode(QuantumNode):
|
|
|
82
82
|
persist: bool = False # Persist ChromaDB to disk
|
|
83
83
|
persist_path: Optional[str] = None # ChromaDB persistence path
|
|
84
84
|
rebuild: bool = False # Force reindex
|
|
85
|
+
on_error: str = 'fail' # IA-5: 'continue' hands a failure to <name>_info
|
|
85
86
|
|
|
86
87
|
def add_source(self, source: KnowledgeSourceNode):
|
|
87
88
|
self.sources.append(source)
|
|
@@ -1896,6 +1896,39 @@ class UIPagerNode(QuantumNode, UILayoutMixin):
|
|
|
1896
1896
|
return [] if self.for_query else ["ui:pager needs for="]
|
|
1897
1897
|
|
|
1898
1898
|
|
|
1899
|
+
class UIStreamNode(QuantumNode, UILayoutMixin):
|
|
1900
|
+
"""<ui:stream for="answer"> — a q:llm answer, arriving as it is written (IA-7)."""
|
|
1901
|
+
|
|
1902
|
+
def __init__(self):
|
|
1903
|
+
self.for_llm: Optional[str] = None
|
|
1904
|
+
self.children: List[QuantumNode] = []
|
|
1905
|
+
self._init_layout_attrs()
|
|
1906
|
+
|
|
1907
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
1908
|
+
return {"type": "ui_stream", "for": self.for_llm}
|
|
1909
|
+
|
|
1910
|
+
def validate(self) -> List[str]:
|
|
1911
|
+
return []
|
|
1912
|
+
|
|
1913
|
+
|
|
1914
|
+
class UIHistoryNode(QuantumNode, UILayoutMixin):
|
|
1915
|
+
"""<ui:history table="t" key="{row.id}" datasource="db"> — a row's change history (DB-11)."""
|
|
1916
|
+
|
|
1917
|
+
def __init__(self):
|
|
1918
|
+
self.table: Optional[str] = None
|
|
1919
|
+
self.key: Optional[str] = None
|
|
1920
|
+
self.datasource: Optional[str] = None
|
|
1921
|
+
self.limit: int = 50
|
|
1922
|
+
self.children: List[QuantumNode] = []
|
|
1923
|
+
self._init_layout_attrs()
|
|
1924
|
+
|
|
1925
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
1926
|
+
return {"type": "ui_history", "table": self.table, "key": self.key}
|
|
1927
|
+
|
|
1928
|
+
def validate(self) -> List[str]:
|
|
1929
|
+
return []
|
|
1930
|
+
|
|
1931
|
+
|
|
1899
1932
|
def is_ui_node(node) -> bool:
|
|
1900
1933
|
"""A ui:* element (UI-1): markup, drawn by a renderer, never executed."""
|
|
1901
1934
|
return type(node).__module__ == __name__
|
|
@@ -1908,6 +1941,6 @@ CORE_TAGS = frozenset({
|
|
|
1908
1941
|
'window', 'hbox', 'vbox', 'grid', 'panel', 'section', 'scrollbox', 'spacer', 'rule',
|
|
1909
1942
|
'header', 'footer', 'card', 'card-header', 'card-body', 'card-footer', 'tabpanel', 'tab',
|
|
1910
1943
|
'text', 'badge', 'alert', 'link', 'image', 'progress',
|
|
1911
|
-
'table', 'column', 'list', 'item', 'pager',
|
|
1944
|
+
'table', 'column', 'list', 'item', 'pager', 'history', 'stream',
|
|
1912
1945
|
'form', 'formitem', 'input', 'checkbox', 'switch', 'radio', 'select', 'option', 'button',
|
|
1913
1946
|
})
|
{quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/features/ui_engine/src/parser.py
RENAMED
|
@@ -28,7 +28,7 @@ from .ast_nodes import (
|
|
|
28
28
|
UIToastNode, UIToastContainerNode,
|
|
29
29
|
UICarouselNode, UISlideNode,
|
|
30
30
|
UIStepperNode, UIStepNode,
|
|
31
|
-
UICalendarNode, UIDatePickerNode, UIPagerNode,
|
|
31
|
+
UICalendarNode, UIDatePickerNode, UIPagerNode, UIHistoryNode, UIStreamNode,
|
|
32
32
|
)
|
|
33
33
|
from quantum.core.features.theming.src import UIThemeNode, UIColorNode
|
|
34
34
|
from quantum.core.ast_nodes import TextNode
|
|
@@ -82,6 +82,8 @@ class UIParser:
|
|
|
82
82
|
'table': '_parse_ui_table',
|
|
83
83
|
'column': '_parse_ui_column',
|
|
84
84
|
'pager': '_parse_ui_pager',
|
|
85
|
+
'history': '_parse_ui_history',
|
|
86
|
+
'stream': '_parse_ui_stream',
|
|
85
87
|
'list': '_parse_ui_list',
|
|
86
88
|
'item': '_parse_ui_item',
|
|
87
89
|
'image': '_parse_ui_image',
|
|
@@ -550,6 +552,29 @@ class UIParser:
|
|
|
550
552
|
self._apply_layout_attrs(node, element)
|
|
551
553
|
return node
|
|
552
554
|
|
|
555
|
+
def _parse_ui_stream(self, element: ET.Element) -> UIStreamNode:
|
|
556
|
+
node = UIStreamNode()
|
|
557
|
+
node.for_llm = element.get('for')
|
|
558
|
+
if not node.for_llm:
|
|
559
|
+
raise UIParseError('<ui:stream> needs for="<the q:llm name>"')
|
|
560
|
+
self._apply_layout_attrs(node, element)
|
|
561
|
+
return node
|
|
562
|
+
|
|
563
|
+
def _parse_ui_history(self, element: ET.Element) -> UIHistoryNode:
|
|
564
|
+
node = UIHistoryNode()
|
|
565
|
+
node.table = element.get('table')
|
|
566
|
+
node.key = element.get('key')
|
|
567
|
+
node.datasource = element.get('datasource')
|
|
568
|
+
missing = [a for a in ('table', 'key', 'datasource') if not element.get(a)]
|
|
569
|
+
if missing:
|
|
570
|
+
raise UIParseError(f'<ui:history> needs {", ".join(a + "=" for a in missing)}')
|
|
571
|
+
limit = element.get('limit')
|
|
572
|
+
if limit is not None and not limit.isdigit():
|
|
573
|
+
raise UIParseError(f'<ui:history limit="{limit}">: a number of entries')
|
|
574
|
+
node.limit = int(limit) if limit is not None else 50
|
|
575
|
+
self._apply_layout_attrs(node, element)
|
|
576
|
+
return node
|
|
577
|
+
|
|
553
578
|
def _parse_ui_list(self, element: ET.Element) -> UIListNode:
|
|
554
579
|
node = UIListNode()
|
|
555
580
|
node.source = element.get('source')
|
|
@@ -480,7 +480,13 @@ class QuantumParser:
|
|
|
480
480
|
A direct else child of a q:if is already the nested form and stays
|
|
481
481
|
where it is. An else with no q:if right before it is an error.
|
|
482
482
|
"""
|
|
483
|
-
|
|
483
|
+
parents = list(root.iter())
|
|
484
|
+
# IF-4 looks at the else children as written: before any sibling-form
|
|
485
|
+
# else is moved into its q:if below (which is not ambiguous).
|
|
486
|
+
for parent in parents:
|
|
487
|
+
if self._get_element_name(parent) == 'if':
|
|
488
|
+
self._check_dangling_else(parent)
|
|
489
|
+
for parent in parents:
|
|
484
490
|
if self._get_element_name(parent) == 'if':
|
|
485
491
|
continue
|
|
486
492
|
anterior = None
|
|
@@ -502,6 +508,27 @@ class QuantumParser:
|
|
|
502
508
|
continue
|
|
503
509
|
anterior = child if nome == 'if' and self._is_quantum_tag(child) else None
|
|
504
510
|
|
|
511
|
+
def _check_dangling_else(self, if_element: ET.Element) -> None:
|
|
512
|
+
"""IF-4: inside a q:if, a q:else/q:elseif right after an inner </q:if> is ambiguous.
|
|
513
|
+
|
|
514
|
+
IF-1 gives it to the outer q:if (a direct child belongs to its if), but
|
|
515
|
+
written right after an inner if it reads as that if's else — the
|
|
516
|
+
sibling form. A page built that way drew the else branch of the inner
|
|
517
|
+
if when the OUTER condition was false. Refused, with both fixes."""
|
|
518
|
+
previous = None
|
|
519
|
+
for child in if_element:
|
|
520
|
+
if not isinstance(child.tag, str):
|
|
521
|
+
continue
|
|
522
|
+
name = self._get_element_name(child)
|
|
523
|
+
if (name in ('else', 'elseif') and self._is_quantum_tag(child) and previous is not None):
|
|
524
|
+
error = QuantumParseError(
|
|
525
|
+
f"<q:{name}> right after an inner </q:if>, inside another <q:if>, is ambiguous "
|
|
526
|
+
f"(IF-4): it would belong to the OUTER q:if (IF-1). If it is the inner q:if's, "
|
|
527
|
+
f"write it inside that q:if; if it is the outer's, put it before the inner q:if.")
|
|
528
|
+
error.line = getattr(child, 'sourceline', None)
|
|
529
|
+
raise error
|
|
530
|
+
previous = child if name == 'if' and self._is_quantum_tag(child) else None
|
|
531
|
+
|
|
505
532
|
# Tags that existed and were removed, with what to use instead.
|
|
506
533
|
REMOVED_TAGS = {
|
|
507
534
|
'persist': ('was removed in Quantum 0.16 (SET-2): it kept variables in the browser, '
|
{quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/agent_parser.py
RENAMED
|
@@ -42,6 +42,15 @@ class AgentParser(BaseTagParser):
|
|
|
42
42
|
|
|
43
43
|
if not name:
|
|
44
44
|
raise ParserError("Agent requires 'name' attribute")
|
|
45
|
+
if element.get('max_iterations') is not None:
|
|
46
|
+
# The guide and the README wrote max_iterations; it was never read
|
|
47
|
+
# and every agent ran up to 10 steps.
|
|
48
|
+
raise ParserError(f'<q:agent name="{name}">: the attribute is maxIterations, '
|
|
49
|
+
f'not max_iterations')
|
|
50
|
+
on_error = self.get_attr(element, 'onerror', 'fail')
|
|
51
|
+
if on_error not in ('fail', 'continue'):
|
|
52
|
+
raise ParserError(f'<q:agent name="{name}"> onerror must be "fail" or "continue", '
|
|
53
|
+
f'not "{on_error}"')
|
|
45
54
|
|
|
46
55
|
agent_node = AgentNode(
|
|
47
56
|
name=name,
|
|
@@ -50,7 +59,8 @@ class AgentParser(BaseTagParser):
|
|
|
50
59
|
provider=self.get_attr(element, 'provider', 'auto'),
|
|
51
60
|
api_key=self.get_attr(element, 'apiKey', ''),
|
|
52
61
|
max_iterations=self.get_int_attr(element, 'maxIterations', 10),
|
|
53
|
-
timeout=self.get_int_attr(element, 'timeout', 60000)
|
|
62
|
+
timeout=self.get_int_attr(element, 'timeout', 60000),
|
|
63
|
+
on_error=on_error,
|
|
54
64
|
)
|
|
55
65
|
|
|
56
66
|
# Parse children
|
{quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/ai/knowledge_parser.py
RENAMED
|
@@ -52,6 +52,11 @@ class KnowledgeParser(BaseTagParser):
|
|
|
52
52
|
persist_path=self.get_attr(element, 'persistPath', './.quantum/knowledge'),
|
|
53
53
|
rebuild=self.get_bool_attr(element, 'rebuild', False)
|
|
54
54
|
)
|
|
55
|
+
# IA-5: a base that cannot be built stops the page unless the page handles it.
|
|
56
|
+
knowledge_node.on_error = self.get_attr(element, 'onerror', 'fail')
|
|
57
|
+
if knowledge_node.on_error not in ('fail', 'continue'):
|
|
58
|
+
raise ParserError(f'<q:knowledge name="{name}"> onerror must be "fail" or "continue", '
|
|
59
|
+
f'not "{knowledge_node.on_error}"')
|
|
55
60
|
|
|
56
61
|
# Parse sources
|
|
57
62
|
for child in element:
|
|
@@ -66,6 +71,11 @@ class KnowledgeParser(BaseTagParser):
|
|
|
66
71
|
def _parse_source(self, element: ET.Element) -> KnowledgeSourceNode:
|
|
67
72
|
"""Parse q:source within q:knowledge."""
|
|
68
73
|
source_type = self.get_attr(element, 'type', 'text')
|
|
74
|
+
# IA-8: type="url" was accepted and read nothing (a log line said "not
|
|
75
|
+
# yet implemented"); an unknown type read nothing at all.
|
|
76
|
+
if source_type not in ('text', 'file', 'directory', 'query'):
|
|
77
|
+
raise ParserError(f'<q:source type="{source_type}">: use text, file, directory or query'
|
|
78
|
+
+ (' (url is not supported)' if source_type == 'url' else ''))
|
|
69
79
|
|
|
70
80
|
source = KnowledgeSourceNode(
|
|
71
81
|
source_type=source_type,
|
|
@@ -60,7 +60,21 @@ class LLMParser(BaseTagParser):
|
|
|
60
60
|
# same attributes now work on both tags.
|
|
61
61
|
llm_node.provider = self.get_attr(element, 'provider')
|
|
62
62
|
llm_node.api_key = self.get_attr(element, 'apiKey')
|
|
63
|
-
|
|
63
|
+
# Seconds to wait for the model; undeclared, the service default (60).
|
|
64
|
+
llm_node.timeout = self.get_int_attr(element, 'timeout', 0) or None
|
|
65
|
+
# M5 (IA-6): answer from a knowledge base, citing its sources.
|
|
66
|
+
llm_node.knowledge = self.get_attr(element, 'knowledge')
|
|
67
|
+
top = self.get_attr(element, 'top')
|
|
68
|
+
if top is not None and not str(top).isdigit():
|
|
69
|
+
raise ParserError(f'<q:llm name="{name}" top="{top}">: the number of sources to retrieve')
|
|
70
|
+
llm_node.top = int(top) if top is not None else 4
|
|
71
|
+
# IA-7: in a web request, the answer arrives as it is written (<ui:stream>).
|
|
72
|
+
llm_node.stream = self.get_bool_attr(element, 'stream', False)
|
|
73
|
+
# IA-5: a failure stops the page unless the page handles it.
|
|
74
|
+
llm_node.on_error = self.get_attr(element, 'onerror', 'fail')
|
|
75
|
+
if llm_node.on_error not in ('fail', 'continue'):
|
|
76
|
+
raise ParserError(f'<q:llm name="{name}"> onerror must be "fail" or "continue", '
|
|
77
|
+
f'not "{llm_node.on_error}"')
|
|
64
78
|
|
|
65
79
|
# Parse children
|
|
66
80
|
for child in element:
|
{quantum_framework-0.18.0 → quantum_framework-0.20.0}/quantum/core/parsers/data/query_parser.py
RENAMED
|
@@ -108,6 +108,12 @@ class QueryParser(BaseTagParser):
|
|
|
108
108
|
or None
|
|
109
109
|
)
|
|
110
110
|
query_node.result = self.get_attr(element, 'result')
|
|
111
|
+
# IA-5 / DB: onerror="continue" hands a failure to the page as <name>_result.
|
|
112
|
+
query_node.on_error = self.get_attr(element, 'onerror', 'fail')
|
|
113
|
+
if query_node.on_error not in ('fail', 'continue'):
|
|
114
|
+
from quantum.core.parser import QuantumParseError
|
|
115
|
+
raise QuantumParseError(f'<q:query name="{name}"> onerror must be "fail" or "continue", '
|
|
116
|
+
f'not "{query_node.on_error}"')
|
|
111
117
|
|
|
112
118
|
# RAG/Knowledge attributes
|
|
113
119
|
query_node.mode = self.get_attr(element, 'mode')
|
|
@@ -122,9 +122,12 @@ class ActionHandler:
|
|
|
122
122
|
context.set_variable(key, value)
|
|
123
123
|
|
|
124
124
|
# 4. Execute action body
|
|
125
|
+
from quantum.runtime import history
|
|
126
|
+
history_token = history.enter(getattr(action, 'name', None), context.session_vars) # M22
|
|
125
127
|
try:
|
|
126
128
|
redirect_info = self._execute_action_body(action, context)
|
|
127
129
|
finally:
|
|
130
|
+
history.leave(history_token)
|
|
128
131
|
# M12: the /_dev panel shows the action's variables (no-op off debug).
|
|
129
132
|
from quantum.runtime.dev_panel import note_scopes
|
|
130
133
|
note_scopes(context, 'action')
|