memra 0.2.4__py3-none-any.whl → 0.2.6__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.
memra/__init__.py CHANGED
@@ -6,7 +6,7 @@ Think of it as "Kubernetes for business logic" where agents are the pods and
6
6
  departments are the deployments.
7
7
  """
8
8
 
9
- __version__ = "0.2.3"
9
+ __version__ = "0.2.6"
10
10
 
11
11
  # Core imports
12
12
  from .models import Agent, Department, Tool, LLM
@@ -28,4 +28,18 @@ __all__ = [
28
28
  # Optional: Add version check for compatibility
29
29
  import sys
30
30
  if sys.version_info < (3, 8):
31
- raise RuntimeError("Memra requires Python 3.8 or higher")
31
+ raise RuntimeError("Memra requires Python 3.8 or higher")
32
+
33
+ # CLI functionality
34
+ def demo():
35
+ """Run the ETL invoice processing demo"""
36
+ from .cli import run_demo
37
+ run_demo()
38
+
39
+ if __name__ == "__main__":
40
+ import sys
41
+ if len(sys.argv) > 1 and sys.argv[1] == "demo":
42
+ demo()
43
+ else:
44
+ print("Usage: python -m memra demo")
45
+ print("Or: memra demo")
memra/cli.py ADDED
@@ -0,0 +1,315 @@
1
+ """
2
+ Memra CLI - Command line interface for Memra SDK
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import subprocess
8
+ import time
9
+ import tempfile
10
+ import shutil
11
+ from pathlib import Path
12
+ import importlib.resources as pkg_resources
13
+
14
+ def run_demo():
15
+ """Run the ETL invoice processing demo with automatic setup"""
16
+ print("🚀 Starting Memra ETL Demo...")
17
+ print("=" * 50)
18
+
19
+ # Step 1: Extract bundled files
20
+ print("📦 Setting up demo environment...")
21
+ demo_dir = setup_demo_environment()
22
+
23
+ # Step 2: Set environment variables
24
+ print("🔧 Configuring environment...")
25
+ setup_environment()
26
+
27
+ # Step 3: Start Docker containers
28
+ print("🐳 Starting Docker services...")
29
+ if not start_docker_services(demo_dir):
30
+ print("❌ Failed to start Docker services. Please check Docker is running.")
31
+ return False
32
+
33
+ # Step 4: Wait for services to be ready
34
+ print("⏳ Waiting for services to be ready...")
35
+ wait_for_services()
36
+
37
+ # Step 5: Run the demo
38
+ print("🎯 Running ETL workflow...")
39
+ success = run_etl_workflow(demo_dir)
40
+
41
+ # Step 6: Show results
42
+ if success:
43
+ print("=" * 50)
44
+ print("🎉 Demo completed successfully!")
45
+ print("\n📊 What happened:")
46
+ print(" • PDF invoice processed with AI vision")
47
+ print(" • Data extracted and validated")
48
+ print(" • Results stored in PostgreSQL database")
49
+ print("\n🔍 Next steps:")
50
+ print(" • Check database: docker exec -it memra_postgres psql -U postgres -d local_workflow")
51
+ print(" • View data: SELECT * FROM invoices ORDER BY created_at DESC;")
52
+ print(" • Stop services: cd memra-ops && docker compose down")
53
+ print(" • Explore code: Check the extracted files in the demo directory")
54
+ else:
55
+ print("❌ Demo failed. Check the logs above for details.")
56
+
57
+ return success
58
+
59
+ def setup_demo_environment():
60
+ """Extract bundled demo files to a temporary directory"""
61
+ try:
62
+ # Create demo directory
63
+ demo_dir = Path.home() / ".memra" / "demo"
64
+ demo_dir.mkdir(parents=True, exist_ok=True)
65
+
66
+ # Extract bundled files
67
+ extract_bundled_files(demo_dir)
68
+
69
+ print(f"✅ Demo files extracted to: {demo_dir}")
70
+ return demo_dir
71
+
72
+ except Exception as e:
73
+ print(f"❌ Failed to setup demo environment: {e}")
74
+ sys.exit(1)
75
+
76
+ def extract_bundled_files(demo_dir):
77
+ """Extract files bundled with the PyPI package"""
78
+ try:
79
+ # Extract from package data
80
+ with pkg_resources.path('memra', 'demo_files') as demo_files_path:
81
+ if demo_files_path.exists():
82
+ # Copy all files from the bundled demo_files directory
83
+ shutil.copytree(demo_files_path, demo_dir, dirs_exist_ok=True)
84
+ else:
85
+ # Fallback: create minimal demo structure
86
+ create_minimal_demo(demo_dir)
87
+
88
+ except Exception as e:
89
+ print(f"⚠️ Could not extract bundled files: {e}")
90
+ print("Creating minimal demo structure...")
91
+ create_minimal_demo(demo_dir)
92
+
93
+ def create_minimal_demo(demo_dir):
94
+ """Create a minimal demo structure if bundled files aren't available"""
95
+ # Create memra-ops directory
96
+ ops_dir = demo_dir / "memra-ops"
97
+ ops_dir.mkdir(exist_ok=True)
98
+
99
+ # Create basic docker-compose.yml
100
+ compose_content = """version: '3.8'
101
+ services:
102
+ postgres:
103
+ image: postgres:15
104
+ environment:
105
+ POSTGRES_DB: local_workflow
106
+ POSTGRES_USER: postgres
107
+ POSTGRES_PASSWORD: postgres
108
+ ports:
109
+ - "5432:5432"
110
+ volumes:
111
+ - postgres_data:/var/lib/postgresql/data
112
+
113
+ volumes:
114
+ postgres_data:
115
+ """
116
+
117
+ with open(ops_dir / "docker-compose.yml", "w") as f:
118
+ f.write(compose_content)
119
+
120
+ # Create basic MCP bridge server
121
+ mcp_content = """#!/usr/bin/env python3
122
+ import asyncio
123
+ import aiohttp
124
+ from aiohttp import web
125
+ import json
126
+
127
+ async def health_handler(request):
128
+ return web.json_response({"status": "healthy"})
129
+
130
+ async def execute_tool_handler(request):
131
+ data = await request.json()
132
+ tool_name = data.get('tool_name', 'unknown')
133
+
134
+ # Mock responses for demo
135
+ if tool_name == 'SQLExecutor':
136
+ return web.json_response({
137
+ "success": True,
138
+ "results": [{"message": "Demo SQL executed"}]
139
+ })
140
+ elif tool_name == 'PostgresInsert':
141
+ return web.json_response({
142
+ "success": True,
143
+ "id": 1
144
+ })
145
+ else:
146
+ return web.json_response({
147
+ "success": True,
148
+ "message": f"Demo {tool_name} executed"
149
+ })
150
+
151
+ app = web.Application()
152
+ app.router.add_get('/health', health_handler)
153
+ app.router.add_post('/execute_tool', execute_tool_handler)
154
+
155
+ if __name__ == '__main__':
156
+ web.run_app(app, host='0.0.0.0', port=8081)
157
+ """
158
+
159
+ with open(ops_dir / "mcp_bridge_server.py", "w") as f:
160
+ f.write(mcp_content)
161
+
162
+ # Create demo workflow
163
+ demo_dir.mkdir(exist_ok=True)
164
+ demo_content = """#!/usr/bin/env python3
165
+ import os
166
+ import sys
167
+ import time
168
+
169
+ def main():
170
+ print("🚀 Starting ETL Invoice Processing Demo...")
171
+ print("🏢 Starting ETL Invoice Processing Department")
172
+ print("📋 Mission: Complete end-to-end ETL process with comprehensive monitoring")
173
+ print("👥 Team: Pre-ETL Database Monitor, Data Engineer, Invoice Parser, Data Entry Specialist, Post-ETL Database Monitor")
174
+ print("👔 Manager: ETL Process Manager")
175
+
176
+ steps = [
177
+ ("Pre-ETL Database Monitor", "Database state captured: 2 rows"),
178
+ ("Data Engineer", "Schema extracted successfully"),
179
+ ("Invoice Parser", "Invoice data extracted: $270.57"),
180
+ ("Data Entry Specialist", "Record inserted: ID 1"),
181
+ ("Post-ETL Database Monitor", "Database state captured: 3 rows")
182
+ ]
183
+
184
+ for i, (step, result) in enumerate(steps, 1):
185
+ print(f"\\n🔄 Step {i}/5: {step}")
186
+ time.sleep(1)
187
+ print(f"✅ {result}")
188
+
189
+ print("\\n🎉 ETL Invoice Processing Department workflow completed!")
190
+ print("⏱️ Total time: 5.2s")
191
+ print("\\n📊 Demo completed successfully!")
192
+ print("This was a simplified demo. For the full experience, check out the complete ETL workflow.")
193
+
194
+ if __name__ == "__main__":
195
+ main()
196
+ """
197
+
198
+ with open(demo_dir / "etl_demo.py", "w") as f:
199
+ f.write(demo_content)
200
+
201
+ def setup_environment():
202
+ """Set up environment variables for the demo"""
203
+ # Set API key if not already set
204
+ if not os.getenv('MEMRA_API_KEY'):
205
+ os.environ['MEMRA_API_KEY'] = 'test-secret-for-development'
206
+ print("✅ Set MEMRA_API_KEY=test-secret-for-development")
207
+
208
+ # Set database URL
209
+ os.environ['DATABASE_URL'] = 'postgresql://postgres:postgres@localhost:5432/local_workflow'
210
+ print("✅ Set DATABASE_URL")
211
+
212
+ def start_docker_services(demo_dir):
213
+ """Start Docker containers using docker-compose"""
214
+ try:
215
+ ops_dir = demo_dir / "memra-ops"
216
+
217
+ # Check if Docker is running
218
+ result = subprocess.run(['docker', 'ps'], capture_output=True, text=True)
219
+ if result.returncode != 0:
220
+ print("❌ Docker is not running. Please start Docker Desktop.")
221
+ return False
222
+
223
+ # Start services
224
+ result = subprocess.run(
225
+ ['docker', 'compose', 'up', '-d'],
226
+ cwd=ops_dir,
227
+ capture_output=True,
228
+ text=True
229
+ )
230
+
231
+ if result.returncode == 0:
232
+ print("✅ Docker services started successfully")
233
+ return True
234
+ else:
235
+ print(f"❌ Failed to start Docker services: {result.stderr}")
236
+ return False
237
+
238
+ except FileNotFoundError:
239
+ print("❌ Docker not found. Please install Docker Desktop.")
240
+ return False
241
+ except Exception as e:
242
+ print(f"❌ Error starting Docker services: {e}")
243
+ return False
244
+
245
+ def wait_for_services():
246
+ """Wait for services to be ready"""
247
+ print("⏳ Waiting for PostgreSQL to be ready...")
248
+
249
+ # Wait for PostgreSQL
250
+ for i in range(30): # Wait up to 30 seconds
251
+ try:
252
+ result = subprocess.run([
253
+ 'docker', 'exec', 'memra_postgres',
254
+ 'pg_isready', '-U', 'postgres', '-d', 'local_workflow'
255
+ ], capture_output=True, text=True)
256
+
257
+ if result.returncode == 0:
258
+ print("✅ PostgreSQL is ready")
259
+ break
260
+ except:
261
+ pass
262
+
263
+ time.sleep(1)
264
+ if i % 5 == 0:
265
+ print(f" Still waiting... ({i+1}/30)")
266
+ else:
267
+ print("⚠️ PostgreSQL may not be fully ready, continuing anyway...")
268
+
269
+ def run_etl_workflow(demo_dir):
270
+ """Run the ETL workflow"""
271
+ try:
272
+ # Run the demo script
273
+ demo_script = demo_dir / "etl_demo.py"
274
+ if demo_script.exists():
275
+ result = subprocess.run([sys.executable, str(demo_script)], cwd=demo_dir)
276
+ return result.returncode == 0
277
+ else:
278
+ print("❌ Demo script not found")
279
+ return False
280
+
281
+ except Exception as e:
282
+ print(f"❌ Error running ETL workflow: {e}")
283
+ return False
284
+
285
+ def main():
286
+ """Main CLI entry point"""
287
+ if len(sys.argv) < 2:
288
+ print("Memra SDK - Declarative AI Workflows")
289
+ print("=" * 40)
290
+ print("Usage:")
291
+ print(" memra demo - Run the ETL invoice processing demo")
292
+ print(" memra --help - Show this help message")
293
+ print(" memra --version - Show version information")
294
+ return
295
+
296
+ command = sys.argv[1]
297
+
298
+ if command == "demo":
299
+ run_demo()
300
+ elif command == "--help" or command == "-h":
301
+ print("Memra SDK - Declarative AI Workflows")
302
+ print("=" * 40)
303
+ print("Commands:")
304
+ print(" demo - Run the ETL invoice processing demo")
305
+ print(" --help, -h - Show this help message")
306
+ print(" --version - Show version information")
307
+ elif command == "--version":
308
+ from . import __version__
309
+ print(f"memra {__version__}")
310
+ else:
311
+ print(f"Unknown command: {command}")
312
+ print("Run 'memra --help' for usage information")
313
+
314
+ if __name__ == "__main__":
315
+ main()