Skip to content

Commit d8af8ac

Browse files
committed
feat(core): Support resource isolation
1 parent b95379b commit d8af8ac

File tree

27 files changed

+1240
-189
lines changed

27 files changed

+1240
-189
lines changed

Cargo.lock

Lines changed: 70 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ default-members = [
99
]
1010

1111
[workspace.package]
12-
version = "0.1.3"
12+
version = "0.1.4-rc0"
1313
authors = ["fangyinc <staneyffer@gmail.com>"]
1414
edition = "2021"
1515
homepage = "https://github.com/fangyinc/lyric"
@@ -43,9 +43,10 @@ tokio-util = { version = "0.7.12", features = ["io"] }
4343
tokio-stream = { version = "0.1.16", features = ["io-util"] }
4444
pyo3 = { version = "0.22.5" }
4545
python3-dll-a = "0.2.10"
46+
bitflags = "2.6"
4647

47-
lyric = { version = "0.1.3", path = "crates/lyric", default-features = false }
48-
lyric-utils = { version = "0.1.3", path = "crates/lyric-utils", default-features = false }
49-
lyric-rpc = { version = "0.1.3", path = "crates/lyric-rpc", default-features = false }
50-
lyric-wasm-runtime = { version = "0.1.3", path = "crates/lyric-wasm-runtime", default-features = false }
51-
lyric-py = { version = "0.1.3", path = "bindings/python/lyric-py", default-features = false }
48+
lyric = { version = "0.1.4-rc0", path = "crates/lyric", default-features = false }
49+
lyric-utils = { version = "0.1.4-rc0", path = "crates/lyric-utils", default-features = false }
50+
lyric-rpc = { version = "0.1.4-rc0", path = "crates/lyric-rpc", default-features = false }
51+
lyric-wasm-runtime = { version = "0.1.4-rc0", path = "crates/lyric-wasm-runtime", default-features = false }
52+
lyric-py = { version = "0.1.4-rc0", path = "bindings/python/lyric-py", default-features = false }

README.md

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ A Rust-powered secure sandbox for multi-language code execution, leveraging WebA
2121
**Install Lyric via pip:**
2222

2323
```bash
24-
pip install "lyric-py>=0.1.3"
24+
pip install "lyric-py>=0.1.4-rc0"
2525
```
2626

2727
**Install default Python webassembly worker:**
2828

2929
```bash
30-
pip install "lyric-py-worker>=0.1.3"
30+
pip install "lyric-py-worker>=0.1.4-rc0"
3131
```
3232

3333
**Install default JavaScript webassembly worker:**
3434

3535
```bash
36-
pip install "lyric-js-worker>=0.1.3"
36+
pip install "lyric-js-worker>=0.1.4-rc0"
3737
```
3838

3939
### Basic Usage
@@ -42,6 +42,17 @@ pip install "lyric-js-worker>=0.1.3"
4242
import asyncio
4343
from lyric import DefaultLyricDriver
4444

45+
python_code = """
46+
def add(a, b):
47+
return a + b
48+
result = add(1, 2)
49+
print(result)
50+
"""
51+
52+
js_code = """
53+
console.log('Hello from JavaScript!');
54+
"""
55+
4556
async def main():
4657
lcd = DefaultLyricDriver(host="localhost", log_level="ERROR")
4758
lcd.start()
@@ -50,21 +61,10 @@ async def main():
5061
await lcd.lyric.load_default_workers()
5162

5263
# Execute Python code
53-
python_code = """
54-
def add(a, b):
55-
return a + b
56-
result = add(1, 2)
57-
print(result)
58-
"""
59-
6064
py_res = await lcd.exec(python_code, "python")
6165
print(py_res)
6266

6367
# Execute JavaScript code
64-
js_code = """
65-
console.log('Hello from JavaScript!');
66-
"""
67-
6868
js_res = await lcd.exec(js_code, "javascript")
6969
print(js_res)
7070

@@ -81,13 +81,7 @@ import asyncio
8181
import json
8282
from lyric import DefaultLyricDriver
8383

