ClikaRT at a glance
ClikaRT is CLIKA's inference runtime, a C++ library (with Python and Kotlin bindings) that loads models and runs them on CPUs, GPUs, and other hardware accelerators through one public API. It is not a training framework, and not a bundle of separate CUDA, Vulkan and Metal wrappers. You link one library, include one header, and the same code runs on every backend the runtime ships.
The mental model
Five ideas carry the whole library, in the order you meet them when you build.
-
Everything arrives in one bundle. A directory with the public headers, the libraries per platform, a CMake package and the examples.
find_package(ClikaRT CONFIG)and the targetClikaRT::ClikaRTare the entire integration. -
Models load from files you already have.
ClikaRT::ioreads safetensors, GGUF and NumPy checkpoints, plus images and audio, and a model checkpoint arrives as aname -> tensormap on the device you name.- C++
- Python
- Kotlin
const NamedTensors weights = io::load_safetensors("model.safetensors");const Tensor w = weights.get("layer.weight");weights = crt.io.load_safetensors("model.safetensors")w = weights["layer.weight"]The Kotlin binding carries no checkpoint reader: a model reaches an app through the model library (
AutoModel.fromPretrained), or as an ONNX model or a compiled graph; tensors and operators carry theOpssurface, one function per operator. -
Tensoris the core building block. Each tensor is assigned to a device (the CPU by default;.to(device)moves it), andops::operators run where their inputs live. The CPU backend is always present; CUDA, Vulkan and Metal load at run time where the machine has them. Everything returns values directly and raisesClikaRT::Erroron failure.- C++
- Python
- Kotlin
const Tensor a = Tensor::ones({2, 3}, DataType::Float32);const Tensor b = ops::add(a, a);std::printf("%s\n", b.to_string().c_str());a = crt.ones(2, 3)b = a + aprint(b)val a = Tensors.ones(longArrayOf(2, 3))val b = a + a // operator extensions return a fresh tensor; operands stay liveprintln(b.summary()) -
Execution is asynchronous by nature. An
ops::call dispatches work and returns; reads wait for the result, so you never observe unfinished bytes. -
Serving is built in. The serving runtime adds sessions, continuous batching and pipelines, so the model you loaded answers requests. Declare a schema, serve it with a lambda, drive it through an executor:
- C++
- Python
- Kotlin
rt::FunctionModel model{schema};model.on_run_once("run", [](rt::PhaseContext& ctx) {ctx.outputs->set("y", ctx.inputs->get("x") * 2.0);});rt::Executor exec = rt::Executor::create(model);const rt::Response resp = exec.await(exec.enqueue(std::move(req)));The Python lane serves a traced graph:
crt.tracecaptures a forward pass as aModelGraph, which runs once finalized, andcrt.runtimecarries theExecutorandPipelineover it. Part 5 walks it.The Kotlin binding carries the serving runtime (
FunctionModel,Executor,Pipeline) and the model library's server (Modelverse.serve), on a phone and on a desktop JVM alike.
The first program series turns these into working programs, explaining each where it first appears; the serving runtime has its own example project.
Platforms
Linux (x86_64, arm64), Android (arm64), Windows (x86_64, arm64) and macOS (Apple silicon), one distribution per platform and architecture, and a distribution works out of the box on every machine of its class: the linux-arm64 dist runs on a Jetson the same way it runs on an arm64 server. System requirements has the platform and accelerator tables.