memra 0.0.1__py3-none-any.whl → 0.2.0__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 +24 -0
- memra/discovery.py +15 -0
- memra/discovery_client.py +49 -0
- memra/execution.py +434 -0
- memra/models.py +98 -0
- memra/tool_registry.py +190 -0
- memra/tool_registry_client.py +106 -0
- memra-0.2.0.dist-info/METADATA +161 -0
- memra-0.2.0.dist-info/RECORD +19 -0
- {memra-0.0.1.dist-info → memra-0.2.0.dist-info}/WHEEL +1 -1
- memra-0.2.0.dist-info/entry_points.txt +2 -0
- memra-0.2.0.dist-info/top_level.txt +2 -0
- memra-sdk-package/examples/accounts_payable_client.py +207 -0
- memra-sdk-package/memra/__init__.py +28 -0
- memra-sdk-package/memra/discovery_client.py +49 -0
- memra-sdk-package/memra/execution.py +418 -0
- memra-sdk-package/memra/models.py +98 -0
- memra-sdk-package/memra/tool_registry_client.py +105 -0
- memra-0.0.1.dist-info/METADATA +0 -24
- memra-0.0.1.dist-info/RECORD +0 -5
- memra-0.0.1.dist-info/top_level.txt +0 -1
- {memra-0.0.1.dist-info → memra-0.2.0.dist-info}/licenses/LICENSE +0 -0
memra/tool_registry.py
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
import importlib
|
2
|
+
import logging
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
from typing import Dict, Any, List, Optional, Callable
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
class ToolRegistry:
|
11
|
+
"""Registry for managing and executing tools"""
|
12
|
+
|
13
|
+
def __init__(self):
|
14
|
+
self.tools: Dict[str, Dict[str, Any]] = {}
|
15
|
+
self._add_project_to_path()
|
16
|
+
self._load_builtin_tools()
|
17
|
+
|
18
|
+
def _add_project_to_path(self):
|
19
|
+
"""Add the project root to Python path so we can import logic modules"""
|
20
|
+
# Get the directory containing this file (memra package)
|
21
|
+
current_dir = Path(__file__).parent
|
22
|
+
# Go up one level to get the project root
|
23
|
+
project_root = current_dir.parent
|
24
|
+
|
25
|
+
if str(project_root) not in sys.path:
|
26
|
+
sys.path.insert(0, str(project_root))
|
27
|
+
|
28
|
+
def _load_builtin_tools(self):
|
29
|
+
"""Load tools from the logic directory"""
|
30
|
+
try:
|
31
|
+
# Load invoice tools
|
32
|
+
from logic.invoice_tools import (
|
33
|
+
DatabaseQueryTool, PDFProcessor, OCRTool,
|
34
|
+
InvoiceExtractionWorkflow, DataValidator, PostgresInsert
|
35
|
+
)
|
36
|
+
|
37
|
+
self.register_tool("DatabaseQueryTool", DatabaseQueryTool, "memra",
|
38
|
+
"Query database schemas and data")
|
39
|
+
self.register_tool("PDFProcessor", PDFProcessor, "memra",
|
40
|
+
"Process PDF files and extract content")
|
41
|
+
self.register_tool("OCRTool", OCRTool, "memra",
|
42
|
+
"Perform OCR on images and documents")
|
43
|
+
self.register_tool("InvoiceExtractionWorkflow", InvoiceExtractionWorkflow, "memra",
|
44
|
+
"Extract structured data from invoices")
|
45
|
+
self.register_tool("DataValidator", DataValidator, "memra",
|
46
|
+
"Validate data against schemas")
|
47
|
+
self.register_tool("PostgresInsert", PostgresInsert, "memra",
|
48
|
+
"Insert data into PostgreSQL database")
|
49
|
+
|
50
|
+
# Load file tools
|
51
|
+
from logic.file_tools import FileReader
|
52
|
+
self.register_tool("FileReader", FileReader, "memra",
|
53
|
+
"Read files from the filesystem")
|
54
|
+
|
55
|
+
logger.info(f"Loaded {len(self.tools)} builtin tools")
|
56
|
+
|
57
|
+
except ImportError as e:
|
58
|
+
logger.warning(f"Could not load some tools: {e}")
|
59
|
+
|
60
|
+
def register_tool(self, name: str, tool_class: type, hosted_by: str, description: str):
|
61
|
+
"""Register a tool in the registry"""
|
62
|
+
self.tools[name] = {
|
63
|
+
"class": tool_class,
|
64
|
+
"hosted_by": hosted_by,
|
65
|
+
"description": description
|
66
|
+
}
|
67
|
+
logger.debug(f"Registered tool: {name} (hosted by {hosted_by})")
|
68
|
+
|
69
|
+
def discover_tools(self, hosted_by: Optional[str] = None) -> List[Dict[str, Any]]:
|
70
|
+
"""Discover available tools, optionally filtered by host"""
|
71
|
+
tools = []
|
72
|
+
for name, info in self.tools.items():
|
73
|
+
if hosted_by is None or info["hosted_by"] == hosted_by:
|
74
|
+
tools.append({
|
75
|
+
"name": name,
|
76
|
+
"hosted_by": info["hosted_by"],
|
77
|
+
"description": info["description"]
|
78
|
+
})
|
79
|
+
return tools
|
80
|
+
|
81
|
+
def execute_tool(self, tool_name: str, hosted_by: str, input_data: Dict[str, Any],
|
82
|
+
config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
83
|
+
"""Execute a tool with the given input data"""
|
84
|
+
if tool_name not in self.tools:
|
85
|
+
return {
|
86
|
+
"success": False,
|
87
|
+
"error": f"Tool '{tool_name}' not found in registry"
|
88
|
+
}
|
89
|
+
|
90
|
+
tool_info = self.tools[tool_name]
|
91
|
+
if tool_info["hosted_by"] != hosted_by:
|
92
|
+
return {
|
93
|
+
"success": False,
|
94
|
+
"error": f"Tool '{tool_name}' is hosted by '{tool_info['hosted_by']}', not '{hosted_by}'"
|
95
|
+
}
|
96
|
+
|
97
|
+
try:
|
98
|
+
# Instantiate tool
|
99
|
+
tool_class = tool_info["class"]
|
100
|
+
|
101
|
+
# Some tools need credentials/config for initialization
|
102
|
+
if tool_name in ["DatabaseQueryTool", "PostgresInsert"]:
|
103
|
+
if "connection" in input_data:
|
104
|
+
# Parse connection string or use credentials
|
105
|
+
credentials = self._parse_connection(input_data["connection"])
|
106
|
+
tool_instance = tool_class(credentials)
|
107
|
+
else:
|
108
|
+
return {
|
109
|
+
"success": False,
|
110
|
+
"error": f"Tool '{tool_name}' requires database credentials"
|
111
|
+
}
|
112
|
+
elif tool_name == "InvoiceExtractionWorkflow":
|
113
|
+
# This tool needs to be instantiated to initialize the LLM client
|
114
|
+
tool_instance = tool_class()
|
115
|
+
else:
|
116
|
+
tool_instance = tool_class()
|
117
|
+
|
118
|
+
# Execute tool based on its type
|
119
|
+
result = self._execute_tool_method(tool_instance, tool_name, input_data, config)
|
120
|
+
|
121
|
+
return {
|
122
|
+
"success": True,
|
123
|
+
"data": result
|
124
|
+
}
|
125
|
+
|
126
|
+
except Exception as e:
|
127
|
+
logger.error(f"Tool execution failed for {tool_name}: {str(e)}")
|
128
|
+
return {
|
129
|
+
"success": False,
|
130
|
+
"error": str(e)
|
131
|
+
}
|
132
|
+
|
133
|
+
def _execute_tool_method(self, tool_instance: Any, tool_name: str,
|
134
|
+
input_data: Dict[str, Any], config: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
135
|
+
"""Execute the appropriate method on the tool instance"""
|
136
|
+
|
137
|
+
if tool_name == "DatabaseQueryTool":
|
138
|
+
return tool_instance.get_schema("invoices") # Default to invoices table
|
139
|
+
|
140
|
+
elif tool_name == "PDFProcessor":
|
141
|
+
file_path = input_data.get("file", "")
|
142
|
+
return tool_instance.process_pdf(file_path)
|
143
|
+
|
144
|
+
elif tool_name == "OCRTool":
|
145
|
+
# Assume PDF processor output is passed as input
|
146
|
+
return {"extracted_text": tool_instance.extract_text(input_data)}
|
147
|
+
|
148
|
+
elif tool_name == "InvoiceExtractionWorkflow":
|
149
|
+
text = input_data.get("extracted_text", "")
|
150
|
+
schema = input_data.get("invoice_schema", {})
|
151
|
+
return tool_instance.extract_data(text, schema)
|
152
|
+
|
153
|
+
elif tool_name == "DataValidator":
|
154
|
+
data = input_data.get("invoice_data", {})
|
155
|
+
schema = input_data.get("invoice_schema", {})
|
156
|
+
return tool_instance.validate(data, schema)
|
157
|
+
|
158
|
+
elif tool_name == "PostgresInsert":
|
159
|
+
data = input_data.get("invoice_data", {})
|
160
|
+
return tool_instance.insert_record("invoices", data)
|
161
|
+
|
162
|
+
elif tool_name == "FileReader":
|
163
|
+
file_path = config.get("path") if config else input_data.get("file_path")
|
164
|
+
if not file_path:
|
165
|
+
raise ValueError("FileReader requires a file path")
|
166
|
+
return tool_instance.read_file(file_path)
|
167
|
+
|
168
|
+
else:
|
169
|
+
raise ValueError(f"Unknown tool execution method for {tool_name}")
|
170
|
+
|
171
|
+
def _parse_connection(self, connection_string: str) -> Dict[str, Any]:
|
172
|
+
"""Parse a connection string into credentials"""
|
173
|
+
# Simple parser for postgres://user:pass@host:port/database
|
174
|
+
if connection_string.startswith("postgres://"):
|
175
|
+
# This is a simplified parser - in production you'd use a proper URL parser
|
176
|
+
parts = connection_string.replace("postgres://", "").split("/")
|
177
|
+
db_part = parts[1] if len(parts) > 1 else "finance"
|
178
|
+
auth_host = parts[0].split("@")
|
179
|
+
host_port = auth_host[1].split(":") if len(auth_host) > 1 else ["localhost", "5432"]
|
180
|
+
user_pass = auth_host[0].split(":") if len(auth_host) > 1 else ["user", "pass"]
|
181
|
+
|
182
|
+
return {
|
183
|
+
"host": host_port[0],
|
184
|
+
"port": int(host_port[1]) if len(host_port) > 1 else 5432,
|
185
|
+
"database": db_part,
|
186
|
+
"user": user_pass[0],
|
187
|
+
"password": user_pass[1] if len(user_pass) > 1 else ""
|
188
|
+
}
|
189
|
+
|
190
|
+
return {"connection_string": connection_string}
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import httpx
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
from typing import Dict, Any, List, Optional
|
5
|
+
import asyncio
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
class ToolRegistryClient:
|
10
|
+
"""Client-side registry that calls Memra API for tool execution"""
|
11
|
+
|
12
|
+
def __init__(self):
|
13
|
+
self.api_base = os.getenv("MEMRA_API_URL", "https://api.memra.co")
|
14
|
+
self.api_key = os.getenv("MEMRA_API_KEY")
|
15
|
+
self.tools_cache = None
|
16
|
+
|
17
|
+
if not self.api_key:
|
18
|
+
raise ValueError(
|
19
|
+
"MEMRA_API_KEY environment variable is required. "
|
20
|
+
"Please contact info@memra.co for an API key."
|
21
|
+
)
|
22
|
+
|
23
|
+
def discover_tools(self, hosted_by: Optional[str] = None) -> List[Dict[str, Any]]:
|
24
|
+
"""Discover available tools from the API"""
|
25
|
+
try:
|
26
|
+
# Use sync httpx for compatibility with existing sync code
|
27
|
+
with httpx.Client(timeout=30.0) as client:
|
28
|
+
response = client.get(
|
29
|
+
f"{self.api_base}/tools/discover",
|
30
|
+
headers={"X-API-Key": self.api_key}
|
31
|
+
)
|
32
|
+
response.raise_for_status()
|
33
|
+
|
34
|
+
data = response.json()
|
35
|
+
tools = data.get("tools", [])
|
36
|
+
|
37
|
+
# Filter by hosted_by if specified
|
38
|
+
if hosted_by:
|
39
|
+
tools = [t for t in tools if t.get("hosted_by") == hosted_by]
|
40
|
+
|
41
|
+
self.tools_cache = tools
|
42
|
+
logger.info(f"Discovered {len(tools)} tools from API")
|
43
|
+
return tools
|
44
|
+
|
45
|
+
except Exception as e:
|
46
|
+
logger.error(f"Failed to discover tools from API: {e}")
|
47
|
+
# Return empty list if API is unavailable
|
48
|
+
return []
|
49
|
+
|
50
|
+
def execute_tool(self, tool_name: str, hosted_by: str, input_data: Dict[str, Any],
|
51
|
+
config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
52
|
+
"""Execute a tool via the API"""
|
53
|
+
try:
|
54
|
+
logger.info(f"Executing tool {tool_name} via API")
|
55
|
+
|
56
|
+
# Prepare request payload
|
57
|
+
payload = {
|
58
|
+
"tool_name": tool_name,
|
59
|
+
"hosted_by": hosted_by,
|
60
|
+
"input_data": input_data,
|
61
|
+
"config": config
|
62
|
+
}
|
63
|
+
|
64
|
+
# Make API call
|
65
|
+
with httpx.Client(timeout=120.0) as client: # Longer timeout for tool execution
|
66
|
+
response = client.post(
|
67
|
+
f"{self.api_base}/tools/execute",
|
68
|
+
headers={
|
69
|
+
"X-API-Key": self.api_key,
|
70
|
+
"Content-Type": "application/json"
|
71
|
+
},
|
72
|
+
json=payload
|
73
|
+
)
|
74
|
+
response.raise_for_status()
|
75
|
+
|
76
|
+
result = response.json()
|
77
|
+
logger.info(f"Tool {tool_name} executed successfully via API")
|
78
|
+
return result
|
79
|
+
|
80
|
+
except httpx.TimeoutException:
|
81
|
+
logger.error(f"Tool {tool_name} execution timed out")
|
82
|
+
return {
|
83
|
+
"success": False,
|
84
|
+
"error": f"Tool execution timed out after 120 seconds"
|
85
|
+
}
|
86
|
+
except httpx.HTTPStatusError as e:
|
87
|
+
logger.error(f"API error for tool {tool_name}: {e.response.status_code}")
|
88
|
+
return {
|
89
|
+
"success": False,
|
90
|
+
"error": f"API error: {e.response.status_code} - {e.response.text}"
|
91
|
+
}
|
92
|
+
except Exception as e:
|
93
|
+
logger.error(f"Tool execution failed for {tool_name}: {str(e)}")
|
94
|
+
return {
|
95
|
+
"success": False,
|
96
|
+
"error": str(e)
|
97
|
+
}
|
98
|
+
|
99
|
+
def health_check(self) -> bool:
|
100
|
+
"""Check if the API is available"""
|
101
|
+
try:
|
102
|
+
with httpx.Client(timeout=10.0) as client:
|
103
|
+
response = client.get(f"{self.api_base}/health")
|
104
|
+
return response.status_code == 200
|
105
|
+
except:
|
106
|
+
return False
|
@@ -0,0 +1,161 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: memra
|
3
|
+
Version: 0.2.0
|
4
|
+
Summary: Declarative framework for enterprise workflows with MCP integration
|
5
|
+
Home-page: https://github.com/memra/memra-sdk
|
6
|
+
Author: Memra
|
7
|
+
Author-email: Memra <info@memra.co>
|
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
|
+
Requires-Dist: psycopg2-binary>=2.9.0
|
29
|
+
Provides-Extra: dev
|
30
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
31
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
32
|
+
Requires-Dist: black; extra == "dev"
|
33
|
+
Requires-Dist: flake8; extra == "dev"
|
34
|
+
Dynamic: author
|
35
|
+
Dynamic: home-page
|
36
|
+
Dynamic: license-file
|
37
|
+
Dynamic: requires-python
|
38
|
+
|
39
|
+
# Memra SDK
|
40
|
+
|
41
|
+
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.
|
42
|
+
|
43
|
+
## 🚀 Team Setup
|
44
|
+
|
45
|
+
**New team member?** See the complete setup guide: **[TEAM_SETUP.md](TEAM_SETUP.md)**
|
46
|
+
|
47
|
+
This includes:
|
48
|
+
- Database setup (PostgreSQL + Docker)
|
49
|
+
- Local development environment
|
50
|
+
- Testing instructions
|
51
|
+
- Troubleshooting guide
|
52
|
+
|
53
|
+
## Quick Start
|
54
|
+
|
55
|
+
```python
|
56
|
+
from memra.sdk.models import Agent, Department, Tool
|
57
|
+
|
58
|
+
# Define your agents
|
59
|
+
data_extractor = Agent(
|
60
|
+
role="Data Extraction Specialist",
|
61
|
+
job="Extract and validate data",
|
62
|
+
tools=[Tool(name="DataExtractor", hosted_by="memra")],
|
63
|
+
input_keys=["input_data"],
|
64
|
+
output_key="extracted_data"
|
65
|
+
)
|
66
|
+
|
67
|
+
# Create a department
|
68
|
+
dept = Department(
|
69
|
+
name="Data Processing",
|
70
|
+
mission="Process and validate data",
|
71
|
+
agents=[data_extractor]
|
72
|
+
)
|
73
|
+
|
74
|
+
# Run the workflow
|
75
|
+
result = dept.run({"input_data": {...}})
|
76
|
+
```
|
77
|
+
|
78
|
+
## Installation
|
79
|
+
|
80
|
+
```bash
|
81
|
+
pip install memra
|
82
|
+
```
|
83
|
+
|
84
|
+
## API Access
|
85
|
+
|
86
|
+
Memra requires an API key to execute workflows on the hosted infrastructure.
|
87
|
+
|
88
|
+
### Get Your API Key
|
89
|
+
Contact [info@memra.co](mailto:info@memra.co) for API access.
|
90
|
+
|
91
|
+
### Set Your API Key
|
92
|
+
```bash
|
93
|
+
# Set environment variable
|
94
|
+
export MEMRA_API_KEY="your-api-key-here"
|
95
|
+
|
96
|
+
# Or add to your shell profile for persistence
|
97
|
+
echo 'export MEMRA_API_KEY="your-api-key-here"' >> ~/.zshrc
|
98
|
+
```
|
99
|
+
|
100
|
+
### Test Your Setup
|
101
|
+
```bash
|
102
|
+
python examples/accounts_payable_client.py
|
103
|
+
```
|
104
|
+
|
105
|
+
## Documentation
|
106
|
+
|
107
|
+
Documentation is coming soon. For now, see the examples below and in the `examples/` directory.
|
108
|
+
|
109
|
+
## Example: Propane Delivery Workflow
|
110
|
+
|
111
|
+
See the `examples/propane_delivery.py` file for a complete example of how to use Memra to orchestrate a propane delivery workflow.
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) for details.
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
120
|
+
|
121
|
+
## Examples
|
122
|
+
|
123
|
+
```
|
124
|
+
├── examples/
|
125
|
+
│ ├── accounts_payable_client.py # API-based example
|
126
|
+
│ ├── accounts_payable.py # Local example
|
127
|
+
│ ├── invoice_processing.py # Simple workflow
|
128
|
+
│ └── propane_delivery.py # Domain example
|
129
|
+
├── memra/ # Core SDK
|
130
|
+
├── logic/ # Tool implementations
|
131
|
+
├── local/dependencies/ # Database setup & schemas
|
132
|
+
└── docker-compose.yml # Database setup
|
133
|
+
```
|
134
|
+
|
135
|
+
## ✨ New: MCP Integration
|
136
|
+
|
137
|
+
Memra now supports **Model Context Protocol (MCP)** integration, allowing you to execute operations on your local infrastructure while leveraging Memra's cloud-based AI processing.
|
138
|
+
|
139
|
+
**Key Benefits:**
|
140
|
+
- 🔒 **Keep sensitive data local** - Your databases stay on your infrastructure
|
141
|
+
- ⚡ **Hybrid processing** - AI processing in the cloud, data operations locally
|
142
|
+
- 🔐 **Secure communication** - HMAC-authenticated requests between cloud and local
|
143
|
+
- 🛠️ **Easy setup** - Simple bridge server connects your local resources
|
144
|
+
|
145
|
+
**Quick Example:**
|
146
|
+
```python
|
147
|
+
# Agent that uses local database via MCP
|
148
|
+
agent = Agent(
|
149
|
+
role="Data Writer",
|
150
|
+
tools=[{
|
151
|
+
"name": "PostgresInsert",
|
152
|
+
"hosted_by": "mcp", # Routes to your local infrastructure
|
153
|
+
"config": {
|
154
|
+
"bridge_url": "http://localhost:8081",
|
155
|
+
"bridge_secret": "your-secret"
|
156
|
+
}
|
157
|
+
}]
|
158
|
+
)
|
159
|
+
```
|
160
|
+
|
161
|
+
📖 **[Complete MCP Integration Guide →](docs/mcp_integration.md)**
|
@@ -0,0 +1,19 @@
|
|
1
|
+
memra/__init__.py,sha256=XLSWpo42Ffp_pi5mvk4xvdYBZ8eNLAJF4_3Oi102i90,560
|
2
|
+
memra/discovery.py,sha256=yJIQnrDQu1nyzKykCIuzG_5SW5dIXHCEBLLKRWacIoY,480
|
3
|
+
memra/discovery_client.py,sha256=AbnKn6qhyrf7vmOvknEeDzH4tiGHsqPHtDaein_qaW0,1271
|
4
|
+
memra/execution.py,sha256=5NIyFVtQEeatYQ-fxexT0eWMtCh28k1hRC2Y6cfQaac,20917
|
5
|
+
memra/models.py,sha256=nTaYLAp0tRzQ0CQaBLNBURfhBQ5_gyty0ams4mghyIc,3289
|
6
|
+
memra/tool_registry.py,sha256=zdyKRShcmKtG7BVfmAHflW9FDl7rooPPAgbdVV4gJ8o,8268
|
7
|
+
memra/tool_registry_client.py,sha256=uzMQ4COvRams9vuPLcqcdljUpDlAYU_tyFxrRhrA0Lc,4009
|
8
|
+
memra-0.2.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
memra-sdk-package/examples/accounts_payable_client.py,sha256=Vu_h5C-qc6_80uz5dXJH4G3zfIbgUNAhQ2y8mWauao0,7401
|
10
|
+
memra-sdk-package/memra/__init__.py,sha256=QRk72YETLgL15GVt26tN_rBraCQkhZO7UB9T6d4u_uU,543
|
11
|
+
memra-sdk-package/memra/discovery_client.py,sha256=AbnKn6qhyrf7vmOvknEeDzH4tiGHsqPHtDaein_qaW0,1271
|
12
|
+
memra-sdk-package/memra/execution.py,sha256=UJ_MJ4getuSk4HJW1sCi7lc26avX-G6-GxnvE-DiSwk,20191
|
13
|
+
memra-sdk-package/memra/models.py,sha256=nTaYLAp0tRzQ0CQaBLNBURfhBQ5_gyty0ams4mghyIc,3289
|
14
|
+
memra-sdk-package/memra/tool_registry_client.py,sha256=KyNNxj84248E-8MoWNj6pJmlllUG8s0lmeXXmbu0U7o,3996
|
15
|
+
memra-0.2.0.dist-info/METADATA,sha256=eOuvH39VFUh-QxTdE5RwT6isgRIJkptEC2lsqlF2AA4,4816
|
16
|
+
memra-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
17
|
+
memra-0.2.0.dist-info/entry_points.txt,sha256=LBVjwWoxWJRzNLgeByPn6xUvWFIRnqnemvAZgIoSt08,41
|
18
|
+
memra-0.2.0.dist-info/top_level.txt,sha256=5dqePB77aj_pPFavlwxtBvdkUM-kP-WiQD3LRbQswwc,24
|
19
|
+
memra-0.2.0.dist-info/RECORD,,
|