inferlet 0.3.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.
- inferlet-0.3.0/.gitignore +251 -0
- inferlet-0.3.0/PKG-INFO +70 -0
- inferlet-0.3.0/README.md +44 -0
- inferlet-0.3.0/pyproject.toml +56 -0
- inferlet-0.3.0/scripts/generate_bindings.py +68 -0
- inferlet-0.3.0/scripts/validate_imports.py +175 -0
- inferlet-0.3.0/src/inferlet/__init__.py +150 -0
- inferlet-0.3.0/src/inferlet/_async.py +37 -0
- inferlet-0.3.0/src/inferlet/adapter.py +64 -0
- inferlet-0.3.0/src/inferlet/bindings/__init__.py +1 -0
- inferlet-0.3.0/src/inferlet/bindings/componentize_py_async_support/__init__.py +529 -0
- inferlet-0.3.0/src/inferlet/bindings/componentize_py_async_support/futures.py +93 -0
- inferlet-0.3.0/src/inferlet/bindings/componentize_py_async_support/streams.py +189 -0
- inferlet-0.3.0/src/inferlet/bindings/componentize_py_runtime.pyi +34 -0
- inferlet-0.3.0/src/inferlet/bindings/componentize_py_types.py +19 -0
- inferlet-0.3.0/src/inferlet/bindings/poll_loop.py +441 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/__init__.py +15 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/__init__.py +0 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/adapter.py +49 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/chat.py +81 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/client.py +107 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/context.py +147 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/error.py +56 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/inference.py +287 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/insecure.py +39 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/insecure_seed.py +40 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/messaging.py +65 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/model.py +80 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/pie_core_types.py +56 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/pie_mcp_types.py +34 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/poll.py +72 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/preopens.py +18 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/random.py +43 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/reasoning.py +57 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/runtime.py +33 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/scheduling.py +42 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/session.py +33 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/streams.py +330 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/tool_use.py +70 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/wall_clock.py +61 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/wasi_filesystem_types.py +594 -0
- inferlet-0.3.0/src/inferlet/bindings/wit_world/imports/zo.py +26 -0
- inferlet-0.3.0/src/inferlet/chat.py +175 -0
- inferlet-0.3.0/src/inferlet/context.py +379 -0
- inferlet-0.3.0/src/inferlet/forward.py +394 -0
- inferlet-0.3.0/src/inferlet/generation.py +551 -0
- inferlet-0.3.0/src/inferlet/grammar.py +307 -0
- inferlet-0.3.0/src/inferlet/mcp.py +83 -0
- inferlet-0.3.0/src/inferlet/messaging.py +78 -0
- inferlet-0.3.0/src/inferlet/model.py +65 -0
- inferlet-0.3.0/src/inferlet/reasoning.py +111 -0
- inferlet-0.3.0/src/inferlet/runtime.py +29 -0
- inferlet-0.3.0/src/inferlet/sample.py +176 -0
- inferlet-0.3.0/src/inferlet/scheduling.py +44 -0
- inferlet-0.3.0/src/inferlet/session.py +54 -0
- inferlet-0.3.0/src/inferlet/spec.py +59 -0
- inferlet-0.3.0/src/inferlet/tools.py +150 -0
- inferlet-0.3.0/src/inferlet/zo.py +59 -0
- inferlet-0.3.0/tests/conftest.py +594 -0
- inferlet-0.3.0/tests/test_sdk.py +543 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Generated by Cargo
|
|
2
|
+
# will have compiled files and executables
|
|
3
|
+
debug/
|
|
4
|
+
target/
|
|
5
|
+
|
|
6
|
+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
7
|
+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
8
|
+
Cargo.lock
|
|
9
|
+
|
|
10
|
+
# These are backup files generated by rustfmt
|
|
11
|
+
**/*.rs.bk
|
|
12
|
+
|
|
13
|
+
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
14
|
+
*.pdb
|
|
15
|
+
|
|
16
|
+
# RustRover
|
|
17
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
18
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
19
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
20
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
21
|
+
.idea/
|
|
22
|
+
|
|
23
|
+
# VSCode
|
|
24
|
+
# .vscode is not sensitive to security, so we can keep it in the repo. Useful for Pylint configuration.
|
|
25
|
+
*.code-workspace
|
|
26
|
+
.vscode
|
|
27
|
+
|
|
28
|
+
# Other local ide/environment
|
|
29
|
+
.claude
|
|
30
|
+
.codex
|
|
31
|
+
.kilocode
|
|
32
|
+
|
|
33
|
+
# Byte-compiled / optimized / DLL files
|
|
34
|
+
__pycache__/
|
|
35
|
+
*.py[cod]
|
|
36
|
+
*$py.class
|
|
37
|
+
|
|
38
|
+
# C extensions
|
|
39
|
+
*.so
|
|
40
|
+
|
|
41
|
+
# Distribution / packaging
|
|
42
|
+
.Python
|
|
43
|
+
build/
|
|
44
|
+
develop-eggs/
|
|
45
|
+
dist/
|
|
46
|
+
downloads/
|
|
47
|
+
eggs/
|
|
48
|
+
.eggs/
|
|
49
|
+
lib64/
|
|
50
|
+
parts/
|
|
51
|
+
sdist/
|
|
52
|
+
var/
|
|
53
|
+
wheels/
|
|
54
|
+
share/python-wheels/
|
|
55
|
+
*.egg-info/
|
|
56
|
+
.installed.cfg
|
|
57
|
+
*.egg
|
|
58
|
+
MANIFEST
|
|
59
|
+
|
|
60
|
+
# PyInstaller
|
|
61
|
+
# Usually these files are written by a python script from a template
|
|
62
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
63
|
+
*.manifest
|
|
64
|
+
*.spec
|
|
65
|
+
|
|
66
|
+
# Installer logs
|
|
67
|
+
pip-log.txt
|
|
68
|
+
pip-delete-this-directory.txt
|
|
69
|
+
|
|
70
|
+
# Unit test / coverage reports
|
|
71
|
+
htmlcov/
|
|
72
|
+
.tox/
|
|
73
|
+
.nox/
|
|
74
|
+
.coverage
|
|
75
|
+
.coverage.*
|
|
76
|
+
.cache
|
|
77
|
+
nosetests.xml
|
|
78
|
+
coverage.xml
|
|
79
|
+
*.cover
|
|
80
|
+
*.py,cover
|
|
81
|
+
.hypothesis/
|
|
82
|
+
.pytest_cache/
|
|
83
|
+
cover/
|
|
84
|
+
|
|
85
|
+
# Translations
|
|
86
|
+
*.mo
|
|
87
|
+
*.pot
|
|
88
|
+
|
|
89
|
+
# Django stuff:
|
|
90
|
+
local_settings.py
|
|
91
|
+
db.sqlite3
|
|
92
|
+
db.sqlite3-journal
|
|
93
|
+
|
|
94
|
+
# Flask stuff:
|
|
95
|
+
.webassets-cache
|
|
96
|
+
|
|
97
|
+
# Scrapy stuff:
|
|
98
|
+
.scrapy
|
|
99
|
+
|
|
100
|
+
# Sphinx documentation
|
|
101
|
+
docs/_build/
|
|
102
|
+
docs/plans/
|
|
103
|
+
|
|
104
|
+
# PyBuilder
|
|
105
|
+
.pybuilder/
|
|
106
|
+
target/
|
|
107
|
+
|
|
108
|
+
# Jupyter Notebook
|
|
109
|
+
.ipynb_checkpoints
|
|
110
|
+
|
|
111
|
+
# IPython
|
|
112
|
+
profile_default/
|
|
113
|
+
ipython_config.py
|
|
114
|
+
|
|
115
|
+
# pyenv
|
|
116
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
117
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
118
|
+
# .python-version
|
|
119
|
+
|
|
120
|
+
# pipenv
|
|
121
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
122
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
123
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
124
|
+
# install all needed dependencies.
|
|
125
|
+
#Pipfile.lock
|
|
126
|
+
|
|
127
|
+
# UV
|
|
128
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
129
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
130
|
+
# commonly ignored for libraries.
|
|
131
|
+
#uv.lock
|
|
132
|
+
|
|
133
|
+
# poetry
|
|
134
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
135
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
136
|
+
# commonly ignored for libraries.
|
|
137
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
138
|
+
#poetry.lock
|
|
139
|
+
|
|
140
|
+
# pdm
|
|
141
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
142
|
+
#pdm.lock
|
|
143
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
144
|
+
# in version control.
|
|
145
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
146
|
+
.pdm.toml
|
|
147
|
+
.pdm-python
|
|
148
|
+
.pdm-build/
|
|
149
|
+
|
|
150
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
151
|
+
__pypackages__/
|
|
152
|
+
|
|
153
|
+
# Celery stuff
|
|
154
|
+
celerybeat-schedule
|
|
155
|
+
celerybeat.pid
|
|
156
|
+
|
|
157
|
+
# SageMath parsed files
|
|
158
|
+
*.sage.py
|
|
159
|
+
|
|
160
|
+
# Environments
|
|
161
|
+
.env
|
|
162
|
+
.venv
|
|
163
|
+
env/
|
|
164
|
+
venv/
|
|
165
|
+
ENV/
|
|
166
|
+
env.bak/
|
|
167
|
+
venv.bak/
|
|
168
|
+
|
|
169
|
+
# Spyder project settings
|
|
170
|
+
.spyderproject
|
|
171
|
+
.spyproject
|
|
172
|
+
|
|
173
|
+
# Rope project settings
|
|
174
|
+
.ropeproject
|
|
175
|
+
|
|
176
|
+
# mkdocs documentation
|
|
177
|
+
/site
|
|
178
|
+
|
|
179
|
+
# mypy
|
|
180
|
+
.mypy_cache/
|
|
181
|
+
.dmypy.json
|
|
182
|
+
dmypy.json
|
|
183
|
+
|
|
184
|
+
# Pyre type checker
|
|
185
|
+
.pyre/
|
|
186
|
+
|
|
187
|
+
# pytype static type analyzer
|
|
188
|
+
.pytype/
|
|
189
|
+
|
|
190
|
+
# Cython debug symbols
|
|
191
|
+
cython_debug/
|
|
192
|
+
|
|
193
|
+
# Ruff stuff:
|
|
194
|
+
.ruff_cache/
|
|
195
|
+
|
|
196
|
+
# PyPI configuration file
|
|
197
|
+
.pypirc
|
|
198
|
+
|
|
199
|
+
# Prompt files
|
|
200
|
+
*.prompt
|
|
201
|
+
|
|
202
|
+
.DS_Store
|
|
203
|
+
|
|
204
|
+
program_cache/
|
|
205
|
+
|
|
206
|
+
*.model
|
|
207
|
+
|
|
208
|
+
.run/
|
|
209
|
+
|
|
210
|
+
backend/backend-pytorch
|
|
211
|
+
|
|
212
|
+
bin/
|
|
213
|
+
*.wasm
|
|
214
|
+
|
|
215
|
+
# logs and temporary files
|
|
216
|
+
logs
|
|
217
|
+
*.log
|
|
218
|
+
.plan
|
|
219
|
+
*profiling_result.json
|
|
220
|
+
/docs/
|
|
221
|
+
|
|
222
|
+
# No MCP files
|
|
223
|
+
.memory-bank
|
|
224
|
+
|
|
225
|
+
# raw eval data
|
|
226
|
+
cuda-protocol-tests*
|
|
227
|
+
metal-protocol-tests*
|
|
228
|
+
cuda_artifacts.tar*
|
|
229
|
+
tensor_references
|
|
230
|
+
|
|
231
|
+
# Github Spec-kit
|
|
232
|
+
.spec-kit/
|
|
233
|
+
|
|
234
|
+
# Node.js
|
|
235
|
+
node_modules/
|
|
236
|
+
/package-lock.json
|
|
237
|
+
!website/package-lock.json
|
|
238
|
+
wandb/
|
|
239
|
+
|
|
240
|
+
# Website (Docusaurus)
|
|
241
|
+
website/build/
|
|
242
|
+
website/.docusaurus/
|
|
243
|
+
website/.cache-loader/
|
|
244
|
+
website/src/data/models.json
|
|
245
|
+
|
|
246
|
+
*.pt
|
|
247
|
+
*.zt
|
|
248
|
+
|
|
249
|
+
PIEX.md
|
|
250
|
+
pie/rollout_logs/
|
|
251
|
+
*.out
|
inferlet-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inferlet
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Python SDK for writing Pie inferlets
|
|
5
|
+
Author: Pie Project
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: inference,inferlet,llm,pie,wasm
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: jinja2>=3.0
|
|
17
|
+
Provides-Extra: build
|
|
18
|
+
Requires-Dist: componentize-py>=0.19; extra == 'build'
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: componentize-py>=0.19; extra == 'dev'
|
|
21
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# inferlet Python SDK
|
|
28
|
+
|
|
29
|
+
Python API for writing Pie inferlets.
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from inferlet import Context, Model, Sampler, runtime
|
|
33
|
+
|
|
34
|
+
async def main(input: dict) -> str:
|
|
35
|
+
model = Model.load(runtime.models()[0])
|
|
36
|
+
ctx = Context(model)
|
|
37
|
+
|
|
38
|
+
ctx.system("You are helpful.").user(input["prompt"])
|
|
39
|
+
|
|
40
|
+
return await ctx.generate(
|
|
41
|
+
Sampler.top_p(0.6, 0.95),
|
|
42
|
+
max_tokens=256,
|
|
43
|
+
).collect_text()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Main pieces
|
|
47
|
+
|
|
48
|
+
- `Context`: owns KV-cache state and chat/raw token buffers.
|
|
49
|
+
- `Forward`: runs one explicit forward pass with samplers, probes, masks, and
|
|
50
|
+
manual page control.
|
|
51
|
+
- `Generator`: multi-step generation loop with stop conditions, constraints,
|
|
52
|
+
speculation, adapters, and JSON collection.
|
|
53
|
+
- `chat`, `reasoning`, `tools`: optional decoders and helpers for model-native
|
|
54
|
+
formats.
|
|
55
|
+
- `runtime`, `session`, `messaging`, `mcp`: host services exposed to inferlets.
|
|
56
|
+
|
|
57
|
+
## Build notes
|
|
58
|
+
|
|
59
|
+
Python inferlets are packaged as Wasm components. Pure-Python dependencies can
|
|
60
|
+
be bundled; native extensions such as `numpy`, `orjson`, `msgspec`, or
|
|
61
|
+
`pydantic_core` cannot be loaded in the Wasm runtime.
|
|
62
|
+
|
|
63
|
+
Build through Bakery:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pie build ./my-python-inferlet -o out.wasm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For constrained decoding details, see
|
|
70
|
+
[`sdk/CONSTRAINED_DECODING.md`](../CONSTRAINED_DECODING.md).
|
inferlet-0.3.0/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# inferlet Python SDK
|
|
2
|
+
|
|
3
|
+
Python API for writing Pie inferlets.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from inferlet import Context, Model, Sampler, runtime
|
|
7
|
+
|
|
8
|
+
async def main(input: dict) -> str:
|
|
9
|
+
model = Model.load(runtime.models()[0])
|
|
10
|
+
ctx = Context(model)
|
|
11
|
+
|
|
12
|
+
ctx.system("You are helpful.").user(input["prompt"])
|
|
13
|
+
|
|
14
|
+
return await ctx.generate(
|
|
15
|
+
Sampler.top_p(0.6, 0.95),
|
|
16
|
+
max_tokens=256,
|
|
17
|
+
).collect_text()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Main pieces
|
|
21
|
+
|
|
22
|
+
- `Context`: owns KV-cache state and chat/raw token buffers.
|
|
23
|
+
- `Forward`: runs one explicit forward pass with samplers, probes, masks, and
|
|
24
|
+
manual page control.
|
|
25
|
+
- `Generator`: multi-step generation loop with stop conditions, constraints,
|
|
26
|
+
speculation, adapters, and JSON collection.
|
|
27
|
+
- `chat`, `reasoning`, `tools`: optional decoders and helpers for model-native
|
|
28
|
+
formats.
|
|
29
|
+
- `runtime`, `session`, `messaging`, `mcp`: host services exposed to inferlets.
|
|
30
|
+
|
|
31
|
+
## Build notes
|
|
32
|
+
|
|
33
|
+
Python inferlets are packaged as Wasm components. Pure-Python dependencies can
|
|
34
|
+
be bundled; native extensions such as `numpy`, `orjson`, `msgspec`, or
|
|
35
|
+
`pydantic_core` cannot be loaded in the Wasm runtime.
|
|
36
|
+
|
|
37
|
+
Build through Bakery:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pie build ./my-python-inferlet -o out.wasm
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For constrained decoding details, see
|
|
44
|
+
[`sdk/CONSTRAINED_DECODING.md`](../CONSTRAINED_DECODING.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "inferlet"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Python SDK for writing Pie inferlets"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = {text = "Apache-2.0"}
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "Pie Project"}
|
|
10
|
+
]
|
|
11
|
+
keywords = ["pie", "inferlet", "wasm", "llm", "inference"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: Apache Software License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"jinja2>=3.0", # For chat template formatting
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
build = [
|
|
28
|
+
"componentize-py>=0.19", # WASM component compiler
|
|
29
|
+
]
|
|
30
|
+
dev = [
|
|
31
|
+
"componentize-py>=0.19",
|
|
32
|
+
"pytest>=7.0",
|
|
33
|
+
"pytest-asyncio>=0.21",
|
|
34
|
+
"mypy>=1.0",
|
|
35
|
+
"ruff>=0.1",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["hatchling"]
|
|
40
|
+
build-backend = "hatchling.build"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/inferlet"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 100
|
|
47
|
+
target-version = "py310"
|
|
48
|
+
|
|
49
|
+
[tool.ruff.lint]
|
|
50
|
+
select = ["E", "F", "I", "N", "W"]
|
|
51
|
+
|
|
52
|
+
[tool.mypy]
|
|
53
|
+
python_version = "3.10"
|
|
54
|
+
warn_return_any = true
|
|
55
|
+
warn_unused_ignores = true
|
|
56
|
+
disallow_untyped_defs = true
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate WIT bindings for inferlet using componentize-py.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python scripts/generate_bindings.py
|
|
7
|
+
|
|
8
|
+
This generates Python bindings in src/inferlet/bindings/ that provide
|
|
9
|
+
typed interfaces to the WIT imports (inferlet:core/runtime, etc.).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> int:
|
|
18
|
+
# Get paths relative to script location
|
|
19
|
+
script_dir = Path(__file__).parent
|
|
20
|
+
project_root = script_dir.parent
|
|
21
|
+
sdk_root = project_root.parent
|
|
22
|
+
pie_root = sdk_root.parent
|
|
23
|
+
|
|
24
|
+
wit_path = pie_root / "runtime" / "wit"
|
|
25
|
+
output_path = project_root / "src" / "inferlet" / "bindings"
|
|
26
|
+
|
|
27
|
+
if not wit_path.exists():
|
|
28
|
+
print(f"Error: WIT directory not found at {wit_path}", file=sys.stderr)
|
|
29
|
+
return 1
|
|
30
|
+
|
|
31
|
+
# Ensure output directory exists
|
|
32
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# Generate bindings using componentize-py
|
|
35
|
+
# The world is "inferlet" as defined in runtime/wit/world.wit
|
|
36
|
+
cmd = [
|
|
37
|
+
"componentize-py",
|
|
38
|
+
"-d",
|
|
39
|
+
str(wit_path),
|
|
40
|
+
"-w",
|
|
41
|
+
"inferlet",
|
|
42
|
+
"bindings",
|
|
43
|
+
str(output_path),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
print(f"Generating bindings from {wit_path}...")
|
|
47
|
+
print(f"Output: {output_path}")
|
|
48
|
+
print(f"Command: {' '.join(cmd)}")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
52
|
+
if result.returncode != 0:
|
|
53
|
+
print(f"Error generating bindings:", file=sys.stderr)
|
|
54
|
+
print(result.stderr, file=sys.stderr)
|
|
55
|
+
return result.returncode
|
|
56
|
+
print(result.stdout)
|
|
57
|
+
print("Bindings generated successfully!")
|
|
58
|
+
return 0
|
|
59
|
+
except FileNotFoundError:
|
|
60
|
+
print(
|
|
61
|
+
"Error: componentize-py not found. Install with: pip install componentize-py",
|
|
62
|
+
file=sys.stderr,
|
|
63
|
+
)
|
|
64
|
+
return 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
sys.exit(main())
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate that user Python code doesn't import forbidden modules.
|
|
4
|
+
|
|
5
|
+
This script checks for imports that won't work in the WASM environment:
|
|
6
|
+
- Network libraries (requests, urllib3, httpx, aiohttp, etc.)
|
|
7
|
+
- Web frameworks (flask, django, fastapi, etc.)
|
|
8
|
+
- System libraries (os.*, subprocess, multiprocessing, etc.)
|
|
9
|
+
- Database drivers (sqlite3, psycopg2, pymysql, etc.)
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python scripts/validate_imports.py <path_to_python_file>
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import ast
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Modules that are forbidden in WASM environment
|
|
21
|
+
FORBIDDEN_MODULES = {
|
|
22
|
+
# Network libraries
|
|
23
|
+
"requests",
|
|
24
|
+
"urllib3",
|
|
25
|
+
"httpx",
|
|
26
|
+
"aiohttp",
|
|
27
|
+
"websockets",
|
|
28
|
+
"socket",
|
|
29
|
+
"ssl",
|
|
30
|
+
# Web frameworks
|
|
31
|
+
"flask",
|
|
32
|
+
"django",
|
|
33
|
+
"fastapi",
|
|
34
|
+
"starlette",
|
|
35
|
+
"tornado",
|
|
36
|
+
"bottle",
|
|
37
|
+
# System libraries
|
|
38
|
+
"subprocess",
|
|
39
|
+
"multiprocessing",
|
|
40
|
+
"threading", # Limited support
|
|
41
|
+
"ctypes",
|
|
42
|
+
"cffi",
|
|
43
|
+
# File system (limited)
|
|
44
|
+
"pathlib", # Some operations may work
|
|
45
|
+
"shutil",
|
|
46
|
+
"tempfile",
|
|
47
|
+
# Database drivers
|
|
48
|
+
"sqlite3",
|
|
49
|
+
"psycopg2",
|
|
50
|
+
"pymysql",
|
|
51
|
+
"pymongo",
|
|
52
|
+
"redis",
|
|
53
|
+
# Other
|
|
54
|
+
"tkinter",
|
|
55
|
+
"pygame",
|
|
56
|
+
"numpy", # Native extension
|
|
57
|
+
"pandas", # Native extension
|
|
58
|
+
"scipy", # Native extension
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Submodules of os that are forbidden
|
|
62
|
+
FORBIDDEN_OS_SUBMODULES = {
|
|
63
|
+
"os.system",
|
|
64
|
+
"os.popen",
|
|
65
|
+
"os.spawn",
|
|
66
|
+
"os.fork",
|
|
67
|
+
"os.exec",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ImportVisitor(ast.NodeVisitor):
|
|
72
|
+
"""AST visitor to collect imports."""
|
|
73
|
+
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
self.imports: list[tuple[str, int]] = [] # (module, line_number)
|
|
76
|
+
|
|
77
|
+
def visit_Import(self, node: ast.Import) -> None:
|
|
78
|
+
for alias in node.names:
|
|
79
|
+
self.imports.append((alias.name, node.lineno))
|
|
80
|
+
self.generic_visit(node)
|
|
81
|
+
|
|
82
|
+
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
|
83
|
+
if node.module:
|
|
84
|
+
self.imports.append((node.module, node.lineno))
|
|
85
|
+
# Also check for specific imports like "from os import system"
|
|
86
|
+
for alias in node.names:
|
|
87
|
+
full_name = f"{node.module}.{alias.name}"
|
|
88
|
+
self.imports.append((full_name, node.lineno))
|
|
89
|
+
self.generic_visit(node)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def validate_imports(source_path: Path) -> list[str]:
|
|
93
|
+
"""
|
|
94
|
+
Validate imports in a Python source file.
|
|
95
|
+
|
|
96
|
+
Returns a list of warning messages for forbidden imports.
|
|
97
|
+
"""
|
|
98
|
+
warnings: list[str] = []
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
source = source_path.read_text()
|
|
102
|
+
tree = ast.parse(source, filename=str(source_path))
|
|
103
|
+
except SyntaxError as e:
|
|
104
|
+
return [f"Syntax error in {source_path}: {e}"]
|
|
105
|
+
|
|
106
|
+
visitor = ImportVisitor()
|
|
107
|
+
visitor.visit(tree)
|
|
108
|
+
|
|
109
|
+
for module, lineno in visitor.imports:
|
|
110
|
+
# Check exact match
|
|
111
|
+
base_module = module.split(".")[0]
|
|
112
|
+
if base_module in FORBIDDEN_MODULES:
|
|
113
|
+
warnings.append(
|
|
114
|
+
f"{source_path}:{lineno}: Forbidden import '{module}' - "
|
|
115
|
+
f"This module won't work in WASM."
|
|
116
|
+
)
|
|
117
|
+
# Check os submodules
|
|
118
|
+
elif module in FORBIDDEN_OS_SUBMODULES:
|
|
119
|
+
warnings.append(
|
|
120
|
+
f"{source_path}:{lineno}: Forbidden import '{module}' - "
|
|
121
|
+
f"This function won't work in WASM."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return warnings
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def main() -> int:
|
|
128
|
+
if len(sys.argv) < 2:
|
|
129
|
+
print("Usage: python validate_imports.py <path>", file=sys.stderr)
|
|
130
|
+
print(" path can be a Python file or directory", file=sys.stderr)
|
|
131
|
+
return 1
|
|
132
|
+
|
|
133
|
+
target = Path(sys.argv[1])
|
|
134
|
+
if not target.exists():
|
|
135
|
+
print(f"Error: {target} does not exist", file=sys.stderr)
|
|
136
|
+
return 1
|
|
137
|
+
|
|
138
|
+
# Collect Python files
|
|
139
|
+
if target.is_file():
|
|
140
|
+
if not target.suffix == ".py":
|
|
141
|
+
print(f"Error: {target} is not a Python file", file=sys.stderr)
|
|
142
|
+
return 1
|
|
143
|
+
files = [target]
|
|
144
|
+
else:
|
|
145
|
+
files = list(target.rglob("*.py"))
|
|
146
|
+
|
|
147
|
+
if not files:
|
|
148
|
+
print(f"No Python files found in {target}", file=sys.stderr)
|
|
149
|
+
return 1
|
|
150
|
+
|
|
151
|
+
# Validate all files
|
|
152
|
+
all_warnings: list[str] = []
|
|
153
|
+
for f in files:
|
|
154
|
+
# Skip test files and __pycache__
|
|
155
|
+
if "__pycache__" in str(f) or "test" in f.stem.lower():
|
|
156
|
+
continue
|
|
157
|
+
warnings = validate_imports(f)
|
|
158
|
+
all_warnings.extend(warnings)
|
|
159
|
+
|
|
160
|
+
if all_warnings:
|
|
161
|
+
print(f"Found {len(all_warnings)} potential issues:\n")
|
|
162
|
+
for warning in all_warnings:
|
|
163
|
+
print(f" - {warning}")
|
|
164
|
+
print(
|
|
165
|
+
"\nNote: Some of these may work with polyfills or alternatives. "
|
|
166
|
+
"Review each import carefully."
|
|
167
|
+
)
|
|
168
|
+
return 1
|
|
169
|
+
else:
|
|
170
|
+
print(f"Validated {len(files)} file(s) - no forbidden imports found.")
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
sys.exit(main())
|