memra 0.2.2__py3-none-any.whl → 0.2.4__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 +6 -2
- memra/execution.py +53 -0
- memra/tool_registry.py +162 -0
- memra-0.2.4.dist-info/METADATA +145 -0
- memra-0.2.4.dist-info/RECORD +58 -0
- {memra-0.2.2.dist-info → memra-0.2.4.dist-info}/WHEEL +1 -1
- memra-0.2.4.dist-info/top_level.txt +4 -0
- memra-ops/app.py +710 -0
- memra-ops/config/config.py +25 -0
- memra-ops/config.py +34 -0
- memra-ops/scripts/release.py +133 -0
- memra-ops/scripts/start_memra.py +334 -0
- memra-ops/scripts/stop_memra.py +132 -0
- memra-ops/server_tool_registry.py +188 -0
- memra-ops/tests/test_llm_text_to_sql.py +115 -0
- memra-ops/tests/test_llm_vs_pattern.py +130 -0
- memra-ops/tests/test_mcp_schema_aware.py +124 -0
- memra-ops/tests/test_schema_aware_sql.py +139 -0
- memra-ops/tests/test_schema_aware_sql_simple.py +66 -0
- memra-ops/tests/test_text_to_sql_demo.py +140 -0
- memra-ops/tools/mcp_bridge_server.py +851 -0
- memra-sdk/examples/accounts_payable.py +215 -0
- memra-sdk/examples/accounts_payable_client.py +217 -0
- memra-sdk/examples/accounts_payable_mcp.py +200 -0
- memra-sdk/examples/ask_questions.py +123 -0
- memra-sdk/examples/invoice_processing.py +116 -0
- memra-sdk/examples/propane_delivery.py +87 -0
- memra-sdk/examples/simple_text_to_sql.py +158 -0
- memra-sdk/memra/__init__.py +31 -0
- memra-sdk/memra/discovery.py +15 -0
- memra-sdk/memra/discovery_client.py +49 -0
- memra-sdk/memra/execution.py +481 -0
- memra-sdk/memra/models.py +99 -0
- memra-sdk/memra/tool_registry.py +343 -0
- memra-sdk/memra/tool_registry_client.py +106 -0
- memra-sdk/scripts/release.py +133 -0
- memra-sdk/setup.py +52 -0
- memra-workflows/accounts_payable/accounts_payable.py +215 -0
- memra-workflows/accounts_payable/accounts_payable_client.py +216 -0
- memra-workflows/accounts_payable/accounts_payable_mcp.py +200 -0
- memra-workflows/accounts_payable/accounts_payable_smart.py +221 -0
- memra-workflows/invoice_processing/invoice_processing.py +116 -0
- memra-workflows/invoice_processing/smart_invoice_processor.py +220 -0
- memra-workflows/logic/__init__.py +1 -0
- memra-workflows/logic/file_tools.py +50 -0
- memra-workflows/logic/invoice_tools.py +501 -0
- memra-workflows/logic/propane_agents.py +52 -0
- memra-workflows/mcp_bridge_server.py +230 -0
- memra-workflows/propane_delivery/propane_delivery.py +87 -0
- memra-workflows/text_to_sql/complete_invoice_workflow_with_queries.py +208 -0
- memra-workflows/text_to_sql/complete_text_to_sql_system.py +266 -0
- memra-workflows/text_to_sql/file_discovery_demo.py +156 -0
- memra-0.2.2.dist-info/METADATA +0 -148
- memra-0.2.2.dist-info/RECORD +0 -13
- memra-0.2.2.dist-info/top_level.txt +0 -1
- {memra-0.2.2.dist-info → memra-0.2.4.dist-info}/entry_points.txt +0 -0
- {memra-0.2.2.dist-info → memra-0.2.4.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Complete Text-to-SQL System
|
4
|
+
Demonstrates the full pipeline: English Question → Schema → SQL Generation → Execution → Real Results
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
import json
|
10
|
+
from pathlib import Path
|
11
|
+
|
12
|
+
# Add the parent directory to the path so we can import memra
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
14
|
+
|
15
|
+
from memra import ExecutionEngine, Agent, Tool
|
16
|
+
|
17
|
+
def create_text_to_sql_system():
|
18
|
+
"""Create a complete text-to-SQL system with real database integration"""
|
19
|
+
|
20
|
+
# Initialize execution engine
|
21
|
+
engine = ExecutionEngine()
|
22
|
+
|
23
|
+
# Schema Extraction Agent
|
24
|
+
schema_agent = Agent(
|
25
|
+
role="Database Schema Analyst",
|
26
|
+
job="Extract and analyze database schemas",
|
27
|
+
output_key="schema_data",
|
28
|
+
tools=[
|
29
|
+
Tool(
|
30
|
+
name="DatabaseQueryTool",
|
31
|
+
hosted_by="memra",
|
32
|
+
description="Query database schemas and structure"
|
33
|
+
)
|
34
|
+
]
|
35
|
+
)
|
36
|
+
|
37
|
+
# SQL Generation Agent
|
38
|
+
sql_generator_agent = Agent(
|
39
|
+
role="SQL Generator",
|
40
|
+
job="Convert natural language to SQL queries",
|
41
|
+
output_key="generated_sql",
|
42
|
+
tools=[
|
43
|
+
Tool(
|
44
|
+
name="TextToSQLGenerator",
|
45
|
+
hosted_by="mcp",
|
46
|
+
description="Generate SQL from natural language questions",
|
47
|
+
config={
|
48
|
+
"bridge_url": "http://localhost:8081",
|
49
|
+
"bridge_secret": "test-secret-for-development"
|
50
|
+
}
|
51
|
+
)
|
52
|
+
]
|
53
|
+
)
|
54
|
+
|
55
|
+
# SQL Execution Agent
|
56
|
+
sql_executor_agent = Agent(
|
57
|
+
role="SQL Executor",
|
58
|
+
job="Execute SQL queries and return results",
|
59
|
+
output_key="query_results",
|
60
|
+
tools=[
|
61
|
+
Tool(
|
62
|
+
name="SQLExecutor",
|
63
|
+
hosted_by="mcp",
|
64
|
+
description="Execute SQL queries against PostgreSQL database",
|
65
|
+
config={
|
66
|
+
"bridge_url": "http://localhost:8081",
|
67
|
+
"bridge_secret": "test-secret-for-development"
|
68
|
+
}
|
69
|
+
)
|
70
|
+
]
|
71
|
+
)
|
72
|
+
|
73
|
+
return engine, schema_agent, sql_generator_agent, sql_executor_agent
|
74
|
+
|
75
|
+
def extract_database_schema(engine, schema_agent):
|
76
|
+
"""Extract the database schema for context"""
|
77
|
+
print("🔍 Extracting database schema...")
|
78
|
+
|
79
|
+
schema_task = {
|
80
|
+
"task": "Extract the complete schema for the invoices table including column names, types, and sample data",
|
81
|
+
"table_name": "invoices",
|
82
|
+
"include_sample_data": True
|
83
|
+
}
|
84
|
+
|
85
|
+
result = engine.execute_task(schema_agent, schema_task)
|
86
|
+
|
87
|
+
if result.get("success"):
|
88
|
+
print(f"✅ Schema extracted successfully ({result.get('execution_time', 0):.1f}s)")
|
89
|
+
schema_data = result.get("result", {})
|
90
|
+
|
91
|
+
# Display schema info
|
92
|
+
if "schema" in schema_data:
|
93
|
+
print("\n📊 Database Schema:")
|
94
|
+
schema = schema_data["schema"]
|
95
|
+
for table_name, table_info in schema.items():
|
96
|
+
print(f" Table: {table_name}")
|
97
|
+
if "columns" in table_info:
|
98
|
+
for col in table_info["columns"]:
|
99
|
+
print(f" - {col['name']} ({col['type']})")
|
100
|
+
|
101
|
+
return schema_data
|
102
|
+
else:
|
103
|
+
print(f"❌ Schema extraction failed: {result.get('error', 'Unknown error')}")
|
104
|
+
return {}
|
105
|
+
|
106
|
+
def generate_sql_from_question(engine, sql_generator_agent, question, schema_info):
|
107
|
+
"""Generate SQL from natural language question"""
|
108
|
+
print(f"\n🤖 Generating SQL for: '{question}'")
|
109
|
+
|
110
|
+
sql_generation_task = {
|
111
|
+
"question": question,
|
112
|
+
"schema_info": schema_info,
|
113
|
+
"context": "Generate SQL query for invoice database analysis"
|
114
|
+
}
|
115
|
+
|
116
|
+
result = engine.execute_task(sql_generator_agent, sql_generation_task)
|
117
|
+
|
118
|
+
if result.get("success"):
|
119
|
+
print(f"✅ SQL generated successfully ({result.get('execution_time', 0):.1f}s)")
|
120
|
+
sql_data = result.get("result", {})
|
121
|
+
|
122
|
+
generated_sql = sql_data.get("generated_sql", "")
|
123
|
+
print(f"\n📝 Generated SQL:")
|
124
|
+
print(f" {generated_sql}")
|
125
|
+
|
126
|
+
return generated_sql
|
127
|
+
else:
|
128
|
+
print(f"❌ SQL generation failed: {result.get('error', 'Unknown error')}")
|
129
|
+
return None
|
130
|
+
|
131
|
+
def execute_sql_query(engine, sql_executor_agent, sql_query):
|
132
|
+
"""Execute the generated SQL query"""
|
133
|
+
print(f"\n⚡ Executing SQL query...")
|
134
|
+
|
135
|
+
execution_task = {
|
136
|
+
"sql_query": sql_query,
|
137
|
+
"timeout": 30
|
138
|
+
}
|
139
|
+
|
140
|
+
result = engine.execute_task(sql_executor_agent, execution_task)
|
141
|
+
|
142
|
+
if result.get("success"):
|
143
|
+
print(f"✅ SQL executed successfully ({result.get('execution_time', 0):.1f}s)")
|
144
|
+
query_results = result.get("result", {})
|
145
|
+
|
146
|
+
# Display results
|
147
|
+
results = query_results.get("results", [])
|
148
|
+
row_count = query_results.get("row_count", 0)
|
149
|
+
|
150
|
+
print(f"\n📋 Query Results ({row_count} rows):")
|
151
|
+
if results:
|
152
|
+
# Display first few results
|
153
|
+
for i, row in enumerate(results[:5]):
|
154
|
+
print(f" Row {i+1}: {row}")
|
155
|
+
|
156
|
+
if len(results) > 5:
|
157
|
+
print(f" ... and {len(results) - 5} more rows")
|
158
|
+
else:
|
159
|
+
print(" No results found")
|
160
|
+
|
161
|
+
return query_results
|
162
|
+
else:
|
163
|
+
print(f"❌ SQL execution failed: {result.get('error', 'Unknown error')}")
|
164
|
+
return {}
|
165
|
+
|
166
|
+
def run_text_to_sql_query(engine, schema_agent, sql_generator_agent, sql_executor_agent, question):
|
167
|
+
"""Run the complete text-to-SQL pipeline"""
|
168
|
+
print(f"\n{'='*60}")
|
169
|
+
print(f"🎯 Processing Question: {question}")
|
170
|
+
print(f"{'='*60}")
|
171
|
+
|
172
|
+
# Step 1: Extract schema (cached after first run)
|
173
|
+
if not hasattr(run_text_to_sql_query, 'cached_schema'):
|
174
|
+
run_text_to_sql_query.cached_schema = extract_database_schema(engine, schema_agent)
|
175
|
+
|
176
|
+
schema_info = run_text_to_sql_query.cached_schema
|
177
|
+
|
178
|
+
# Step 2: Generate SQL
|
179
|
+
sql_query = generate_sql_from_question(engine, sql_generator_agent, question, schema_info)
|
180
|
+
|
181
|
+
if not sql_query:
|
182
|
+
return None
|
183
|
+
|
184
|
+
# Step 3: Execute SQL
|
185
|
+
results = execute_sql_query(engine, sql_executor_agent, sql_query)
|
186
|
+
|
187
|
+
return {
|
188
|
+
"question": question,
|
189
|
+
"sql_query": sql_query,
|
190
|
+
"results": results
|
191
|
+
}
|
192
|
+
|
193
|
+
def main():
|
194
|
+
"""Main function to demonstrate the complete text-to-SQL system"""
|
195
|
+
print("🚀 Starting Complete Text-to-SQL System")
|
196
|
+
print("=" * 60)
|
197
|
+
|
198
|
+
# Create the system
|
199
|
+
engine, schema_agent, sql_generator_agent, sql_executor_agent = create_text_to_sql_system()
|
200
|
+
|
201
|
+
# Example questions to test
|
202
|
+
test_questions = [
|
203
|
+
"Show me all invoices from Air Liquide",
|
204
|
+
"What is the total amount of all invoices?",
|
205
|
+
"How many invoices do we have in the database?",
|
206
|
+
"Show me the most recent 5 invoices",
|
207
|
+
"What is the average invoice amount?",
|
208
|
+
]
|
209
|
+
|
210
|
+
print("📝 Available test questions:")
|
211
|
+
for i, question in enumerate(test_questions, 1):
|
212
|
+
print(f" {i}. {question}")
|
213
|
+
|
214
|
+
print("\n" + "="*60)
|
215
|
+
|
216
|
+
# Interactive mode
|
217
|
+
while True:
|
218
|
+
print("\n🤔 What would you like to know about the invoices?")
|
219
|
+
print(" (Enter a question, number 1-5 for examples, or 'quit' to exit)")
|
220
|
+
|
221
|
+
user_input = input("\n❓ Your question: ").strip()
|
222
|
+
|
223
|
+
if user_input.lower() in ['quit', 'exit', 'q']:
|
224
|
+
print("\n👋 Goodbye!")
|
225
|
+
break
|
226
|
+
|
227
|
+
# Check if it's a number for example questions
|
228
|
+
if user_input.isdigit():
|
229
|
+
question_num = int(user_input)
|
230
|
+
if 1 <= question_num <= len(test_questions):
|
231
|
+
question = test_questions[question_num - 1]
|
232
|
+
else:
|
233
|
+
print(f"❌ Please enter a number between 1 and {len(test_questions)}")
|
234
|
+
continue
|
235
|
+
else:
|
236
|
+
question = user_input
|
237
|
+
|
238
|
+
if not question:
|
239
|
+
print("❌ Please enter a question")
|
240
|
+
continue
|
241
|
+
|
242
|
+
# Run the complete pipeline
|
243
|
+
try:
|
244
|
+
result = run_text_to_sql_query(
|
245
|
+
engine, schema_agent, sql_generator_agent, sql_executor_agent, question
|
246
|
+
)
|
247
|
+
|
248
|
+
if result:
|
249
|
+
print(f"\n✨ Query completed successfully!")
|
250
|
+
|
251
|
+
# Check if results are real or mock
|
252
|
+
results_data = result.get("results", {})
|
253
|
+
if results_data.get("_mock"):
|
254
|
+
print("ℹ️ Note: Results are mocked (MCP bridge not fully connected)")
|
255
|
+
else:
|
256
|
+
print("🎉 Real database results!")
|
257
|
+
else:
|
258
|
+
print("❌ Query failed")
|
259
|
+
|
260
|
+
except KeyboardInterrupt:
|
261
|
+
print("\n\n⏹️ Query interrupted by user")
|
262
|
+
except Exception as e:
|
263
|
+
print(f"\n❌ Error: {str(e)}")
|
264
|
+
|
265
|
+
if __name__ == "__main__":
|
266
|
+
main()
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
File Discovery and Management Demo
|
4
|
+
Simple demonstration of intelligent file discovery tools
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
10
|
+
|
11
|
+
from memra import Agent, Department
|
12
|
+
from memra.execution import ExecutionEngine
|
13
|
+
|
14
|
+
# Set up environment
|
15
|
+
os.environ['MEMRA_API_KEY'] = 'memra-prod-2024-001'
|
16
|
+
|
17
|
+
def create_file_discovery_department():
|
18
|
+
"""Create a simple department for file discovery"""
|
19
|
+
file_manager_agent = Agent(
|
20
|
+
role="File Manager",
|
21
|
+
job="Discover and manage files in directories",
|
22
|
+
sops=[
|
23
|
+
"Scan specified directory for files matching pattern",
|
24
|
+
"List all discovered files with metadata",
|
25
|
+
"Copy files from external locations to standard directories",
|
26
|
+
"Provide file selection interface for multiple files"
|
27
|
+
],
|
28
|
+
systems=["FileSystem"],
|
29
|
+
tools=[
|
30
|
+
{"name": "FileDiscovery", "hosted_by": "mcp"}
|
31
|
+
],
|
32
|
+
input_keys=["directory", "pattern"],
|
33
|
+
output_key="file_operations"
|
34
|
+
)
|
35
|
+
|
36
|
+
return Department(
|
37
|
+
name="File Discovery",
|
38
|
+
mission="Discover files in directories",
|
39
|
+
agents=[file_manager_agent],
|
40
|
+
workflow_order=["File Manager"],
|
41
|
+
context={
|
42
|
+
"mcp_bridge_url": "http://localhost:8081",
|
43
|
+
"mcp_bridge_secret": "test-secret-for-development"
|
44
|
+
}
|
45
|
+
)
|
46
|
+
|
47
|
+
def create_file_copy_department():
|
48
|
+
"""Create a simple department for file copying"""
|
49
|
+
file_copy_agent = Agent(
|
50
|
+
role="File Copier",
|
51
|
+
job="Copy files from external locations to standard directories",
|
52
|
+
sops=[
|
53
|
+
"Accept source file path and destination directory",
|
54
|
+
"Copy file to destination with proper naming",
|
55
|
+
"Verify copy operation success",
|
56
|
+
"Return copy confirmation with metadata"
|
57
|
+
],
|
58
|
+
systems=["FileSystem"],
|
59
|
+
tools=[
|
60
|
+
{"name": "FileCopy", "hosted_by": "mcp"}
|
61
|
+
],
|
62
|
+
input_keys=["source_path", "destination_dir"],
|
63
|
+
output_key="file_operations"
|
64
|
+
)
|
65
|
+
|
66
|
+
return Department(
|
67
|
+
name="File Copy",
|
68
|
+
mission="Copy files to standard directories",
|
69
|
+
agents=[file_copy_agent],
|
70
|
+
workflow_order=["File Copier"],
|
71
|
+
context={
|
72
|
+
"mcp_bridge_url": "http://localhost:8081",
|
73
|
+
"mcp_bridge_secret": "test-secret-for-development"
|
74
|
+
}
|
75
|
+
)
|
76
|
+
|
77
|
+
def main():
|
78
|
+
print("📁 File Discovery and Management Demo")
|
79
|
+
print("=" * 50)
|
80
|
+
|
81
|
+
engine = ExecutionEngine()
|
82
|
+
|
83
|
+
# Demo 1: Discover files in invoices directory
|
84
|
+
print("\n🔍 Demo 1: Discover files in invoices/ directory")
|
85
|
+
|
86
|
+
discovery_dept = create_file_discovery_department()
|
87
|
+
discovery_input = {
|
88
|
+
"directory": "invoices",
|
89
|
+
"pattern": "*.pdf"
|
90
|
+
}
|
91
|
+
|
92
|
+
result = engine.execute_department(discovery_dept, discovery_input)
|
93
|
+
|
94
|
+
if result.success:
|
95
|
+
print("✅ File discovery completed!")
|
96
|
+
file_data = result.data.get('file_operations', {})
|
97
|
+
|
98
|
+
if 'files' in file_data:
|
99
|
+
print(f"\n📄 Found {file_data['files_found']} files:")
|
100
|
+
for file_info in file_data['files']:
|
101
|
+
print(f" • {file_info['filename']} ({file_info['size']}) - {file_info['modified']}")
|
102
|
+
else:
|
103
|
+
print(f"📊 Scanned: {file_data.get('directory', 'unknown')} directory")
|
104
|
+
print(f"🔍 Pattern: {file_data.get('pattern', 'unknown')}")
|
105
|
+
print(f"📁 Files found: {file_data.get('files_found', 0)}")
|
106
|
+
else:
|
107
|
+
print(f"❌ Discovery failed: {result.error}")
|
108
|
+
|
109
|
+
print("\n" + "="*50)
|
110
|
+
|
111
|
+
# Demo 2: Copy external file
|
112
|
+
print("\n📋 Demo 2: Copy external file to invoices/ directory")
|
113
|
+
|
114
|
+
copy_dept = create_file_copy_department()
|
115
|
+
copy_input = {
|
116
|
+
"source_path": "/Users/tarpus/Downloads/new_invoice.pdf",
|
117
|
+
"destination_dir": "invoices"
|
118
|
+
}
|
119
|
+
|
120
|
+
result = engine.execute_department(copy_dept, copy_input)
|
121
|
+
|
122
|
+
if result.success:
|
123
|
+
print("✅ File copy completed!")
|
124
|
+
copy_data = result.data.get('file_operations', {})
|
125
|
+
|
126
|
+
print(f"\n📁 Copy Details:")
|
127
|
+
print(f" Source: {copy_data.get('source_path', 'unknown')}")
|
128
|
+
print(f" Destination: {copy_data.get('destination_path', 'unknown')}")
|
129
|
+
print(f" Size: {copy_data.get('file_size', 'unknown')}")
|
130
|
+
print(f" Status: {copy_data.get('message', 'unknown')}")
|
131
|
+
else:
|
132
|
+
print(f"❌ Copy failed: {result.error}")
|
133
|
+
|
134
|
+
print("\n" + "="*50)
|
135
|
+
|
136
|
+
# Demo 3: Discover files in different directory
|
137
|
+
print("\n🗂 Demo 3: Discover files in documents/ directory")
|
138
|
+
|
139
|
+
docs_input = {
|
140
|
+
"directory": "documents",
|
141
|
+
"pattern": "*.*"
|
142
|
+
}
|
143
|
+
|
144
|
+
result = engine.execute_department(discovery_dept, docs_input)
|
145
|
+
|
146
|
+
if result.success:
|
147
|
+
print("✅ Document discovery completed!")
|
148
|
+
doc_data = result.data.get('file_operations', {})
|
149
|
+
print(f"📊 Scanned: {doc_data.get('directory', 'unknown')} directory")
|
150
|
+
print(f"🔍 Pattern: {doc_data.get('pattern', 'unknown')}")
|
151
|
+
print(f"📁 Files found: {doc_data.get('files_found', 0)}")
|
152
|
+
else:
|
153
|
+
print(f"❌ Document discovery failed: {result.error}")
|
154
|
+
|
155
|
+
if __name__ == "__main__":
|
156
|
+
main()
|
memra-0.2.2.dist-info/METADATA
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: memra
|
3
|
-
Version: 0.2.2
|
4
|
-
Summary: Declarative framework for enterprise workflows with MCP integration - Client SDK
|
5
|
-
Home-page: https://github.com/memra/memra-sdk
|
6
|
-
Author: Memra
|
7
|
-
Author-email: Memra <support@memra.com>
|
8
|
-
License: MIT
|
9
|
-
Project-URL: Homepage, https://memra.co
|
10
|
-
Project-URL: Repository, https://github.com/memra-platform/memra-sdk
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
12
|
-
Classifier: Intended Audience :: Developers
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
14
|
-
Classifier: Operating System :: OS Independent
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
20
|
-
Requires-Python: >=3.8
|
21
|
-
Description-Content-Type: text/markdown
|
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
|
28
|
-
Provides-Extra: dev
|
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
|
-
Provides-Extra: mcp
|
34
|
-
Requires-Dist: psycopg2-binary>=2.9.0; extra == "mcp"
|
35
|
-
|
36
|
-
# Memra SDK
|
37
|
-
|
38
|
-
A declarative orchestration framework for AI-powered business workflows. Think of it as "Kubernetes for business logic" where agents are the pods and departments are the deployments.
|
39
|
-
|
40
|
-
## 🚀 Team Setup
|
41
|
-
|
42
|
-
**New team member?** See the complete setup guide: **[TEAM_SETUP.md](TEAM_SETUP.md)**
|
43
|
-
|
44
|
-
This includes:
|
45
|
-
- Database setup (PostgreSQL + Docker)
|
46
|
-
- Local development environment
|
47
|
-
- Testing instructions
|
48
|
-
- Troubleshooting guide
|
49
|
-
|
50
|
-
## Quick Start
|
51
|
-
|
52
|
-
```python
|
53
|
-
from memra.sdk.models import Agent, Department, Tool
|
54
|
-
|
55
|
-
# Define your agents
|
56
|
-
data_extractor = Agent(
|
57
|
-
role="Data Extraction Specialist",
|
58
|
-
job="Extract and validate data",
|
59
|
-
tools=[Tool(name="DataExtractor", hosted_by="memra")],
|
60
|
-
input_keys=["input_data"],
|
61
|
-
output_key="extracted_data"
|
62
|
-
)
|
63
|
-
|
64
|
-
# Create a department
|
65
|
-
dept = Department(
|
66
|
-
name="Data Processing",
|
67
|
-
mission="Process and validate data",
|
68
|
-
agents=[data_extractor]
|
69
|
-
)
|
70
|
-
|
71
|
-
# Run the workflow
|
72
|
-
result = dept.run({"input_data": {...}})
|
73
|
-
```
|
74
|
-
|
75
|
-
## Installation
|
76
|
-
|
77
|
-
```bash
|
78
|
-
pip install memra
|
79
|
-
```
|
80
|
-
|
81
|
-
## API Access
|
82
|
-
|
83
|
-
Memra requires an API key to execute workflows on the hosted infrastructure.
|
84
|
-
|
85
|
-
### Get Your API Key
|
86
|
-
Contact [info@memra.co](mailto:info@memra.co) for API access.
|
87
|
-
|
88
|
-
### Set Your API Key
|
89
|
-
```bash
|
90
|
-
# Set environment variable
|
91
|
-
export MEMRA_API_KEY="your-api-key-here"
|
92
|
-
|
93
|
-
# Or add to your shell profile for persistence
|
94
|
-
echo 'export MEMRA_API_KEY="your-api-key-here"' >> ~/.zshrc
|
95
|
-
```
|
96
|
-
|
97
|
-
### Test Your Setup
|
98
|
-
```bash
|
99
|
-
python examples/accounts_payable_client.py
|
100
|
-
```
|
101
|
-
|
102
|
-
## Architecture
|
103
|
-
|
104
|
-
The Memra platform consists of three main components:
|
105
|
-
|
106
|
-
- **Memra SDK** (this repository): Client library for building and executing workflows
|
107
|
-
- **Memra Server**: Hosted infrastructure for heavy AI processing tools
|
108
|
-
- **MCP Bridge**: Local execution environment for database operations
|
109
|
-
|
110
|
-
Tools are automatically routed between server and local execution based on their `hosted_by` configuration.
|
111
|
-
|
112
|
-
## Documentation
|
113
|
-
|
114
|
-
Documentation is coming soon. For now, see the examples below and in the `examples/` directory.
|
115
|
-
|
116
|
-
## Example: Propane Delivery Workflow
|
117
|
-
|
118
|
-
See the `examples/propane_delivery.py` file for a complete example of how to use Memra to orchestrate a propane delivery workflow.
|
119
|
-
|
120
|
-
## Contributing
|
121
|
-
|
122
|
-
We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) for details.
|
123
|
-
|
124
|
-
## License
|
125
|
-
|
126
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
127
|
-
|
128
|
-
## Repository Structure
|
129
|
-
|
130
|
-
```
|
131
|
-
├── examples/ # Example workflows and use cases
|
132
|
-
│ ├── accounts_payable_client.py # API-based accounts payable workflow
|
133
|
-
│ ├── accounts_payable_mcp.py # MCP-enabled accounts payable workflow
|
134
|
-
│ ├── invoice_processing.py # Simple invoice processing example
|
135
|
-
│ └── propane_delivery.py # Propane delivery domain example
|
136
|
-
├── memra/ # Core SDK package
|
137
|
-
│ ├── __init__.py # Package initialization
|
138
|
-
│ ├── tool_registry.py # Tool discovery and routing
|
139
|
-
│ └── sdk/ # SDK components
|
140
|
-
│ ├── __init__.py
|
141
|
-
│ ├── client.py # API client
|
142
|
-
│ ├── execution_engine.py # Workflow execution
|
143
|
-
│ └── models.py # Core data models
|
144
|
-
├── docs/ # Documentation
|
145
|
-
├── tests/ # Test suite
|
146
|
-
├── local/dependencies/ # Local development setup
|
147
|
-
└── scripts/ # Utility scripts
|
148
|
-
```
|
memra-0.2.2.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
memra/__init__.py,sha256=5WPh9vku8_ZV4T6WayAqArKAj1RDkbL47SsnA9GWD7A,662
|
2
|
-
memra/discovery.py,sha256=yJIQnrDQu1nyzKykCIuzG_5SW5dIXHCEBLLKRWacIoY,480
|
3
|
-
memra/discovery_client.py,sha256=AbnKn6qhyrf7vmOvknEeDzH4tiGHsqPHtDaein_qaW0,1271
|
4
|
-
memra/execution.py,sha256=bg822ED6yYN7APjPac1LRhv48gtxV4DUPvzpyLyBa2I,21443
|
5
|
-
memra/models.py,sha256=sXMPRnMB_mUVtJdBFyd0ElCf_uh1yqx7iLssIYNm0vI,3333
|
6
|
-
memra/tool_registry.py,sha256=N7kpYQxgJcSMDDCX-_6og1-of3QKEaoz6H16ptCCg48,7784
|
7
|
-
memra/tool_registry_client.py,sha256=uzMQ4COvRams9vuPLcqcdljUpDlAYU_tyFxrRhrA0Lc,4009
|
8
|
-
memra-0.2.2.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
memra-0.2.2.dist-info/METADATA,sha256=jrZ9AwcGtPK-pc9TGGGoi1oJzXxcGx7LUsbsd6NxcxA,4856
|
10
|
-
memra-0.2.2.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
11
|
-
memra-0.2.2.dist-info/entry_points.txt,sha256=LBVjwWoxWJRzNLgeByPn6xUvWFIRnqnemvAZgIoSt08,41
|
12
|
-
memra-0.2.2.dist-info/top_level.txt,sha256=pXWcTRS1zctdiSUivW4iyKpJ4tcfIu-1BW_fpbal3OY,6
|
13
|
-
memra-0.2.2.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
memra
|
File without changes
|
File without changes
|