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,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket Manager for Quantum Admin
|
|
3
|
+
Handles real-time log streaming for deployments
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, List, Optional, Set
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
from fastapi import WebSocket, WebSocketDisconnect
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class LogMessage:
|
|
18
|
+
"""A log message with metadata"""
|
|
19
|
+
timestamp: datetime
|
|
20
|
+
level: str # info, warning, error, success
|
|
21
|
+
message: str
|
|
22
|
+
step: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
def to_dict(self):
|
|
25
|
+
return {
|
|
26
|
+
"timestamp": self.timestamp.isoformat(),
|
|
27
|
+
"level": self.level,
|
|
28
|
+
"message": self.message,
|
|
29
|
+
"step": self.step
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class DeploymentChannel:
|
|
35
|
+
"""A channel for a specific deployment"""
|
|
36
|
+
deploy_id: str
|
|
37
|
+
connections: Set[WebSocket] = field(default_factory=set)
|
|
38
|
+
logs: List[LogMessage] = field(default_factory=list)
|
|
39
|
+
status: str = "pending"
|
|
40
|
+
current_step: Optional[str] = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class WebSocketManager:
|
|
44
|
+
"""
|
|
45
|
+
Manages WebSocket connections for real-time deployment logs
|
|
46
|
+
|
|
47
|
+
Features:
|
|
48
|
+
- Multiple connections per deployment
|
|
49
|
+
- Log buffering for late joiners
|
|
50
|
+
- Status updates and step progress
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, max_log_buffer: int = 1000):
|
|
54
|
+
"""
|
|
55
|
+
Initialize WebSocket manager
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
max_log_buffer: Maximum number of logs to buffer per deployment
|
|
59
|
+
"""
|
|
60
|
+
self.channels: Dict[str, DeploymentChannel] = {}
|
|
61
|
+
self.max_log_buffer = max_log_buffer
|
|
62
|
+
self._lock = asyncio.Lock()
|
|
63
|
+
|
|
64
|
+
async def connect(self, websocket: WebSocket, deploy_id: str) -> bool:
|
|
65
|
+
"""
|
|
66
|
+
Connect a WebSocket to a deployment channel
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
websocket: The WebSocket connection
|
|
70
|
+
deploy_id: Deployment ID to subscribe to
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if connected successfully
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
await websocket.accept()
|
|
77
|
+
|
|
78
|
+
async with self._lock:
|
|
79
|
+
if deploy_id not in self.channels:
|
|
80
|
+
self.channels[deploy_id] = DeploymentChannel(deploy_id=deploy_id)
|
|
81
|
+
|
|
82
|
+
channel = self.channels[deploy_id]
|
|
83
|
+
channel.connections.add(websocket)
|
|
84
|
+
|
|
85
|
+
# Send buffered logs to new connection
|
|
86
|
+
for log in channel.logs:
|
|
87
|
+
try:
|
|
88
|
+
await websocket.send_json({
|
|
89
|
+
"type": "log",
|
|
90
|
+
"data": log.to_dict()
|
|
91
|
+
})
|
|
92
|
+
except Exception:
|
|
93
|
+
break
|
|
94
|
+
|
|
95
|
+
# Send current status
|
|
96
|
+
await websocket.send_json({
|
|
97
|
+
"type": "status",
|
|
98
|
+
"data": {
|
|
99
|
+
"status": channel.status,
|
|
100
|
+
"step": channel.current_step
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
logger.info(f"WebSocket connected to deployment {deploy_id}")
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error(f"Failed to connect WebSocket: {e}")
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
async def disconnect(self, websocket: WebSocket, deploy_id: str):
|
|
112
|
+
"""
|
|
113
|
+
Disconnect a WebSocket from a deployment channel
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
websocket: The WebSocket connection
|
|
117
|
+
deploy_id: Deployment ID to unsubscribe from
|
|
118
|
+
"""
|
|
119
|
+
async with self._lock:
|
|
120
|
+
if deploy_id in self.channels:
|
|
121
|
+
channel = self.channels[deploy_id]
|
|
122
|
+
channel.connections.discard(websocket)
|
|
123
|
+
|
|
124
|
+
# Clean up empty channels for completed deployments
|
|
125
|
+
if not channel.connections and channel.status in ("completed", "failed", "cancelled"):
|
|
126
|
+
del self.channels[deploy_id]
|
|
127
|
+
logger.info(f"Cleaned up channel for deployment {deploy_id}")
|
|
128
|
+
|
|
129
|
+
logger.info(f"WebSocket disconnected from deployment {deploy_id}")
|
|
130
|
+
|
|
131
|
+
async def broadcast_log(
|
|
132
|
+
self,
|
|
133
|
+
deploy_id: str,
|
|
134
|
+
message: str,
|
|
135
|
+
level: str = "info",
|
|
136
|
+
step: Optional[str] = None
|
|
137
|
+
):
|
|
138
|
+
"""
|
|
139
|
+
Broadcast a log message to all connected clients
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
deploy_id: Deployment ID
|
|
143
|
+
message: Log message
|
|
144
|
+
level: Log level (info, warning, error, success)
|
|
145
|
+
step: Current pipeline step (optional)
|
|
146
|
+
"""
|
|
147
|
+
log = LogMessage(
|
|
148
|
+
timestamp=datetime.now(),
|
|
149
|
+
level=level,
|
|
150
|
+
message=message,
|
|
151
|
+
step=step
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async with self._lock:
|
|
155
|
+
if deploy_id not in self.channels:
|
|
156
|
+
self.channels[deploy_id] = DeploymentChannel(deploy_id=deploy_id)
|
|
157
|
+
|
|
158
|
+
channel = self.channels[deploy_id]
|
|
159
|
+
|
|
160
|
+
# Buffer the log
|
|
161
|
+
channel.logs.append(log)
|
|
162
|
+
if len(channel.logs) > self.max_log_buffer:
|
|
163
|
+
channel.logs = channel.logs[-self.max_log_buffer:]
|
|
164
|
+
|
|
165
|
+
# Broadcast to all connections
|
|
166
|
+
disconnected = set()
|
|
167
|
+
for ws in channel.connections:
|
|
168
|
+
try:
|
|
169
|
+
await ws.send_json({
|
|
170
|
+
"type": "log",
|
|
171
|
+
"data": log.to_dict()
|
|
172
|
+
})
|
|
173
|
+
except Exception:
|
|
174
|
+
disconnected.add(ws)
|
|
175
|
+
|
|
176
|
+
# Remove disconnected clients
|
|
177
|
+
channel.connections -= disconnected
|
|
178
|
+
|
|
179
|
+
async def broadcast_status(
|
|
180
|
+
self,
|
|
181
|
+
deploy_id: str,
|
|
182
|
+
status: str,
|
|
183
|
+
step: Optional[str] = None,
|
|
184
|
+
progress: Optional[int] = None
|
|
185
|
+
):
|
|
186
|
+
"""
|
|
187
|
+
Broadcast a status update to all connected clients
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
deploy_id: Deployment ID
|
|
191
|
+
status: Deployment status (pending, running, completed, failed, cancelled)
|
|
192
|
+
step: Current pipeline step
|
|
193
|
+
progress: Progress percentage (0-100)
|
|
194
|
+
"""
|
|
195
|
+
async with self._lock:
|
|
196
|
+
if deploy_id not in self.channels:
|
|
197
|
+
self.channels[deploy_id] = DeploymentChannel(deploy_id=deploy_id)
|
|
198
|
+
|
|
199
|
+
channel = self.channels[deploy_id]
|
|
200
|
+
channel.status = status
|
|
201
|
+
channel.current_step = step
|
|
202
|
+
|
|
203
|
+
data = {
|
|
204
|
+
"status": status,
|
|
205
|
+
"step": step
|
|
206
|
+
}
|
|
207
|
+
if progress is not None:
|
|
208
|
+
data["progress"] = progress
|
|
209
|
+
|
|
210
|
+
# Broadcast to all connections
|
|
211
|
+
disconnected = set()
|
|
212
|
+
for ws in channel.connections:
|
|
213
|
+
try:
|
|
214
|
+
await ws.send_json({
|
|
215
|
+
"type": "status",
|
|
216
|
+
"data": data
|
|
217
|
+
})
|
|
218
|
+
except Exception:
|
|
219
|
+
disconnected.add(ws)
|
|
220
|
+
|
|
221
|
+
# Remove disconnected clients
|
|
222
|
+
channel.connections -= disconnected
|
|
223
|
+
|
|
224
|
+
async def broadcast_step_complete(
|
|
225
|
+
self,
|
|
226
|
+
deploy_id: str,
|
|
227
|
+
step: str,
|
|
228
|
+
success: bool,
|
|
229
|
+
duration_seconds: float,
|
|
230
|
+
message: Optional[str] = None
|
|
231
|
+
):
|
|
232
|
+
"""
|
|
233
|
+
Broadcast that a pipeline step has completed
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
deploy_id: Deployment ID
|
|
237
|
+
step: Step name that completed
|
|
238
|
+
success: Whether the step succeeded
|
|
239
|
+
duration_seconds: How long the step took
|
|
240
|
+
message: Optional completion message
|
|
241
|
+
"""
|
|
242
|
+
async with self._lock:
|
|
243
|
+
if deploy_id not in self.channels:
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
channel = self.channels[deploy_id]
|
|
247
|
+
|
|
248
|
+
data = {
|
|
249
|
+
"step": step,
|
|
250
|
+
"success": success,
|
|
251
|
+
"duration": duration_seconds,
|
|
252
|
+
"message": message
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
disconnected = set()
|
|
256
|
+
for ws in channel.connections:
|
|
257
|
+
try:
|
|
258
|
+
await ws.send_json({
|
|
259
|
+
"type": "step_complete",
|
|
260
|
+
"data": data
|
|
261
|
+
})
|
|
262
|
+
except Exception:
|
|
263
|
+
disconnected.add(ws)
|
|
264
|
+
|
|
265
|
+
channel.connections -= disconnected
|
|
266
|
+
|
|
267
|
+
async def broadcast_complete(
|
|
268
|
+
self,
|
|
269
|
+
deploy_id: str,
|
|
270
|
+
success: bool,
|
|
271
|
+
message: str,
|
|
272
|
+
version: Optional[str] = None
|
|
273
|
+
):
|
|
274
|
+
"""
|
|
275
|
+
Broadcast that the deployment has completed
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
deploy_id: Deployment ID
|
|
279
|
+
success: Whether the deployment succeeded
|
|
280
|
+
message: Completion message
|
|
281
|
+
version: Deployed version (git commit or docker tag)
|
|
282
|
+
"""
|
|
283
|
+
status = "completed" if success else "failed"
|
|
284
|
+
|
|
285
|
+
async with self._lock:
|
|
286
|
+
if deploy_id not in self.channels:
|
|
287
|
+
self.channels[deploy_id] = DeploymentChannel(deploy_id=deploy_id)
|
|
288
|
+
|
|
289
|
+
channel = self.channels[deploy_id]
|
|
290
|
+
channel.status = status
|
|
291
|
+
|
|
292
|
+
data = {
|
|
293
|
+
"success": success,
|
|
294
|
+
"message": message,
|
|
295
|
+
"version": version
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
disconnected = set()
|
|
299
|
+
for ws in channel.connections:
|
|
300
|
+
try:
|
|
301
|
+
await ws.send_json({
|
|
302
|
+
"type": "complete",
|
|
303
|
+
"data": data
|
|
304
|
+
})
|
|
305
|
+
except Exception:
|
|
306
|
+
disconnected.add(ws)
|
|
307
|
+
|
|
308
|
+
channel.connections -= disconnected
|
|
309
|
+
|
|
310
|
+
def get_channel_info(self, deploy_id: str) -> Optional[Dict]:
|
|
311
|
+
"""
|
|
312
|
+
Get information about a deployment channel
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
deploy_id: Deployment ID
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Channel info dict or None
|
|
319
|
+
"""
|
|
320
|
+
channel = self.channels.get(deploy_id)
|
|
321
|
+
if not channel:
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
"deploy_id": channel.deploy_id,
|
|
326
|
+
"connections": len(channel.connections),
|
|
327
|
+
"log_count": len(channel.logs),
|
|
328
|
+
"status": channel.status,
|
|
329
|
+
"current_step": channel.current_step
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
def get_logs(self, deploy_id: str, limit: int = 100) -> List[Dict]:
|
|
333
|
+
"""
|
|
334
|
+
Get buffered logs for a deployment
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
deploy_id: Deployment ID
|
|
338
|
+
limit: Maximum number of logs to return
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
List of log dicts
|
|
342
|
+
"""
|
|
343
|
+
channel = self.channels.get(deploy_id)
|
|
344
|
+
if not channel:
|
|
345
|
+
return []
|
|
346
|
+
|
|
347
|
+
logs = channel.logs[-limit:] if limit else channel.logs
|
|
348
|
+
return [log.to_dict() for log in logs]
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# Singleton instance
|
|
352
|
+
_ws_manager = None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def get_websocket_manager() -> WebSocketManager:
|
|
356
|
+
"""Get singleton instance of WebSocketManager"""
|
|
357
|
+
global _ws_manager
|
|
358
|
+
if _ws_manager is None:
|
|
359
|
+
_ws_manager = WebSocketManager()
|
|
360
|
+
return _ws_manager
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<q:component name="AdminShell" require_auth="true" require_role="admin" login_url="/admin/login">
|
|
2
|
+
<!--
|
|
3
|
+
Moldura das telas do admin: barra lateral, cabeçalho e mensagens.
|
|
4
|
+
Uso numa tela:
|
|
5
|
+
<q:import component="AdminShell" from="admin/_layout" />
|
|
6
|
+
<AdminShell title="Applications" active="applications" flash="{flash}" flashType="{flashType}">
|
|
7
|
+
...conteúdo da tela...
|
|
8
|
+
</AdminShell>
|
|
9
|
+
O conteúdo é renderizado no escopo da tela (COMP-3). Antes, as 16 telas
|
|
10
|
+
repetiam esta barra lateral inteira, cada uma com a sua cópia.
|
|
11
|
+
-->
|
|
12
|
+
<q:param name="title" type="string" required="true" />
|
|
13
|
+
<q:param name="active" type="string" default="" />
|
|
14
|
+
<q:param name="flash" type="string" default="" />
|
|
15
|
+
<q:param name="flashType" type="string" default="success" />
|
|
16
|
+
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<head>
|
|
19
|
+
<meta charset="UTF-8" />
|
|
20
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
21
|
+
<title>{title} - Quantum Admin</title>
|
|
22
|
+
<link rel="stylesheet" href="/static/quantum-admin.css" />
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div class="qa-layout">
|
|
26
|
+
<aside class="qa-sidebar">
|
|
27
|
+
<div class="qa-sidebar-header">
|
|
28
|
+
<a href="/admin/applications" class="qa-sidebar-logo">
|
|
29
|
+
<div class="qa-sidebar-logo-icon">Q</div>
|
|
30
|
+
<span class="qa-sidebar-logo-text">Quantum<span class="qa-sidebar-version">Admin</span></span>
|
|
31
|
+
</a>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<nav class="qa-sidebar-nav">
|
|
35
|
+
<div class="qa-sidebar-section">
|
|
36
|
+
<div class="qa-sidebar-section-title">Overview</div>
|
|
37
|
+
<a href="/admin/dashboard" class="{'qa-sidebar-link active' if active == 'dashboard' else 'qa-sidebar-link'}">
|
|
38
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
39
|
+
Dashboard
|
|
40
|
+
</a>
|
|
41
|
+
<a href="/admin/applications" class="{'qa-sidebar-link active' if active == 'applications' else 'qa-sidebar-link'}">
|
|
42
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
|
43
|
+
Applications
|
|
44
|
+
</a>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="qa-sidebar-section">
|
|
48
|
+
<div class="qa-sidebar-section-title">Framework</div>
|
|
49
|
+
<a href="/admin/components" class="{'qa-sidebar-link active' if active == 'components' else 'qa-sidebar-link'}">
|
|
50
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
|
|
51
|
+
Components
|
|
52
|
+
</a>
|
|
53
|
+
<a href="/admin/features" class="{'qa-sidebar-link active' if active == 'features' else 'qa-sidebar-link'}">
|
|
54
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
|
55
|
+
Features
|
|
56
|
+
</a>
|
|
57
|
+
<a href="/admin/tests" class="{'qa-sidebar-link active' if active == 'tests' else 'qa-sidebar-link'}">
|
|
58
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
|
59
|
+
Tests
|
|
60
|
+
</a>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="qa-sidebar-section">
|
|
64
|
+
<div class="qa-sidebar-section-title">Runtime</div>
|
|
65
|
+
<a href="/admin/connectors" class="{'qa-sidebar-link active' if active == 'connectors' else 'qa-sidebar-link'}">
|
|
66
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8h1a4 4 0 0 1 0 8h-1"/><path d="M6 8H5a4 4 0 0 0 0 8h1"/><line x1="6" y1="12" x2="18" y2="12"/></svg>
|
|
67
|
+
Connectors
|
|
68
|
+
</a>
|
|
69
|
+
<a href="/admin/database" class="{'qa-sidebar-link active' if active == 'database' else 'qa-sidebar-link'}">
|
|
70
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
|
|
71
|
+
Database
|
|
72
|
+
</a>
|
|
73
|
+
<a href="/admin/agents" class="{'qa-sidebar-link active' if active == 'agents' else 'qa-sidebar-link'}">
|
|
74
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
|
75
|
+
Agents
|
|
76
|
+
</a>
|
|
77
|
+
<a href="/admin/jobs" class="{'qa-sidebar-link active' if active == 'jobs' else 'qa-sidebar-link'}">
|
|
78
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
79
|
+
Jobs
|
|
80
|
+
</a>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<div class="qa-sidebar-section">
|
|
84
|
+
<div class="qa-sidebar-section-title">System</div>
|
|
85
|
+
<a href="/admin/settings" class="{'qa-sidebar-link active' if active == 'settings' else 'qa-sidebar-link'}">
|
|
86
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
87
|
+
Settings
|
|
88
|
+
</a>
|
|
89
|
+
</div>
|
|
90
|
+
</nav>
|
|
91
|
+
|
|
92
|
+
<div class="qa-sidebar-footer">
|
|
93
|
+
<div class="qa-sidebar-user">
|
|
94
|
+
<div class="qa-sidebar-user-avatar">{upper(first(session.userName)) if session.userName else 'Q'}</div>
|
|
95
|
+
<div class="qa-sidebar-user-info">
|
|
96
|
+
<div class="qa-sidebar-user-name">{session.userName}</div>
|
|
97
|
+
<form method="POST" action="/admin/logout" style="margin: 0;">
|
|
98
|
+
<button type="submit" class="qa-btn qa-btn-ghost qa-btn-sm">Sign out</button>
|
|
99
|
+
</form>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</aside>
|
|
104
|
+
|
|
105
|
+
<main class="qa-main">
|
|
106
|
+
<header class="qa-header">
|
|
107
|
+
<h1 class="qa-header-title">{title}</h1>
|
|
108
|
+
</header>
|
|
109
|
+
<div class="qa-content">
|
|
110
|
+
<q:if condition="flash">
|
|
111
|
+
<div class="{'qa-flash qa-flash-error' if flashType == 'error' else 'qa-flash qa-flash-success'}" role="status">
|
|
112
|
+
<span class="qa-text-sm qa-font-medium">{flash}</span>
|
|
113
|
+
</div>
|
|
114
|
+
</q:if>
|
|
115
|
+
<q:slot />
|
|
116
|
+
</div>
|
|
117
|
+
</main>
|
|
118
|
+
</div>
|
|
119
|
+
</body>
|
|
120
|
+
</html>
|
|
121
|
+
</q:component>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<q:component name="AdminAgents" require_auth="true" require_role="admin" login_url="/admin/login">
|
|
2
|
+
<!--
|
|
3
|
+
q:agent e q:team declarados em components/, examples/ e projects/
|
|
4
|
+
(admin.agents.list). A contagem de providers é a dos que as declarações
|
|
5
|
+
usam — antes somava ollama/openai/anthropic mesmo sem nenhum agente.
|
|
6
|
+
-->
|
|
7
|
+
<q:import component="AdminShell" from="admin/_layout" />
|
|
8
|
+
|
|
9
|
+
<q:invoke name="ia" service="admin.agents.list" />
|
|
10
|
+
|
|
11
|
+
<AdminShell title="Agents" active="agents" flash="{flash}" flashType="{flashType}">
|
|
12
|
+
|
|
13
|
+
<div class="qa-grid qa-grid-4 qa-mb-6">
|
|
14
|
+
<div class="qa-stat-card">
|
|
15
|
+
<div class="qa-stat-label">Agent Definitions</div>
|
|
16
|
+
<div class="qa-stat-value">{len(ia.agents)}</div>
|
|
17
|
+
<div class="qa-stat-change"><span class="qa-text-muted">q:agent tags found</span></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="qa-stat-card">
|
|
20
|
+
<div class="qa-stat-label">Team Definitions</div>
|
|
21
|
+
<div class="qa-stat-value">{len(ia.teams)}</div>
|
|
22
|
+
<div class="qa-stat-change"><span class="qa-text-muted">q:team tags found</span></div>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="qa-stat-card">
|
|
25
|
+
<div class="qa-stat-label">Source Files</div>
|
|
26
|
+
<div class="qa-stat-value">{len(ia.sources)}</div>
|
|
27
|
+
<div class="qa-stat-change"><span class="qa-text-muted">files with agents or teams</span></div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="qa-stat-card">
|
|
30
|
+
<div class="qa-stat-label">Providers</div>
|
|
31
|
+
<div class="qa-stat-value">{len(ia.providers)}</div>
|
|
32
|
+
<div class="qa-stat-change"><span class="qa-text-muted">{join(ia.providers, ', ') if ia.providers else 'none declared'}</span></div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<q:if condition="len(ia.agents) > 0">
|
|
37
|
+
<div class="qa-card qa-mb-4">
|
|
38
|
+
<div class="qa-card-header">
|
|
39
|
+
<div class="qa-card-title">Agent Definitions</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="qa-card-body qa-p-0" style="overflow-x: auto;">
|
|
42
|
+
<table class="qa-table">
|
|
43
|
+
<thead><tr><th>Name</th><th>Model</th><th>Provider</th><th>Source File</th></tr></thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
<q:loop type="array" var="agente" items="{ia.agents}">
|
|
46
|
+
<tr>
|
|
47
|
+
<td><span class="qa-font-mono">{agente.name or '-'}</span></td>
|
|
48
|
+
<td><span class="qa-badge qa-badge-info">{agente.model or '-'}</span></td>
|
|
49
|
+
<td><span class="qa-badge qa-badge-primary">{agente.provider or '-'}</span></td>
|
|
50
|
+
<td><a href="/admin/source?file={agente.source}" class="qa-font-mono qa-text-sm">{agente.source}</a></td>
|
|
51
|
+
</tr>
|
|
52
|
+
</q:loop>
|
|
53
|
+
</tbody>
|
|
54
|
+
</table>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</q:if>
|
|
58
|
+
|
|
59
|
+
<q:if condition="len(ia.teams) > 0">
|
|
60
|
+
<div class="qa-card qa-mb-4">
|
|
61
|
+
<div class="qa-card-header">
|
|
62
|
+
<div class="qa-card-title">Team Definitions</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="qa-card-body qa-p-0" style="overflow-x: auto;">
|
|
65
|
+
<table class="qa-table">
|
|
66
|
+
<thead><tr><th>Team</th><th>Supervisor</th><th>Source File</th></tr></thead>
|
|
67
|
+
<tbody>
|
|
68
|
+
<q:loop type="array" var="time" items="{ia.teams}">
|
|
69
|
+
<tr>
|
|
70
|
+
<td><span class="qa-font-mono">{time.name or '-'}</span></td>
|
|
71
|
+
<td><span class="qa-badge qa-badge-warning">{time.supervisor or '-'}</span></td>
|
|
72
|
+
<td><a href="/admin/source?file={time.source}" class="qa-font-mono qa-text-sm">{time.source}</a></td>
|
|
73
|
+
</tr>
|
|
74
|
+
</q:loop>
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</q:if>
|
|
80
|
+
|
|
81
|
+
<q:if condition="ia.llm">
|
|
82
|
+
<div class="qa-card qa-mb-4">
|
|
83
|
+
<div class="qa-card-header">
|
|
84
|
+
<div>
|
|
85
|
+
<div class="qa-card-title">LLM Configuration</div>
|
|
86
|
+
<div class="qa-card-subtitle">llm section of quantum.config.yaml</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="qa-card-body">
|
|
90
|
+
<div class="qa-flex qa-items-center qa-justify-between"><span class="qa-text-sm qa-text-muted">Base URL</span><span class="qa-font-mono">{ia.llm.base_url or '-'}</span></div>
|
|
91
|
+
<div class="qa-flex qa-items-center qa-justify-between"><span class="qa-text-sm qa-text-muted">Default Model</span><span class="qa-font-mono">{ia.llm.default_model or '-'}</span></div>
|
|
92
|
+
<div class="qa-flex qa-items-center qa-justify-between"><span class="qa-text-sm qa-text-muted">Timeout</span><span class="qa-font-mono">{ia.llm.timeout}s</span></div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</q:if>
|
|
96
|
+
|
|
97
|
+
<q:if condition="len(ia.agents) == 0 and len(ia.teams) == 0">
|
|
98
|
+
<div class="qa-card">
|
|
99
|
+
<div class="qa-empty">
|
|
100
|
+
<div class="qa-empty-title">No Agents Found</div>
|
|
101
|
+
<div class="qa-empty-text">No q:agent or q:team was found in components/, examples/ or projects/.</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</q:if>
|
|
105
|
+
|
|
106
|
+
</AdminShell>
|
|
107
|
+
</q:component>
|