memra 0.2.11__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/__init__.py +1 -1
- memra/cli.py +129 -9
- 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.11.dist-info → memra-0.2.13.dist-info}/METADATA +53 -78
- memra-0.2.13.dist-info/RECORD +120 -0
- {memra-0.2.11.dist-info → memra-0.2.13.dist-info}/WHEEL +1 -1
- memra-0.2.11.dist-info/RECORD +0 -68
- {memra-0.2.11.dist-info/licenses → memra-0.2.13.dist-info}/LICENSE +0 -0
- {memra-0.2.11.dist-info → memra-0.2.13.dist-info}/entry_points.txt +0 -0
- {memra-0.2.11.dist-info → memra-0.2.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test PostgresInsert tool with new invoice_json table
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import sys
|
8
|
+
import json
|
9
|
+
import requests
|
10
|
+
|
11
|
+
# Add the parent directory to the path to import memra
|
12
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
13
|
+
|
14
|
+
from memra.tool_registry import ToolRegistry
|
15
|
+
|
16
|
+
def test_postgres_insert():
|
17
|
+
"""Test PostgresInsert tool with sample invoice data"""
|
18
|
+
|
19
|
+
# Sample invoice data that includes due date
|
20
|
+
sample_invoice_data = {
|
21
|
+
"headerSection": {
|
22
|
+
"vendorName": "Test Vendor Inc.",
|
23
|
+
"subtotal": 1500.00
|
24
|
+
},
|
25
|
+
"billingDetails": {
|
26
|
+
"invoiceNumber": "TEST-001",
|
27
|
+
"invoiceDate": "2024-12-01",
|
28
|
+
"dueDate": "2024-12-31" # This was previously lost!
|
29
|
+
},
|
30
|
+
"chargesSummary": {
|
31
|
+
"document_total": 1695.00,
|
32
|
+
"secondary_tax": 195.00,
|
33
|
+
"lineItemsBreakdown": [
|
34
|
+
{
|
35
|
+
"description": "Test Service",
|
36
|
+
"quantity": 1,
|
37
|
+
"unit_price": 1500.00,
|
38
|
+
"amount": 1500.00
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
print("🔧 Testing PostgresInsert tool with new invoice_json table...")
|
45
|
+
print(f"📄 Sample data includes due date: {sample_invoice_data['billingDetails']['dueDate']}")
|
46
|
+
|
47
|
+
# Test input data
|
48
|
+
input_data = {
|
49
|
+
"invoice_data": sample_invoice_data,
|
50
|
+
"table_name": "invoice_json"
|
51
|
+
}
|
52
|
+
|
53
|
+
# Execute the tool
|
54
|
+
try:
|
55
|
+
registry = ToolRegistry()
|
56
|
+
result = registry.execute_tool(
|
57
|
+
tool_name="PostgresInsert",
|
58
|
+
hosted_by="mcp",
|
59
|
+
input_data=input_data,
|
60
|
+
config={
|
61
|
+
"bridge_url": "http://localhost:8081",
|
62
|
+
"bridge_secret": "test-secret-for-development"
|
63
|
+
}
|
64
|
+
)
|
65
|
+
|
66
|
+
print(f"📊 Result: {json.dumps(result, indent=2)}")
|
67
|
+
|
68
|
+
if result.get("success"):
|
69
|
+
print("✅ PostgresInsert executed successfully!")
|
70
|
+
print(f"📝 Record ID: {result['data']['record_id']}")
|
71
|
+
print(f"📊 JSON size: {result['data']['inserted_data']['raw_json_size']} bytes")
|
72
|
+
print(f"🔑 JSON keys: {result['data']['inserted_data']['json_keys']}")
|
73
|
+
|
74
|
+
# Now let's verify the data was stored correctly
|
75
|
+
print("\n🔍 Verifying stored data...")
|
76
|
+
verify_stored_data(result['data']['record_id'])
|
77
|
+
|
78
|
+
else:
|
79
|
+
print(f"❌ PostgresInsert failed: {result.get('error')}")
|
80
|
+
|
81
|
+
except Exception as e:
|
82
|
+
print(f"❌ Error executing PostgresInsert: {str(e)}")
|
83
|
+
|
84
|
+
def verify_stored_data(record_id):
|
85
|
+
"""Verify that the data was stored correctly in the database"""
|
86
|
+
|
87
|
+
# Query to get the stored JSON
|
88
|
+
query = f"SELECT raw_json FROM invoice_json WHERE id = {record_id}"
|
89
|
+
|
90
|
+
try:
|
91
|
+
registry = ToolRegistry()
|
92
|
+
result = registry.execute_tool(
|
93
|
+
tool_name="SQLExecutor",
|
94
|
+
hosted_by="mcp",
|
95
|
+
input_data={"sql_query": query},
|
96
|
+
config={
|
97
|
+
"bridge_url": "http://localhost:8081",
|
98
|
+
"bridge_secret": "test-secret-for-development"
|
99
|
+
}
|
100
|
+
)
|
101
|
+
|
102
|
+
if result.get("success") and result["data"]["results"]:
|
103
|
+
stored_json = result["data"]["results"][0]["raw_json"]
|
104
|
+
print(f"📄 Stored JSON: {json.dumps(stored_json, indent=2)}")
|
105
|
+
|
106
|
+
# Check if due date is preserved
|
107
|
+
due_date = stored_json.get("billingDetails", {}).get("dueDate")
|
108
|
+
if due_date:
|
109
|
+
print(f"✅ Due date preserved: {due_date}")
|
110
|
+
else:
|
111
|
+
print("❌ Due date not found in stored data")
|
112
|
+
|
113
|
+
else:
|
114
|
+
print(f"❌ Failed to retrieve stored data: {result.get('error')}")
|
115
|
+
|
116
|
+
except Exception as e:
|
117
|
+
print(f"❌ Error verifying stored data: {str(e)}")
|
118
|
+
|
119
|
+
if __name__ == "__main__":
|
120
|
+
test_postgres_insert()
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script to verify file upload functionality with remote API
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import base64
|
8
|
+
import requests
|
9
|
+
import json
|
10
|
+
|
11
|
+
def test_remote_upload():
|
12
|
+
"""Test the complete upload and processing workflow with remote API"""
|
13
|
+
|
14
|
+
# Remote API URL
|
15
|
+
API_BASE = "https://api.memra.co"
|
16
|
+
API_KEY = "test-secret-for-development"
|
17
|
+
|
18
|
+
print("🧪 Testing Remote API File Upload Functionality")
|
19
|
+
print("=" * 50)
|
20
|
+
|
21
|
+
# Step 1: Check server health
|
22
|
+
print("\n1️⃣ Checking server health...")
|
23
|
+
try:
|
24
|
+
response = requests.get(f"{API_BASE}/health")
|
25
|
+
if response.status_code == 200:
|
26
|
+
print("✅ Server is healthy")
|
27
|
+
else:
|
28
|
+
print(f"❌ Server health check failed: {response.status_code}")
|
29
|
+
return
|
30
|
+
except Exception as e:
|
31
|
+
print(f"❌ Cannot connect to server: {e}")
|
32
|
+
return
|
33
|
+
|
34
|
+
# Step 2: Discover available tools
|
35
|
+
print("\n2️⃣ Discovering available tools...")
|
36
|
+
try:
|
37
|
+
response = requests.get(f"{API_BASE}/tools/discover", headers={"X-API-Key": API_KEY})
|
38
|
+
if response.status_code == 200:
|
39
|
+
tools = response.json().get("tools", [])
|
40
|
+
print(f"✅ Found {len(tools)} tools:")
|
41
|
+
for tool in tools:
|
42
|
+
print(f" - {tool['name']}: {tool['description']}")
|
43
|
+
else:
|
44
|
+
print(f"❌ Tool discovery failed: {response.status_code}")
|
45
|
+
print(f" Response: {response.text}")
|
46
|
+
return
|
47
|
+
except Exception as e:
|
48
|
+
print(f"❌ Tool discovery error: {e}")
|
49
|
+
return
|
50
|
+
|
51
|
+
# Step 3: Test with a real PDF file
|
52
|
+
print("\n3️⃣ Testing with real PDF file...")
|
53
|
+
pdf_path = "data/invoices/10352260169.PDF"
|
54
|
+
|
55
|
+
if not os.path.exists(pdf_path):
|
56
|
+
print(f"❌ PDF file not found: {pdf_path}")
|
57
|
+
return
|
58
|
+
|
59
|
+
print(f"✅ Found PDF file: {pdf_path}")
|
60
|
+
|
61
|
+
# Step 4: Upload the file
|
62
|
+
print("\n4️⃣ Uploading file to remote API...")
|
63
|
+
try:
|
64
|
+
with open(pdf_path, 'rb') as f:
|
65
|
+
file_content = f.read()
|
66
|
+
|
67
|
+
file_b64 = base64.b64encode(file_content).decode('utf-8')
|
68
|
+
|
69
|
+
upload_data = {
|
70
|
+
"filename": os.path.basename(pdf_path),
|
71
|
+
"content": file_b64,
|
72
|
+
"content_type": "application/pdf"
|
73
|
+
}
|
74
|
+
|
75
|
+
response = requests.post(
|
76
|
+
f"{API_BASE}/upload",
|
77
|
+
json=upload_data,
|
78
|
+
headers={
|
79
|
+
"X-API-Key": API_KEY,
|
80
|
+
"Content-Type": "application/json"
|
81
|
+
}
|
82
|
+
)
|
83
|
+
|
84
|
+
print(f"Upload response status: {response.status_code}")
|
85
|
+
print(f"Upload response: {response.text}")
|
86
|
+
|
87
|
+
if response.status_code == 200:
|
88
|
+
result = response.json()
|
89
|
+
if result.get("success"):
|
90
|
+
remote_path = result["data"]["remote_path"]
|
91
|
+
print(f"✅ File uploaded successfully")
|
92
|
+
print(f" Remote path: {remote_path}")
|
93
|
+
print(f" File ID: {result['data']['file_id']}")
|
94
|
+
|
95
|
+
# Step 5: Process the uploaded file
|
96
|
+
print("\n5️⃣ Processing uploaded file...")
|
97
|
+
process_data = {
|
98
|
+
"tool_name": "PDFProcessor",
|
99
|
+
"hosted_by": "memra",
|
100
|
+
"input_data": {
|
101
|
+
"file": remote_path
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
response = requests.post(
|
106
|
+
f"{API_BASE}/tools/execute",
|
107
|
+
json=process_data,
|
108
|
+
headers={
|
109
|
+
"X-API-Key": API_KEY,
|
110
|
+
"Content-Type": "application/json"
|
111
|
+
}
|
112
|
+
)
|
113
|
+
|
114
|
+
print(f"Processing response status: {response.status_code}")
|
115
|
+
print(f"Processing response: {response.text}")
|
116
|
+
|
117
|
+
if response.status_code == 200:
|
118
|
+
result = response.json()
|
119
|
+
if result.get("success") and result.get("data", {}).get("success"):
|
120
|
+
extracted_data = result["data"]["data"].get("extracted_data", {})
|
121
|
+
print("✅ File processed successfully")
|
122
|
+
print("📄 Extracted data:")
|
123
|
+
print(f" Vendor: {extracted_data.get('headerSection', {}).get('vendorName', 'N/A')}")
|
124
|
+
print(f" Invoice: {extracted_data.get('billingDetails', {}).get('invoiceNumber', 'N/A')}")
|
125
|
+
print(f" Amount: ${extracted_data.get('chargesSummary', {}).get('document_total', 'N/A')}")
|
126
|
+
else:
|
127
|
+
print(f"❌ Processing failed: {result.get('data', {}).get('error', 'Unknown error')}")
|
128
|
+
else:
|
129
|
+
print(f"❌ Processing request failed: {response.status_code}")
|
130
|
+
print(f" Response: {response.text}")
|
131
|
+
else:
|
132
|
+
print(f"❌ Upload failed: {result.get('error')}")
|
133
|
+
else:
|
134
|
+
print(f"❌ Upload request failed: {response.status_code}")
|
135
|
+
print(f" Response: {response.text}")
|
136
|
+
|
137
|
+
except Exception as e:
|
138
|
+
print(f"❌ Upload/Processing error: {e}")
|
139
|
+
|
140
|
+
print("\n🎉 Test completed!")
|
141
|
+
|
142
|
+
if __name__ == "__main__":
|
143
|
+
test_remote_upload()
|
@@ -0,0 +1,39 @@
|
|
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 more detailed schema format
|
9
|
+
schema = {
|
10
|
+
"fields": [
|
11
|
+
{"name": "invoice_number", "type": "string", "required": True},
|
12
|
+
{"name": "vendor_name", "type": "string", "required": True},
|
13
|
+
{"name": "invoice_date", "type": "date", "required": True},
|
14
|
+
{"name": "total_amount", "type": "numeric", "required": True}
|
15
|
+
]
|
16
|
+
}
|
17
|
+
|
18
|
+
print("Testing PDFProcessor with schema:")
|
19
|
+
print(json.dumps(schema, indent=2))
|
20
|
+
|
21
|
+
resp = requests.post(
|
22
|
+
f"{api_url}/tools/execute",
|
23
|
+
json={
|
24
|
+
"tool_name": "PDFProcessor",
|
25
|
+
"hosted_by": "memra",
|
26
|
+
"input_data": {
|
27
|
+
"file": "/uploads/6f4538c0-8fce-4488-be49-1a78afc58a4a.pdf",
|
28
|
+
"schema": schema
|
29
|
+
}
|
30
|
+
},
|
31
|
+
headers={"X-API-Key": api_key}
|
32
|
+
)
|
33
|
+
|
34
|
+
if resp.status_code == 200:
|
35
|
+
result = resp.json()
|
36
|
+
if result.get('success') and 'data' in result and 'data' in result['data']:
|
37
|
+
prompt = result['data']['data'].get('vision_prompt', '')
|
38
|
+
print("\nVision prompt generated:")
|
39
|
+
print(prompt[:500])
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test SQLExecutor tool execution directly
|
4
|
+
"""
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import os
|
8
|
+
sys.path.insert(0, '/Users/tarpus/memra')
|
9
|
+
|
10
|
+
from memra.tool_registry import ToolRegistry
|
11
|
+
import logging
|
12
|
+
|
13
|
+
# Set up logging
|
14
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
def test_sql_executor():
|
18
|
+
"""Test SQLExecutor tool execution"""
|
19
|
+
|
20
|
+
# Create tool registry
|
21
|
+
registry = ToolRegistry()
|
22
|
+
|
23
|
+
# Test configuration
|
24
|
+
config = {
|
25
|
+
"bridge_url": "http://localhost:8081",
|
26
|
+
"bridge_secret": "test-secret-for-development"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Test input data
|
30
|
+
input_data = {
|
31
|
+
"sql_query": "SELECT COUNT(*) as row_count FROM invoices"
|
32
|
+
}
|
33
|
+
|
34
|
+
print("🔧 Testing SQLExecutor tool execution...")
|
35
|
+
print(f"Config: {config}")
|
36
|
+
print(f"Input: {input_data}")
|
37
|
+
|
38
|
+
# Execute the tool
|
39
|
+
result = registry.execute_tool(
|
40
|
+
tool_name="SQLExecutor",
|
41
|
+
hosted_by="mcp",
|
42
|
+
input_data=input_data,
|
43
|
+
config=config
|
44
|
+
)
|
45
|
+
|
46
|
+
print(f"\n📊 Result: {result}")
|
47
|
+
|
48
|
+
if result.get("success"):
|
49
|
+
print("✅ SQLExecutor executed successfully!")
|
50
|
+
if "_mock" in result.get("data", {}):
|
51
|
+
print("⚠️ But returned mock data")
|
52
|
+
else:
|
53
|
+
print("🎉 Real data returned!")
|
54
|
+
else:
|
55
|
+
print(f"❌ SQLExecutor failed: {result.get('error')}")
|
56
|
+
|
57
|
+
if __name__ == "__main__":
|
58
|
+
test_sql_executor()
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test SQLExecutor tool execution with extra fields (like agent_input)
|
4
|
+
"""
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import os
|
8
|
+
sys.path.insert(0, '/Users/tarpus/memra')
|
9
|
+
|
10
|
+
from memra.tool_registry import ToolRegistry
|
11
|
+
import logging
|
12
|
+
|
13
|
+
# Set up logging
|
14
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
def test_sql_executor_with_extra_fields():
|
18
|
+
"""Test SQLExecutor tool execution with extra fields like agent_input"""
|
19
|
+
|
20
|
+
# Create tool registry
|
21
|
+
registry = ToolRegistry()
|
22
|
+
|
23
|
+
# Test configuration
|
24
|
+
config = {
|
25
|
+
"bridge_url": "http://localhost:8081",
|
26
|
+
"bridge_secret": "test-secret-for-development"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Test input data with extra fields (like agent_input)
|
30
|
+
input_data = {
|
31
|
+
"sql_query": "SELECT COUNT(*) as row_count FROM invoices",
|
32
|
+
"table_name": "invoices",
|
33
|
+
"connection": "postgresql://memra:memra123@localhost:5432/memra_invoice_db",
|
34
|
+
"monitoring_phase": "before"
|
35
|
+
}
|
36
|
+
|
37
|
+
print("🔧 Testing SQLExecutor tool execution with extra fields...")
|
38
|
+
print(f"Config: {config}")
|
39
|
+
print(f"Input: {input_data}")
|
40
|
+
|
41
|
+
# Execute the tool
|
42
|
+
result = registry.execute_tool(
|
43
|
+
tool_name="SQLExecutor",
|
44
|
+
hosted_by="mcp",
|
45
|
+
input_data=input_data,
|
46
|
+
config=config
|
47
|
+
)
|
48
|
+
|
49
|
+
print(f"\n📊 Result: {result}")
|
50
|
+
|
51
|
+
if result.get("success"):
|
52
|
+
print("✅ SQLExecutor executed successfully!")
|
53
|
+
if "_mock" in result.get("data", {}):
|
54
|
+
print("⚠️ But returned mock data")
|
55
|
+
else:
|
56
|
+
print("🎉 Real data returned!")
|
57
|
+
else:
|
58
|
+
print(f"❌ SQLExecutor failed: {result.get('error')}")
|
59
|
+
|
60
|
+
if __name__ == "__main__":
|
61
|
+
test_sql_executor_with_extra_fields()
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test SQLExecutor fix in execution engine
|
4
|
+
"""
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import os
|
8
|
+
sys.path.insert(0, '/Users/tarpus/memra')
|
9
|
+
|
10
|
+
from memra.execution import ExecutionEngine
|
11
|
+
|
12
|
+
def test_sql_executor_real_work():
|
13
|
+
"""Test that SQLExecutor is correctly identified as real work"""
|
14
|
+
|
15
|
+
engine = ExecutionEngine()
|
16
|
+
|
17
|
+
# Mock SQLExecutor result (what the MCP bridge returns)
|
18
|
+
sql_executor_result = {
|
19
|
+
"query": "SELECT COUNT(*) as row_count FROM invoices",
|
20
|
+
"results": [{"row_count": 3}],
|
21
|
+
"row_count": 1,
|
22
|
+
"columns": ["row_count"],
|
23
|
+
"success": True
|
24
|
+
}
|
25
|
+
|
26
|
+
print("🔧 Testing SQLExecutor real work detection...")
|
27
|
+
print(f"SQLExecutor result: {sql_executor_result}")
|
28
|
+
|
29
|
+
# Test the _is_real_work method
|
30
|
+
is_real = engine._is_real_work("SQLExecutor", sql_executor_result)
|
31
|
+
|
32
|
+
print(f"✅ Is real work: {is_real}")
|
33
|
+
|
34
|
+
if is_real:
|
35
|
+
print("🎉 SQLExecutor fix is working!")
|
36
|
+
else:
|
37
|
+
print("❌ SQLExecutor fix is not working!")
|
38
|
+
|
39
|
+
if __name__ == "__main__":
|
40
|
+
test_sql_executor_real_work()
|
@@ -0,0 +1,50 @@
|
|
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 the schema format the client sends
|
9
|
+
schema = [
|
10
|
+
{"column_name": "invoice_number", "data_type": "character varying", "is_nullable": "NO"},
|
11
|
+
{"column_name": "vendor_name", "data_type": "character varying", "is_nullable": "NO"},
|
12
|
+
{"column_name": "invoice_date", "data_type": "date", "is_nullable": "NO"},
|
13
|
+
{"column_name": "total_amount", "data_type": "numeric", "is_nullable": "NO"}
|
14
|
+
]
|
15
|
+
|
16
|
+
print("Testing updated server with client schema format:")
|
17
|
+
print(json.dumps(schema[:2], indent=2)) # Show first 2 fields
|
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"\nResponse status: {resp.status_code}")
|
33
|
+
if resp.status_code == 200:
|
34
|
+
result = resp.json()
|
35
|
+
if result.get('success') and 'data' in result and 'data' in result['data']:
|
36
|
+
data = result['data']['data']
|
37
|
+
vision_prompt = data.get('vision_prompt', '')
|
38
|
+
print(f"\nVision prompt generated (first 500 chars):")
|
39
|
+
print(vision_prompt[:500])
|
40
|
+
|
41
|
+
# Check if vendor_name is in the prompt
|
42
|
+
if 'vendor_name' in vision_prompt:
|
43
|
+
print("\n✅ SUCCESS: vendor_name is now in the vision prompt\!")
|
44
|
+
else:
|
45
|
+
print("\n❌ FAIL: vendor_name is NOT in the vision prompt")
|
46
|
+
|
47
|
+
# Check vision response
|
48
|
+
vision_resp = data.get('vision_response', '')
|
49
|
+
if vision_resp:
|
50
|
+
print(f"\nVision response includes vendor info: {'vendor' in vision_resp.lower()}")
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script to verify file upload functionality
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import base64
|
8
|
+
import requests
|
9
|
+
import json
|
10
|
+
|
11
|
+
def test_upload_functionality():
|
12
|
+
"""Test the complete upload and processing workflow"""
|
13
|
+
|
14
|
+
# Test server URL (change this to api.memra.co when implementing on remote)
|
15
|
+
API_BASE = "http://localhost:8000"
|
16
|
+
|
17
|
+
print("🧪 Testing File Upload Functionality")
|
18
|
+
print("=" * 50)
|
19
|
+
|
20
|
+
# Step 1: Check server health
|
21
|
+
print("\n1️⃣ Checking server health...")
|
22
|
+
try:
|
23
|
+
response = requests.get(f"{API_BASE}/health")
|
24
|
+
if response.status_code == 200:
|
25
|
+
print("✅ Server is healthy")
|
26
|
+
else:
|
27
|
+
print(f"❌ Server health check failed: {response.status_code}")
|
28
|
+
return
|
29
|
+
except Exception as e:
|
30
|
+
print(f"❌ Cannot connect to server: {e}")
|
31
|
+
print("💡 Make sure to start the test server: python3 test_upload_server.py")
|
32
|
+
return
|
33
|
+
|
34
|
+
# Step 2: Discover available tools
|
35
|
+
print("\n2️⃣ Discovering available tools...")
|
36
|
+
try:
|
37
|
+
response = requests.get(f"{API_BASE}/tools/discover")
|
38
|
+
if response.status_code == 200:
|
39
|
+
tools = response.json().get("tools", [])
|
40
|
+
print(f"✅ Found {len(tools)} tools:")
|
41
|
+
for tool in tools:
|
42
|
+
print(f" - {tool['name']}: {tool['description']}")
|
43
|
+
else:
|
44
|
+
print(f"❌ Tool discovery failed: {response.status_code}")
|
45
|
+
return
|
46
|
+
except Exception as e:
|
47
|
+
print(f"❌ Tool discovery error: {e}")
|
48
|
+
return
|
49
|
+
|
50
|
+
# Step 3: Create a test PDF file
|
51
|
+
print("\n3️⃣ Creating test PDF file...")
|
52
|
+
test_pdf_path = "test_invoice.pdf"
|
53
|
+
|
54
|
+
# Create a simple test PDF (this is just for testing - in real usage you'd use actual PDFs)
|
55
|
+
try:
|
56
|
+
# For testing, we'll create a simple text file and pretend it's a PDF
|
57
|
+
with open(test_pdf_path, 'w') as f:
|
58
|
+
f.write("Test Invoice\nVendor: Test Corp\nAmount: $1234.56\n")
|
59
|
+
print(f"✅ Created test file: {test_pdf_path}")
|
60
|
+
except Exception as e:
|
61
|
+
print(f"❌ Failed to create test file: {e}")
|
62
|
+
return
|
63
|
+
|
64
|
+
# Step 4: Upload the file
|
65
|
+
print("\n4️⃣ Uploading file to server...")
|
66
|
+
try:
|
67
|
+
with open(test_pdf_path, 'rb') as f:
|
68
|
+
file_content = f.read()
|
69
|
+
|
70
|
+
file_b64 = base64.b64encode(file_content).decode('utf-8')
|
71
|
+
|
72
|
+
upload_data = {
|
73
|
+
"filename": os.path.basename(test_pdf_path),
|
74
|
+
"content": file_b64,
|
75
|
+
"content_type": "application/pdf"
|
76
|
+
}
|
77
|
+
|
78
|
+
response = requests.post(
|
79
|
+
f"{API_BASE}/upload",
|
80
|
+
json=upload_data,
|
81
|
+
headers={"Content-Type": "application/json"}
|
82
|
+
)
|
83
|
+
|
84
|
+
if response.status_code == 200:
|
85
|
+
result = response.json()
|
86
|
+
if result.get("success"):
|
87
|
+
remote_path = result["data"]["remote_path"]
|
88
|
+
print(f"✅ File uploaded successfully")
|
89
|
+
print(f" Remote path: {remote_path}")
|
90
|
+
print(f" File ID: {result['data']['file_id']}")
|
91
|
+
else:
|
92
|
+
print(f"❌ Upload failed: {result.get('error')}")
|
93
|
+
return
|
94
|
+
else:
|
95
|
+
print(f"❌ Upload request failed: {response.status_code}")
|
96
|
+
print(f" Response: {response.text}")
|
97
|
+
return
|
98
|
+
|
99
|
+
except Exception as e:
|
100
|
+
print(f"❌ Upload error: {e}")
|
101
|
+
return
|
102
|
+
|
103
|
+
# Step 5: Process the uploaded file
|
104
|
+
print("\n5️⃣ Processing uploaded file...")
|
105
|
+
try:
|
106
|
+
process_data = {
|
107
|
+
"tool_name": "PDFProcessor",
|
108
|
+
"hosted_by": "memra",
|
109
|
+
"input_data": {
|
110
|
+
"file": remote_path
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
response = requests.post(
|
115
|
+
f"{API_BASE}/tools/execute",
|
116
|
+
json=process_data,
|
117
|
+
headers={"Content-Type": "application/json"}
|
118
|
+
)
|
119
|
+
|
120
|
+
if response.status_code == 200:
|
121
|
+
result = response.json()
|
122
|
+
if result.get("success"):
|
123
|
+
extracted_data = result["data"]["extracted_data"]
|
124
|
+
print("✅ File processed successfully")
|
125
|
+
print("📄 Extracted data:")
|
126
|
+
print(f" Vendor: {extracted_data['headerSection']['vendorName']}")
|
127
|
+
print(f" Invoice: {extracted_data['billingDetails']['invoiceNumber']}")
|
128
|
+
print(f" Amount: ${extracted_data['chargesSummary']['document_total']}")
|
129
|
+
else:
|
130
|
+
print(f"❌ Processing failed: {result.get('error')}")
|
131
|
+
return
|
132
|
+
else:
|
133
|
+
print(f"❌ Processing request failed: {response.status_code}")
|
134
|
+
print(f" Response: {response.text}")
|
135
|
+
return
|
136
|
+
|
137
|
+
except Exception as e:
|
138
|
+
print(f"❌ Processing error: {e}")
|
139
|
+
return
|
140
|
+
|
141
|
+
# Step 6: Cleanup
|
142
|
+
print("\n6️⃣ Cleaning up...")
|
143
|
+
try:
|
144
|
+
os.remove(test_pdf_path)
|
145
|
+
print("✅ Test file removed")
|
146
|
+
except:
|
147
|
+
pass
|
148
|
+
|
149
|
+
print("\n🎉 All tests passed! File upload functionality is working correctly.")
|
150
|
+
print("\n📋 Next Steps:")
|
151
|
+
print("1. Implement the upload endpoint on the remote API (api.memra.co)")
|
152
|
+
print("2. Update the demo to use the real remote API")
|
153
|
+
print("3. Test with actual PDF files")
|
154
|
+
|
155
|
+
if __name__ == "__main__":
|
156
|
+
test_upload_functionality()
|