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/api/studio.py
CHANGED
@@ -13,9 +13,9 @@ import json
|
|
13
13
|
import logging
|
14
14
|
import os
|
15
15
|
import uuid
|
16
|
-
from datetime import
|
16
|
+
from datetime import UTC, datetime
|
17
17
|
from pathlib import Path
|
18
|
-
from typing import Any
|
18
|
+
from typing import Any
|
19
19
|
|
20
20
|
import uvicorn
|
21
21
|
from fastapi import FastAPI, HTTPException, Query, WebSocket, WebSocketDisconnect
|
@@ -49,25 +49,25 @@ class NodeDefinition(BaseModel):
|
|
49
49
|
category: str
|
50
50
|
name: str
|
51
51
|
description: str
|
52
|
-
parameters:
|
53
|
-
inputs:
|
54
|
-
outputs:
|
52
|
+
parameters: list[dict[str, Any]]
|
53
|
+
inputs: list[dict[str, Any]]
|
54
|
+
outputs: list[dict[str, Any]]
|
55
55
|
|
56
56
|
|
57
57
|
class WorkflowCreate(BaseModel):
|
58
58
|
"""Workflow creation request"""
|
59
59
|
|
60
60
|
name: str
|
61
|
-
description:
|
62
|
-
definition:
|
61
|
+
description: str | None = None
|
62
|
+
definition: dict[str, Any]
|
63
63
|
|
64
64
|
|
65
65
|
class WorkflowUpdate(BaseModel):
|
66
66
|
"""Workflow update request"""
|
67
67
|
|
68
|
-
name:
|
69
|
-
description:
|
70
|
-
definition:
|
68
|
+
name: str | None = None
|
69
|
+
description: str | None = None
|
70
|
+
definition: dict[str, Any] | None = None
|
71
71
|
|
72
72
|
|
73
73
|
class WorkflowResponse(BaseModel):
|
@@ -75,8 +75,8 @@ class WorkflowResponse(BaseModel):
|
|
75
75
|
|
76
76
|
id: str
|
77
77
|
name: str
|
78
|
-
description:
|
79
|
-
definition:
|
78
|
+
description: str | None
|
79
|
+
definition: dict[str, Any]
|
80
80
|
created_at: datetime
|
81
81
|
updated_at: datetime
|
82
82
|
|
@@ -84,7 +84,7 @@ class WorkflowResponse(BaseModel):
|
|
84
84
|
class ExecutionRequest(BaseModel):
|
85
85
|
"""Workflow execution request"""
|
86
86
|
|
87
|
-
parameters:
|
87
|
+
parameters: dict[str, Any] | None = None
|
88
88
|
|
89
89
|
|
90
90
|
class ExecutionResponse(BaseModel):
|
@@ -94,16 +94,16 @@ class ExecutionResponse(BaseModel):
|
|
94
94
|
workflow_id: str
|
95
95
|
status: str
|
96
96
|
started_at: datetime
|
97
|
-
completed_at:
|
98
|
-
result:
|
99
|
-
error:
|
97
|
+
completed_at: datetime | None
|
98
|
+
result: dict[str, Any] | None
|
99
|
+
error: str | None
|
100
100
|
|
101
101
|
|
102
102
|
class WorkflowImportRequest(BaseModel):
|
103
103
|
"""Workflow import request"""
|
104
104
|
|
105
105
|
name: str
|
106
|
-
description:
|
106
|
+
description: str | None = None
|
107
107
|
format: str = Field(..., pattern="^(yaml|json|python)$")
|
108
108
|
content: str
|
109
109
|
|
@@ -113,10 +113,10 @@ class WorkflowImportResponse(BaseModel):
|
|
113
113
|
|
114
114
|
id: str
|
115
115
|
name: str
|
116
|
-
description:
|
117
|
-
definition:
|
116
|
+
description: str | None
|
117
|
+
definition: dict[str, Any]
|
118
118
|
created_at: datetime
|
119
|
-
warnings:
|
119
|
+
warnings: list[str] = []
|
120
120
|
|
121
121
|
|
122
122
|
class WorkflowStudioAPI:
|
@@ -135,8 +135,8 @@ class WorkflowStudioAPI:
|
|
135
135
|
self.setup_middleware()
|
136
136
|
self.setup_routes()
|
137
137
|
self.setup_storage()
|
138
|
-
self.active_executions:
|
139
|
-
self.websocket_connections:
|
138
|
+
self.active_executions: dict[str, asyncio.Task] = {}
|
139
|
+
self.websocket_connections: dict[str, list[WebSocket]] = {}
|
140
140
|
|
141
141
|
# Register custom nodes on startup
|
142
142
|
self.app.add_event_handler("startup", self._register_custom_nodes)
|
@@ -231,7 +231,7 @@ class WorkflowStudioAPI:
|
|
231
231
|
return {"status": "healthy", "tenant_id": self.tenant_id}
|
232
232
|
|
233
233
|
# Node discovery endpoints
|
234
|
-
@self.app.get("/api/nodes", response_model=
|
234
|
+
@self.app.get("/api/nodes", response_model=dict[str, list[NodeDefinition]])
|
235
235
|
async def list_nodes():
|
236
236
|
"""List all available nodes grouped by category"""
|
237
237
|
try:
|
@@ -385,7 +385,7 @@ class WorkflowStudioAPI:
|
|
385
385
|
|
386
386
|
# Add alias for backward compatibility
|
387
387
|
@self.app.get(
|
388
|
-
"/api/nodes/discover", response_model=
|
388
|
+
"/api/nodes/discover", response_model=dict[str, list[NodeDefinition]]
|
389
389
|
)
|
390
390
|
async def discover_nodes():
|
391
391
|
"""Alias for list_nodes endpoint for backward compatibility"""
|
@@ -420,7 +420,7 @@ class WorkflowStudioAPI:
|
|
420
420
|
)
|
421
421
|
|
422
422
|
# Workflow management endpoints
|
423
|
-
@self.app.get("/api/workflows", response_model=
|
423
|
+
@self.app.get("/api/workflows", response_model=list[WorkflowResponse])
|
424
424
|
async def list_workflows(
|
425
425
|
limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0)
|
426
426
|
):
|
@@ -434,7 +434,7 @@ class WorkflowStudioAPI:
|
|
434
434
|
|
435
435
|
for workflow_file in workflow_files[offset : offset + limit]:
|
436
436
|
try:
|
437
|
-
with open(workflow_file
|
437
|
+
with open(workflow_file) as f:
|
438
438
|
data = json.load(f)
|
439
439
|
workflows.append(WorkflowResponse(**data))
|
440
440
|
except Exception as e:
|
@@ -446,7 +446,7 @@ class WorkflowStudioAPI:
|
|
446
446
|
async def create_workflow(workflow: WorkflowCreate):
|
447
447
|
"""Create a new workflow"""
|
448
448
|
workflow_id = str(uuid.uuid4())
|
449
|
-
now = datetime.now(
|
449
|
+
now = datetime.now(UTC)
|
450
450
|
|
451
451
|
workflow_data = {
|
452
452
|
"id": workflow_id,
|
@@ -471,7 +471,7 @@ class WorkflowStudioAPI:
|
|
471
471
|
if not workflow_file.exists():
|
472
472
|
raise HTTPException(status_code=404, detail="Workflow not found")
|
473
473
|
|
474
|
-
with open(workflow_file
|
474
|
+
with open(workflow_file) as f:
|
475
475
|
data = json.load(f)
|
476
476
|
|
477
477
|
return WorkflowResponse(**data)
|
@@ -484,7 +484,7 @@ class WorkflowStudioAPI:
|
|
484
484
|
raise HTTPException(status_code=404, detail="Workflow not found")
|
485
485
|
|
486
486
|
# Load existing workflow
|
487
|
-
with open(workflow_file
|
487
|
+
with open(workflow_file) as f:
|
488
488
|
data = json.load(f)
|
489
489
|
|
490
490
|
# Update fields
|
@@ -495,7 +495,7 @@ class WorkflowStudioAPI:
|
|
495
495
|
if update.definition is not None:
|
496
496
|
data["definition"] = update.definition
|
497
497
|
|
498
|
-
data["updated_at"] = datetime.now(
|
498
|
+
data["updated_at"] = datetime.now(UTC).isoformat()
|
499
499
|
|
500
500
|
# Save updated workflow
|
501
501
|
with open(workflow_file, "w") as f:
|
@@ -524,7 +524,7 @@ class WorkflowStudioAPI:
|
|
524
524
|
if not workflow_file.exists():
|
525
525
|
raise HTTPException(status_code=404, detail="Workflow not found")
|
526
526
|
|
527
|
-
with open(workflow_file
|
527
|
+
with open(workflow_file) as f:
|
528
528
|
workflow_data = json.load(f)
|
529
529
|
|
530
530
|
# Create execution record
|
@@ -533,7 +533,7 @@ class WorkflowStudioAPI:
|
|
533
533
|
"id": execution_id,
|
534
534
|
"workflow_id": workflow_id,
|
535
535
|
"status": "running",
|
536
|
-
"started_at": datetime.now(
|
536
|
+
"started_at": datetime.now(UTC).isoformat(),
|
537
537
|
"completed_at": None,
|
538
538
|
"result": None,
|
539
539
|
"error": None,
|
@@ -560,7 +560,7 @@ class WorkflowStudioAPI:
|
|
560
560
|
except Exception as e:
|
561
561
|
execution_data["status"] = "failed"
|
562
562
|
execution_data["error"] = str(e)
|
563
|
-
execution_data["completed_at"] = datetime.now(
|
563
|
+
execution_data["completed_at"] = datetime.now(UTC).isoformat()
|
564
564
|
|
565
565
|
with open(execution_file, "w") as f:
|
566
566
|
json.dump(execution_data, f, indent=2)
|
@@ -576,7 +576,7 @@ class WorkflowStudioAPI:
|
|
576
576
|
if not execution_file.exists():
|
577
577
|
raise HTTPException(status_code=404, detail="Execution not found")
|
578
578
|
|
579
|
-
with open(execution_file
|
579
|
+
with open(execution_file) as f:
|
580
580
|
data = json.load(f)
|
581
581
|
|
582
582
|
return ExecutionResponse(**data)
|
@@ -602,7 +602,7 @@ class WorkflowStudioAPI:
|
|
602
602
|
break
|
603
603
|
|
604
604
|
# Send current status
|
605
|
-
with open(execution_file
|
605
|
+
with open(execution_file) as f:
|
606
606
|
data = json.load(f)
|
607
607
|
await websocket.send_json(data)
|
608
608
|
|
@@ -633,7 +633,7 @@ class WorkflowStudioAPI:
|
|
633
633
|
if not workflow_file.exists():
|
634
634
|
raise HTTPException(status_code=404, detail="Workflow not found")
|
635
635
|
|
636
|
-
with open(workflow_file
|
636
|
+
with open(workflow_file) as f:
|
637
637
|
workflow_data = json.load(f)
|
638
638
|
|
639
639
|
# Create workflow from definition
|
@@ -686,7 +686,7 @@ class WorkflowStudioAPI:
|
|
686
686
|
warnings.append(f"Workflow validation warning: {str(e)}")
|
687
687
|
|
688
688
|
# Create workflow record
|
689
|
-
now = datetime.now(
|
689
|
+
now = datetime.now(UTC)
|
690
690
|
workflow_data = {
|
691
691
|
"id": workflow_id,
|
692
692
|
"name": request.name,
|
@@ -718,7 +718,7 @@ class WorkflowStudioAPI:
|
|
718
718
|
execution_id: str,
|
719
719
|
workflow: Workflow,
|
720
720
|
runtime: LocalRuntime,
|
721
|
-
parameters:
|
721
|
+
parameters: dict[str, Any],
|
722
722
|
):
|
723
723
|
"""Execute workflow asynchronously and update status"""
|
724
724
|
execution_file = self.executions_path / f"{execution_id}.json"
|
@@ -728,11 +728,11 @@ class WorkflowStudioAPI:
|
|
728
728
|
result, run_id = runtime.execute(workflow, parameters=parameters)
|
729
729
|
|
730
730
|
# Update execution record
|
731
|
-
with open(execution_file
|
731
|
+
with open(execution_file) as f:
|
732
732
|
execution_data = json.load(f)
|
733
733
|
|
734
734
|
execution_data["status"] = "completed"
|
735
|
-
execution_data["completed_at"] = datetime.now(
|
735
|
+
execution_data["completed_at"] = datetime.now(UTC).isoformat()
|
736
736
|
execution_data["result"] = result
|
737
737
|
|
738
738
|
with open(execution_file, "w") as f:
|
@@ -743,11 +743,11 @@ class WorkflowStudioAPI:
|
|
743
743
|
|
744
744
|
except Exception as e:
|
745
745
|
# Update execution record with error
|
746
|
-
with open(execution_file
|
746
|
+
with open(execution_file) as f:
|
747
747
|
execution_data = json.load(f)
|
748
748
|
|
749
749
|
execution_data["status"] = "failed"
|
750
|
-
execution_data["completed_at"] = datetime.now(
|
750
|
+
execution_data["completed_at"] = datetime.now(UTC).isoformat()
|
751
751
|
execution_data["error"] = str(e)
|
752
752
|
|
753
753
|
with open(execution_file, "w") as f:
|
@@ -761,7 +761,7 @@ class WorkflowStudioAPI:
|
|
761
761
|
if execution_id in self.active_executions:
|
762
762
|
del self.active_executions[execution_id]
|
763
763
|
|
764
|
-
async def _notify_websocket_clients(self, execution_id: str, data:
|
764
|
+
async def _notify_websocket_clients(self, execution_id: str, data: dict[str, Any]):
|
765
765
|
"""Notify all WebSocket clients watching this execution"""
|
766
766
|
if execution_id in self.websocket_connections:
|
767
767
|
for websocket in self.websocket_connections[execution_id]:
|
@@ -851,7 +851,7 @@ class WorkflowStudioAPI:
|
|
851
851
|
|
852
852
|
return "\n".join(lines)
|
853
853
|
|
854
|
-
def _parse_python_workflow(self, python_code: str) ->
|
854
|
+
def _parse_python_workflow(self, python_code: str) -> dict[str, Any]:
|
855
855
|
"""Parse Python code to extract workflow definition.
|
856
856
|
|
857
857
|
This is a simplified parser that extracts workflow structure from Python code.
|
@@ -868,7 +868,7 @@ class WorkflowStudioAPI:
|
|
868
868
|
},
|
869
869
|
}
|
870
870
|
|
871
|
-
def _format_config(self, config:
|
871
|
+
def _format_config(self, config: dict[str, Any]) -> str:
|
872
872
|
"""Format config dict as Python code"""
|
873
873
|
if not config:
|
874
874
|
return ""
|
kailash/api/workflow_api.py
CHANGED
@@ -8,7 +8,7 @@ workflow as a REST API with minimal configuration.
|
|
8
8
|
import asyncio
|
9
9
|
from contextlib import asynccontextmanager
|
10
10
|
from enum import Enum
|
11
|
-
from typing import Any
|
11
|
+
from typing import Any
|
12
12
|
|
13
13
|
import uvicorn
|
14
14
|
from fastapi import BackgroundTasks, FastAPI, HTTPException
|
@@ -31,8 +31,8 @@ class ExecutionMode(str, Enum):
|
|
31
31
|
class WorkflowRequest(BaseModel):
|
32
32
|
"""Base request model for workflow execution."""
|
33
33
|
|
34
|
-
inputs:
|
35
|
-
config:
|
34
|
+
inputs: dict[str, Any] = Field(..., description="Input data for workflow nodes")
|
35
|
+
config: dict[str, Any] | None = Field(
|
36
36
|
None, description="Node configuration overrides"
|
37
37
|
)
|
38
38
|
mode: ExecutionMode = Field(ExecutionMode.SYNC, description="Execution mode")
|
@@ -41,7 +41,7 @@ class WorkflowRequest(BaseModel):
|
|
41
41
|
class WorkflowResponse(BaseModel):
|
42
42
|
"""Base response model for workflow execution."""
|
43
43
|
|
44
|
-
outputs:
|
44
|
+
outputs: dict[str, Any] = Field(..., description="Output data from workflow nodes")
|
45
45
|
execution_time: float = Field(..., description="Execution time in seconds")
|
46
46
|
workflow_id: str = Field(..., description="Workflow identifier")
|
47
47
|
version: str = Field(..., description="Workflow version")
|
@@ -63,7 +63,7 @@ class WorkflowAPI:
|
|
63
63
|
|
64
64
|
def __init__(
|
65
65
|
self,
|
66
|
-
workflow:
|
66
|
+
workflow: WorkflowBuilder | Workflow,
|
67
67
|
app_name: str = "Kailash Workflow API",
|
68
68
|
version: str = "1.0.0",
|
69
69
|
description: str = "API wrapper for Kailash workflow execution",
|
@@ -102,7 +102,7 @@ class WorkflowAPI:
|
|
102
102
|
self._setup_routes()
|
103
103
|
|
104
104
|
# Cache for async executions
|
105
|
-
self._execution_cache:
|
105
|
+
self._execution_cache: dict[str, dict[str, Any]] = {}
|
106
106
|
|
107
107
|
@asynccontextmanager
|
108
108
|
async def _lifespan(self, app: FastAPI):
|
@@ -317,12 +317,12 @@ class HierarchicalRAGAPI(WorkflowAPI):
|
|
317
317
|
|
318
318
|
class RAGResponse(BaseModel):
|
319
319
|
answer: str
|
320
|
-
sources:
|
320
|
+
sources: list[dict[str, Any]]
|
321
321
|
query: str
|
322
322
|
execution_time: float
|
323
323
|
|
324
324
|
@self.app.post("/documents")
|
325
|
-
async def add_documents(documents:
|
325
|
+
async def add_documents(documents: list[Document]):
|
326
326
|
"""Add documents to the knowledge base."""
|
327
327
|
# This would integrate with document storage
|
328
328
|
return {"message": f"Added {len(documents)} documents"}
|
kailash/cli/commands.py
CHANGED
@@ -4,7 +4,6 @@ import json
|
|
4
4
|
import logging
|
5
5
|
import sys
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Optional
|
8
7
|
|
9
8
|
import click
|
10
9
|
|
@@ -77,7 +76,7 @@ def init(name: str, template: str):
|
|
77
76
|
@click.option("--params", "-p", help="JSON file with parameter overrides")
|
78
77
|
@click.option("--debug", is_flag=True, help="Enable debug mode")
|
79
78
|
@click.option("--no-tracking", is_flag=True, help="Disable task tracking")
|
80
|
-
def run(workflow_file: str, params:
|
79
|
+
def run(workflow_file: str, params: str | None, debug: bool, no_tracking: bool):
|
81
80
|
"""Run a workflow locally."""
|
82
81
|
try:
|
83
82
|
# Validate workflow file exists
|
@@ -97,7 +96,7 @@ def run(workflow_file: str, params: Optional[str], debug: bool, no_tracking: boo
|
|
97
96
|
parameters = {}
|
98
97
|
if params:
|
99
98
|
try:
|
100
|
-
with open(params
|
99
|
+
with open(params) as f:
|
101
100
|
parameters = json.load(f)
|
102
101
|
except FileNotFoundError:
|
103
102
|
raise CLIException(f"Parameters file not found: {params}")
|
@@ -190,7 +189,7 @@ def validate(workflow_file: str):
|
|
190
189
|
"--format", default="yaml", type=click.Choice(["yaml", "json", "manifest"])
|
191
190
|
)
|
192
191
|
@click.option("--registry", help="Container registry URL")
|
193
|
-
def export(workflow_file: str, output_file: str, format: str, registry:
|
192
|
+
def export(workflow_file: str, output_file: str, format: str, registry: str | None):
|
194
193
|
"""Export workflow to Kailash format."""
|
195
194
|
try:
|
196
195
|
# Validate workflow file exists
|
@@ -233,14 +232,13 @@ def export(workflow_file: str, output_file: str, format: str, registry: Optional
|
|
233
232
|
@cli.group()
|
234
233
|
def tasks():
|
235
234
|
"""Task tracking commands."""
|
236
|
-
pass
|
237
235
|
|
238
236
|
|
239
237
|
@tasks.command("list")
|
240
238
|
@click.option("--workflow", help="Filter by workflow name")
|
241
239
|
@click.option("--status", help="Filter by status")
|
242
240
|
@click.option("--limit", default=10, help="Number of runs to show")
|
243
|
-
def list_tasks(workflow:
|
241
|
+
def list_tasks(workflow: str | None, status: str | None, limit: int):
|
244
242
|
"""List workflow runs."""
|
245
243
|
try:
|
246
244
|
task_manager = TaskManager()
|
@@ -380,7 +378,6 @@ def clear_tasks():
|
|
380
378
|
@cli.group()
|
381
379
|
def nodes():
|
382
380
|
"""Node management commands."""
|
383
|
-
pass
|
384
381
|
|
385
382
|
|
386
383
|
@nodes.command("list")
|
@@ -497,7 +494,7 @@ def _load_python_workflow(workflow_file: str) -> Workflow:
|
|
497
494
|
try:
|
498
495
|
# Read and execute Python file
|
499
496
|
global_scope = {}
|
500
|
-
with open(workflow_file
|
497
|
+
with open(workflow_file) as f:
|
501
498
|
code = f.read()
|
502
499
|
|
503
500
|
exec(code, global_scope)
|