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.
- cactus_compute-2.0.0/PKG-INFO +435 -0
- cactus_compute-2.0.0/README.md +387 -0
- cactus_compute-2.0.0/cactus/__init__.py +61 -0
- cactus_compute-2.0.0/cactus/__main__.py +4 -0
- cactus_compute-2.0.0/cactus/_version.py +1 -0
- cactus_compute-2.0.0/cactus/assets/test.wav +0 -0
- cactus_compute-2.0.0/cactus/assets/test_monkey.png +0 -0
- cactus_compute-2.0.0/cactus/bindings/__init__.py +0 -0
- cactus_compute-2.0.0/cactus/bindings/cactus.py +3035 -0
- cactus_compute-2.0.0/cactus/cli/__init__.py +522 -0
- cactus_compute-2.0.0/cactus/cli/auth.py +41 -0
- cactus_compute-2.0.0/cactus/cli/clean.py +121 -0
- cactus_compute-2.0.0/cactus/cli/code.py +188 -0
- cactus_compute-2.0.0/cactus/cli/common.py +128 -0
- cactus_compute-2.0.0/cactus/cli/compile.py +220 -0
- cactus_compute-2.0.0/cactus/cli/config_utils.py +57 -0
- cactus_compute-2.0.0/cactus/cli/convert.py +183 -0
- cactus_compute-2.0.0/cactus/cli/download.py +73 -0
- cactus_compute-2.0.0/cactus/cli/list.py +95 -0
- cactus_compute-2.0.0/cactus/cli/model.py +394 -0
- cactus_compute-2.0.0/cactus/cli/run.py +54 -0
- cactus_compute-2.0.0/cactus/cli/runtime.py +115 -0
- cactus_compute-2.0.0/cactus/cli/serve.py +90 -0
- cactus_compute-2.0.0/cactus/cli/test.py +111 -0
- cactus_compute-2.0.0/cactus/cli/transcribe.py +31 -0
- cactus_compute-2.0.0/cactus/cli/transpile.py +66 -0
- cactus_compute-2.0.0/cactus/cli/upload.py +53 -0
- cactus_compute-2.0.0/cactus/cli/utils.py +408 -0
- cactus_compute-2.0.0/cactus/convert/__init__.py +1 -0
- cactus_compute-2.0.0/cactus/convert/assets/gemma-4-e2b-it/probe.pt +0 -0
- cactus_compute-2.0.0/cactus/convert/cactus_adapters/__init__.py +2 -0
- cactus_compute-2.0.0/cactus/convert/cactus_adapters/config_utils.py +626 -0
- cactus_compute-2.0.0/cactus/convert/cactus_adapters/tensor_io.py +647 -0
- cactus_compute-2.0.0/cactus/convert/cactus_adapters/tokenizer.py +597 -0
- cactus_compute-2.0.0/cactus/convert/cactus_adapters/weight_patterns.py +421 -0
- cactus_compute-2.0.0/cactus/convert/calibration/__init__.py +2 -0
- cactus_compute-2.0.0/cactus/convert/calibration/hessian.py +440 -0
- cactus_compute-2.0.0/cactus/convert/calibration/loaders.py +55 -0
- cactus_compute-2.0.0/cactus/convert/cli.py +638 -0
- cactus_compute-2.0.0/cactus/convert/compat.py +34 -0
- cactus_compute-2.0.0/cactus/convert/export/__init__.py +2 -0
- cactus_compute-2.0.0/cactus/convert/export/files.py +68 -0
- cactus_compute-2.0.0/cactus/convert/export/qdq.py +564 -0
- cactus_compute-2.0.0/cactus/convert/export/reports.py +54 -0
- cactus_compute-2.0.0/cactus/convert/export/validate.py +104 -0
- cactus_compute-2.0.0/cactus/convert/handoff_probe.py +201 -0
- cactus_compute-2.0.0/cactus/convert/interleave_orthogonal_cq4.py +97 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/__init__.py +2 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/adapters.py +958 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/detection.py +22 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/naming.py +424 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/nemo.py +140 -0
- cactus_compute-2.0.0/cactus/convert/model_adapters/policy.py +74 -0
- cactus_compute-2.0.0/cactus/convert/quantization/__init__.py +2 -0
- cactus_compute-2.0.0/cactus/convert/quantization/cq.py +410 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_cq.py +157 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_gemma4_adapter.py +120 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_hessian.py +56 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_lfm2_adapter.py +106 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_naming_qdq.py +404 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_policy.py +293 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_qwen_adapter.py +64 -0
- cactus_compute-2.0.0/cactus/convert/tests/test_reports.py +36 -0
- cactus_compute-2.0.0/cactus/py.typed +0 -0
- cactus_compute-2.0.0/cactus/server.py +922 -0
- cactus_compute-2.0.0/cactus/transpile/__init__.py +1 -0
- cactus_compute-2.0.0/cactus/transpile/aten_ops.py +191 -0
- cactus_compute-2.0.0/cactus/transpile/audio_preprocess.py +400 -0
- cactus_compute-2.0.0/cactus/transpile/canonicalize/cleanup.py +707 -0
- cactus_compute-2.0.0/cactus/transpile/canonicalize/utils.py +277 -0
- cactus_compute-2.0.0/cactus/transpile/capture_jax.py +1663 -0
- cactus_compute-2.0.0/cactus/transpile/capture_pytorch.py +375 -0
- cactus_compute-2.0.0/cactus/transpile/component_partition.py +335 -0
- cactus_compute-2.0.0/cactus/transpile/component_pipeline.py +213 -0
- cactus_compute-2.0.0/cactus/transpile/component_plan.py +248 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/__init__.py +47 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/attention.py +550 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/common.py +73 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/conv.py +123 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/deltanet.py +357 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/linear.py +58 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/lstm.py +216 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/mlp.py +53 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/rel_pos_bias.py +179 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/rms_norm.py +133 -0
- cactus_compute-2.0.0/cactus/transpile/fusion/rope.py +252 -0
- cactus_compute-2.0.0/cactus/transpile/graph_ir.py +201 -0
- cactus_compute-2.0.0/cactus/transpile/hf_model.py +3576 -0
- cactus_compute-2.0.0/cactus/transpile/import_ir.py +277 -0
- cactus_compute-2.0.0/cactus/transpile/import_semantics.py +337 -0
- cactus_compute-2.0.0/cactus/transpile/importers.py +1960 -0
- cactus_compute-2.0.0/cactus/transpile/jax_semantic_rewrites.py +875 -0
- cactus_compute-2.0.0/cactus/transpile/jax_user_graph_bundle.py +575 -0
- cactus_compute-2.0.0/cactus/transpile/lower.py +3533 -0
- cactus_compute-2.0.0/cactus/transpile/media_limits.py +35 -0
- cactus_compute-2.0.0/cactus/transpile/model_adapters.py +7329 -0
- cactus_compute-2.0.0/cactus/transpile/model_patterns.py +64 -0
- cactus_compute-2.0.0/cactus/transpile/model_profiles.py +307 -0
- cactus_compute-2.0.0/cactus/transpile/multimodal_runtime.py +722 -0
- cactus_compute-2.0.0/cactus/transpile/normalize.py +29 -0
- cactus_compute-2.0.0/cactus/transpile/npu/__init__.py +3 -0
- cactus_compute-2.0.0/cactus/transpile/npu/audio.py +115 -0
- cactus_compute-2.0.0/cactus/transpile/npu/coremltools_patches.py +434 -0
- cactus_compute-2.0.0/cactus/transpile/npu/pipeline.py +94 -0
- cactus_compute-2.0.0/cactus/transpile/npu/source.py +118 -0
- cactus_compute-2.0.0/cactus/transpile/npu/vision.py +126 -0
- cactus_compute-2.0.0/cactus/transpile/ops.py +292 -0
- cactus_compute-2.0.0/cactus/transpile/optimize_graph.py +1656 -0
- cactus_compute-2.0.0/cactus/transpile/runtime_compat.py +376 -0
- cactus_compute-2.0.0/cactus/transpile/runtime_support.py +178 -0
- cactus_compute-2.0.0/cactus/transpile/tdt_runtime.py +1028 -0
- cactus_compute-2.0.0/cactus/transpile/weight_binding.py +363 -0
- cactus_compute-2.0.0/cactus/transpile/weight_compat.py +568 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/PKG-INFO +435 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/SOURCES.txt +141 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/dependency_links.txt +1 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/entry_points.txt +2 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/requires.txt +24 -0
- cactus_compute-2.0.0/cactus_compute.egg-info/top_level.txt +1 -0
- cactus_compute-2.0.0/pyproject.toml +74 -0
- cactus_compute-2.0.0/setup.cfg +4 -0
- cactus_compute-2.0.0/tests/test_bindings.py +500 -0
- cactus_compute-2.0.0/tests/test_capture_jax.py +197 -0
- cactus_compute-2.0.0/tests/test_capture_jax_user_graph_bundle.py +196 -0
- cactus_compute-2.0.0/tests/test_chat_template_golden.py +239 -0
- cactus_compute-2.0.0/tests/test_cli_run.py +70 -0
- cactus_compute-2.0.0/tests/test_cli_transpile_defaults.py +187 -0
- cactus_compute-2.0.0/tests/test_cloud_handoff_live.py +73 -0
- cactus_compute-2.0.0/tests/test_component_partition.py +55 -0
- cactus_compute-2.0.0/tests/test_component_plan.py +36 -0
- cactus_compute-2.0.0/tests/test_download.py +247 -0
- cactus_compute-2.0.0/tests/test_encoder_cross_kv_route.py +79 -0
- cactus_compute-2.0.0/tests/test_graph.py +745 -0
- cactus_compute-2.0.0/tests/test_model.py +162 -0
- cactus_compute-2.0.0/tests/test_nomic_text_embedding.py +37 -0
- cactus_compute-2.0.0/tests/test_optimize_gemma4_attention.py +328 -0
- cactus_compute-2.0.0/tests/test_server.py +272 -0
- cactus_compute-2.0.0/tests/test_server_live.py +518 -0
- cactus_compute-2.0.0/tests/test_tool_constraint_state.py +224 -0
- cactus_compute-2.0.0/tests/test_transpile_aten_ops.py +272 -0
- cactus_compute-2.0.0/tests/test_transpile_importers.py +32 -0
- cactus_compute-2.0.0/tests/test_transpile_layer_keys.py +68 -0
- 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
|