pybolo 0.1.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.
- pybolo-0.1.0/LICENSE +21 -0
- pybolo-0.1.0/MANIFEST.in +3 -0
- pybolo-0.1.0/PKG-INFO +156 -0
- pybolo-0.1.0/README.md +125 -0
- pybolo-0.1.0/pyproject.toml +73 -0
- pybolo-0.1.0/setup.cfg +4 -0
- pybolo-0.1.0/src/bolo/__init__.py +13 -0
- pybolo-0.1.0/src/bolo/_version.py +2 -0
- pybolo-0.1.0/src/bolo/api.py +317 -0
- pybolo-0.1.0/src/bolo/cli.py +78 -0
- pybolo-0.1.0/src/bolo/py.typed +0 -0
- pybolo-0.1.0/src/pybolo.egg-info/PKG-INFO +156 -0
- pybolo-0.1.0/src/pybolo.egg-info/SOURCES.txt +16 -0
- pybolo-0.1.0/src/pybolo.egg-info/dependency_links.txt +1 -0
- pybolo-0.1.0/src/pybolo.egg-info/entry_points.txt +2 -0
- pybolo-0.1.0/src/pybolo.egg-info/requires.txt +11 -0
- pybolo-0.1.0/src/pybolo.egg-info/top_level.txt +1 -0
- pybolo-0.1.0/tests/test_pipeline.py +1 -0
pybolo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Illinois CreateLab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pybolo-0.1.0/MANIFEST.in
ADDED
pybolo-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pybolo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight library of verified inference pipelines for HuggingFace models.
|
|
5
|
+
Author: Illinois CreateLab
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/illinoisdata/Bolo
|
|
8
|
+
Project-URL: Repository, https://github.com/illinoisdata/Bolo
|
|
9
|
+
Project-URL: Issues, https://github.com/illinoisdata/Bolo/issues
|
|
10
|
+
Keywords: huggingface,inference,pipeline,verified,curated,model-registry,llm-agents
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: jinja2>=3.0
|
|
22
|
+
Provides-Extra: hf
|
|
23
|
+
Requires-Dist: transformers>=4.40; extra == "hf"
|
|
24
|
+
Requires-Dist: torch>=2.0; extra == "hf"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://github.com/illinoisdata/bolo/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/illinoisdata/bolo.svg?color=blue"></a>
|
|
34
|
+
<a href="https://github.com/illinoisdata/bolo-templates/releases"><img alt="bolo-templates release" src="https://img.shields.io/github/v/release/illinoisdata/bolo-templates?include_prereleases&label=templates"></a>
|
|
35
|
+
<a href="https://pypi.org/project/pybolo"><img alt="PyPI" src="https://img.shields.io/pypi/v/pybolo.svg"></a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="assets/logo.png" alt="bolo logo" width="180">
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<h1 align="center">Bolo: Curated, Verified, and Ready-to-run Inference Pipelines for HuggingFace Models</h1>
|
|
43
|
+
|
|
44
|
+
**Bolo** is a lightweight Python library that gives you curated, verified, and ready-to-run inference pipelines for HuggingFace models with ZERO efforts.
|
|
45
|
+
|
|
46
|
+
📦 **Curated templates**: every supported model ships with a tested Jinja2 inference template maintained by the Illinois CreateLab team.
|
|
47
|
+
|
|
48
|
+
🔒 **Isolated venvs**: each model runs inside its own `uv`-managed virtual environment, so dependency conflicts between models are impossible.
|
|
49
|
+
|
|
50
|
+
⚡ **One-call API**: `bolo.pipeline(repo_id, device="cuda", ...)` conducts inference in one API call, simpler than HuggingFace two-stages `pipeline` API.
|
|
51
|
+
|
|
52
|
+
🖥️ **CLI included**: a `bolo` command lets you manage venvs and run inference directly from the shell.
|
|
53
|
+
|
|
54
|
+
🌐 **Auto-fetching templates**: template bundles are downloaded on first use from the [bolo-templates](https://github.com/illinoisdata/bolo-templates) GitHub release and cached locally — no manual setup needed.
|
|
55
|
+
|
|
56
|
+
## Quick demo
|
|
57
|
+
|
|
58
|
+
Install bolo:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install pybolo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run inference with two lines of Python:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import bolo
|
|
68
|
+
|
|
69
|
+
result = bolo.pipeline("<REPO_ID>", device="cuda:0")
|
|
70
|
+
print(result)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Before running inference you can inspect what parameters the template accepts:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
bolo.list_params("<REPO_ID>")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
And manage venvs explicitly:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
python_bin = bolo.create_a_venv("<REPO_ID>")
|
|
83
|
+
# ... activate the created venv and do inference ...
|
|
84
|
+
bolo.remove_venv("<REPO_ID>")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## CLI
|
|
88
|
+
|
|
89
|
+
The `bolo` command mirrors the Python API from your shell.
|
|
90
|
+
|
|
91
|
+
**Create an isolated venv for a model:**
|
|
92
|
+
```bash
|
|
93
|
+
bolo create-venv <REPO_ID>
|
|
94
|
+
bolo create-venv <REPO_ID> --venv-path /path/to/venv
|
|
95
|
+
# then activate the venv
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Run inference:**
|
|
99
|
+
```bash
|
|
100
|
+
bolo run <REPO_ID> device=cuda:0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Pre-download the templates cache (optional, useful on air-gapped machines):**
|
|
104
|
+
```bash
|
|
105
|
+
bolo fetch-templates
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How does bolo work?
|
|
109
|
+
|
|
110
|
+
bolo separates *what to run* (the Jinja2 template) from *where to run it* (the model's isolated venv).
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
flowchart LR
|
|
114
|
+
A[User calls bolo.pipeline] --> B[Fetch / load templates]
|
|
115
|
+
B --> C[Render Jinja2 template\nwith user params]
|
|
116
|
+
C --> D[Execute rendered script\ninside model venv]
|
|
117
|
+
D --> E[Return RESULT]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
1. **Templates** — stored in the [bolo-templates](https://github.com/illinoisdata/bolo-templates) release bundle. Each model folder contains a `template.j2` (the inference script template) and a `requirements.txt` (the model's exact dependencies). Templates are downloaded once and cached at `~/.cache/bolo/templates/`.
|
|
121
|
+
|
|
122
|
+
2. **Venvs** — created with `uv venv` + `uv pip install -r requirements.txt`. Every model gets its own venv so you can safely use models with conflicting PyTorch or CUDA versions side-by-side.
|
|
123
|
+
|
|
124
|
+
3. **Rendering** — template parameters are collected from the leading `{% set key = default %}` blocks in each `template.j2`. `bolo list_params` shows you every knob with its type and default value.
|
|
125
|
+
|
|
126
|
+
4. **Execution** — the rendered script is executed and its `RESULT` variable is returned to the caller.
|
|
127
|
+
|
|
128
|
+
## Install from source
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git clone https://github.com/illinoisdata/Bolo.git
|
|
132
|
+
cd Bolo
|
|
133
|
+
pip install -e .
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Custom templates directory
|
|
137
|
+
|
|
138
|
+
Set `BOLO_TEMPLATES_DIR` to point bolo at your own templates folder:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
export BOLO_TEMPLATES_DIR=/path/to/my/templates
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Platforms
|
|
145
|
+
`bolo` is developed and tested on Red Hat Enterprise Linux, with CUDA version 12.8. So all dependencies (`torch`-related) assume the `cu128`.
|
|
146
|
+
|
|
147
|
+
## Contributing
|
|
148
|
+
|
|
149
|
+
Contributions are welcome! Please open an issue or pull request on [GitHub](https://github.com/illinoisdata/bolo/issues).
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
154
|
+
|
|
155
|
+
## Acknowledgement
|
|
156
|
+
- Thanks Jojo and her sister for designing the mascot.
|
pybolo-0.1.0/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/illinoisdata/bolo/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/illinoisdata/bolo.svg?color=blue"></a>
|
|
3
|
+
<a href="https://github.com/illinoisdata/bolo-templates/releases"><img alt="bolo-templates release" src="https://img.shields.io/github/v/release/illinoisdata/bolo-templates?include_prereleases&label=templates"></a>
|
|
4
|
+
<a href="https://pypi.org/project/pybolo"><img alt="PyPI" src="https://img.shields.io/pypi/v/pybolo.svg"></a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="assets/logo.png" alt="bolo logo" width="180">
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<h1 align="center">Bolo: Curated, Verified, and Ready-to-run Inference Pipelines for HuggingFace Models</h1>
|
|
12
|
+
|
|
13
|
+
**Bolo** is a lightweight Python library that gives you curated, verified, and ready-to-run inference pipelines for HuggingFace models with ZERO efforts.
|
|
14
|
+
|
|
15
|
+
📦 **Curated templates**: every supported model ships with a tested Jinja2 inference template maintained by the Illinois CreateLab team.
|
|
16
|
+
|
|
17
|
+
🔒 **Isolated venvs**: each model runs inside its own `uv`-managed virtual environment, so dependency conflicts between models are impossible.
|
|
18
|
+
|
|
19
|
+
⚡ **One-call API**: `bolo.pipeline(repo_id, device="cuda", ...)` conducts inference in one API call, simpler than HuggingFace two-stages `pipeline` API.
|
|
20
|
+
|
|
21
|
+
🖥️ **CLI included**: a `bolo` command lets you manage venvs and run inference directly from the shell.
|
|
22
|
+
|
|
23
|
+
🌐 **Auto-fetching templates**: template bundles are downloaded on first use from the [bolo-templates](https://github.com/illinoisdata/bolo-templates) GitHub release and cached locally — no manual setup needed.
|
|
24
|
+
|
|
25
|
+
## Quick demo
|
|
26
|
+
|
|
27
|
+
Install bolo:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install pybolo
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run inference with two lines of Python:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import bolo
|
|
37
|
+
|
|
38
|
+
result = bolo.pipeline("<REPO_ID>", device="cuda:0")
|
|
39
|
+
print(result)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Before running inference you can inspect what parameters the template accepts:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
bolo.list_params("<REPO_ID>")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
And manage venvs explicitly:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
python_bin = bolo.create_a_venv("<REPO_ID>")
|
|
52
|
+
# ... activate the created venv and do inference ...
|
|
53
|
+
bolo.remove_venv("<REPO_ID>")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI
|
|
57
|
+
|
|
58
|
+
The `bolo` command mirrors the Python API from your shell.
|
|
59
|
+
|
|
60
|
+
**Create an isolated venv for a model:**
|
|
61
|
+
```bash
|
|
62
|
+
bolo create-venv <REPO_ID>
|
|
63
|
+
bolo create-venv <REPO_ID> --venv-path /path/to/venv
|
|
64
|
+
# then activate the venv
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Run inference:**
|
|
68
|
+
```bash
|
|
69
|
+
bolo run <REPO_ID> device=cuda:0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Pre-download the templates cache (optional, useful on air-gapped machines):**
|
|
73
|
+
```bash
|
|
74
|
+
bolo fetch-templates
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## How does bolo work?
|
|
78
|
+
|
|
79
|
+
bolo separates *what to run* (the Jinja2 template) from *where to run it* (the model's isolated venv).
|
|
80
|
+
|
|
81
|
+
```mermaid
|
|
82
|
+
flowchart LR
|
|
83
|
+
A[User calls bolo.pipeline] --> B[Fetch / load templates]
|
|
84
|
+
B --> C[Render Jinja2 template\nwith user params]
|
|
85
|
+
C --> D[Execute rendered script\ninside model venv]
|
|
86
|
+
D --> E[Return RESULT]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
1. **Templates** — stored in the [bolo-templates](https://github.com/illinoisdata/bolo-templates) release bundle. Each model folder contains a `template.j2` (the inference script template) and a `requirements.txt` (the model's exact dependencies). Templates are downloaded once and cached at `~/.cache/bolo/templates/`.
|
|
90
|
+
|
|
91
|
+
2. **Venvs** — created with `uv venv` + `uv pip install -r requirements.txt`. Every model gets its own venv so you can safely use models with conflicting PyTorch or CUDA versions side-by-side.
|
|
92
|
+
|
|
93
|
+
3. **Rendering** — template parameters are collected from the leading `{% set key = default %}` blocks in each `template.j2`. `bolo list_params` shows you every knob with its type and default value.
|
|
94
|
+
|
|
95
|
+
4. **Execution** — the rendered script is executed and its `RESULT` variable is returned to the caller.
|
|
96
|
+
|
|
97
|
+
## Install from source
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/illinoisdata/Bolo.git
|
|
101
|
+
cd Bolo
|
|
102
|
+
pip install -e .
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Custom templates directory
|
|
106
|
+
|
|
107
|
+
Set `BOLO_TEMPLATES_DIR` to point bolo at your own templates folder:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export BOLO_TEMPLATES_DIR=/path/to/my/templates
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Platforms
|
|
114
|
+
`bolo` is developed and tested on Red Hat Enterprise Linux, with CUDA version 12.8. So all dependencies (`torch`-related) assume the `cu128`.
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Contributions are welcome! Please open an issue or pull request on [GitHub](https://github.com/illinoisdata/bolo/issues).
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
123
|
+
|
|
124
|
+
## Acknowledgement
|
|
125
|
+
- Thanks Jojo and her sister for designing the mascot.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pybolo"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A lightweight library of verified inference pipelines for HuggingFace models."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Illinois CreateLab" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"huggingface",
|
|
17
|
+
"inference",
|
|
18
|
+
"pipeline",
|
|
19
|
+
"verified",
|
|
20
|
+
"curated",
|
|
21
|
+
"model-registry",
|
|
22
|
+
"llm-agents"
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Intended Audience :: Science/Research",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
31
|
+
"Typing :: Typed"
|
|
32
|
+
]
|
|
33
|
+
dependencies = [
|
|
34
|
+
"jinja2>=3.0"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
hf = [
|
|
39
|
+
"transformers>=4.40",
|
|
40
|
+
"torch>=2.0"
|
|
41
|
+
]
|
|
42
|
+
dev = [
|
|
43
|
+
"build>=1.2",
|
|
44
|
+
"pytest>=8.0",
|
|
45
|
+
"twine>=5.0",
|
|
46
|
+
"ruff>=0.6"
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
bolo = "bolo.cli:main"
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://github.com/illinoisdata/Bolo"
|
|
54
|
+
Repository = "https://github.com/illinoisdata/Bolo"
|
|
55
|
+
Issues = "https://github.com/illinoisdata/Bolo/issues"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools]
|
|
58
|
+
package-dir = {"" = "src"}
|
|
59
|
+
include-package-data = true
|
|
60
|
+
|
|
61
|
+
[tool.setuptools.packages.find]
|
|
62
|
+
where = ["src"]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.package-data]
|
|
65
|
+
bolo = ["py.typed"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
line-length = 100
|
|
69
|
+
src = ["src", "tests"]
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
addopts = "-q"
|
|
73
|
+
testpaths = ["tests"]
|
pybolo-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""BoloPipe: a lightweight registry for verified HuggingFace inference pipelines."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .api import list_params, create_a_venv, pipeline, remove_venv, fetch_templates
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"__version__",
|
|
8
|
+
"list_params",
|
|
9
|
+
"create_a_venv",
|
|
10
|
+
"pipeline",
|
|
11
|
+
"remove_venv",
|
|
12
|
+
"fetch_templates",
|
|
13
|
+
]
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import ast
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import tarfile
|
|
8
|
+
import urllib.request
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from jinja2 import Environment, StrictUndefined
|
|
11
|
+
|
|
12
|
+
from ._version import __version__, __templates_version__
|
|
13
|
+
|
|
14
|
+
_TEMPLATES_RELEASE_URL = (
|
|
15
|
+
"https://github.com/illinoisdata/bolo-templates/releases/download/"
|
|
16
|
+
"v{version}/templates.tar.gz"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InferClient:
|
|
21
|
+
_instance = None
|
|
22
|
+
_torch_cu128_url = "https://download.pytorch.org/whl/cu128"
|
|
23
|
+
|
|
24
|
+
def __new__(cls, *args, **kwargs):
|
|
25
|
+
if cls._instance is None:
|
|
26
|
+
cls._instance = super().__new__(cls)
|
|
27
|
+
return cls._instance
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
if getattr(self, "_initialized", False):
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
self.db_dir = self._ensure_templates()
|
|
34
|
+
self.supported_models = set()
|
|
35
|
+
|
|
36
|
+
for rid_dir in self.db_dir.iterdir():
|
|
37
|
+
if rid_dir.is_dir():
|
|
38
|
+
repo_name = rid_dir.name
|
|
39
|
+
repo_id = "/".join(repo_name.split('__SEP__'))
|
|
40
|
+
self.supported_models.add(repo_id)
|
|
41
|
+
|
|
42
|
+
self._initialized = True
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _ensure_templates() -> Path:
|
|
46
|
+
env_path = os.environ.get("BOLO_TEMPLATES_DIR")
|
|
47
|
+
if env_path:
|
|
48
|
+
return Path(env_path).expanduser()
|
|
49
|
+
|
|
50
|
+
local = Path(__file__).parent / "templates"
|
|
51
|
+
if local.exists() and any(local.iterdir()):
|
|
52
|
+
return local
|
|
53
|
+
|
|
54
|
+
cache_root = Path.home() / ".cache" / "bolo" / "templates"
|
|
55
|
+
cache = cache_root / __templates_version__
|
|
56
|
+
if cache.exists() and any(cache.iterdir()):
|
|
57
|
+
return cache
|
|
58
|
+
|
|
59
|
+
url = _TEMPLATES_RELEASE_URL.format(version=__templates_version__)
|
|
60
|
+
print(f"fetching bolo templates v{__templates_version__} from {url} ...", flush=True)
|
|
61
|
+
|
|
62
|
+
cache_root.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
tmp = cache_root / f".tmp.{__templates_version__}"
|
|
64
|
+
if tmp.exists():
|
|
65
|
+
shutil.rmtree(tmp)
|
|
66
|
+
tmp.mkdir()
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
with urllib.request.urlopen(url) as resp, tarfile.open(fileobj=resp, mode="r|gz") as tar:
|
|
70
|
+
tar.extractall(tmp)
|
|
71
|
+
tmp.rename(cache)
|
|
72
|
+
except Exception:
|
|
73
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
return cache
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _bolo_install_args() -> list[str]:
|
|
80
|
+
"""Args to pass to `uv pip install` when seeding bolo into a model venv."""
|
|
81
|
+
project_root = Path(__file__).resolve().parent.parent.parent
|
|
82
|
+
if (project_root / "pyproject.toml").exists():
|
|
83
|
+
return ["-e", str(project_root)]
|
|
84
|
+
return ["bolo"]
|
|
85
|
+
|
|
86
|
+
def query_a_repo(self, repo_id: str):
|
|
87
|
+
if repo_id not in self.supported_models:
|
|
88
|
+
print(f"bolo does not support {repo_id}")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
def create_venv(self, repo_id: str, venv_path: str | Path | None = None):
|
|
94
|
+
repo_name = "__SEP__".join(repo_id.split('/'))
|
|
95
|
+
|
|
96
|
+
if not venv_path:
|
|
97
|
+
venv_path = self.db_dir / "tmp_venv" / repo_name
|
|
98
|
+
print(f"you do not specify the venv path for {repo_id}, we create by default at {venv_path}")
|
|
99
|
+
|
|
100
|
+
if venv_path.exists():
|
|
101
|
+
print(f"ven for {repo_id} has been already existed")
|
|
102
|
+
return venv_path / "bin" / "python", True
|
|
103
|
+
|
|
104
|
+
os.makedirs(venv_path, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
req_txt = self.db_dir / repo_name / "requirements.txt"
|
|
107
|
+
|
|
108
|
+
cmd1 = ['uv', 'venv', str(venv_path), '--python', '3.13']
|
|
109
|
+
try:
|
|
110
|
+
subprocess.run(cmd1, check=True, text=True, capture_output=True)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print(f"failed to create uv venv for {repo_id}, due to the following error : ")
|
|
113
|
+
print(e.stderr)
|
|
114
|
+
return "", False
|
|
115
|
+
|
|
116
|
+
python_bin = venv_path / "bin" / "python"
|
|
117
|
+
cmd2 = ['uv', 'pip', 'install', '-r', str(req_txt), '--python', str(python_bin), '--extra-index-url', self._torch_cu128_url]
|
|
118
|
+
try:
|
|
119
|
+
subprocess.run(cmd2, check=True, text=True, capture_output=True)
|
|
120
|
+
except subprocess.CalledProcessError as e:
|
|
121
|
+
print(f"failed to install requirements.txt for {repo_id}, due to the following error : ")
|
|
122
|
+
print(e.stderr)
|
|
123
|
+
return "", False
|
|
124
|
+
|
|
125
|
+
cmd3 = ['uv', 'pip', 'install', *self._bolo_install_args(), '--python', str(python_bin)]
|
|
126
|
+
try:
|
|
127
|
+
subprocess.run(cmd3, check=True, text=True, capture_output=True)
|
|
128
|
+
except subprocess.CalledProcessError as e:
|
|
129
|
+
print(f"failed to install bolo into the venv for {repo_id}, due to the following error : ")
|
|
130
|
+
print(e.stderr)
|
|
131
|
+
return "", False
|
|
132
|
+
|
|
133
|
+
print(f"installed requirements.txt and bolo in a venv : {python_bin}!")
|
|
134
|
+
return python_bin, True
|
|
135
|
+
|
|
136
|
+
def remove_venv(self, repo_id: str, venv_path: str | Path | None = None):
|
|
137
|
+
repo_name = "__SEP__".join(repo_id.split('/'))
|
|
138
|
+
if not venv_path:
|
|
139
|
+
venv_path = self.db_dir / "tmp_venv" / repo_name
|
|
140
|
+
shutil.rmtree(venv_path, ignore_errors=True)
|
|
141
|
+
|
|
142
|
+
def render_template(self, repo_id: str, params: dict):
|
|
143
|
+
rid_name = "__SEP__".join(repo_id.split('/'))
|
|
144
|
+
template_path = self.db_dir / rid_name / "template.j2"
|
|
145
|
+
|
|
146
|
+
params["repo_id"] = repo_id
|
|
147
|
+
|
|
148
|
+
with open(template_path, 'r', encoding="utf-8") as f:
|
|
149
|
+
template = f.read().strip()
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
rendered_script = Environment(undefined=StrictUndefined).from_string(template).render(**params)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
print(f"failed to render template for {repo_id}m due to the following error : ")
|
|
155
|
+
print(e)
|
|
156
|
+
return "", False
|
|
157
|
+
|
|
158
|
+
return rendered_script, True
|
|
159
|
+
|
|
160
|
+
def list_params(self, repo_id: str):
|
|
161
|
+
rid_name = "__SEP__".join(repo_id.split('/'))
|
|
162
|
+
template_path = self.db_dir / rid_name / "template.j2"
|
|
163
|
+
|
|
164
|
+
with open(template_path, 'r', encoding="utf-8") as f:
|
|
165
|
+
template = f.read().strip()
|
|
166
|
+
|
|
167
|
+
set_pattern = re.compile(r'^\s*\{\%\s*set\s+([a-zA-Z_]\w*)\s*=\s*(.*?)\s*\%\}\s*$')
|
|
168
|
+
lines = template.splitlines()
|
|
169
|
+
params = []
|
|
170
|
+
started = False
|
|
171
|
+
|
|
172
|
+
for line in lines:
|
|
173
|
+
matched = set_pattern.match(line)
|
|
174
|
+
if matched:
|
|
175
|
+
started = True
|
|
176
|
+
name = matched.group(1)
|
|
177
|
+
raw_default = matched.group(2).strip()
|
|
178
|
+
|
|
179
|
+
lowered = raw_default.lower()
|
|
180
|
+
if lowered == "true":
|
|
181
|
+
default = True
|
|
182
|
+
elif lowered == "false":
|
|
183
|
+
default = False
|
|
184
|
+
elif lowered in {"none", "null"}:
|
|
185
|
+
default = None
|
|
186
|
+
else:
|
|
187
|
+
try:
|
|
188
|
+
default = ast.literal_eval(raw_default)
|
|
189
|
+
except Exception:
|
|
190
|
+
default = raw_default
|
|
191
|
+
|
|
192
|
+
if name == "repo_id" or name == "output_dir":
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
params.append(
|
|
196
|
+
{
|
|
197
|
+
"name": name,
|
|
198
|
+
"default": default,
|
|
199
|
+
"type": type(default).__name__,
|
|
200
|
+
"raw_default": raw_default,
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
if started:
|
|
206
|
+
break
|
|
207
|
+
|
|
208
|
+
if not any(p["name"] == "device" for p in params):
|
|
209
|
+
params.insert(0, {
|
|
210
|
+
"name": "device",
|
|
211
|
+
"default": "<required>",
|
|
212
|
+
"type": "str",
|
|
213
|
+
"raw_default": "<required>",
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
result = {
|
|
217
|
+
"num_params": len(params),
|
|
218
|
+
"params": params,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
def _to_display(v):
|
|
222
|
+
if isinstance(v, (dict, list)):
|
|
223
|
+
return json.dumps(v, ensure_ascii=False)
|
|
224
|
+
return str(v)
|
|
225
|
+
|
|
226
|
+
headers = ["name", "type", "default", "raw_default"]
|
|
227
|
+
rows = [
|
|
228
|
+
[p["name"], p["type"], p["default"], p["raw_default"]]
|
|
229
|
+
for p in params
|
|
230
|
+
]
|
|
231
|
+
widths = [len(h) for h in headers]
|
|
232
|
+
for row in rows:
|
|
233
|
+
for i, cell in enumerate(row):
|
|
234
|
+
widths[i] = max(widths[i], len(_to_display(cell)))
|
|
235
|
+
|
|
236
|
+
print(f"repo_id: {_to_display(repo_id)}")
|
|
237
|
+
print(f"num_params: {_to_display(len(params))}")
|
|
238
|
+
if not rows:
|
|
239
|
+
print("(no top-level Jinja set variables found)")
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
sep = " | "
|
|
243
|
+
header_line = sep.join(headers[i].ljust(widths[i]) for i in range(len(headers)))
|
|
244
|
+
divider = "-+-".join("-" * widths[i] for i in range(len(headers)))
|
|
245
|
+
print(header_line)
|
|
246
|
+
print(divider)
|
|
247
|
+
for row in rows:
|
|
248
|
+
print(sep.join(_to_display(row[i]).ljust(widths[i]) for i in range(len(headers))))
|
|
249
|
+
|
|
250
|
+
return result
|
|
251
|
+
|
|
252
|
+
def run_inference(self, code: str):
|
|
253
|
+
try:
|
|
254
|
+
ns = {"__name__": "__api__"}
|
|
255
|
+
exec(code, ns, ns)
|
|
256
|
+
if "RESULT" not in ns:
|
|
257
|
+
raise RuntimeError("RESULT not defined")
|
|
258
|
+
return ns["RESULT"]
|
|
259
|
+
except Exception:
|
|
260
|
+
raise
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def list_params(repo_id: str):
|
|
264
|
+
client = InferClient()
|
|
265
|
+
|
|
266
|
+
is_exist = client.query_a_repo(repo_id)
|
|
267
|
+
if not is_exist:
|
|
268
|
+
raise ValueError(f"bolo does not support {repo_id}")
|
|
269
|
+
|
|
270
|
+
_ = client.list_params(repo_id)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def create_a_venv(repo_id: str, venv_path: str | Path | None = None):
|
|
274
|
+
client = InferClient()
|
|
275
|
+
|
|
276
|
+
is_exist = client.query_a_repo(repo_id)
|
|
277
|
+
if not is_exist:
|
|
278
|
+
raise ValueError(f"bolo does not support {repo_id}")
|
|
279
|
+
|
|
280
|
+
if venv_path is not None and os.path.exists(venv_path):
|
|
281
|
+
print(f"already created venv at : {venv_path}")
|
|
282
|
+
return venv_path
|
|
283
|
+
|
|
284
|
+
venv_path, staus = client.create_venv(repo_id, venv_path)
|
|
285
|
+
|
|
286
|
+
if not staus:
|
|
287
|
+
raise RuntimeError(f"failed to create venv for {repo_id}")
|
|
288
|
+
|
|
289
|
+
return venv_path
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def pipeline(repo_id: str, **kwargs):
|
|
293
|
+
client = InferClient()
|
|
294
|
+
|
|
295
|
+
is_exist = client.query_a_repo(repo_id)
|
|
296
|
+
if not is_exist:
|
|
297
|
+
raise ValueError(f"bolo does not support {repo_id}")
|
|
298
|
+
|
|
299
|
+
_ = client.list_params(repo_id)
|
|
300
|
+
|
|
301
|
+
code, is_success = client.render_template(repo_id, kwargs)
|
|
302
|
+
if not is_success:
|
|
303
|
+
raise RuntimeError(f"failed to render template for {repo_id}")
|
|
304
|
+
|
|
305
|
+
return client.run_inference(code)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def remove_venv(repo_id: str):
|
|
309
|
+
client = InferClient()
|
|
310
|
+
client.remove_venv(repo_id)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def fetch_templates() -> Path:
|
|
314
|
+
"""Pre-populate the bolo templates cache (downloads from the GitHub release if needed)."""
|
|
315
|
+
path = InferClient._ensure_templates()
|
|
316
|
+
print(f"templates available at : {path}")
|
|
317
|
+
return path
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Sequence
|
|
6
|
+
|
|
7
|
+
from .api import InferClient, create_a_venv, pipeline, remove_venv, fetch_templates
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
11
|
+
parser = argparse.ArgumentParser(prog="bolopipe", description="Inspect BoloPipe registry records")
|
|
12
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
13
|
+
|
|
14
|
+
create_venv_parser = sub.add_parser("create-venv", help="Create a venv for a model")
|
|
15
|
+
create_venv_parser.add_argument("repo_id", help="HuggingFace repository id")
|
|
16
|
+
create_venv_parser.add_argument("--venv-path", default=None, help="Path to create the venv")
|
|
17
|
+
|
|
18
|
+
run_parser = sub.add_parser("run", help="Run inference for a model")
|
|
19
|
+
run_parser.add_argument("repo_id", help="HuggingFace repository id")
|
|
20
|
+
run_parser.add_argument("params", nargs="*", metavar="KEY=VALUE", help="Template parameters")
|
|
21
|
+
|
|
22
|
+
remove_venv_parser = sub.add_parser("remove-venv", help="Remove the venv for a model")
|
|
23
|
+
remove_venv_parser.add_argument("repo_id", help="HuggingFace repository id")
|
|
24
|
+
|
|
25
|
+
sub.add_parser("fetch-templates", help="Download the bolo templates cache for this version")
|
|
26
|
+
|
|
27
|
+
return parser
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
31
|
+
parser = build_parser()
|
|
32
|
+
args = parser.parse_args(argv)
|
|
33
|
+
|
|
34
|
+
if args.command == "create-venv":
|
|
35
|
+
create_a_venv(args.repo_id, args.venv_path)
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
if args.command == "run":
|
|
39
|
+
client = InferClient()
|
|
40
|
+
repo_name = "__SEP__".join(args.repo_id.split("/"))
|
|
41
|
+
venv_dir = client.db_dir / "tmp_venv" / repo_name
|
|
42
|
+
venv_python = venv_dir / "bin" / "python"
|
|
43
|
+
venv_bolo = venv_dir / "bin" / "bolo"
|
|
44
|
+
|
|
45
|
+
if not venv_python.exists():
|
|
46
|
+
parser.error(
|
|
47
|
+
f"no venv found at {venv_dir}; "
|
|
48
|
+
f"run `bolo create-venv {args.repo_id}` first"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if Path(sys.executable).resolve() != venv_python.resolve():
|
|
52
|
+
return subprocess.call([str(venv_bolo), "run", args.repo_id, *args.params])
|
|
53
|
+
|
|
54
|
+
kwargs = {}
|
|
55
|
+
for item in args.params:
|
|
56
|
+
if "=" not in item:
|
|
57
|
+
parser.error(f"invalid parameter {item!r}, expected KEY=VALUE")
|
|
58
|
+
k, _, v = item.partition("=")
|
|
59
|
+
kwargs[k] = v
|
|
60
|
+
result = pipeline(args.repo_id, **kwargs)
|
|
61
|
+
if result is not None:
|
|
62
|
+
print(result)
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
if args.command == "remove-venv":
|
|
66
|
+
remove_venv(args.repo_id)
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
if args.command == "fetch-templates":
|
|
70
|
+
fetch_templates()
|
|
71
|
+
return 0
|
|
72
|
+
|
|
73
|
+
parser.error(f"Unknown command: {args.command}")
|
|
74
|
+
return 2
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__": # pragma: no cover
|
|
78
|
+
raise SystemExit(main())
|
|
File without changes
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pybolo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight library of verified inference pipelines for HuggingFace models.
|
|
5
|
+
Author: Illinois CreateLab
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/illinoisdata/Bolo
|
|
8
|
+
Project-URL: Repository, https://github.com/illinoisdata/Bolo
|
|
9
|
+
Project-URL: Issues, https://github.com/illinoisdata/Bolo/issues
|
|
10
|
+
Keywords: huggingface,inference,pipeline,verified,curated,model-registry,llm-agents
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: jinja2>=3.0
|
|
22
|
+
Provides-Extra: hf
|
|
23
|
+
Requires-Dist: transformers>=4.40; extra == "hf"
|
|
24
|
+
Requires-Dist: torch>=2.0; extra == "hf"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://github.com/illinoisdata/bolo/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/illinoisdata/bolo.svg?color=blue"></a>
|
|
34
|
+
<a href="https://github.com/illinoisdata/bolo-templates/releases"><img alt="bolo-templates release" src="https://img.shields.io/github/v/release/illinoisdata/bolo-templates?include_prereleases&label=templates"></a>
|
|
35
|
+
<a href="https://pypi.org/project/pybolo"><img alt="PyPI" src="https://img.shields.io/pypi/v/pybolo.svg"></a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="assets/logo.png" alt="bolo logo" width="180">
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<h1 align="center">Bolo: Curated, Verified, and Ready-to-run Inference Pipelines for HuggingFace Models</h1>
|
|
43
|
+
|
|
44
|
+
**Bolo** is a lightweight Python library that gives you curated, verified, and ready-to-run inference pipelines for HuggingFace models with ZERO efforts.
|
|
45
|
+
|
|
46
|
+
📦 **Curated templates**: every supported model ships with a tested Jinja2 inference template maintained by the Illinois CreateLab team.
|
|
47
|
+
|
|
48
|
+
🔒 **Isolated venvs**: each model runs inside its own `uv`-managed virtual environment, so dependency conflicts between models are impossible.
|
|
49
|
+
|
|
50
|
+
⚡ **One-call API**: `bolo.pipeline(repo_id, device="cuda", ...)` conducts inference in one API call, simpler than HuggingFace two-stages `pipeline` API.
|
|
51
|
+
|
|
52
|
+
🖥️ **CLI included**: a `bolo` command lets you manage venvs and run inference directly from the shell.
|
|
53
|
+
|
|
54
|
+
🌐 **Auto-fetching templates**: template bundles are downloaded on first use from the [bolo-templates](https://github.com/illinoisdata/bolo-templates) GitHub release and cached locally — no manual setup needed.
|
|
55
|
+
|
|
56
|
+
## Quick demo
|
|
57
|
+
|
|
58
|
+
Install bolo:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install pybolo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run inference with two lines of Python:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import bolo
|
|
68
|
+
|
|
69
|
+
result = bolo.pipeline("<REPO_ID>", device="cuda:0")
|
|
70
|
+
print(result)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Before running inference you can inspect what parameters the template accepts:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
bolo.list_params("<REPO_ID>")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
And manage venvs explicitly:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
python_bin = bolo.create_a_venv("<REPO_ID>")
|
|
83
|
+
# ... activate the created venv and do inference ...
|
|
84
|
+
bolo.remove_venv("<REPO_ID>")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## CLI
|
|
88
|
+
|
|
89
|
+
The `bolo` command mirrors the Python API from your shell.
|
|
90
|
+
|
|
91
|
+
**Create an isolated venv for a model:**
|
|
92
|
+
```bash
|
|
93
|
+
bolo create-venv <REPO_ID>
|
|
94
|
+
bolo create-venv <REPO_ID> --venv-path /path/to/venv
|
|
95
|
+
# then activate the venv
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Run inference:**
|
|
99
|
+
```bash
|
|
100
|
+
bolo run <REPO_ID> device=cuda:0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Pre-download the templates cache (optional, useful on air-gapped machines):**
|
|
104
|
+
```bash
|
|
105
|
+
bolo fetch-templates
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How does bolo work?
|
|
109
|
+
|
|
110
|
+
bolo separates *what to run* (the Jinja2 template) from *where to run it* (the model's isolated venv).
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
flowchart LR
|
|
114
|
+
A[User calls bolo.pipeline] --> B[Fetch / load templates]
|
|
115
|
+
B --> C[Render Jinja2 template\nwith user params]
|
|
116
|
+
C --> D[Execute rendered script\ninside model venv]
|
|
117
|
+
D --> E[Return RESULT]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
1. **Templates** — stored in the [bolo-templates](https://github.com/illinoisdata/bolo-templates) release bundle. Each model folder contains a `template.j2` (the inference script template) and a `requirements.txt` (the model's exact dependencies). Templates are downloaded once and cached at `~/.cache/bolo/templates/`.
|
|
121
|
+
|
|
122
|
+
2. **Venvs** — created with `uv venv` + `uv pip install -r requirements.txt`. Every model gets its own venv so you can safely use models with conflicting PyTorch or CUDA versions side-by-side.
|
|
123
|
+
|
|
124
|
+
3. **Rendering** — template parameters are collected from the leading `{% set key = default %}` blocks in each `template.j2`. `bolo list_params` shows you every knob with its type and default value.
|
|
125
|
+
|
|
126
|
+
4. **Execution** — the rendered script is executed and its `RESULT` variable is returned to the caller.
|
|
127
|
+
|
|
128
|
+
## Install from source
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git clone https://github.com/illinoisdata/Bolo.git
|
|
132
|
+
cd Bolo
|
|
133
|
+
pip install -e .
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Custom templates directory
|
|
137
|
+
|
|
138
|
+
Set `BOLO_TEMPLATES_DIR` to point bolo at your own templates folder:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
export BOLO_TEMPLATES_DIR=/path/to/my/templates
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Platforms
|
|
145
|
+
`bolo` is developed and tested on Red Hat Enterprise Linux, with CUDA version 12.8. So all dependencies (`torch`-related) assume the `cu128`.
|
|
146
|
+
|
|
147
|
+
## Contributing
|
|
148
|
+
|
|
149
|
+
Contributions are welcome! Please open an issue or pull request on [GitHub](https://github.com/illinoisdata/bolo/issues).
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
154
|
+
|
|
155
|
+
## Acknowledgement
|
|
156
|
+
- Thanks Jojo and her sister for designing the mascot.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
src/bolo/__init__.py
|
|
6
|
+
src/bolo/_version.py
|
|
7
|
+
src/bolo/api.py
|
|
8
|
+
src/bolo/cli.py
|
|
9
|
+
src/bolo/py.typed
|
|
10
|
+
src/pybolo.egg-info/PKG-INFO
|
|
11
|
+
src/pybolo.egg-info/SOURCES.txt
|
|
12
|
+
src/pybolo.egg-info/dependency_links.txt
|
|
13
|
+
src/pybolo.egg-info/entry_points.txt
|
|
14
|
+
src/pybolo.egg-info/requires.txt
|
|
15
|
+
src/pybolo.egg-info/top_level.txt
|
|
16
|
+
tests/test_pipeline.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bolo
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# TODO
|