kailash 0.6.1__py3-none-any.whl → 0.6.3__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/core/actors/connection_actor.py +3 -3
- kailash/gateway/api.py +7 -5
- kailash/gateway/enhanced_gateway.py +1 -1
- kailash/{mcp → mcp_server}/__init__.py +12 -7
- kailash/{mcp → mcp_server}/ai_registry_server.py +2 -2
- kailash/{mcp/server_enhanced.py → mcp_server/server.py} +231 -48
- kailash/{mcp → mcp_server}/servers/ai_registry.py +2 -2
- kailash/{mcp → mcp_server}/utils/__init__.py +1 -6
- kailash/middleware/auth/access_control.py +5 -5
- kailash/middleware/gateway/checkpoint_manager.py +45 -8
- kailash/middleware/mcp/client_integration.py +1 -1
- kailash/middleware/mcp/enhanced_server.py +2 -2
- kailash/nodes/admin/permission_check.py +110 -30
- kailash/nodes/admin/schema.sql +387 -0
- kailash/nodes/admin/tenant_isolation.py +249 -0
- kailash/nodes/admin/transaction_utils.py +244 -0
- kailash/nodes/admin/user_management.py +37 -9
- kailash/nodes/ai/ai_providers.py +55 -3
- kailash/nodes/ai/iterative_llm_agent.py +1 -1
- kailash/nodes/ai/llm_agent.py +118 -16
- kailash/nodes/data/sql.py +24 -0
- kailash/resources/registry.py +6 -0
- kailash/runtime/async_local.py +7 -0
- kailash/utils/export.py +152 -0
- kailash/workflow/builder.py +42 -0
- kailash/workflow/graph.py +86 -17
- kailash/workflow/templates.py +4 -9
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/METADATA +3 -2
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/RECORD +40 -38
- kailash/mcp/server.py +0 -292
- /kailash/{mcp → mcp_server}/client.py +0 -0
- /kailash/{mcp → mcp_server}/client_new.py +0 -0
- /kailash/{mcp → mcp_server}/utils/cache.py +0 -0
- /kailash/{mcp → mcp_server}/utils/config.py +0 -0
- /kailash/{mcp → mcp_server}/utils/formatters.py +0 -0
- /kailash/{mcp → mcp_server}/utils/metrics.py +0 -0
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/WHEEL +0 -0
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/entry_points.txt +0 -0
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.6.1.dist-info → kailash-0.6.3.dist-info}/top_level.txt +0 -0
kailash/__init__.py
CHANGED
@@ -47,7 +47,7 @@ class Message:
|
|
47
47
|
type: MessageType = MessageType.QUERY
|
48
48
|
payload: Any = None
|
49
49
|
reply_to: Optional[asyncio.Queue] = None
|
50
|
-
timestamp: datetime = field(default_factory=datetime.
|
50
|
+
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
|
51
51
|
|
52
52
|
|
53
53
|
@dataclass
|
@@ -70,8 +70,8 @@ class ConnectionStats:
|
|
70
70
|
total_execution_time: float = 0.0
|
71
71
|
health_checks_passed: int = 0
|
72
72
|
health_checks_failed: int = 0
|
73
|
-
created_at: datetime = field(default_factory=datetime.
|
74
|
-
last_used_at: datetime = field(default_factory=datetime.
|
73
|
+
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
74
|
+
last_used_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
75
75
|
health_score: float = 100.0
|
76
76
|
|
77
77
|
|
kailash/gateway/api.py
CHANGED
@@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
11
11
|
|
12
12
|
from fastapi import APIRouter, BackgroundTasks, Depends, FastAPI, HTTPException
|
13
13
|
from fastapi.responses import JSONResponse
|
14
|
-
from pydantic import BaseModel, Field
|
14
|
+
from pydantic import BaseModel, ConfigDict, Field
|
15
15
|
|
16
16
|
from ..resources.registry import ResourceRegistry
|
17
17
|
from .enhanced_gateway import (
|
@@ -37,14 +37,15 @@ class ResourceReferenceModel(BaseModel):
|
|
37
37
|
None, description="Reference to credentials secret"
|
38
38
|
)
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
model_config = ConfigDict(
|
41
|
+
json_schema_extra={
|
42
42
|
"example": {
|
43
43
|
"type": "database",
|
44
44
|
"config": {"host": "localhost", "port": 5432, "database": "myapp"},
|
45
45
|
"credentials_ref": "db_credentials",
|
46
46
|
}
|
47
47
|
}
|
48
|
+
)
|
48
49
|
|
49
50
|
|
50
51
|
class WorkflowRequestModel(BaseModel):
|
@@ -59,8 +60,8 @@ class WorkflowRequestModel(BaseModel):
|
|
59
60
|
None, description="Additional context variables"
|
60
61
|
)
|
61
62
|
|
62
|
-
|
63
|
-
|
63
|
+
model_config = ConfigDict(
|
64
|
+
json_schema_extra={
|
64
65
|
"example": {
|
65
66
|
"inputs": {"user_id": 123, "action": "process"},
|
66
67
|
"resources": {
|
@@ -74,6 +75,7 @@ class WorkflowRequestModel(BaseModel):
|
|
74
75
|
"context": {"environment": "production", "trace_id": "abc123"},
|
75
76
|
}
|
76
77
|
}
|
78
|
+
)
|
77
79
|
|
78
80
|
|
79
81
|
class WorkflowResponseModel(BaseModel):
|
@@ -40,7 +40,7 @@ class WorkflowRequest:
|
|
40
40
|
inputs: Dict[str, Any] = field(default_factory=dict)
|
41
41
|
resources: Dict[str, Union[str, ResourceReference]] = field(default_factory=dict)
|
42
42
|
context: Dict[str, Any] = field(default_factory=dict)
|
43
|
-
timestamp: datetime = field(default_factory=datetime.
|
43
|
+
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
|
44
44
|
|
45
45
|
def to_dict(self) -> Dict[str, Any]:
|
46
46
|
"""Convert to JSON-serializable dict."""
|
@@ -12,8 +12,8 @@ Design Philosophy:
|
|
12
12
|
|
13
13
|
Key Components:
|
14
14
|
- MCPClient: Connects to MCP servers for tool and resource access
|
15
|
-
- MCPServer:
|
16
|
-
-
|
15
|
+
- MCPServer: Main production-ready server with all features
|
16
|
+
- MCPServerBase: Abstract base class for custom server implementations
|
17
17
|
|
18
18
|
Upstream Dependencies:
|
19
19
|
- Official Anthropic MCP Python SDK for protocol implementation
|
@@ -28,26 +28,31 @@ Downstream Consumers:
|
|
28
28
|
Examples:
|
29
29
|
Basic MCP client usage:
|
30
30
|
|
31
|
-
>>> from kailash.
|
31
|
+
>>> from kailash.mcp_server import MCPClient
|
32
32
|
>>> client = MCPClient()
|
33
33
|
>>> tools = await client.discover_tools(server_config)
|
34
34
|
>>> result = await client.call_tool(server_config, "search", {"query": "AI"})
|
35
35
|
|
36
36
|
Simple MCP server creation:
|
37
37
|
|
38
|
-
>>> from kailash.
|
39
|
-
>>> server =
|
38
|
+
>>> from kailash.mcp_server import MCPServer
|
39
|
+
>>> server = MCPServer("my-tools")
|
40
40
|
>>> @server.tool()
|
41
41
|
... def calculate(a: int, b: int) -> int:
|
42
42
|
... return a + b
|
43
|
-
>>> server.
|
43
|
+
>>> server.run()
|
44
44
|
"""
|
45
45
|
|
46
46
|
from .client import MCPClient
|
47
|
-
|
47
|
+
|
48
|
+
# For backward compatibility
|
49
|
+
from .server import EnhancedMCPServer, MCPServer, MCPServerBase, SimpleMCPServer
|
48
50
|
|
49
51
|
__all__ = [
|
50
52
|
"MCPClient",
|
51
53
|
"MCPServer",
|
54
|
+
"MCPServerBase",
|
55
|
+
# Backward compatibility
|
52
56
|
"SimpleMCPServer",
|
57
|
+
"EnhancedMCPServer",
|
53
58
|
]
|
@@ -5,7 +5,7 @@ AI Registry MCP Server using Anthropic's Official MCP Python SDK.
|
|
5
5
|
This creates a real MCP server that exposes AI Registry tools following
|
6
6
|
the actual Model Context Protocol specification.
|
7
7
|
|
8
|
-
Run as: python -m kailash.
|
8
|
+
Run as: python -m kailash.mcp_server.ai_registry_server
|
9
9
|
"""
|
10
10
|
|
11
11
|
import asyncio
|
@@ -708,5 +708,5 @@ if __name__ == "__main__":
|
|
708
708
|
|
709
709
|
# For module execution
|
710
710
|
def run_server():
|
711
|
-
"""Entry point for python -m kailash.
|
711
|
+
"""Entry point for python -m kailash.mcp_server.ai_registry_server"""
|
712
712
|
asyncio.run(main())
|
@@ -1,16 +1,39 @@
|
|
1
1
|
"""
|
2
|
-
|
3
|
-
|
4
|
-
This module provides
|
5
|
-
|
6
|
-
|
2
|
+
MCP Server Framework with production-ready capabilities.
|
3
|
+
|
4
|
+
This module provides both basic and enhanced MCP server implementations using
|
5
|
+
the official FastMCP framework from Anthropic. Servers run as long-lived
|
6
|
+
services that expose tools, resources, and prompts to MCP clients.
|
7
|
+
|
8
|
+
Basic Usage:
|
9
|
+
Abstract base class for custom servers:
|
10
|
+
|
11
|
+
>>> class MyServer(MCPServerBase):
|
12
|
+
... def setup(self):
|
13
|
+
... @self.add_tool()
|
14
|
+
... def calculate(a: int, b: int) -> int:
|
15
|
+
... return a + b
|
16
|
+
>>> server = MyServer("calculator")
|
17
|
+
>>> server.start()
|
18
|
+
|
19
|
+
Production Usage:
|
20
|
+
Main server with all production features:
|
21
|
+
|
22
|
+
>>> from kailash.mcp_server import MCPServer
|
23
|
+
>>> server = MCPServer("my-server", enable_cache=True)
|
24
|
+
>>> @server.tool(cache_key="search", cache_ttl=600)
|
25
|
+
... def search(query: str) -> dict:
|
26
|
+
... return {"results": f"Found data for {query}"}
|
27
|
+
>>> server.run()
|
7
28
|
"""
|
8
29
|
|
9
30
|
import asyncio
|
10
31
|
import functools
|
11
32
|
import logging
|
33
|
+
from abc import ABC, abstractmethod
|
34
|
+
from collections.abc import Callable
|
12
35
|
from pathlib import Path
|
13
|
-
from typing import Any,
|
36
|
+
from typing import Any, Dict, Optional, TypeVar, Union
|
14
37
|
|
15
38
|
from .utils import CacheManager, ConfigManager, MetricsCollector, format_response
|
16
39
|
|
@@ -19,18 +42,199 @@ logger = logging.getLogger(__name__)
|
|
19
42
|
F = TypeVar("F", bound=Callable[..., Any])
|
20
43
|
|
21
44
|
|
45
|
+
class MCPServerBase(ABC):
|
46
|
+
"""Base class for MCP servers using FastMCP.
|
47
|
+
|
48
|
+
This provides a framework for creating MCP servers that expose
|
49
|
+
tools, resources, and prompts via the Model Context Protocol.
|
50
|
+
|
51
|
+
Examples:
|
52
|
+
Creating a custom server:
|
53
|
+
|
54
|
+
>>> class MyServer(MCPServerBase):
|
55
|
+
... def setup(self):
|
56
|
+
... @self.add_tool()
|
57
|
+
... def search(query: str) -> str:
|
58
|
+
... return f"Results for: {query}"
|
59
|
+
... @self.add_resource("data://example")
|
60
|
+
... def get_example():
|
61
|
+
... return "Example data"
|
62
|
+
>>> server = MyServer("my-server", port=8080)
|
63
|
+
>>> server.start() # Runs until stopped
|
64
|
+
"""
|
65
|
+
|
66
|
+
def __init__(self, name: str, port: int = 8080, host: str = "localhost"):
|
67
|
+
"""Initialize the MCP server.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
name: Name of the server.
|
71
|
+
port: Port to listen on (default: 8080).
|
72
|
+
host: Host to bind to (default: "localhost").
|
73
|
+
"""
|
74
|
+
self.name = name
|
75
|
+
self.port = port
|
76
|
+
self.host = host
|
77
|
+
self._mcp = None
|
78
|
+
self._running = False
|
79
|
+
|
80
|
+
@abstractmethod
|
81
|
+
def setup(self):
|
82
|
+
"""Setup server tools, resources, and prompts.
|
83
|
+
|
84
|
+
This method should be implemented by subclasses to define
|
85
|
+
the server's capabilities using decorators.
|
86
|
+
|
87
|
+
Note:
|
88
|
+
Use @self.add_tool(), @self.add_resource(uri), and
|
89
|
+
@self.add_prompt(name) decorators to register capabilities.
|
90
|
+
"""
|
91
|
+
|
92
|
+
def add_tool(self):
|
93
|
+
"""Decorator to add a tool to the server.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
Function decorator for registering tools.
|
97
|
+
|
98
|
+
Examples:
|
99
|
+
>>> @server.add_tool()
|
100
|
+
... def calculate(a: int, b: int) -> int:
|
101
|
+
... '''Add two numbers'''
|
102
|
+
... return a + b
|
103
|
+
"""
|
104
|
+
|
105
|
+
def decorator(func: Callable):
|
106
|
+
if self._mcp is None:
|
107
|
+
self._init_mcp()
|
108
|
+
|
109
|
+
# Use FastMCP's tool decorator
|
110
|
+
return self._mcp.tool()(func)
|
111
|
+
|
112
|
+
return decorator
|
113
|
+
|
114
|
+
def add_resource(self, uri: str):
|
115
|
+
"""Decorator to add a resource to the server.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
uri: URI pattern for the resource (supports wildcards).
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
Function decorator for registering resources.
|
122
|
+
|
123
|
+
Examples:
|
124
|
+
>>> @server.add_resource("file:///data/*")
|
125
|
+
... def get_file(path: str) -> str:
|
126
|
+
... return f"Content of {path}"
|
127
|
+
"""
|
128
|
+
|
129
|
+
def decorator(func: Callable):
|
130
|
+
if self._mcp is None:
|
131
|
+
self._init_mcp()
|
132
|
+
|
133
|
+
# Use FastMCP's resource decorator
|
134
|
+
return self._mcp.resource(uri)(func)
|
135
|
+
|
136
|
+
return decorator
|
137
|
+
|
138
|
+
def add_prompt(self, name: str):
|
139
|
+
"""Decorator to add a prompt template to the server.
|
140
|
+
|
141
|
+
Args:
|
142
|
+
name: Name of the prompt.
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
Function decorator for registering prompts.
|
146
|
+
|
147
|
+
Examples:
|
148
|
+
>>> @server.add_prompt("analyze")
|
149
|
+
... def analyze_prompt(data: str) -> str:
|
150
|
+
... return f"Please analyze the following data: {data}"
|
151
|
+
"""
|
152
|
+
|
153
|
+
def decorator(func: Callable):
|
154
|
+
if self._mcp is None:
|
155
|
+
self._init_mcp()
|
156
|
+
|
157
|
+
# Use FastMCP's prompt decorator
|
158
|
+
return self._mcp.prompt(name)(func)
|
159
|
+
|
160
|
+
return decorator
|
161
|
+
|
162
|
+
def _init_mcp(self):
|
163
|
+
"""Initialize the FastMCP instance."""
|
164
|
+
try:
|
165
|
+
from mcp.server import FastMCP
|
166
|
+
|
167
|
+
self._mcp = FastMCP(self.name)
|
168
|
+
except ImportError:
|
169
|
+
logger.error(
|
170
|
+
"FastMCP not available. Install with: pip install 'mcp[server]'"
|
171
|
+
)
|
172
|
+
raise
|
173
|
+
|
174
|
+
def start(self):
|
175
|
+
"""Start the MCP server.
|
176
|
+
|
177
|
+
This runs the server as a long-lived process until stopped.
|
178
|
+
|
179
|
+
Raises:
|
180
|
+
ImportError: If FastMCP is not available.
|
181
|
+
Exception: If server fails to start.
|
182
|
+
"""
|
183
|
+
if self._mcp is None:
|
184
|
+
self._init_mcp()
|
185
|
+
|
186
|
+
# Run setup to register tools/resources
|
187
|
+
self.setup()
|
188
|
+
|
189
|
+
logger.info(f"Starting MCP server '{self.name}' on {self.host}:{self.port}")
|
190
|
+
self._running = True
|
191
|
+
|
192
|
+
try:
|
193
|
+
# Run the FastMCP server
|
194
|
+
logger.info("Running FastMCP server in stdio mode")
|
195
|
+
self._mcp.run()
|
196
|
+
except Exception as e:
|
197
|
+
logger.error(f"Failed to start server: {e}")
|
198
|
+
raise
|
199
|
+
finally:
|
200
|
+
self._running = False
|
201
|
+
|
202
|
+
def stop(self):
|
203
|
+
"""Stop the MCP server."""
|
204
|
+
logger.info(f"Stopping MCP server '{self.name}'")
|
205
|
+
self._running = False
|
206
|
+
# In a real implementation, we'd need to handle graceful shutdown
|
207
|
+
|
208
|
+
|
22
209
|
class EnhancedMCPServer:
|
23
210
|
"""
|
24
|
-
Production-ready MCP server
|
211
|
+
Production-ready MCP server (available as SimpleMCPServer).
|
25
212
|
|
26
|
-
|
27
|
-
|
213
|
+
This is the main concrete MCP server implementation with all production
|
214
|
+
features available. Features can be enabled/disabled as needed.
|
215
|
+
|
216
|
+
Features available:
|
217
|
+
- Caching with TTL support (enable_cache=True)
|
218
|
+
- Metrics collection and monitoring (enable_metrics=True)
|
219
|
+
- Response formatting utilities (enable_formatting=True)
|
28
220
|
- Hierarchical configuration management
|
29
|
-
- Metrics collection and monitoring
|
30
|
-
- Response formatting utilities
|
31
221
|
- Error handling and logging
|
32
222
|
|
33
|
-
|
223
|
+
Examples:
|
224
|
+
Basic usage (recommended):
|
225
|
+
>>> from kailash.mcp_server import MCPServer
|
226
|
+
>>> server = MCPServer("my-server")
|
227
|
+
>>> @server.tool()
|
228
|
+
... def search(query: str) -> dict:
|
229
|
+
... return {"results": f"Found: {query}"}
|
230
|
+
>>> server.run()
|
231
|
+
|
232
|
+
With production features enabled:
|
233
|
+
>>> server = MCPServer("my-server", enable_cache=True, enable_metrics=True)
|
234
|
+
>>> @server.tool(cache_key="search", cache_ttl=600)
|
235
|
+
... def search(query: str) -> dict:
|
236
|
+
... return {"results": f"Found: {query}"}
|
237
|
+
>>> server.run()
|
34
238
|
"""
|
35
239
|
|
36
240
|
def __init__(
|
@@ -104,15 +308,21 @@ class EnhancedMCPServer:
|
|
104
308
|
return
|
105
309
|
|
106
310
|
try:
|
107
|
-
from mcp.server
|
311
|
+
# Now we can safely import from external mcp.server (no namespace collision)
|
312
|
+
from mcp.server import FastMCP
|
108
313
|
|
109
314
|
self._mcp = FastMCP(self.name)
|
110
315
|
logger.info(f"Initialized FastMCP server: {self.name}")
|
111
|
-
except ImportError:
|
316
|
+
except ImportError as e:
|
317
|
+
logger.error(
|
318
|
+
f"FastMCP import failed with: {e}. Details: {type(e).__name__}"
|
319
|
+
)
|
112
320
|
logger.error(
|
113
321
|
"FastMCP not available. Install with: pip install 'mcp[server]'"
|
114
322
|
)
|
115
|
-
raise
|
323
|
+
raise ImportError(
|
324
|
+
"FastMCP not available. Install with: pip install 'mcp[server]'"
|
325
|
+
) from e
|
116
326
|
|
117
327
|
def tool(
|
118
328
|
self,
|
@@ -413,37 +623,10 @@ class EnhancedMCPServer:
|
|
413
623
|
self._running = False
|
414
624
|
|
415
625
|
|
416
|
-
#
|
626
|
+
# Clean public API design:
|
627
|
+
# - MCPServerBase: Abstract base for custom implementations (e.g., AIRegistryServer)
|
628
|
+
# - MCPServer: Main concrete server with all production features
|
629
|
+
# - SimpleMCPServer: Alias for backward compatibility
|
630
|
+
# - EnhancedMCPServer: Alias for backward compatibility
|
417
631
|
MCPServer = EnhancedMCPServer
|
418
|
-
|
419
|
-
|
420
|
-
class SimpleMCPServer(EnhancedMCPServer):
|
421
|
-
"""
|
422
|
-
Simplified MCP server with minimal configuration.
|
423
|
-
|
424
|
-
This inherits all enhanced capabilities but disables some features
|
425
|
-
by default for simpler use cases.
|
426
|
-
"""
|
427
|
-
|
428
|
-
def __init__(self, name: str, description: str = ""):
|
429
|
-
"""
|
430
|
-
Initialize simple MCP server.
|
431
|
-
|
432
|
-
Args:
|
433
|
-
name: Server name
|
434
|
-
description: Server description
|
435
|
-
"""
|
436
|
-
# Initialize with some features disabled for simplicity
|
437
|
-
super().__init__(
|
438
|
-
name=name,
|
439
|
-
enable_cache=False, # Disable cache by default
|
440
|
-
enable_metrics=False, # Disable metrics by default
|
441
|
-
enable_formatting=True, # Keep formatting for better output
|
442
|
-
)
|
443
|
-
|
444
|
-
self.description = description
|
445
|
-
|
446
|
-
# Update config for simple use
|
447
|
-
self.config.update(
|
448
|
-
{"server": {"name": name, "description": description, "version": "1.0.0"}}
|
449
|
-
)
|
632
|
+
SimpleMCPServer = EnhancedMCPServer
|
@@ -9,10 +9,10 @@ import os
|
|
9
9
|
from pathlib import Path
|
10
10
|
from typing import Any
|
11
11
|
|
12
|
-
from kailash.
|
12
|
+
from kailash.mcp_server.server import MCPServerBase
|
13
13
|
|
14
14
|
|
15
|
-
class AIRegistryServer(
|
15
|
+
class AIRegistryServer(MCPServerBase):
|
16
16
|
"""MCP server for AI use case registry.
|
17
17
|
|
18
18
|
Provides tools and resources for exploring AI use cases from
|
@@ -10,12 +10,7 @@ This module provides production-ready utilities for MCP servers including:
|
|
10
10
|
|
11
11
|
from .cache import CacheManager, LRUCache, cached_query
|
12
12
|
from .config import ConfigManager
|
13
|
-
from .formatters import
|
14
|
-
format_response,
|
15
|
-
json_formatter,
|
16
|
-
markdown_formatter,
|
17
|
-
search_formatter,
|
18
|
-
)
|
13
|
+
from .formatters import format_response, json_formatter, markdown_formatter, search_formatter
|
19
14
|
from .metrics import MetricsCollector
|
20
15
|
|
21
16
|
__all__ = [
|
@@ -60,11 +60,11 @@ class MiddlewareAccessControlManager:
|
|
60
60
|
self.enable_audit = enable_audit
|
61
61
|
|
62
62
|
# Kailash nodes for operations
|
63
|
-
self.user_mgmt_node = UserManagementNode(
|
64
|
-
self.role_mgmt_node = RoleManagementNode(
|
65
|
-
self.permission_check_node = PermissionCheckNode(
|
66
|
-
self.audit_node = AuditLogNode(
|
67
|
-
self.security_event_node = SecurityEventNode(
|
63
|
+
self.user_mgmt_node = UserManagementNode()
|
64
|
+
self.role_mgmt_node = RoleManagementNode()
|
65
|
+
self.permission_check_node = PermissionCheckNode()
|
66
|
+
self.audit_node = AuditLogNode() if enable_audit else None
|
67
|
+
self.security_event_node = SecurityEventNode()
|
68
68
|
|
69
69
|
async def check_session_access(
|
70
70
|
self, user_context: UserContext, session_id: str, action: str = "access"
|
@@ -175,8 +175,32 @@ class CheckpointManager:
|
|
175
175
|
compression_enabled: bool = True,
|
176
176
|
compression_threshold_bytes: int = 1024, # 1KB
|
177
177
|
retention_hours: int = 24,
|
178
|
+
# Backward compatibility parameter
|
179
|
+
storage: Optional[DiskStorage] = None,
|
178
180
|
):
|
179
|
-
"""Initialize checkpoint manager.
|
181
|
+
"""Initialize checkpoint manager.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
memory_storage: Memory storage backend (optional)
|
185
|
+
disk_storage: Disk storage backend (optional)
|
186
|
+
cloud_storage: Cloud storage backend (optional)
|
187
|
+
compression_enabled: Enable compression for large checkpoints
|
188
|
+
compression_threshold_bytes: Minimum size for compression
|
189
|
+
retention_hours: Hours to retain checkpoints
|
190
|
+
storage: DEPRECATED - Use disk_storage instead
|
191
|
+
"""
|
192
|
+
# Handle backward compatibility
|
193
|
+
if storage is not None:
|
194
|
+
import warnings
|
195
|
+
|
196
|
+
warnings.warn(
|
197
|
+
"The 'storage' parameter is deprecated. Use 'disk_storage' instead.",
|
198
|
+
DeprecationWarning,
|
199
|
+
stacklevel=2,
|
200
|
+
)
|
201
|
+
if disk_storage is None:
|
202
|
+
disk_storage = storage
|
203
|
+
|
180
204
|
self.memory_storage = memory_storage or MemoryStorage()
|
181
205
|
self.disk_storage = disk_storage or DiskStorage()
|
182
206
|
self.cloud_storage = cloud_storage # Optional cloud backend
|
@@ -189,11 +213,23 @@ class CheckpointManager:
|
|
189
213
|
self.load_count = 0
|
190
214
|
self.compression_ratio_sum = 0.0
|
191
215
|
|
192
|
-
#
|
193
|
-
self._gc_task =
|
216
|
+
# Initialize garbage collection task (will be started when first used)
|
217
|
+
self._gc_task = None
|
218
|
+
self._gc_started = False
|
219
|
+
|
220
|
+
def _ensure_gc_started(self):
|
221
|
+
"""Ensure garbage collection task is started (lazy initialization)."""
|
222
|
+
if not self._gc_started:
|
223
|
+
try:
|
224
|
+
self._gc_task = asyncio.create_task(self._garbage_collection_loop())
|
225
|
+
self._gc_started = True
|
226
|
+
except RuntimeError:
|
227
|
+
# No event loop running, GC will be started later
|
228
|
+
pass
|
194
229
|
|
195
230
|
async def save_checkpoint(self, checkpoint: Checkpoint) -> None:
|
196
231
|
"""Save checkpoint to storage."""
|
232
|
+
self._ensure_gc_started()
|
197
233
|
start_time = time.time()
|
198
234
|
|
199
235
|
# Serialize checkpoint
|
@@ -391,8 +427,9 @@ class CheckpointManager:
|
|
391
427
|
|
392
428
|
async def close(self) -> None:
|
393
429
|
"""Close checkpoint manager and cleanup."""
|
394
|
-
self._gc_task
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
430
|
+
if self._gc_task is not None:
|
431
|
+
self._gc_task.cancel()
|
432
|
+
try:
|
433
|
+
await self._gc_task
|
434
|
+
except asyncio.CancelledError:
|
435
|
+
pass
|
@@ -24,8 +24,8 @@ from kailash.workflow.builder import WorkflowBuilder
|
|
24
24
|
|
25
25
|
# Import existing Kailash MCP components
|
26
26
|
try:
|
27
|
-
from kailash.
|
28
|
-
from kailash.
|
27
|
+
from kailash.mcp_server import MCPServer, SimpleMCPServer
|
28
|
+
from kailash.mcp_server.utils import CacheManager, ConfigManager, MetricsCollector
|
29
29
|
|
30
30
|
_KAILASH_MCP_AVAILABLE = True
|
31
31
|
except ImportError:
|