cactus-compute 2.0.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 (143) hide show
  1. cactus_compute-2.0.0/PKG-INFO +435 -0
  2. cactus_compute-2.0.0/README.md +387 -0
  3. cactus_compute-2.0.0/cactus/__init__.py +61 -0
  4. cactus_compute-2.0.0/cactus/__main__.py +4 -0
  5. cactus_compute-2.0.0/cactus/_version.py +1 -0
  6. cactus_compute-2.0.0/cactus/assets/test.wav +0 -0
  7. cactus_compute-2.0.0/cactus/assets/test_monkey.png +0 -0
  8. cactus_compute-2.0.0/cactus/bindings/__init__.py +0 -0
  9. cactus_compute-2.0.0/cactus/bindings/cactus.py +3035 -0
  10. cactus_compute-2.0.0/cactus/cli/__init__.py +522 -0
  11. cactus_compute-2.0.0/cactus/cli/auth.py +41 -0
  12. cactus_compute-2.0.0/cactus/cli/clean.py +121 -0
  13. cactus_compute-2.0.0/cactus/cli/code.py +188 -0
  14. cactus_compute-2.0.0/cactus/cli/common.py +128 -0
  15. cactus_compute-2.0.0/cactus/cli/compile.py +220 -0
  16. cactus_compute-2.0.0/cactus/cli/config_utils.py +57 -0
  17. cactus_compute-2.0.0/cactus/cli/convert.py +183 -0
  18. cactus_compute-2.0.0/cactus/cli/download.py +73 -0
  19. cactus_compute-2.0.0/cactus/cli/list.py +95 -0
  20. cactus_compute-2.0.0/cactus/cli/model.py +394 -0
  21. cactus_compute-2.0.0/cactus/cli/run.py +54 -0
  22. cactus_compute-2.0.0/cactus/cli/runtime.py +115 -0
  23. cactus_compute-2.0.0/cactus/cli/serve.py +90 -0
  24. cactus_compute-2.0.0/cactus/cli/test.py +111 -0
  25. cactus_compute-2.0.0/cactus/cli/transcribe.py +31 -0
  26. cactus_compute-2.0.0/cactus/cli/transpile.py +66 -0
  27. cactus_compute-2.0.0/cactus/cli/upload.py +53 -0
  28. cactus_compute-2.0.0/cactus/cli/utils.py +408 -0
  29. cactus_compute-2.0.0/cactus/convert/__init__.py +1 -0
  30. cactus_compute-2.0.0/cactus/convert/assets/gemma-4-e2b-it/probe.pt +0 -0
  31. cactus_compute-2.0.0/cactus/convert/cactus_adapters/__init__.py +2 -0
  32. cactus_compute-2.0.0/cactus/convert/cactus_adapters/config_utils.py +626 -0
  33. cactus_compute-2.0.0/cactus/convert/cactus_adapters/tensor_io.py +647 -0
  34. cactus_compute-2.0.0/cactus/convert/cactus_adapters/tokenizer.py +597 -0
  35. cactus_compute-2.0.0/cactus/convert/cactus_adapters/weight_patterns.py +421 -0
  36. cactus_compute-2.0.0/cactus/convert/calibration/__init__.py +2 -0
  37. cactus_compute-2.0.0/cactus/convert/calibration/hessian.py +440 -0
  38. cactus_compute-2.0.0/cactus/convert/calibration/loaders.py +55 -0
  39. cactus_compute-2.0.0/cactus/convert/cli.py +638 -0
  40. cactus_compute-2.0.0/cactus/convert/compat.py +34 -0
  41. cactus_compute-2.0.0/cactus/convert/export/__init__.py +2 -0
  42. cactus_compute-2.0.0/cactus/convert/export/files.py +68 -0
  43. cactus_compute-2.0.0/cactus/convert/export/qdq.py +564 -0
  44. cactus_compute-2.0.0/cactus/convert/export/reports.py +54 -0
  45. cactus_compute-2.0.0/cactus/convert/export/validate.py +104 -0
  46. cactus_compute-2.0.0/cactus/convert/handoff_probe.py +201 -0
  47. cactus_compute-2.0.0/cactus/convert/interleave_orthogonal_cq4.py +97 -0
  48. cactus_compute-2.0.0/cactus/convert/model_adapters/__init__.py +2 -0
  49. cactus_compute-2.0.0/cactus/convert/model_adapters/adapters.py +958 -0
  50. cactus_compute-2.0.0/cactus/convert/model_adapters/detection.py +22 -0
  51. cactus_compute-2.0.0/cactus/convert/model_adapters/naming.py +424 -0
  52. cactus_compute-2.0.0/cactus/convert/model_adapters/nemo.py +140 -0
  53. cactus_compute-2.0.0/cactus/convert/model_adapters/policy.py +74 -0
  54. cactus_compute-2.0.0/cactus/convert/quantization/__init__.py +2 -0
  55. cactus_compute-2.0.0/cactus/convert/quantization/cq.py +410 -0
  56. cactus_compute-2.0.0/cactus/convert/tests/test_cq.py +157 -0
  57. cactus_compute-2.0.0/cactus/convert/tests/test_gemma4_adapter.py +120 -0
  58. cactus_compute-2.0.0/cactus/convert/tests/test_hessian.py +56 -0
  59. cactus_compute-2.0.0/cactus/convert/tests/test_lfm2_adapter.py +106 -0
  60. cactus_compute-2.0.0/cactus/convert/tests/test_naming_qdq.py +404 -0
  61. cactus_compute-2.0.0/cactus/convert/tests/test_policy.py +293 -0
  62. cactus_compute-2.0.0/cactus/convert/tests/test_qwen_adapter.py +64 -0
  63. cactus_compute-2.0.0/cactus/convert/tests/test_reports.py +36 -0
  64. cactus_compute-2.0.0/cactus/py.typed +0 -0
  65. cactus_compute-2.0.0/cactus/server.py +922 -0
  66. cactus_compute-2.0.0/cactus/transpile/__init__.py +1 -0
  67. cactus_compute-2.0.0/cactus/transpile/aten_ops.py +191 -0
  68. cactus_compute-2.0.0/cactus/transpile/audio_preprocess.py +400 -0
  69. cactus_compute-2.0.0/cactus/transpile/canonicalize/cleanup.py +707 -0
  70. cactus_compute-2.0.0/cactus/transpile/canonicalize/utils.py +277 -0
  71. cactus_compute-2.0.0/cactus/transpile/capture_jax.py +1663 -0
  72. cactus_compute-2.0.0/cactus/transpile/capture_pytorch.py +375 -0
  73. cactus_compute-2.0.0/cactus/transpile/component_partition.py +335 -0
  74. cactus_compute-2.0.0/cactus/transpile/component_pipeline.py +213 -0
  75. cactus_compute-2.0.0/cactus/transpile/component_plan.py +248 -0
  76. cactus_compute-2.0.0/cactus/transpile/fusion/__init__.py +47 -0
  77. cactus_compute-2.0.0/cactus/transpile/fusion/attention.py +550 -0
  78. cactus_compute-2.0.0/cactus/transpile/fusion/common.py +73 -0
  79. cactus_compute-2.0.0/cactus/transpile/fusion/conv.py +123 -0
  80. cactus_compute-2.0.0/cactus/transpile/fusion/deltanet.py +357 -0
  81. cactus_compute-2.0.0/cactus/transpile/fusion/linear.py +58 -0
  82. cactus_compute-2.0.0/cactus/transpile/fusion/lstm.py +216 -0
  83. cactus_compute-2.0.0/cactus/transpile/fusion/mlp.py +53 -0
  84. cactus_compute-2.0.0/cactus/transpile/fusion/rel_pos_bias.py +179 -0
  85. cactus_compute-2.0.0/cactus/transpile/fusion/rms_norm.py +133 -0
  86. cactus_compute-2.0.0/cactus/transpile/fusion/rope.py +252 -0
  87. cactus_compute-2.0.0/cactus/transpile/graph_ir.py +201 -0
  88. cactus_compute-2.0.0/cactus/transpile/hf_model.py +3576 -0
  89. cactus_compute-2.0.0/cactus/transpile/import_ir.py +277 -0
  90. cactus_compute-2.0.0/cactus/transpile/import_semantics.py +337 -0
  91. cactus_compute-2.0.0/cactus/transpile/importers.py +1960 -0
  92. cactus_compute-2.0.0/cactus/transpile/jax_semantic_rewrites.py +875 -0
  93. cactus_compute-2.0.0/cactus/transpile/jax_user_graph_bundle.py +575 -0
  94. cactus_compute-2.0.0/cactus/transpile/lower.py +3533 -0
  95. cactus_compute-2.0.0/cactus/transpile/media_limits.py +35 -0
  96. cactus_compute-2.0.0/cactus/transpile/model_adapters.py +7329 -0
  97. cactus_compute-2.0.0/cactus/transpile/model_patterns.py +64 -0
  98. cactus_compute-2.0.0/cactus/transpile/model_profiles.py +307 -0
  99. cactus_compute-2.0.0/cactus/transpile/multimodal_runtime.py +722 -0
  100. cactus_compute-2.0.0/cactus/transpile/normalize.py +29 -0
  101. cactus_compute-2.0.0/cactus/transpile/npu/__init__.py +3 -0
  102. cactus_compute-2.0.0/cactus/transpile/npu/audio.py +115 -0
  103. cactus_compute-2.0.0/cactus/transpile/npu/coremltools_patches.py +434 -0
  104. cactus_compute-2.0.0/cactus/transpile/npu/pipeline.py +94 -0
  105. cactus_compute-2.0.0/cactus/transpile/npu/source.py +118 -0
  106. cactus_compute-2.0.0/cactus/transpile/npu/vision.py +126 -0
  107. cactus_compute-2.0.0/cactus/transpile/ops.py +292 -0
  108. cactus_compute-2.0.0/cactus/transpile/optimize_graph.py +1656 -0
  109. cactus_compute-2.0.0/cactus/transpile/runtime_compat.py +376 -0
  110. cactus_compute-2.0.0/cactus/transpile/runtime_support.py +178 -0
  111. cactus_compute-2.0.0/cactus/transpile/tdt_runtime.py +1028 -0
  112. cactus_compute-2.0.0/cactus/transpile/weight_binding.py +363 -0
  113. cactus_compute-2.0.0/cactus/transpile/weight_compat.py +568 -0
  114. cactus_compute-2.0.0/cactus_compute.egg-info/PKG-INFO +435 -0
  115. cactus_compute-2.0.0/cactus_compute.egg-info/SOURCES.txt +141 -0
  116. cactus_compute-2.0.0/cactus_compute.egg-info/dependency_links.txt +1 -0
  117. cactus_compute-2.0.0/cactus_compute.egg-info/entry_points.txt +2 -0
  118. cactus_compute-2.0.0/cactus_compute.egg-info/requires.txt +24 -0
  119. cactus_compute-2.0.0/cactus_compute.egg-info/top_level.txt +1 -0
  120. cactus_compute-2.0.0/pyproject.toml +74 -0
  121. cactus_compute-2.0.0/setup.cfg +4 -0
  122. cactus_compute-2.0.0/tests/test_bindings.py +500 -0
  123. cactus_compute-2.0.0/tests/test_capture_jax.py +197 -0
  124. cactus_compute-2.0.0/tests/test_capture_jax_user_graph_bundle.py +196 -0
  125. cactus_compute-2.0.0/tests/test_chat_template_golden.py +239 -0
  126. cactus_compute-2.0.0/tests/test_cli_run.py +70 -0
  127. cactus_compute-2.0.0/tests/test_cli_transpile_defaults.py +187 -0
  128. cactus_compute-2.0.0/tests/test_cloud_handoff_live.py +73 -0
  129. cactus_compute-2.0.0/tests/test_component_partition.py +55 -0
  130. cactus_compute-2.0.0/tests/test_component_plan.py +36 -0
  131. cactus_compute-2.0.0/tests/test_download.py +247 -0
  132. cactus_compute-2.0.0/tests/test_encoder_cross_kv_route.py +79 -0
  133. cactus_compute-2.0.0/tests/test_graph.py +745 -0
  134. cactus_compute-2.0.0/tests/test_model.py +162 -0
  135. cactus_compute-2.0.0/tests/test_nomic_text_embedding.py +37 -0
  136. cactus_compute-2.0.0/tests/test_optimize_gemma4_attention.py +328 -0
  137. cactus_compute-2.0.0/tests/test_server.py +272 -0
  138. cactus_compute-2.0.0/tests/test_server_live.py +518 -0
  139. cactus_compute-2.0.0/tests/test_tool_constraint_state.py +224 -0
  140. cactus_compute-2.0.0/tests/test_transpile_aten_ops.py +272 -0
  141. cactus_compute-2.0.0/tests/test_transpile_importers.py +32 -0
  142. cactus_compute-2.0.0/tests/test_transpile_layer_keys.py +68 -0
  143. cactus_compute-2.0.0/tests/test_transpile_weight_compat.py +386 -0
