geniex 0.3.14__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.
- geniex-0.3.14/LICENSE +29 -0
- geniex-0.3.14/MANIFEST.in +9 -0
- geniex-0.3.14/PKG-INFO +223 -0
- geniex-0.3.14/README.md +199 -0
- geniex-0.3.14/_geniex_backend.py +44 -0
- geniex-0.3.14/_sdk_fetch.py +454 -0
- geniex-0.3.14/_shared_setup.py +46 -0
- geniex-0.3.14/geniex/__init__.py +39 -0
- geniex-0.3.14/geniex/_ffi/__init__.py +82 -0
- geniex-0.3.14/geniex/_ffi/_api.py +441 -0
- geniex-0.3.14/geniex/_ffi/_lib.py +197 -0
- geniex-0.3.14/geniex/_ffi/_types.py +423 -0
- geniex-0.3.14/geniex/_progress.py +75 -0
- geniex-0.3.14/geniex/_version.py +2 -0
- geniex-0.3.14/geniex/auto.py +501 -0
- geniex-0.3.14/geniex/cli.py +602 -0
- geniex-0.3.14/geniex/generation/__init__.py +8 -0
- geniex-0.3.14/geniex/generation/config.py +30 -0
- geniex-0.3.14/geniex/generation/output.py +85 -0
- geniex-0.3.14/geniex/generation/streamer.py +79 -0
- geniex-0.3.14/geniex/model_manager.py +530 -0
- geniex-0.3.14/geniex/modeling.py +577 -0
- geniex-0.3.14/geniex/tokenizer.py +78 -0
- geniex-0.3.14/geniex.egg-info/PKG-INFO +223 -0
- geniex-0.3.14/geniex.egg-info/SOURCES.txt +43 -0
- geniex-0.3.14/geniex.egg-info/dependency_links.txt +1 -0
- geniex-0.3.14/geniex.egg-info/entry_points.txt +2 -0
- geniex-0.3.14/geniex.egg-info/requires.txt +7 -0
- geniex-0.3.14/geniex.egg-info/top_level.txt +2 -0
- geniex-0.3.14/pyproject.toml +67 -0
- geniex-0.3.14/setup.cfg +4 -0
- geniex-0.3.14/setup.py +20 -0
- geniex-0.3.14/tests/__init__.py +3 -0
- geniex-0.3.14/tests/conftest.py +49 -0
- geniex-0.3.14/tests/test_cli.py +122 -0
- geniex-0.3.14/tests/test_local_aihub_pull.py +197 -0
- geniex-0.3.14/tests/test_model_manager.py +49 -0
- geniex-0.3.14/tests/test_progress.py +88 -0
- geniex-0.3.14/tests/unit/test_cli_load_banner.py +71 -0
- geniex-0.3.14/tests/unit/test_default_precision.py +95 -0
- geniex-0.3.14/tests/unit/test_error_messages.py +296 -0
- geniex-0.3.14/tests/unit/test_modeling_decode.py +44 -0
- geniex-0.3.14/tests/unit/test_qairt_coerce.py +40 -0
- geniex-0.3.14/tests/unit/test_quant_validation.py +93 -0
- geniex-0.3.14/tests/unit/test_sdk_fetch.py +339 -0
geniex-0.3.14/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
geniex-0.3.14/PKG-INFO
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: geniex
|
|
3
|
+
Version: 0.3.14
|
|
4
|
+
Summary: GenieX-Bridge Python binding for running AI models on Qualcomm platforms. Subject to Qualcomm Terms of Use: https://www.qualcomm.com/site/terms-of-use
|
|
5
|
+
Author: Qualcomm
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/qualcomm/GenieX
|
|
8
|
+
Project-URL: Repository, https://github.com/qualcomm/GenieX
|
|
9
|
+
Project-URL: License, https://github.com/qualcomm/geniex/blob/main/LICENSE
|
|
10
|
+
Project-URL: Terms of Use, https://www.qualcomm.com/site/terms-of-use
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: tqdm>=4.65
|
|
19
|
+
Provides-Extra: notebook
|
|
20
|
+
Requires-Dist: ipywidgets>=8; extra == "notebook"
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# geniex
|
|
26
|
+
|
|
27
|
+
Python bindings for the **GenieX SDK** — run LLMs and VLMs locally on
|
|
28
|
+
Qualcomm platforms (CPU, GPU, Hexagon NPU) with a single `pip install`.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
Pick a distribution based on which native backend you need. Three
|
|
33
|
+
distributions ship the same Python surface — they only differ in which
|
|
34
|
+
plugin libraries the install-time SDK fetcher stages:
|
|
35
|
+
|
|
36
|
+
| Command | Plugins staged | Approx download |
|
|
37
|
+
|--------------------------------|--------------------------|-----------------|
|
|
38
|
+
| `pip install geniex` | llama.cpp **and** QAIRT | ~220 MB |
|
|
39
|
+
| `pip install geniex-llama-cpp` | llama.cpp only | ~15 MB |
|
|
40
|
+
| `pip install geniex-qairt` | QAIRT only | ~210 MB |
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple geniex
|
|
44
|
+
# or, e.g.:
|
|
45
|
+
# pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple geniex-llama-cpp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The three distributions share the same top-level ``geniex`` package — they
|
|
49
|
+
are **mutually exclusive**. Installing two into the same environment will
|
|
50
|
+
have the second install overwrite the first; install the variant matching
|
|
51
|
+
the backend you intend to use.
|
|
52
|
+
|
|
53
|
+
Each installer contacts the GenieX SDK release mirror and pulls only the
|
|
54
|
+
plugin slice it needs via HTTP Range requests, falling back to a full
|
|
55
|
+
download if the mirror doesn't honor ranges.
|
|
56
|
+
|
|
57
|
+
Supported platforms (the install-time fetcher auto-provisions the native SDK):
|
|
58
|
+
|
|
59
|
+
| OS | Arch |
|
|
60
|
+
|---------|-----------|
|
|
61
|
+
| Linux | aarch64 |
|
|
62
|
+
| Windows | arm64 |
|
|
63
|
+
|
|
64
|
+
Python 3.10+ required.
|
|
65
|
+
|
|
66
|
+
> [!WARNING]
|
|
67
|
+
> Do not install `llama-cpp-python` into the same environment. Both packages
|
|
68
|
+
> embed their own llama.cpp shared libraries; loading both leads to symbol
|
|
69
|
+
> conflicts — `DLL load failed` on Windows, segfaults, or wrong outputs.
|
|
70
|
+
> Use a separate virtualenv, or uninstall `llama-cpp-python`
|
|
71
|
+
> (`pip list | grep llama-cpp-python` to check).
|
|
72
|
+
|
|
73
|
+
## Library usage
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from geniex import AutoModelForCausalLM
|
|
77
|
+
|
|
78
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
79
|
+
"qwen3", # short alias, HF repo id, or local GGUF path
|
|
80
|
+
device_map="auto", # "auto" | "cpu" | "gpu" | "npu" | "hybrid"
|
|
81
|
+
# | "<plugin>" | "<plugin>:<device>"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
messages = [{"role": "user", "content": "What is 2+2?"}]
|
|
85
|
+
prompt = model.tokenizer.apply_chat_template(
|
|
86
|
+
messages, tokenize=False, add_generation_prompt=True,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# One-shot
|
|
90
|
+
output = model.generate(prompt, max_new_tokens=256)
|
|
91
|
+
print(output.text)
|
|
92
|
+
print(f"[{output.profile.generated_tokens} tok, "
|
|
93
|
+
f"{output.profile.decode_speed:.1f} tok/s, stop={output.profile.stop_reason}]")
|
|
94
|
+
|
|
95
|
+
# Streaming
|
|
96
|
+
streamer = model.generate(prompt, max_new_tokens=256, stream=True)
|
|
97
|
+
for chunk in streamer:
|
|
98
|
+
print(chunk, end="", flush=True)
|
|
99
|
+
|
|
100
|
+
model.close()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`from_pretrained` accepts any of: a short alias (resolved via the bundled
|
|
104
|
+
registry), a HuggingFace `org/repo` (optionally `org/repo:precision`), or a
|
|
105
|
+
local path to a `.gguf` file or a pre-downloaded directory.
|
|
106
|
+
|
|
107
|
+
### VLM
|
|
108
|
+
|
|
109
|
+
`AutoModelForCausalLM` auto-detects multimodal models and returns a `GenieXVLM`:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from geniex import AutoModelForCausalLM
|
|
113
|
+
|
|
114
|
+
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-VL-2B-Instruct-GGUF", device_map="auto")
|
|
115
|
+
messages = [{
|
|
116
|
+
"role": "user",
|
|
117
|
+
"content": [
|
|
118
|
+
{"type": "image", "image": "/path/to/image.jpg"},
|
|
119
|
+
{"type": "text", "text": "Describe the image."},
|
|
120
|
+
],
|
|
121
|
+
}]
|
|
122
|
+
prompt = model.tokenizer.apply_chat_template(
|
|
123
|
+
messages, tokenize=False, add_generation_prompt=True,
|
|
124
|
+
)
|
|
125
|
+
print(model.generate(prompt, images=["/path/to/image.jpg"]).text)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Model management
|
|
129
|
+
|
|
130
|
+
The same model manager the CLI uses is available programmatically:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from geniex import model_manager as mm
|
|
134
|
+
|
|
135
|
+
mm.pull("qwen3") # alias or "org/repo[:precision]"
|
|
136
|
+
paths = mm.get_paths("qwen3") # resolved local paths
|
|
137
|
+
mm.list_models() # cached models
|
|
138
|
+
mm.remove("qwen3")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Plugin / device enumeration
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from geniex import get_plugin_list, get_device_list
|
|
145
|
+
|
|
146
|
+
for plugin in get_plugin_list():
|
|
147
|
+
print(plugin, get_device_list(plugin))
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Friendly aliases accepted by `device_map`:
|
|
151
|
+
|
|
152
|
+
| Alias | llama_cpp resolves to | qairt resolves to | Notes |
|
|
153
|
+
|----------|------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------|
|
|
154
|
+
| `cpu` | empty `device_id`, `ngl=0` | `NPU` + warning | Pure CPU for `llama_cpp`. QAIRT is NPU-only; other aliases are coerced with a stderr warning (no hard error). |
|
|
155
|
+
| `gpu` | `GPUOpenCL` | `NPU` + warning | Adreno via `ggml-opencl`. |
|
|
156
|
+
| `npu` | `HTP0` | `NPU` | Pinned single-session HTP. Deterministic; slower than `hybrid` on LLMs (~30% TTFT). |
|
|
157
|
+
| `hybrid` | empty `device_id`, `ngl=999` | `NPU` + warning | llama.cpp's per-tensor HTP+CPU scheduler — the fast path on Snapdragon. Used as the default when `device_map="auto"`. |
|
|
158
|
+
|
|
159
|
+
`device_map="auto"` (the default) picks `hybrid` for `llama_cpp` and
|
|
160
|
+
`npu` for `qairt`. When the model was pulled via `geniex.model_manager`
|
|
161
|
+
the manifest already records its plugin, so a bare alias binds to that
|
|
162
|
+
plugin — `device_map="npu"` on a cached llama_cpp model resolves to
|
|
163
|
+
`llama_cpp:HTP0`, not qairt. Pass a concrete id as
|
|
164
|
+
`device_map="<plugin>:<device_id>"` for full control (e.g.
|
|
165
|
+
`"llama_cpp:HTP0,HTP1,HTP2,HTP3"`). Run `geniex-py devices` or
|
|
166
|
+
`geniex._ffi.get_device_list(plugin)` to enumerate what your host
|
|
167
|
+
actually exposes.
|
|
168
|
+
|
|
169
|
+
## CLI
|
|
170
|
+
|
|
171
|
+
After install, the `geniex-py` console script is on your `$PATH` (the
|
|
172
|
+
name avoids clashing with the Go `geniex` binary when both are
|
|
173
|
+
installed):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
geniex-py chat qwen3 # interactive chat (auto-downloads)
|
|
177
|
+
geniex-py chat unsloth/Qwen3-4B-GGUF --quant Q4_K_M
|
|
178
|
+
geniex-py chat /path/to/model.gguf --system "You are a concise assistant."
|
|
179
|
+
|
|
180
|
+
geniex-py pull qwen3 # download into the cache only
|
|
181
|
+
geniex-py ls # list cached models (table)
|
|
182
|
+
geniex-py ls qwen3 # show one model's geniex.json
|
|
183
|
+
geniex-py rm qwen3 # remove from cache
|
|
184
|
+
geniex-py devices # list plugins and their devices
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Inside chat: `/reset` clears history, `/exit` or Ctrl-D quits, Ctrl-C
|
|
188
|
+
interrupts the current reply. `geniex-py <cmd> --help` for all flags.
|
|
189
|
+
|
|
190
|
+
## Supported backends
|
|
191
|
+
|
|
192
|
+
| Plugin | Accelerators | Notes |
|
|
193
|
+
|-------------|---------------------------------------|--------------------------------------------------------------------------------------|
|
|
194
|
+
| `llama_cpp` | CPU / GPU / NPU (via ggml backends) | Default. Devices available depend on the host build (e.g. CUDA, Metal, Vulkan, HTP). |
|
|
195
|
+
| `qairt` | Hexagon NPU | Qualcomm Snapdragon only. |
|
|
196
|
+
|
|
197
|
+
## Environment variables
|
|
198
|
+
|
|
199
|
+
| Var | Purpose |
|
|
200
|
+
|--------------------|------------------------------------------------------|
|
|
201
|
+
| `GENIEX_DATADIR` | Model cache directory (default: `~/.cache/geniex`). |
|
|
202
|
+
| `GENIEX_HFTOKEN` | HuggingFace token for gated repos. |
|
|
203
|
+
| `GENIEX_LIB_PATH` | Point at a pre-built `libgeniex.so` / `geniex.dll`. |
|
|
204
|
+
| `GENIEX_LOG` | Log level: `trace`/`debug`/`info`/`warn`/`error`/`none`. Default `info`. |
|
|
205
|
+
|
|
206
|
+
## Logging
|
|
207
|
+
|
|
208
|
+
SDK and binding logs flow through Python's stdlib `logging` under the
|
|
209
|
+
`geniex` logger. Set `GENIEX_LOG` before `geniex.init()` or call
|
|
210
|
+
`geniex.set_log_level("debug")` at runtime. If the `geniex` logger has no
|
|
211
|
+
handlers configured, a default `StreamHandler` is attached when `GENIEX_LOG`
|
|
212
|
+
is set; otherwise your own logging config takes precedence.
|
|
213
|
+
|
|
214
|
+
## Building from source
|
|
215
|
+
|
|
216
|
+
See [BUILD.md](BUILD.md) for dev-mode setup, building the SDK, building
|
|
217
|
+
the sdist, and the TestPyPI smoke-test workflow.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
BSD 3-Clause — see [LICENSE](https://github.com/qualcomm/geniex/blob/main/LICENSE).
|
|
222
|
+
|
|
223
|
+
Use of this package is also subject to Qualcomm's [Terms of Use](https://www.qualcomm.com/site/terms-of-use).
|
geniex-0.3.14/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# geniex
|
|
2
|
+
|
|
3
|
+
Python bindings for the **GenieX SDK** — run LLMs and VLMs locally on
|
|
4
|
+
Qualcomm platforms (CPU, GPU, Hexagon NPU) with a single `pip install`.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
Pick a distribution based on which native backend you need. Three
|
|
9
|
+
distributions ship the same Python surface — they only differ in which
|
|
10
|
+
plugin libraries the install-time SDK fetcher stages:
|
|
11
|
+
|
|
12
|
+
| Command | Plugins staged | Approx download |
|
|
13
|
+
|--------------------------------|--------------------------|-----------------|
|
|
14
|
+
| `pip install geniex` | llama.cpp **and** QAIRT | ~220 MB |
|
|
15
|
+
| `pip install geniex-llama-cpp` | llama.cpp only | ~15 MB |
|
|
16
|
+
| `pip install geniex-qairt` | QAIRT only | ~210 MB |
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple geniex
|
|
20
|
+
# or, e.g.:
|
|
21
|
+
# pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple geniex-llama-cpp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The three distributions share the same top-level ``geniex`` package — they
|
|
25
|
+
are **mutually exclusive**. Installing two into the same environment will
|
|
26
|
+
have the second install overwrite the first; install the variant matching
|
|
27
|
+
the backend you intend to use.
|
|
28
|
+
|
|
29
|
+
Each installer contacts the GenieX SDK release mirror and pulls only the
|
|
30
|
+
plugin slice it needs via HTTP Range requests, falling back to a full
|
|
31
|
+
download if the mirror doesn't honor ranges.
|
|
32
|
+
|
|
33
|
+
Supported platforms (the install-time fetcher auto-provisions the native SDK):
|
|
34
|
+
|
|
35
|
+
| OS | Arch |
|
|
36
|
+
|---------|-----------|
|
|
37
|
+
| Linux | aarch64 |
|
|
38
|
+
| Windows | arm64 |
|
|
39
|
+
|
|
40
|
+
Python 3.10+ required.
|
|
41
|
+
|
|
42
|
+
> [!WARNING]
|
|
43
|
+
> Do not install `llama-cpp-python` into the same environment. Both packages
|
|
44
|
+
> embed their own llama.cpp shared libraries; loading both leads to symbol
|
|
45
|
+
> conflicts — `DLL load failed` on Windows, segfaults, or wrong outputs.
|
|
46
|
+
> Use a separate virtualenv, or uninstall `llama-cpp-python`
|
|
47
|
+
> (`pip list | grep llama-cpp-python` to check).
|
|
48
|
+
|
|
49
|
+
## Library usage
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from geniex import AutoModelForCausalLM
|
|
53
|
+
|
|
54
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
55
|
+
"qwen3", # short alias, HF repo id, or local GGUF path
|
|
56
|
+
device_map="auto", # "auto" | "cpu" | "gpu" | "npu" | "hybrid"
|
|
57
|
+
# | "<plugin>" | "<plugin>:<device>"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
messages = [{"role": "user", "content": "What is 2+2?"}]
|
|
61
|
+
prompt = model.tokenizer.apply_chat_template(
|
|
62
|
+
messages, tokenize=False, add_generation_prompt=True,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# One-shot
|
|
66
|
+
output = model.generate(prompt, max_new_tokens=256)
|
|
67
|
+
print(output.text)
|
|
68
|
+
print(f"[{output.profile.generated_tokens} tok, "
|
|
69
|
+
f"{output.profile.decode_speed:.1f} tok/s, stop={output.profile.stop_reason}]")
|
|
70
|
+
|
|
71
|
+
# Streaming
|
|
72
|
+
streamer = model.generate(prompt, max_new_tokens=256, stream=True)
|
|
73
|
+
for chunk in streamer:
|
|
74
|
+
print(chunk, end="", flush=True)
|
|
75
|
+
|
|
76
|
+
model.close()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`from_pretrained` accepts any of: a short alias (resolved via the bundled
|
|
80
|
+
registry), a HuggingFace `org/repo` (optionally `org/repo:precision`), or a
|
|
81
|
+
local path to a `.gguf` file or a pre-downloaded directory.
|
|
82
|
+
|
|
83
|
+
### VLM
|
|
84
|
+
|
|
85
|
+
`AutoModelForCausalLM` auto-detects multimodal models and returns a `GenieXVLM`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from geniex import AutoModelForCausalLM
|
|
89
|
+
|
|
90
|
+
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-VL-2B-Instruct-GGUF", device_map="auto")
|
|
91
|
+
messages = [{
|
|
92
|
+
"role": "user",
|
|
93
|
+
"content": [
|
|
94
|
+
{"type": "image", "image": "/path/to/image.jpg"},
|
|
95
|
+
{"type": "text", "text": "Describe the image."},
|
|
96
|
+
],
|
|
97
|
+
}]
|
|
98
|
+
prompt = model.tokenizer.apply_chat_template(
|
|
99
|
+
messages, tokenize=False, add_generation_prompt=True,
|
|
100
|
+
)
|
|
101
|
+
print(model.generate(prompt, images=["/path/to/image.jpg"]).text)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Model management
|
|
105
|
+
|
|
106
|
+
The same model manager the CLI uses is available programmatically:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from geniex import model_manager as mm
|
|
110
|
+
|
|
111
|
+
mm.pull("qwen3") # alias or "org/repo[:precision]"
|
|
112
|
+
paths = mm.get_paths("qwen3") # resolved local paths
|
|
113
|
+
mm.list_models() # cached models
|
|
114
|
+
mm.remove("qwen3")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Plugin / device enumeration
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from geniex import get_plugin_list, get_device_list
|
|
121
|
+
|
|
122
|
+
for plugin in get_plugin_list():
|
|
123
|
+
print(plugin, get_device_list(plugin))
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Friendly aliases accepted by `device_map`:
|
|
127
|
+
|
|
128
|
+
| Alias | llama_cpp resolves to | qairt resolves to | Notes |
|
|
129
|
+
|----------|------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------|
|
|
130
|
+
| `cpu` | empty `device_id`, `ngl=0` | `NPU` + warning | Pure CPU for `llama_cpp`. QAIRT is NPU-only; other aliases are coerced with a stderr warning (no hard error). |
|
|
131
|
+
| `gpu` | `GPUOpenCL` | `NPU` + warning | Adreno via `ggml-opencl`. |
|
|
132
|
+
| `npu` | `HTP0` | `NPU` | Pinned single-session HTP. Deterministic; slower than `hybrid` on LLMs (~30% TTFT). |
|
|
133
|
+
| `hybrid` | empty `device_id`, `ngl=999` | `NPU` + warning | llama.cpp's per-tensor HTP+CPU scheduler — the fast path on Snapdragon. Used as the default when `device_map="auto"`. |
|
|
134
|
+
|
|
135
|
+
`device_map="auto"` (the default) picks `hybrid` for `llama_cpp` and
|
|
136
|
+
`npu` for `qairt`. When the model was pulled via `geniex.model_manager`
|
|
137
|
+
the manifest already records its plugin, so a bare alias binds to that
|
|
138
|
+
plugin — `device_map="npu"` on a cached llama_cpp model resolves to
|
|
139
|
+
`llama_cpp:HTP0`, not qairt. Pass a concrete id as
|
|
140
|
+
`device_map="<plugin>:<device_id>"` for full control (e.g.
|
|
141
|
+
`"llama_cpp:HTP0,HTP1,HTP2,HTP3"`). Run `geniex-py devices` or
|
|
142
|
+
`geniex._ffi.get_device_list(plugin)` to enumerate what your host
|
|
143
|
+
actually exposes.
|
|
144
|
+
|
|
145
|
+
## CLI
|
|
146
|
+
|
|
147
|
+
After install, the `geniex-py` console script is on your `$PATH` (the
|
|
148
|
+
name avoids clashing with the Go `geniex` binary when both are
|
|
149
|
+
installed):
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
geniex-py chat qwen3 # interactive chat (auto-downloads)
|
|
153
|
+
geniex-py chat unsloth/Qwen3-4B-GGUF --quant Q4_K_M
|
|
154
|
+
geniex-py chat /path/to/model.gguf --system "You are a concise assistant."
|
|
155
|
+
|
|
156
|
+
geniex-py pull qwen3 # download into the cache only
|
|
157
|
+
geniex-py ls # list cached models (table)
|
|
158
|
+
geniex-py ls qwen3 # show one model's geniex.json
|
|
159
|
+
geniex-py rm qwen3 # remove from cache
|
|
160
|
+
geniex-py devices # list plugins and their devices
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Inside chat: `/reset` clears history, `/exit` or Ctrl-D quits, Ctrl-C
|
|
164
|
+
interrupts the current reply. `geniex-py <cmd> --help` for all flags.
|
|
165
|
+
|
|
166
|
+
## Supported backends
|
|
167
|
+
|
|
168
|
+
| Plugin | Accelerators | Notes |
|
|
169
|
+
|-------------|---------------------------------------|--------------------------------------------------------------------------------------|
|
|
170
|
+
| `llama_cpp` | CPU / GPU / NPU (via ggml backends) | Default. Devices available depend on the host build (e.g. CUDA, Metal, Vulkan, HTP). |
|
|
171
|
+
| `qairt` | Hexagon NPU | Qualcomm Snapdragon only. |
|
|
172
|
+
|
|
173
|
+
## Environment variables
|
|
174
|
+
|
|
175
|
+
| Var | Purpose |
|
|
176
|
+
|--------------------|------------------------------------------------------|
|
|
177
|
+
| `GENIEX_DATADIR` | Model cache directory (default: `~/.cache/geniex`). |
|
|
178
|
+
| `GENIEX_HFTOKEN` | HuggingFace token for gated repos. |
|
|
179
|
+
| `GENIEX_LIB_PATH` | Point at a pre-built `libgeniex.so` / `geniex.dll`. |
|
|
180
|
+
| `GENIEX_LOG` | Log level: `trace`/`debug`/`info`/`warn`/`error`/`none`. Default `info`. |
|
|
181
|
+
|
|
182
|
+
## Logging
|
|
183
|
+
|
|
184
|
+
SDK and binding logs flow through Python's stdlib `logging` under the
|
|
185
|
+
`geniex` logger. Set `GENIEX_LOG` before `geniex.init()` or call
|
|
186
|
+
`geniex.set_log_level("debug")` at runtime. If the `geniex` logger has no
|
|
187
|
+
handlers configured, a default `StreamHandler` is attached when `GENIEX_LOG`
|
|
188
|
+
is set; otherwise your own logging config takes precedence.
|
|
189
|
+
|
|
190
|
+
## Building from source
|
|
191
|
+
|
|
192
|
+
See [BUILD.md](BUILD.md) for dev-mode setup, building the SDK, building
|
|
193
|
+
the sdist, and the TestPyPI smoke-test workflow.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
BSD 3-Clause — see [LICENSE](https://github.com/qualcomm/geniex/blob/main/LICENSE).
|
|
198
|
+
|
|
199
|
+
Use of this package is also subject to Qualcomm's [Terms of Use](https://www.qualcomm.com/site/terms-of-use).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright 2024-2026 Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
|
|
4
|
+
"""PEP 517 wrapper around setuptools.build_meta.
|
|
5
|
+
|
|
6
|
+
Qualcomm Linux ships a stripped-down Python 3.12 without ``tomllib`` in
|
|
7
|
+
stdlib. ``setuptools.compat.py310`` imports ``tomllib`` unconditionally
|
|
8
|
+
on Python 3.11+ — there is no upstream tomli fallback on that branch —
|
|
9
|
+
so the isolated build env explodes before our ``setup.py`` runs.
|
|
10
|
+
|
|
11
|
+
This wrapper registers ``tomli`` under the stdlib name first, then
|
|
12
|
+
delegates every PEP 517 / PEP 660 hook to ``setuptools.build_meta``
|
|
13
|
+
verbatim. On hosts that already have stdlib ``tomllib`` the alias path
|
|
14
|
+
is skipped and behaviour matches plain setuptools. See GH #538.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
if 'tomllib' not in sys.modules:
|
|
20
|
+
try:
|
|
21
|
+
import tomllib # noqa: F401 — normal stdlib path on Python 3.11+
|
|
22
|
+
except ModuleNotFoundError:
|
|
23
|
+
import tomli
|
|
24
|
+
|
|
25
|
+
sys.modules['tomllib'] = tomli
|
|
26
|
+
|
|
27
|
+
from setuptools.build_meta import ( # noqa: E402,F401
|
|
28
|
+
build_sdist,
|
|
29
|
+
build_wheel,
|
|
30
|
+
get_requires_for_build_sdist,
|
|
31
|
+
get_requires_for_build_wheel,
|
|
32
|
+
prepare_metadata_for_build_wheel,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# PEP 660 editable hooks are only present in modern setuptools; re-export
|
|
36
|
+
# when available so `pip install -e .` keeps working unchanged.
|
|
37
|
+
try:
|
|
38
|
+
from setuptools.build_meta import ( # noqa: E402,F401
|
|
39
|
+
build_editable,
|
|
40
|
+
get_requires_for_build_editable,
|
|
41
|
+
prepare_metadata_for_build_editable,
|
|
42
|
+
)
|
|
43
|
+
except ImportError:
|
|
44
|
+
pass
|