kailash 0.3.0__py3-none-any.whl → 0.3.1__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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +40 -39
- kailash/api/auth.py +26 -32
- kailash/api/custom_nodes.py +29 -29
- kailash/api/custom_nodes_secure.py +35 -35
- kailash/api/database.py +17 -17
- kailash/api/gateway.py +19 -19
- kailash/api/mcp_integration.py +24 -23
- kailash/api/studio.py +45 -45
- kailash/api/workflow_api.py +8 -8
- kailash/cli/commands.py +5 -8
- kailash/manifest.py +42 -42
- kailash/mcp/__init__.py +1 -1
- kailash/mcp/ai_registry_server.py +20 -20
- kailash/mcp/client.py +9 -11
- kailash/mcp/client_new.py +10 -10
- kailash/mcp/server.py +1 -2
- kailash/mcp/server_enhanced.py +449 -0
- kailash/mcp/servers/ai_registry.py +6 -6
- kailash/mcp/utils/__init__.py +31 -0
- kailash/mcp/utils/cache.py +267 -0
- kailash/mcp/utils/config.py +263 -0
- kailash/mcp/utils/formatters.py +293 -0
- kailash/mcp/utils/metrics.py +418 -0
- kailash/nodes/ai/agents.py +9 -9
- kailash/nodes/ai/ai_providers.py +33 -34
- kailash/nodes/ai/embedding_generator.py +31 -32
- kailash/nodes/ai/intelligent_agent_orchestrator.py +62 -66
- kailash/nodes/ai/iterative_llm_agent.py +48 -48
- kailash/nodes/ai/llm_agent.py +32 -33
- kailash/nodes/ai/models.py +13 -13
- kailash/nodes/ai/self_organizing.py +44 -44
- kailash/nodes/api/auth.py +11 -11
- kailash/nodes/api/graphql.py +13 -13
- kailash/nodes/api/http.py +19 -19
- kailash/nodes/api/monitoring.py +20 -20
- kailash/nodes/api/rate_limiting.py +9 -13
- kailash/nodes/api/rest.py +29 -29
- kailash/nodes/api/security.py +44 -47
- kailash/nodes/base.py +21 -23
- kailash/nodes/base_async.py +7 -7
- kailash/nodes/base_cycle_aware.py +12 -12
- kailash/nodes/base_with_acl.py +5 -5
- kailash/nodes/code/python.py +56 -55
- kailash/nodes/data/directory.py +6 -6
- kailash/nodes/data/event_generation.py +10 -10
- kailash/nodes/data/file_discovery.py +28 -31
- kailash/nodes/data/readers.py +8 -8
- kailash/nodes/data/retrieval.py +10 -10
- kailash/nodes/data/sharepoint_graph.py +17 -17
- kailash/nodes/data/sources.py +5 -5
- kailash/nodes/data/sql.py +13 -13
- kailash/nodes/data/streaming.py +25 -25
- kailash/nodes/data/vector_db.py +22 -22
- kailash/nodes/data/writers.py +7 -7
- kailash/nodes/logic/async_operations.py +17 -17
- kailash/nodes/logic/convergence.py +11 -11
- kailash/nodes/logic/loop.py +4 -4
- kailash/nodes/logic/operations.py +11 -11
- kailash/nodes/logic/workflow.py +8 -9
- kailash/nodes/mixins/mcp.py +17 -17
- kailash/nodes/mixins.py +8 -10
- kailash/nodes/transform/chunkers.py +3 -3
- kailash/nodes/transform/formatters.py +7 -7
- kailash/nodes/transform/processors.py +10 -10
- kailash/runtime/access_controlled.py +18 -18
- kailash/runtime/async_local.py +17 -19
- kailash/runtime/docker.py +20 -22
- kailash/runtime/local.py +16 -16
- kailash/runtime/parallel.py +23 -23
- kailash/runtime/parallel_cyclic.py +27 -27
- kailash/runtime/runner.py +6 -6
- kailash/runtime/testing.py +20 -20
- kailash/sdk_exceptions.py +0 -58
- kailash/security.py +14 -26
- kailash/tracking/manager.py +38 -38
- kailash/tracking/metrics_collector.py +15 -14
- kailash/tracking/models.py +53 -53
- kailash/tracking/storage/base.py +7 -17
- kailash/tracking/storage/database.py +22 -23
- kailash/tracking/storage/filesystem.py +38 -40
- kailash/utils/export.py +21 -21
- kailash/utils/templates.py +2 -3
- kailash/visualization/api.py +30 -34
- kailash/visualization/dashboard.py +17 -17
- kailash/visualization/performance.py +16 -16
- kailash/visualization/reports.py +25 -27
- kailash/workflow/builder.py +8 -8
- kailash/workflow/convergence.py +13 -12
- kailash/workflow/cycle_analyzer.py +30 -32
- kailash/workflow/cycle_builder.py +12 -12
- kailash/workflow/cycle_config.py +16 -15
- kailash/workflow/cycle_debugger.py +40 -40
- kailash/workflow/cycle_exceptions.py +29 -29
- kailash/workflow/cycle_profiler.py +21 -21
- kailash/workflow/cycle_state.py +20 -22
- kailash/workflow/cyclic_runner.py +44 -44
- kailash/workflow/graph.py +40 -40
- kailash/workflow/mermaid_visualizer.py +9 -11
- kailash/workflow/migration.py +22 -22
- kailash/workflow/mock_registry.py +6 -6
- kailash/workflow/runner.py +9 -9
- kailash/workflow/safety.py +12 -13
- kailash/workflow/state.py +8 -11
- kailash/workflow/templates.py +19 -19
- kailash/workflow/validation.py +14 -14
- kailash/workflow/visualization.py +22 -22
- {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/METADATA +53 -5
- kailash-0.3.1.dist-info/RECORD +136 -0
- kailash-0.3.0.dist-info/RECORD +0 -130
- {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/WHEEL +0 -0
- {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/entry_points.txt +0 -0
- {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.3.0.dist-info → kailash-0.3.1.dist-info}/top_level.txt +0 -0
kailash/visualization/api.py
CHANGED
@@ -27,7 +27,7 @@ import json
|
|
27
27
|
import logging
|
28
28
|
from datetime import datetime
|
29
29
|
from pathlib import Path
|
30
|
-
from typing import Any,
|
30
|
+
from typing import Any, Optional
|
31
31
|
|
32
32
|
try:
|
33
33
|
from fastapi import (
|
@@ -59,8 +59,8 @@ if FASTAPI_AVAILABLE:
|
|
59
59
|
class RunRequest(BaseModel):
|
60
60
|
"""Request model for starting monitoring."""
|
61
61
|
|
62
|
-
run_id:
|
63
|
-
config:
|
62
|
+
run_id: str | None = None
|
63
|
+
config: dict[str, Any] | None = None
|
64
64
|
|
65
65
|
class MetricsResponse(BaseModel):
|
66
66
|
"""Response model for metrics data."""
|
@@ -80,12 +80,12 @@ if FASTAPI_AVAILABLE:
|
|
80
80
|
node_id: str
|
81
81
|
node_type: str
|
82
82
|
status: str
|
83
|
-
started_at:
|
84
|
-
ended_at:
|
85
|
-
duration:
|
86
|
-
cpu_usage:
|
87
|
-
memory_usage_mb:
|
88
|
-
error_message:
|
83
|
+
started_at: datetime | None
|
84
|
+
ended_at: datetime | None
|
85
|
+
duration: float | None
|
86
|
+
cpu_usage: float | None
|
87
|
+
memory_usage_mb: float | None
|
88
|
+
error_message: str | None
|
89
89
|
|
90
90
|
class RunResponse(BaseModel):
|
91
91
|
"""Response model for run information."""
|
@@ -93,8 +93,8 @@ if FASTAPI_AVAILABLE:
|
|
93
93
|
run_id: str
|
94
94
|
workflow_name: str
|
95
95
|
status: str
|
96
|
-
started_at:
|
97
|
-
ended_at:
|
96
|
+
started_at: datetime | None
|
97
|
+
ended_at: datetime | None
|
98
98
|
total_tasks: int
|
99
99
|
completed_tasks: int
|
100
100
|
failed_tasks: int
|
@@ -105,7 +105,7 @@ if FASTAPI_AVAILABLE:
|
|
105
105
|
run_id: str
|
106
106
|
format: str = "html"
|
107
107
|
include_charts: bool = True
|
108
|
-
compare_runs:
|
108
|
+
compare_runs: list[str] | None = None
|
109
109
|
detail_level: str = "detailed"
|
110
110
|
|
111
111
|
|
@@ -123,7 +123,7 @@ class DashboardAPIServer:
|
|
123
123
|
def __init__(
|
124
124
|
self,
|
125
125
|
task_manager: TaskManager,
|
126
|
-
dashboard_config:
|
126
|
+
dashboard_config: DashboardConfig | None = None,
|
127
127
|
):
|
128
128
|
"""Initialize API server.
|
129
129
|
|
@@ -145,8 +145,8 @@ class DashboardAPIServer:
|
|
145
145
|
self.reporter = WorkflowPerformanceReporter(task_manager)
|
146
146
|
|
147
147
|
# WebSocket connections for real-time updates
|
148
|
-
self._websocket_connections:
|
149
|
-
self._broadcast_task:
|
148
|
+
self._websocket_connections: list[WebSocket] = []
|
149
|
+
self._broadcast_task: asyncio.Task | None = None
|
150
150
|
|
151
151
|
# Create FastAPI app
|
152
152
|
self.app = FastAPI(
|
@@ -177,7 +177,7 @@ class DashboardAPIServer:
|
|
177
177
|
"""Health check endpoint."""
|
178
178
|
return {"status": "healthy", "timestamp": datetime.now()}
|
179
179
|
|
180
|
-
@self.app.get("/api/v1/runs", response_model=
|
180
|
+
@self.app.get("/api/v1/runs", response_model=list[RunResponse])
|
181
181
|
async def list_runs(limit: int = 10, offset: int = 0):
|
182
182
|
"""Get list of workflow runs."""
|
183
183
|
try:
|
@@ -243,7 +243,7 @@ class DashboardAPIServer:
|
|
243
243
|
self.logger.error(f"Failed to get run {run_id}: {e}")
|
244
244
|
raise HTTPException(status_code=500, detail=str(e))
|
245
245
|
|
246
|
-
@self.app.get("/api/v1/runs/{run_id}/tasks", response_model=
|
246
|
+
@self.app.get("/api/v1/runs/{run_id}/tasks", response_model=list[TaskResponse])
|
247
247
|
async def get_run_tasks(run_id: str):
|
248
248
|
"""Get tasks for a specific run."""
|
249
249
|
try:
|
@@ -352,7 +352,7 @@ class DashboardAPIServer:
|
|
352
352
|
self.logger.error(f"Failed to get current metrics: {e}")
|
353
353
|
raise HTTPException(status_code=500, detail=str(e))
|
354
354
|
|
355
|
-
@self.app.get("/api/v1/metrics/history", response_model=
|
355
|
+
@self.app.get("/api/v1/metrics/history", response_model=list[MetricsResponse])
|
356
356
|
async def get_metrics_history(minutes: int = 30):
|
357
357
|
"""Get metrics history for specified time period."""
|
358
358
|
try:
|
@@ -475,7 +475,7 @@ class DashboardAPIServer:
|
|
475
475
|
run_id: str,
|
476
476
|
output_path: Path,
|
477
477
|
report_format: ReportFormat,
|
478
|
-
compare_runs:
|
478
|
+
compare_runs: list[str] | None = None,
|
479
479
|
):
|
480
480
|
"""Generate report in background task."""
|
481
481
|
try:
|
@@ -564,7 +564,7 @@ class SimpleDashboardAPI:
|
|
564
564
|
def __init__(
|
565
565
|
self,
|
566
566
|
task_manager: TaskManager,
|
567
|
-
dashboard_config:
|
567
|
+
dashboard_config: DashboardConfig | None = None,
|
568
568
|
):
|
569
569
|
"""Initialize simple API interface.
|
570
570
|
|
@@ -578,7 +578,7 @@ class SimpleDashboardAPI:
|
|
578
578
|
self.reporter = WorkflowPerformanceReporter(task_manager)
|
579
579
|
self.logger = logger
|
580
580
|
|
581
|
-
def get_runs(self, limit: int = 10, offset: int = 0) ->
|
581
|
+
def get_runs(self, limit: int = 10, offset: int = 0) -> list[dict[str, Any]]:
|
582
582
|
"""Get list of workflow runs."""
|
583
583
|
all_runs = self.task_manager.list_runs()
|
584
584
|
runs = all_runs[offset : offset + limit]
|
@@ -604,7 +604,7 @@ class SimpleDashboardAPI:
|
|
604
604
|
|
605
605
|
return result
|
606
606
|
|
607
|
-
def get_run_details(self, run_id: str) ->
|
607
|
+
def get_run_details(self, run_id: str) -> dict[str, Any] | None:
|
608
608
|
"""Get details for a specific run."""
|
609
609
|
run = self.task_manager.get_run(run_id)
|
610
610
|
if not run:
|
@@ -641,17 +641,17 @@ class SimpleDashboardAPI:
|
|
641
641
|
],
|
642
642
|
}
|
643
643
|
|
644
|
-
def start_monitoring(self, run_id:
|
644
|
+
def start_monitoring(self, run_id: str | None = None) -> dict[str, Any]:
|
645
645
|
"""Start real-time monitoring."""
|
646
646
|
self.dashboard.start_monitoring(run_id)
|
647
647
|
return {"status": "started", "run_id": run_id}
|
648
648
|
|
649
|
-
def stop_monitoring(self) ->
|
649
|
+
def stop_monitoring(self) -> dict[str, Any]:
|
650
650
|
"""Stop real-time monitoring."""
|
651
651
|
self.dashboard.stop_monitoring()
|
652
652
|
return {"status": "stopped"}
|
653
653
|
|
654
|
-
def get_current_metrics(self) ->
|
654
|
+
def get_current_metrics(self) -> dict[str, Any] | None:
|
655
655
|
"""Get current live metrics."""
|
656
656
|
metrics = self.dashboard.get_current_metrics()
|
657
657
|
if not metrics:
|
@@ -668,7 +668,7 @@ class SimpleDashboardAPI:
|
|
668
668
|
"avg_task_duration": metrics.avg_task_duration,
|
669
669
|
}
|
670
670
|
|
671
|
-
def get_metrics_history(self, minutes: int = 30) ->
|
671
|
+
def get_metrics_history(self, minutes: int = 30) -> list[dict[str, Any]]:
|
672
672
|
"""Get metrics history."""
|
673
673
|
history = self.dashboard.get_metrics_history(minutes=minutes)
|
674
674
|
|
@@ -690,8 +690,8 @@ class SimpleDashboardAPI:
|
|
690
690
|
self,
|
691
691
|
run_id: str,
|
692
692
|
format: str = "html",
|
693
|
-
output_path:
|
694
|
-
compare_runs:
|
693
|
+
output_path: str | Path | None = None,
|
694
|
+
compare_runs: list[str] | None = None,
|
695
695
|
) -> Path:
|
696
696
|
"""Generate performance report."""
|
697
697
|
try:
|
@@ -708,9 +708,7 @@ class SimpleDashboardAPI:
|
|
708
708
|
compare_runs=compare_runs,
|
709
709
|
)
|
710
710
|
|
711
|
-
def generate_dashboard(
|
712
|
-
self, output_path: Optional[Union[str, Path]] = None
|
713
|
-
) -> Path:
|
711
|
+
def generate_dashboard(self, output_path: str | Path | None = None) -> Path:
|
714
712
|
"""Generate live dashboard HTML."""
|
715
713
|
if output_path is None:
|
716
714
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
@@ -718,9 +716,7 @@ class SimpleDashboardAPI:
|
|
718
716
|
|
719
717
|
return self.dashboard.generate_live_report(output_path, include_charts=True)
|
720
718
|
|
721
|
-
def export_metrics_json(
|
722
|
-
self, output_path: Optional[Union[str, Path]] = None
|
723
|
-
) -> Path:
|
719
|
+
def export_metrics_json(self, output_path: str | Path | None = None) -> Path:
|
724
720
|
"""Export current metrics as JSON."""
|
725
721
|
if output_path is None:
|
726
722
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
@@ -27,7 +27,7 @@ import time
|
|
27
27
|
from dataclasses import dataclass, field
|
28
28
|
from datetime import datetime, timedelta
|
29
29
|
from pathlib import Path
|
30
|
-
from typing import Any
|
30
|
+
from typing import Any
|
31
31
|
|
32
32
|
import numpy as np
|
33
33
|
|
@@ -100,7 +100,7 @@ class RealTimeDashboard:
|
|
100
100
|
"""
|
101
101
|
|
102
102
|
def __init__(
|
103
|
-
self, task_manager: TaskManager, config:
|
103
|
+
self, task_manager: TaskManager, config: DashboardConfig | None = None
|
104
104
|
):
|
105
105
|
"""Initialize real-time dashboard.
|
106
106
|
|
@@ -114,17 +114,17 @@ class RealTimeDashboard:
|
|
114
114
|
|
115
115
|
# Live monitoring state
|
116
116
|
self._monitoring = False
|
117
|
-
self._monitor_thread:
|
118
|
-
self._metrics_history:
|
119
|
-
self._current_run_id:
|
117
|
+
self._monitor_thread: threading.Thread | None = None
|
118
|
+
self._metrics_history: list[LiveMetrics] = []
|
119
|
+
self._current_run_id: str | None = None
|
120
120
|
|
121
121
|
# Event callbacks
|
122
|
-
self._status_callbacks:
|
123
|
-
self._metrics_callbacks:
|
122
|
+
self._status_callbacks: list[callable] = []
|
123
|
+
self._metrics_callbacks: list[callable] = []
|
124
124
|
|
125
125
|
self.logger = logger
|
126
126
|
|
127
|
-
def start_monitoring(self, run_id:
|
127
|
+
def start_monitoring(self, run_id: str | None = None):
|
128
128
|
"""Start real-time monitoring for a workflow run.
|
129
129
|
|
130
130
|
Args:
|
@@ -297,11 +297,11 @@ class RealTimeDashboard:
|
|
297
297
|
"""
|
298
298
|
self._status_callbacks.append(callback)
|
299
299
|
|
300
|
-
def get_current_metrics(self) ->
|
300
|
+
def get_current_metrics(self) -> LiveMetrics | None:
|
301
301
|
"""Get the most recent metrics."""
|
302
302
|
return self._metrics_history[-1] if self._metrics_history else None
|
303
303
|
|
304
|
-
def get_metrics_history(self, minutes:
|
304
|
+
def get_metrics_history(self, minutes: int | None = None) -> list[LiveMetrics]:
|
305
305
|
"""Get metrics history for specified time period.
|
306
306
|
|
307
307
|
Args:
|
@@ -317,7 +317,7 @@ class RealTimeDashboard:
|
|
317
317
|
return [m for m in self._metrics_history if m.timestamp >= cutoff]
|
318
318
|
|
319
319
|
def generate_live_report(
|
320
|
-
self, output_path:
|
320
|
+
self, output_path: str | Path, include_charts: bool = True
|
321
321
|
) -> Path:
|
322
322
|
"""Generate comprehensive live dashboard report.
|
323
323
|
|
@@ -396,7 +396,7 @@ class RealTimeDashboard:
|
|
396
396
|
|
397
397
|
return html_template
|
398
398
|
|
399
|
-
def _generate_status_section(self, metrics:
|
399
|
+
def _generate_status_section(self, metrics: LiveMetrics | None) -> str:
|
400
400
|
"""Generate status overview section."""
|
401
401
|
if not metrics:
|
402
402
|
return """
|
@@ -442,7 +442,7 @@ class RealTimeDashboard:
|
|
442
442
|
</section>
|
443
443
|
"""
|
444
444
|
|
445
|
-
def _generate_live_metrics_section(self, history:
|
445
|
+
def _generate_live_metrics_section(self, history: list[LiveMetrics]) -> str:
|
446
446
|
"""Generate live metrics charts section."""
|
447
447
|
if not history:
|
448
448
|
return """
|
@@ -860,7 +860,7 @@ class DashboardExporter:
|
|
860
860
|
self.dashboard = dashboard
|
861
861
|
self.logger = logger
|
862
862
|
|
863
|
-
def export_metrics_json(self, output_path:
|
863
|
+
def export_metrics_json(self, output_path: str | Path) -> Path:
|
864
864
|
"""Export current metrics as JSON.
|
865
865
|
|
866
866
|
Args:
|
@@ -895,7 +895,7 @@ class DashboardExporter:
|
|
895
895
|
self.logger.info(f"Exported metrics to: {output_path}")
|
896
896
|
return output_path
|
897
897
|
|
898
|
-
def _metrics_to_dict(self, metrics: LiveMetrics) ->
|
898
|
+
def _metrics_to_dict(self, metrics: LiveMetrics) -> dict[str, Any]:
|
899
899
|
"""Convert LiveMetrics to dictionary."""
|
900
900
|
return {
|
901
901
|
"timestamp": metrics.timestamp.isoformat(),
|
@@ -909,8 +909,8 @@ class DashboardExporter:
|
|
909
909
|
}
|
910
910
|
|
911
911
|
def create_dashboard_snapshot(
|
912
|
-
self, output_dir:
|
913
|
-
) ->
|
912
|
+
self, output_dir: str | Path, include_static_charts: bool = True
|
913
|
+
) -> dict[str, Path]:
|
914
914
|
"""Create complete dashboard snapshot with all assets.
|
915
915
|
|
916
916
|
Args:
|
@@ -23,7 +23,7 @@ Downstream Consumers:
|
|
23
23
|
|
24
24
|
import logging
|
25
25
|
from pathlib import Path
|
26
|
-
from typing import Any
|
26
|
+
from typing import Any
|
27
27
|
|
28
28
|
import matplotlib.pyplot as plt
|
29
29
|
import numpy as np
|
@@ -51,8 +51,8 @@ class PerformanceVisualizer:
|
|
51
51
|
self.logger = logger
|
52
52
|
|
53
53
|
def create_run_performance_summary(
|
54
|
-
self, run_id: str, output_dir:
|
55
|
-
) ->
|
54
|
+
self, run_id: str, output_dir: Path | None = None
|
55
|
+
) -> dict[str, Path]:
|
56
56
|
"""Create comprehensive performance summary for a workflow run.
|
57
57
|
|
58
58
|
Args:
|
@@ -112,7 +112,7 @@ class PerformanceVisualizer:
|
|
112
112
|
return outputs
|
113
113
|
|
114
114
|
def _create_execution_timeline(
|
115
|
-
self, tasks:
|
115
|
+
self, tasks: list[TaskRun], output_path: Path
|
116
116
|
) -> Path:
|
117
117
|
"""Create Gantt-style execution timeline."""
|
118
118
|
fig, ax = plt.subplots(figsize=(12, max(6, len(tasks) * 0.5)))
|
@@ -210,7 +210,7 @@ class PerformanceVisualizer:
|
|
210
210
|
return output_path
|
211
211
|
|
212
212
|
def _create_resource_usage_chart(
|
213
|
-
self, tasks:
|
213
|
+
self, tasks: list[TaskRun], output_path: Path
|
214
214
|
) -> Path:
|
215
215
|
"""Create resource usage comparison chart."""
|
216
216
|
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 12))
|
@@ -258,7 +258,7 @@ class PerformanceVisualizer:
|
|
258
258
|
ax1.grid(True, axis="y", alpha=0.3)
|
259
259
|
|
260
260
|
# Add value labels
|
261
|
-
for bar, value in zip(bars1, cpu_usage):
|
261
|
+
for bar, value in zip(bars1, cpu_usage, strict=False):
|
262
262
|
if value > 0:
|
263
263
|
ax1.text(
|
264
264
|
bar.get_x() + bar.get_width() / 2,
|
@@ -295,7 +295,7 @@ class PerformanceVisualizer:
|
|
295
295
|
ax3.grid(True, axis="y", alpha=0.3)
|
296
296
|
|
297
297
|
# Add value labels
|
298
|
-
for bar, value in zip(bars3, durations):
|
298
|
+
for bar, value in zip(bars3, durations, strict=False):
|
299
299
|
if value > 0:
|
300
300
|
ax3.text(
|
301
301
|
bar.get_x() + bar.get_width() / 2,
|
@@ -314,7 +314,7 @@ class PerformanceVisualizer:
|
|
314
314
|
return output_path
|
315
315
|
|
316
316
|
def _create_node_performance_comparison(
|
317
|
-
self, tasks:
|
317
|
+
self, tasks: list[TaskRun], output_path: Path
|
318
318
|
) -> Path:
|
319
319
|
"""Create performance comparison radar chart."""
|
320
320
|
# Group tasks by node type
|
@@ -385,7 +385,9 @@ class PerformanceVisualizer:
|
|
385
385
|
|
386
386
|
colors = plt.cm.tab10(np.linspace(0, 1, len(avg_metrics)))
|
387
387
|
|
388
|
-
for (node_type, metrics), color in zip(
|
388
|
+
for (node_type, metrics), color in zip(
|
389
|
+
avg_metrics.items(), colors, strict=False
|
390
|
+
):
|
389
391
|
values = list(metrics.values())
|
390
392
|
|
391
393
|
# Normalize values to 0-100 scale for better visualization
|
@@ -404,7 +406,7 @@ class PerformanceVisualizer:
|
|
404
406
|
}
|
405
407
|
|
406
408
|
normalized_values = []
|
407
|
-
for cat, val in zip(categories, values):
|
409
|
+
for cat, val in zip(categories, values, strict=False):
|
408
410
|
normalized_values.append((val / max_vals[cat]) * 100)
|
409
411
|
|
410
412
|
normalized_values += normalized_values[:1]
|
@@ -435,7 +437,7 @@ class PerformanceVisualizer:
|
|
435
437
|
|
436
438
|
return output_path
|
437
439
|
|
438
|
-
def _create_io_analysis(self, tasks:
|
440
|
+
def _create_io_analysis(self, tasks: list[TaskRun], output_path: Path) -> Path:
|
439
441
|
"""Create I/O operations analysis chart."""
|
440
442
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
|
441
443
|
|
@@ -543,7 +545,7 @@ class PerformanceVisualizer:
|
|
543
545
|
return output_path
|
544
546
|
|
545
547
|
def _create_performance_heatmap(
|
546
|
-
self, tasks:
|
548
|
+
self, tasks: list[TaskRun], output_path: Path
|
547
549
|
) -> Path:
|
548
550
|
"""Create performance metrics heatmap."""
|
549
551
|
# Prepare data matrix
|
@@ -636,7 +638,7 @@ class PerformanceVisualizer:
|
|
636
638
|
return output_path
|
637
639
|
|
638
640
|
def _create_performance_report(
|
639
|
-
self, run: Any, tasks:
|
641
|
+
self, run: Any, tasks: list[TaskRun], output_path: Path
|
640
642
|
) -> Path:
|
641
643
|
"""Create markdown performance report."""
|
642
644
|
lines = []
|
@@ -718,9 +720,7 @@ class PerformanceVisualizer:
|
|
718
720
|
|
719
721
|
return output_path
|
720
722
|
|
721
|
-
def compare_runs(
|
722
|
-
self, run_ids: List[str], output_path: Optional[Path] = None
|
723
|
-
) -> Path:
|
723
|
+
def compare_runs(self, run_ids: list[str], output_path: Path | None = None) -> Path:
|
724
724
|
"""Compare performance across multiple runs."""
|
725
725
|
if output_path is None:
|
726
726
|
# Use centralized output directory
|
kailash/visualization/reports.py
CHANGED
@@ -27,7 +27,7 @@ from dataclasses import dataclass, field
|
|
27
27
|
from datetime import datetime
|
28
28
|
from enum import Enum
|
29
29
|
from pathlib import Path
|
30
|
-
from typing import Any
|
30
|
+
from typing import Any
|
31
31
|
|
32
32
|
import numpy as np
|
33
33
|
|
@@ -86,7 +86,7 @@ class PerformanceInsight:
|
|
86
86
|
title: str
|
87
87
|
description: str
|
88
88
|
recommendation: str
|
89
|
-
metrics:
|
89
|
+
metrics: dict[str, Any] = field(default_factory=dict)
|
90
90
|
|
91
91
|
|
92
92
|
@dataclass
|
@@ -134,9 +134,7 @@ class WorkflowPerformanceReporter:
|
|
134
134
|
report = reporter.generate_report(run_id, output_path="report.html")
|
135
135
|
"""
|
136
136
|
|
137
|
-
def __init__(
|
138
|
-
self, task_manager: TaskManager, config: Optional[ReportConfig] = None
|
139
|
-
):
|
137
|
+
def __init__(self, task_manager: TaskManager, config: ReportConfig | None = None):
|
140
138
|
"""Initialize performance reporter.
|
141
139
|
|
142
140
|
Args:
|
@@ -151,9 +149,9 @@ class WorkflowPerformanceReporter:
|
|
151
149
|
def generate_report(
|
152
150
|
self,
|
153
151
|
run_id: str,
|
154
|
-
output_path:
|
152
|
+
output_path: str | Path | None = None,
|
155
153
|
format: ReportFormat = ReportFormat.HTML,
|
156
|
-
compare_runs:
|
154
|
+
compare_runs: list[str] | None = None,
|
157
155
|
) -> Path:
|
158
156
|
"""Generate comprehensive performance report.
|
159
157
|
|
@@ -211,7 +209,7 @@ class WorkflowPerformanceReporter:
|
|
211
209
|
self.logger.info(f"Generated {format.value.upper()} report: {output_path}")
|
212
210
|
return output_path
|
213
211
|
|
214
|
-
def _analyze_workflow_run(self, run_id: str) ->
|
212
|
+
def _analyze_workflow_run(self, run_id: str) -> dict[str, Any]:
|
215
213
|
"""Perform detailed analysis of a workflow run.
|
216
214
|
|
217
215
|
Args:
|
@@ -264,7 +262,7 @@ class WorkflowPerformanceReporter:
|
|
264
262
|
}
|
265
263
|
|
266
264
|
def _calculate_workflow_summary(
|
267
|
-
self, run: Any, tasks:
|
265
|
+
self, run: Any, tasks: list[TaskRun]
|
268
266
|
) -> WorkflowSummary:
|
269
267
|
"""Calculate summary statistics for the workflow run."""
|
270
268
|
summary = WorkflowSummary(
|
@@ -340,7 +338,7 @@ class WorkflowPerformanceReporter:
|
|
340
338
|
|
341
339
|
return summary
|
342
340
|
|
343
|
-
def _analyze_task_performance(self, tasks:
|
341
|
+
def _analyze_task_performance(self, tasks: list[TaskRun]) -> dict[str, Any]:
|
344
342
|
"""Analyze performance patterns across tasks."""
|
345
343
|
analysis = {
|
346
344
|
"by_node_type": {},
|
@@ -403,7 +401,7 @@ class WorkflowPerformanceReporter:
|
|
403
401
|
|
404
402
|
return analysis
|
405
403
|
|
406
|
-
def _identify_bottlenecks(self, tasks:
|
404
|
+
def _identify_bottlenecks(self, tasks: list[TaskRun]) -> list[dict[str, Any]]:
|
407
405
|
"""Identify performance bottlenecks in the workflow."""
|
408
406
|
bottlenecks = []
|
409
407
|
|
@@ -497,7 +495,7 @@ class WorkflowPerformanceReporter:
|
|
497
495
|
|
498
496
|
return sorted(bottlenecks, key=lambda x: x["value"], reverse=True)
|
499
497
|
|
500
|
-
def _analyze_resource_utilization(self, tasks:
|
498
|
+
def _analyze_resource_utilization(self, tasks: list[TaskRun]) -> dict[str, Any]:
|
501
499
|
"""Analyze overall resource utilization patterns."""
|
502
500
|
analysis = {
|
503
501
|
"cpu_distribution": {},
|
@@ -581,7 +579,7 @@ class WorkflowPerformanceReporter:
|
|
581
579
|
|
582
580
|
return analysis
|
583
581
|
|
584
|
-
def _analyze_errors(self, tasks:
|
582
|
+
def _analyze_errors(self, tasks: list[TaskRun]) -> dict[str, Any]:
|
585
583
|
"""Analyze error patterns and failure modes."""
|
586
584
|
analysis = {
|
587
585
|
"error_summary": {},
|
@@ -634,7 +632,7 @@ class WorkflowPerformanceReporter:
|
|
634
632
|
|
635
633
|
return analysis
|
636
634
|
|
637
|
-
def _generate_insights(self, analysis:
|
635
|
+
def _generate_insights(self, analysis: dict[str, Any]) -> list[PerformanceInsight]:
|
638
636
|
"""Generate actionable insights from analysis results."""
|
639
637
|
insights = []
|
640
638
|
|
@@ -741,8 +739,8 @@ class WorkflowPerformanceReporter:
|
|
741
739
|
return insights
|
742
740
|
|
743
741
|
def _generate_analysis_charts(
|
744
|
-
self, run_id: str, tasks:
|
745
|
-
) ->
|
742
|
+
self, run_id: str, tasks: list[TaskRun]
|
743
|
+
) -> dict[str, str]:
|
746
744
|
"""Generate analysis charts and return file paths."""
|
747
745
|
charts = {}
|
748
746
|
|
@@ -755,7 +753,7 @@ class WorkflowPerformanceReporter:
|
|
755
753
|
|
756
754
|
return charts
|
757
755
|
|
758
|
-
def _compare_runs(self, run_ids:
|
756
|
+
def _compare_runs(self, run_ids: list[str]) -> dict[str, Any]:
|
759
757
|
"""Compare performance across multiple runs."""
|
760
758
|
comparison = {"runs": [], "trends": {}, "relative_performance": {}}
|
761
759
|
|
@@ -814,9 +812,9 @@ class WorkflowPerformanceReporter:
|
|
814
812
|
|
815
813
|
def _generate_html_report(
|
816
814
|
self,
|
817
|
-
analysis:
|
818
|
-
insights:
|
819
|
-
comparison_data:
|
815
|
+
analysis: dict[str, Any],
|
816
|
+
insights: list[PerformanceInsight],
|
817
|
+
comparison_data: dict[str, Any] | None = None,
|
820
818
|
) -> str:
|
821
819
|
"""Generate HTML report content."""
|
822
820
|
run_info = analysis["run_info"]
|
@@ -982,9 +980,9 @@ class WorkflowPerformanceReporter:
|
|
982
980
|
|
983
981
|
def _generate_markdown_report(
|
984
982
|
self,
|
985
|
-
analysis:
|
986
|
-
insights:
|
987
|
-
comparison_data:
|
983
|
+
analysis: dict[str, Any],
|
984
|
+
insights: list[PerformanceInsight],
|
985
|
+
comparison_data: dict[str, Any] | None = None,
|
988
986
|
) -> str:
|
989
987
|
"""Generate Markdown report content."""
|
990
988
|
run_info = analysis["run_info"]
|
@@ -1098,9 +1096,9 @@ class WorkflowPerformanceReporter:
|
|
1098
1096
|
|
1099
1097
|
def _generate_json_report(
|
1100
1098
|
self,
|
1101
|
-
analysis:
|
1102
|
-
insights:
|
1103
|
-
comparison_data:
|
1099
|
+
analysis: dict[str, Any],
|
1100
|
+
insights: list[PerformanceInsight],
|
1101
|
+
comparison_data: dict[str, Any] | None = None,
|
1104
1102
|
) -> str:
|
1105
1103
|
"""Generate JSON report content."""
|
1106
1104
|
report_data = {
|
@@ -1144,7 +1142,7 @@ class WorkflowPerformanceReporter:
|
|
1144
1142
|
|
1145
1143
|
return json.dumps(report_data, indent=2, default=str)
|
1146
1144
|
|
1147
|
-
def _generate_comparison_html(self, comparison_data:
|
1145
|
+
def _generate_comparison_html(self, comparison_data: dict[str, Any]) -> str:
|
1148
1146
|
"""Generate HTML for run comparison section."""
|
1149
1147
|
runs = comparison_data.get("runs", [])
|
1150
1148
|
trends = comparison_data.get("trends", {})
|
kailash/workflow/builder.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import logging
|
4
4
|
import uuid
|
5
|
-
from typing import Any
|
5
|
+
from typing import Any
|
6
6
|
|
7
7
|
from kailash.sdk_exceptions import ConnectionError, WorkflowValidationError
|
8
8
|
from kailash.workflow.graph import Workflow
|
@@ -15,15 +15,15 @@ class WorkflowBuilder:
|
|
15
15
|
|
16
16
|
def __init__(self):
|
17
17
|
"""Initialize an empty workflow builder."""
|
18
|
-
self.nodes:
|
19
|
-
self.connections:
|
20
|
-
self._metadata:
|
18
|
+
self.nodes: dict[str, dict[str, Any]] = {}
|
19
|
+
self.connections: list[dict[str, str]] = []
|
20
|
+
self._metadata: dict[str, Any] = {}
|
21
21
|
|
22
22
|
def add_node(
|
23
23
|
self,
|
24
24
|
node_type: str,
|
25
|
-
node_id:
|
26
|
-
config:
|
25
|
+
node_id: str | None = None,
|
26
|
+
config: dict[str, Any] | None = None,
|
27
27
|
) -> str:
|
28
28
|
"""
|
29
29
|
Add a node to the workflow.
|
@@ -106,7 +106,7 @@ class WorkflowBuilder:
|
|
106
106
|
self._metadata.update(kwargs)
|
107
107
|
return self
|
108
108
|
|
109
|
-
def build(self, workflow_id:
|
109
|
+
def build(self, workflow_id: str | None = None, **kwargs) -> Workflow:
|
110
110
|
"""
|
111
111
|
Build and return a Workflow instance.
|
112
112
|
|
@@ -193,7 +193,7 @@ class WorkflowBuilder:
|
|
193
193
|
return self
|
194
194
|
|
195
195
|
@classmethod
|
196
|
-
def from_dict(cls, config:
|
196
|
+
def from_dict(cls, config: dict[str, Any]) -> "WorkflowBuilder":
|
197
197
|
"""
|
198
198
|
Create builder from dictionary configuration.
|
199
199
|
|