C++ API reference · Kjarni 0.2.8

C++ API

Kjarni for C++23.

A header-only wrapper over kjarni.h. Nothing here allocates on its own account: every handle is a std::unique_ptr with the matching C deleter, and every array the C API hands back is owned by a move-only RAII type. Leaking or double-freeing takes deliberate effort.

Errors are values. Fallible calls return std::expected<T, kjarni::Error> rather than throwing, so a caller decides whether a missing model is exceptional. Nothing in this header throws except std::bad_alloc.

#include "kjarni.hpp"
auto embedder = kjarni::Embedder::create({.model = "minilm-l6-v2"});
if (!embedder) return std::println("{}", embedder.error().message()), 1;
auto vec = embedder->encode("semantic search, locally");
std::println("{} dimensions", vec->size());

Requires C++23 for std::expected. For C++20, swap std::expected for a std::variant or exceptions; everything else here is C++20.

Types

Error

A failed call, carrying the library's own diagnostic text.

The message is captured at construction rather than read lazily: the C API keeps one global "last error", so reading it later would report whatever failed most recently instead of what failed here.

Error

explicit Error(KjarniErrorCode code) : code_(code), message_(capture()) {} KjarniErrorCode code() const noexcept

code

KjarniErrorCode code() const noexcept

message

const std::string& message() const noexcept

Result

The result of any fallible Kjarni call.

using Result = std::expected<T, Error>;

Embedding

A single embedding. Borrowed as a std::span, so no copy is made.

Embedding

explicit Embedding(KjarniFloatArray arr) noexcept : arr_(arr) {} std::span<const float> values() const noexcept

values

std::span<const float> values() const noexcept

size

std::size_t size() const noexcept

data

const float* data() const noexcept

begin

auto begin() const noexcept

end

auto end() const noexcept

to_vector

std::vector<float> to_vector() const

Copies into an owning vector, for when the embedding must outlive this object.

Label

One predicted label.

std::string name;
float score{};

Ranked

One reranked document: an index into the input, and its score.

std::size_t index{};
float score{};

Hit

One search hit.

float score{};
std::size_t document_id{};
std::string text;
std::string metadata_json;

EmbedderOptions

Turns text into vectors. Designated-initialiser options: Embedder::create({.model = "mpnet-base-v2"}).

std::string model = "minilm-l6-v2";
std::string cache_dir;      ///< empty = default location
bool gpu = false;
bool normalize = true;
bool quiet = true;

Embedder

create

static Result<Embedder> create(const EmbedderOptions& opts = {})

encode

Result<Embedding> encode(std::string_view text) const

encode

Result<std::vector<std::vector<float>>> encode( std::span<const std::string> texts) const

Encodes a batch in one call, which is markedly faster than looping.

dimensions

std::size_t dimensions() const noexcept

ClassifierOptions

Sentiment, emotion and toxicity classification.

std::string model = "distilbert-sentiment";
std::string cache_dir;
bool gpu = false;
bool quiet = true;

Classifier

create

static Result<Classifier> create(const ClassifierOptions& opts = {})

classify

Result<std::vector<Label>> classify(std::string_view text) const

Every label with its score, highest first.

classify

Result<std::vector<std::vector<Label>>> classify(std::span<const std::string> texts) const

Every label with its score for each text, in order. One call for many texts is much faster than calling classify for each.

top

Result<std::optional<Label>> top(std::string_view text) const

The highest-scoring label, or std::nullopt if the model returned nothing.

num_labels

std::size_t num_labels() const noexcept

RerankerOptions

Cross-encoder reranking: rescores candidates you already have.

std::string model = "minilm-l6-v2-cross-encoder";
std::string cache_dir;
bool gpu = false;
bool quiet = true;

Reranker

create

static Result<Reranker> create(const RerankerOptions& opts = {})

score

Result<float> score(std::string_view query, std::string_view document) const

Scores one query-document pair. Raw logits: negative values are normal, and only the ordering is meaningful.

rerank

Result<std::vector<Ranked>> rerank(std::string_view query, std::span<const std::string> documents) const

Reorders documents, best first. Ranked::index points back into the input, so the caller keeps its own IDs and metadata.

rerank

