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.
Files changed (45) hide show
  1. geniex-0.3.14/LICENSE +29 -0
  2. geniex-0.3.14/MANIFEST.in +9 -0
  3. geniex-0.3.14/PKG-INFO +223 -0
  4. geniex-0.3.14/README.md +199 -0
  5. geniex-0.3.14/_geniex_backend.py +44 -0
  6. geniex-0.3.14/_sdk_fetch.py +454 -0
  7. geniex-0.3.14/_shared_setup.py +46 -0
  8. geniex-0.3.14/geniex/__init__.py +39 -0
  9. geniex-0.3.14/geniex/_ffi/__init__.py +82 -0
  10. geniex-0.3.14/geniex/_ffi/_api.py +441 -0
  11. geniex-0.3.14/geniex/_ffi/_lib.py +197 -0
  12. geniex-0.3.14/geniex/_ffi/_types.py +423 -0
  13. geniex-0.3.14/geniex/_progress.py +75 -0
  14. geniex-0.3.14/geniex/_version.py +2 -0
  15. geniex-0.3.14/geniex/auto.py +501 -0
  16. geniex-0.3.14/geniex/cli.py +602 -0
  17. geniex-0.3.14/geniex/generation/__init__.py +8 -0
  18. geniex-0.3.14/geniex/generation/config.py +30 -0
  19. geniex-0.3.14/geniex/generation/output.py +85 -0
  20. geniex-0.3.14/geniex/generation/streamer.py +79 -0
  21. geniex-0.3.14/geniex/model_manager.py +530 -0
  22. geniex-0.3.14/geniex/modeling.py +577 -0
  23. geniex-0.3.14/geniex/tokenizer.py +78 -0
  24. geniex-0.3.14/geniex.egg-info/PKG-INFO +223 -0
  25. geniex-0.3.14/geniex.egg-info/SOURCES.txt +43 -0
  26. geniex-0.3.14/geniex.egg-info/dependency_links.txt +1 -0
  27. geniex-0.3.14/geniex.egg-info/entry_points.txt +2 -0
  28. geniex-0.3.14/geniex.egg-info/requires.txt +7 -0
  29. geniex-0.3.14/geniex.egg-info/top_level.txt +2 -0
  30. geniex-0.3.14/pyproject.toml +67 -0
  31. geniex-0.3.14/setup.cfg +4 -0
  32. geniex-0.3.14/setup.py +20 -0
  33. geniex-0.3.14/tests/__init__.py +3 -0
  34. geniex-0.3.14/tests/conftest.py +49 -0
  35. geniex-0.3.14/tests/test_cli.py +122 -0
  36. geniex-0.3.14/tests/test_local_aihub_pull.py +197 -0
  37. geniex-0.3.14/tests/test_model_manager.py +49 -0
  38. geniex-0.3.14/tests/test_progress.py +88 -0
  39. geniex-0.3.14/tests/unit/test_cli_load_banner.py +71 -0
  40. geniex-0.3.14/tests/unit/test_default_precision.py +95 -0
  41. geniex-0.3.14/tests/unit/test_error_messages.py +296 -0
  42. geniex-0.3.14/tests/unit/test_modeling_decode.py +44 -0
  43. geniex-0.3.14/tests/unit/test_qairt_coerce.py +40 -0
  44. geniex-0.3.14/tests/unit/test_quant_validation.py +93 -0
  45. 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.
@@ -0,0 +1,9 @@
1
+ include setup.py
2
+ include _sdk_fetch.py
3
+ include _shared_setup.py
4
+ include _geniex_backend.py
5
+ include README.md
6
+ include LICENSE
7
+ include pyproject.toml
8
+ recursive-include geniex *.py
9
+ prune geniex/lib
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).
@@ -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