desireeia 0.0.1__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.
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: desireeia
3
+ Version: 0.0.1
4
+ Summary: Python wrapper for DesireeIA — local LLM inference engine
5
+ Author: Passaro Francesco Paolo
6
+ License: Proprietary
7
+ Keywords: llm,inference,local,gguf,desireeia
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # desireeia
12
+
13
+ Python wrapper for **DesireeIA**, a local LLM inference engine (GGUF
14
+ models, CPU/GPU backends). This package ships a thin ctypes binding over
15
+ the native engine, with prebuilt binaries bundled per-platform so it works
16
+ without a separate native install step.
17
+
18
+ Full documentation, model compatibility notes, and the C++/CLI side of the
19
+ project live in the main repository:
20
+ https://github.com/passaroweb/DesireeIA (see the project README for
21
+ details).
22
+
23
+ ## Install
24
+
25
+ ```
26
+ pip install desireeia
27
+ ```
28
+
29
+ ## Bundled platforms
30
+
31
+ This wheel bundles the native engine for the platforms it was built on.
32
+ Check `desireeia/lib/` in the installed package for the exact list — at
33
+ minimum `win-x64` and `linux-x64` are included. If your platform/arch
34
+ isn't bundled yet, the package will raise a clear error on import rather
35
+ than silently failing.
36
+
37
+ - **Windows**: the engine DLL and its CUDA runtime (`cudart64_13.dll`,
38
+ for GPU acceleration) are bundled. It also needs the **Microsoft
39
+ Visual C++ Redistributable (x64)** on the system — already present on
40
+ most Windows machines (installed by countless other apps, .NET, GPU
41
+ drivers, etc.), but not bundled in this package. If import fails with
42
+ a missing-DLL error, install it from
43
+ https://aka.ms/vs/17/release/vc_redist.x64.exe and retry.
44
+ - **Linux**: the engine `.so` only depends on `libstdc++`, `libgcc_s`
45
+ and `glibc` — present on essentially every distribution by default,
46
+ nothing extra to install. GPU acceleration is not packaged for Linux
47
+ yet (CPU-only); this is a known gap, not a silent limitation.
48
+ - **macOS**: not built yet (`osx-x64`/`osx-arm64` are not bundled) —
49
+ importing on macOS will fail with a clear error until a macOS build is
50
+ produced.
51
+
52
+ ## License
53
+
54
+ See `LICENSE` — proprietary, no modification or unauthorized integration,
55
+ no AI training/ingestion without explicit written consent from the
56
+ author.
@@ -0,0 +1,46 @@
1
+ # desireeia
2
+
3
+ Python wrapper for **DesireeIA**, a local LLM inference engine (GGUF
4
+ models, CPU/GPU backends). This package ships a thin ctypes binding over
5
+ the native engine, with prebuilt binaries bundled per-platform so it works
6
+ without a separate native install step.
7
+
8
+ Full documentation, model compatibility notes, and the C++/CLI side of the
9
+ project live in the main repository:
10
+ https://github.com/passaroweb/DesireeIA (see the project README for
11
+ details).
12
+
13
+ ## Install
14
+
15
+ ```
16
+ pip install desireeia
17
+ ```
18
+
19
+ ## Bundled platforms
20
+
21
+ This wheel bundles the native engine for the platforms it was built on.
22
+ Check `desireeia/lib/` in the installed package for the exact list — at
23
+ minimum `win-x64` and `linux-x64` are included. If your platform/arch
24
+ isn't bundled yet, the package will raise a clear error on import rather
25
+ than silently failing.
26
+
27
+ - **Windows**: the engine DLL and its CUDA runtime (`cudart64_13.dll`,
28
+ for GPU acceleration) are bundled. It also needs the **Microsoft
29
+ Visual C++ Redistributable (x64)** on the system — already present on
30
+ most Windows machines (installed by countless other apps, .NET, GPU
31
+ drivers, etc.), but not bundled in this package. If import fails with
32
+ a missing-DLL error, install it from
33
+ https://aka.ms/vs/17/release/vc_redist.x64.exe and retry.
34
+ - **Linux**: the engine `.so` only depends on `libstdc++`, `libgcc_s`
35
+ and `glibc` — present on essentially every distribution by default,
36
+ nothing extra to install. GPU acceleration is not packaged for Linux
37
+ yet (CPU-only); this is a known gap, not a silent limitation.
38
+ - **macOS**: not built yet (`osx-x64`/`osx-arm64` are not bundled) —
39
+ importing on macOS will fail with a clear error until a macOS build is
40
+ produced.
41
+
42
+ ## License
43
+
44
+ See `LICENSE` — proprietary, no modification or unauthorized integration,
45
+ no AI training/ingestion without explicit written consent from the
46
+ author.
@@ -0,0 +1,82 @@
1
+ """DesireeIA Python wrapper — public API.
2
+
3
+ Usage::
4
+
5
+ import desireeia
6
+
7
+ plan = desireeia.build_plan("model.gguf")
8
+ with desireeia.LocalModel.load("model.gguf", plan) as model:
9
+ tokens = model.tokenize("Hello, how are you?")
10
+ for piece in model.chat_stream([("user", "Hello!")]):
11
+ print(piece, end="", flush=True)
12
+ """
13
+
14
+ from .enums import (
15
+ Backend,
16
+ Error,
17
+ InferenceBackend,
18
+ ModelFormat,
19
+ Quantization,
20
+ SsdTierMode,
21
+ SpecialToken,
22
+ StreamType,
23
+ )
24
+ from .types import (
25
+ ExecutionPlan,
26
+ GenerateOptions,
27
+ HardwareProfile,
28
+ SamplingOptions,
29
+ ToolCall,
30
+ ToolDefinition,
31
+ VisionConfig,
32
+ )
33
+ from .engine import (
34
+ build_plan,
35
+ detect_hardware,
36
+ profile_dump,
37
+ profile_reset,
38
+ version,
39
+ )
40
+ from .model import LocalModel
41
+ from .vision import VisionImageWrapper
42
+ from .generation import (
43
+ StopSequenceScanner,
44
+ ToolCalling,
45
+ StructuredOutput,
46
+ )
47
+
48
+ __version__ = "0.1.0"
49
+
50
+ __all__ = [
51
+ # enums
52
+ "Backend",
53
+ "Error",
54
+ "InferenceBackend",
55
+ "ModelFormat",
56
+ "Quantization",
57
+ "SsdTierMode",
58
+ "SpecialToken",
59
+ "StreamType",
60
+ # types
61
+ "ExecutionPlan",
62
+ "GenerateOptions",
63
+ "HardwareProfile",
64
+ "SamplingOptions",
65
+ "ToolCall",
66
+ "ToolDefinition",
67
+ "VisionConfig",
68
+ # engine
69
+ "build_plan",
70
+ "detect_hardware",
71
+ "profile_dump",
72
+ "profile_reset",
73
+ "version",
74
+ # model
75
+ "LocalModel",
76
+ # vision
77
+ "VisionImageWrapper",
78
+ # generation
79
+ "StopSequenceScanner",
80
+ "ToolCalling",
81
+ "StructuredOutput",
82
+ ]
@@ -0,0 +1,389 @@
1
+ """ctypes bindings to DesireeIALocaleEngine — the C ABI bridge.
2
+
3
+ Mirrors src/DesireeIA/Native/NativeMethods.cs exactly. Struct field order and
4
+ types MUST stay identical to the C structs in abi.h: this is a sequential
5
+ blit, not a marshalled conversion.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ctypes
11
+ import os
12
+ import sys
13
+ import threading
14
+ from ctypes import (
15
+ POINTER,
16
+ c_char_p,
17
+ c_int32,
18
+ c_int64,
19
+ c_uint32,
20
+ c_uint64,
21
+ c_float,
22
+ c_void_p,
23
+ CFUNCTYPE,
24
+ Structure,
25
+ )
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Library loading
29
+ # ---------------------------------------------------------------------------
30
+
31
+ _LIB_NAMES = {
32
+ "win32": "DesireeIALocaleEngine.dll",
33
+ "darwin": "libDesireeIALocaleEngine.dylib",
34
+ "linux": "libDesireeIALocaleEngine.so",
35
+ }
36
+
37
+ # Python sys.platform -> (fname suffix, arch candidates in priority order).
38
+ # The bundle ships the engine under desireeia/lib/<RID>/ following the same
39
+ # .NET RID convention used by Build/runtimes/<RID>/native/ (win-x64,
40
+ # linux-x64, osx-x64, osx-arm64, ...).
41
+ _RID_BY_BASE = {
42
+ "win": "win-{arch}",
43
+ "osx": "osx-{arch}",
44
+ "linux": "linux-{arch}",
45
+ }
46
+
47
+ _ARCH_MAP = {
48
+ "win": {"amd64": "x64", "x86_64": "x64", "arm64": "arm64", "aarch64": "arm64"},
49
+ "osx": {"arm64": "arm64", "aarch64": "arm64", "x86_64": "x64", "amd64": "x64"},
50
+ "linux": {"x86_64": "x64", "amd64": "x64", "aarch64": "arm64", "arm64": "arm64", "armv7l": "arm", "armv8l": "arm64"},
51
+ }
52
+
53
+ def _candidate_rids() -> list[str]:
54
+ """The .NET RIDs this machine could match, most specific first."""
55
+ import platform
56
+ sysname = platform.system().lower()
57
+ machine_raw = platform.machine().lower()
58
+
59
+ base = None
60
+ for key in ("win", "osx", "linux", "windows", "darwin"):
61
+ if sysname == key:
62
+ base = {"windows": "win", "darwin": "osx"}.get(key, key)
63
+ break
64
+ if base is None:
65
+ return []
66
+
67
+ arch_map = _ARCH_MAP.get(base, {})
68
+ arch = arch_map.get(machine_raw, "")
69
+ if not arch:
70
+ return []
71
+ return [f"{base}-{arch}"]
72
+
73
+
74
+ def _find_lib() -> ctypes.CDLL:
75
+ env = os.environ.get("DESIREEIA_NATIVE_LIB")
76
+ if env:
77
+ if not os.path.isfile(env):
78
+ raise OSError(f"DESIREEIA_NATIVE_LIB points to a missing file: {env}")
79
+ return _load_with_deps(env)
80
+
81
+ name = _LIB_NAMES.get(sys.platform)
82
+ if name is None:
83
+ raise OSError(f"Unsupported platform: {sys.platform}")
84
+
85
+ pkg_dir = os.path.dirname(os.path.abspath(__file__))
86
+ search_dirs: list[str] = []
87
+
88
+ # 1. Bundled per-platform library: desireeia/lib/<RID>/<fname>
89
+ for rid in _candidate_rids():
90
+ search_dirs.append(os.path.join(pkg_dir, "lib", rid))
91
+
92
+ # 2. Same directory as this package (development layout)
93
+ search_dirs.append(pkg_dir)
94
+
95
+ # 3. Alongside the Python package root
96
+ search_dirs.append(os.path.dirname(pkg_dir))
97
+
98
+ # 4. Plain ctypes search (system paths, LD_LIBRARY_PATH, PATH, ...)
99
+ try:
100
+ return _load_with_deps(name)
101
+ except OSError:
102
+ pass
103
+
104
+ for d in search_dirs:
105
+ candidate = os.path.join(d, name)
106
+ if os.path.isfile(candidate):
107
+ return _load_with_deps(candidate)
108
+
109
+ raise OSError(
110
+ f"Cannot find {name}. Ship it next to the desireeia package, bundle it "
111
+ f"under desireeia/lib/<RID>/ (Build/python.ps1 does this automatically), "
112
+ f"set DESIREEIA_NATIVE_LIB to its path, or add its directory to "
113
+ f"PATH / LD_LIBRARY_PATH."
114
+ )
115
+
116
+
117
+ def _load_with_deps(path: str) -> ctypes.CDLL:
118
+ """Load the engine, making sibling runtime DLLs (MinGW: libstdc++-6.dll
119
+ and friends) discoverable: on Windows the loader does NOT search the
120
+ engine's own directory for its dependencies, so the bundle dir must be
121
+ registered as a DLL search dir first."""
122
+ if sys.platform == "win32":
123
+ dll_dir = os.path.dirname(os.path.abspath(path))
124
+ if dll_dir and os.path.isdir(dll_dir):
125
+ os.add_dll_directory(dll_dir)
126
+ return ctypes.CDLL(path)
127
+
128
+ _lib: ctypes.CDLL | None = None
129
+ _lib_lock = threading.Lock()
130
+
131
+ def get_lib() -> ctypes.CDLL:
132
+ global _lib
133
+ if _lib is not None:
134
+ return _lib
135
+ with _lib_lock:
136
+ if _lib is not None:
137
+ return _lib
138
+ _lib = _find_lib()
139
+ _setup_signatures(_lib)
140
+ return _lib
141
+
142
+ # ---------------------------------------------------------------------------
143
+ # Log callback type
144
+ # ---------------------------------------------------------------------------
145
+
146
+ LOG_CB = CFUNCTYPE(None, c_int32, c_char_p, c_void_p)
147
+
148
+ # ---------------------------------------------------------------------------
149
+ # C structs (sequential layout, matching abi.h exactly)
150
+ # ---------------------------------------------------------------------------
151
+
152
+ class HwInfo(Structure):
153
+ _fields_ = [
154
+ ("cpu_threads", c_int32),
155
+ ("cpu_has_avx", c_int32),
156
+ ("cpu_has_avx2", c_int32),
157
+ ("cpu_has_avx512", c_int32),
158
+ ("cpu_has_neon", c_int32),
159
+ ("cuda_device_count", c_int32),
160
+ ("cuda_total_mb", c_int32),
161
+ ("ram_total_mb", c_uint64),
162
+ ("ram_free_mb", c_uint64),
163
+ ("has_metal", c_int32),
164
+ ("has_vulkan", c_int32),
165
+ ("intel_gpu_count", c_int32),
166
+ ("axelera_device_count", c_int32),
167
+ ]
168
+
169
+ class Plan(Structure):
170
+ _fields_ = [
171
+ ("backend", c_int32),
172
+ ("format", c_int32),
173
+ ("dense_quant", c_int32),
174
+ ("expert_quant", c_int32),
175
+ ("n_threads", c_int32),
176
+ ("ram_budget_mb", c_uint64),
177
+ ("expert_cache_count", c_int32),
178
+ ("expert_prefetch_enabled", c_int32),
179
+ ("kv_compression_enabled", c_int32),
180
+ ("expert_pin_enabled", c_int32),
181
+ ("expert_prefetch_depth", c_int32),
182
+ ("batch_union_enabled", c_int32),
183
+ ("dual_ssd_enabled", c_int32),
184
+ ("ssd_tier_mode", c_int32),
185
+ ("ssd_tier_cache_mb", c_uint64),
186
+ ]
187
+
188
+ class Sampling(Structure):
189
+ _fields_ = [
190
+ ("temperature", c_float),
191
+ ("top_k", c_int32),
192
+ ("top_p", c_float),
193
+ ("penalty_repeat", c_float),
194
+ ("penalty_freq", c_float),
195
+ ("penalty_present", c_float),
196
+ ("penalty_last_n", c_int32),
197
+ ("seed", c_uint32),
198
+ ]
199
+
200
+ class VisionImage(Structure):
201
+ _fields_ = [
202
+ ("width", c_uint32),
203
+ ("height", c_uint32),
204
+ ("channels", c_uint32),
205
+ ("data", c_void_p),
206
+ ]
207
+
208
+ class VisionConfig(Structure):
209
+ _fields_ = [
210
+ ("embedding_dim", c_int32),
211
+ ("patch_size", c_int32),
212
+ ("image_size", c_int32),
213
+ ("num_heads", c_int32),
214
+ ("num_layers", c_int32),
215
+ ("projection_dim", c_int32),
216
+ ("has_encoder", c_int32),
217
+ ]
218
+
219
+ # ---------------------------------------------------------------------------
220
+ # Function signatures
221
+ # ---------------------------------------------------------------------------
222
+
223
+ def _setup_signatures(lib: ctypes.CDLL) -> None:
224
+ # version
225
+ lib.desireeia_version.argtypes = []
226
+ lib.desireeia_version.restype = c_char_p
227
+
228
+ # logger
229
+ lib.desireeia_set_logger.argtypes = [LOG_CB, c_void_p]
230
+ lib.desireeia_set_logger.restype = c_int32
231
+
232
+ # hardware probe
233
+ lib.desireeia_probe_hw.argtypes = [POINTER(HwInfo)]
234
+ lib.desireeia_probe_hw.restype = c_int32
235
+
236
+ # plan
237
+ lib.desireeia_make_plan.argtypes = [
238
+ POINTER(HwInfo), c_char_p, POINTER(Plan), POINTER(Plan)
239
+ ]
240
+ lib.desireeia_make_plan.restype = c_int32
241
+
242
+ # create / destroy
243
+ lib.desireeia_create.argtypes = [
244
+ c_char_p, POINTER(Plan), LOG_CB, c_void_p, POINTER(c_void_p)
245
+ ]
246
+ lib.desireeia_create.restype = c_int32
247
+
248
+ lib.desireeia_destroy.argtypes = [c_void_p]
249
+ lib.desireeia_destroy.restype = c_int32
250
+
251
+ # predict / next_token / context_size
252
+ lib.desireeia_predict.argtypes = [
253
+ c_void_p, POINTER(c_int32), c_uint64, POINTER(c_int32)
254
+ ]
255
+ lib.desireeia_predict.restype = c_int32
256
+
257
+ lib.desireeia_next_token.argtypes = [c_void_p, POINTER(c_int32)]
258
+ lib.desireeia_next_token.restype = c_int32
259
+
260
+ lib.desireeia_context_size.argtypes = [c_void_p]
261
+ lib.desireeia_context_size.restype = c_uint64
262
+
263
+ # tokenize
264
+ lib.desireeia_tokenize.argtypes = [
265
+ c_void_p, c_char_p, c_int32,
266
+ POINTER(c_int32), c_uint64, POINTER(c_uint64)
267
+ ]
268
+ lib.desireeia_tokenize.restype = c_int32
269
+
270
+ # token_piece
271
+ lib.desireeia_token_piece.argtypes = [
272
+ c_void_p, c_int32, POINTER(ctypes.c_char), c_uint64
273
+ ]
274
+ lib.desireeia_token_piece.restype = c_int32
275
+
276
+ # special_token_id
277
+ lib.desireeia_special_token_id.argtypes = [
278
+ c_void_p, c_int32, POINTER(c_int32)
279
+ ]
280
+ lib.desireeia_special_token_id.restype = c_int32
281
+
282
+ # is_eog_token
283
+ lib.desireeia_is_eog_token.argtypes = [
284
+ c_void_p, c_int32, POINTER(c_int32)
285
+ ]
286
+ lib.desireeia_is_eog_token.restype = c_int32
287
+
288
+ # embed
289
+ lib.desireeia_embed.argtypes = [
290
+ c_void_p, POINTER(c_int32), c_uint64,
291
+ POINTER(c_float), c_uint64, POINTER(c_uint64), POINTER(c_uint32)
292
+ ]
293
+ lib.desireeia_embed.restype = c_int32
294
+
295
+ # sampling
296
+ lib.desireeia_set_sampling.argtypes = [c_void_p, POINTER(Sampling)]
297
+ lib.desireeia_set_sampling.restype = c_int32
298
+
299
+ lib.desireeia_get_sampling.argtypes = [c_void_p, POINTER(Sampling)]
300
+ lib.desireeia_get_sampling.restype = c_int32
301
+
302
+ # lora
303
+ lib.desireeia_load_lora_adapter.argtypes = [c_void_p, c_char_p, c_float]
304
+ lib.desireeia_load_lora_adapter.restype = c_int32
305
+
306
+ lib.desireeia_clear_lora_adapters.argtypes = [c_void_p]
307
+ lib.desireeia_clear_lora_adapters.restype = c_int32
308
+
309
+ # prerouter
310
+ lib.desireeia_load_prerouter.argtypes = [c_void_p, c_char_p]
311
+ lib.desireeia_load_prerouter.restype = c_int32
312
+
313
+ lib.desireeia_clear_prerouter.argtypes = [c_void_p]
314
+ lib.desireeia_clear_prerouter.restype = c_int32
315
+
316
+ lib.desireeia_set_prerouter_heuristic.argtypes = [c_void_p, c_int32]
317
+ lib.desireeia_set_prerouter_heuristic.restype = c_int32
318
+
319
+ # chat template
320
+ lib.desireeia_apply_chat_template.argtypes = [
321
+ c_void_p,
322
+ POINTER(c_char_p), POINTER(c_char_p), c_uint64, c_int32,
323
+ POINTER(ctypes.c_char), c_uint64, POINTER(c_uint64)
324
+ ]
325
+ lib.desireeia_apply_chat_template.restype = c_int32
326
+
327
+ # profiling
328
+ lib.desireeia_profile_dump.argtypes = [
329
+ POINTER(ctypes.c_char), c_uint64
330
+ ]
331
+ lib.desireeia_profile_dump.restype = None
332
+
333
+ lib.desireeia_profile_reset.argtypes = []
334
+ lib.desireeia_profile_reset.restype = None
335
+
336
+ # vision: image load/free
337
+ lib.desireeia_load_image.argtypes = [
338
+ c_char_p, c_int32, POINTER(VisionImage)
339
+ ]
340
+ lib.desireeia_load_image.restype = c_int32
341
+
342
+ lib.desireeia_free_image.argtypes = [POINTER(VisionImage)]
343
+ lib.desireeia_free_image.restype = None
344
+
345
+ # vision: standalone context
346
+ lib.desireeia_vision_create.argtypes = [c_char_p, LOG_CB, c_void_p]
347
+ lib.desireeia_vision_create.restype = c_void_p
348
+
349
+ lib.desireeia_vision_destroy.argtypes = [c_void_p]
350
+ lib.desireeia_vision_destroy.restype = None
351
+
352
+ lib.desireeia_vision_get_config.argtypes = [
353
+ c_void_p, POINTER(VisionConfig)
354
+ ]
355
+ lib.desireeia_vision_get_config.restype = c_int32
356
+
357
+ lib.desireeia_vision_encode.argtypes = [
358
+ c_void_p, POINTER(VisionImage),
359
+ POINTER(c_float), c_uint64, POINTER(c_uint64), POINTER(c_uint32)
360
+ ]
361
+ lib.desireeia_vision_encode.restype = c_int32
362
+
363
+ lib.desireeia_vision_preprocess.argtypes = [
364
+ POINTER(VisionImage), c_int32,
365
+ POINTER(c_float), c_uint64, POINTER(c_uint64)
366
+ ]
367
+ lib.desireeia_vision_preprocess.restype = c_int32
368
+
369
+ # vision: ctx-based multimodal
370
+ lib.desireeia_has_vision.argtypes = [c_void_p, POINTER(c_int32)]
371
+ lib.desireeia_has_vision.restype = c_int32
372
+
373
+ lib.desireeia_vision_token_count.argtypes = [c_void_p, POINTER(c_int32)]
374
+ lib.desireeia_vision_token_count.restype = c_int32
375
+
376
+ lib.desireeia_vision_image_token.argtypes = [c_void_p, POINTER(c_int32)]
377
+ lib.desireeia_vision_image_token.restype = c_int32
378
+
379
+ lib.desireeia_vision_encode_ctx.argtypes = [
380
+ c_void_p, POINTER(VisionImage),
381
+ POINTER(c_float), c_uint64, POINTER(c_uint64), POINTER(c_uint32)
382
+ ]
383
+ lib.desireeia_vision_encode_ctx.restype = c_int32
384
+
385
+ lib.desireeia_predict_image.argtypes = [
386
+ c_void_p, POINTER(c_int32), c_uint64,
387
+ POINTER(c_float), c_uint64, c_int32, POINTER(c_int32)
388
+ ]
389
+ lib.desireeia_predict_image.restype = c_int32
@@ -0,0 +1,77 @@
1
+ """DesireeIAEngine — static entry point. Mirrors C# DesireeIAEngine.cs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ from . import _native as _nat
8
+ from .enums import Error
9
+ from .types import ExecutionPlan, HardwareProfile
10
+
11
+
12
+ def version() -> str:
13
+ """Return the native engine version string."""
14
+ return _nat.get_lib().desireeia_version().decode("utf-8")
15
+
16
+
17
+ def detect_hardware() -> HardwareProfile:
18
+ """Probe the machine and return a HardwareProfile."""
19
+ hw = _nat.HwInfo()
20
+ err = _nat.get_lib().desireeia_probe_hw(hw)
21
+ if err != Error.OK:
22
+ raise RuntimeError(f"Hardware probe failed: {Error(err).name}")
23
+ return HardwareProfile.from_native(hw)
24
+
25
+
26
+ def build_plan(
27
+ model_path: Optional[str] = None,
28
+ overrides: Optional[ExecutionPlan] = None,
29
+ ) -> ExecutionPlan:
30
+ """Let the native engine auto-detect the best plan, optionally with overrides."""
31
+ lib = _nat.get_lib()
32
+
33
+ hw = _nat.HwInfo()
34
+ err = lib.desireeia_probe_hw(hw)
35
+ if err != Error.OK:
36
+ raise RuntimeError(f"Hardware probe failed: {Error(err).name}")
37
+
38
+ # Bug fixed here: this used to construct a blank, all-zero `_nat.Plan()`
39
+ # and never populate it from `overrides` - `overrides.to_native()` was
40
+ # never called, so every field a caller explicitly set (thread_count,
41
+ # kv_compression, batch_union, expert_prefetch, dense_quantization,
42
+ # ...) was silently discarded and replaced by whatever the native
43
+ # auto-detect logic picks for a zeroed field. The C# binding
44
+ # (DesireeIAEngine.cs BuildPlan, `overrides.ToNative()`) always did
45
+ # this correctly; only this Python wrapper had the gap - which is why
46
+ # a server built on this wrapper (desireeiaserver) measured
47
+ # dramatically lower decode throughput than the CLI on the identical
48
+ # model/hardware, despite requesting the same plan.
49
+ override_native = overrides.to_native() if overrides is not None else _nat.Plan()
50
+ override_ptr = _nat.ctypes.pointer(override_native) if overrides is not None else None
51
+ plan_out = _nat.Plan()
52
+
53
+ model_bytes = model_path.encode("utf-8") if model_path else None
54
+
55
+ err = lib.desireeia_make_plan(
56
+ _nat.ctypes.byref(hw),
57
+ model_bytes,
58
+ override_ptr,
59
+ _nat.ctypes.byref(plan_out),
60
+ )
61
+ if err != Error.OK:
62
+ raise RuntimeError(f"Plan build failed: {Error(err).name}")
63
+
64
+ return ExecutionPlan.from_native(plan_out)
65
+
66
+
67
+ def profile_dump() -> str:
68
+ """Human-readable cumulative matmul profiling counters."""
69
+ buf = ( _nat.ctypes.c_char * 2048 )()
70
+ _nat.get_lib().desireeia_profile_dump(buf, 2048)
71
+ raw = buf.value
72
+ return raw.decode("utf-8", errors="replace")
73
+
74
+
75
+ def profile_reset() -> None:
76
+ """Zero the process-global profiling counters."""
77
+ _nat.get_lib().desireeia_profile_reset()