Connecting a model
knaif is local-only. There is no key to configure and no hosted endpoint; you point it at a model running on the same machine.
Ollama
Section titled “Ollama”import knaif.cli as nk
orch = nk.local_ollama(model="qwen3:4b") # None + a warning if Ollama is unreachableapp = nk.App([now], orchestrator=orch)Prefer local_ollama() over building the orchestrator yourself. It is not a
convenience wrapper — it picks the settings that make reasoning models work.
If you do construct it directly, pass the same three:
from knaif.orchestrator import InferenceOrchestrator
orch = InferenceOrchestrator( backend="ollama", model_name="qwen3:4b", model_config={"json_mode": False, "thinking_enabled": True, "max_tokens": 2048},)llama.cpp (GGUF)
Section titled “llama.cpp (GGUF)”orch = InferenceOrchestrator(backend="llama_cpp", model_path="models/qwen3-4b.gguf")app = nk.App([now], orchestrator=orch)No model at all
Section titled “No model at all”Leave the orchestrator out and App.invoke() uses a mock backend with seeded
responses. This is the right default for tests: it exercises retrieval, validation,
coercion and dispatch without downloading anything, so CI stays fast and offline.
result = nk.App([now]).invoke("what time is it in Tokyo", dry_run=True)Which model
Section titled “Which model”Start with a 4B. Below that, accuracy on argument extraction drops off sharply, and knaif’s own evaluation says so rather than hiding it.
knaif publishes two fine-tunes of Qwen3 on HuggingFace — knaif-qwen3-4b-v1 (2.5 GB, the
default) and knaif-qwen3-1.7b-v1 (1.32 GB). Both are trained on its bundled skills, so
they are a reasonable starting point for an SDK app too, though they are tuned for those
skills’ vocabulary — if your commands are far from media and document work, a stock
instruct model may route just as well. Measuring beats guessing; see
Evaluate a skill for the harness.
Names, sizes, checksums and the evidence behind each choice: Released models.
A custom backend
Section titled “A custom backend”To use something knaif does not ship, implement the InferenceBackend protocol from
knaif.orchestrator and pass the instance straight to App:
from collections.abc import Iterator
class MyBackend: def infer(self, prompt: str) -> str: ... def infer_stream(self, prompt: str) -> Iterator[str]: ...
app = nk.App([now], orchestrator=MyBackend())The contract is deliberately small — prompt in, text out. Everything knaif does with that text is identical regardless of where it came from.