memra 0.2.13__py3-none-any.whl → 0.2.15__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 +322 -51
- {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/METADATA +1 -1
- {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/RECORD +7 -61
- memra-0.2.15.dist-info/top_level.txt +1 -0
- memra-0.2.13.dist-info/top_level.txt +0 -4
- memra-ops/app.py +0 -808
- memra-ops/config/config.py +0 -25
- memra-ops/config.py +0 -34
- memra-ops/logic/__init__.py +0 -1
- memra-ops/logic/file_tools.py +0 -43
- memra-ops/logic/invoice_tools.py +0 -668
- memra-ops/logic/invoice_tools_fix.py +0 -66
- memra-ops/mcp_bridge_server.py +0 -1178
- memra-ops/scripts/check_database.py +0 -37
- memra-ops/scripts/clear_database.py +0 -48
- memra-ops/scripts/monitor_database.py +0 -67
- memra-ops/scripts/release.py +0 -133
- memra-ops/scripts/reset_database.py +0 -65
- memra-ops/scripts/start_memra.py +0 -334
- memra-ops/scripts/stop_memra.py +0 -132
- memra-ops/server_tool_registry.py +0 -190
- memra-ops/tests/test_llm_text_to_sql.py +0 -115
- memra-ops/tests/test_llm_vs_pattern.py +0 -130
- memra-ops/tests/test_mcp_schema_aware.py +0 -124
- memra-ops/tests/test_schema_aware_sql.py +0 -139
- memra-ops/tests/test_schema_aware_sql_simple.py +0 -66
- memra-ops/tests/test_text_to_sql_demo.py +0 -140
- memra-ops/tools/mcp_bridge_server.py +0 -851
- memra-sdk/examples/accounts_payable.py +0 -215
- memra-sdk/examples/accounts_payable_client.py +0 -217
- memra-sdk/examples/accounts_payable_mcp.py +0 -200
- memra-sdk/examples/ask_questions.py +0 -123
- memra-sdk/examples/invoice_processing.py +0 -116
- memra-sdk/examples/propane_delivery.py +0 -87
- memra-sdk/examples/simple_text_to_sql.py +0 -158
- memra-sdk/memra/__init__.py +0 -31
- memra-sdk/memra/discovery.py +0 -15
- memra-sdk/memra/discovery_client.py +0 -49
- memra-sdk/memra/execution.py +0 -481
- memra-sdk/memra/models.py +0 -99
- memra-sdk/memra/tool_registry.py +0 -343
- memra-sdk/memra/tool_registry_client.py +0 -106
- memra-sdk/scripts/release.py +0 -133
- memra-sdk/setup.py +0 -52
- memra-workflows/accounts_payable/accounts_payable.py +0 -215
- memra-workflows/accounts_payable/accounts_payable_client.py +0 -216
- memra-workflows/accounts_payable/accounts_payable_mcp.py +0 -200
- memra-workflows/accounts_payable/accounts_payable_smart.py +0 -221
- memra-workflows/invoice_processing/invoice_processing.py +0 -116
- memra-workflows/invoice_processing/smart_invoice_processor.py +0 -220
- memra-workflows/logic/__init__.py +0 -1
- memra-workflows/logic/file_tools.py +0 -50
- memra-workflows/logic/invoice_tools.py +0 -501
- memra-workflows/logic/propane_agents.py +0 -52
- memra-workflows/mcp_bridge_server.py +0 -230
- memra-workflows/propane_delivery/propane_delivery.py +0 -87
- memra-workflows/text_to_sql/complete_invoice_workflow_with_queries.py +0 -208
- memra-workflows/text_to_sql/complete_text_to_sql_system.py +0 -266
- memra-workflows/text_to_sql/file_discovery_demo.py +0 -156
- {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/LICENSE +0 -0
- {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/WHEEL +0 -0
- {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/entry_points.txt +0 -0
@@ -1,221 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Smart Accounts Payable Example with File Discovery
|
4
|
-
This example demonstrates intelligent file discovery and management
|
5
|
-
"""
|
6
|
-
|
7
|
-
import os
|
8
|
-
import sys
|
9
|
-
import logging
|
10
|
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
11
|
-
|
12
|
-
from memra import Agent, Department
|
13
|
-
from memra.execution import ExecutionEngine
|
14
|
-
|
15
|
-
# Set up environment
|
16
|
-
os.environ['MEMRA_API_KEY'] = 'memra-prod-2024-001'
|
17
|
-
|
18
|
-
# Set up logging
|
19
|
-
logging.basicConfig(level=logging.INFO)
|
20
|
-
logger = logging.getLogger(__name__)
|
21
|
-
|
22
|
-
# Enhanced agents with smart file discovery
|
23
|
-
etl_agent = Agent(
|
24
|
-
role="Data Engineer",
|
25
|
-
job="Extract invoice schema from Postgres database",
|
26
|
-
sops=[
|
27
|
-
"Connect to PostgresDB using credentials",
|
28
|
-
"Query information_schema for invoices table",
|
29
|
-
"Extract column names, types, and constraints",
|
30
|
-
"Return schema as structured JSON"
|
31
|
-
],
|
32
|
-
systems=["PostgresDB"],
|
33
|
-
tools=[
|
34
|
-
{"name": "DatabaseQueryTool", "hosted_by": "memra"}
|
35
|
-
],
|
36
|
-
output_key="invoice_schema"
|
37
|
-
)
|
38
|
-
|
39
|
-
# Smart Invoice Parser with file discovery
|
40
|
-
smart_parser_agent = Agent(
|
41
|
-
role="Smart Invoice Parser",
|
42
|
-
job="Extract structured data from invoice PDFs",
|
43
|
-
sops=[
|
44
|
-
"Load and process the invoice PDF file",
|
45
|
-
"Convert to high-contrast images if needed",
|
46
|
-
"Run OCR to extract text",
|
47
|
-
"Use schema to identify and extract fields",
|
48
|
-
"Validate extracted data against schema types",
|
49
|
-
"Return structured invoice data"
|
50
|
-
],
|
51
|
-
systems=["InvoiceStore"],
|
52
|
-
tools=[
|
53
|
-
{"name": "PDFProcessor", "hosted_by": "memra"},
|
54
|
-
{"name": "OCRTool", "hosted_by": "memra"},
|
55
|
-
{"name": "InvoiceExtractionWorkflow", "hosted_by": "memra"}
|
56
|
-
],
|
57
|
-
input_keys=["file", "invoice_schema"], # file is the correct key
|
58
|
-
output_key="invoice_data"
|
59
|
-
)
|
60
|
-
|
61
|
-
writer_agent = Agent(
|
62
|
-
role="Data Entry Specialist",
|
63
|
-
job="Write validated invoice data to Postgres database",
|
64
|
-
sops=[
|
65
|
-
"Validate invoice data completeness",
|
66
|
-
"Map fields to database columns using schema",
|
67
|
-
"Connect to PostgresDB",
|
68
|
-
"Insert record into invoices table",
|
69
|
-
"Return confirmation with record ID"
|
70
|
-
],
|
71
|
-
systems=["PostgresDB"],
|
72
|
-
tools=[
|
73
|
-
{"name": "DataValidator", "hosted_by": "memra"},
|
74
|
-
{"name": "PostgresInsert", "hosted_by": "memra"}
|
75
|
-
],
|
76
|
-
input_keys=["invoice_data", "invoice_schema"],
|
77
|
-
output_key="write_confirmation"
|
78
|
-
)
|
79
|
-
|
80
|
-
# Smart manager that handles file discovery workflow
|
81
|
-
smart_manager_agent = Agent(
|
82
|
-
role="Smart Accounts Payable Manager",
|
83
|
-
job="Coordinate intelligent invoice processing with file discovery",
|
84
|
-
sops=[
|
85
|
-
"Check if specific file path was provided",
|
86
|
-
"If no file specified, discover available files in invoices/ directory",
|
87
|
-
"Handle file copying from external locations if needed",
|
88
|
-
"Coordinate schema extraction and invoice processing",
|
89
|
-
"Validate results and provide comprehensive reporting"
|
90
|
-
],
|
91
|
-
allow_delegation=True,
|
92
|
-
output_key="workflow_status"
|
93
|
-
)
|
94
|
-
|
95
|
-
# Create the smart department
|
96
|
-
smart_ap_department = Department(
|
97
|
-
name="Smart Accounts Payable",
|
98
|
-
mission="Intelligently discover and process invoices with minimal user input",
|
99
|
-
agents=[etl_agent, smart_parser_agent, writer_agent],
|
100
|
-
manager_agent=smart_manager_agent,
|
101
|
-
workflow_order=["Data Engineer", "Smart Invoice Parser", "Data Entry Specialist"],
|
102
|
-
dependencies=["PostgresDB", "InvoiceStore", "FileSystem"],
|
103
|
-
execution_policy={
|
104
|
-
"retry_on_fail": True,
|
105
|
-
"max_retries": 2,
|
106
|
-
"halt_on_validation_error": True,
|
107
|
-
"timeout_seconds": 300
|
108
|
-
},
|
109
|
-
context={
|
110
|
-
"company_id": "acme_corp",
|
111
|
-
"fiscal_year": "2024",
|
112
|
-
"default_invoice_dir": "invoices",
|
113
|
-
"mcp_bridge_url": "http://localhost:8081",
|
114
|
-
"mcp_bridge_secret": "test-secret-for-development"
|
115
|
-
}
|
116
|
-
)
|
117
|
-
|
118
|
-
def main():
|
119
|
-
print("🧠 Smart Accounts Payable with File Discovery")
|
120
|
-
print("=" * 60)
|
121
|
-
|
122
|
-
# Create execution engine
|
123
|
-
engine = ExecutionEngine()
|
124
|
-
|
125
|
-
# Example 1: Auto-discovery mode (no file specified)
|
126
|
-
print("\n📂 Example 1: Auto-discovery mode")
|
127
|
-
print("Scanning invoices/ directory for available files...")
|
128
|
-
|
129
|
-
input_data_auto = {
|
130
|
-
"file": "invoices/10352259310.PDF", # Default file for auto-discovery demo
|
131
|
-
"connection": "postgresql://tarpus@localhost:5432/memra_invoice_db"
|
132
|
-
}
|
133
|
-
|
134
|
-
result = engine.execute_department(smart_ap_department, input_data_auto)
|
135
|
-
|
136
|
-
if result.success:
|
137
|
-
print("✅ Auto-discovery workflow completed successfully!")
|
138
|
-
display_results(result)
|
139
|
-
else:
|
140
|
-
print(f"❌ Auto-discovery failed: {result.error}")
|
141
|
-
|
142
|
-
print("\n" + "="*60)
|
143
|
-
|
144
|
-
# Example 2: External file mode
|
145
|
-
print("\n📁 Example 2: External file processing")
|
146
|
-
print("Processing file from external location...")
|
147
|
-
|
148
|
-
input_data_external = {
|
149
|
-
"file": "invoices/10352259310.PDF", # Use existing file for demo
|
150
|
-
"connection": "postgresql://tarpus@localhost:5432/memra_invoice_db"
|
151
|
-
}
|
152
|
-
|
153
|
-
result = engine.execute_department(smart_ap_department, input_data_external)
|
154
|
-
|
155
|
-
if result.success:
|
156
|
-
print("✅ External file workflow completed successfully!")
|
157
|
-
display_results(result)
|
158
|
-
else:
|
159
|
-
print(f"❌ External file processing failed: {result.error}")
|
160
|
-
|
161
|
-
print("\n" + "="*60)
|
162
|
-
|
163
|
-
# Example 3: Specific file mode
|
164
|
-
print("\n🎯 Example 3: Specific file processing")
|
165
|
-
print("Processing specific file from invoices/ directory...")
|
166
|
-
|
167
|
-
input_data_specific = {
|
168
|
-
"file": "invoices/10352259310.PDF", # Specific file
|
169
|
-
"connection": "postgresql://tarpus@localhost:5432/memra_invoice_db"
|
170
|
-
}
|
171
|
-
|
172
|
-
result = engine.execute_department(smart_ap_department, input_data_specific)
|
173
|
-
|
174
|
-
if result.success:
|
175
|
-
print("✅ Specific file workflow completed successfully!")
|
176
|
-
display_results(result)
|
177
|
-
else:
|
178
|
-
print(f"❌ Specific file processing failed: {result.error}")
|
179
|
-
|
180
|
-
def display_results(result):
|
181
|
-
"""Display comprehensive workflow results"""
|
182
|
-
|
183
|
-
# Show manager validation results
|
184
|
-
if 'workflow_status' in result.data:
|
185
|
-
manager_report = result.data['workflow_status']
|
186
|
-
print(f"\n🔍 Manager Report:")
|
187
|
-
print(f"Status: {manager_report.get('validation_status', 'unknown')}")
|
188
|
-
print(f"Summary: {manager_report.get('summary', 'No summary available')}")
|
189
|
-
|
190
|
-
# Show agent performance
|
191
|
-
if 'agent_performance' in manager_report:
|
192
|
-
print(f"\n📊 Agent Performance:")
|
193
|
-
for agent_role, performance in manager_report['agent_performance'].items():
|
194
|
-
work_quality = performance['work_quality']
|
195
|
-
status_emoji = "✅" if work_quality == "real" else "🔄"
|
196
|
-
print(f"{status_emoji} {agent_role}: {performance['status']}")
|
197
|
-
if performance['tools_real_work']:
|
198
|
-
print(f" Real work: {', '.join(performance['tools_real_work'])}")
|
199
|
-
|
200
|
-
# Show file discovery results
|
201
|
-
if 'invoice_data' in result.data:
|
202
|
-
invoice_data = result.data['invoice_data']
|
203
|
-
if isinstance(invoice_data, dict) and 'file_metadata' in invoice_data:
|
204
|
-
print(f"\n📄 File Processing:")
|
205
|
-
metadata = invoice_data['file_metadata']
|
206
|
-
print(f"Processed: {metadata.get('filename', 'unknown')}")
|
207
|
-
print(f"Location: {metadata.get('path', 'unknown')}")
|
208
|
-
print(f"Size: {metadata.get('size', 'unknown')}")
|
209
|
-
|
210
|
-
# Show database results
|
211
|
-
if 'write_confirmation' in result.data:
|
212
|
-
confirmation = result.data['write_confirmation']
|
213
|
-
if isinstance(confirmation, dict) and 'record_id' in confirmation:
|
214
|
-
print(f"\n💾 Database: Record ID {confirmation['record_id']}")
|
215
|
-
|
216
|
-
# Show execution trace
|
217
|
-
print(f"\n🔄 Execution: {', '.join(result.trace.agents_executed)}")
|
218
|
-
print(f"🛠 Tools: {', '.join(result.trace.tools_invoked)}")
|
219
|
-
|
220
|
-
if __name__ == "__main__":
|
221
|
-
main()
|
@@ -1,116 +0,0 @@
|
|
1
|
-
from memra import Agent, Department, LLM
|
2
|
-
|
3
|
-
# Define LLMs that agents can use
|
4
|
-
default_llm = LLM(model="gpt-4", temperature=0.1)
|
5
|
-
parsing_llm = LLM(model="claude-3-opus", temperature=0) # More accurate for structured extraction
|
6
|
-
manager_llm = LLM(model="gpt-4-turbo", temperature=0.3) # Balanced for decision-making
|
7
|
-
|
8
|
-
# Define agents with specific LLMs
|
9
|
-
etl_agent = Agent(
|
10
|
-
role="Data Engineer",
|
11
|
-
job="Extract invoice schema from Postgres database",
|
12
|
-
llm=default_llm, # Standard LLM for SQL generation
|
13
|
-
sops=[
|
14
|
-
"Connect to PostgresDB using credentials",
|
15
|
-
"Query information_schema for invoices table",
|
16
|
-
"Extract column names, types, and constraints",
|
17
|
-
"Return schema as structured JSON"
|
18
|
-
],
|
19
|
-
systems=["PostgresDB"],
|
20
|
-
tools=[
|
21
|
-
{"name": "DatabaseQueryTool", "hosted_by": "memra"}
|
22
|
-
],
|
23
|
-
output_key="invoice_schema"
|
24
|
-
)
|
25
|
-
|
26
|
-
parser_agent = Agent(
|
27
|
-
role="Invoice Parser",
|
28
|
-
job="Extract structured data from invoice PDF using schema",
|
29
|
-
llm=parsing_llm, # High-accuracy LLM for document parsing
|
30
|
-
sops=[
|
31
|
-
"Load invoice PDF file",
|
32
|
-
"Convert to high-contrast images if needed",
|
33
|
-
"Run OCR to extract text",
|
34
|
-
"Use schema to identify and extract fields",
|
35
|
-
"Validate extracted data against schema types",
|
36
|
-
"Return structured invoice data"
|
37
|
-
],
|
38
|
-
systems=["InvoiceStore"],
|
39
|
-
tools=[
|
40
|
-
{"name": "PDFProcessor", "hosted_by": "memra"},
|
41
|
-
{"name": "OCRTool", "hosted_by": "memra"},
|
42
|
-
{"name": "InvoiceExtractionWorkflow", "hosted_by": "memra"}
|
43
|
-
],
|
44
|
-
input_keys=["file", "invoice_schema"],
|
45
|
-
output_key="invoice_data"
|
46
|
-
)
|
47
|
-
|
48
|
-
writer_agent = Agent(
|
49
|
-
role="Data Entry Specialist",
|
50
|
-
job="Write validated invoice data to Postgres database",
|
51
|
-
llm=default_llm,
|
52
|
-
sops=[
|
53
|
-
"Validate invoice data completeness",
|
54
|
-
"Map fields to database columns using schema",
|
55
|
-
"Connect to PostgresDB",
|
56
|
-
"Insert record into invoices table",
|
57
|
-
"Return confirmation with record ID"
|
58
|
-
],
|
59
|
-
systems=["PostgresDB"],
|
60
|
-
tools=[
|
61
|
-
{"name": "DataValidator", "hosted_by": "memra"},
|
62
|
-
{"name": "PostgresInsert", "hosted_by": "mcp"}
|
63
|
-
],
|
64
|
-
input_keys=["invoice_data", "invoice_schema"],
|
65
|
-
output_key="write_confirmation"
|
66
|
-
)
|
67
|
-
|
68
|
-
# Manager with its own LLM
|
69
|
-
manager_agent = Agent(
|
70
|
-
role="Accounts Payable Manager",
|
71
|
-
job="Coordinate invoice processing pipeline and handle exceptions",
|
72
|
-
llm=manager_llm, # Manager gets a more flexible LLM
|
73
|
-
sops=[
|
74
|
-
"Check if schema extraction succeeded",
|
75
|
-
"If schema missing, delegate to Schema Loader",
|
76
|
-
"Validate parsed invoice has required fields",
|
77
|
-
"Ensure invoice total matches line items before DB write",
|
78
|
-
"Handle and log any errors with appropriate escalation"
|
79
|
-
],
|
80
|
-
allow_delegation=True,
|
81
|
-
fallback_agents={
|
82
|
-
"Data Engineer": "Schema Loader"
|
83
|
-
},
|
84
|
-
output_key="workflow_status"
|
85
|
-
)
|
86
|
-
|
87
|
-
# Create the Accounts Payable Department
|
88
|
-
ap_department = Department(
|
89
|
-
name="Accounts Payable",
|
90
|
-
mission="Process invoices accurately into financial system per company data standards",
|
91
|
-
agents=[etl_agent, parser_agent, writer_agent],
|
92
|
-
manager_agent=manager_agent,
|
93
|
-
default_llm=default_llm, # Fallback for any agent without explicit LLM
|
94
|
-
workflow_order=["Data Engineer", "Invoice Parser", "Data Entry Specialist"]
|
95
|
-
)
|
96
|
-
|
97
|
-
# Example usage
|
98
|
-
if __name__ == "__main__":
|
99
|
-
from memra.execution import ExecutionEngine
|
100
|
-
|
101
|
-
# This is how a developer would use the department
|
102
|
-
engine = ExecutionEngine()
|
103
|
-
input_data = {
|
104
|
-
"file": "path/to/invoice.pdf",
|
105
|
-
"connection": "postgres://ap_user:password@localhost:5432/finance"
|
106
|
-
}
|
107
|
-
|
108
|
-
result = engine.execute_department(ap_department, input_data)
|
109
|
-
|
110
|
-
if result.success:
|
111
|
-
print("✅ Invoice processing completed!")
|
112
|
-
print(f"Result: {result.data}")
|
113
|
-
else:
|
114
|
-
print(f"❌ Processing failed: {result.error}")
|
115
|
-
|
116
|
-
print(f"Workflow result: {result}")
|
@@ -1,220 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Smart Invoice Processor with Intelligent File Discovery
|
4
|
-
|
5
|
-
This system automatically:
|
6
|
-
1. Empty invoices/ directory → Ask user for file path
|
7
|
-
2. Single file in invoices/ → Process it automatically
|
8
|
-
3. Multiple files in invoices/ → Show list, let user choose or batch process
|
9
|
-
"""
|
10
|
-
|
11
|
-
import os
|
12
|
-
import sys
|
13
|
-
import glob
|
14
|
-
from pathlib import Path
|
15
|
-
|
16
|
-
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
17
|
-
|
18
|
-
from memra import Agent, Department, ExecutionEngine
|
19
|
-
|
20
|
-
# Set up environment
|
21
|
-
os.environ['MEMRA_API_KEY'] = 'memra-prod-2024-001'
|
22
|
-
|
23
|
-
def discover_invoice_files():
|
24
|
-
"""Discover invoice files in the invoices directory"""
|
25
|
-
invoice_dir = Path("invoices")
|
26
|
-
if not invoice_dir.exists():
|
27
|
-
invoice_dir.mkdir()
|
28
|
-
return []
|
29
|
-
|
30
|
-
# Look for PDF files
|
31
|
-
pdf_files = list(invoice_dir.glob("*.pdf")) + list(invoice_dir.glob("*.PDF"))
|
32
|
-
return [str(f) for f in pdf_files]
|
33
|
-
|
34
|
-
def get_file_to_process():
|
35
|
-
"""Intelligent file discovery logic"""
|
36
|
-
files = discover_invoice_files()
|
37
|
-
|
38
|
-
if len(files) == 0:
|
39
|
-
print("📂 The invoices/ directory is empty.")
|
40
|
-
print("Please provide the path to the invoice you want to process:")
|
41
|
-
file_path = input("File path: ").strip()
|
42
|
-
|
43
|
-
if not file_path:
|
44
|
-
print("❌ No file path provided.")
|
45
|
-
return None
|
46
|
-
|
47
|
-
if not os.path.exists(file_path):
|
48
|
-
print(f"❌ File not found: {file_path}")
|
49
|
-
return None
|
50
|
-
|
51
|
-
# Copy file to invoices directory
|
52
|
-
import shutil
|
53
|
-
filename = os.path.basename(file_path)
|
54
|
-
dest_path = f"invoices/{filename}"
|
55
|
-
shutil.copy2(file_path, dest_path)
|
56
|
-
print(f"📁 Copied {filename} to invoices/ directory")
|
57
|
-
return dest_path
|
58
|
-
|
59
|
-
elif len(files) == 1:
|
60
|
-
print(f"📄 Found 1 invoice file: {files[0]}")
|
61
|
-
print("🚀 Processing automatically...")
|
62
|
-
return files[0]
|
63
|
-
|
64
|
-
else:
|
65
|
-
print(f"📄 Found {len(files)} invoice files:")
|
66
|
-
for i, file in enumerate(files, 1):
|
67
|
-
filename = os.path.basename(file)
|
68
|
-
size = os.path.getsize(file)
|
69
|
-
size_kb = size // 1024
|
70
|
-
print(f" {i}. {filename} ({size_kb}KB)")
|
71
|
-
|
72
|
-
print("\nOptions:")
|
73
|
-
print(" • Enter a number (1-{}) to process that file".format(len(files)))
|
74
|
-
print(" • Enter 'all' to process all files in batch")
|
75
|
-
print(" • Enter 'quit' to exit")
|
76
|
-
|
77
|
-
choice = input("Your choice: ").strip().lower()
|
78
|
-
|
79
|
-
if choice == 'quit':
|
80
|
-
return None
|
81
|
-
elif choice == 'all':
|
82
|
-
return 'batch'
|
83
|
-
else:
|
84
|
-
try:
|
85
|
-
index = int(choice) - 1
|
86
|
-
if 0 <= index < len(files):
|
87
|
-
return files[index]
|
88
|
-
else:
|
89
|
-
print("❌ Invalid selection")
|
90
|
-
return None
|
91
|
-
except ValueError:
|
92
|
-
print("❌ Invalid input")
|
93
|
-
return None
|
94
|
-
|
95
|
-
def create_invoice_processing_department():
|
96
|
-
"""Create the invoice processing department"""
|
97
|
-
|
98
|
-
# Schema extraction agent
|
99
|
-
schema_agent = Agent(
|
100
|
-
role="Schema Engineer",
|
101
|
-
job="Extract invoice database schema",
|
102
|
-
tools=[
|
103
|
-
{"name": "DatabaseQueryTool", "hosted_by": "memra"}
|
104
|
-
],
|
105
|
-
output_key="invoice_schema"
|
106
|
-
)
|
107
|
-
|
108
|
-
# Invoice processing agent
|
109
|
-
processor_agent = Agent(
|
110
|
-
role="Invoice Processor",
|
111
|
-
job="Extract structured data from invoice PDF",
|
112
|
-
tools=[
|
113
|
-
{"name": "PDFProcessor", "hosted_by": "memra"},
|
114
|
-
{"name": "InvoiceExtractionWorkflow", "hosted_by": "memra"}
|
115
|
-
],
|
116
|
-
input_keys=["file", "invoice_schema"],
|
117
|
-
output_key="invoice_data"
|
118
|
-
)
|
119
|
-
|
120
|
-
# Database writer agent
|
121
|
-
writer_agent = Agent(
|
122
|
-
role="Database Writer",
|
123
|
-
job="Insert validated invoice data into database",
|
124
|
-
tools=[
|
125
|
-
{"name": "DataValidator", "hosted_by": "mcp"},
|
126
|
-
{"name": "PostgresInsert", "hosted_by": "mcp"}
|
127
|
-
],
|
128
|
-
input_keys=["invoice_data", "invoice_schema"],
|
129
|
-
output_key="write_confirmation"
|
130
|
-
)
|
131
|
-
|
132
|
-
return Department(
|
133
|
-
name="Smart Invoice Processing",
|
134
|
-
mission="Intelligently process invoices from PDF to database",
|
135
|
-
agents=[schema_agent, processor_agent, writer_agent],
|
136
|
-
workflow_order=["Schema Engineer", "Invoice Processor", "Database Writer"],
|
137
|
-
context={
|
138
|
-
"mcp_bridge_url": "http://localhost:8081",
|
139
|
-
"mcp_bridge_secret": "test-secret-for-development"
|
140
|
-
}
|
141
|
-
)
|
142
|
-
|
143
|
-
def process_single_invoice(file_path):
|
144
|
-
"""Process a single invoice file"""
|
145
|
-
print(f"\n🔄 Processing: {os.path.basename(file_path)}")
|
146
|
-
print("=" * 60)
|
147
|
-
|
148
|
-
department = create_invoice_processing_department()
|
149
|
-
engine = ExecutionEngine()
|
150
|
-
|
151
|
-
input_data = {
|
152
|
-
"file": file_path,
|
153
|
-
"connection": "postgresql://tarpus@localhost:5432/memra_invoice_db"
|
154
|
-
}
|
155
|
-
|
156
|
-
result = engine.execute_department(department, input_data)
|
157
|
-
|
158
|
-
if result.success:
|
159
|
-
print(f"✅ Successfully processed {os.path.basename(file_path)}")
|
160
|
-
|
161
|
-
# Show extracted data
|
162
|
-
invoice_data = result.data.get('invoice_data', {})
|
163
|
-
if 'headerSection' in invoice_data:
|
164
|
-
vendor = invoice_data['headerSection'].get('vendorName', 'Unknown')
|
165
|
-
print(f"🏢 Vendor: {vendor}")
|
166
|
-
|
167
|
-
# Show database result
|
168
|
-
confirmation = result.data.get('write_confirmation', {})
|
169
|
-
if 'record_id' in confirmation:
|
170
|
-
print(f"💾 Database Record ID: {confirmation['record_id']}")
|
171
|
-
elif confirmation.get('_mock'):
|
172
|
-
print("🔄 Database: Mock insertion (MCP bridge issue)")
|
173
|
-
|
174
|
-
return True
|
175
|
-
else:
|
176
|
-
print(f"❌ Failed to process {os.path.basename(file_path)}: {result.error}")
|
177
|
-
return False
|
178
|
-
|
179
|
-
def process_batch(file_list):
|
180
|
-
"""Process multiple invoice files in batch"""
|
181
|
-
print(f"\n🔄 Batch Processing: {len(file_list)} files")
|
182
|
-
print("=" * 60)
|
183
|
-
|
184
|
-
results = []
|
185
|
-
for i, file_path in enumerate(file_list, 1):
|
186
|
-
print(f"\n📄 File {i}/{len(file_list)}: {os.path.basename(file_path)}")
|
187
|
-
success = process_single_invoice(file_path)
|
188
|
-
results.append((file_path, success))
|
189
|
-
|
190
|
-
# Summary
|
191
|
-
print(f"\n📊 Batch Processing Summary:")
|
192
|
-
successful = sum(1 for _, success in results if success)
|
193
|
-
print(f"✅ Successful: {successful}/{len(file_list)}")
|
194
|
-
|
195
|
-
if successful < len(file_list):
|
196
|
-
print("❌ Failed files:")
|
197
|
-
for file_path, success in results:
|
198
|
-
if not success:
|
199
|
-
print(f" • {os.path.basename(file_path)}")
|
200
|
-
|
201
|
-
def main():
|
202
|
-
print("🧠 Smart Invoice Processor")
|
203
|
-
print("Intelligent file discovery and processing")
|
204
|
-
print("=" * 60)
|
205
|
-
|
206
|
-
# Discover what to process
|
207
|
-
target = get_file_to_process()
|
208
|
-
|
209
|
-
if target is None:
|
210
|
-
print("👋 Goodbye!")
|
211
|
-
return
|
212
|
-
|
213
|
-
if target == 'batch':
|
214
|
-
files = discover_invoice_files()
|
215
|
-
process_batch(files)
|
216
|
-
else:
|
217
|
-
process_single_invoice(target)
|
218
|
-
|
219
|
-
if __name__ == "__main__":
|
220
|
-
main()
|
@@ -1 +0,0 @@
|
|
1
|
-
# Logic package - contains the actual implementation of tools and workflows
|
@@ -1,50 +0,0 @@
|
|
1
|
-
from typing import Dict, Any
|
2
|
-
import logging
|
3
|
-
import json
|
4
|
-
from pathlib import Path
|
5
|
-
|
6
|
-
logger = logging.getLogger(__name__)
|
7
|
-
|
8
|
-
class FileReader:
|
9
|
-
"""Tool for reading files from the filesystem"""
|
10
|
-
|
11
|
-
def read_file(self, file_path: str) -> Dict[str, Any]:
|
12
|
-
"""Read a file and return its contents"""
|
13
|
-
logger.info(f"Reading file: {file_path}")
|
14
|
-
try:
|
15
|
-
path = Path(file_path)
|
16
|
-
if not path.exists():
|
17
|
-
return {
|
18
|
-
"success": False,
|
19
|
-
"error": f"File not found: {file_path}"
|
20
|
-
}
|
21
|
-
|
22
|
-
with open(path, 'r') as f:
|
23
|
-
content = f.read()
|
24
|
-
|
25
|
-
# Try to parse as JSON if it's a .json file
|
26
|
-
if path.suffix.lower() == '.json':
|
27
|
-
try:
|
28
|
-
parsed_content = json.loads(content)
|
29
|
-
return {
|
30
|
-
"success": True,
|
31
|
-
"content": parsed_content,
|
32
|
-
"raw_content": content
|
33
|
-
}
|
34
|
-
except json.JSONDecodeError as e:
|
35
|
-
return {
|
36
|
-
"success": False,
|
37
|
-
"error": f"Invalid JSON: {str(e)}"
|
38
|
-
}
|
39
|
-
|
40
|
-
return {
|
41
|
-
"success": True,
|
42
|
-
"content": content
|
43
|
-
}
|
44
|
-
|
45
|
-
except Exception as e:
|
46
|
-
logger.error(f"Error reading file {file_path}: {str(e)}")
|
47
|
-
return {
|
48
|
-
"success": False,
|
49
|
-
"error": str(e)
|
50
|
-
}
|