Small local HTTP server for SemIf.
The server keeps one SemIf decision model loaded in memory and exposes generation-free semantic decisions over HTTP. It returns option probabilities and logits instead of generating answer text.
POST /v1/noul— binary yes/no decisionsPOST /v1/choice— decisions between arbitrary optionsPOST /v1/shared— multiple decisions over one shared statePOST /v1/cache/clear— clear reusable runtime caches without unloading the model- serial prefix-cache reuse
- request latency and probability statistics
- lightweight browser playground and dashboard
- JSONL request logs
- FastAPI/OpenAPI interface
- MLX, CUDA, and llama.cpp backends
POST /v1/decision is kept as a compatibility alias for POST /v1/choice.
- Python 3.10+
- uv
- one of the supported runtimes below
| Backend | Platforms | Hardware |
|---|---|---|
mlx |
macOS Apple Silicon | Apple GPU / unified memory |
cuda |
Linux, Windows | one visible NVIDIA CUDA GPU |
llamacpp |
macOS, Linux, Windows | CPU |
The default config.json is configured for the MLX backend on Apple Silicon.
git clone https://github.com/ILuce/semif-local
cd semif-local-server
uv python install 3.12Runtime defaults live in config.json:
{
"backend": "mlx",
"model": "models/semif-qwen3.5-4b-mlx-4bit",
"model_revision": "local-vinci00-semif-qwen35-4b-mlx4",
"max_tokens": 4096,
"mlx_cache_mib": 256,
"log": "logs/requests.jsonl",
"torch_dtype": "bfloat16",
"llama_gguf": "models/llamacpp/Qwen_Qwen3.5-4B-Q4_K_M.gguf",
"llama_threads": null
}Relative file paths are resolved relative to config.json.
The original environment variables remain available as optional overrides:
SEMIF_MODELSEMIF_MODEL_REVISIONSEMIF_MAX_TOKENSSEMIF_MLX_CACHE_MIBSEMIF_LOG
Additional overrides:
SEMIF_CONFIG— alternate JSON configuration fileSEMIF_BACKEND—mlx,cuda, orllamacppSEMIF_TORCH_DTYPE—bfloat16,float16, orfloat32SEMIF_GGUF— llama.cpp GGUF pathSEMIF_LLAMA_THREADS— positive CPU thread count
Install the locked project:
uv sync --frozenDownload the 4-bit MLX model:
uv run hf download \
vinci00/semif-qwen3.5-4b-mlx-4bit \
--local-dir models/semif-qwen3.5-4b-mlx-4bitUse the default config.json:
{
"backend": "mlx",
"model": "models/semif-qwen3.5-4b-mlx-4bit",
"model_revision": "local-vinci00-semif-qwen35-4b-mlx4",
"max_tokens": 4096,
"mlx_cache_mib": 256,
"log": "logs/requests.jsonl",
"torch_dtype": "bfloat16",
"llama_gguf": "models/llamacpp/Qwen_Qwen3.5-4B-Q4_K_M.gguf",
"llama_threads": null
}Run:
uv run semif-serverInstall:
uv sync --frozenA compatible NVIDIA driver is required. SemIf expects exactly one visible CUDA GPU for a scorer process.
Set config.json to:
{
"backend": "cuda",
"model": "Qwen/Qwen3.5-4B",
"model_revision": "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a",
"max_tokens": 4096,
"mlx_cache_mib": 256,
"log": "logs/requests.jsonl",
"torch_dtype": "bfloat16",
"llama_gguf": "models/llamacpp/Qwen_Qwen3.5-4B-Q4_K_M.gguf",
"llama_threads": null
}Then run with one visible GPU:
CUDA_VISIBLE_DEVICES=0 uv run semif-serverThe model is downloaded by Transformers/Hugging Face on first use unless model points to a local Transformers checkpoint.
Install from PowerShell:
uv sync --frozenUse the same CUDA config.json shown in the Linux section.
Expose one GPU and start the server:
$env:CUDA_VISIBLE_DEVICES="0"
uv run semif-serverA compatible NVIDIA driver is required. The model is downloaded on first use unless a local Transformers checkpoint is configured.
Install the llama.cpp extra:
uv sync --frozen --extra llamacppDownload a compatible GGUF:
mkdir -p models/llamacpp
uv run hf download \
bartowski/Qwen_Qwen3.5-4B-GGUF \
Qwen_Qwen3.5-4B-Q4_K_M.gguf \
--local-dir models/llamacppOn PowerShell, create the directory with:
New-Item -ItemType Directory -Force models/llamacppThen run the same uv run hf download ... command on one line or using PowerShell backticks for line continuation.
Set config.json to:
{
"backend": "llamacpp",
"model": "Qwen/Qwen3.5-4B",
"model_revision": "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a",
"max_tokens": 4096,
"mlx_cache_mib": 256,
"log": "logs/requests.jsonl",
"torch_dtype": "bfloat16",
"llama_gguf": "models/llamacpp/Qwen_Qwen3.5-4B-Q4_K_M.gguf",
"llama_threads": null
}llama_threads: null lets SemIf use the visible CPU count. Set a positive integer to cap it.
Start:
uv run semif-serverllama-cpp-python is an upstream native dependency. On platforms where a compatible wheel is unavailable, a local C/C++ build toolchain may be required during installation.
Default addresses:
- server:
http://127.0.0.1:8787 - OpenAPI:
http://127.0.0.1:8787/docs - playground/dashboard:
http://127.0.0.1:8787/ui - health:
http://127.0.0.1:8787/health
The model is loaded once when the server starts and remains resident.
The /ui page can build and send all public decision request types without manually writing JSON:
Noul— context, question, direct/serial modeChoice— context, question, dynamic option list, direct/serial modeShared— one context and multiple dynamic decisions/options- Text or structured JSON state
- live generated request JSON
- probabilities, logits, token count, latency and cache status
- runtime statistics and recent requests
- runtime-cache clearing without unloading model weights
The cache button clears reusable prefix state and backend allocator/context caches where supported. It does not delete model files or the Hugging Face disk cache.
Binary decision:
curl -s \
-X POST http://127.0.0.1:8787/v1/noul \
-H 'Content-Type: application/json' \
-d '{
"state": "The cookie contains sugar, chocolate and vanilla.",
"question": "Is the cookie sweet?"
}'Choice decision:
curl -s \
-X POST http://127.0.0.1:8787/v1/choice \
-H 'Content-Type: application/json' \
-d '{
"state": "The customer cannot access their account.",
"question": "Which team should handle this request?",
"options": [
{"id": "access", "description": "Account access support."},
{"id": "billing", "description": "Billing support."}
]
}'Clear runtime cache:
curl -s -X POST http://127.0.0.1:8787/v1/cache/clearInstall development dependencies:
uv sync --frozen --extra devFor llama.cpp development:
uv sync --frozen --extra dev --extra llamacppValidate:
uv run python -m compileall -q src tests
uv run pytestRepository changes should follow AGENTS.md.
SemIf is pinned to an immutable Git revision in pyproject.toml.
When intentionally upgrading it:
- change the
revvalue under[tool.uv.sources]; - run
uv lock; - run the full test suite;
- start each backend available on the test machine;
- test
noul,choice,shared, and cache clearing; - compare representative probabilities and latency;
- commit both
pyproject.tomlanduv.lock.
This project is licensed under the MIT License. See LICENSE.
SemIf and model weights are separate upstream works and remain subject to their own licenses and terms.