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.
Files changed (62) hide show
  1. memra/cli.py +322 -51
  2. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/METADATA +1 -1
  3. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/RECORD +7 -61
  4. memra-0.2.15.dist-info/top_level.txt +1 -0
  5. memra-0.2.13.dist-info/top_level.txt +0 -4
  6. memra-ops/app.py +0 -808
  7. memra-ops/config/config.py +0 -25
  8. memra-ops/config.py +0 -34
  9. memra-ops/logic/__init__.py +0 -1
  10. memra-ops/logic/file_tools.py +0 -43
  11. memra-ops/logic/invoice_tools.py +0 -668
  12. memra-ops/logic/invoice_tools_fix.py +0 -66
  13. memra-ops/mcp_bridge_server.py +0 -1178
  14. memra-ops/scripts/check_database.py +0 -37
  15. memra-ops/scripts/clear_database.py +0 -48
  16. memra-ops/scripts/monitor_database.py +0 -67
  17. memra-ops/scripts/release.py +0 -133
  18. memra-ops/scripts/reset_database.py +0 -65
  19. memra-ops/scripts/start_memra.py +0 -334
  20. memra-ops/scripts/stop_memra.py +0 -132
  21. memra-ops/server_tool_registry.py +0 -190
  22. memra-ops/tests/test_llm_text_to_sql.py +0 -115
  23. memra-ops/tests/test_llm_vs_pattern.py +0 -130
  24. memra-ops/tests/test_mcp_schema_aware.py +0 -124
  25. memra-ops/tests/test_schema_aware_sql.py +0 -139
  26. memra-ops/tests/test_schema_aware_sql_simple.py +0 -66
  27. memra-ops/tests/test_text_to_sql_demo.py +0 -140
  28. memra-ops/tools/mcp_bridge_server.py +0 -851
  29. memra-sdk/examples/accounts_payable.py +0 -215
  30. memra-sdk/examples/accounts_payable_client.py +0 -217
  31. memra-sdk/examples/accounts_payable_mcp.py +0 -200
  32. memra-sdk/examples/ask_questions.py +0 -123
  33. memra-sdk/examples/invoice_processing.py +0 -116
  34. memra-sdk/examples/propane_delivery.py +0 -87
  35. memra-sdk/examples/simple_text_to_sql.py +0 -158
  36. memra-sdk/memra/__init__.py +0 -31
  37. memra-sdk/memra/discovery.py +0 -15
  38. memra-sdk/memra/discovery_client.py +0 -49
  39. memra-sdk/memra/execution.py +0 -481
  40. memra-sdk/memra/models.py +0 -99
  41. memra-sdk/memra/tool_registry.py +0 -343
  42. memra-sdk/memra/tool_registry_client.py +0 -106
  43. memra-sdk/scripts/release.py +0 -133
  44. memra-sdk/setup.py +0 -52
  45. memra-workflows/accounts_payable/accounts_payable.py +0 -215
  46. memra-workflows/accounts_payable/accounts_payable_client.py +0 -216
  47. memra-workflows/accounts_payable/accounts_payable_mcp.py +0 -200
  48. memra-workflows/accounts_payable/accounts_payable_smart.py +0 -221
  49. memra-workflows/invoice_processing/invoice_processing.py +0 -116
  50. memra-workflows/invoice_processing/smart_invoice_processor.py +0 -220
  51. memra-workflows/logic/__init__.py +0 -1
  52. memra-workflows/logic/file_tools.py +0 -50
  53. memra-workflows/logic/invoice_tools.py +0 -501
  54. memra-workflows/logic/propane_agents.py +0 -52
  55. memra-workflows/mcp_bridge_server.py +0 -230
  56. memra-workflows/propane_delivery/propane_delivery.py +0 -87
  57. memra-workflows/text_to_sql/complete_invoice_workflow_with_queries.py +0 -208
  58. memra-workflows/text_to_sql/complete_text_to_sql_system.py +0 -266
  59. memra-workflows/text_to_sql/file_discovery_demo.py +0 -156
  60. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/LICENSE +0 -0
  61. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/WHEEL +0 -0
  62. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/entry_points.txt +0 -0
