memra 0.2.2__py3-none-any.whl → 0.2.4__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.
Files changed (57) hide show
  1. memra/__init__.py +6 -2
  2. memra/execution.py +53 -0
  3. memra/tool_registry.py +162 -0
  4. memra-0.2.4.dist-info/METADATA +145 -0
  5. memra-0.2.4.dist-info/RECORD +58 -0
  6. {memra-0.2.2.dist-info → memra-0.2.4.dist-info}/WHEEL +1 -1
  7. memra-0.2.4.dist-info/top_level.txt +4 -0
  8. memra-ops/app.py +710 -0
  9. memra-ops/config/config.py +25 -0
  10. memra-ops/config.py +34 -0
  11. memra-ops/scripts/release.py +133 -0
  12. memra-ops/scripts/start_memra.py +334 -0
  13. memra-ops/scripts/stop_memra.py +132 -0
  14. memra-ops/server_tool_registry.py +188 -0
  15. memra-ops/tests/test_llm_text_to_sql.py +115 -0
  16. memra-ops/tests/test_llm_vs_pattern.py +130 -0
  17. memra-ops/tests/test_mcp_schema_aware.py +124 -0
  18. memra-ops/tests/test_schema_aware_sql.py +139 -0
  19. memra-ops/tests/test_schema_aware_sql_simple.py +66 -0
  20. memra-ops/tests/test_text_to_sql_demo.py +140 -0
  21. memra-ops/tools/mcp_bridge_server.py +851 -0
  22. memra-sdk/examples/accounts_payable.py +215 -0
  23. memra-sdk/examples/accounts_payable_client.py +217 -0
  24. memra-sdk/examples/accounts_payable_mcp.py +200 -0
  25. memra-sdk/examples/ask_questions.py +123 -0
  26. memra-sdk/examples/invoice_processing.py +116 -0
  27. memra-sdk/examples/propane_delivery.py +87 -0
  28. memra-sdk/examples/simple_text_to_sql.py +158 -0
  29. memra-sdk/memra/__init__.py +31 -0
  30. memra-sdk/memra/discovery.py +15 -0
  31. memra-sdk/memra/discovery_client.py +49 -0
  32. memra-sdk/memra/execution.py +481 -0
  33. memra-sdk/memra/models.py +99 -0
  34. memra-sdk/memra/tool_registry.py +343 -0
  35. memra-sdk/memra/tool_registry_client.py +106 -0
  36. memra-sdk/scripts/release.py +133 -0
  37. memra-sdk/setup.py +52 -0
  38. memra-workflows/accounts_payable/accounts_payable.py +215 -0
  39. memra-workflows/accounts_payable/accounts_payable_client.py +216 -0
  40. memra-workflows/accounts_payable/accounts_payable_mcp.py +200 -0
  41. memra-workflows/accounts_payable/accounts_payable_smart.py +221 -0
  42. memra-workflows/invoice_processing/invoice_processing.py +116 -0
  43. memra-workflows/invoice_processing/smart_invoice_processor.py +220 -0
  44. memra-workflows/logic/__init__.py +1 -0
  45. memra-workflows/logic/file_tools.py +50 -0
  46. memra-workflows/logic/invoice_tools.py +501 -0
  47. memra-workflows/logic/propane_agents.py +52 -0
  48. memra-workflows/mcp_bridge_server.py +230 -0
  49. memra-workflows/propane_delivery/propane_delivery.py +87 -0
  50. memra-workflows/text_to_sql/complete_invoice_workflow_with_queries.py +208 -0
  51. memra-workflows/text_to_sql/complete_text_to_sql_system.py +266 -0
  52. memra-workflows/text_to_sql/file_discovery_demo.py +156 -0
  53. memra-0.2.2.dist-info/METADATA +0 -148
  54. memra-0.2.2.dist-info/RECORD +0 -13
  55. memra-0.2.2.dist-info/top_level.txt +0 -1
  56. {memra-0.2.2.dist-info → memra-0.2.4.dist-info}/entry_points.txt +0 -0
  57. {memra-0.2.2.dist-info → memra-0.2.4.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,25 @@
1
+ """
2
+ Configuration for Memra SDK examples
3
+ Contains LLM configurations for different agent types
4
+ """
5
+
6
+ # Default LLM configuration for general use
7
+ DEFAULT_LLM_CONFIG = {
8
+ "model": "llama-3.2-11b-vision-preview",
9
+ "temperature": 0.1,
10
+ "max_tokens": 2000
11
+ }
12
+
13
+ # Specialized LLM configurations for different agent types
14
+ AGENT_LLM_CONFIG = {
15
+ "parsing": {
16
+ "model": "llama-3.2-11b-vision-preview",
17
+ "temperature": 0.0,
18
+ "max_tokens": 4000
19
+ },
20
+ "manager": {
21
+ "model": "llama-3.2-11b-vision-preview",
22
+ "temperature": 0.2,
23
+ "max_tokens": 1000
24
+ }
25
+ }
memra-ops/config.py ADDED
@@ -0,0 +1,34 @@
1
+ # Memra SDK Configuration
2
+ # LLM API Configuration for agent processing
3
+
4
+ API_CONFIG = {
5
+ "huggingface": {
6
+ "api_key": "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",
7
+ "model": "meta-llama/Llama-4-Maverick-17B-128E-Instruct",
8
+ "max_tokens": 2000
9
+ }
10
+ }
11
+
12
+ # Default LLM settings for agents
13
+ DEFAULT_LLM_CONFIG = {
14
+ "provider": "huggingface",
15
+ "model": "meta-llama/Llama-4-Maverick-17B-128E-Instruct",
16
+ "temperature": 0.1,
17
+ "max_tokens": 2000
18
+ }
19
+
20
+ # Agent-specific LLM configurations
21
+ AGENT_LLM_CONFIG = {
22
+ "parsing": {
23
+ "provider": "huggingface",
24
+ "model": "meta-llama/Llama-4-Maverick-17B-128E-Instruct",
25
+ "temperature": 0.0, # More deterministic for data extraction
26
+ "max_tokens": 2000
27
+ },
28
+ "manager": {
29
+ "provider": "huggingface",
30
+ "model": "meta-llama/Llama-4-Maverick-17B-128E-Instruct",
31
+ "temperature": 0.3, # More flexible for decision making
32
+ "max_tokens": 1500
33
+ }
34
+ }
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Release script for Memra SDK
4
+ Builds and uploads the package to PyPI
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import subprocess
10
+ import shutil
11
+ from pathlib import Path
12
+
13
+ def run_command(cmd, description):
14
+ """Run a command and handle errors"""
15
+ print(f"🔄 {description}...")
16
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
17
+ if result.returncode != 0:
18
+ print(f"❌ {description} failed:")
19
+ print(result.stderr)
20
+ sys.exit(1)
21
+ print(f"✅ {description} completed")
22
+ return result.stdout
23
+
24
+ def clean_build_artifacts():
25
+ """Clean up build artifacts"""
26
+ print("🧹 Cleaning build artifacts...")
27
+
28
+ # Remove build directories
29
+ for dir_name in ['build', 'dist', 'memra.egg-info']:
30
+ if os.path.exists(dir_name):
31
+ shutil.rmtree(dir_name)
32
+ print(f" Removed {dir_name}/")
33
+
34
+ # Remove __pycache__ directories
35
+ for root, dirs, files in os.walk('.'):
36
+ for dir_name in dirs:
37
+ if dir_name == '__pycache__':
38
+ pycache_path = os.path.join(root, dir_name)
39
+ shutil.rmtree(pycache_path)
40
+ print(f" Removed {pycache_path}")
41
+
42
+ print("✅ Build artifacts cleaned")
43
+
44
+ def run_tests():
45
+ """Run tests before release"""
46
+ print("🧪 Running tests...")
47
+
48
+ # Check if pytest is available
49
+ try:
50
+ subprocess.run(['pytest', '--version'], check=True, capture_output=True)
51
+ run_command('pytest tests/', 'Running pytest')
52
+ except (subprocess.CalledProcessError, FileNotFoundError):
53
+ print("⚠️ pytest not found, skipping tests")
54
+
55
+ # Run basic import test
56
+ run_command('python -c "import memra; print(f\'Memra SDK version: {memra.__version__ if hasattr(memra, \"__version__\") else \"unknown\"}\')"', 'Testing basic import')
57
+
58
+ def build_package():
59
+ """Build the package"""
60
+ print("📦 Building package...")
61
+
62
+ # Install build dependencies
63
+ run_command('pip install build twine', 'Installing build tools')
64
+
65
+ # Build the package
66
+ run_command('python -m build', 'Building wheel and source distribution')
67
+
68
+ # Check the package
69
+ run_command('twine check dist/*', 'Checking package')
70
+
71
+ def upload_package(test=False):
72
+ """Upload package to PyPI"""
73
+ if test:
74
+ print("🚀 Uploading to Test PyPI...")
75
+ run_command('twine upload --repository testpypi dist/*', 'Uploading to Test PyPI')
76
+ print("📍 Package uploaded to Test PyPI: https://test.pypi.org/project/memra/")
77
+ else:
78
+ print("🚀 Uploading to PyPI...")
79
+ run_command('twine upload dist/*', 'Uploading to PyPI')
80
+ print("📍 Package uploaded to PyPI: https://pypi.org/project/memra/")
81
+
82
+ def main():
83
+ """Main release process"""
84
+ print("🎯 Memra SDK Release Process")
85
+ print("=" * 40)
86
+
87
+ # Parse arguments
88
+ test_release = '--test' in sys.argv
89
+ skip_tests = '--skip-tests' in sys.argv
90
+
91
+ if test_release:
92
+ print("🧪 Test release mode enabled")
93
+
94
+ # Ensure we're in the right directory
95
+ if not os.path.exists('setup.py'):
96
+ print("❌ setup.py not found. Please run from the project root.")
97
+ sys.exit(1)
98
+
99
+ try:
100
+ # Clean up
101
+ clean_build_artifacts()
102
+
103
+ # Run tests
104
+ if not skip_tests:
105
+ run_tests()
106
+ else:
107
+ print("⚠️ Skipping tests")
108
+
109
+ # Build package
110
+ build_package()
111
+
112
+ # Upload package
113
+ upload_package(test=test_release)
114
+
115
+ print("\n🎉 Release completed successfully!")
116
+
117
+ if test_release:
118
+ print("\n📋 Next steps:")
119
+ print("1. Test the package: pip install -i https://test.pypi.org/simple/ memra")
120
+ print("2. If everything works, run: python scripts/release.py")
121
+ else:
122
+ print("\n📋 Package is now available on PyPI!")
123
+ print("Install with: pip install memra")
124
+
125
+ except KeyboardInterrupt:
126
+ print("\n❌ Release cancelled by user")
127
+ sys.exit(1)
128
+ except Exception as e:
129
+ print(f"\n❌ Release failed: {e}")
130
+ sys.exit(1)
131
+
132
+ if __name__ == '__main__':
133
+ main()
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Memra System Startup Script
4
+ Starts all dependencies required for the Memra system to run
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import time
10
+ import subprocess
11
+ import requests
12
+ import signal
13
+ from pathlib import Path
14
+
15
+ # Add the project root to the path
16
+ project_root = Path(__file__).parent.parent
17
+ sys.path.insert(0, str(project_root))
18
+
19
+ class MemraStartup:
20
+ def __init__(self):
21
+ self.project_root = project_root
22
+ self.docker_compose_file = project_root / "docker-compose.yml"
23
+ self.mcp_bridge_script = project_root / "mcp_bridge_server.py"
24
+ self.processes = []
25
+
26
+ def print_banner(self):
27
+ """Print startup banner"""
28
+ print("=" * 60)
29
+ print("🚀 MEMRA SYSTEM STARTUP")
30
+ print("=" * 60)
31
+ print("Starting all dependencies for Memra AI workflow system...")
32
+ print()
33
+
34
+ def check_conda_environment(self):
35
+ """Check if we're in the correct conda environment"""
36
+ print("🔍 Checking conda environment...")
37
+
38
+ # Check if we're in the memra environment
39
+ conda_env = os.getenv('CONDA_DEFAULT_ENV')
40
+ if conda_env != 'memra':
41
+ print(f"❌ Warning: Not in 'memra' conda environment (current: {conda_env})")
42
+ print(" Please run: conda activate memra")
43
+ print(" Then run this script again.")
44
+ return False
45
+
46
+ print(f"✅ Conda environment: {conda_env}")
47
+ return True
48
+
49
+ def check_docker(self):
50
+ """Check if Docker is running"""
51
+ print("🐳 Checking Docker...")
52
+ try:
53
+ result = subprocess.run(['docker', 'info'],
54
+ capture_output=True, text=True, timeout=10)
55
+ if result.returncode == 0:
56
+ print("✅ Docker is running")
57
+ return True
58
+ else:
59
+ print("❌ Docker is not running")
60
+ return False
61
+ except (subprocess.TimeoutExpired, FileNotFoundError):
62
+ print("❌ Docker is not running or not installed")
63
+ return False
64
+
65
+ def start_postgresql(self):
66
+ """Start PostgreSQL using Docker Compose"""
67
+ print("🐘 Starting PostgreSQL...")
68
+
69
+ try:
70
+ # Check if containers are already running
71
+ result = subprocess.run(['docker', 'ps', '--filter', 'name=memra-postgres'],
72
+ capture_output=True, text=True)
73
+
74
+ if 'memra-postgres' in result.stdout:
75
+ print("✅ PostgreSQL is already running")
76
+ return True
77
+
78
+ # Start PostgreSQL
79
+ print(" Starting PostgreSQL container...")
80
+ result = subprocess.run(['docker-compose', 'up', '-d', 'postgres'],
81
+ cwd=self.project_root, capture_output=True, text=True)
82
+
83
+ if result.returncode == 0:
84
+ print("✅ PostgreSQL started successfully")
85
+ return True
86
+ else:
87
+ print(f"❌ Failed to start PostgreSQL: {result.stderr}")
88
+ return False
89
+
90
+ except Exception as e:
91
+ print(f"❌ Error starting PostgreSQL: {e}")
92
+ return False
93
+
94
+ def wait_for_postgresql(self, max_attempts=30):
95
+ """Wait for PostgreSQL to be ready"""
96
+ print("⏳ Waiting for PostgreSQL to be ready...")
97
+
98
+ for attempt in range(max_attempts):
99
+ try:
100
+ # Try to connect to PostgreSQL
101
+ result = subprocess.run([
102
+ 'docker', 'exec', 'memra-postgres',
103
+ 'pg_isready', '-U', 'memra', '-d', 'memra_invoice_db'
104
+ ], capture_output=True, text=True, timeout=5)
105
+
106
+ if result.returncode == 0:
107
+ print("✅ PostgreSQL is ready")
108
+ return True
109
+
110
+ except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
111
+ pass
112
+
113
+ print(f" Attempt {attempt + 1}/{max_attempts}...")
114
+ time.sleep(2)
115
+
116
+ print("❌ PostgreSQL failed to start within timeout")
117
+ return False
118
+
119
+ def check_memra_api_key(self):
120
+ """Check if MEMRA_API_KEY is set"""
121
+ print("🔑 Checking Memra API key...")
122
+
123
+ api_key = os.getenv('MEMRA_API_KEY')
124
+ if not api_key:
125
+ print("❌ MEMRA_API_KEY environment variable is not set")
126
+ print(" Please set: export MEMRA_API_KEY='your-key-here'")
127
+ return False
128
+
129
+ print(f"✅ Memra API key is set: {api_key[:8]}...")
130
+ return True
131
+
132
+ def start_mcp_bridge(self):
133
+ """Start the MCP bridge server"""
134
+ print("🌉 Starting MCP Bridge Server...")
135
+
136
+ try:
137
+ # Check if MCP bridge is already running
138
+ try:
139
+ response = requests.get('http://localhost:8081/health', timeout=5)
140
+ if response.status_code == 200:
141
+ print("✅ MCP Bridge Server is already running")
142
+ return True
143
+ except requests.RequestException:
144
+ pass
145
+
146
+ # Start MCP bridge server in background
147
+ print(" Starting MCP bridge server...")
148
+ process = subprocess.Popen([
149
+ sys.executable, str(self.mcp_bridge_script)
150
+ ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
151
+
152
+ self.processes.append(process)
153
+
154
+ # Wait a moment for server to start
155
+ time.sleep(3)
156
+
157
+ # Check if server started successfully
158
+ try:
159
+ response = requests.get('http://localhost:8081/health', timeout=5)
160
+ if response.status_code == 200:
161
+ print("✅ MCP Bridge Server started successfully")
162
+ return True
163
+ else:
164
+ print(f"❌ MCP Bridge Server returned status {response.status_code}")
165
+ return False
166
+ except requests.RequestException as e:
167
+ print(f"❌ MCP Bridge Server failed to start: {e}")
168
+ return False
169
+
170
+ except Exception as e:
171
+ print(f"❌ Error starting MCP Bridge Server: {e}")
172
+ return False
173
+
174
+ def test_memra_api(self):
175
+ """Test the Memra API connection"""
176
+ print("🌐 Testing Memra API connection...")
177
+
178
+ try:
179
+ from memra import get_api_status
180
+ api_status = get_api_status()
181
+
182
+ if api_status['api_healthy']:
183
+ print(f"✅ Memra API is healthy")
184
+ print(f" URL: {api_status['api_url']}")
185
+ print(f" Tools Available: {api_status['tools_available']}")
186
+ return True
187
+ else:
188
+ print("❌ Memra API is not healthy")
189
+ return False
190
+
191
+ except Exception as e:
192
+ print(f"❌ Error testing Memra API: {e}")
193
+ return False
194
+
195
+ def run_test_workflow(self):
196
+ """Run a quick test to verify everything works"""
197
+ print("🧪 Running system test...")
198
+
199
+ try:
200
+ # Import and run a simple test
201
+ from memra import Agent, Department, LLM
202
+ from memra.execution import ExecutionEngine
203
+
204
+ # Create a simple test agent
205
+ test_agent = Agent(
206
+ role="Test Agent",
207
+ job="Verify system is working",
208
+ llm=LLM(model="llama-3.2-11b-vision-preview", temperature=0.1),
209
+ sops=["Return a simple success message"],
210
+ output_key="test_result"
211
+ )
212
+
213
+ # Create test department
214
+ test_dept = Department(
215
+ name="Test Department",
216
+ mission="Verify Memra system is working",
217
+ agents=[test_agent],
218
+ workflow_order=["Test Agent"]
219
+ )
220
+
221
+ # Run test
222
+ engine = ExecutionEngine()
223
+ result = engine.execute_department(test_dept, {})
224
+
225
+ if result.success:
226
+ print("✅ System test passed - Memra is ready!")
227
+ return True
228
+ else:
229
+ print(f"❌ System test failed: {result.error}")
230
+ return False
231
+
232
+ except Exception as e:
233
+ print(f"❌ Error running system test: {e}")
234
+ return False
235
+
236
+ def cleanup(self):
237
+ """Cleanup processes on exit"""
238
+ print("\n🧹 Cleaning up processes...")
239
+ for process in self.processes:
240
+ try:
241
+ process.terminate()
242
+ process.wait(timeout=5)
243
+ except:
244
+ try:
245
+ process.kill()
246
+ except:
247
+ pass
248
+
249
+ def signal_handler(self, signum, frame):
250
+ """Handle interrupt signals"""
251
+ print("\n🛑 Received interrupt signal, shutting down...")
252
+ self.cleanup()
253
+ sys.exit(0)
254
+
255
+ def start(self):
256
+ """Main startup sequence"""
257
+ try:
258
+ self.print_banner()
259
+
260
+ # Set up signal handlers
261
+ signal.signal(signal.SIGINT, self.signal_handler)
262
+ signal.signal(signal.SIGTERM, self.signal_handler)
263
+
264
+ # Check environment
265
+ if not self.check_conda_environment():
266
+ return False
267
+
268
+ # Check Docker
269
+ if not self.check_docker():
270
+ print("❌ Please start Docker Desktop and try again")
271
+ return False
272
+
273
+ # Start PostgreSQL
274
+ if not self.start_postgresql():
275
+ return False
276
+
277
+ if not self.wait_for_postgresql():
278
+ return False
279
+
280
+ # Check API key
281
+ if not self.check_memra_api_key():
282
+ return False
283
+
284
+ # Start MCP bridge
285
+ if not self.start_mcp_bridge():
286
+ return False
287
+
288
+ # Test API
289
+ if not self.test_memra_api():
290
+ return False
291
+
292
+ # Run system test
293
+ if not self.run_test_workflow():
294
+ return False
295
+
296
+ print("\n" + "=" * 60)
297
+ print("🎉 MEMRA SYSTEM STARTED SUCCESSFULLY!")
298
+ print("=" * 60)
299
+ print("✅ All dependencies are running:")
300
+ print(" • PostgreSQL Database (Docker)")
301
+ print(" • MCP Bridge Server (localhost:8081)")
302
+ print(" • Memra API (https://api.memra.co)")
303
+ print()
304
+ print("🚀 Ready to run workflows!")
305
+ print(" Example: python3 examples/accounts_payable_client.py")
306
+ print()
307
+ print("💡 Keep this terminal open to maintain the MCP bridge server")
308
+ print(" Press Ctrl+C to stop all services")
309
+ print("=" * 60)
310
+
311
+ # Keep the script running to maintain the MCP bridge server
312
+ try:
313
+ while True:
314
+ time.sleep(1)
315
+ except KeyboardInterrupt:
316
+ print("\n🛑 Shutting down Memra system...")
317
+ self.cleanup()
318
+ print("✅ Memra system stopped")
319
+
320
+ except Exception as e:
321
+ print(f"❌ Startup failed: {e}")
322
+ self.cleanup()
323
+ return False
324
+
325
+ return True
326
+
327
+ def main():
328
+ """Main entry point"""
329
+ startup = MemraStartup()
330
+ success = startup.start()
331
+ sys.exit(0 if success else 1)
332
+
333
+ if __name__ == "__main__":
334
+ main()
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Memra System Stop Script
4
+ Gracefully stops all Memra system dependencies
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import subprocess
10
+ import requests
11
+ import signal
12
+ from pathlib import Path
13
+
14
+ class MemraStop:
15
+ def __init__(self):
16
+ self.project_root = Path(__file__).parent.parent
17
+
18
+ def print_banner(self):
19
+ """Print stop banner"""
20
+ print("=" * 60)
21
+ print("🛑 MEMRA SYSTEM SHUTDOWN")
22
+ print("=" * 60)
23
+ print("Stopping all Memra system dependencies...")
24
+ print()
25
+
26
+ def stop_mcp_bridge(self):
27
+ """Stop the MCP bridge server"""
28
+ print("🌉 Stopping MCP Bridge Server...")
29
+
30
+ try:
31
+ # Try to send a graceful shutdown signal
32
+ response = requests.get('http://localhost:8081/health', timeout=5)
33
+ if response.status_code == 200:
34
+ print(" MCP Bridge Server is running, stopping...")
35
+
36
+ # Find and kill the MCP bridge process
37
+ result = subprocess.run(['pkill', '-f', 'mcp_bridge_server.py'],
38
+ capture_output=True, text=True)
39
+
40
+ if result.returncode == 0:
41
+ print("✅ MCP Bridge Server stopped")
42
+ else:
43
+ print("⚠️ MCP Bridge Server process not found (may already be stopped)")
44
+ else:
45
+ print("✅ MCP Bridge Server is not running")
46
+
47
+ except requests.RequestException:
48
+ print("✅ MCP Bridge Server is not running")
49
+
50
+ def stop_postgresql(self):
51
+ """Stop PostgreSQL using Docker Compose"""
52
+ print("🐘 Stopping PostgreSQL...")
53
+
54
+ try:
55
+ # Check if PostgreSQL container is running
56
+ result = subprocess.run(['docker', 'ps', '--filter', 'name=memra-postgres'],
57
+ capture_output=True, text=True)
58
+
59
+ if 'memra-postgres' in result.stdout:
60
+ print(" PostgreSQL container is running, stopping...")
61
+
62
+ # Stop PostgreSQL container
63
+ result = subprocess.run(['docker-compose', 'stop', 'postgres'],
64
+ cwd=self.project_root, capture_output=True, text=True)
65
+
66
+ if result.returncode == 0:
67
+ print("✅ PostgreSQL stopped")
68
+ else:
69
+ print(f"⚠️ Warning: Failed to stop PostgreSQL: {result.stderr}")
70
+ else:
71
+ print("✅ PostgreSQL is not running")
72
+
73
+ except Exception as e:
74
+ print(f"⚠️ Warning: Error stopping PostgreSQL: {e}")
75
+
76
+ def cleanup_docker(self):
77
+ """Clean up any orphaned Docker containers"""
78
+ print("🧹 Cleaning up Docker containers...")
79
+
80
+ try:
81
+ # Remove any stopped containers
82
+ result = subprocess.run(['docker', 'container', 'prune', '-f'],
83
+ capture_output=True, text=True)
84
+
85
+ if result.returncode == 0:
86
+ print("✅ Docker containers cleaned up")
87
+ else:
88
+ print("⚠️ Warning: Failed to clean up Docker containers")
89
+
90
+ except Exception as e:
91
+ print(f"⚠️ Warning: Error cleaning up Docker: {e}")
92
+
93
+ def stop(self):
94
+ """Main stop sequence"""
95
+ try:
96
+ self.print_banner()
97
+
98
+ # Stop MCP bridge server
99
+ self.stop_mcp_bridge()
100
+
101
+ # Stop PostgreSQL
102
+ self.stop_postgresql()
103
+
104
+ # Clean up Docker
105
+ self.cleanup_docker()
106
+
107
+ print("\n" + "=" * 60)
108
+ print("✅ MEMRA SYSTEM STOPPED SUCCESSFULLY!")
109
+ print("=" * 60)
110
+ print("All services have been stopped:")
111
+ print(" • MCP Bridge Server")
112
+ print(" • PostgreSQL Database")
113
+ print(" • Docker containers cleaned up")
114
+ print()
115
+ print("💡 To start the system again, run:")
116
+ print(" ./scripts/start_memra.sh")
117
+ print("=" * 60)
118
+
119
+ except Exception as e:
120
+ print(f"❌ Stop failed: {e}")
121
+ return False
122
+
123
+ return True
124
+
125
+ def main():
126
+ """Main entry point"""
127
+ stop = MemraStop()
128
+ success = stop.stop()
129
+ sys.exit(0 if success else 1)
130
+
131
+ if __name__ == "__main__":
132
+ main()