84-
async def main():
85-
lcd = DefaultLyricDriver(host="localhost", log_level="ERROR")
86-
lcd.start()
87-
88-
# Load workers(default: Python, JavaScript)
89-
await lcd.lyric.load_default_workers()
90-
py_func = """
84+
py_func = """
9185
def message_handler(message_dict):
9286
user_message = message_dict.get("user_message")
9387
ai_message = message_dict.get("ai_message")
@@ -99,18 +93,8 @@ def message_handler(message_dict):
9993
"handler_language": "python",
10094
}
10195
"""
102-
input_data = {
103-
"user_message": "Hello from user",
104-
"ai_message": "Hello from AI",
105-
}
106-
input_bytes = json.dumps(input_data).encode("utf-8")
107-
py_res = await lcd.exec1(py_func, input_bytes, "message_handler", lang="python")
108-
# Get the result of the function execution
109-
result_dict = py_res.output
110-
print("Python result:", result_dict)
111-
print(f"Full output: {py_res}")
11296

113-
js_func = """
97+
js_func = """
11498
function message_handler(message_dict) {
11599
return {
116100
user: message_dict.user_message,
@@ -121,6 +105,25 @@ function message_handler(message_dict) {
121105
};
122106
}
123107
"""
108+
async def main():
109+
lcd = DefaultLyricDriver(host="localhost", log_level="ERROR")
110+
lcd.start()
111+
112+
# Load workers(default: Python, JavaScript)
113+
await lcd.lyric.load_default_workers()
114+
115+
input_data = {
116+
"user_message": "Hello from user",
117+
"ai_message": "Hello from AI",
118+
}
119+
input_bytes = json.dumps(input_data).encode("utf-8")
120+
121+
py_res = await lcd.exec1(py_func, input_bytes, "message_handler", lang="python")
122+
# Get the result of the function execution
123+
result_dict = py_res.output
124+
print("Python result:", result_dict)
125+
print(f"Full output: {py_res}")
126+
124127
js_res = await lcd.exec1(js_func, input_bytes, "message_handler", lang="javascript")
125128
# Get the result of the function execution
126129
result_dict = js_res.output
@@ -133,6 +136,59 @@ function message_handler(message_dict) {
133136
asyncio.run(main())
134137
```
135138

139+
## Advanced Usage
140+
141+
### Execution With Specific Resources
142+
143+
```python
144+
import asyncio
145+
from lyric import DefaultLyricDriver, PyTaskResourceConfig, PyTaskFsConfig, PyTaskMemoryConfig
146+
147+
lcd = DefaultLyricDriver(host="localhost", log_level="ERROR")
148+
lcd.start()
149+
150+
python_code = """
151+
import os
152+
153+
# List the files in the root directory
154+
root = os.listdir('/tmp/')
155+
print("Files in the root directory:", root)
156+
157+
# Create a new file in the home directory
158+
with open('/home/new_file.txt', 'w') as f:
159+
f.write('Hello, World!')
160+
"""
161+
162+
async def main():
163+
# Load workers(default: Python, JavaScript)
164+
await lcd.lyric.load_default_workers()
165+
166+
dir_read, dir_write = 1, 2
167+
file_read, file_write = 3, 4
168+
resources = PyTaskResourceConfig(
169+
fs=PyTaskFsConfig(
170+
preopens=[
171+
# Mount current directory in host to "/tmp" in the sandbox with read permission
172+
(".", "/tmp", dir_read, file_read),
173+
# Mount "/tmp" in host to "/home" in the sandbox with read and write permission
174+
("/tmp", "/home", dir_read | dir_write, file_read | file_write),
175+
]
176+
),
177+
memory=PyTaskMemoryConfig(
178+
# Set the memory limit to 30MB
179+
memory_limit=30 * 1024 * 1024 # 30MB in bytes
180+
)
181+
)
182+
183+
py_res = await lcd.exec(python_code, "python", resources=resources)
184+
assert py_res.exit_code == 0, "Python code should exit with 0"
185+
186+
# Stop the driver
187+
lcd.stop()
188+
189+
asyncio.run(main())
190+
```
191+
136192
## Examples
137193

138194
- [Notebook-Qick Start](examples/notebook/lyric_quick_start.ipynb): A Jupyter notebook demonstrating how to use Lyric to execute Python and JavaScript code.

bindings/javascript/lyric-js-worker/wit/deps/task/types.wit

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,92 @@
11
interface types {
2+
3+
/// CPU Resource Configuration
4+
record cpu-config {
5+
/// CPU core limit (e.g. 1.0 represents a full core, 0.5 represents half a core)
6+
cpu-cores: option<f32>,
7+
/// CPU share value (similar to --cpu-shares in docker, default 1024)
8+
cpu-shares: option<u32>,
9+
/// CPU period configuration (microseconds, at most use quota microseconds within period)
10+
cpu-quota: option<u32>,
11+
/// CPU period time (microseconds, default 100000, i.e. 100ms)
12+
cpu-period: option<u32>
13+
}
14+
15+
/// Memory Resource Configuration
16+
record memory-config {
17+
/// Memory limit in bytes
18+
memory-limit: option<u64>
19+
}
20+
21+
/// Network Resource Configuration
22+
record network-config {
23+
/// Whether to enable network access
24+
enable-network: bool,
25+
/// Inbound bandwidth limit (KB/s)
26+
ingress-limit-kbps: option<u32>,
27+
/// Outbound bandwidth limit (KB/s)
28+
egress-limit-kbps: option<u32>,
29+
/// Allowed host list
30+
allowed-hosts: option<list<string>>,
31+
/// Allowed port range list (start_port, end_port)
32+
allowed-ports: option<list<tuple<u16, u16>>>
33+
}
34+
35+
/// File system permission
36+
flags file-perms {
37+
/// Read permission
38+
read,
39+
/// Write permission
40+
write,
41+
}
42+
43+
/// File system configuration
44+
record fs-config {
45+
/// Pre-mapped directory list (host path, container path, directory permissions, file permissions)
46+
preopens: list<tuple<string, string, file-perms, file-perms>>,
47+
/// File system size limit in bytes
48+
fs-size-limit: option<u64>,
49+
/// Temporary directory for wasi
50+
temp-dir: option<string>
51+
}
52+
53+
/// Instance limits
54+
record instance-limits {
55+
// Max number of instances
56+
max-instances: option<u32>,
57+
/// Max number of tables
58+
max-tables: option<u32>,
59+
/// Max number of elements in each table
60+
max-table-elements: option<u32>
61+
}
62+
63+
/// Full resource configuration
64+
record resource-config {
65+
/// CPU configuration
66+
cpu: option<cpu-config>,
67+
/// Memory configuration
68+
memory: option<memory-config>,
69+
/// Network configuration
70+
network: option<network-config>,
71+
/// File system configuration
72+
fs: option<fs-config>,
73+
// Instance limits
74+
instance: option<instance-limits>,
75+
/// Task timeout in milliseconds
76+
timeout-ms: option<u32>,
77+
/// List of environment variables
78+
env-vars: list<tuple<string, string>>
79+
}
80+
281
/// A request to interpret a script
382
record interpreter-request {
83+
/// Resource configuration
84+
resources: option<resource-config>,
485
/// The protocol version of the interpreter
586
protocol: u32,
687
/// The language of the script
788
lang: string,
8-
/// The script to be interpreted
89+
/// The script to be interpreted
990
code: string
1091
}
1192

@@ -29,6 +110,8 @@ interface types {
29110
}
30111

31112
record binary-request {
113+
/// Resource configuration
114+
resources: option<resource-config>,
32115
protocol: u32,
33116
data: list<u8>
34117
}

bindings/python/lyric-js-worker/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "lyric-js-worker"
3-
version = "0.1.3"
3+
version = "0.1.4-rc0"
44
description = "Add your description here"
55
authors = [
66
{ name = "Fangyin Cheng", email = "staneyffer@gmail.com" },

bindings/python/lyric-py-worker/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "lyric-py-worker"
3-
version = "0.1.3"
3+
version = "0.1.4-rc0"
44
description = "Add your description here"
55
authors = [
66
{ name = "Fangyin Cheng", email = "staneyffer@gmail.com" },

0 commit comments

Comments
 (0)