@@ -1,123 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Simple interactive script to ask questions about invoices
4
- """
5
-
6
- import requests
7
- import json
8
-
9
- def ask_question(question):
10
- """Ask a question and get SQL + results"""
11
- bridge_url = "http://localhost:8081"
12
- bridge_secret = "test-secret-for-development"
13
-
14
- # No hard-coded schema - let the server fetch it dynamically
15
- schema_info = {}
16
-
17
- headers = {
18
- "Content-Type": "application/json",
19
- "X-Bridge-Secret": bridge_secret
20
- }
21
-
22
- print(f"\n🤖 Processing: {question}")
23
- print("-" * 50)
24
-
25
- # Step 1: Generate SQL
26
- sql_request = {
27
- "tool_name": "TextToSQLGenerator",
28
- "input_data": {
29
- "question": question,
30
- "schema_info": schema_info
31
- }
32
- }
33
-
34
- try:
35
- response = requests.post(f"{bridge_url}/execute_tool", json=sql_request, headers=headers, timeout=60)
36
-
37
- if response.status_code != 200:
38
- print(f"❌ SQL generation failed: HTTP {response.status_code}")
39
- return
40
-
41
- result = response.json()
42
- if not result.get("success"):
43
- print(f"❌ SQL generation failed: {result.get('error')}")
44
- return
45
-
46
- data = result.get("data", {})
47
- sql_query = data.get("generated_sql", "")
48
- method = data.get("method", "unknown")
49
-
50
- # Check if SQL is incomplete
51
- if not sql_query or sql_query.strip() == "SELECT" or len(sql_query.strip()) < 10:
52
- print(f"❌ Generated incomplete SQL: '{sql_query}'")
53
- print("💡 Try rephrasing your question more simply")
54
- return
55
-
56
- print(f"📝 Generated SQL: {sql_query}")
57
- print(f"💡 Method: {method}")
58
-
59
- # Step 2: Execute SQL
60
- exec_request = {
61
- "tool_name": "SQLExecutor",
62
- "input_data": {
63
- "sql_query": sql_query
64
- }
65
- }
66
-
67
- response = requests.post(f"{bridge_url}/execute_tool", json=exec_request, headers=headers, timeout=60)
68
-
69
- if response.status_code != 200:
70
- print(f"❌ SQL execution failed: HTTP {response.status_code}")
71
- return
72
-
73
- result = response.json()
74
- if not result.get("success"):
75
- print(f"❌ SQL execution failed: {result.get('error')}")
76
- return
77
-
78
- exec_data = result.get("data", {})
79
- results = exec_data.get("results", [])
80
- row_count = exec_data.get("row_count", 0)
81
-
82
- print(f"\n📊 Results ({row_count} rows):")
83
- if results:
84
- # Show first 5 results
85
- for i, row in enumerate(results[:5], 1):
86
- print(f" {i}. {row}")
87
-
88
- if len(results) > 5:
89
- print(f" ... and {len(results) - 5} more rows")
90
- else:
91
- print(" No results found")
92
-
93
- except Exception as e:
94
- print(f"❌ Error: {str(e)}")
95
-
96
- def main():
97
- """Interactive question asking"""
98
- print("🎯 Invoice Question Assistant")
99
- print("=" * 50)
100
- print("Ask questions about your invoices in natural language!")
101
- print("Examples:")
102
- print(" - Show me all invoices from Air Liquide")
103
- print(" - Find invoices with amounts greater than 1000")
104
- print(" - What is the total amount of all invoices?")
105
- print(" - How many invoices do we have?")
106
- print("\nType 'quit' to exit")
107
-
108
- while True:
109
- print("\n" + "="*50)
110
- question = input("❓ Your question: ").strip()
111
-
112
- if question.lower() in ['quit', 'exit', 'q']:
113
- print("👋 Goodbye!")
114
- break
115
-
116
- if not question:
117
- print("Please enter a question")
118
- continue
119
-
120
- ask_question(question)
121
-
122
- if __name__ == "__main__":
123
- 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,87 +0,0 @@
1
- from memra.sdk.models import Agent, Department, Tool
2
-
3
- # Define the tools that our agents will use
4
- data_extraction_tool = Tool(
5
- name="PropaneDataExtractor",
6
- description="Extracts propane-related data from various sources",
7
- hosted_by="memra"
8
- )
9
-
10
- planning_tool = Tool(
11
- name="PropaneDeliveryPlanner",
12
- description="Plans optimal propane delivery routes and schedules",
13
- hosted_by="memra"
14
- )
15
-
16
- execution_tool = Tool(
17
- name="PropaneDeliveryExecutor",
18
- description="Executes and tracks propane deliveries",
19
- hosted_by="memra"
20
- )
21
-
22
- # Define our agents
23
- data_extractor = Agent(
24
- role="Data Extraction Specialist",
25
- job="Extract and validate propane delivery data",
26
- tools=[data_extraction_tool],
27
- systems=["CustomerDatabase", "PropaneLevelsAPI"],
28
- input_keys=["customer_ids", "date_range"],
29
- output_key="extracted_data"
30
- )
31
-
32
- delivery_planner = Agent(
33
- role="Delivery Route Planner",
34
- job="Plan optimal delivery routes and schedules",
35
- tools=[planning_tool],
36
- systems=["RouteOptimizationEngine"],
37
- input_keys=["extracted_data"],
38
- output_key="delivery_plan"
39
- )
40
-
41
- delivery_executor = Agent(
42
- role="Delivery Coordinator",
43
- job="Execute and monitor propane deliveries",
44
- tools=[execution_tool],
45
- systems=["DeliveryTrackingSystem"],
46
- input_keys=["delivery_plan"],
47
- output_key="delivery_status"
48
- )
49
-
50
- # Define the manager agent that oversees the workflow
51
- manager = Agent(
52
- role="Propane Operations Manager",
53
- job="Oversee and coordinate the propane delivery workflow",
54
- llm={"model": "claude-3-opus"},
55
- sops=[
56
- "Validate data quality",
57
- "Handle delivery exceptions",
58
- "Optimize resource allocation"
59
- ],
60
- input_keys=["extracted_data", "delivery_plan", "delivery_status"],
61
- output_key="workflow_status"
62
- )
63
-
64
- # Create the Propane Delivery Department
65
- propane_department = Department(
66
- name="Propane Delivery Operations",
67
- mission="Efficiently manage and execute propane deliveries",
68
- agents=[data_extractor, delivery_planner, delivery_executor],
69
- manager_agent=manager,
70
- workflow_order=[
71
- "Data Extraction Specialist",
72
- "Delivery Route Planner",
73
- "Delivery Coordinator"
74
- ]
75
- )
76
-
77
- # Example usage
78
- if __name__ == "__main__":
79
- # This is how a developer would use the department
80
- result = propane_department.run({
81
- "customer_ids": ["CUST001", "CUST002"],
82
- "date_range": {
83
- "start": "2024-03-20",
84
- "end": "2024-03-27"
85
- }
86
- })
87
- print(f"Workflow result: {result}")
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Simple Text-to-SQL Interface
4
- Direct connection to MCP bridge for asking questions about invoices
5
- """
6
-
7
- import requests
8
- import json
9
-
10
- def ask_question(question):
11
- """Ask a question and get SQL + results"""
12
- bridge_url = "http://localhost:8081"
13
- bridge_secret = "test-secret-for-development"
14
-
15
- # Simulated schema info (in real system this would come from schema extraction)
16
- schema_info = {
17
- "schema": {
18
- "invoices": {
19
- "columns": [
20
- {"name": "id", "type": "integer"},
21
- {"name": "vendor_name", "type": "text"},
22
- {"name": "invoice_number", "type": "text"},
23
- {"name": "invoice_date", "type": "date"},
24
- {"name": "total_amount", "type": "numeric"},
25
- {"name": "tax_amount", "type": "numeric"},
26
- {"name": "line_items", "type": "jsonb"},
27
- {"name": "status", "type": "text"}
28
- ]
29
- }
30
- }
31
- }
32
-
33
- headers = {
34
- "Content-Type": "application/json",
35
- "X-Bridge-Secret": bridge_secret
36
- }
37
-
38
- print(f"\n🤖 Processing: {question}")
39
- print("-" * 50)
40
-
41
- # Step 1: Generate SQL
42
- sql_request = {
43
- "tool_name": "TextToSQLGenerator",
44
- "input_data": {
45
- "question": question,
46
- "schema_info": schema_info
47
- }
48
- }
49
-
50
- try:
51
- response = requests.post(f"{bridge_url}/execute_tool", json=sql_request, headers=headers, timeout=10)
52
-
53
- if response.status_code != 200:
54
- print(f"❌ SQL generation failed: HTTP {response.status_code}")
55
- return
56
-
57
- result = response.json()
58
- if not result.get("success"):
59
- print(f"❌ SQL generation failed: {result.get('error')}")
60
- return
61
-
62
- data = result.get("data", {})
63
- sql_query = data.get("generated_sql", "")
64
- explanation = data.get("explanation", "")
65
-
66
- print(f"📝 Generated SQL: {sql_query}")
67
- print(f"💡 Explanation: {explanation}")
68
-
69
- # Step 2: Execute SQL
70
- exec_request = {
71
- "tool_name": "SQLExecutor",
72
- "input_data": {
73
- "sql_query": sql_query
74
- }
75
- }
76
-
77
- response = requests.post(f"{bridge_url}/execute_tool", json=exec_request, headers=headers, timeout=10)
78
-
79
- if response.status_code != 200:
80
- print(f"❌ SQL execution failed: HTTP {response.status_code}")
81
- return
82
-
83
- result = response.json()
84
- if not result.get("success"):
85
- print(f"❌ SQL execution failed: {result.get('error')}")
86
- return
87
-
88
- exec_data = result.get("data", {})
89
- results = exec_data.get("results", [])
90
- row_count = exec_data.get("row_count", 0)
91
-
92
- print(f"\n📊 Results ({row_count} rows):")
93
- if results:
94
- # Show first 10 results
95
- for i, row in enumerate(results[:10], 1):
96
- print(f" {i}. {row}")
97
-
98
- if len(results) > 10:
99
- print(f" ... and {len(results) - 10} more rows")
100
- else:
101
- print(" No results found")
102
-
103
- except Exception as e:
104
- print(f"❌ Error: {str(e)}")
105
-
106
- def main():
107
- """Interactive text-to-SQL interface"""
108
- print("🚀 Simple Text-to-SQL Interface")
109
- print("=" * 50)
110
- print("Ask questions about your invoice database!")
111
- print("Examples:")
112
- print(" • Show me all invoices from Air Liquide")
113
- print(" • What is the total amount of all invoices?")
114
- print(" • How many invoices do we have?")
115
- print(" • Show me the 5 most recent invoices")
116
- print(" • What is the average invoice amount?")
117
- print("\nType 'quit' to exit")
118
- print("=" * 50)
119
-
120
- # Check if MCP bridge is running
121
- try:
122
- response = requests.get("http://localhost:8081/health", timeout=5)
123
- if response.status_code == 200:
124
- print("✅ MCP Bridge server is running")
125
- else:
126
- print("❌ MCP Bridge server is not responding")
127
- return
128
- except Exception as e:
129
- print(f"❌ Cannot connect to MCP Bridge server: {str(e)}")
130
- print("💡 Make sure to start the MCP bridge server first:")
131
- print(" source /Users/tarpus/miniconda3/bin/activate memra && \\")
132
- print(" export MCP_POSTGRES_URL=\"postgresql://tarpus@localhost:5432/memra_invoice_db\" && \\")
133
- print(" export MCP_BRIDGE_SECRET=\"test-secret-for-development\" && \\")
134
- print(" python3 mcp_bridge_server.py")
135
- return
136
-
137
- while True:
138
- try:
139
- question = input("\n❓ Your question: ").strip()
140
-
141
- if question.lower() in ['quit', 'exit', 'q']:
142
- print("\n👋 Goodbye!")
143
- break
144
-
145
- if not question:
146
- print("Please enter a question")
147
- continue
148
-
149
- ask_question(question)
150
-
151
- except KeyboardInterrupt:
152
- print("\n\n👋 Goodbye!")
153
- break
154
- except Exception as e:
155
- print(f"\n❌ Error: {str(e)}")
156
-
157
- if __name__ == "__main__":
158
- main()
@@ -1,31 +0,0 @@
1
- """
2
- Memra SDK - Declarative AI Workflows
3
-
4
- A framework for building AI-powered business workflows using a declarative approach.
5
- Think of it as "Kubernetes for business logic" where agents are the pods and
6
- departments are the deployments.
7
- """
8
-
9
- __version__ = "0.2.4"
10
-
11
- # Core imports
12
- from .models import Agent, Department, Tool, LLM
13
- from .execution import ExecutionEngine
14
- from .discovery_client import check_api_health, get_api_status
15
-
16
- # Make key classes available at package level
17
- __all__ = [
18
- "Agent",
19
- "Department",
20
- "Tool",
21
- "LLM",
22
- "ExecutionEngine",
23
- "check_api_health",
24
- "get_api_status",
25
- "__version__"
26
- ]
27
-
28
- # Optional: Add version check for compatibility
29
- import sys
30
- if sys.version_info < (3, 8):
31
- raise RuntimeError("Memra requires Python 3.8 or higher")
@@ -1,15 +0,0 @@
1
- from typing import List, Dict, Any, Optional
2
- from .tool_registry import ToolRegistry
3
-
4
- def discover_tools(hosted_by: Optional[str] = None) -> List[Dict[str, Any]]:
5
- """
6
- Discover available tools in the Memra platform.
7
-
8
- Args:
9
- hosted_by: Filter tools by host ("memra" or "mcp"). If None, returns all tools.
10
-
11
- Returns:
12
- List of available tools with their metadata
13
- """
14
- registry = ToolRegistry()
15
- return registry.discover_tools(hosted_by)
@@ -1,49 +0,0 @@
1
- """
2
- Client-side tool discovery for Memra SDK
3
- Queries the Memra API to discover available tools
4
- """
5
-
6
- from typing import List, Dict, Any, Optional
7
- from .tool_registry_client import ToolRegistryClient
8
-
9
- def discover_tools(hosted_by: Optional[str] = None) -> List[Dict[str, Any]]:
10
- """
11
- Discover available tools from the Memra API
12
-
13
- Args:
14
- hosted_by: Filter tools by hosting provider ("memra" or "mcp")
15
-
16
- Returns:
17
- List of available tools with their descriptions
18
- """
19
- registry = ToolRegistryClient()
20
- return registry.discover_tools(hosted_by)
21
-
22
- def check_api_health() -> bool:
23
- """
24
- Check if the Memra API is available
25
-
26
- Returns:
27
- True if API is healthy, False otherwise
28
- """
29
- registry = ToolRegistryClient()
30
- return registry.health_check()
31
-
32
- def get_api_status() -> Dict[str, Any]:
33
- """
34
- Get detailed API status information
35
-
36
- Returns:
37
- Dictionary with API status details
38
- """
39
- registry = ToolRegistryClient()
40
-
41
- is_healthy = registry.health_check()
42
- tools = registry.discover_tools() if is_healthy else []
43
-
44
- return {
45
- "api_healthy": is_healthy,
46
- "api_url": registry.api_base,
47
- "tools_available": len(tools),
48
- "tools": tools
49
- }