memra 0.2.12__py3-none-any.whl → 0.2.13__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/cli.py +114 -8
- memra/demos/etl_invoice_processing/check_after_workflow.py +50 -0
- memra/demos/etl_invoice_processing/check_database.py +44 -0
- memra/demos/etl_invoice_processing/check_recent_db.py +42 -0
- memra/demos/etl_invoice_processing/data/README.md +112 -0
- memra/demos/etl_invoice_processing/data/invoices/10352259401.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352259823.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352260169.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352260417.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352260599.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352260912.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352261134.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352261563.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352261647.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352261720.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352261811.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352262025.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352262454.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352262702.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352262884.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352263346.PDF +0 -0
- memra/demos/etl_invoice_processing/data/invoices/10352263429.PDF +0 -0
- memra/demos/etl_invoice_processing/database_monitor_agent.py +89 -0
- memra/demos/etl_invoice_processing/debug_mcp.py +66 -0
- memra/demos/etl_invoice_processing/debug_schema.py +45 -0
- memra/demos/etl_invoice_processing/etl_invoice_demo.py +1233 -0
- memra/demos/etl_invoice_processing/modify_database.py +65 -0
- memra/demos/etl_invoice_processing/run_etl_batch.py +60 -0
- memra/demos/etl_invoice_processing/setup_demo_data.py +154 -0
- memra/demos/etl_invoice_processing/simple_pdf_processor.py +181 -0
- memra/demos/etl_invoice_processing/test_agent3.py +56 -0
- memra/demos/etl_invoice_processing/test_agent3_v2.py +32 -0
- memra/demos/etl_invoice_processing/test_api.py +28 -0
- memra/demos/etl_invoice_processing/test_api_client_direct.py +89 -0
- memra/demos/etl_invoice_processing/test_conversion.py +172 -0
- memra/demos/etl_invoice_processing/test_debug.py +41 -0
- memra/demos/etl_invoice_processing/test_direct_vision.py +114 -0
- memra/demos/etl_invoice_processing/test_full_response.py +22 -0
- memra/demos/etl_invoice_processing/test_memra_response.py +124 -0
- memra/demos/etl_invoice_processing/test_pdf_processor_response.py +118 -0
- memra/demos/etl_invoice_processing/test_pdfprocessor_direct.py +96 -0
- memra/demos/etl_invoice_processing/test_postgres_insert.py +120 -0
- memra/demos/etl_invoice_processing/test_remote_upload.py +143 -0
- memra/demos/etl_invoice_processing/test_schema_format.py +39 -0
- memra/demos/etl_invoice_processing/test_sql_executor.py +58 -0
- memra/demos/etl_invoice_processing/test_sql_executor_extra_fields.py +61 -0
- memra/demos/etl_invoice_processing/test_sql_executor_fix.py +40 -0
- memra/demos/etl_invoice_processing/test_updated_server.py +50 -0
- memra/demos/etl_invoice_processing/test_upload_functionality.py +156 -0
- memra/demos/etl_invoice_processing/test_upload_server.py +232 -0
- memra/demos/etl_invoice_processing/test_vision_output.py +75 -0
- memra/demos/etl_invoice_processing/test_vision_prompt.py +43 -0
- memra/demos/etl_invoice_processing/test_vision_simple.py +60 -0
- {memra-0.2.12.dist-info → memra-0.2.13.dist-info}/METADATA +53 -78
- memra-0.2.13.dist-info/RECORD +120 -0
- {memra-0.2.12.dist-info → memra-0.2.13.dist-info}/WHEEL +1 -1
- memra-0.2.12.dist-info/RECORD +0 -68
- {memra-0.2.12.dist-info/licenses → memra-0.2.13.dist-info}/LICENSE +0 -0
- {memra-0.2.12.dist-info → memra-0.2.13.dist-info}/entry_points.txt +0 -0
- {memra-0.2.12.dist-info → memra-0.2.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Local Test Server for File Upload Functionality
|
4
|
+
This simulates the remote API upload endpoint for testing
|
5
|
+
"""
|
6
|
+
|
7
|
+
import base64
|
8
|
+
import os
|
9
|
+
import uuid
|
10
|
+
import asyncio
|
11
|
+
from datetime import datetime, timedelta
|
12
|
+
from fastapi import FastAPI, HTTPException
|
13
|
+
from pydantic import BaseModel
|
14
|
+
from typing import Optional
|
15
|
+
import uvicorn
|
16
|
+
|
17
|
+
# Models
|
18
|
+
class FileUploadRequest(BaseModel):
|
19
|
+
filename: str
|
20
|
+
content: str # base64 encoded
|
21
|
+
content_type: str
|
22
|
+
|
23
|
+
class FileUploadResponse(BaseModel):
|
24
|
+
success: bool
|
25
|
+
data: Optional[dict] = None
|
26
|
+
error: Optional[str] = None
|
27
|
+
|
28
|
+
class ToolExecuteRequest(BaseModel):
|
29
|
+
tool_name: str
|
30
|
+
hosted_by: str
|
31
|
+
input_data: dict
|
32
|
+
|
33
|
+
class ToolExecuteResponse(BaseModel):
|
34
|
+
success: bool
|
35
|
+
data: Optional[dict] = None
|
36
|
+
error: Optional[str] = None
|
37
|
+
|
38
|
+
# File storage configuration
|
39
|
+
UPLOAD_DIR = "/tmp/test_uploads"
|
40
|
+
FILE_EXPIRY_HOURS = 24
|
41
|
+
|
42
|
+
# Ensure upload directory exists
|
43
|
+
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
44
|
+
|
45
|
+
app = FastAPI(title="Test Upload Server", version="1.0.0")
|
46
|
+
|
47
|
+
@app.get("/health")
|
48
|
+
async def health_check():
|
49
|
+
"""Health check endpoint"""
|
50
|
+
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
|
51
|
+
|
52
|
+
@app.post("/upload", response_model=FileUploadResponse)
|
53
|
+
async def upload_file(request: FileUploadRequest):
|
54
|
+
"""Upload a file to the server for processing"""
|
55
|
+
try:
|
56
|
+
# Validate file type
|
57
|
+
if not request.content_type.startswith("application/pdf"):
|
58
|
+
raise HTTPException(status_code=400, detail="Only PDF files are supported")
|
59
|
+
|
60
|
+
# Validate file size (50MB limit)
|
61
|
+
try:
|
62
|
+
file_content = base64.b64decode(request.content)
|
63
|
+
if len(file_content) > 50 * 1024 * 1024: # 50MB
|
64
|
+
raise HTTPException(status_code=400, detail="File too large (max 50MB)")
|
65
|
+
except Exception as e:
|
66
|
+
raise HTTPException(status_code=400, detail="Invalid base64 content")
|
67
|
+
|
68
|
+
# Generate unique filename
|
69
|
+
file_id = str(uuid.uuid4())
|
70
|
+
file_extension = os.path.splitext(request.filename)[1]
|
71
|
+
remote_filename = f"{file_id}{file_extension}"
|
72
|
+
remote_path = os.path.join(UPLOAD_DIR, remote_filename)
|
73
|
+
|
74
|
+
# Save file
|
75
|
+
with open(remote_path, 'wb') as f:
|
76
|
+
f.write(file_content)
|
77
|
+
|
78
|
+
# Calculate expiry time
|
79
|
+
expires_at = datetime.utcnow() + timedelta(hours=FILE_EXPIRY_HOURS)
|
80
|
+
|
81
|
+
print(f"📤 Uploaded file: {request.filename} -> {remote_filename}")
|
82
|
+
|
83
|
+
return FileUploadResponse(
|
84
|
+
success=True,
|
85
|
+
data={
|
86
|
+
"remote_path": f"/uploads/{remote_filename}",
|
87
|
+
"file_id": file_id,
|
88
|
+
"expires_at": expires_at.isoformat(),
|
89
|
+
"original_filename": request.filename
|
90
|
+
}
|
91
|
+
)
|
92
|
+
|
93
|
+
except HTTPException:
|
94
|
+
raise
|
95
|
+
except Exception as e:
|
96
|
+
return FileUploadResponse(
|
97
|
+
success=False,
|
98
|
+
error=f"Upload failed: {str(e)}"
|
99
|
+
)
|
100
|
+
|
101
|
+
@app.post("/tools/execute", response_model=ToolExecuteResponse)
|
102
|
+
async def execute_tool(request: ToolExecuteRequest):
|
103
|
+
"""Execute a tool (simulated PDFProcessor)"""
|
104
|
+
try:
|
105
|
+
if request.tool_name == "PDFProcessor":
|
106
|
+
return await simulate_pdf_processor(request.input_data)
|
107
|
+
else:
|
108
|
+
return ToolExecuteResponse(
|
109
|
+
success=False,
|
110
|
+
error=f"Unknown tool: {request.tool_name}"
|
111
|
+
)
|
112
|
+
except Exception as e:
|
113
|
+
return ToolExecuteResponse(
|
114
|
+
success=False,
|
115
|
+
error=str(e)
|
116
|
+
)
|
117
|
+
|
118
|
+
async def simulate_pdf_processor(input_data: dict) -> ToolExecuteResponse:
|
119
|
+
"""Simulate PDF processing"""
|
120
|
+
try:
|
121
|
+
file_path = input_data.get('file', '')
|
122
|
+
|
123
|
+
if not file_path:
|
124
|
+
return ToolExecuteResponse(
|
125
|
+
success=False,
|
126
|
+
error="No file path provided"
|
127
|
+
)
|
128
|
+
|
129
|
+
# Handle uploaded files
|
130
|
+
if file_path.startswith('/uploads/'):
|
131
|
+
full_path = os.path.join(UPLOAD_DIR, os.path.basename(file_path))
|
132
|
+
else:
|
133
|
+
full_path = file_path
|
134
|
+
|
135
|
+
if not os.path.exists(full_path):
|
136
|
+
return ToolExecuteResponse(
|
137
|
+
success=False,
|
138
|
+
error=f"PDF file not found: {file_path}"
|
139
|
+
)
|
140
|
+
|
141
|
+
# Simulate processing (in real implementation, this would use vision model)
|
142
|
+
print(f"🔍 Processing PDF: {file_path}")
|
143
|
+
|
144
|
+
# Mock extracted data
|
145
|
+
invoice_data = {
|
146
|
+
"headerSection": {
|
147
|
+
"vendorName": "Test Vendor Corp",
|
148
|
+
"subtotal": 1234.56
|
149
|
+
},
|
150
|
+
"billingDetails": {
|
151
|
+
"invoiceNumber": "INV-001",
|
152
|
+
"invoiceDate": "2024-01-15"
|
153
|
+
},
|
154
|
+
"chargesSummary": {
|
155
|
+
"document_total": 1395.05,
|
156
|
+
"secondary_tax": 160.49,
|
157
|
+
"lineItemsBreakdown": [
|
158
|
+
{
|
159
|
+
"description": "Test Product",
|
160
|
+
"quantity": 1,
|
161
|
+
"unit_price": 1234.56,
|
162
|
+
"amount": 1234.56,
|
163
|
+
"main_product": True
|
164
|
+
}
|
165
|
+
]
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
return ToolExecuteResponse(
|
170
|
+
success=True,
|
171
|
+
data={
|
172
|
+
"file_path": file_path,
|
173
|
+
"extracted_data": invoice_data
|
174
|
+
}
|
175
|
+
)
|
176
|
+
|
177
|
+
except Exception as e:
|
178
|
+
return ToolExecuteResponse(
|
179
|
+
success=False,
|
180
|
+
error=str(e)
|
181
|
+
)
|
182
|
+
|
183
|
+
@app.get("/tools/discover")
|
184
|
+
async def discover_tools():
|
185
|
+
"""Discover available tools"""
|
186
|
+
return {
|
187
|
+
"tools": [
|
188
|
+
{
|
189
|
+
"name": "PDFProcessor",
|
190
|
+
"hosted_by": "memra",
|
191
|
+
"description": "Process PDF files and extract content"
|
192
|
+
},
|
193
|
+
{
|
194
|
+
"name": "InvoiceExtractionWorkflow",
|
195
|
+
"hosted_by": "memra",
|
196
|
+
"description": "Extract structured data from invoices"
|
197
|
+
}
|
198
|
+
]
|
199
|
+
}
|
200
|
+
|
201
|
+
async def cleanup_expired_files():
|
202
|
+
"""Remove files older than FILE_EXPIRY_HOURS"""
|
203
|
+
while True:
|
204
|
+
try:
|
205
|
+
current_time = datetime.utcnow()
|
206
|
+
for filename in os.listdir(UPLOAD_DIR):
|
207
|
+
file_path = os.path.join(UPLOAD_DIR, filename)
|
208
|
+
file_time = datetime.fromtimestamp(os.path.getctime(file_path))
|
209
|
+
|
210
|
+
if current_time - file_time > timedelta(hours=FILE_EXPIRY_HOURS):
|
211
|
+
try:
|
212
|
+
os.remove(file_path)
|
213
|
+
print(f"🧹 Cleaned up expired file: {filename}")
|
214
|
+
except Exception as e:
|
215
|
+
print(f"Failed to clean up {filename}: {e}")
|
216
|
+
|
217
|
+
except Exception as e:
|
218
|
+
print(f"File cleanup error: {e}")
|
219
|
+
|
220
|
+
await asyncio.sleep(3600) # Run every hour
|
221
|
+
|
222
|
+
@app.on_event("startup")
|
223
|
+
async def start_cleanup():
|
224
|
+
asyncio.create_task(cleanup_expired_files())
|
225
|
+
|
226
|
+
if __name__ == "__main__":
|
227
|
+
print("🚀 Starting Test Upload Server...")
|
228
|
+
print(f"📁 Upload directory: {UPLOAD_DIR}")
|
229
|
+
print("🌐 Server will be available at: http://localhost:8000")
|
230
|
+
print("📚 API docs available at: http://localhost:8000/docs")
|
231
|
+
|
232
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import os
|
2
|
+
import requests
|
3
|
+
import json
|
4
|
+
|
5
|
+
api_url = "https://api.memra.co"
|
6
|
+
api_key = os.getenv("MEMRA_API_KEY", "test-secret-for-development")
|
7
|
+
|
8
|
+
# Test with a comprehensive schema that includes all fields
|
9
|
+
schema = [
|
10
|
+
{"column_name": "vendor_name", "data_type": "character varying"},
|
11
|
+
{"column_name": "invoice_number", "data_type": "character varying"},
|
12
|
+
{"column_name": "invoice_date", "data_type": "date"},
|
13
|
+
{"column_name": "due_date", "data_type": "date"},
|
14
|
+
{"column_name": "total_amount", "data_type": "numeric"},
|
15
|
+
{"column_name": "tax_amount", "data_type": "numeric"},
|
16
|
+
{"column_name": "line_items", "data_type": "jsonb"}
|
17
|
+
]
|
18
|
+
|
19
|
+
resp = requests.post(
|
20
|
+
f"{api_url}/tools/execute",
|
21
|
+
json={
|
22
|
+
"tool_name": "PDFProcessor",
|
23
|
+
"hosted_by": "memra",
|
24
|
+
"input_data": {
|
25
|
+
"file": "/uploads/6f4538c0-8fce-4488-be49-1a78afc58a4a.pdf",
|
26
|
+
"schema": schema
|
27
|
+
}
|
28
|
+
},
|
29
|
+
headers={"X-API-Key": api_key}
|
30
|
+
)
|
31
|
+
|
32
|
+
print(f"Status: {resp.status_code}")
|
33
|
+
if resp.status_code == 200:
|
34
|
+
result = resp.json()
|
35
|
+
print(f"\nSuccess: {result.get('success')}")
|
36
|
+
|
37
|
+
if 'data' in result and 'data' in result['data']:
|
38
|
+
inner_data = result['data']['data']
|
39
|
+
|
40
|
+
# Print the vision response
|
41
|
+
if 'vision_response' in inner_data:
|
42
|
+
print("\n=== VISION MODEL RAW RESPONSE ===")
|
43
|
+
vision_resp = inner_data['vision_response']
|
44
|
+
try:
|
45
|
+
# Try to parse and pretty print if it's JSON
|
46
|
+
parsed = json.loads(vision_resp)
|
47
|
+
print(json.dumps(parsed, indent=2))
|
48
|
+
except:
|
49
|
+
print(vision_resp)
|
50
|
+
print("=" * 40)
|
51
|
+
|
52
|
+
# Print the extracted data after transformation
|
53
|
+
if 'extracted_data' in inner_data:
|
54
|
+
print("\n=== TRANSFORMED DATA (MCP Format) ===")
|
55
|
+
extracted = inner_data['extracted_data']
|
56
|
+
print(json.dumps(extracted, indent=2))
|
57
|
+
|
58
|
+
# Check specific fields
|
59
|
+
print("\n=== FIELD ANALYSIS ===")
|
60
|
+
billing = extracted.get('billingDetails', {})
|
61
|
+
charges = extracted.get('chargesSummary', {})
|
62
|
+
|
63
|
+
print(f"due_date: '{billing.get('dueDate', 'MISSING')}'")
|
64
|
+
print(f"total_amount: {charges.get('document_total', 'MISSING')}")
|
65
|
+
print(f"tax_amount: {charges.get('secondary_tax', 'MISSING')}")
|
66
|
+
print("=" * 40)
|
67
|
+
|
68
|
+
# Save to file for easier viewing
|
69
|
+
with open("vision_output.json", "w") as f:
|
70
|
+
json.dump({
|
71
|
+
"vision_response": inner_data.get('vision_response', ''),
|
72
|
+
"extracted_data": extracted
|
73
|
+
}, f, indent=2)
|
74
|
+
else:
|
75
|
+
print(f"Error: {resp.text}")
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Test what happens when we pass schema to PDFProcessor
|
2
|
+
import os
|
3
|
+
import requests
|
4
|
+
import json
|
5
|
+
|
6
|
+
api_url = "https://api.memra.co"
|
7
|
+
api_key = os.getenv("MEMRA_API_KEY", "test-secret-for-development")
|
8
|
+
|
9
|
+
# Schema that matches database fields
|
10
|
+
schema = [
|
11
|
+
{"column_name": "invoice_number", "data_type": "character varying", "is_nullable": "NO"},
|
12
|
+
{"column_name": "vendor_name", "data_type": "character varying", "is_nullable": "NO"},
|
13
|
+
{"column_name": "invoice_date", "data_type": "date", "is_nullable": "NO"},
|
14
|
+
{"column_name": "total_amount", "data_type": "numeric", "is_nullable": "NO"}
|
15
|
+
]
|
16
|
+
|
17
|
+
print("Testing with database schema format:")
|
18
|
+
resp = requests.post(
|
19
|
+
f"{api_url}/tools/execute",
|
20
|
+
json={
|
21
|
+
"tool_name": "PDFProcessor",
|
22
|
+
"hosted_by": "memra",
|
23
|
+
"input_data": {
|
24
|
+
"file": "/uploads/6f4538c0-8fce-4488-be49-1a78afc58a4a.pdf",
|
25
|
+
"schema": schema
|
26
|
+
}
|
27
|
+
},
|
28
|
+
headers={"X-API-Key": api_key}
|
29
|
+
)
|
30
|
+
|
31
|
+
if resp.status_code == 200:
|
32
|
+
result = resp.json()
|
33
|
+
if result.get('success') and 'data' in result and 'data' in result['data']:
|
34
|
+
vision_prompt = result['data']['data'].get('vision_prompt', '')
|
35
|
+
print("\nVision prompt with schema:")
|
36
|
+
print(vision_prompt)
|
37
|
+
|
38
|
+
# Check if vendor was extracted
|
39
|
+
vision_resp = result['data']['data'].get('vision_response', '')
|
40
|
+
if 'vendor' in vision_resp.lower():
|
41
|
+
print("\n✅ Vision model extracted vendor info\!")
|
42
|
+
else:
|
43
|
+
print("\n❌ Vision model did not extract vendor info")
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import os
|
2
|
+
import requests
|
3
|
+
import json
|
4
|
+
|
5
|
+
api_url = "https://api.memra.co"
|
6
|
+
api_key = os.getenv("MEMRA_API_KEY", "test-secret-for-development")
|
7
|
+
|
8
|
+
# Test with a comprehensive schema that includes all fields
|
9
|
+
schema = [
|
10
|
+
{"column_name": "vendor_name", "data_type": "character varying"},
|
11
|
+
{"column_name": "invoice_number", "data_type": "character varying"},
|
12
|
+
{"column_name": "invoice_date", "data_type": "date"},
|
13
|
+
{"column_name": "due_date", "data_type": "date"},
|
14
|
+
{"column_name": "total_amount", "data_type": "numeric"},
|
15
|
+
{"column_name": "tax_amount", "data_type": "numeric"},
|
16
|
+
{"column_name": "line_items", "data_type": "jsonb"}
|
17
|
+
]
|
18
|
+
|
19
|
+
resp = requests.post(
|
20
|
+
f"{api_url}/tools/execute",
|
21
|
+
json={
|
22
|
+
"tool_name": "PDFProcessor",
|
23
|
+
"hosted_by": "memra",
|
24
|
+
"input_data": {
|
25
|
+
"file": "/uploads/6f4538c0-8fce-4488-be49-1a78afc58a4a.pdf",
|
26
|
+
"schema": schema
|
27
|
+
}
|
28
|
+
},
|
29
|
+
headers={"X-API-Key": api_key}
|
30
|
+
)
|
31
|
+
|
32
|
+
print(f"Status: {resp.status_code}")
|
33
|
+
if resp.status_code == 200:
|
34
|
+
result = resp.json()
|
35
|
+
|
36
|
+
if 'data' in result and 'data' in result['data']:
|
37
|
+
inner_data = result['data']['data']
|
38
|
+
|
39
|
+
# Get the vision response
|
40
|
+
if 'vision_response' in inner_data:
|
41
|
+
try:
|
42
|
+
vision_parsed = json.loads(inner_data['vision_response'])
|
43
|
+
print("\nVision Model Response Fields:")
|
44
|
+
for key, value in vision_parsed.items():
|
45
|
+
print(f" {key}: {value}")
|
46
|
+
except:
|
47
|
+
print("Could not parse vision response as JSON")
|
48
|
+
|
49
|
+
# Get the extracted data
|
50
|
+
if 'extracted_data' in inner_data:
|
51
|
+
extracted = inner_data['extracted_data']
|
52
|
+
billing = extracted.get('billingDetails', {})
|
53
|
+
charges = extracted.get('chargesSummary', {})
|
54
|
+
|
55
|
+
print("\nExtracted Fields:")
|
56
|
+
print(f" due_date: '{billing.get('dueDate', 'MISSING')}'")
|
57
|
+
print(f" total_amount: {charges.get('document_total', 'MISSING')}")
|
58
|
+
print(f" tax_amount: {charges.get('secondary_tax', 'MISSING')}")
|
59
|
+
else:
|
60
|
+
print(f"Error: {resp.text}")
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.1
|
2
2
|
Name: memra
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.13
|
4
4
|
Summary: Declarative framework for enterprise workflows with MCP integration - Client SDK
|
5
5
|
Home-page: https://github.com/memra/memra-sdk
|
6
6
|
Author: Memra
|
@@ -20,22 +20,18 @@ Classifier: Programming Language :: Python :: 3.11
|
|
20
20
|
Requires-Python: >=3.8
|
21
21
|
Description-Content-Type: text/markdown
|
22
22
|
License-File: LICENSE
|
23
|
-
Requires-Dist: pydantic>=1.8.0
|
24
|
-
Requires-Dist: httpx>=0.24.0
|
25
|
-
Requires-Dist: typing-extensions>=4.0.0
|
26
|
-
Requires-Dist: aiohttp>=3.8.0
|
27
|
-
Requires-Dist: aiohttp-cors>=0.7.0
|
23
|
+
Requires-Dist: pydantic >=1.8.0
|
24
|
+
Requires-Dist: httpx >=0.24.0
|
25
|
+
Requires-Dist: typing-extensions >=4.0.0
|
26
|
+
Requires-Dist: aiohttp >=3.8.0
|
27
|
+
Requires-Dist: aiohttp-cors >=0.7.0
|
28
28
|
Provides-Extra: dev
|
29
|
-
Requires-Dist: pytest>=6.0; extra ==
|
30
|
-
Requires-Dist: pytest-asyncio; extra ==
|
31
|
-
Requires-Dist: black; extra ==
|
32
|
-
Requires-Dist: flake8; extra ==
|
29
|
+
Requires-Dist: pytest >=6.0 ; extra == 'dev'
|
30
|
+
Requires-Dist: pytest-asyncio ; extra == 'dev'
|
31
|
+
Requires-Dist: black ; extra == 'dev'
|
32
|
+
Requires-Dist: flake8 ; extra == 'dev'
|
33
33
|
Provides-Extra: mcp
|
34
|
-
Requires-Dist: psycopg2-binary>=2.9.0; extra ==
|
35
|
-
Dynamic: author
|
36
|
-
Dynamic: home-page
|
37
|
-
Dynamic: license-file
|
38
|
-
Dynamic: requires-python
|
34
|
+
Requires-Dist: psycopg2-binary >=2.9.0 ; extra == 'mcp'
|
39
35
|
|
40
36
|
# Memra SDK
|
41
37
|
|
@@ -56,24 +52,26 @@ Building AI-powered business workflows is hard. You need to:
|
|
56
52
|
|
57
53
|
**Memra solves these challenges** by providing a declarative framework that lets you focus on *what* you want to accomplish, not *how* to wire everything together.
|
58
54
|
|
59
|
-
## 🚀 Quick Start (
|
55
|
+
## 🚀 Quick Start (2 minutes!)
|
60
56
|
|
61
|
-
**Want to see Memra in action immediately?**
|
62
|
-
|
63
|
-
### Installation
|
57
|
+
**Want to see Memra in action immediately?**
|
64
58
|
|
65
59
|
```bash
|
60
|
+
# Install Memra
|
66
61
|
pip install memra
|
67
|
-
```
|
68
62
|
|
69
|
-
|
70
|
-
|
71
|
-
```bash
|
72
|
-
# Automated setup for new users
|
73
|
-
bash scripts/setup_newbie.sh
|
63
|
+
# Run the ETL demo
|
64
|
+
memra demo
|
74
65
|
```
|
75
66
|
|
76
|
-
|
67
|
+
**That's it!** The demo will automatically:
|
68
|
+
- ✅ Set up PostgreSQL database
|
69
|
+
- ✅ Start MCP bridge server
|
70
|
+
- ✅ Process PDF invoices with AI vision
|
71
|
+
- ✅ Store data in the database
|
72
|
+
- ✅ Show you the results
|
73
|
+
|
74
|
+
**📖 Need more details?** Check out our [Quick Start Guide](QUICK_START.md) for step-by-step instructions.
|
77
75
|
|
78
76
|
### Basic Example
|
79
77
|
|
@@ -139,7 +137,7 @@ Configuration for language models used by agents, supporting:
|
|
139
137
|
|
140
138
|
```bash
|
141
139
|
# Run the ETL demo
|
142
|
-
|
140
|
+
memra demo
|
143
141
|
```
|
144
142
|
|
145
143
|
This demo showcases:
|
@@ -241,43 +239,19 @@ Scale from prototype to production:
|
|
241
239
|
## 📚 Documentation
|
242
240
|
|
243
241
|
- **[Quick Start Guide](QUICK_START.md)** - Get up and running in 5 minutes
|
244
|
-
- **[
|
242
|
+
- **[Installation Guide](INSTALLATION_GUIDE.md)** - Detailed setup instructions
|
243
|
+
- **[Text-to-SQL Guide](TEXT_TO_SQL_USAGE_GUIDE.md)** - Build SQL queries with natural language
|
244
|
+
- **[Contributing](CONTRIBUTING.md)** - How to contribute to Memra
|
245
245
|
- **[System Architecture](memra_system_architecture.md)** - Deep dive into Memra's design
|
246
|
-
- **[Text-to-SQL Guide](TEXT_TO_SQL_USAGE_GUIDE.md)** - Building database query workflows
|
247
|
-
- **[Examples Directory](examples/)** - Complete working examples
|
248
|
-
- **[Demos Directory](demos/)** - Advanced workflow demonstrations
|
249
|
-
|
250
|
-
## 🏢 Use Cases
|
251
|
-
|
252
|
-
### Financial Services
|
253
|
-
- **Invoice processing** and accounts payable automation
|
254
|
-
- **Document classification** and routing
|
255
|
-
- **Compliance monitoring** and reporting
|
256
|
-
- **Risk assessment** and fraud detection
|
257
|
-
|
258
|
-
### Healthcare
|
259
|
-
- **Medical record processing** and data extraction
|
260
|
-
- **Claims processing** and validation
|
261
|
-
- **Patient data analysis** and insights
|
262
|
-
- **Regulatory compliance** workflows
|
263
|
-
|
264
|
-
### Manufacturing
|
265
|
-
- **Quality control** and inspection workflows
|
266
|
-
- **Supply chain** optimization and monitoring
|
267
|
-
- **Equipment maintenance** scheduling
|
268
|
-
- **Production planning** and optimization
|
269
|
-
|
270
|
-
### Retail & E-commerce
|
271
|
-
- **Order processing** and fulfillment
|
272
|
-
- **Customer service** automation
|
273
|
-
- **Inventory management** and forecasting
|
274
|
-
- **Market analysis** and trend detection
|
275
246
|
|
276
|
-
##
|
247
|
+
## 🔧 Development
|
277
248
|
|
278
|
-
|
249
|
+
### Prerequisites
|
250
|
+
- Python 3.8+
|
251
|
+
- Docker (for database and services)
|
252
|
+
- Git
|
279
253
|
|
280
|
-
### Development Setup
|
254
|
+
### Local Development Setup
|
281
255
|
|
282
256
|
```bash
|
283
257
|
# Clone the repository
|
@@ -287,33 +261,34 @@ cd memra-sdk
|
|
287
261
|
# Install in development mode
|
288
262
|
pip install -e .
|
289
263
|
|
290
|
-
# Run
|
291
|
-
|
264
|
+
# Run the demo
|
265
|
+
memra demo
|
266
|
+
```
|
267
|
+
|
268
|
+
### Running Tests
|
269
|
+
|
270
|
+
```bash
|
271
|
+
# Run all tests
|
272
|
+
python -m pytest tests/
|
273
|
+
|
274
|
+
# Run specific test file
|
275
|
+
python -m pytest tests/test_execution.py
|
292
276
|
```
|
293
277
|
|
294
278
|
## 📄 License
|
295
279
|
|
296
280
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
297
281
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
- ✅ **Distribution** - Distribute copies
|
302
|
-
- ✅ **Private use** - Use privately
|
303
|
-
- ❌ **Liability** - No warranty provided
|
304
|
-
- ❌ **Warranty** - No warranty provided
|
282
|
+
## 🤝 Contributing
|
283
|
+
|
284
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and contribute to the project.
|
305
285
|
|
306
|
-
##
|
286
|
+
## 📞 Support
|
307
287
|
|
288
|
+
- **Documentation**: Check our guides above
|
308
289
|
- **Issues**: [GitHub Issues](https://github.com/memra-platform/memra-sdk/issues)
|
309
290
|
- **Discussions**: [GitHub Discussions](https://github.com/memra-platform/memra-sdk/discussions)
|
310
|
-
- **Email**: hello@memra.com
|
311
|
-
|
312
|
-
## 🔗 Related Projects
|
313
|
-
|
314
|
-
- **[memra-workflows](https://github.com/memra-platform/memra-workflows)** - Production workflow templates
|
315
|
-
- **[memra-ops](https://github.com/memra-platform/memra-ops)** - Operations and deployment tools
|
316
291
|
|
317
292
|
---
|
318
293
|
|
319
|
-
**
|
294
|
+
**Ready to build AI workflows that scale?** Start with `pip install memra` and `memra demo`! 🚀
|