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 +16 -2
- memra/cli.py +315 -0
- memra/execution.py +217 -46
- memra/models.py +1 -0
- memra/tool_registry_client.py +2 -2
- memra-0.2.6.dist-info/METADATA +319 -0
- {memra-0.2.4.dist-info → memra-0.2.6.dist-info}/RECORD +24 -14
- memra-0.2.6.dist-info/licenses/LICENSE +21 -0
- memra-ops/app.py +98 -0
- memra-ops/logic/__init__.py +1 -0
- memra-ops/logic/file_tools.py +43 -0
- memra-ops/logic/invoice_tools.py +668 -0
- memra-ops/logic/invoice_tools_fix.py +66 -0
- memra-ops/mcp_bridge_server.py +1178 -0
- memra-ops/scripts/check_database.py +37 -0
- memra-ops/scripts/clear_database.py +48 -0
- memra-ops/scripts/monitor_database.py +67 -0
- memra-ops/scripts/reset_database.py +65 -0
- memra-ops/server_tool_registry.py +3 -1
- memra-sdk/memra/__init__.py +1 -1
- memra-sdk/setup.py +1 -1
- memra-0.2.4.dist-info/METADATA +0 -145
- memra-0.2.4.dist-info/licenses/LICENSE +0 -0
- {memra-0.2.4.dist-info → memra-0.2.6.dist-info}/WHEEL +0 -0
- {memra-0.2.4.dist-info → memra-0.2.6.dist-info}/entry_points.txt +0 -0
- {memra-0.2.4.dist-info → memra-0.2.6.dist-info}/top_level.txt +0 -0
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.
|
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()
|