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.
Files changed (36) hide show
  1. inferkit-0.1.0/.gitignore +41 -0
  2. inferkit-0.1.0/PKG-INFO +191 -0
  3. inferkit-0.1.0/README.md +166 -0
  4. inferkit-0.1.0/examples/.gitignore +1 -0
  5. inferkit-0.1.0/examples/classification/language_detection.py +59 -0
  6. inferkit-0.1.0/examples/classification/moderation.py +76 -0
  7. inferkit-0.1.0/examples/classification/sentiment.py +57 -0
  8. inferkit-0.1.0/examples/classification/spam_detection.py +60 -0
  9. inferkit-0.1.0/examples/classification/tone_detection.py +59 -0
  10. inferkit-0.1.0/examples/classification/topic_categorization.py +59 -0
  11. inferkit-0.1.0/examples/classification/urgency_triage.py +60 -0
  12. inferkit-0.1.0/examples/mlx/sentiment.py +57 -0
  13. inferkit-0.1.0/pyproject.toml +57 -0
  14. inferkit-0.1.0/src/slmkit/__init__.py +11 -0
  15. inferkit-0.1.0/src/slmkit/backends/__init__.py +26 -0
  16. inferkit-0.1.0/src/slmkit/backends/llamacpp.py +59 -0
  17. inferkit-0.1.0/src/slmkit/backends/mlx.py +63 -0
  18. inferkit-0.1.0/src/slmkit/backends/onnx.py +1 -0
  19. inferkit-0.1.0/src/slmkit/cli.py +104 -0
  20. inferkit-0.1.0/src/slmkit/compiler.py +172 -0
  21. inferkit-0.1.0/src/slmkit/formats/__init__.py +1 -0
  22. inferkit-0.1.0/src/slmkit/formats/artifact.py +136 -0
  23. inferkit-0.1.0/src/slmkit/formats/manifest.py +74 -0
  24. inferkit-0.1.0/src/slmkit/formats/schema.py +31 -0
  25. inferkit-0.1.0/src/slmkit/model.py +88 -0
  26. inferkit-0.1.0/src/slmkit/tasks/__init__.py +26 -0
  27. inferkit-0.1.0/src/slmkit/tasks/classification.py +41 -0
  28. inferkit-0.1.0/src/slmkit/tasks/embedding.py +1 -0
  29. inferkit-0.1.0/src/slmkit/tasks/extraction.py +1 -0
  30. inferkit-0.1.0/src/slmkit/tasks/generation.py +1 -0
  31. inferkit-0.1.0/tests/__init__.py +0 -0
  32. inferkit-0.1.0/tests/test_artifact.py +118 -0
  33. inferkit-0.1.0/tests/test_classification.py +53 -0
  34. inferkit-0.1.0/tests/test_manifest.py +57 -0
  35. inferkit-0.1.0/tests/test_schema.py +51 -0
  36. 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
@@ -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
@@ -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()