rowan-mcp 1.0.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rowan-mcp might be problematic. Click here for more details.
- rowan_mcp/__init__.py +1 -1
- rowan_mcp/__main__.py +3 -5
- rowan_mcp/functions_v2/BENCHMARK.md +86 -0
- rowan_mcp/functions_v2/molecule_lookup.py +232 -0
- rowan_mcp/functions_v2/protein_management.py +141 -0
- rowan_mcp/functions_v2/submit_basic_calculation_workflow.py +195 -0
- rowan_mcp/functions_v2/submit_conformer_search_workflow.py +158 -0
- rowan_mcp/functions_v2/submit_descriptors_workflow.py +52 -0
- rowan_mcp/functions_v2/submit_docking_workflow.py +244 -0
- rowan_mcp/functions_v2/submit_fukui_workflow.py +114 -0
- rowan_mcp/functions_v2/submit_irc_workflow.py +58 -0
- rowan_mcp/functions_v2/submit_macropka_workflow.py +99 -0
- rowan_mcp/functions_v2/submit_pka_workflow.py +72 -0
- rowan_mcp/functions_v2/submit_protein_cofolding_workflow.py +88 -0
- rowan_mcp/functions_v2/submit_redox_potential_workflow.py +55 -0
- rowan_mcp/functions_v2/submit_scan_workflow.py +82 -0
- rowan_mcp/functions_v2/submit_solubility_workflow.py +157 -0
- rowan_mcp/functions_v2/submit_tautomer_search_workflow.py +51 -0
- rowan_mcp/functions_v2/workflow_management_v2.py +382 -0
- rowan_mcp/server.py +109 -144
- rowan_mcp/tests/basic_calculation_from_json.py +0 -0
- rowan_mcp/tests/basic_calculation_with_constraint.py +33 -0
- rowan_mcp/tests/basic_calculation_with_solvent.py +0 -0
- rowan_mcp/tests/bde.py +37 -0
- rowan_mcp/tests/benchmark_queries.md +120 -0
- rowan_mcp/tests/cofolding_screen.py +131 -0
- rowan_mcp/tests/conformer_dependent_redox.py +37 -0
- rowan_mcp/tests/conformers.py +31 -0
- rowan_mcp/tests/data.json +189 -0
- rowan_mcp/tests/docking_screen.py +157 -0
- rowan_mcp/tests/irc.py +24 -0
- rowan_mcp/tests/macropka.py +13 -0
- rowan_mcp/tests/multistage_opt.py +13 -0
- rowan_mcp/tests/optimization.py +21 -0
- rowan_mcp/tests/phenol_pka.py +36 -0
- rowan_mcp/tests/pka.py +36 -0
- rowan_mcp/tests/protein_cofolding.py +17 -0
- rowan_mcp/tests/scan.py +28 -0
- {rowan_mcp-1.0.2.dist-info → rowan_mcp-2.0.0.dist-info}/METADATA +41 -33
- rowan_mcp-2.0.0.dist-info/RECORD +42 -0
- rowan_mcp/functions/admet.py +0 -94
- rowan_mcp/functions/bde.py +0 -113
- rowan_mcp/functions/calculation_retrieve.py +0 -89
- rowan_mcp/functions/conformers.py +0 -80
- rowan_mcp/functions/descriptors.py +0 -92
- rowan_mcp/functions/docking.py +0 -340
- rowan_mcp/functions/docking_enhanced.py +0 -174
- rowan_mcp/functions/electronic_properties.py +0 -205
- rowan_mcp/functions/folder_management.py +0 -137
- rowan_mcp/functions/fukui.py +0 -219
- rowan_mcp/functions/hydrogen_bond_basicity.py +0 -94
- rowan_mcp/functions/irc.py +0 -125
- rowan_mcp/functions/macropka.py +0 -120
- rowan_mcp/functions/molecular_converter.py +0 -423
- rowan_mcp/functions/molecular_dynamics.py +0 -191
- rowan_mcp/functions/molecule_lookup.py +0 -57
- rowan_mcp/functions/multistage_opt.py +0 -171
- rowan_mcp/functions/pdb_handler.py +0 -200
- rowan_mcp/functions/pka.py +0 -88
- rowan_mcp/functions/redox_potential.py +0 -352
- rowan_mcp/functions/scan.py +0 -536
- rowan_mcp/functions/scan_analyzer.py +0 -347
- rowan_mcp/functions/solubility.py +0 -277
- rowan_mcp/functions/spin_states.py +0 -747
- rowan_mcp/functions/system_management.py +0 -368
- rowan_mcp/functions/tautomers.py +0 -91
- rowan_mcp/functions/workflow_management.py +0 -422
- rowan_mcp-1.0.2.dist-info/RECORD +0 -34
- {rowan_mcp-1.0.2.dist-info → rowan_mcp-2.0.0.dist-info}/WHEEL +0 -0
- {rowan_mcp-1.0.2.dist-info → rowan_mcp-2.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
System management operations for Rowan API.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
import sys
|
|
7
|
-
import rowan
|
|
8
|
-
import logging
|
|
9
|
-
from typing import Optional
|
|
10
|
-
|
|
11
|
-
# Set up logging
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
# Configure rowan API key
|
|
15
|
-
if not hasattr(rowan, 'api_key') or not rowan.api_key:
|
|
16
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
17
|
-
if api_key:
|
|
18
|
-
rowan.api_key = api_key
|
|
19
|
-
logger.info("Rowan API key configured")
|
|
20
|
-
else:
|
|
21
|
-
logger.error("No ROWAN_API_KEY found in environment")
|
|
22
|
-
|
|
23
|
-
def rowan_system_management(
|
|
24
|
-
action: str,
|
|
25
|
-
job_uuid: Optional[str] = None,
|
|
26
|
-
log_level: Optional[str] = None
|
|
27
|
-
) -> str:
|
|
28
|
-
"""Unified system management tool for server utilities and information.
|
|
29
|
-
|
|
30
|
-
**Available Actions:**
|
|
31
|
-
- **help**: Get list of all available Rowan MCP tools with descriptions
|
|
32
|
-
- **server_info**: Get server status and configuration information
|
|
33
|
-
- **server_status**: Check server health and connectivity
|
|
34
|
-
- **set_log_level**: Set logging level for debugging (requires: log_level)
|
|
35
|
-
- **job_redirect**: Redirect legacy job queries to workflow management (requires: job_uuid)
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
action: Action to perform ('help', 'server_info', 'server_status', 'set_log_level', 'job_redirect')
|
|
39
|
-
job_uuid: UUID of the job (required for job_redirect)
|
|
40
|
-
log_level: Logging level - DEBUG, INFO, WARNING, ERROR (required for set_log_level)
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
Results of the system operation
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
action = action.lower()
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
if action == "help":
|
|
50
|
-
result = "**Available Rowan MCP Tools** \n\n"
|
|
51
|
-
|
|
52
|
-
result += "**Now with unified management tools!**\n"
|
|
53
|
-
result += "Each tool has tailored documentation and parameters.\n\n"
|
|
54
|
-
|
|
55
|
-
# Group by common use cases
|
|
56
|
-
result += "**Quantum Chemistry & Basic Calculations:**\n"
|
|
57
|
-
result += "• `rowan_electronic_properties` - HOMO/LUMO, orbitals\n"
|
|
58
|
-
result += "• `rowan_multistage_opt` - Multi-level optimization (for geometry)\n\n"
|
|
59
|
-
|
|
60
|
-
result += "**Molecular Analysis:**\n"
|
|
61
|
-
result += "• `rowan_conformers` - Find molecular conformations\n"
|
|
62
|
-
result += "• `rowan_tautomers` - Tautomer enumeration\n"
|
|
63
|
-
result += "• `rowan_descriptors` - Molecular descriptors for ML\n\n"
|
|
64
|
-
|
|
65
|
-
result += "**Chemical Properties:**\n"
|
|
66
|
-
result += "• `rowan_pka` - pKa prediction\n"
|
|
67
|
-
result += "• `rowan_redox_potential` - Redox potentials vs SCE\n"
|
|
68
|
-
|
|
69
|
-
result += "• `rowan_solubility` - Solubility prediction\n\n"
|
|
70
|
-
|
|
71
|
-
result += "**Drug Discovery:**\n"
|
|
72
|
-
result += "• `rowan_admet` - ADME-Tox properties\n"
|
|
73
|
-
result += "• `rowan_docking` - Protein-ligand docking\n\n"
|
|
74
|
-
|
|
75
|
-
result += "**Advanced Analysis:**\n"
|
|
76
|
-
result += "• `rowan_scan` - Potential energy surface scans (bond/angle/dihedral)\n"
|
|
77
|
-
result += "• `rowan_fukui` - Reactivity analysis\n"
|
|
78
|
-
result += "• `rowan_spin_states` - Spin state preferences\n"
|
|
79
|
-
result += "• `rowan_irc` - Reaction coordinate following\n"
|
|
80
|
-
result += "• `rowan_molecular_dynamics` - MD simulations\n"
|
|
81
|
-
result += "\n"
|
|
82
|
-
|
|
83
|
-
result += "**Usage Guidelines:**\n"
|
|
84
|
-
result += "• For geometry optimization: use `rowan_multistage_opt`\n"
|
|
85
|
-
result += "• For conformer search: use `rowan_conformers`\n"
|
|
86
|
-
result += "• For pKa prediction: use `rowan_pka`\n"
|
|
87
|
-
result += "• For electronic structure: use `rowan_electronic_properties`\n"
|
|
88
|
-
result += "• For drug properties: use `rowan_admet`\n"
|
|
89
|
-
result += "• For reaction mechanisms: use `rowan_scan` then `rowan_irc`\n"
|
|
90
|
-
result += "• For potential energy scans: use `rowan_scan` with coordinate specification\n\n"
|
|
91
|
-
|
|
92
|
-
result += "**Management Tools:**\n"
|
|
93
|
-
result += "• `rowan_folder_management` - Unified folder operations (create, retrieve, update, delete, list)\n"
|
|
94
|
-
result += "• `rowan_workflow_management` - Unified workflow operations (create, retrieve, update, stop, status, delete, list)\n"
|
|
95
|
-
result += "• `rowan_system_management` - System utilities (help, set_log_level, job_redirect)\n"
|
|
96
|
-
result += "• `rowan_calculation_retrieve` - Get calculation results\n"
|
|
97
|
-
result += "• `rowan_molecule_lookup` - SMILES lookup for common molecules\n\n"
|
|
98
|
-
|
|
99
|
-
result += "**Total Available:** 20+ specialized tools + management tools\n"
|
|
100
|
-
result += "**Each tool has specific documentation - check individual tool descriptions**\n"
|
|
101
|
-
result += "**Management tools use 'action' parameter to specify operation**\n"
|
|
102
|
-
|
|
103
|
-
return result
|
|
104
|
-
|
|
105
|
-
elif action == "server_info":
|
|
106
|
-
result = "**Rowan MCP Server Information**\n\n"
|
|
107
|
-
|
|
108
|
-
# Server configuration
|
|
109
|
-
result += "**Configuration:**\n"
|
|
110
|
-
result += f"• API Key: {'Configured' if os.getenv('ROWAN_API_KEY') else 'Missing'}\n"
|
|
111
|
-
result += f"• Rowan Package: {'Available' if rowan else 'Not Found'}\n"
|
|
112
|
-
result += f"• Log Level: {logger.level}\n"
|
|
113
|
-
result += f"• Python Version: {sys.version.split()[0]}\n\n"
|
|
114
|
-
|
|
115
|
-
# Available tools count
|
|
116
|
-
result += f"**Available Tools:** 20+ specialized computational chemistry tools\n"
|
|
117
|
-
result += f"• Core Calculations: 4 tools (electronic_properties, multistage_opt, etc.)\n"
|
|
118
|
-
result += f"• Molecular Analysis: 3 tools (conformers, tautomers, descriptors)\n"
|
|
119
|
-
result += f"• Chemical Properties: 4 tools (pka, redox_potential, bde, solubility)\n"
|
|
120
|
-
result += f"• Advanced Analysis: 6 tools (scan, fukui, spin_states, irc, md, etc.)\n"
|
|
121
|
-
result += f"• Drug Discovery: 2 tools (admet, docking)\n"
|
|
122
|
-
result += f"• Management Tools: 5 tools (workflow, folder, system, etc.)\n\n"
|
|
123
|
-
|
|
124
|
-
# Quick status check
|
|
125
|
-
try:
|
|
126
|
-
# Try a simple API call to test connectivity
|
|
127
|
-
recent_workflows = rowan.Workflow.list(size=1)
|
|
128
|
-
total_workflows = len(recent_workflows.get('workflows', []))
|
|
129
|
-
result += f"**API Connectivity:** Connected\n"
|
|
130
|
-
result += f"**Workflow Check:** {total_workflows} workflows accessible\n\n"
|
|
131
|
-
except Exception as e:
|
|
132
|
-
result += f"**API Connectivity:** Error: {str(e)[:100]}...\n\n"
|
|
133
|
-
|
|
134
|
-
result += f"**Usage:**\n"
|
|
135
|
-
result += f"• Use rowan_system_management(action='help') for tool descriptions\n"
|
|
136
|
-
result += f"• Use rowan_workflow_management(action='list') to see your workflows\n"
|
|
137
|
-
result += f"• Note: Folder management currently has API issues - use workflow organization instead\n"
|
|
138
|
-
|
|
139
|
-
return result
|
|
140
|
-
|
|
141
|
-
elif action == "server_status":
|
|
142
|
-
result = "**Rowan MCP Server Health Check**\n\n"
|
|
143
|
-
|
|
144
|
-
# Test API connectivity with workflow endpoint (the main one)
|
|
145
|
-
status_checks = []
|
|
146
|
-
|
|
147
|
-
# Test workflow list
|
|
148
|
-
try:
|
|
149
|
-
workflows = rowan.Workflow.list(size=1)
|
|
150
|
-
workflow_count = len(workflows.get('workflows', []))
|
|
151
|
-
status_checks.append(("Workflow API", "OK", f"{workflow_count} workflows accessible"))
|
|
152
|
-
except Exception as e:
|
|
153
|
-
status_checks.append(("Workflow API", "Error", str(e)[:50]))
|
|
154
|
-
|
|
155
|
-
# Test workflow retrieve (if we have any workflows)
|
|
156
|
-
try:
|
|
157
|
-
workflows = rowan.Workflow.list(size=1)
|
|
158
|
-
if workflows.get('workflows'):
|
|
159
|
-
first_workflow_uuid = workflows['workflows'][0]['uuid']
|
|
160
|
-
rowan.Workflow.retrieve(uuid=first_workflow_uuid)
|
|
161
|
-
status_checks.append(("Workflow Retrieve", "OK", "Can access workflow details"))
|
|
162
|
-
else:
|
|
163
|
-
status_checks.append(("Workflow Retrieve", "Skipped", "No workflows to test"))
|
|
164
|
-
except Exception as e:
|
|
165
|
-
status_checks.append(("Workflow Retrieve", "Error", str(e)[:50]))
|
|
166
|
-
|
|
167
|
-
# Display results
|
|
168
|
-
for check_name, status, details in status_checks:
|
|
169
|
-
result += f"**{check_name}:** {status}\n"
|
|
170
|
-
result += f" Details: {details}\n\n"
|
|
171
|
-
|
|
172
|
-
# Overall status
|
|
173
|
-
all_ok = all("OK" in check[1] or "Skipped" in check[1] for check in status_checks)
|
|
174
|
-
result += f"**Overall Status:** {'All Systems Operational' if all_ok else 'Some Issues Detected'}\n\n"
|
|
175
|
-
|
|
176
|
-
if not all_ok:
|
|
177
|
-
result += f"**Troubleshooting:**\n"
|
|
178
|
-
result += f"• Check your ROWAN_API_KEY environment variable\n"
|
|
179
|
-
result += f"• Verify internet connectivity\n"
|
|
180
|
-
result += f"• Check Rowan service status at labs.rowansci.com\n"
|
|
181
|
-
|
|
182
|
-
return result
|
|
183
|
-
|
|
184
|
-
elif action == "set_log_level":
|
|
185
|
-
if not log_level:
|
|
186
|
-
return "Error: 'log_level' is required for set_log_level action"
|
|
187
|
-
|
|
188
|
-
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR"]
|
|
189
|
-
log_level = log_level.upper()
|
|
190
|
-
|
|
191
|
-
if log_level not in valid_levels:
|
|
192
|
-
return f"Invalid log level. Use one of: {', '.join(valid_levels)}"
|
|
193
|
-
|
|
194
|
-
logger.setLevel(getattr(logging, log_level))
|
|
195
|
-
logger.info(f"Log level changed to: {log_level}")
|
|
196
|
-
|
|
197
|
-
return f"Log level set to {log_level}"
|
|
198
|
-
|
|
199
|
-
elif action == "job_redirect":
|
|
200
|
-
if not job_uuid:
|
|
201
|
-
return "Error: 'job_uuid' is required for job_redirect action"
|
|
202
|
-
|
|
203
|
-
# Try to treat the job_uuid as a workflow_uuid and retrieve results directly
|
|
204
|
-
try:
|
|
205
|
-
workflow = rowan.Workflow.retrieve(uuid=job_uuid)
|
|
206
|
-
|
|
207
|
-
# Get status and interpret it
|
|
208
|
-
status = workflow.get('object_status', 'Unknown')
|
|
209
|
-
status_names = {
|
|
210
|
-
0: "Queued",
|
|
211
|
-
1: "Running",
|
|
212
|
-
2: "Completed",
|
|
213
|
-
3: "Failed",
|
|
214
|
-
4: "Stopped",
|
|
215
|
-
5: "Awaiting Queue"
|
|
216
|
-
}
|
|
217
|
-
status_name = status_names.get(status, f"Unknown ({status})")
|
|
218
|
-
|
|
219
|
-
formatted = f"**Found Workflow {job_uuid}:**\n\n"
|
|
220
|
-
formatted += f"Name: {workflow.get('name', 'N/A')}\n"
|
|
221
|
-
formatted += f"Type: {workflow.get('object_type', 'N/A')}\n"
|
|
222
|
-
formatted += f"Status: {status_name} ({status})\n"
|
|
223
|
-
formatted += f"Created: {workflow.get('created_at', 'N/A')}\n"
|
|
224
|
-
formatted += f"Elapsed: {workflow.get('elapsed', 0):.2f}s\n\n"
|
|
225
|
-
|
|
226
|
-
if status == 2: # Completed
|
|
227
|
-
formatted += f"**Getting Results...**\n\n"
|
|
228
|
-
|
|
229
|
-
# Try to retrieve calculation results
|
|
230
|
-
try:
|
|
231
|
-
calc_result = rowan.Calculation.retrieve(uuid=job_uuid)
|
|
232
|
-
|
|
233
|
-
# Extract workflow type to provide specific result formatting
|
|
234
|
-
workflow_type = workflow.get('object_type', '')
|
|
235
|
-
|
|
236
|
-
if workflow_type == 'electronic_properties':
|
|
237
|
-
formatted += f"**Electronic Properties Results:**\n\n"
|
|
238
|
-
|
|
239
|
-
# Extract key electronic properties from the result
|
|
240
|
-
object_data = calc_result.get('object_data', {})
|
|
241
|
-
|
|
242
|
-
# Molecular orbital energies (HOMO/LUMO)
|
|
243
|
-
if 'molecular_orbitals' in object_data:
|
|
244
|
-
mo_data = object_data['molecular_orbitals']
|
|
245
|
-
if isinstance(mo_data, dict) and 'energies' in mo_data:
|
|
246
|
-
energies = mo_data['energies']
|
|
247
|
-
if isinstance(energies, list) and len(energies) > 0:
|
|
248
|
-
# Find HOMO/LUMO
|
|
249
|
-
occupations = mo_data.get('occupations', [])
|
|
250
|
-
if occupations:
|
|
251
|
-
homo_idx = None
|
|
252
|
-
lumo_idx = None
|
|
253
|
-
for i, occ in enumerate(occupations):
|
|
254
|
-
if occ > 0.5: # Occupied
|
|
255
|
-
homo_idx = i
|
|
256
|
-
elif occ < 0.5 and lumo_idx is None: # First unoccupied
|
|
257
|
-
lumo_idx = i
|
|
258
|
-
break
|
|
259
|
-
|
|
260
|
-
if homo_idx is not None and lumo_idx is not None:
|
|
261
|
-
homo_energy = energies[homo_idx]
|
|
262
|
-
lumo_energy = energies[lumo_idx]
|
|
263
|
-
gap = lumo_energy - homo_energy
|
|
264
|
-
|
|
265
|
-
formatted += f"• HOMO Energy: {homo_energy:.4f} hartree ({homo_energy * 27.2114:.2f} eV)\n"
|
|
266
|
-
formatted += f"• LUMO Energy: {lumo_energy:.4f} hartree ({lumo_energy * 27.2114:.2f} eV)\n"
|
|
267
|
-
formatted += f"• HOMO-LUMO Gap: {gap:.4f} hartree ({gap * 27.2114:.2f} eV)\n\n"
|
|
268
|
-
|
|
269
|
-
# Dipole moment
|
|
270
|
-
if 'dipole' in object_data:
|
|
271
|
-
dipole = object_data['dipole']
|
|
272
|
-
if isinstance(dipole, dict) and 'magnitude' in dipole:
|
|
273
|
-
formatted += f"**Dipole Moment:** {dipole['magnitude']:.4f} Debye\n\n"
|
|
274
|
-
elif isinstance(dipole, (int, float)):
|
|
275
|
-
formatted += f"**Dipole Moment:** {dipole:.4f} Debye\n\n"
|
|
276
|
-
|
|
277
|
-
# If no specific electronic properties found, show available keys
|
|
278
|
-
if not any(key in object_data for key in ['molecular_orbitals', 'dipole']):
|
|
279
|
-
if object_data:
|
|
280
|
-
formatted += f"**Available Properties:** {', '.join(object_data.keys())}\n\n"
|
|
281
|
-
else:
|
|
282
|
-
formatted += f"**No electronic properties data found in results**\n\n"
|
|
283
|
-
|
|
284
|
-
else:
|
|
285
|
-
# For other workflow types, show general calculation results
|
|
286
|
-
formatted += f"**{workflow_type.replace('_', ' ').title()} Results:**\n\n"
|
|
287
|
-
|
|
288
|
-
object_data = calc_result.get('object_data', {})
|
|
289
|
-
if object_data:
|
|
290
|
-
# Show first few key-value pairs
|
|
291
|
-
count = 0
|
|
292
|
-
for key, value in object_data.items():
|
|
293
|
-
if count >= 5: # Limit to first 5 items for job_redirect
|
|
294
|
-
formatted += f" ... and {len(object_data) - 5} more properties\n"
|
|
295
|
-
break
|
|
296
|
-
|
|
297
|
-
# Format the value nicely
|
|
298
|
-
if isinstance(value, (int, float)):
|
|
299
|
-
formatted += f"• **{key}**: {value}\n"
|
|
300
|
-
elif isinstance(value, str):
|
|
301
|
-
formatted += f"• **{key}**: {value[:50]}{'...' if len(value) > 50 else ''}\n"
|
|
302
|
-
elif isinstance(value, list):
|
|
303
|
-
formatted += f"• **{key}**: {len(value)} items\n"
|
|
304
|
-
elif isinstance(value, dict):
|
|
305
|
-
formatted += f"• **{key}**: {len(value)} properties\n"
|
|
306
|
-
else:
|
|
307
|
-
formatted += f"• **{key}**: {type(value).__name__}\n"
|
|
308
|
-
count += 1
|
|
309
|
-
formatted += "\n"
|
|
310
|
-
else:
|
|
311
|
-
formatted += f"**No calculation data found in results**\n\n"
|
|
312
|
-
|
|
313
|
-
except Exception as retrieve_error:
|
|
314
|
-
formatted += f"**Results retrieval failed:** {str(retrieve_error)}\n\n"
|
|
315
|
-
|
|
316
|
-
elif status in [0, 1, 5]: # Still running
|
|
317
|
-
formatted += f"**Workflow is still {status_name.lower()}**\n"
|
|
318
|
-
formatted += f"Check back later for results\n\n"
|
|
319
|
-
|
|
320
|
-
elif status == 3: # Failed
|
|
321
|
-
formatted += f"**Workflow failed**\n"
|
|
322
|
-
formatted += f"Check the workflow parameters and try again\n\n"
|
|
323
|
-
|
|
324
|
-
formatted += f"**For more details:**\n"
|
|
325
|
-
formatted += f"• Use rowan_workflow_management(action='retrieve', workflow_uuid='{job_uuid}') for full workflow details\n"
|
|
326
|
-
formatted += f"• Use rowan_calculation_retrieve('{job_uuid}') for raw calculation data\n"
|
|
327
|
-
|
|
328
|
-
return formatted
|
|
329
|
-
|
|
330
|
-
except Exception as e:
|
|
331
|
-
# If workflow retrieval fails, provide the legacy guidance
|
|
332
|
-
formatted = f"**Could not find workflow {job_uuid}:** {str(e)}\n\n"
|
|
333
|
-
formatted += f"**Important Note:**\n"
|
|
334
|
-
formatted += f"Rowan manages computations through workflows, not individual jobs.\n"
|
|
335
|
-
formatted += f"The job/results concept is legacy from older versions.\n\n"
|
|
336
|
-
formatted += f"**To find your workflow:**\n"
|
|
337
|
-
formatted += f"• Use rowan_workflow_management(action='list') to see all workflows\n"
|
|
338
|
-
formatted += f"• Look for workflows with similar names or recent creation times\n"
|
|
339
|
-
formatted += f"• Use rowan_workflow_management(action='retrieve', workflow_uuid='UUID') to get results\n\n"
|
|
340
|
-
formatted += f"**Migration Guide:**\n"
|
|
341
|
-
formatted += f"• Old: rowan_job_status('{job_uuid}') → New: rowan_workflow_management(action='status', workflow_uuid='UUID')\n"
|
|
342
|
-
formatted += f"• Old: rowan_job_results('{job_uuid}') → New: rowan_workflow_management(action='retrieve', workflow_uuid='UUID')\n"
|
|
343
|
-
|
|
344
|
-
return formatted
|
|
345
|
-
|
|
346
|
-
else:
|
|
347
|
-
return f"Error: Unknown action '{action}'. Available actions: help, server_info, server_status, set_log_level, job_redirect"
|
|
348
|
-
|
|
349
|
-
except Exception as e:
|
|
350
|
-
return f"Error in system {action}: {str(e)}"
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
def test_rowan_system_management():
|
|
354
|
-
"""Test the rowan_system_management function."""
|
|
355
|
-
try:
|
|
356
|
-
# Test the help action
|
|
357
|
-
result = rowan_system_management(action="help")
|
|
358
|
-
print("System management test successful!")
|
|
359
|
-
print(f"Result length: {len(result)} characters")
|
|
360
|
-
print(f"First line: {result.split(chr(10))[0]}")
|
|
361
|
-
return True
|
|
362
|
-
except Exception as e:
|
|
363
|
-
print(f"System management test failed: {e}")
|
|
364
|
-
return False
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if __name__ == "__main__":
|
|
368
|
-
test_rowan_system_management()
|
rowan_mcp/functions/tautomers.py
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Rowan tautomers function for drug design.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
import logging
|
|
7
|
-
import time
|
|
8
|
-
from typing import Optional
|
|
9
|
-
|
|
10
|
-
try:
|
|
11
|
-
import rowan
|
|
12
|
-
except ImportError:
|
|
13
|
-
rowan = None
|
|
14
|
-
|
|
15
|
-
# Setup logging
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
# Setup API key
|
|
19
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
20
|
-
if rowan and api_key:
|
|
21
|
-
rowan.api_key = api_key
|
|
22
|
-
|
|
23
|
-
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
24
|
-
"""Log Rowan API calls with detailed parameters."""
|
|
25
|
-
|
|
26
|
-
try:
|
|
27
|
-
start_time = time.time()
|
|
28
|
-
result = rowan.compute(workflow_type=workflow_type, **kwargs)
|
|
29
|
-
api_time = time.time() - start_time
|
|
30
|
-
|
|
31
|
-
if isinstance(result, dict) and 'uuid' in result:
|
|
32
|
-
job_status = result.get('status', result.get('object_status', 'Unknown'))
|
|
33
|
-
status_names = {0: "Queued", 1: "Running", 2: "Completed", 3: "Failed", 4: "Stopped", 5: "Awaiting Queue"}
|
|
34
|
-
status_text = status_names.get(job_status, f"Unknown ({job_status})")
|
|
35
|
-
|
|
36
|
-
return result
|
|
37
|
-
|
|
38
|
-
except Exception as e:
|
|
39
|
-
api_time = time.time() - start_time
|
|
40
|
-
raise e
|
|
41
|
-
|
|
42
|
-
def rowan_tautomers(
|
|
43
|
-
name: str,
|
|
44
|
-
molecule: str,
|
|
45
|
-
folder_uuid: Optional[str] = None,
|
|
46
|
-
blocking: bool = True,
|
|
47
|
-
ping_interval: int = 5
|
|
48
|
-
) -> str:
|
|
49
|
-
"""Enumerate and rank tautomers by stability.
|
|
50
|
-
|
|
51
|
-
Finds all possible tautomeric forms and ranks them by relative energy:
|
|
52
|
-
- Prototropic tautomers (keto-enol, etc.)
|
|
53
|
-
- Relative populations at room temperature
|
|
54
|
-
- Dominant tautomeric forms
|
|
55
|
-
|
|
56
|
-
Use this for: Drug design, understanding protonation states, reaction mechanisms
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
name: Name for the calculation
|
|
60
|
-
molecule: Molecule SMILES string
|
|
61
|
-
folder_uuid: Optional folder UUID for organization
|
|
62
|
-
blocking: Whether to wait for completion (default: True)
|
|
63
|
-
ping_interval: Check status interval in seconds (default: 5)
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Tautomer enumeration and ranking results
|
|
67
|
-
"""
|
|
68
|
-
result = log_rowan_api_call(
|
|
69
|
-
workflow_type="tautomers",
|
|
70
|
-
name=name,
|
|
71
|
-
molecule=molecule,
|
|
72
|
-
folder_uuid=folder_uuid,
|
|
73
|
-
blocking=blocking,
|
|
74
|
-
ping_interval=ping_interval
|
|
75
|
-
)
|
|
76
|
-
return str(result)
|
|
77
|
-
|
|
78
|
-
def test_rowan_tautomers():
|
|
79
|
-
"""Test the rowan_tautomers function."""
|
|
80
|
-
try:
|
|
81
|
-
# Test with a simple molecule (non-blocking to avoid long wait)
|
|
82
|
-
result = rowan_tautomers("test_tautomers", "CCO", blocking=False)
|
|
83
|
-
print(" Tautomers test successful!")
|
|
84
|
-
print(f"Result length: {len(result)} characters")
|
|
85
|
-
return True
|
|
86
|
-
except Exception as e:
|
|
87
|
-
print(f" Tautomers test failed: {e}")
|
|
88
|
-
return False
|
|
89
|
-
|
|
90
|
-
if __name__ == "__main__":
|
|
91
|
-
test_rowan_tautomers()
|