Result<std::vector<Ranked>> rerank(std::string_view query, std::span<const std::string> documents, std::size_t k) const

Reranks and keeps only the best k.

Generation

Sampling controls. Defaults come from the model.

std::optional<float> temperature;
std::optional<int> top_k;
std::optional<float> top_p;
std::optional<int> max_new_tokens;
/// Deterministic output: same prompt, same answer.
///
/// Fields are assigned rather than designated-initialised so that a partial
/// initialiser list does not warn under `-Wextra` in consumers' builds.
[[nodiscard]] static Generation greedy(int max_tokens = 512) {
    Generation g;
    g.temperature = 0.f;
    g.max_new_tokens = max_tokens;
    return g;
}
[[nodiscard]] KjarniGenerationConfig to_c() const noexcept {
    KjarniGenerationConfig cfg = kjarni_generation_config_default();
    if (temperature) {
        cfg.temperature = *temperature;
        cfg.do_sample = *temperature > 0.f ? 1 : 0;
    }
    if (top_k) cfg.top_k = *top_k;
    if (top_p) cfg.top_p = *top_p;
    if (max_new_tokens) cfg.max_new_tokens = *max_new_tokens;
    return cfg;
}

ChatOptions

A local language model.

Generation is compute-bound and the underlying model is not re-entrant, so one instance serves one request at a time. Share it behind your own queue rather than creating several.

std::string model = "llama3.2-3b-instruct";
std::string system_prompt;
bool gpu = false;
bool quiet = true;
/// Load the 4-bit GGUF weights instead of full precision: a fraction of the
/// memory and several times faster to generate on a CPU.
bool quantized = false;

Chat

create

static Result<Chat> create(const ChatOptions& opts = {})

send

Result<std::string> send(std::string_view message) const

Generates a full response before returning.

send

Result<std::string> send(std::string_view message, const Generation& gen) const

stream

template <typename F> std::expected<void, Error> stream(std::string_view message, F&& on_token) const

Streams tokens as they are produced.

on_token is any callable taking std::string_view. Return false from it to stop generation early rather than running to completion and discarding the result.

stream

template <typename F> std::expected<void, Error> stream(std::string_view message, const Generation& gen, F&& on_token) const

context_size

std::size_t context_size() const noexcept

The model's context window, in tokens.

IndexStats

What an indexing run did.

std::size_t documents_indexed{};
std::size_t chunks_created{};
std::size_t dimension{};
std::uint64_t size_bytes{};
std::size_t files_processed{};
std::size_t files_skipped{};
std::uint64_t elapsed_ms{};

IndexerOptions

std::string model = "minilm-l6-v2";
std::string cache_dir;
/// Characters per chunk; 0 keeps the library's default.
std::size_t chunk_size = 0;
/// Characters shared by neighbouring chunks; 0 keeps the library's default.
std::size_t chunk_overlap = 0;
/// File extensions to index, e.g. {"md", "txt"}; empty keeps the defaults.
std::vector<std::string> extensions;
/// Names or `*` patterns to skip, e.g. {"node_modules", "*.min.js"}.
std::vector<std::string> exclude;
bool recursive = true;
bool include_hidden = false;
bool gpu = false;
bool quiet = true;

Indexer

Builds a search index on disk from files and folders.

create

static Result<Indexer> create(const IndexerOptions& opts = {})

create_index

Result<IndexStats> create_index(std::string_view index_path, std::span<const std::string> inputs, bool force = false) const

Indexes inputs (files and folders) into a new index at index_path. force replaces an index already there.

add

Result<std::size_t> add(std::string_view index_path, std::span<const std::string> inputs) const

Adds inputs to an existing index; returns how many chunks were added.

SearchMode

enum class SearchMode { keyword = KJARNI_SEARCH_MODE_KEYWORD, semantic = KJARNI_SEARCH_MODE_SEMANTIC, hybrid = KJARNI_SEARCH_MODE_HYBRID };

SearcherOptions

/// Must be the model the index was built with.
std::string model = "minilm-l6-v2";
/// A cross-encoder for reranking, e.g. "minilm-l6-v2-cross-encoder"; empty for none.
std::string rerank_model;
std::string cache_dir;
SearchMode mode = SearchMode::hybrid;
std::size_t top_k = 10;
bool gpu = false;
bool quiet = true;

