napistu 0.2.5.dev6__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.
- napistu/__main__.py +126 -96
- napistu/constants.py +35 -41
- napistu/context/__init__.py +10 -0
- napistu/context/discretize.py +462 -0
- napistu/context/filtering.py +387 -0
- napistu/gcs/__init__.py +1 -1
- napistu/identifiers.py +74 -15
- napistu/indices.py +68 -0
- napistu/ingestion/__init__.py +1 -1
- napistu/ingestion/bigg.py +47 -62
- napistu/ingestion/constants.py +18 -133
- napistu/ingestion/gtex.py +113 -0
- napistu/ingestion/hpa.py +147 -0
- napistu/ingestion/sbml.py +0 -97
- napistu/ingestion/string.py +2 -2
- napistu/matching/__init__.py +10 -0
- napistu/matching/constants.py +18 -0
- napistu/matching/interactions.py +518 -0
- napistu/matching/mount.py +529 -0
- napistu/matching/species.py +510 -0
- napistu/mcp/__init__.py +7 -4
- napistu/mcp/__main__.py +128 -72
- napistu/mcp/client.py +16 -25
- napistu/mcp/codebase.py +201 -153
- napistu/mcp/component_base.py +170 -0
- napistu/mcp/config.py +223 -0
- napistu/mcp/constants.py +45 -2
- napistu/mcp/documentation.py +253 -136
- napistu/mcp/documentation_utils.py +13 -48
- napistu/mcp/execution.py +372 -305
- napistu/mcp/health.py +49 -67
- napistu/mcp/profiles.py +10 -6
- napistu/mcp/server.py +161 -80
- napistu/mcp/tutorials.py +139 -87
- napistu/modify/__init__.py +1 -1
- napistu/modify/gaps.py +1 -1
- napistu/network/__init__.py +1 -1
- napistu/network/constants.py +101 -34
- napistu/network/data_handling.py +388 -0
- napistu/network/ig_utils.py +351 -0
- napistu/network/napistu_graph_core.py +354 -0
- napistu/network/neighborhoods.py +40 -40
- napistu/network/net_create.py +373 -309
- napistu/network/net_propagation.py +47 -19
- napistu/network/{net_utils.py → ng_utils.py} +124 -272
- napistu/network/paths.py +67 -51
- napistu/network/precompute.py +11 -11
- napistu/ontologies/__init__.py +10 -0
- napistu/ontologies/constants.py +129 -0
- napistu/ontologies/dogma.py +243 -0
- napistu/ontologies/genodexito.py +649 -0
- napistu/ontologies/mygene.py +369 -0
- napistu/ontologies/renaming.py +198 -0
- napistu/rpy2/__init__.py +229 -86
- napistu/rpy2/callr.py +47 -77
- napistu/rpy2/constants.py +24 -23
- napistu/rpy2/rids.py +61 -648
- napistu/sbml_dfs_core.py +587 -222
- napistu/scverse/__init__.py +15 -0
- napistu/scverse/constants.py +28 -0
- napistu/scverse/loading.py +727 -0
- napistu/utils.py +118 -10
- {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/METADATA +8 -3
- napistu-0.3.1.dist-info/RECORD +133 -0
- tests/conftest.py +22 -0
- tests/test_context_discretize.py +56 -0
- tests/test_context_filtering.py +267 -0
- tests/test_identifiers.py +100 -0
- tests/test_indices.py +65 -0
- tests/{test_edgelist.py → test_ingestion_napistu_edgelist.py} +2 -2
- tests/test_matching_interactions.py +108 -0
- tests/test_matching_mount.py +305 -0
- tests/test_matching_species.py +394 -0
- tests/test_mcp_config.py +193 -0
- tests/test_mcp_documentation_utils.py +12 -3
- tests/test_mcp_server.py +356 -0
- tests/test_network_data_handling.py +397 -0
- tests/test_network_ig_utils.py +23 -0
- tests/test_network_neighborhoods.py +19 -0
- tests/test_network_net_create.py +459 -0
- tests/test_network_ng_utils.py +30 -0
- tests/test_network_paths.py +56 -0
- tests/{test_precomputed_distances.py → test_network_precompute.py} +8 -6
- tests/test_ontologies_genodexito.py +58 -0
- tests/test_ontologies_mygene.py +39 -0
- tests/test_ontologies_renaming.py +110 -0
- tests/test_rpy2_callr.py +79 -0
- tests/test_rpy2_init.py +151 -0
- tests/test_sbml.py +0 -31
- tests/test_sbml_dfs_core.py +134 -10
- tests/test_scverse_loading.py +778 -0
- tests/test_set_coverage.py +2 -2
- tests/test_utils.py +121 -1
- napistu/mechanism_matching.py +0 -1353
- napistu/rpy2/netcontextr.py +0 -467
- napistu-0.2.5.dev6.dist-info/RECORD +0 -97
- tests/test_igraph.py +0 -367
- tests/test_mechanism_matching.py +0 -784
- tests/test_net_utils.py +0 -149
- tests/test_netcontextr.py +0 -105
- tests/test_rpy2.py +0 -61
- /napistu/ingestion/{cpr_edgelist.py → napistu_edgelist.py} +0 -0
- {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/WHEEL +0 -0
- {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/entry_points.txt +0 -0
- {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/top_level.txt +0 -0
- /tests/{test_obo.py → test_ingestion_obo.py} +0 -0
napistu/mcp/execution.py
CHANGED
@@ -4,379 +4,446 @@ Function execution components for the Napistu MCP server.
|
|
4
4
|
|
5
5
|
from typing import Dict, List, Any, Optional
|
6
6
|
import inspect
|
7
|
+
import logging
|
7
8
|
|
8
|
-
|
9
|
-
_session_context = {}
|
10
|
-
_session_objects = {}
|
9
|
+
from fastmcp import FastMCP
|
11
10
|
|
11
|
+
from napistu.mcp.component_base import ComponentState, MCPComponent
|
12
12
|
|
13
|
-
|
14
|
-
"""
|
15
|
-
Initialize execution components.
|
16
|
-
|
17
|
-
Returns
|
18
|
-
-------
|
19
|
-
bool
|
20
|
-
True if initialization is successful.
|
21
|
-
"""
|
22
|
-
global _session_context, _session_objects
|
23
|
-
import napistu
|
24
|
-
|
25
|
-
_session_context["napistu"] = napistu
|
26
|
-
return True
|
13
|
+
logger = logging.getLogger(__name__)
|
27
14
|
|
28
15
|
|
29
|
-
|
30
|
-
"""
|
31
|
-
Register an object with the execution component.
|
16
|
+
class ExecutionState(ComponentState):
|
17
|
+
"""State management for execution component."""
|
32
18
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
session_context: Optional[Dict] = None,
|
22
|
+
object_registry: Optional[Dict] = None,
|
23
|
+
):
|
24
|
+
super().__init__()
|
25
|
+
# Session context contains global functions and modules
|
26
|
+
self.session_context = session_context or {}
|
27
|
+
# Object registry contains user-registered objects
|
28
|
+
self.session_objects = object_registry or {}
|
40
29
|
|
30
|
+
def is_healthy(self) -> bool:
|
31
|
+
"""Component is healthy if it has a session context."""
|
32
|
+
return bool(self.session_context)
|
41
33
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
34
|
+
def get_health_details(self) -> Dict[str, Any]:
|
35
|
+
"""Provide execution-specific health details."""
|
36
|
+
return {
|
37
|
+
"session_context_items": len(self.session_context),
|
38
|
+
"registered_objects": len(self.session_objects),
|
39
|
+
"context_keys": list(self.session_context.keys()),
|
40
|
+
"object_names": list(self.session_objects.keys()),
|
41
|
+
}
|
45
42
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
"""
|
51
|
-
global _session_context, _session_objects
|
43
|
+
def register_object(self, name: str, obj: Any) -> None:
|
44
|
+
"""Register an object with the execution component."""
|
45
|
+
self.session_objects[name] = obj
|
46
|
+
logger.info(f"Registered object '{name}' with MCP server")
|
52
47
|
|
53
|
-
# Initialize context
|
54
|
-
if session_context:
|
55
|
-
_session_context = session_context
|
56
48
|
|
57
|
-
|
58
|
-
|
49
|
+
class ExecutionComponent(MCPComponent):
|
50
|
+
"""MCP component for function execution and object management."""
|
59
51
|
|
60
|
-
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
session_context: Optional[Dict] = None,
|
55
|
+
object_registry: Optional[Dict] = None,
|
56
|
+
):
|
57
|
+
# Override parent constructor to pass context to state
|
58
|
+
self.state = ExecutionState(session_context, object_registry)
|
61
59
|
|
62
|
-
|
60
|
+
def _create_state(self) -> ExecutionState:
|
61
|
+
"""This won't be called due to overridden constructor."""
|
62
|
+
pass
|
63
63
|
|
64
|
-
|
65
|
-
@mcp.resource("napistu-local://registry")
|
66
|
-
async def get_registry_summary() -> Dict[str, Any]:
|
67
|
-
"""
|
68
|
-
Get a summary of all objects registered with the server.
|
64
|
+
async def initialize(self) -> bool:
|
69
65
|
"""
|
70
|
-
|
71
|
-
"object_count": len(_session_objects),
|
72
|
-
"object_names": list(_session_objects.keys()),
|
73
|
-
"object_types": {
|
74
|
-
name: type(obj).__name__ for name, obj in _session_objects.items()
|
75
|
-
},
|
76
|
-
}
|
66
|
+
Initialize execution component by setting up the session context.
|
77
67
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
bool
|
71
|
+
True if initialization successful
|
82
72
|
"""
|
83
73
|
try:
|
74
|
+
# Import and add napistu to session context
|
84
75
|
import napistu
|
85
76
|
|
86
|
-
|
87
|
-
except ImportError:
|
88
|
-
napistu_version = "not installed"
|
77
|
+
self.state.session_context["napistu"] = napistu
|
89
78
|
|
90
|
-
|
79
|
+
logger.info("Execution component initialized with napistu module")
|
80
|
+
return True
|
91
81
|
|
92
|
-
|
93
|
-
"
|
94
|
-
|
95
|
-
"platform": sys.platform,
|
96
|
-
"registered_objects": list(_session_objects.keys()),
|
97
|
-
}
|
82
|
+
except ImportError as e:
|
83
|
+
logger.error(f"Failed to import napistu module: {e}")
|
84
|
+
return False
|
98
85
|
|
99
|
-
|
100
|
-
@mcp.tool()
|
101
|
-
async def list_registry() -> Dict[str, Any]:
|
86
|
+
def register_object(self, name: str, obj: Any) -> None:
|
102
87
|
"""
|
103
|
-
|
88
|
+
Register an object with the execution component.
|
104
89
|
|
105
|
-
|
106
|
-
|
90
|
+
Args:
|
91
|
+
name: Name to reference the object by
|
92
|
+
obj: The object to register
|
107
93
|
"""
|
108
|
-
|
94
|
+
self.state.register_object(name, obj)
|
109
95
|
|
110
|
-
|
111
|
-
|
96
|
+
def register(self, mcp: FastMCP) -> None:
|
97
|
+
"""
|
98
|
+
Register execution resources and tools with the MCP server.
|
112
99
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
}
|
119
|
-
elif hasattr(obj, "__len__"): # For lists, dicts, etc.
|
120
|
-
obj_info = {
|
121
|
-
"type": obj_type,
|
122
|
-
"length": len(obj),
|
123
|
-
}
|
124
|
-
else:
|
125
|
-
obj_info = {
|
126
|
-
"type": obj_type,
|
127
|
-
}
|
100
|
+
Parameters
|
101
|
+
----------
|
102
|
+
mcp : FastMCP
|
103
|
+
FastMCP server instance
|
104
|
+
"""
|
128
105
|
|
129
|
-
|
106
|
+
# Register resources
|
107
|
+
@mcp.resource("napistu://execution/registry")
|
108
|
+
async def get_registry():
|
109
|
+
"""Get a summary of all objects registered with the server."""
|
110
|
+
return {
|
111
|
+
"object_count": len(self.state.session_objects),
|
112
|
+
"object_names": list(self.state.session_objects.keys()),
|
113
|
+
"object_types": {
|
114
|
+
name: type(obj).__name__
|
115
|
+
for name, obj in self.state.session_objects.items()
|
116
|
+
},
|
117
|
+
}
|
130
118
|
|
131
|
-
|
119
|
+
@mcp.resource("napistu://execution/environment")
|
120
|
+
async def get_environment_info() -> Dict[str, Any]:
|
121
|
+
"""Get information about the local Python environment."""
|
122
|
+
try:
|
123
|
+
import napistu
|
132
124
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
Get detailed information about a registered object.
|
125
|
+
napistu_version = getattr(napistu, "__version__", "unknown")
|
126
|
+
except ImportError:
|
127
|
+
napistu_version = "not installed"
|
137
128
|
|
138
|
-
|
139
|
-
object_name: Name of the registered object
|
129
|
+
import sys
|
140
130
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
obj_type = type(obj).__name__
|
149
|
-
|
150
|
-
# Basic info for all objects
|
151
|
-
result = {
|
152
|
-
"name": object_name,
|
153
|
-
"type": obj_type,
|
154
|
-
"methods": [],
|
155
|
-
"attributes": [],
|
156
|
-
}
|
131
|
+
return {
|
132
|
+
"python_version": sys.version,
|
133
|
+
"napistu_version": napistu_version,
|
134
|
+
"platform": sys.platform,
|
135
|
+
"registered_objects": list(self.state.session_objects.keys()),
|
136
|
+
"session_context": list(self.state.session_context.keys()),
|
137
|
+
}
|
157
138
|
|
158
|
-
#
|
159
|
-
|
160
|
-
|
161
|
-
|
139
|
+
# Register tools
|
140
|
+
@mcp.tool()
|
141
|
+
async def list_registry() -> Dict[str, Any]:
|
142
|
+
"""List all objects registered with the server."""
|
143
|
+
result = {}
|
162
144
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
145
|
+
for name, obj in self.state.session_objects.items():
|
146
|
+
obj_type = type(obj).__name__
|
147
|
+
|
148
|
+
# Get additional info based on object type
|
149
|
+
if hasattr(obj, "shape"): # For pandas DataFrame or numpy array
|
150
|
+
obj_info = {
|
151
|
+
"type": obj_type,
|
152
|
+
"shape": str(obj.shape),
|
153
|
+
}
|
154
|
+
elif hasattr(obj, "__len__"): # For lists, dicts, etc.
|
155
|
+
obj_info = {
|
156
|
+
"type": obj_type,
|
157
|
+
"length": len(obj),
|
158
|
+
}
|
177
159
|
else:
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
{
|
182
|
-
"name": name,
|
183
|
-
"type": attr_type,
|
184
|
-
}
|
185
|
-
)
|
186
|
-
except Exception:
|
187
|
-
# Skip attributes that can't be accessed
|
188
|
-
pass
|
189
|
-
|
190
|
-
return result
|
191
|
-
|
192
|
-
@mcp.tool()
|
193
|
-
async def execute_function(
|
194
|
-
function_name: str,
|
195
|
-
object_name: Optional[str] = None,
|
196
|
-
args: Optional[List] = None,
|
197
|
-
kwargs: Optional[Dict] = None,
|
198
|
-
) -> Dict[str, Any]:
|
199
|
-
"""
|
200
|
-
Execute a Napistu function on a registered object.
|
160
|
+
obj_info = {
|
161
|
+
"type": obj_type,
|
162
|
+
}
|
201
163
|
|
202
|
-
|
203
|
-
function_name: Name of the function to execute
|
204
|
-
object_name: Name of the registered object to operate on (if method call)
|
205
|
-
args: Positional arguments to pass to the function
|
206
|
-
kwargs: Keyword arguments to pass to the function
|
164
|
+
result[name] = obj_info
|
207
165
|
|
208
|
-
|
209
|
-
Dictionary with execution results
|
210
|
-
"""
|
211
|
-
args = args or []
|
212
|
-
kwargs = kwargs or {}
|
166
|
+
return result
|
213
167
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
168
|
+
@mcp.tool()
|
169
|
+
async def describe_object(object_name: str) -> Dict[str, Any]:
|
170
|
+
"""Get detailed information about a registered object."""
|
171
|
+
if object_name not in self.state.session_objects:
|
172
|
+
return {"error": f"Object '{object_name}' not found in registry"}
|
219
173
|
|
220
|
-
|
174
|
+
obj = self.state.session_objects[object_name]
|
175
|
+
obj_type = type(obj).__name__
|
221
176
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
177
|
+
# Basic info for all objects
|
178
|
+
result = {
|
179
|
+
"name": object_name,
|
180
|
+
"type": obj_type,
|
181
|
+
"methods": [],
|
182
|
+
"attributes": [],
|
183
|
+
}
|
184
|
+
|
185
|
+
# Add methods and attributes
|
186
|
+
for name in dir(obj):
|
187
|
+
if name.startswith("_"):
|
188
|
+
continue
|
189
|
+
|
190
|
+
try:
|
191
|
+
attr = getattr(obj, name)
|
192
|
+
|
193
|
+
if callable(attr):
|
194
|
+
# Method
|
195
|
+
sig = str(inspect.signature(attr))
|
196
|
+
doc = inspect.getdoc(attr) or ""
|
197
|
+
result["methods"].append(
|
198
|
+
{
|
199
|
+
"name": name,
|
200
|
+
"signature": sig,
|
201
|
+
"docstring": doc,
|
202
|
+
}
|
203
|
+
)
|
204
|
+
else:
|
205
|
+
# Attribute
|
206
|
+
attr_type = type(attr).__name__
|
207
|
+
result["attributes"].append(
|
208
|
+
{
|
209
|
+
"name": name,
|
210
|
+
"type": attr_type,
|
211
|
+
}
|
212
|
+
)
|
213
|
+
except Exception:
|
214
|
+
# Skip attributes that can't be accessed
|
215
|
+
pass
|
216
|
+
|
217
|
+
return result
|
218
|
+
|
219
|
+
@mcp.tool()
|
220
|
+
async def execute_function(
|
221
|
+
function_name: str,
|
222
|
+
object_name: Optional[str] = None,
|
223
|
+
args: Optional[List] = None,
|
224
|
+
kwargs: Optional[Dict] = None,
|
225
|
+
) -> Dict[str, Any]:
|
226
|
+
"""Execute a Napistu function on a registered object."""
|
227
|
+
args = args or []
|
228
|
+
kwargs = kwargs or {}
|
229
|
+
|
230
|
+
try:
|
231
|
+
if object_name:
|
232
|
+
# Method call on an object
|
233
|
+
if object_name not in self.state.session_objects:
|
234
|
+
return {
|
235
|
+
"error": f"Object '{object_name}' not found in registry"
|
236
|
+
}
|
237
|
+
|
238
|
+
obj = self.state.session_objects[object_name]
|
239
|
+
|
240
|
+
if not hasattr(obj, function_name):
|
241
|
+
return {
|
242
|
+
"error": f"Method '{function_name}' not found on object '{object_name}'"
|
243
|
+
}
|
226
244
|
|
227
|
-
|
228
|
-
result = func(*args, **kwargs)
|
229
|
-
else:
|
230
|
-
# Global function call
|
231
|
-
if function_name in _session_context:
|
232
|
-
# Function from session context
|
233
|
-
func = _session_context[function_name]
|
245
|
+
func = getattr(obj, function_name)
|
234
246
|
result = func(*args, **kwargs)
|
235
247
|
else:
|
236
|
-
#
|
237
|
-
|
238
|
-
|
248
|
+
# Global function call
|
249
|
+
if function_name in self.state.session_context:
|
250
|
+
# Function from session context
|
251
|
+
func = self.state.session_context[function_name]
|
252
|
+
result = func(*args, **kwargs)
|
253
|
+
else:
|
254
|
+
# Try to find the function in Napistu
|
255
|
+
try:
|
256
|
+
import napistu
|
257
|
+
|
258
|
+
# Split function name by dots for nested modules
|
259
|
+
parts = function_name.split(".")
|
260
|
+
current = napistu
|
261
|
+
|
262
|
+
for part in parts[:-1]:
|
263
|
+
current = getattr(current, part)
|
264
|
+
|
265
|
+
func = getattr(current, parts[-1])
|
266
|
+
result = func(*args, **kwargs)
|
267
|
+
except (ImportError, AttributeError):
|
268
|
+
return {"error": f"Function '{function_name}' not found"}
|
269
|
+
|
270
|
+
# Register result if it's a return value
|
271
|
+
if result is not None:
|
272
|
+
result_name = f"result_{len(self.state.session_objects) + 1}"
|
273
|
+
self.state.session_objects[result_name] = result
|
274
|
+
|
275
|
+
# Basic type conversion for JSON serialization
|
276
|
+
if hasattr(result, "to_dict"):
|
277
|
+
# For pandas DataFrame or similar
|
278
|
+
return {
|
279
|
+
"success": True,
|
280
|
+
"result_name": result_name,
|
281
|
+
"result_type": type(result).__name__,
|
282
|
+
"result_preview": (
|
283
|
+
result.to_dict()
|
284
|
+
if hasattr(result, "__len__") and len(result) < 10
|
285
|
+
else "Result too large to preview"
|
286
|
+
),
|
287
|
+
}
|
288
|
+
elif hasattr(result, "to_json"):
|
289
|
+
# For objects with JSON serialization
|
290
|
+
return {
|
291
|
+
"success": True,
|
292
|
+
"result_name": result_name,
|
293
|
+
"result_type": type(result).__name__,
|
294
|
+
"result_preview": result.to_json(),
|
295
|
+
}
|
296
|
+
elif hasattr(result, "__dict__"):
|
297
|
+
# For custom objects
|
298
|
+
return {
|
299
|
+
"success": True,
|
300
|
+
"result_name": result_name,
|
301
|
+
"result_type": type(result).__name__,
|
302
|
+
"result_preview": str(result),
|
303
|
+
}
|
304
|
+
else:
|
305
|
+
# For simple types
|
306
|
+
return {
|
307
|
+
"success": True,
|
308
|
+
"result_name": result_name,
|
309
|
+
"result_type": type(result).__name__,
|
310
|
+
"result_preview": str(result),
|
311
|
+
}
|
312
|
+
else:
|
313
|
+
return {
|
314
|
+
"success": True,
|
315
|
+
"result": None,
|
316
|
+
}
|
317
|
+
except Exception as e:
|
318
|
+
import traceback
|
319
|
+
|
320
|
+
return {
|
321
|
+
"error": str(e),
|
322
|
+
"traceback": traceback.format_exc(),
|
323
|
+
}
|
239
324
|
|
240
|
-
|
241
|
-
|
242
|
-
|
325
|
+
@mcp.tool()
|
326
|
+
async def search_paths(
|
327
|
+
source_node: str,
|
328
|
+
target_node: str,
|
329
|
+
network_object: str,
|
330
|
+
max_depth: int = 3,
|
331
|
+
) -> Dict[str, Any]:
|
332
|
+
"""Find paths between two nodes in a network."""
|
333
|
+
if network_object not in self.state.session_objects:
|
334
|
+
return {
|
335
|
+
"error": f"Network object '{network_object}' not found in registry"
|
336
|
+
}
|
243
337
|
|
244
|
-
|
245
|
-
current = getattr(current, part)
|
338
|
+
network = self.state.session_objects[network_object]
|
246
339
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
340
|
+
try:
|
341
|
+
# Import necessary modules
|
342
|
+
import napistu
|
343
|
+
|
344
|
+
# Check if the object is a valid network type
|
345
|
+
if hasattr(network, "find_paths"):
|
346
|
+
# Direct method call
|
347
|
+
paths = network.find_paths(
|
348
|
+
source_node, target_node, max_depth=max_depth
|
349
|
+
)
|
350
|
+
elif hasattr(napistu.graph, "find_paths"):
|
351
|
+
# Function call
|
352
|
+
paths = napistu.graph.find_paths(
|
353
|
+
network, source_node, target_node, max_depth=max_depth
|
354
|
+
)
|
355
|
+
else:
|
356
|
+
return {"error": "Could not find appropriate path-finding function"}
|
251
357
|
|
252
|
-
|
253
|
-
|
254
|
-
result_name =
|
255
|
-
_session_objects[result_name] = result
|
358
|
+
# Register result
|
359
|
+
result_name = f"paths_{len(self.state.session_objects) + 1}"
|
360
|
+
self.state.session_objects[result_name] = paths
|
256
361
|
|
257
|
-
#
|
258
|
-
if hasattr(
|
259
|
-
# For pandas DataFrame or similar
|
362
|
+
# Return results
|
363
|
+
if hasattr(paths, "to_dict"):
|
260
364
|
return {
|
261
365
|
"success": True,
|
262
366
|
"result_name": result_name,
|
263
|
-
"
|
367
|
+
"paths_found": (
|
368
|
+
len(paths) if hasattr(paths, "__len__") else "unknown"
|
369
|
+
),
|
264
370
|
"result_preview": (
|
265
|
-
|
266
|
-
if hasattr(
|
371
|
+
paths.to_dict()
|
372
|
+
if hasattr(paths, "__len__") and len(paths) < 10
|
267
373
|
else "Result too large to preview"
|
268
374
|
),
|
269
375
|
}
|
270
|
-
elif hasattr(result, "to_json"):
|
271
|
-
# For objects with JSON serialization
|
272
|
-
return {
|
273
|
-
"success": True,
|
274
|
-
"result_name": result_name,
|
275
|
-
"result_type": type(result).__name__,
|
276
|
-
"result_preview": result.to_json(),
|
277
|
-
}
|
278
|
-
elif hasattr(result, "__dict__"):
|
279
|
-
# For custom objects
|
280
|
-
return {
|
281
|
-
"success": True,
|
282
|
-
"result_name": result_name,
|
283
|
-
"result_type": type(result).__name__,
|
284
|
-
"result_preview": str(result),
|
285
|
-
}
|
286
376
|
else:
|
287
|
-
# For simple types
|
288
377
|
return {
|
289
378
|
"success": True,
|
290
379
|
"result_name": result_name,
|
291
|
-
"
|
292
|
-
|
380
|
+
"paths_found": (
|
381
|
+
len(paths) if hasattr(paths, "__len__") else "unknown"
|
382
|
+
),
|
383
|
+
"result_preview": str(paths),
|
293
384
|
}
|
294
|
-
|
385
|
+
except Exception as e:
|
386
|
+
import traceback
|
387
|
+
|
295
388
|
return {
|
296
|
-
"
|
297
|
-
"
|
389
|
+
"error": str(e),
|
390
|
+
"traceback": traceback.format_exc(),
|
298
391
|
}
|
299
|
-
except Exception as e:
|
300
|
-
import traceback
|
301
392
|
|
302
|
-
return {
|
303
|
-
"error": str(e),
|
304
|
-
"traceback": traceback.format_exc(),
|
305
|
-
}
|
306
393
|
|
307
|
-
|
308
|
-
|
309
|
-
source_node: str,
|
310
|
-
target_node: str,
|
311
|
-
network_object: str,
|
312
|
-
max_depth: int = 3,
|
313
|
-
) -> Dict[str, Any]:
|
314
|
-
"""
|
315
|
-
Find paths between two nodes in a network.
|
394
|
+
# Module-level component instance (will be created by server with proper context)
|
395
|
+
_component: Optional[ExecutionComponent] = None
|
316
396
|
|
317
|
-
Args:
|
318
|
-
source_node: Source node identifier
|
319
|
-
target_node: Target node identifier
|
320
|
-
network_object: Name of the registered network object
|
321
|
-
max_depth: Maximum path length
|
322
397
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
398
|
+
def create_component(
|
399
|
+
session_context: Optional[Dict] = None, object_registry: Optional[Dict] = None
|
400
|
+
) -> ExecutionComponent:
|
401
|
+
"""
|
402
|
+
Create and configure the execution component with session context.
|
328
403
|
|
329
|
-
|
404
|
+
Args:
|
405
|
+
session_context: Dictionary of the user's current session (e.g., globals())
|
406
|
+
object_registry: Dictionary of named objects to make available
|
330
407
|
|
331
|
-
|
332
|
-
|
333
|
-
|
408
|
+
Returns:
|
409
|
+
ExecutionComponent: Configured execution component
|
410
|
+
"""
|
411
|
+
global _component
|
412
|
+
_component = ExecutionComponent(session_context, object_registry)
|
413
|
+
return _component
|
334
414
|
|
335
|
-
# Check if the object is a valid network type
|
336
|
-
if hasattr(network, "find_paths"):
|
337
|
-
# Direct method call
|
338
|
-
paths = network.find_paths(
|
339
|
-
source_node, target_node, max_depth=max_depth
|
340
|
-
)
|
341
|
-
elif hasattr(napistu.graph, "find_paths"):
|
342
|
-
# Function call
|
343
|
-
paths = napistu.graph.find_paths(
|
344
|
-
network, source_node, target_node, max_depth=max_depth
|
345
|
-
)
|
346
|
-
else:
|
347
|
-
return {"error": "Could not find appropriate path-finding function"}
|
348
|
-
|
349
|
-
# Register result
|
350
|
-
result_name = f"paths_{len(_session_objects) + 1}"
|
351
|
-
_session_objects[result_name] = paths
|
352
|
-
|
353
|
-
# Return results
|
354
|
-
if hasattr(paths, "to_dict"):
|
355
|
-
return {
|
356
|
-
"success": True,
|
357
|
-
"result_name": result_name,
|
358
|
-
"paths_found": (
|
359
|
-
len(paths) if hasattr(paths, "__len__") else "unknown"
|
360
|
-
),
|
361
|
-
"result_preview": (
|
362
|
-
paths.to_dict()
|
363
|
-
if hasattr(paths, "__len__") and len(paths) < 10
|
364
|
-
else "Result too large to preview"
|
365
|
-
),
|
366
|
-
}
|
367
|
-
else:
|
368
|
-
return {
|
369
|
-
"success": True,
|
370
|
-
"result_name": result_name,
|
371
|
-
"paths_found": (
|
372
|
-
len(paths) if hasattr(paths, "__len__") else "unknown"
|
373
|
-
),
|
374
|
-
"result_preview": str(paths),
|
375
|
-
}
|
376
|
-
except Exception as e:
|
377
|
-
import traceback
|
378
415
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
416
|
+
def get_component() -> ExecutionComponent:
|
417
|
+
"""
|
418
|
+
Get the execution component instance.
|
419
|
+
|
420
|
+
Returns
|
421
|
+
-------
|
422
|
+
ExecutionComponent
|
423
|
+
The execution component instance
|
424
|
+
|
425
|
+
Raises
|
426
|
+
------
|
427
|
+
RuntimeError
|
428
|
+
If component hasn't been created yet
|
429
|
+
"""
|
430
|
+
if _component is None:
|
431
|
+
raise RuntimeError(
|
432
|
+
"Execution component not created. Call create_component() first."
|
433
|
+
)
|
434
|
+
return _component
|
435
|
+
|
436
|
+
|
437
|
+
def register_object(name: str, obj: Any) -> None:
|
438
|
+
"""
|
439
|
+
Register an object with the execution component (legacy function).
|
440
|
+
|
441
|
+
Args:
|
442
|
+
name: Name to reference the object by
|
443
|
+
obj: The object to register
|
444
|
+
"""
|
445
|
+
if _component is None:
|
446
|
+
raise RuntimeError(
|
447
|
+
"Execution component not created. Call create_component() first."
|
448
|
+
)
|
449
|
+
_component.register_object(name, obj)
|