lam-cli 0.1.5__tar.gz → 1.0.0__tar.gz
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.
- lam_cli-1.0.0/CHANGELOG.md +17 -0
- lam_cli-1.0.0/DOCS.md +174 -0
- lam_cli-1.0.0/MANIFEST.in +5 -0
- lam_cli-1.0.0/PKG-INFO +229 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/README.md +3 -0
- lam_cli-1.0.0/lam/core.py +95 -0
- lam_cli-1.0.0/lam/engines/__init__.py +23 -0
- lam_cli-1.0.0/lam/engines/base.py +56 -0
- lam_cli-1.0.0/lam/engines/javascript.py +283 -0
- lam_cli-1.0.0/lam/engines/jq.py +65 -0
- lam_cli-1.0.0/lam/engines/python.py +839 -0
- lam_cli-1.0.0/lam/lam.py +135 -0
- lam_cli-1.0.0/lam/utils.py +22 -0
- lam_cli-1.0.0/lam_cli.egg-info/PKG-INFO +229 -0
- lam_cli-1.0.0/lam_cli.egg-info/SOURCES.txt +22 -0
- lam_cli-1.0.0/lam_cli.egg-info/dependency_links.txt +1 -0
- lam_cli-1.0.0/requirements.txt +16 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/setup.py +7 -6
- lam_cli-0.1.5/PKG-INFO +0 -53
- lam_cli-0.1.5/lam/__init__.py +0 -0
- lam_cli-0.1.5/lam/lam.py +0 -945
- lam_cli-0.1.5/lam_cli.egg-info/PKG-INFO +0 -53
- lam_cli-0.1.5/lam_cli.egg-info/SOURCES.txt +0 -11
- {lam_cli-0.1.5 → lam_cli-1.0.0}/LICENSE +0 -0
- /lam_cli-0.1.5/lam_cli.egg-info/dependency_links.txt → /lam_cli-1.0.0/lam/__init__.py +0 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/lam_cli.egg-info/entry_points.txt +0 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/lam_cli.egg-info/requires.txt +0 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/lam_cli.egg-info/top_level.txt +0 -0
- {lam_cli-0.1.5 → lam_cli-1.0.0}/setup.cfg +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0] - 2024-01-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Support for handling multiple JSON objects from JQ output
|
|
7
|
+
- Comprehensive logging throughout JQ execution
|
|
8
|
+
- New test suite for JQ engine
|
|
9
|
+
- Better error handling and reporting
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- JQ output processing now properly handles arrays of objects
|
|
13
|
+
- Improved logging verbosity and clarity
|
|
14
|
+
- Updated documentation structure
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Issue with JQ output not being properly parsed when multiple objects are returned
|
lam_cli-1.0.0/DOCS.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# LAM
|
|
2
|
+
LAM is a data transformation tool designed for Laminar's API integration platform.
|
|
3
|
+
|
|
4
|
+
## Overview
|
|
5
|
+
LAM enables you to write efficient transformations for your API data using either JavaScript (Bun) or Python. It's designed to be secure, fast, and easy to integrate into your Laminar workflows.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
- **Dual Engine Support**: Choose between JavaScript (Bun runtime) for fast execution or Python for complex data processing
|
|
9
|
+
- **Built-in Libraries**: Access lodash and date-fns in JavaScript, comprehensive Python standard library modules
|
|
10
|
+
- **Security**: Runs in sandboxed environments with strict resource limits and security restrictions
|
|
11
|
+
- **Performance**: Uses Bun runtime for JavaScript and sandboxed Python interpreter
|
|
12
|
+
- **Monitoring**: Built-in execution statistics and error tracking
|
|
13
|
+
|
|
14
|
+
## Execution Environments
|
|
15
|
+
|
|
16
|
+
### Bun JavaScript Runtime (js)
|
|
17
|
+
**Configuration**:
|
|
18
|
+
- **Engine**: Bun
|
|
19
|
+
- **Timeout**: 5 seconds
|
|
20
|
+
- **Execution**: Isolated with `--no-fetch --smol --silent` flags
|
|
21
|
+
- **Storage**: No localStorage/sessionStorage support
|
|
22
|
+
- **Modules**: Shared node_modules directory
|
|
23
|
+
|
|
24
|
+
**Available Libraries**:
|
|
25
|
+
- **lodash** (^4.17.21): Utility library for array/object manipulation, data transformations (Global: `_`)
|
|
26
|
+
- **date-fns** (^2.30.0): Modern date utility library with `format`, `parseISO` functions
|
|
27
|
+
|
|
28
|
+
**Transform Function Signature**:
|
|
29
|
+
```js
|
|
30
|
+
(input) => { /* transform logic */ return result; }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Python Interpreter with Sandboxing (py)
|
|
34
|
+
**Configuration**:
|
|
35
|
+
- **Engine**: Python interpreter
|
|
36
|
+
- **Timeout**: 5 seconds
|
|
37
|
+
- **Memory Limit**: 100MB
|
|
38
|
+
- **CPU Limit**: 5 seconds (RLIMIT_CPU)
|
|
39
|
+
- **Virtual Memory**: 100MB (RLIMIT_AS)
|
|
40
|
+
- **Execution**: Isolated with `-I` flag (ignores environment/site packages)
|
|
41
|
+
|
|
42
|
+
**Security Restrictions**:
|
|
43
|
+
- **Blocked Modules**: subprocess, sys, os, shutil, pathlib, importlib, builtins, _thread, ctypes, socket, pickle, multiprocessing
|
|
44
|
+
- **Blocked Functions**: __import__, eval, exec, globals, locals, getattr, setattr, delattr, compile, open
|
|
45
|
+
- **Blocked Patterns**: __subclasses__, dunder attributes access
|
|
46
|
+
|
|
47
|
+
**Available Standard Library Modules**:
|
|
48
|
+
- **json**: JSON encoder and decoder
|
|
49
|
+
- **datetime**: Date and time handling
|
|
50
|
+
- **time**: Time-related functions
|
|
51
|
+
- **math**: Mathematical functions and constants
|
|
52
|
+
- **statistics**: Statistical functions (mean, median, mode, standard deviation)
|
|
53
|
+
- **collections**: Counter, defaultdict, OrderedDict, deque
|
|
54
|
+
- **itertools**: Efficient looping, combinations, permutations
|
|
55
|
+
- **functools**: reduce, partial, lru_cache
|
|
56
|
+
- **re**: Regular expression operations
|
|
57
|
+
- **copy**: Shallow and deep copy operations
|
|
58
|
+
- **decimal**: Precise decimal calculations
|
|
59
|
+
- **csv**: CSV file reading and writing
|
|
60
|
+
- **io**: StringIO, BytesIO for in-memory files
|
|
61
|
+
- **dataclasses**: Data classes for storing data
|
|
62
|
+
- **typing**: Type hints support
|
|
63
|
+
- **enum**: Support for enumerations
|
|
64
|
+
- **random**: Random number generation
|
|
65
|
+
- **uuid**: UUID generation
|
|
66
|
+
- **hashlib**: Secure hash and message digest algorithms
|
|
67
|
+
- **base64**: Base64 encoding and decoding
|
|
68
|
+
- **urllib**: URL handling modules
|
|
69
|
+
- **urllib.parse**: URL parsing utilities
|
|
70
|
+
- **html**: HTML processing utilities
|
|
71
|
+
- **xml**: XML processing
|
|
72
|
+
- **xml.etree**: XML ElementTree API
|
|
73
|
+
- **xml.etree.ElementTree**: XML parsing and creation
|
|
74
|
+
- **string**: String constants and classes
|
|
75
|
+
- **textwrap**: Text wrapping and filling
|
|
76
|
+
- **operator**: Standard operators as functions
|
|
77
|
+
- **bisect**: Array bisection algorithm
|
|
78
|
+
- **heapq**: Heap queue algorithm
|
|
79
|
+
- **array**: Efficient arrays of numeric values
|
|
80
|
+
- **unicodedata**: Unicode character database
|
|
81
|
+
- **locale**: Internationalization services
|
|
82
|
+
- **calendar**: Calendar-related functions
|
|
83
|
+
- **zoneinfo**: Time zone support (Python 3.9+)
|
|
84
|
+
- **struct**: Pack and unpack binary data
|
|
85
|
+
- **binascii**: Binary/ASCII conversions
|
|
86
|
+
- **codecs**: Encode and decode data
|
|
87
|
+
- **difflib**: Sequence comparison utilities
|
|
88
|
+
- **pprint**: Pretty-printer for data structures
|
|
89
|
+
- **reprlib**: Alternate repr() implementation
|
|
90
|
+
- **abc**: Abstract base classes
|
|
91
|
+
- **contextlib**: Context management utilities
|
|
92
|
+
- **secrets**: Cryptographically secure random numbers
|
|
93
|
+
- **fractions**: Rational numbers
|
|
94
|
+
- **numbers**: Numeric abstract base classes
|
|
95
|
+
|
|
96
|
+
**Safe Built-in Functions**:
|
|
97
|
+
`abs`, `all`, `any`, `bool`, `chr`, `dict`, `divmod`, `enumerate`, `filter`, `float`, `format`, `frozenset`, `hash`, `hex`, `int`, `isinstance`, `issubclass`, `iter`, `len`, `list`, `map`, `max`, `min`, `next`, `oct`, `ord`, `pow`, `print`, `range`, `repr`, `reversed`, `round`, `set`, `slice`, `sorted`, `str`, `sum`, `tuple`, `type`, `zip`
|
|
98
|
+
|
|
99
|
+
**Transform Function Signature**:
|
|
100
|
+
```py
|
|
101
|
+
def transform(input_data):
|
|
102
|
+
# transform logic
|
|
103
|
+
return result
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
### JavaScript (Bun) Transformations
|
|
109
|
+
Perfect for fast data manipulation with familiar syntax:
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
(input) => {
|
|
113
|
+
// Use lodash for data manipulation
|
|
114
|
+
const processed = _.map(input.data, item => ({
|
|
115
|
+
id: item.id,
|
|
116
|
+
formattedDate: format(parseISO(item.date), 'MMM dd, yyyy'),
|
|
117
|
+
value: item.value * 2
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
processed,
|
|
122
|
+
summary: {
|
|
123
|
+
total: _.sumBy(processed, 'value'),
|
|
124
|
+
count: processed.length
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Python Transformations
|
|
131
|
+
Ideal for complex data processing and statistical analysis:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
def transform(input_data):
|
|
135
|
+
import statistics
|
|
136
|
+
from collections import Counter
|
|
137
|
+
|
|
138
|
+
# Process numerical data
|
|
139
|
+
values = [item["value"] for item in input_data["data"] if "value" in item]
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
"statistics": {
|
|
143
|
+
"mean": statistics.mean(values) if values else 0,
|
|
144
|
+
"median": statistics.median(values) if values else 0,
|
|
145
|
+
"count": len(values)
|
|
146
|
+
},
|
|
147
|
+
"frequency": dict(Counter(item["category"] for item in input_data["data"])),
|
|
148
|
+
"processedAt": datetime.now().isoformat()
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Integration with Laminar
|
|
153
|
+
LAM is designed to work seamlessly with Laminar's integration platform:
|
|
154
|
+
|
|
155
|
+
1. **Flows**: Add data transformations to your API flows
|
|
156
|
+
2. **Automation**: Schedule and automate data processing
|
|
157
|
+
3. **Monitoring**: Track execution statistics and errors
|
|
158
|
+
|
|
159
|
+
## Getting Started
|
|
160
|
+
|
|
161
|
+
### Using LAM in Laminar
|
|
162
|
+
1. Create a new flow in [Laminar](https://app.laminar.run)
|
|
163
|
+
2. Add a transformation step
|
|
164
|
+
3. Choose your engine (JavaScript or Python)
|
|
165
|
+
4. Write your transformation function
|
|
166
|
+
5. Deploy and monitor
|
|
167
|
+
|
|
168
|
+
## Resources
|
|
169
|
+
- [Laminar Documentation](https://docs.laminar.run)
|
|
170
|
+
- [Sign up for Laminar](https://app.laminar.run)
|
|
171
|
+
|
|
172
|
+
## Support
|
|
173
|
+
Get help with LAM:
|
|
174
|
+
- [Contact Support](mailto:connect@laminar.run)
|
lam_cli-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lam-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Secure data transformation tool supporting JQ and JavaScript (Bun)
|
|
5
|
+
Home-page: https://github.com/laminar-run/lam
|
|
6
|
+
Author: Laminar Run, Inc.
|
|
7
|
+
Author-email: connect@laminar.run
|
|
8
|
+
License: GPLv3
|
|
9
|
+
Project-URL: Documentation, https://docs.laminar.run
|
|
10
|
+
Project-URL: Source, https://github.com/laminar-run/lam
|
|
11
|
+
Project-URL: Issue Tracker, https://github.com/laminar-run/lam/issues
|
|
12
|
+
Keywords: laminar,api,integration,transformation,json,jq,javascript,bun
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
24
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: backoff>=2.2.1
|
|
29
|
+
Requires-Dist: certifi>=2024.12.14
|
|
30
|
+
Requires-Dist: charset-normalizer>=3.3.2
|
|
31
|
+
Requires-Dist: click>=8.1.7
|
|
32
|
+
Requires-Dist: idna>=3.7
|
|
33
|
+
Requires-Dist: logtail-python>=0.2.2
|
|
34
|
+
Requires-Dist: monotonic>=1.6
|
|
35
|
+
Requires-Dist: msgpack>=1.0.8
|
|
36
|
+
Requires-Dist: posthog>=3.4.0
|
|
37
|
+
Requires-Dist: psutil>=5.9.0
|
|
38
|
+
Requires-Dist: python-dateutil>=2.8.2
|
|
39
|
+
Requires-Dist: requests>=2.32.3
|
|
40
|
+
Requires-Dist: six>=1.16.0
|
|
41
|
+
Requires-Dist: urllib3>=2.2.2
|
|
42
|
+
Dynamic: author
|
|
43
|
+
Dynamic: author-email
|
|
44
|
+
Dynamic: classifier
|
|
45
|
+
Dynamic: description
|
|
46
|
+
Dynamic: description-content-type
|
|
47
|
+
Dynamic: home-page
|
|
48
|
+
Dynamic: keywords
|
|
49
|
+
Dynamic: license
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
Dynamic: project-url
|
|
52
|
+
Dynamic: requires-dist
|
|
53
|
+
Dynamic: requires-python
|
|
54
|
+
Dynamic: summary
|
|
55
|
+
|
|
56
|
+
# LAM
|
|
57
|
+
LAM is a data transformation tool designed for Laminar's API integration platform.
|
|
58
|
+
|
|
59
|
+
## Overview
|
|
60
|
+
LAM enables you to write efficient transformations for your API data using either JavaScript (Bun) or Python. It's designed to be secure, fast, and easy to integrate into your Laminar workflows.
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
- **Dual Engine Support**: Choose between JavaScript (Bun runtime) for fast execution or Python for complex data processing
|
|
64
|
+
- **Built-in Libraries**: Access lodash and date-fns in JavaScript, comprehensive Python standard library modules
|
|
65
|
+
- **Security**: Runs in sandboxed environments with strict resource limits and security restrictions
|
|
66
|
+
- **Performance**: Uses Bun runtime for JavaScript and sandboxed Python interpreter
|
|
67
|
+
- **Monitoring**: Built-in execution statistics and error tracking
|
|
68
|
+
|
|
69
|
+
## Execution Environments
|
|
70
|
+
|
|
71
|
+
### Bun JavaScript Runtime (js)
|
|
72
|
+
**Configuration**:
|
|
73
|
+
- **Engine**: Bun
|
|
74
|
+
- **Timeout**: 5 seconds
|
|
75
|
+
- **Execution**: Isolated with `--no-fetch --smol --silent` flags
|
|
76
|
+
- **Storage**: No localStorage/sessionStorage support
|
|
77
|
+
- **Modules**: Shared node_modules directory
|
|
78
|
+
|
|
79
|
+
**Available Libraries**:
|
|
80
|
+
- **lodash** (^4.17.21): Utility library for array/object manipulation, data transformations (Global: `_`)
|
|
81
|
+
- **date-fns** (^2.30.0): Modern date utility library with `format`, `parseISO` functions
|
|
82
|
+
|
|
83
|
+
**Transform Function Signature**:
|
|
84
|
+
```js
|
|
85
|
+
(input) => { /* transform logic */ return result; }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Python Interpreter with Sandboxing (py)
|
|
89
|
+
**Configuration**:
|
|
90
|
+
- **Engine**: Python interpreter
|
|
91
|
+
- **Timeout**: 5 seconds
|
|
92
|
+
- **Memory Limit**: 100MB
|
|
93
|
+
- **CPU Limit**: 5 seconds (RLIMIT_CPU)
|
|
94
|
+
- **Virtual Memory**: 100MB (RLIMIT_AS)
|
|
95
|
+
- **Execution**: Isolated with `-I` flag (ignores environment/site packages)
|
|
96
|
+
|
|
97
|
+
**Security Restrictions**:
|
|
98
|
+
- **Blocked Modules**: subprocess, sys, os, shutil, pathlib, importlib, builtins, _thread, ctypes, socket, pickle, multiprocessing
|
|
99
|
+
- **Blocked Functions**: __import__, eval, exec, globals, locals, getattr, setattr, delattr, compile, open
|
|
100
|
+
- **Blocked Patterns**: __subclasses__, dunder attributes access
|
|
101
|
+
|
|
102
|
+
**Available Standard Library Modules**:
|
|
103
|
+
- **json**: JSON encoder and decoder
|
|
104
|
+
- **datetime**: Date and time handling
|
|
105
|
+
- **time**: Time-related functions
|
|
106
|
+
- **math**: Mathematical functions and constants
|
|
107
|
+
- **statistics**: Statistical functions (mean, median, mode, standard deviation)
|
|
108
|
+
- **collections**: Counter, defaultdict, OrderedDict, deque
|
|
109
|
+
- **itertools**: Efficient looping, combinations, permutations
|
|
110
|
+
- **functools**: reduce, partial, lru_cache
|
|
111
|
+
- **re**: Regular expression operations
|
|
112
|
+
- **copy**: Shallow and deep copy operations
|
|
113
|
+
- **decimal**: Precise decimal calculations
|
|
114
|
+
- **csv**: CSV file reading and writing
|
|
115
|
+
- **io**: StringIO, BytesIO for in-memory files
|
|
116
|
+
- **dataclasses**: Data classes for storing data
|
|
117
|
+
- **typing**: Type hints support
|
|
118
|
+
- **enum**: Support for enumerations
|
|
119
|
+
- **random**: Random number generation
|
|
120
|
+
- **uuid**: UUID generation
|
|
121
|
+
- **hashlib**: Secure hash and message digest algorithms
|
|
122
|
+
- **base64**: Base64 encoding and decoding
|
|
123
|
+
- **urllib**: URL handling modules
|
|
124
|
+
- **urllib.parse**: URL parsing utilities
|
|
125
|
+
- **html**: HTML processing utilities
|
|
126
|
+
- **xml**: XML processing
|
|
127
|
+
- **xml.etree**: XML ElementTree API
|
|
128
|
+
- **xml.etree.ElementTree**: XML parsing and creation
|
|
129
|
+
- **string**: String constants and classes
|
|
130
|
+
- **textwrap**: Text wrapping and filling
|
|
131
|
+
- **operator**: Standard operators as functions
|
|
132
|
+
- **bisect**: Array bisection algorithm
|
|
133
|
+
- **heapq**: Heap queue algorithm
|
|
134
|
+
- **array**: Efficient arrays of numeric values
|
|
135
|
+
- **unicodedata**: Unicode character database
|
|
136
|
+
- **locale**: Internationalization services
|
|
137
|
+
- **calendar**: Calendar-related functions
|
|
138
|
+
- **zoneinfo**: Time zone support (Python 3.9+)
|
|
139
|
+
- **struct**: Pack and unpack binary data
|
|
140
|
+
- **binascii**: Binary/ASCII conversions
|
|
141
|
+
- **codecs**: Encode and decode data
|
|
142
|
+
- **difflib**: Sequence comparison utilities
|
|
143
|
+
- **pprint**: Pretty-printer for data structures
|
|
144
|
+
- **reprlib**: Alternate repr() implementation
|
|
145
|
+
- **abc**: Abstract base classes
|
|
146
|
+
- **contextlib**: Context management utilities
|
|
147
|
+
- **secrets**: Cryptographically secure random numbers
|
|
148
|
+
- **fractions**: Rational numbers
|
|
149
|
+
- **numbers**: Numeric abstract base classes
|
|
150
|
+
|
|
151
|
+
**Safe Built-in Functions**:
|
|
152
|
+
`abs`, `all`, `any`, `bool`, `chr`, `dict`, `divmod`, `enumerate`, `filter`, `float`, `format`, `frozenset`, `hash`, `hex`, `int`, `isinstance`, `issubclass`, `iter`, `len`, `list`, `map`, `max`, `min`, `next`, `oct`, `ord`, `pow`, `print`, `range`, `repr`, `reversed`, `round`, `set`, `slice`, `sorted`, `str`, `sum`, `tuple`, `type`, `zip`
|
|
153
|
+
|
|
154
|
+
**Transform Function Signature**:
|
|
155
|
+
```py
|
|
156
|
+
def transform(input_data):
|
|
157
|
+
# transform logic
|
|
158
|
+
return result
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Examples
|
|
162
|
+
|
|
163
|
+
### JavaScript (Bun) Transformations
|
|
164
|
+
Perfect for fast data manipulation with familiar syntax:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
(input) => {
|
|
168
|
+
// Use lodash for data manipulation
|
|
169
|
+
const processed = _.map(input.data, item => ({
|
|
170
|
+
id: item.id,
|
|
171
|
+
formattedDate: format(parseISO(item.date), 'MMM dd, yyyy'),
|
|
172
|
+
value: item.value * 2
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
processed,
|
|
177
|
+
summary: {
|
|
178
|
+
total: _.sumBy(processed, 'value'),
|
|
179
|
+
count: processed.length
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Python Transformations
|
|
186
|
+
Ideal for complex data processing and statistical analysis:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
def transform(input_data):
|
|
190
|
+
import statistics
|
|
191
|
+
from collections import Counter
|
|
192
|
+
|
|
193
|
+
# Process numerical data
|
|
194
|
+
values = [item["value"] for item in input_data["data"] if "value" in item]
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
"statistics": {
|
|
198
|
+
"mean": statistics.mean(values) if values else 0,
|
|
199
|
+
"median": statistics.median(values) if values else 0,
|
|
200
|
+
"count": len(values)
|
|
201
|
+
},
|
|
202
|
+
"frequency": dict(Counter(item["category"] for item in input_data["data"])),
|
|
203
|
+
"processedAt": datetime.now().isoformat()
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Integration with Laminar
|
|
208
|
+
LAM is designed to work seamlessly with Laminar's integration platform:
|
|
209
|
+
|
|
210
|
+
1. **Flows**: Add data transformations to your API flows
|
|
211
|
+
2. **Automation**: Schedule and automate data processing
|
|
212
|
+
3. **Monitoring**: Track execution statistics and errors
|
|
213
|
+
|
|
214
|
+
## Getting Started
|
|
215
|
+
|
|
216
|
+
### Using LAM in Laminar
|
|
217
|
+
1. Create a new flow in [Laminar](https://app.laminar.run)
|
|
218
|
+
2. Add a transformation step
|
|
219
|
+
3. Choose your engine (JavaScript or Python)
|
|
220
|
+
4. Write your transformation function
|
|
221
|
+
5. Deploy and monitor
|
|
222
|
+
|
|
223
|
+
## Resources
|
|
224
|
+
- [Laminar Documentation](https://docs.laminar.run)
|
|
225
|
+
- [Sign up for Laminar](https://app.laminar.run)
|
|
226
|
+
|
|
227
|
+
## Support
|
|
228
|
+
Get help with LAM:
|
|
229
|
+
- [Contact Support](mailto:connect@laminar.run)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import tempfile
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import psutil
|
|
12
|
+
from logtail import LogtailHandler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setup_logging():
|
|
16
|
+
"""Configure logging with UTC timezone and handlers"""
|
|
17
|
+
# Configure logging with UTC timezone
|
|
18
|
+
logging.Formatter.converter = lambda *args: datetime.now(timezone.utc).timetuple()
|
|
19
|
+
|
|
20
|
+
# Set up root logger configuration
|
|
21
|
+
logging.basicConfig(
|
|
22
|
+
level=logging.DEBUG,
|
|
23
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
24
|
+
force=True # Override any existing configuration
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Suppress noisy loggers
|
|
28
|
+
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
|
29
|
+
|
|
30
|
+
# Add LogTail handler to root logger
|
|
31
|
+
root_logger = logging.getLogger()
|
|
32
|
+
handler = LogtailHandler(source_token="TYz3WrrvC8ehYjXdAEGGyiDp")
|
|
33
|
+
root_logger.addHandler(handler)
|
|
34
|
+
|
|
35
|
+
return logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LAMError(Exception):
|
|
39
|
+
"""Base exception for LAM errors"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class UserError(LAMError):
|
|
44
|
+
"""Errors caused by user input"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SystemError(LAMError):
|
|
49
|
+
"""Errors caused by system issues"""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ResourceLimitError(LAMError):
|
|
54
|
+
"""Errors caused by resource limits"""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ProcessingError(Exception):
|
|
59
|
+
"""Custom exception for processing errors"""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check_resource_limits(modules_dir: Optional[Path] = None) -> None:
|
|
64
|
+
"""Check system resource availability"""
|
|
65
|
+
disk = shutil.disk_usage(tempfile.gettempdir())
|
|
66
|
+
if disk.free < 100 * 1024 * 1024: # 100MB minimum
|
|
67
|
+
raise ResourceLimitError("Insufficient disk space")
|
|
68
|
+
|
|
69
|
+
if modules_dir and modules_dir.exists():
|
|
70
|
+
modules_size = sum(
|
|
71
|
+
os.path.getsize(os.path.join(dirpath, filename))
|
|
72
|
+
for dirpath, _, filenames in os.walk(modules_dir)
|
|
73
|
+
for filename in filenames
|
|
74
|
+
)
|
|
75
|
+
if modules_size > 500 * 1024 * 1024:
|
|
76
|
+
shutil.rmtree(modules_dir)
|
|
77
|
+
modules_dir.mkdir(exist_ok=True)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Stats:
|
|
81
|
+
"""Track execution statistics"""
|
|
82
|
+
def __init__(self):
|
|
83
|
+
self.start_time = datetime.now()
|
|
84
|
+
self.memory_start = self.get_memory_usage()
|
|
85
|
+
|
|
86
|
+
def get_memory_usage(self):
|
|
87
|
+
process = psutil.Process()
|
|
88
|
+
return process.memory_info().rss
|
|
89
|
+
|
|
90
|
+
def finalize(self):
|
|
91
|
+
return {
|
|
92
|
+
'duration_ms': (datetime.now() - self.start_time).total_seconds() * 1000,
|
|
93
|
+
'memory_used_mb': (self.get_memory_usage() - self.memory_start) / (1024 * 1024),
|
|
94
|
+
'timestamp': datetime.now().isoformat()
|
|
95
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .base import Engine, EngineType
|
|
2
|
+
from .javascript import BunEngine
|
|
3
|
+
from .jq import JQEngine
|
|
4
|
+
from .python import PythonEngine
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_engine(engine_type: str, workspace_id: str, flow_id: str, execution_id: str) -> Engine:
|
|
8
|
+
"""Factory function to get the appropriate execution engine"""
|
|
9
|
+
engines = {
|
|
10
|
+
EngineType.JQ.value: JQEngine,
|
|
11
|
+
EngineType.JAVASCRIPT.value: BunEngine,
|
|
12
|
+
EngineType.PYTHON.value: PythonEngine
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
engine_class = engines.get(engine_type)
|
|
16
|
+
if not engine_class:
|
|
17
|
+
raise ValueError(f"Unsupported engine type: {engine_type}")
|
|
18
|
+
|
|
19
|
+
engine = engine_class(workspace_id, flow_id, execution_id)
|
|
20
|
+
if not engine.validate_environment():
|
|
21
|
+
raise EnvironmentError(f"Required dependencies not found for {engine_type}")
|
|
22
|
+
|
|
23
|
+
return engine
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import socket
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Dict, Optional, Tuple, Union
|
|
7
|
+
|
|
8
|
+
from posthog import Posthog
|
|
9
|
+
|
|
10
|
+
# Initialize analytics
|
|
11
|
+
posthog = Posthog(project_api_key='phc_wfeHFG0p5yZIdBpjVYy00o5x1HbEpggdMzIuFYgNPSK',
|
|
12
|
+
host='https://app.posthog.com')
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EngineType(Enum):
|
|
18
|
+
JQ = "jq"
|
|
19
|
+
JAVASCRIPT = "js"
|
|
20
|
+
PYTHON = "py"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Engine:
|
|
24
|
+
"""Base class for execution engines"""
|
|
25
|
+
def __init__(self, workspace_id: str, flow_id: str, execution_id: str):
|
|
26
|
+
self.workspace_id = workspace_id
|
|
27
|
+
self.flow_id = flow_id
|
|
28
|
+
self.execution_id = execution_id
|
|
29
|
+
self.timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
30
|
+
|
|
31
|
+
def get_log_file(self) -> str:
|
|
32
|
+
return f"lam_run_{self.workspace_id}_{self.flow_id}_{self.execution_id}_{self.timestamp}.log"
|
|
33
|
+
|
|
34
|
+
def get_result_file(self) -> str:
|
|
35
|
+
return f"lam_result_{self.workspace_id}_{self.flow_id}_{self.execution_id}_{self.timestamp}.json"
|
|
36
|
+
|
|
37
|
+
def track_event(self, event_name: str, properties: Dict[str, Any]) -> None:
|
|
38
|
+
"""Track events with PostHog"""
|
|
39
|
+
try:
|
|
40
|
+
distinct_id = f"{os.getuid()}_{socket.gethostname()}_{self.workspace_id}_{self.flow_id}"
|
|
41
|
+
properties |= {
|
|
42
|
+
'workspace_id': self.workspace_id,
|
|
43
|
+
'flow_id': self.flow_id,
|
|
44
|
+
'engine': self.__class__.__name__,
|
|
45
|
+
}
|
|
46
|
+
posthog.capture(distinct_id=distinct_id, event=event_name, properties=properties)
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.error(f"Error tracking event: {e}")
|
|
49
|
+
|
|
50
|
+
def validate_environment(self) -> bool:
|
|
51
|
+
"""Validate that the engine can run in this environment"""
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
def execute(self, program_file: str, input_data: str) -> Tuple[Union[Dict, str], Optional[str]]:
|
|
55
|
+
"""Execute the program with input data"""
|
|
56
|
+
raise NotImplementedError
|