@@ -0,0 +1,435 @@
1
+ Metadata-Version: 2.4
2
+ Name: cactus-compute
3
+ Version: 2.0.0
4
+ Summary: On-device AI inference — LLM, vision, speech, embeddings, and RAG
5
+ Author: Cactus Compute
6
+ License: Cactus Compute License
7
+ Project-URL: Homepage, https://cactuscompute.com
8
+ Project-URL: Repository, https://github.com/cactus-compute/cactus
9
+ Project-URL: Documentation, https://docs.cactuscompute.com
10
+ Project-URL: Bug Tracker, https://github.com/cactus-compute/cactus/issues
11
+ Keywords: ai,inference,on-device,llm,embeddings,transcription,rag,quantization,edge-ai
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: Other/Proprietary License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
25
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
26
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
27
+ Requires-Python: <3.14,>=3.10
28
+ Description-Content-Type: text/markdown
29
+ Requires-Dist: torch<3,>=2.8.0
30
+ Requires-Dist: transformers==5.5.4
31
+ Requires-Dist: numpy<3,>=1.26.0
32
+ Requires-Dist: huggingface-hub<2.0,>=1.5.0
33
+ Requires-Dist: scipy>=1.13
34
+ Requires-Dist: Pillow<12,>=11.0.0
35
+ Requires-Dist: torchvision<1,>=0.23.0
36
+ Requires-Dist: sentencepiece>=0.2.0
37
+ Requires-Dist: coremltools>=9.0; sys_platform == "darwin"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pytest>=8.0; extra == "dev"
40
+ Requires-Dist: httpx>=0.25.0; extra == "dev"
41
+ Requires-Dist: einops>=0.7; extra == "dev"
42
+ Provides-Extra: lora
43
+ Requires-Dist: peft>=0.15; extra == "lora"
44
+ Provides-Extra: serve
45
+ Requires-Dist: fastapi>=0.100.0; extra == "serve"
46
+ Requires-Dist: uvicorn>=0.20.0; extra == "serve"
47
+ Requires-Dist: python-multipart>=0.0.5; extra == "serve"
48
+
49
+ ---
50
+ title: "Cactus Python Package"
51
+ description: "Python package and ctypes bindings for the Cactus on-device AI inference engine."
52
+ keywords: ["Python package", "Python bindings", "on-device AI", "Python FFI", "embeddings", "transcription", "RAG"]
53
+ ---
54
+
55
+ # Cactus Python Package
56
+
57
+ Python bindings for Cactus Engine via FFI. Auto-installed when you run `source ./setup`.
58
+
59
+ > **Model bundles:** Pre-built runtime bundles for all supported models at [huggingface.co/Cactus-Compute](https://huggingface.co/Cactus-Compute).
60
+
61
+ ## Getting Started
62
+
63
+ <!-- --8<-- [start:install] -->
64
+ ```bash
65
+ git clone https://github.com/cactus-compute/cactus && cd cactus && source ./setup
66
+ cactus build --python
67
+ ```
68
+ <!-- --8<-- [end:install] -->
69
+
70
+ ```bash
71
+ # Download pre-built bundles (defaults to --weights general, the portable build)
72
+ cactus download LiquidAI/LFM2-VL-450M
73
+ cactus download openai/whisper-small --weights apple # CoreML/NPU variant
74
+
75
+ # Optional: set your Cactus Cloud API key for automatic cloud fallback
76
+ cactus auth
77
+ ```
78
+
79
+ ## Quick Example
80
+
81
+ <!-- --8<-- [start:example] -->
82
+ ```python
83
+ from cactus import ensure_model, cactus_init, cactus_complete, cactus_destroy
84
+ import json
85
+
86
+ # Downloads the pre-built bundle from HuggingFace if not already present
87
+ bundle = ensure_model("LiquidAI/LFM2-VL-450M")
88
+
89
+ model = cactus_init(str(bundle), None, False)
90
+ messages = json.dumps([{"role": "user", "content": "What is 2+2?"}])
91
+ result = cactus_complete(model, messages, None, None, None)
92
+ print(result["response"])
93
+ cactus_destroy(model)
94
+ ```
95
+ <!-- --8<-- [end:example] -->
96
+
97
+ ## API Reference
98
+
99
+ All functions are module-level and mirror the C FFI directly. Handles are plain `int` values (C pointers).
100
+
101
+ ### Model Downloads
102
+
103
+ Download pre-built bundles programmatically (no CLI needed):
104
+
105
+ ```python
106
+ from cactus import ensure_model, get_bundle_dir
107
+
108
+ # ensure_model downloads the pre-built bundle if missing, returns its Path
109
+ bundle = ensure_model("openai/whisper-tiny")
110
+
111
+ # Or resolve the expected on-disk location explicitly
112
+ bundle_dir = get_bundle_dir("openai/whisper-tiny", bits=4, platform=None)
113
+ # -> Path("weights/whisper-tiny-cq4") (or `-cq4-apple` with platform="apple")
114
+ ```
115
+
116
+ ### Init / Lifecycle
117
+
118
+ ```python
119
+ model = cactus_init(model_path: str, corpus_dir: str | None, cache_index: bool) -> int
120
+ cactus_destroy(model: int)
121
+ cactus_reset(model: int) # clear KV cache
122
+ cactus_stop(model: int) # abort ongoing generation
123
+ cactus_get_last_error() -> str | None
124
+ ```
125
+
126
+ ### Completion
127
+
128
+ Returns a `dict` with `success`, `error`, `cloud_handoff`, `response`, optional `thinking` (only present when the model emits chain-of-thought content, placed before `function_calls`), `function_calls`, `segments` (always `[]` for completion — populated only for Whisper transcription with the `timestamps` option), `confidence`, timing stats (`time_to_first_token_ms`, `total_time_ms`, `prefill_tps`, `decode_tps`, `ram_usage_mb`), and token counts (`prefill_tokens`, `decode_tokens`, `total_tokens`).
129
+
130
+ ```python
131
+ result = cactus_complete(
132
+ model: int,
133
+ messages: list | str, # list of {role, content} dicts or JSON string
134
+ options: dict | str | None, # optional inference options
135
+ tools: list | str | None, # optional tool definitions
136
+ callback: Callable[[str, int], None] | None, # streaming token callback
137
+ pcm_data: list[int] | None = None # optional raw audio bytes
138
+ ) -> dict
139
+ ```
140
+
141
+ ```python
142
+ # With options and streaming
143
+ options = json.dumps({"max_tokens": 256, "temperature": 0.7})
144
+ def on_token(token, token_id): print(token, end="", flush=True)
145
+
146
+ result = cactus_complete(model, messages, options, None, on_token)
147
+ if result["cloud_handoff"]:
148
+ # response already contains cloud result
149
+ pass
150
+ ```
151
+
152
+ **Response format:**
153
+ ```json
154
+ {
155
+ "success": true,
156
+ "error": null,
157
+ "cloud_handoff": false,
158
+ "response": "4",
159
+ "function_calls": [],
160
+ "segments": [],
161
+ "confidence": 0.92,
162
+ "confidence_threshold": 0.7,
163
+ "time_to_first_token_ms": 45.2,
164
+ "total_time_ms": 163.7,
165
+ "prefill_tps": 619.5,
166
+ "decode_tps": 168.4,
167
+ "ram_usage_mb": 512.3,
168
+ "prefill_tokens": 28,
169
+ "decode_tokens": 12,
170
+ "total_tokens": 40
171
+ }
172
+ ```
173
+
174
+ ### Prefill
175
+
176
+ Pre-processes input text and populates the KV cache without generating output tokens. This reduces latency for subsequent calls to `cactus_complete`.
177
+
178
+ ```python
179
+ cactus_prefill(
180
+ model: int,
181
+ messages: list | str, # list of {role, content} dicts or JSON string
182
+ options: dict | str | None, # optional inference options
183
+ tools: list | str | None, # optional tool definitions
184
+ pcm_data: list[int] | None = None # optional raw audio bytes
185
+ ) -> dict
186
+ ```
187
+
188
+ ```python
189
+ tools = json.dumps([{
190
+ "type": "function",
191
+ "function": {
192
+ "name": "get_weather",
193
+ "description": "Get weather for a location",
194
+ "parameters": {
195
+ "type": "object",
196
+ "properties": {
197
+ "location": {"type": "string", "description": "City, State, Country"}
198
+ },
199
+ "required": ["location"]
200
+ }
201
+ }
202
+ }])
203
+
204
+ messages = json.dumps([
205
+ {"role": "system", "content": "You are a helpful assistant."},
206
+ {"role": "user", "content": "What is the weather in Paris?"},
207
+ {"role": "assistant", "content": "<|tool_call_start|>get_weather(location=\"Paris\")<|tool_call_end|>"},
208
+ {"role": "tool", "content": "{\"name\": \"get_weather\", \"content\": \"Sunny, 72°F\"}"},
209
+ {"role": "assistant", "content": "It's sunny and 72°F in Paris!"}
210
+ ])
211
+ cactus_prefill(model, messages, None, tools)
212
+
213
+ completion_messages = json.dumps([
214
+ {"role": "system", "content": "You are a helpful assistant."},
215
+ {"role": "user", "content": "What is the weather in Paris?"},
216
+ {"role": "assistant", "content": "<|tool_call_start|>get_weather(location=\"Paris\")<|tool_call_end|>"},
217
+ {"role": "tool", "content": "{\"name\": \"get_weather\", \"content\": \"Sunny, 72°F\"}"},
218
+ {"role": "assistant", "content": "It's sunny and 72°F in Paris!"},
219
+ {"role": "user", "content": "What about SF?"}
220
+ ])
221
+ result = cactus_complete(model, completion_messages, None, tools, None)
222
+ ```
223
+
224
+ **Response format:**
225
+ ```json
226
+ {
227
+ "success": true,
228
+ "error": null,
229
+ "prefill_tokens": 25,
230
+ "prefill_tps": 166.1,
231
+ "total_time_ms": 150.5,
232
+ "ram_usage_mb": 245.67
233
+ }
234
+ ```
235
+
236
+ ### Transcription
237
+
238
+ Returns a `dict` with the `response` field (transcribed text) and a `segments` array of `{start, end, text}` objects. `segments` is populated only for Whisper models when the `timestamps` option is set (`{"timestamps": True}`); it is empty otherwise, including for all Parakeet transcription.
239
+
240
+ ```python
241
+ result = cactus_transcribe(
242
+ model: int,
243
+ audio_path: str | None,
244
+ prompt: str | None,
245
+ options: dict | str | None,
246
+ callback: Callable[[str, int], None] | None,
247
+ pcm_data: list[int] | bytes | None
248
+ ) -> dict
249
+ ```
250
+
251
+ **Custom vocabulary** biases the decoder toward domain-specific words (supported for Whisper and Moonshine models). Pass `custom_vocabulary` and `vocabulary_boost` in `options`:
252
+
253
+ ```python
254
+ options = json.dumps({
255
+ "custom_vocabulary": ["Omeprazole", "HIPAA", "Cactus"],
256
+ "vocabulary_boost": 3.0
257
+ })
258
+ result = cactus_transcribe(model, "medical_notes.wav", None, options, None, None)
259
+ ```
260
+
261
+ ```python
262
+ result = cactus_transcribe(model, "/path/to/audio.wav", None, {"timestamps": True}, None, None)
263
+ print(result["response"])
264
+ for seg in result["segments"]:
265
+ print(f"[{seg['start']:.3f}s - {seg['end']:.3f}s] {seg['text']}")
266
+ ```
267
+
268
+ ### Streaming transcription
269
+
270
+ Transcribe continuously while audio is still being captured (Whisper and Parakeet TDT). Open a session, push 16 kHz mono 16-bit PCM chunks, and read text back as it stabilizes: `confirmed` words are final (append them to your transcript), `pending` is the volatile tail (replace it each call, for live display only).
271
+
272
+ ```python
273
+ stream = cactus_stream_transcribe_start(model: int, options: dict | str | None) -> int
274
+ result = cactus_stream_transcribe_process(stream: int, pcm_data: bytes) -> dict # {"confirmed": str, "pending": str, plus per-call timing stats}
275
+ result = cactus_stream_transcribe_stop(stream: int) -> dict # {"confirmed": str, "pending": ""}; destroys the session
276
+ ```
277
+
278
+ `options` is forwarded to `cactus_transcribe` for **Whisper only** (e.g. `language`, `max_tokens`); the Parakeet TDT path ignores it. Chunking is handled internally.
279
+
280
+ ```python
281
+ stream = cactus_stream_transcribe_start(model, {"language": "en"})
282
+ transcript = ""
283
+ for chunk in pcm_chunks: # each chunk: 16 kHz mono 16-bit PCM bytes
284
+ out = cactus_stream_transcribe_process(stream, chunk)
285
+ transcript += out["confirmed"] # show out["pending"] separately as a live preview
286
+ transcript += cactus_stream_transcribe_stop(stream)["confirmed"]
287
+ ```
288
+
289
+ ### Embeddings
290
+
291
+ ```python
292
+ embedding = cactus_embed(model: int, text: str, normalize: bool) -> list[float]
293
+ embedding = cactus_image_embed(model: int, image_path: str) -> list[float]
294
+ embedding = cactus_audio_embed(model: int, audio_path: str) -> list[float]
295
+ ```
296
+
297
+ ### Tokenization
298
+
299
+ ```python
300
+ tokens = cactus_tokenize(model: int, text: str) -> list[int]
301
+ result = cactus_score_window(model: int, tokens: list[int], start: int, end: int, context: int) -> dict
302
+ ```
303
+
304
+ ### RAG
305
+
306
+ ```python
307
+ result = cactus_rag_query(model: int, query: str, top_k: int) -> dict
308
+ ```
309
+
310
+ Returns a `dict` with a `chunks` array. Each chunk has `score` (float), `source` (str, from document metadata), and `content` (str):
311
+
312
+ ```json
313
+ {
314
+ "chunks": [
315
+ {"score": 0.0142, "source": "doc.txt", "content": "relevant passage..."}
316
+ ]
317
+ }
318
+ ```
319
+
320
+ ### Vector Index
321
+
322
+ ```python
323
+ index = cactus_index_init(index_dir: str, embedding_dim: int) -> int
324
+ cactus_index_add(index: int, ids: list[int], documents: list[str],
325
+ metadatas: list[str] | None, embeddings: list[list[float]])
326
+ cactus_index_delete(index: int, ids: list[int])
327
+ result = cactus_index_get(index: int, ids: list[int]) -> dict
328
+ result = cactus_index_query(index: int, embedding: list[float], options: dict | str | None) -> dict
329
+ cactus_index_compact(index: int)
330
+ cactus_index_destroy(index: int)
331
+ ```
332
+
333
+ `cactus_index_query` returns `{"results":[{"id":<int>,"score":<float>}, ...]}`. `cactus_index_get` returns `{"results":[{"document":"...","metadata":<str|null>,"embedding":[...]}, ...]}`.
334
+
335
+ ### Logging
336
+
337
+ ```python
338
+ cactus_log_set_level(level: int) # 0=DEBUG 1=INFO 2=WARN (default) 3=ERROR 4=NONE
339
+ cactus_log_set_callback(callback: Callable[[int, str, str], None] | None)
340
+ ```
341
+
342
+ ### Telemetry
343
+
344
+ ```python
345
+ cactus_set_telemetry_environment(framework: str, cache_location: str | None, version: str | None)
346
+ cactus_set_app_id(app_id: str)
347
+ cactus_telemetry_flush()
348
+ cactus_telemetry_shutdown()
349
+ ```
350
+
351
+ Functions that return a value raise `RuntimeError` on failure. `cactus_index_add`, `cactus_index_delete`, and `cactus_index_compact` also raise `RuntimeError` on failure despite not returning a value. Truly void functions that never raise: `cactus_destroy`, `cactus_reset`, `cactus_stop`, `cactus_index_destroy`, logging and telemetry functions.
352
+
353
+ ## Vision (VLM)
354
+
355
+ Pass images in the messages content for vision-language models (LFM2-VL, LFM2.5-VL, Gemma4, Qwen3.5):
356
+
357
+ ```python
358
+ messages = json.dumps([{
359
+ "role": "user",
360
+ "content": "Describe this image",
361
+ "images": ["path/to/image.png"]
362
+ }])
363
+ result = cactus_complete(model, messages, None, None, None)
364
+ print(result["response"])
365
+ ```
366
+
367
+ ## Audio (Multimodal)
368
+
369
+ Pass audio files in messages for models with native audio understanding (Gemma4):
370
+
371
+ ```python
372
+ messages = json.dumps([{
373
+ "role": "user",
374
+ "content": "Transcribe the audio.",
375
+ "audio": ["path/to/audio.wav"]
376
+ }])
377
+ result = cactus_complete(model, messages, None, None, None)
378
+ print(result["response"])
379
+
380
+ # Combined vision + audio
381
+ messages = json.dumps([{
382
+ "role": "user",
383
+ "content": "Describe the image and transcribe the audio.",
384
+ "images": ["path/to/image.png"],
385
+ "audio": ["path/to/audio.wav"]
386
+ }])
387
+ result = cactus_complete(model, messages, None, None, None)
388
+ ```
389
+
390
+ ## Compute Graph
391
+
392
+ The `Graph` API provides a tensor computation graph for building and executing dataflow pipelines on the Cactus kernel layer:
393
+
394
+ ```python
395
+ from cactus.bindings.cactus import Graph
396
+ import numpy as np
397
+
398
+ g = Graph()
399
+ a = g.input((2, 2))
400
+ b = g.input((2, 2))
401
+ y = ((a - b) * (a + b)).abs().pow(2.0).view((4,))
402
+
403
+ g.set_input(a, np.array([[2, 4], [6, 8]], dtype=np.float16))
404
+ g.set_input(b, np.array([[1, 2], [3, 4]], dtype=np.float16))
405
+ g.execute()
406
+
407
+ print(y.numpy()) # [9. 144. 729. 2304.]
408
+ ```
409
+
410
+ Supported ops: `+`, `-`, `*`, `/`, `abs`, `pow`, `view`, `flatten`, `concat`, `cat`, `relu`, `sigmoid`, `tanh`, `gelu`, `softmax`.
411
+
412
+ ## Testing
413
+
414
+ Run the full test suite:
415
+
416
+ ```bash
417
+ python python/test.py # compact output
418
+ python python/test.py -v # verbose
419
+ ```
420
+
421
+ Tests are in `python/tests/` — bindings, CLI, server, graph, model, transpile,
422
+ and component-partition coverage. Add a new `test_*.py` to extend.
423
+
424
+ ## See Also
425
+
426
+ - `Cactus Engine API` — Full C API reference that the Python bindings wrap
427
+ - `Cactus Index API` — Vector database API for RAG applications
428
+ - `Fine-tuning Guide` — Train and deploy custom LoRA fine-tunes
429
+ - `Runtime Compatibility` — Weight versioning across releases
430
+ - [Apple Build Step](/apple/) — Builds Apple native artifacts used by bindings
431
+ - [Android Build Step](/android/) — Builds Android native artifacts used by bindings
432
+ - [Swift Bindings](/bindings/swift/) — Swift C-module bindings
433
+ - [Kotlin Bindings](/bindings/kotlin/) — Kotlin/JNI bindings
434
+ - [Flutter Bindings](/bindings/flutter/) — Dart FFI bindings
435
+ - [Rust Bindings](/bindings/rust/) — Raw Rust FFI declarations