SearchOptions

Per-query overrides; anything left unset uses the searcher's defaults.

std::optional<SearchMode> mode;
std::size_t top_k = 0;
std::optional<bool> rerank;
/// Drop results scoring below this.
float threshold = 0.f;
/// Only files whose path matches this `*` pattern.
std::string source_pattern;
std::string filter_key;
std::string filter_value;

Searcher

Searches an index built by Indexer, by keyword, meaning or both.

create

static Result<Searcher> create(const SearcherOptions& opts = {})
Result<std::vector<Hit>> search(std::string_view index_path, std::string_view query) const

search

Result<std::vector<Hit>> search(std::string_view index_path, std::string_view query, const SearchOptions& options) const

has_reranker

bool has_reranker() const noexcept

Segment

One timed piece of a transcript, in seconds from the start.

float start{};
float end{};
std::string text;

Transcript

std::string text;
/// ISO code of the language transcribed: the one given, or the one detected.
std::string language;
float duration_secs{};
std::vector<Segment> segments;

TranscriberOptions

std::string model = "whisper-small";
/// ISO code such as "en" or "is"; empty detects it from the audio.
std::string language;
/// Translate the speech into English instead of transcribing it.
bool translate = false;
bool gpu = false;
bool quiet = true;

Transcriber

Speech to text with Whisper.

create

static Result<Transcriber> create(const TranscriberOptions& opts = {})

transcribe_file

Result<Transcript> transcribe_file(std::string_view path) const

Transcribes a wav, mp3, flac or ogg file.

transcribe

Result<Transcript> transcribe(std::span<const float> samples, std::uint32_t sample_rate) const

Transcribes mono samples in -1..1 at any sample rate.

SummarizerOptions

std::string model = "distilbart-cnn";
std::string cache_dir;
/// Summary length bounds in tokens; 0 keeps the model's defaults.
std::size_t min_length = 0;
std::size_t max_length = 0;
bool gpu = false;
bool quiet = true;

Summarizer

Summarizes article-length text.

create

static Result<Summarizer> create(const SummarizerOptions& opts = {})

summarize

Result<std::string> summarize(std::string_view text) const

TranslatorOptions

std::string model = "flan-t5-base";
std::string cache_dir;
/// Default languages for `translate(text)`, e.g. "en" and "de".
std::string from;
std::string to;
bool gpu = false;
bool quiet = true;

Translator

Translates text between languages.

create

static Result<Translator> create(const TranslatorOptions& opts = {})

translate

Result<std::string> translate(std::string_view text) const

Translates between the default languages given at creation.

translate

Result<std::string> translate(std::string_view text, std::string_view from, std::string_view to) const

ImageHit

std::string path;
/// Cosine similarity; small even for good matches, so only the order matters.
float score{};

ScanReport

std::size_t added{};
/// Files that could not be decoded.
std::size_t skipped{};

ImageIndexOptions

/// A CLIP model directory; empty uses clip-vit-base-32, downloaded on first use.
std::string model_dir;

ImageIndex

Photos searched by description, with CLIP. Not safe to share across threads while adding.

create

static Result<ImageIndex> create(const ImageIndexOptions& opts = {})

add

std::expected<void, Error> add(std::string_view path)

add_directory

Result<ScanReport> add_directory(std::string_view dir)

Adds every JPEG and PNG under dir.

search

Result<std::vector<ImageHit>> search(std::string_view query, std::size_t top_k = 10) const

save

std::expected<void, Error> save(std::string_view path) const

Writes the vectors (not the images) to path.

load

Result<std::size_t> load(std::string_view path)

Restores vectors saved by save; returns how many.

size

std::size_t size() const noexcept

Functions

cosine

float cosine(std::span<const float> a, std::span<const float> b) noexcept

Cosine similarity between two embeddings. Vectors of unequal length compare over the shorter prefix, which is what the C function does; mismatched dimensions almost always mean two different models.

cosine

float cosine(const Embedding& a, const Embedding& b) noexcept

version

std::string_view version() noexcept

Library version, e.g. "0.1.0".

Kjarni runs the same engine on every platform: