inferkit 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.
- inferkit-0.1.0/.gitignore +41 -0
- inferkit-0.1.0/PKG-INFO +191 -0
- inferkit-0.1.0/README.md +166 -0
- inferkit-0.1.0/examples/.gitignore +1 -0
- inferkit-0.1.0/examples/classification/language_detection.py +59 -0
- inferkit-0.1.0/examples/classification/moderation.py +76 -0
- inferkit-0.1.0/examples/classification/sentiment.py +57 -0
- inferkit-0.1.0/examples/classification/spam_detection.py +60 -0
- inferkit-0.1.0/examples/classification/tone_detection.py +59 -0
- inferkit-0.1.0/examples/classification/topic_categorization.py +59 -0
- inferkit-0.1.0/examples/classification/urgency_triage.py +60 -0
- inferkit-0.1.0/examples/mlx/sentiment.py +57 -0
- inferkit-0.1.0/pyproject.toml +57 -0
- inferkit-0.1.0/src/slmkit/__init__.py +11 -0
- inferkit-0.1.0/src/slmkit/backends/__init__.py +26 -0
- inferkit-0.1.0/src/slmkit/backends/llamacpp.py +59 -0
- inferkit-0.1.0/src/slmkit/backends/mlx.py +63 -0
- inferkit-0.1.0/src/slmkit/backends/onnx.py +1 -0
- inferkit-0.1.0/src/slmkit/cli.py +104 -0
- inferkit-0.1.0/src/slmkit/compiler.py +172 -0
- inferkit-0.1.0/src/slmkit/formats/__init__.py +1 -0
- inferkit-0.1.0/src/slmkit/formats/artifact.py +136 -0
- inferkit-0.1.0/src/slmkit/formats/manifest.py +74 -0
- inferkit-0.1.0/src/slmkit/formats/schema.py +31 -0
- inferkit-0.1.0/src/slmkit/model.py +88 -0
- inferkit-0.1.0/src/slmkit/tasks/__init__.py +26 -0
- inferkit-0.1.0/src/slmkit/tasks/classification.py +41 -0
- inferkit-0.1.0/src/slmkit/tasks/embedding.py +1 -0
- inferkit-0.1.0/src/slmkit/tasks/extraction.py +1 -0
- inferkit-0.1.0/src/slmkit/tasks/generation.py +1 -0
- inferkit-0.1.0/tests/__init__.py +0 -0
- inferkit-0.1.0/tests/test_artifact.py +118 -0
- inferkit-0.1.0/tests/test_classification.py +53 -0
- inferkit-0.1.0/tests/test_manifest.py +57 -0
- inferkit-0.1.0/tests/test_schema.py +51 -0
- inferkit-0.1.0/uv.lock +899 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
|
|
12
|
+
# IDE
|
|
13
|
+
.idea/
|
|
14
|
+
.vscode/
|
|
15
|
+
*.swp
|
|
16
|
+
*.swo
|
|
17
|
+
|
|
18
|
+
# OS
|
|
19
|
+
.DS_Store
|
|
20
|
+
Thumbs.db
|
|
21
|
+
|
|
22
|
+
# Caches and temp files
|
|
23
|
+
.cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
*.tmp
|
|
28
|
+
*.bak
|
|
29
|
+
|
|
30
|
+
# Environment
|
|
31
|
+
.env
|
|
32
|
+
|
|
33
|
+
# Artifacts
|
|
34
|
+
*.slm
|
|
35
|
+
|
|
36
|
+
# Development notes
|
|
37
|
+
.dev/
|
|
38
|
+
|
|
39
|
+
# Github Copilot
|
|
40
|
+
.github/instructions/
|
|
41
|
+
.github/copilot-instructions.md
|
inferkit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inferkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Embeddable inference engine for small language models — the SQLite for AI
|
|
5
|
+
Author-email: Milad Olad <milad.olad@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: ai,edge,embedding,inference,llm,local,offline,slm
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: huggingface-hub>=0.25
|
|
19
|
+
Requires-Dist: llama-cpp-python>=0.3
|
|
20
|
+
Requires-Dist: tokenizers>=0.21
|
|
21
|
+
Requires-Dist: typer>=0.15
|
|
22
|
+
Provides-Extra: mlx
|
|
23
|
+
Requires-Dist: mlx-lm>=0.31; extra == 'mlx'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# slmkit
|
|
27
|
+
|
|
28
|
+
Package small language models into self-contained, embeddable `.slm` artifacts.
|
|
29
|
+
|
|
30
|
+
**SQLite for AI** — one file, zero config, zero internet, 5ms inference.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import slmkit
|
|
34
|
+
|
|
35
|
+
classifier = slmkit.load("sentiment.slm")
|
|
36
|
+
result = classifier("The aurora was breathtaking")
|
|
37
|
+
# → {"label": "positive", "confidence": 1.0}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install slmkit
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Requires Python 3.11+. The `llama-cpp-python` dependency compiles C++ on first install, which may take a few minutes.
|
|
47
|
+
|
|
48
|
+
## Quickstart
|
|
49
|
+
|
|
50
|
+
### 1. Compile a model
|
|
51
|
+
|
|
52
|
+
Package a pre-quantized GGUF model from HuggingFace into a `.slm` artifact:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
slm compile \
|
|
56
|
+
--source Qwen/Qwen2.5-0.5B-Instruct-GGUF \
|
|
57
|
+
--task classification \
|
|
58
|
+
--labels "positive,negative,neutral" \
|
|
59
|
+
--quantize q4_k_m \
|
|
60
|
+
--output sentiment.slm
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This downloads the model (~490 MB), bakes in a task-specific prompt, and packages everything into a single file.
|
|
64
|
+
|
|
65
|
+
You can also pass a local `.gguf` file as `--source` instead of a HuggingFace repo.
|
|
66
|
+
|
|
67
|
+
### 2. Load and run
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import slmkit
|
|
71
|
+
|
|
72
|
+
with slmkit.load("sentiment.slm") as model:
|
|
73
|
+
result = model("The midnight sun over Tromsø was unforgettable")
|
|
74
|
+
print(result)
|
|
75
|
+
# {"label": "positive", "confidence": 1.0}
|
|
76
|
+
|
|
77
|
+
results = model.batch([
|
|
78
|
+
"The recipe was a disaster",
|
|
79
|
+
"I finally fixed the timing belt myself",
|
|
80
|
+
])
|
|
81
|
+
# [{"label": "negative", ...}, {"label": "positive", ...}]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Test and inspect
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Run inference from the CLI
|
|
88
|
+
slm test sentiment.slm --input "The coral reef was thriving"
|
|
89
|
+
|
|
90
|
+
# View artifact metadata
|
|
91
|
+
slm info sentiment.slm
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Validation
|
|
95
|
+
|
|
96
|
+
Bake test cases into the artifact at compile time:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
slm compile \
|
|
100
|
+
--source Qwen/Qwen2.5-0.5B-Instruct-GGUF \
|
|
101
|
+
--task classification \
|
|
102
|
+
--labels "positive,negative,neutral" \
|
|
103
|
+
--quantize q4_k_m \
|
|
104
|
+
--validation tests.json \
|
|
105
|
+
--output sentiment.slm
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Where `tests.json` contains:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
[
|
|
112
|
+
{"input": "The view from the summit was breathtaking", "expected_label": "positive"},
|
|
113
|
+
{"input": "The bridge collapsed during rush hour", "expected_label": "negative"}
|
|
114
|
+
]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then run the validation suite:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
slm test sentiment.slm
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## The `.slm` format
|
|
124
|
+
|
|
125
|
+
A `.slm` file is a ZIP archive with a fixed structure:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
sentiment.slm (ZIP)
|
|
129
|
+
├── model.gguf # Quantized model weights
|
|
130
|
+
├── manifest.json # Task type, backend, labels, source info
|
|
131
|
+
├── prompt_template.txt # Baked-in system prompt
|
|
132
|
+
├── tokenizer.json # Tokenizer config (optional)
|
|
133
|
+
└── validation.json # Test cases (optional)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
One file. Everything included. Ship it, load it, call it.
|
|
137
|
+
|
|
138
|
+
## Current scope
|
|
139
|
+
|
|
140
|
+
This is an early release focused on proving the core workflow:
|
|
141
|
+
|
|
142
|
+
- **Backends**: llama-cpp-python (default), MLX (Apple Silicon, `pip install slmkit[mlx]`)
|
|
143
|
+
- **Task**: classification
|
|
144
|
+
- **Models**: pre-quantized GGUF or MLX-format models from HuggingFace
|
|
145
|
+
|
|
146
|
+
### MLX on Apple Silicon
|
|
147
|
+
|
|
148
|
+
For Metal-accelerated inference on M-series Macs:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pip install slmkit[mlx]
|
|
152
|
+
|
|
153
|
+
slm compile \
|
|
154
|
+
--source mlx-community/Qwen2.5-0.5B-Instruct-4bit \
|
|
155
|
+
--task classification \
|
|
156
|
+
--labels "positive,negative,neutral" \
|
|
157
|
+
--backend mlx \
|
|
158
|
+
--output sentiment-mlx.slm
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The backend is auto-detected from the model source, or set explicitly with `--backend`.
|
|
162
|
+
|
|
163
|
+
## Examples
|
|
164
|
+
|
|
165
|
+
Runnable examples in [`examples/`](examples/):
|
|
166
|
+
|
|
167
|
+
### Classification
|
|
168
|
+
|
|
169
|
+
| Example | Model | Size | Description |
|
|
170
|
+
|---------|-------|------|-------------|
|
|
171
|
+
| [sentiment.py](examples/classification/sentiment.py) | Qwen2.5-0.5B | ~490 MB | Restaurant review sentiment |
|
|
172
|
+
| [moderation.py](examples/classification/moderation.py) | Qwen2.5-0.5B | ~490 MB | Content moderation with validation |
|
|
173
|
+
| [spam_detection.py](examples/classification/spam_detection.py) | SmolLM2-360M | ~300 MB | Binary spam vs ham |
|
|
174
|
+
| [urgency_triage.py](examples/classification/urgency_triage.py) | Llama-3.2-1B | ~700 MB | Priority routing |
|
|
175
|
+
| [language_detection.py](examples/classification/language_detection.py) | Qwen2.5-1.5B | ~1 GB | Six-language detection |
|
|
176
|
+
| [topic_categorization.py](examples/classification/topic_categorization.py) | Gemma-2-2B | ~1.5 GB | Article topic sorting |
|
|
177
|
+
| [tone_detection.py](examples/classification/tone_detection.py) | Phi-3.5-Mini | ~2.2 GB | Tone/intent classification |
|
|
178
|
+
|
|
179
|
+
### MLX (Apple Silicon)
|
|
180
|
+
|
|
181
|
+
| Example | Model | Size | Description |
|
|
182
|
+
|---------|-------|------|-------------|
|
|
183
|
+
| [sentiment.py](examples/mlx/sentiment.py) | Qwen2.5-0.5B-4bit | ~243 MB | Metal-accelerated sentiment |
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
uv run python examples/classification/sentiment.py
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
inferkit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# slmkit
|
|
2
|
+
|
|
3
|
+
Package small language models into self-contained, embeddable `.slm` artifacts.
|
|
4
|
+
|
|
5
|
+
**SQLite for AI** — one file, zero config, zero internet, 5ms inference.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import slmkit
|
|
9
|
+
|
|
10
|
+
classifier = slmkit.load("sentiment.slm")
|
|
11
|
+
result = classifier("The aurora was breathtaking")
|
|
12
|
+
# → {"label": "positive", "confidence": 1.0}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install slmkit
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.11+. The `llama-cpp-python` dependency compiles C++ on first install, which may take a few minutes.
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
### 1. Compile a model
|
|
26
|
+
|
|
27
|
+
Package a pre-quantized GGUF model from HuggingFace into a `.slm` artifact:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
slm compile \
|
|
31
|
+
--source Qwen/Qwen2.5-0.5B-Instruct-GGUF \
|
|
32
|
+
--task classification \
|
|
33
|
+
--labels "positive,negative,neutral" \
|
|
34
|
+
--quantize q4_k_m \
|
|
35
|
+
--output sentiment.slm
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This downloads the model (~490 MB), bakes in a task-specific prompt, and packages everything into a single file.
|
|
39
|
+
|
|
40
|
+
You can also pass a local `.gguf` file as `--source` instead of a HuggingFace repo.
|
|
41
|
+
|
|
42
|
+
### 2. Load and run
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import slmkit
|
|
46
|
+
|
|
47
|
+
with slmkit.load("sentiment.slm") as model:
|
|
48
|
+
result = model("The midnight sun over Tromsø was unforgettable")
|
|
49
|
+
print(result)
|
|
50
|
+
# {"label": "positive", "confidence": 1.0}
|
|
51
|
+
|
|
52
|
+
results = model.batch([
|
|
53
|
+
"The recipe was a disaster",
|
|
54
|
+
"I finally fixed the timing belt myself",
|
|
55
|
+
])
|
|
56
|
+
# [{"label": "negative", ...}, {"label": "positive", ...}]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Test and inspect
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Run inference from the CLI
|
|
63
|
+
slm test sentiment.slm --input "The coral reef was thriving"
|
|
64
|
+
|
|
65
|
+
# View artifact metadata
|
|
66
|
+
slm info sentiment.slm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Validation
|
|
70
|
+
|
|
71
|
+
Bake test cases into the artifact at compile time:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
slm compile \
|
|
75
|
+
--source Qwen/Qwen2.5-0.5B-Instruct-GGUF \
|
|
76
|
+
--task classification \
|
|
77
|
+
--labels "positive,negative,neutral" \
|
|
78
|
+
--quantize q4_k_m \
|
|
79
|
+
--validation tests.json \
|
|
80
|
+
--output sentiment.slm
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Where `tests.json` contains:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
[
|
|
87
|
+
{"input": "The view from the summit was breathtaking", "expected_label": "positive"},
|
|
88
|
+
{"input": "The bridge collapsed during rush hour", "expected_label": "negative"}
|
|
89
|
+
]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then run the validation suite:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
slm test sentiment.slm
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## The `.slm` format
|
|
99
|
+
|
|
100
|
+
A `.slm` file is a ZIP archive with a fixed structure:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
sentiment.slm (ZIP)
|
|
104
|
+
├── model.gguf # Quantized model weights
|
|
105
|
+
├── manifest.json # Task type, backend, labels, source info
|
|
106
|
+
├── prompt_template.txt # Baked-in system prompt
|
|
107
|
+
├── tokenizer.json # Tokenizer config (optional)
|
|
108
|
+
└── validation.json # Test cases (optional)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
One file. Everything included. Ship it, load it, call it.
|
|
112
|
+
|
|
113
|
+
## Current scope
|
|
114
|
+
|
|
115
|
+
This is an early release focused on proving the core workflow:
|
|
116
|
+
|
|
117
|
+
- **Backends**: llama-cpp-python (default), MLX (Apple Silicon, `pip install slmkit[mlx]`)
|
|
118
|
+
- **Task**: classification
|
|
119
|
+
- **Models**: pre-quantized GGUF or MLX-format models from HuggingFace
|
|
120
|
+
|
|
121
|
+
### MLX on Apple Silicon
|
|
122
|
+
|
|
123
|
+
For Metal-accelerated inference on M-series Macs:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install slmkit[mlx]
|
|
127
|
+
|
|
128
|
+
slm compile \
|
|
129
|
+
--source mlx-community/Qwen2.5-0.5B-Instruct-4bit \
|
|
130
|
+
--task classification \
|
|
131
|
+
--labels "positive,negative,neutral" \
|
|
132
|
+
--backend mlx \
|
|
133
|
+
--output sentiment-mlx.slm
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The backend is auto-detected from the model source, or set explicitly with `--backend`.
|
|
137
|
+
|
|
138
|
+
## Examples
|
|
139
|
+
|
|
140
|
+
Runnable examples in [`examples/`](examples/):
|
|
141
|
+
|
|
142
|
+
### Classification
|
|
143
|
+
|
|
144
|
+
| Example | Model | Size | Description |
|
|
145
|
+
|---------|-------|------|-------------|
|
|
146
|
+
| [sentiment.py](examples/classification/sentiment.py) | Qwen2.5-0.5B | ~490 MB | Restaurant review sentiment |
|
|
147
|
+
| [moderation.py](examples/classification/moderation.py) | Qwen2.5-0.5B | ~490 MB | Content moderation with validation |
|
|
148
|
+
| [spam_detection.py](examples/classification/spam_detection.py) | SmolLM2-360M | ~300 MB | Binary spam vs ham |
|
|
149
|
+
| [urgency_triage.py](examples/classification/urgency_triage.py) | Llama-3.2-1B | ~700 MB | Priority routing |
|
|
150
|
+
| [language_detection.py](examples/classification/language_detection.py) | Qwen2.5-1.5B | ~1 GB | Six-language detection |
|
|
151
|
+
| [topic_categorization.py](examples/classification/topic_categorization.py) | Gemma-2-2B | ~1.5 GB | Article topic sorting |
|
|
152
|
+
| [tone_detection.py](examples/classification/tone_detection.py) | Phi-3.5-Mini | ~2.2 GB | Tone/intent classification |
|
|
153
|
+
|
|
154
|
+
### MLX (Apple Silicon)
|
|
155
|
+
|
|
156
|
+
| Example | Model | Size | Description |
|
|
157
|
+
|---------|-------|------|-------------|
|
|
158
|
+
| [sentiment.py](examples/mlx/sentiment.py) | Qwen2.5-0.5B-4bit | ~243 MB | Metal-accelerated sentiment |
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uv run python examples/classification/sentiment.py
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.slm
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Compile and run a language detection classifier.
|
|
2
|
+
|
|
3
|
+
Identifies the language of short text snippets across six languages.
|
|
4
|
+
Demonstrates multi-class classification with non-English inputs.
|
|
5
|
+
Uses Qwen2.5-1.5B for stronger multilingual understanding.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
uv run python examples/classification/language_detection.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import slmkit
|
|
14
|
+
from slmkit.compiler import compile_model
|
|
15
|
+
|
|
16
|
+
_HERE = Path(__file__).parent
|
|
17
|
+
ARTIFACT = _HERE / "qwen1.5b-language.slm"
|
|
18
|
+
SOURCE = "Qwen/Qwen2.5-1.5B-Instruct-GGUF" # ~1 GB — good multilingual coverage
|
|
19
|
+
LABELS = ["english", "spanish", "french", "german", "italian", "portuguese"]
|
|
20
|
+
|
|
21
|
+
SAMPLES = [
|
|
22
|
+
"The James Webb telescope captured infrared light from a galaxy 13 billion years old",
|
|
23
|
+
"El volcán Popocatépetl lanzó cenizas sobre los pueblos cercanos esta mañana",
|
|
24
|
+
"Les vendanges en Bourgogne ont commencé plus tôt que prévu cette année",
|
|
25
|
+
"Der Biergarten am Englischen Garten war trotz Regen gut besucht",
|
|
26
|
+
"La gondola scivolava silenziosa lungo il Canal Grande al tramonto",
|
|
27
|
+
"O carnaval de Olinda atrai milhões de foliões todos os anos",
|
|
28
|
+
"A herd of elephants crossed the Zambezi at dawn",
|
|
29
|
+
"La receta original del mole poblano lleva más de veinte ingredientes",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main() -> None:
|
|
34
|
+
if not ARTIFACT.exists():
|
|
35
|
+
print(f"Compiling {SOURCE} → {ARTIFACT} ...")
|
|
36
|
+
compile_model(
|
|
37
|
+
SOURCE,
|
|
38
|
+
task="classification",
|
|
39
|
+
output=str(ARTIFACT),
|
|
40
|
+
labels=LABELS,
|
|
41
|
+
quantization="q4_k_m",
|
|
42
|
+
)
|
|
43
|
+
size_mb = ARTIFACT.stat().st_size / 1024 / 1024
|
|
44
|
+
print(f"Done: {size_mb:.0f} MB\n")
|
|
45
|
+
|
|
46
|
+
with slmkit.load(str(ARTIFACT)) as model:
|
|
47
|
+
print("Language detection demo")
|
|
48
|
+
print(f"Labels: {', '.join(LABELS)}\n")
|
|
49
|
+
|
|
50
|
+
for text in SAMPLES:
|
|
51
|
+
result = model(text)
|
|
52
|
+
label = result["label"]
|
|
53
|
+
conf = result["confidence"]
|
|
54
|
+
snippet = text[:65] + ("…" if len(text) > 65 else "")
|
|
55
|
+
print(f" [{label:<12s} {conf:.0%}] {snippet}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
main()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Compile and run a content moderation classifier.
|
|
2
|
+
|
|
3
|
+
Classifies user-generated text into safe / unsafe / borderline categories.
|
|
4
|
+
Demonstrates custom labels, validation cases, and batch processing.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run python examples/classification/moderation.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import slmkit
|
|
14
|
+
from slmkit.compiler import compile_model
|
|
15
|
+
|
|
16
|
+
_HERE = Path(__file__).parent
|
|
17
|
+
ARTIFACT = _HERE / "qwen0.5b-moderation.slm"
|
|
18
|
+
SOURCE = "Qwen/Qwen2.5-0.5B-Instruct-GGUF"
|
|
19
|
+
LABELS = ["safe", "unsafe", "borderline"]
|
|
20
|
+
|
|
21
|
+
# Baked-in validation cases — these get packaged into the .slm artifact
|
|
22
|
+
VALIDATION = [
|
|
23
|
+
{"input": "What time does the library close on Saturdays?", "expected_label": "safe"},
|
|
24
|
+
{"input": "How do I pick a lock?", "expected_label": "unsafe"},
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
# Test inputs that simulate a comment feed
|
|
28
|
+
COMMENTS = [
|
|
29
|
+
"Does anyone know a good recipe for sourdough bread?",
|
|
30
|
+
"I want to mass-report their account until it gets banned",
|
|
31
|
+
"The new telescope images of Jupiter's moons are stunning",
|
|
32
|
+
"Here's how to bypass the school firewall to access blocked sites",
|
|
33
|
+
"She's annoying but I guess her presentation was okay",
|
|
34
|
+
"The trail to the summit was steep but the wildflowers made it worth it",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main() -> None:
|
|
39
|
+
if not ARTIFACT.exists():
|
|
40
|
+
# Write validation file
|
|
41
|
+
val_path = _HERE / "moderation_validation.json"
|
|
42
|
+
val_path.write_text(json.dumps(VALIDATION, indent=2))
|
|
43
|
+
|
|
44
|
+
print(f"Compiling {SOURCE} → {ARTIFACT} ...")
|
|
45
|
+
compile_model(
|
|
46
|
+
SOURCE,
|
|
47
|
+
task="classification",
|
|
48
|
+
output=str(ARTIFACT),
|
|
49
|
+
labels=LABELS,
|
|
50
|
+
quantization="q4_k_m",
|
|
51
|
+
validation=VALIDATION,
|
|
52
|
+
)
|
|
53
|
+
val_path.unlink() # clean up temp file
|
|
54
|
+
size_mb = ARTIFACT.stat().st_size / 1024 / 1024
|
|
55
|
+
print(f"Done: {size_mb:.0f} MB\n")
|
|
56
|
+
|
|
57
|
+
with slmkit.load(str(ARTIFACT)) as model:
|
|
58
|
+
print("Content moderation demo")
|
|
59
|
+
print(f"Labels: {', '.join(LABELS)}\n")
|
|
60
|
+
|
|
61
|
+
results = model.batch(COMMENTS)
|
|
62
|
+
for comment, result in zip(COMMENTS, results, strict=True):
|
|
63
|
+
label = result["label"]
|
|
64
|
+
conf = result["confidence"]
|
|
65
|
+
flag = "🔴" if label == "unsafe" else "🟡" if label == "borderline" else "🟢"
|
|
66
|
+
snippet = comment[:65] + ("…" if len(comment) > 65 else "")
|
|
67
|
+
print(f" {flag} [{label:<10s} {conf:.0%}] {snippet}")
|
|
68
|
+
|
|
69
|
+
# Show validation cases
|
|
70
|
+
cases = model.validation_cases()
|
|
71
|
+
if cases:
|
|
72
|
+
print(f"\n{len(cases)} validation case(s) baked in")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Compile and run a sentiment classifier.
|
|
2
|
+
|
|
3
|
+
Downloads Qwen2.5-0.5B (GGUF, ~490 MB), packages it as a .slm artifact,
|
|
4
|
+
then classifies a batch of restaurant review snippets.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run python examples/classification/sentiment.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import slmkit
|
|
13
|
+
from slmkit.compiler import compile_model
|
|
14
|
+
|
|
15
|
+
_HERE = Path(__file__).parent
|
|
16
|
+
ARTIFACT = _HERE / "qwen0.5b-sentiment.slm"
|
|
17
|
+
SOURCE = "Qwen/Qwen2.5-0.5B-Instruct-GGUF"
|
|
18
|
+
LABELS = ["positive", "negative", "neutral"]
|
|
19
|
+
|
|
20
|
+
REVIEWS = [
|
|
21
|
+
"The wood-fired pizza had a perfect char and the burrata was impossibly fresh",
|
|
22
|
+
"We waited 45 minutes for cold pasta that tasted like it came from a freezer",
|
|
23
|
+
"Decent sushi, nothing wrong with it but nothing memorable either",
|
|
24
|
+
"The sommelier paired a 2019 Barolo with our osso buco — genuinely transcendent",
|
|
25
|
+
"They forgot our reservation, seated us by the kitchen, and overcharged us",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main() -> None:
|
|
30
|
+
# Compile (skips download if already cached by HuggingFace)
|
|
31
|
+
if not ARTIFACT.exists():
|
|
32
|
+
print(f"Compiling {SOURCE} → {ARTIFACT} ...")
|
|
33
|
+
compile_model(
|
|
34
|
+
SOURCE,
|
|
35
|
+
task="classification",
|
|
36
|
+
output=str(ARTIFACT),
|
|
37
|
+
labels=LABELS,
|
|
38
|
+
quantization="q4_k_m",
|
|
39
|
+
)
|
|
40
|
+
size_mb = ARTIFACT.stat().st_size / 1024 / 1024
|
|
41
|
+
print(f"Done: {size_mb:.0f} MB\n")
|
|
42
|
+
|
|
43
|
+
# Load and classify
|
|
44
|
+
with slmkit.load(str(ARTIFACT)) as model:
|
|
45
|
+
print(f"Model: {model.info()['source']}")
|
|
46
|
+
print(f"Labels: {', '.join(LABELS)}\n")
|
|
47
|
+
|
|
48
|
+
for review in REVIEWS:
|
|
49
|
+
result = model(review)
|
|
50
|
+
label = result["label"]
|
|
51
|
+
conf = result["confidence"]
|
|
52
|
+
snippet = review[:72] + ("…" if len(review) > 72 else "")
|
|
53
|
+
print(f" [{label:<8s} {conf:.0%}] {snippet}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Compile and run a spam detection classifier.
|
|
2
|
+
|
|
3
|
+
Binary spam vs ham classifier for short messages — demonstrates the
|
|
4
|
+
simplest possible label set (two classes).
|
|
5
|
+
Uses Qwen2.5-0.5B — reliable for binary tasks.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
uv run python examples/classification/spam_detection.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import slmkit
|
|
14
|
+
from slmkit.compiler import compile_model
|
|
15
|
+
|
|
16
|
+
_HERE = Path(__file__).parent
|
|
17
|
+
ARTIFACT = _HERE / "qwen0.5b-spam.slm"
|
|
18
|
+
SOURCE = "Qwen/Qwen2.5-0.5B-Instruct-GGUF" # ~490 MB — reliable for binary tasks
|
|
19
|
+
LABELS = ["ham", "spam"]
|
|
20
|
+
|
|
21
|
+
MESSAGES = [
|
|
22
|
+
"Hey, are we still meeting at the trailhead at 7am tomorrow?",
|
|
23
|
+
"CONGRATULATIONS! You've been selected to receive a $1,000 gift card. Click here NOW!",
|
|
24
|
+
"The quarterly review has been moved to Thursday — same room, same time",
|
|
25
|
+
"Make $5,000/week from home with this ONE WEIRD TRICK. Limited spots available!!!",
|
|
26
|
+
"Can you pick up olive oil and sourdough on your way home?",
|
|
27
|
+
"URGENT: Your account will be suspended unless you verify your identity immediately",
|
|
28
|
+
"The new batch of honey from the rooftop hives is ready for bottling",
|
|
29
|
+
"You have (1) unread message from a secret admirer. Open now before it expires!",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main() -> None:
|
|
34
|
+
if not ARTIFACT.exists():
|
|
35
|
+
print(f"Compiling {SOURCE} → {ARTIFACT} ...")
|
|
36
|
+
compile_model(
|
|
37
|
+
SOURCE,
|
|
38
|
+
task="classification",
|
|
39
|
+
output=str(ARTIFACT),
|
|
40
|
+
labels=LABELS,
|
|
41
|
+
quantization="q4_k_m",
|
|
42
|
+
)
|
|
43
|
+
size_mb = ARTIFACT.stat().st_size / 1024 / 1024
|
|
44
|
+
print(f"Done: {size_mb:.0f} MB\n")
|
|
45
|
+
|
|
46
|
+
with slmkit.load(str(ARTIFACT)) as model:
|
|
47
|
+
print("Spam detection demo")
|
|
48
|
+
print(f"Labels: {', '.join(LABELS)}\n")
|
|
49
|
+
|
|
50
|
+
for msg in MESSAGES:
|
|
51
|
+
result = model(msg)
|
|
52
|
+
label = result["label"]
|
|
53
|
+
conf = result["confidence"]
|
|
54
|
+
icon = "🚫" if label == "spam" else "✉️"
|
|
55
|
+
snippet = msg[:65] + ("…" if len(msg) > 65 else "")
|
|
56
|
+
print(f" {icon} [{label:<4s} {conf:.0%}] {snippet}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
main()
|