The rampart-langtools modules¶
Preface¶
Acknowledgment¶
The rampart-langtools package provides five modules built on best-in-class machine-learning libraries:
- The rampart-llamacpp module is built on llama.cpp, the C/C++ LLM inference engine created by Georgi Gerganov and contributors.
- The rampart-clip module is built on clip.cpp, a CLIP inference implementation by Yusuf Sarıgöz, ported onto the same ggml tensor library that rampart-llamacpp uses.
- The rampart-onnx module is built on ONNX Runtime, the cross-platform inference engine for ONNX models from Microsoft (with tokenizers from onnxruntime-extensions).
- The rampart-faiss module is built on FAISS, the library for efficient similarity search and clustering of dense vectors from Meta AI Research.
- The rampart-sentencepiece module is built on SentencePiece, the unsupervised text tokenizer from Google.
The authors of Rampart extend their thanks to the authors and contributors of each of these libraries.
License¶
The llama.cpp library is licensed under the MIT License. The clip.cpp implementation (and the ggml library it uses) is licensed under the MIT License. The ONNX Runtime library is licensed under the MIT License. The FAISS library is licensed under the MIT License. The SentencePiece library is licensed under the Apache 2.0 License.
The rampart-langtools modules are released under the MIT license.
What do they do?¶
Together the modules provide the building blocks for semantic search and local LLM inference inside Rampart:
- rampart-llamacpp runs GGUF models directly inside the rampart process: text embedding (initEmbed), reranking (initRerank) and text generation (initGen).
- rampart-onnx runs ONNX models: text embedding (onnx.initEmbed) and reranking (onnx.initRerank) with the same handle API as rampart-llamacpp, plus a general-purpose session API (onnx.initSession) for running any ONNX model.
- rampart-clip runs CLIP models: it embeds images and text into one shared vector space (clip.initEmbed), so a text query can search a collection of images, or an image can find similar images — cross-modal semantic search.
- rampart-faiss builds, trains, saves and searches vector indexes, from small exact-search indexes to compressed indexes holding hundreds of millions of vectors.
- rampart-sentencepiece tokenizes text into subword pieces (and back) using a SentencePiece model.
The package also ships rampart-models, a pure-JavaScript helper that downloads
and locates the models the engines above consume — models.get('bge-m3') returns a
ready-to-use local path, fetching from HuggingFace on first use. See The rampart-models module.
A typical pipeline embeds documents with rampart-llamacpp or rampart-onnx, stores the vectors in a rampart-faiss index (and/or a rampart-sql table), searches that index with an embedded query vector, and optionally reranks the results with a reranking model.
Note that the rampart-sql module has this pipeline built in: its embed() SQL function generates
vectors in the SQL engine using this package’s rampart-llamacpp module, and its CREATE VECTOR
INDEX / LIKEV similarity search uses a FAISS-backed (IVFPQ) index internally. When
vectors live in a SQL table, that integrated path is usually the simplest choice — see
Vector Search in the rampart-sql documentation. The modules documented
here are for using the same engines directly: custom or standalone faiss indexes, embedding
outside the SQL engine, reranking and text generation.
Platform Availability¶
The rampart-langtools modules are not available on the following platforms:
- macOS x86_64 (Intel Macs).
- 32-bit ARM Linux (the
raspberry_pi_os-buster-armv7lbuild).
On macOS (Apple Silicon):
- initEmbed requires macOS 12 (Monterey) or later.
-
initGen requires macOS 15 (Sequoia) or
later, due to a Metal regression in older versions of macOS. The requirement is checked at
runtime and
initGenwill throw a descriptive error on macOS 14 and below.
On Linux, the modules are available in CPU and CUDA (GPU) builds. GPU-only features (such as the faiss idx.enableGpu() function) are noted where applicable.
The rampart-onnx module additionally requires glibc 2.28 or later on Linux (it is not included in the packages built for older distributions), and its CUDA support requires an NVIDIA driver supporting CUDA 12 or later — see CPU and GPU (runtime selection).
Errors, Warnings and Logs¶
The modules never write to stdout or stderr. Instead:
- A failure throws a JavaScript Error (a model
that cannot be loaded, a malformed tensor, a session used after
destroy()). Catch it as usual. - A warning — a non-fatal problem that did not stop the call — is placed in
an
errMsgproperty, exactly as rampart-sql does. The property is set on the object the call was made on: the module object foronnx.initEmbed(), a handle for a handle method. It is cleared at the start of every call, so it always describes the most recent one, and it isundefinedwhen the call had nothing to report. Typical warnings are a GPU that could not be used and silently fell back to the CPU, or an unusableRAMPART_ONNX_RUNTIMEoverride.
var emb = onnx.initEmbed(model);
if (onnx.errMsg) // e.g. "no usable GPU; using CPU"
console.log("warning: " + onnx.errMsg);
errMsg is
deliberately kept separate from getLog /
onnx.getLog /
onnx.clearLog, which capture the informational output of the underlying libraries
(ggml/llama.cpp and ONNX Runtime). That log is verbose — a single embedding can produce
thousands of lines — and a warning placed there would be lost in it. Use errMsg to find out whether
something went wrong, and getLog() when you want the engine’s own diagnostics.
The rampart-llamacpp module¶
Loading the module is a simple matter of using the require() function:
var llamacpp = require("rampart-llamacpp");
Models are standard GGUF files, as downloaded from, e.g., Hugging Face.
modelInfo¶
The
modelInfofunction returns a model’s key parameters by reading only its GGUF metadata and vocabulary. It does not load the weight tensors, so there is no GPU upload and the call is fast even for multi-gigabyte models.Usage:
var llamacpp = require("rampart-llamacpp"); var info = llamacpp.modelInfo(path);Where
pathis a String, the path to a.ggufmodel file.
- Return Value:
An Object with the following properties:
embedDim- A Number, the size of the vector that initEmbed’s embedding functions produce. It prefers the GGUFembedding_length_outof a projection head, falling back toembedding_length.hiddenDim- A Number, the model hidden size.nCtxTrain- A Number, the trained context length.nLayer- A Number, the number of layers.arch- A String, the GGUFgeneral.architecture(e.g."bert","llama").pooling- A String, the model’s declared pooling type:"none","mean","cls","last","rank"or"unspecified".nParams- A Number, the parameter count.Example:
var llamacpp = require("rampart-llamacpp"); var info = llamacpp.modelInfo("bge-m3-FP16.gguf"); /* { embedDim: 1024, hiddenDim: 1024, nCtxTrain: 8192, nLayer: 24, arch: "bert", pooling: "cls", nParams: 566703104 } */ /* size vector storage from the model itself: */ var vecDim = info.embedDim;
initEmbed¶
The
initEmbedfunction loads an embedding model and returns a handle used to convert text into semantic vectors.Usage:
var llamacpp = require("rampart-llamacpp"); var emb = llamacpp.initEmbed(path[, options]);Where:
pathis a String, the path to a.ggufembedding model.
optionsis an optional Object accepting all of the settings in Common Model and Context Options below, plus the following embedding-specific options:
pooling- A String, one of"none","mean","cls","last"or"rank"(--poolingin llama-server). Default: the model’s own declared pooling, read from its GGUF metadata, falling back to"mean"only if the model declares none.Most embedding models do declare one, and they disagree: the
bge-*family uses"cls",nomic-embed-textandall-MiniLMuse"mean",Qwen3-Embeddinguses"last". Setting this by hand is therefore usually a mistake — it overrides what the model was trained with and produces vectors unlike the model’s intended output. modelInfo reports what a given file declares, without loading its weights.
attention- A String,"causal"or"non-causal"(--attention).
split- A String or Function."auto"(the default) chunks long text on its structure — paragraphs, merged or packed as configured below;"window"uses plain token windows. A Function replaces the built-in chunker entirely: it is called with the document text and must return an Array of Strings — N strings always produce N vectors. A string that fits the token window gets its exact vector; an oversized string gets its combined (average) vector over its sub-chunks, and itschunksentry is markedoversized. The strings may freely transform the input (inject a title, drop boilerplate, …), so the returnedchunkscarry{text, tokens}without byte spans. Two notes: a splitter shared with other rampart threads (via a copied handle) must be self-contained — it cannot reference variables outside itself; and the SQLchunkembed()/ 5-argumentabstract()machinery always uses the built-in chunker, so custom splitters are for JS-side pipelines. (The built-in chunker is the same as onnx.initEmbed’s — the two modules share it, so identical options produce identical chunk boundaries for the same tokenizer.)
minTokens- A Number, the paragraph-fragment floor: shorter paragraphs are merged with a neighbor.-1disables merging.
packParagraphs- A Boolean. Iftrue, consecutive paragraphs are packed together up to the token window (fewer, fuller chunks) instead of one vector per paragraph.
sentenceSplit- A Boolean. Iftrue, an oversized paragraph (or structureless text) is split at sentence boundaries and the sentences greedily packed to the token window, instead of being cut at raw token windows mid-sentence. Boundaries come from a multi-script terminator table (ASCII.!?with a whitespace guard; self-delimiting CJK。!?, Arabic, Devanagari and others with none; fullwidth.digit-guarded so3.14never splits), and a chunk never ends on a tiny trailing fragment (so a false boundary after “Mr.” can’t take a cut). Languages without sentence punctuation (e.g. Thai) fall back to token windows. Defaultfalse: enabling it changes chunk boundaries, which tables built WITHOUT value headers depend on for snippet spans.
batchChunks/batchTokens- Per-handle overrides of the process-wide chunk-batching defaults. See embedDefaults for what they control, what they are worth on each backend, and whybatchTokensshould not simply be raised. Given here they apply to this handle only, and take precedence over theembedDefaultsvalues.The legacy option names
nctx,ubatch,nthreadsandnthreads_batchare accepted as aliases fornCtx,nUBatch,threadsandthreadsBatch.If
nCtxis not given, the context size defaults to the model’s trained maximum, capped at 8192 tokens.Model weights are shared: loading the same model file from several handles or rampart threads keeps a single copy of the weights in memory. A handle that is copied to another rampart thread transparently builds its own per-thread context on first use.
- Note:
- The rampart-sql
embed()SQL function runs this same engine inside the SQL module:sql.set({llamaEmbed: '/path/to/model.gguf'})loads the model through rampart-llamacpp andembed(?)then produces the same vectors as emb.embedTextToFp16Buf()’savgVec. When embedding rows of a SQL table, that path avoids round-trips through JavaScript — see Generating embeddings and the llamaEmbed property.- Return Value:
- An Object (the embedding handle) with the functions emb.embedTextToFp16Buf(), emb.embedTextToFp32Buf(), emb.embedTextToNumbers() and emb.destroy() documented below.
Example:
var llamacpp = require("rampart-llamacpp"); var emb = llamacpp.initEmbed("all-minilm-l6-v2_f16.gguf"); var v = emb.embedTextToFp16Buf("about a paragraph of text ..."); /* v = { vecs: [vec1, ...], avgVec: avg, coherence: n, chunks: [ {start,end,tokens,text}, ... ] } If the text fits the model's context window, v.vecs.length == 1 and v.vecs[0] == v.avgVec. */ /* store the vector, e.g., in a sql table */ sql.exec("insert into vecs values(?,?,?)", [v.avgVec, docId, text]); /* unload when no longer needed */ emb.destroy();
emb.embedTextToFp16Buf()¶
Embed text and return the vector(s) packed as 16-bit floats (little-endian) in a Buffer. This is the most compact format and is directly usable by rampart.vector functions, the rampart-sql
vecdist()function and the faiss idx.addFp16() / idx.searchFp16() functions.Usage:
var ret = emb.embedTextToFp16Buf(text);Where
textis a String (or a Buffer containing text) to be embedded.If the tokenized text does not fit the model’s context window, it is chunked; with the default
split: "auto"the chunking is structure-aware: text is split on paragraph boundaries (falling back to token windows for oversized paragraphs), so each vector corresponds to a semantically meaningful span of the input, and the spans are reported inchunks. Each vector is L2-normalized.
- Return Value:
An Object with the following properties:
vecs- An Array of Buffers, one vector per chunk (2 * embedDimbytes each).avgVec- A Buffer. If only one chunk was produced, the same vector asvecs[0]. Otherwise the re-normalized average of all the chunk vectors, suitable as a single whole-document vector.coherence- A Number in[0, 1]: the average pairwise cosine similarity of the chunk vectors (1.0for a single-chunk document). A low value means the document spans several topics andavgVecis a blurrier summary of it.chunks- An Array of Objects, one per vector, withstart/end(the chunk’s byte span in the input),tokens(its token count),text(the chunk text itself) andoversized(Booleantruewhen this vector is one of several token windows over a single span that exceeded the model window; such sub-chunks share their span).For empty or whitespace-only input,
vecsis an empty Array andavgVecis not set.
emb.embedTextToFp32Buf()¶
The same as emb.embedTextToFp16Buf() except that vectors are packed as 32-bit floats (
4 * embedDimbytes per vector).Usage:
var ret = emb.embedTextToFp32Buf(text);
emb.embedTextToNumbers()¶
The same as emb.embedTextToFp16Buf() except that each vector is returned as an Array of Numbers rather than a packed Buffer.
Usage:
var ret = emb.embedTextToNumbers(text);
emb.destroy()¶
Free the model context (and release the model weights if this was the last handle using them). Using the handle after calling
destroy()throws an error. Handles are also freed automatically when garbage collected, but for large models it is good practice to free them deterministically.Usage:
emb.destroy();
initRerank¶
The
initRerankfunction loads a reranking model (such as bge-reranker-v2-m3) and returns a handle used to score how well documents answer a query. Reranking is commonly applied to the top results of a vector search to improve final ordering.Usage:
var llamacpp = require("rampart-llamacpp"); var rr = llamacpp.initRerank(path[, options]);Where:
pathis a String, the path to a.ggufreranking model.optionsis an optional Object accepting the same settings as initEmbed above. For reranking,poolingdefaults to"rank", the context size defaults to the model’s trained maximum capped at 1024 tokens, andnUBatchdefaults to 512. Input longer thannUBatchtokens is truncated.
- Return Value:
- An Object (the reranker handle) with the functions rr.rerank() and
destroy()(as in emb.destroy()).- Note:
- Instruct-style rerankers — the Qwen3-Reranker family — judge relevance by answering a yes/no question inside a chat prompt rather than through a bert-style classifier head.
initRerankdetects them by model architecture and wraps each (query, document) pair in the required prompt automatically. Their scores are the probability of “yes” (roughly 0.5 – 1.0 for plausible documents) rather than the wide-range scores of bert-style rerankers; orderings are comparable, magnitudes are not. Beware that many community GGUF conversions of these models were made as plain language models and lack the ranking head — such a file loads but scores every document identically. The catalog’smodels.ggufGet("qwen3-reranker-0.6b")is pinned to a verified conversion.Example:
var llamacpp = require("rampart-llamacpp"); var rr = llamacpp.initRerank("bge-reranker-v2-m3-Q8_0.gguf"); var question = "How tall is the Eiffel Tower?"; /* score a single document */ var score = rr.rerank(question, "The Eiffel Tower is 330 metres tall."); /* score several documents at once */ var scored = rr.rerank(question, [ "The Eiffel Tower is 330 metres tall.", "Gustave Eiffel also designed bridges.", "Paris is the capital of France." ]); /* [ { document: "The Eiffel Tower is ...", score: 4.21 }, { document: "Gustave Eiffel also ...", score: -1.7 }, ... ] */ rr.destroy();
rr.rerank()¶
Score one or more documents against a query.
Usage:
var score = rr.rerank(query, document); var scored = rr.rerank(query, documents[, scoresOnly]);Where:
queryis a String, the question or search text.document/documentsis a String (a single document) or an Array of Strings.scoresOnlyis an optional Boolean (only meaningful with an Array). Defaultfalse.
- Return Value:
- Given a single String document: a Number, the relevance score. Higher is more relevant; the range depends on the model (scores are typically logits, not probabilities).
- Given an Array of documents: an Array of Objects, each
{document: String, score: Number}, in the same order as the input.- Given an Array and
scoresOnly=true: an Array of Numbers.
initGen¶
The
initGenfunction loads a text-generation model and returns a handle for synchronous and streaming generation.Experimental.
initGen,predictandpredictAsyncare under active development and the API may change. Vision / multimodal input (mmproj) is not supported.
initGenruns a single shared, continuously-batched engine on a dedicated rampart thread. When the returned handle is shared across rampart threads (e.g. server threads), their requests are transparently pooled into that one engine: one copy of the model in memory, batched decoding.nSeqMaxsets how many requests may decode together.On macOS,
initGenrequires macOS 15 (Sequoia) or later — see Platform Availability.Usage:
var llamacpp = require("rampart-llamacpp"); var gen = llamacpp.initGen(path[, options]);Where:
pathis a String, the path to a.gguftext-generation model.optionsis an optional Object accepting all of the settings in Common Model and Context Options below, plus the following generation-specific options:
jinja- A Boolean, whether to apply the model’s chat template via Jinja whenmessagesare given to gen.predict(). Default:true.chatTemplate- A String, a custom Jinja chat template overriding the model’s built-in template.chatTemplateFile- A String, a file from which to read the custom chat template.
- Return Value:
- An Object (the gen handle) with the properties
nCtxandnVocab(Numbers, the resolved context size and vocabulary size) and the functions gen.predict(), gen.predictAsync(), gen.getLast() and gen.destroy().Example:
var llamacpp = require("rampart-llamacpp"); var gen = llamacpp.initGen("gemma-3-4b-it-Q4_K_M.gguf", { nCtx: 4096, nSeqMax: 4 /* up to 4 requests batched together */ }); /* synchronous: blocks and returns the full text */ var text = gen.predict({ messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What is the capital of France?" } ], maxTokens: 128 }); rampart.utils.printf("%s\n", text); /* streaming: tokens are delivered as they are produced */ var h = gen.predictAsync( { prompt: "Explain how a combustion engine works.", maxTokens: 256, temp: 0.7 }, function(res) { /* per token */ if (!res.done && !res.error) rampart.utils.printf("%s", res.token); }, function(res) { /* done: res.fullText, res.error */ rampart.utils.printf("\n[done]\n"); } ); /* h.cancel(); -- stop this generation early */ gen.destroy();
gen.predict()¶
Generate text synchronously. The call blocks the current thread until generation completes and returns the full generated text. On a server, this blocks the worker thread’s event loop — use gen.predictAsync() in hot handlers.
Usage:
var text = gen.predict(options);Where
optionsis an Object with the following properties (one ofpromptormessagesis required):
prompt- A String, a plain text prompt.messages- An Array of{role: String, content: String}Objects, chat-style messages. The model’s chat template is applied (see thejinjaoption of initGen).maxTokens- A Number, the maximum number of tokens to generate. Default:512.temp- A Number, the sampling temperature.topP- A Number, top-p (nucleus) sampling.topK- A Number, top-k sampling.minP- A Number, min-p sampling.repeatPenalty- A Number, the repetition penalty.repeatLastN- A Number, how many recent tokens the repetition penalty considers.seed- A Number, the RNG seed. If not given, a random seed is used per request.stop- An Array of Strings; generation stops when any of them is produced.addAssistant- A Boolean, whether to append the assistant generation prompt when applying a chat template. Default:true.Unset sampling options use the model/engine defaults.
- Return Value:
- A String, the full generated text. If the engine reports an error, the returned string is
"[gen err:<message>]".
gen.predictAsync()¶
Generate text asynchronously, streaming tokens as they are produced. The call returns immediately; callbacks fire from the event loop. Multiple in-flight calls (across threads, or from one event loop) batch together through the shared engine.
Usage:
var h = gen.predictAsync(options, perToken[, final]);Where:
optionsis the same Object accepted by gen.predict().perTokenis a Function, called once per generated token with an Object:
token- A String, the token text.done- A Boolean,falsefor token callbacks.finalis an optional Function, called once when the generation ends, with an Object:
fullText- A String, the complete generated text.error- A String, set if the generation failed.
- Return Value:
- An Object with a single function
cancel(), which stops this generation early and frees its slot in the engine.
gen.getLast()¶
Return the full text of the last completed gen.predict() call made through this handle on the current thread.
Usage:
var text = gen.getLast();
gen.destroy()¶
Shut down the shared generation engine and free the model context. The handle (and any copies of it on other threads) must not be used afterwards.
Usage:
gen.destroy();
Common Model and Context Options¶
initEmbed, initRerank and initGen accept a common set of model-loading and context options. Most map 1:1 onto the matching
llama-servercommand-line flag (the camelCase of the flag); see the llama.cpp server documentation for full descriptions of each.
Option llama-server flag Value gpuLayers--gpu-layersNumber. Layers to offload to the GPU ( -1= all).mainGpu--main-gpuNumber. GPU device to use. splitMode--split-modeString: "none","layer"or"row".useMmap--no-mmapBoolean. Memory-map the model file. useMlock--mlockBoolean. Lock the model in RAM. checkTensors--check-tensorsBoolean. Validate tensor data while loading. nCtx--ctx-sizeNumber. Context size in tokens ( 0or-1= the model’s trained maximum).nBatch--batch-sizeNumber. Logical batch size. nUBatch--ubatch-sizeNumber. Physical (micro-)batch size. nSeqMax--parallelNumber. Maximum parallel sequences (initGen: how many requests decode together). threads--threadsNumber. Threads for generation. threadsBatch--threads-batchNumber. Threads for batch/prompt processing. flashAttn--flash-attnBoolean or String: "on","off"or"auto".cacheTypeK--cache-type-kString. KV cache type for K: "f32","f16","bf16","q8_0","q4_0","q4_1","q5_0","q5_1"or"iq4_nl".cacheTypeV--cache-type-vString. KV cache type for V (same values). offloadKqv--no-kv-offloadBoolean. Offload the KV cache to the GPU. offloadKQVis accepted as an alias.opOffload--op-offloadBoolean. Offload host-tensor operations. kvUnified--kv-unifiedBoolean. Use a unified KV cache. ropeScaling--rope-scalingString: "none","linear","yarn"or"longrope".ropeFreqBase--rope-freq-baseNumber. ropeFreqScale--rope-freq-scaleNumber. yarnExtFactor--yarn-ext-factorNumber. yarnAttnFactor--yarn-attn-factorNumber. yarnBetaFast--yarn-beta-fastNumber. yarnBetaSlow--yarn-beta-slowNumber. yarnOrigCtx--yarn-orig-ctxNumber.
embedDefaults¶
The
embedDefaultsfunction gets and sets process-global defaults for embedding. They seed initEmbed’s options — an option given on theinitEmbedcall itself still wins — and they are the only way to configure embedding done inside rampart-sql (via sql.set({llamaEmbed:…})), which loads the model through this module but takes no options object of its own.Usage:
var llamacpp = require("rampart-llamacpp"); var settings = llamacpp.embedDefaults([options]);Where
optionsis an optional Object with any of the following properties. Omitting the argument entirely returns the current settings without changing them.
batchChunks- A Boolean, Number or null. Controls how many of a document’s chunks are packed into a single decode.null(the default) means auto: batch on a GPU backend, one chunk per decode on CPU.truepacks as many as the context allows,falsepacks one at a time, and a Number caps the sequences per decode.Experimental. Chunk batching is under active development. It has been verified for correctness on CUDA and Metal, but its performance is hardware-dependent and it is untested on some backends, so the defaults may change.
falsealways gives the unbatched behavior exactly.Auto is the recommended setting. Batching is measurably faster on a GPU backend and no faster at all on CPU, and it perturbs the resulting vectors slightly — a larger batch selects different matmul kernels, which moves each element on the order of
1e-3(cosine similarity ~0.9999 against the unbatched vector). It never changes how text is chunked: the chunk count and byte offsets are identical either way.
batchTokens- A Number, the soft cap on total tokens in one packed decode. Default512.Raising it is not a way to go faster, and usually does the opposite: a packed batch’s attention cost grows quadratically with its token count, while the per-decode overhead batching saves grows only linearly. Past a few hundred tokens the quadratic wins, and batching can end up slower than not batching at all.
512is a tuned value — re-measure before changing it.A chunk longer than the cap is never split; it goes through on its own.
threads- A Number, the per-token decode thread count (n_threads). Default1.
threadsBatch- A Number, the multi-token decode thread count (n_threads_batch). This is the one that matters for embedding, where every decode is multi-token.Default
-1, which hands the choice to ggml — and ggml chooses 4 regardless of the machine’s core count. On anything larger than a 4-core box, set this explicitly.
- Return Value:
- An Object with the settings in effect after applying any changes:
batchChunks,batchTokens,threads,threadsBatch, plusgpuInUse, a Boolean reporting whether a GPU backend is registered in this process — i.e. which waybatchChunks: nullwill resolve. ggml registers its GPU backend when the first model loads, sogpuInUsereadsfalseon a GPU machine until then; theautodecision itself is always made after a load and is unaffected.- Note:
- Call this before the model is loaded. Models are cached by path: the second and later
initEmbedorsql.set({llamaEmbed: …})for the same file hand back the already-loaded model unchanged, and an existing context keeps the thread counts it was built with. AnembedDefaultscall made afterwards will silently not apply to that model.Example — overriding the batching default on the SQL path:
var Sql = require("rampart-sql"); /* BEFORE sql.set({llamaEmbed: ...}), and before any other code loads the same model. */ require("rampart-llamacpp").embedDefaults({ batchChunks: false, /* one chunk per decode */ threadsBatch: 8 /* else ggml uses 4, always */ }); var sql = new Sql("/path/to/db"); sql.set({llamaEmbed: "/models/bge-m3-FP16.gguf"}); sql.exec("insert into docs values(?, chunkembed(?))", [id, text]);Reasons to turn batching off include reproducing vectors generated by a build that predates it, and A/B measuring the difference on a particular machine. For ordinary use, leave it at auto.
getLog¶
llama.cpp produces log output during model loading and initialization. This output is captured in an internal buffer rather than printed to stdout/stderr.
getLogretrieves the captured log.Usage:
var llamacpp = require("rampart-llamacpp"); var emb = llamacpp.initEmbed("all-minilm-l6-v2_f16.gguf"); var log = llamacpp.getLog(); console.log(log);
- Return Value:
- A String, the captured log output.
- Note:
- The log buffer has a maximum size of 40KB. If it overflows, the oldest half of the log is discarded and the first line will read
WARN: log overflow. The log buffer is process-global (all threads write to the same, mutex-protected buffer), butgetLog/resetLogare only serviceable from the module object of the thread that first loaded the module; on other threads they throw.
Environment Variables¶
RAMPART_LLAMA_CUDA_GRAPHS- On CUDA builds, ggml caches a captured CUDA graph per compute-graph shape and only evicts entries after they have been idle for 10 seconds. Batched embedding and reranking decode a stream of varying shapes, which fills that cache faster than it drains and makes GPU memory climb until it runs out. CUDA graphs only speed up single-stream text generation, so initEmbed, initRerank and the rampart-sql embedding path disable them automatically. A process that only calls initGen never disables them, so generation performance is unaffected. SetRAMPART_LLAMA_CUDA_GRAPHS(to any value) to opt out and keep CUDA graphs on even for embedding/reranking, accepting the memory growth above. The setting is process-global and read once at startup. It has no effect on CPU or Metal (Apple) builds.RAMPART_METAL_RESIDENCY- On macOS, Metal “residency sets” are disabled by default to avoid an assertion at process exit. SetRAMPART_METAL_RESIDENCY=1to keep the feature (a marginal performance optimization) enabled.
The rampart-onnx module¶
Experimental. rampart-onnx is new in this release. It is under active development and its API may change.
Loading the module is a simple matter of using the require() function:
var onnx = require("rampart-onnx");
The module runs models in the ONNX format via ONNX Runtime. It provides two layers:
- High-level text embedding (onnx.initEmbed) and reranking (onnx.initRerank) with the same handle API as rampart-llamacpp’s initEmbed / initRerank — use it when a model is published in ONNX form rather than GGUF (as most sentence-transformers models are).
- A general-purpose session API (onnx.initSession) for running any ONNX model — named tensors in, named tensors out.
For embedding and reranking, the simplest input is a HuggingFace model directory (e.g. a
git clone of all-MiniLM-L6-v2): the
.onnx file, the
tokenizer, the pooling mode and the token window are all discovered from the directory contents.
A bare .onnx file path
also works, but then a tokenizer must be supplied.
CPU and GPU (runtime selection)¶
A single
rampart-onnx.soserves both CPU and GPU: the module contains a complete CPU-only ONNX Runtime, and GPU installs add an optional CUDA runtime directory (onnx-cu12/oronnx-cu13/) next to the module. At first use the module picks a runtime:
- If the environment variable
RAMPART_ONNX_RUNTIMEis set (cpu,cu12,cu13or an absolute directory path), it wins.- Otherwise, if an NVIDIA driver supporting CUDA 12 or later is present and reports at least one GPU, the newest CUDA runtime directory the driver supports is used, preferring one built for the GPU’s exact compute capability.
- Otherwise (or if the chosen runtime fails to load), the built-in CPU runtime is used.
onnx.runtimeInfo reports which runtime was picked. Selecting a GPU runtime makes GPU execution available, and sessions then use it automatically: on a build where a GPU runtime was selected, onnx.initSession, onnx.initEmbed and onnx.initRerank run on the GPU by default (the same auto-GPU behavior as the rampart-sql embedding path). Pass
gpu: false(orprovider: "cpu") to force CPU. A session that requests the GPU but cannot create one (no device, or a driver problem) falls back to CPU with a one-line notice rather than failing.
onnx.initEmbed¶
The
initEmbedfunction loads an ONNX embedding model and returns a handle used to convert text into semantic vectors.Usage:
var onnx = require("rampart-onnx"); var oemb = onnx.initEmbed(modelPath[, options]);Where:
modelPathis a String: a HuggingFace-layout model directory (recommended), or the path of a.onnxfile. Given a directory, the module discovers the.onnxmodel file, the tokenizer (a*vocab.txtselects WordPiece, otherwisetokenizer.jsonselects a SentencePiece/BPE tokenizer), the pooling mode (from1_Pooling/config.json) and the model’s token window. Given a bare.onnxfile, the module still tries to discover the tokenizer beside the model — in the file’s own directory and, when the file sits in anonnx/subdirectory (the common HuggingFace layout), in its parent — so pointing at a specific.onnx(e.g. an fp16 variant) usually works withoutoptions.tokenizer; supply it explicitly only when discovery cannot find one.optionsis an optional Object accepting all of the session options of onnx.initSession (notablygpu), plus the following. Every discovered setting can be overridden here.
tokenizer- A String (the path of a SentencePiece model, loaded via rampart-sentencepiece) or an Object with anencodeIds(text)function (e.g. from onnx.wordPieceTokenizer or onnx.spTokenizer, or custom JavaScript).pooling- A String,"mean"or"cls". Default: the directory’s declared pooling, else"mean".normalize- A Boolean, L2-normalize each vector. Default:true.maxTokens- A Number, the per-chunk token window. Default: the model’s discovered positional capacity (capped at 8192), else512.queryPrefix/passagePrefix- Strings prepended to query / passage text before embedding, for models trained with instruction prefixes (e.g. e5’s"query: "/"passage: "). See theisQueryargument of oemb.embedTextToFp16Buf().split- A String or Function."auto"(the default) chunks long text on its structure — paragraphs, merged or packed as configured below;"window"uses plain token windows. A Function replaces the built-in chunker entirely: it is called with the document text and must return an Array of Strings — N strings always produce N vectors. A string that fits the token window gets its exact vector; an oversized string gets its combined (average) vector over its sub-chunks, and itschunksentry is markedoversized. The strings may freely transform the input (inject a title, drop boilerplate, …), so the returnedchunkscarry{text, tokens}without byte spans. Two notes: a splitter shared with other rampart threads (via a copied handle) must be self-contained — it cannot reference variables outside itself; and the SQLchunkembed()/ 5-argumentabstract()machinery always uses the built-in chunker, so custom splitters are for JS-side pipelines.minTokens- A Number, the paragraph-fragment floor: shorter paragraphs are merged with a neighbor.-1disables merging.packParagraphs- A Boolean. Iftrue, consecutive paragraphs are packed together up to the token window (fewer, fuller chunks) instead of one vector per paragraph.sentenceSplit- A Boolean. Iftrue, an oversized paragraph (or structureless text) is split at sentence boundaries and the sentences greedily packed to the token window, instead of being cut at raw token windows mid-sentence. Boundaries come from a multi-script terminator table (ASCII.!?with a whitespace guard; self-delimiting CJK。!?, Arabic, Devanagari and others with none; fullwidth.digit-guarded so3.14never splits), and a chunk never ends on a tiny trailing fragment (so a false boundary after “Mr.” can’t take a cut). Languages without sentence punctuation (e.g. Thai) fall back to token windows. Defaultfalse: enabling it changes chunk boundaries, which tables built WITHOUT value headers depend on for snippet spans.maxChunkBatch- A Number, the maximum chunks per batched model run (bounds memory for many-chunk documents). Default:64(CPU) or32(GPU).bosId,eosId,padId,idOffset- Numbers, special-token overrides. Defaults follow the detected tokenizer family (e.g.[CLS]/[SEP]ids 101/102 for WordPiece).lowercase,stripAccents,tokenizeChinese- Booleans, WordPiece tokenizer settings (see onnx.wordPieceTokenizer). Default:true.
- Note:
- The rampart-sql
embed(),chunkembed()and related SQL functions can run this same engine inside the SQL module:sql.set({onnxEmbed: {model: '/path/to/modeldir'}})loads the model through rampart-onnx. When embedding rows of a SQL table, that path avoids round-trips through JavaScript — see Generating embeddings, Chunked documents and the onnxEmbed property.- Return Value:
- An Object (the embedding handle) with the functions oemb.embedTextToFp16Buf(),
embedTextToFp32Buf(),embedTextToNumbers(), oemb.embedTextsToNumbers() anddestroy(), plussession(the underlying onnx.initSession handle).Example:
var onnx = require("rampart-onnx"); var oemb = onnx.initEmbed("./all-MiniLM-L6-v2"); var v = oemb.embedTextToFp16Buf("about a paragraph of text ..."); /* v = { vecs: [vec1, ...], avgVec: avg, coherence: n, chunks: [ {start,end,tokens,text}, ... ] } */ /* store the whole-document vector in a sql table */ sql.exec("insert into vecs values(?,?,?)", [v.avgVec, docId, text]); oemb.destroy();
oemb.embedTextToFp16Buf()¶
Embed text and return the vector(s) packed as 16-bit floats (little-endian) in Buffers, directly usable by rampart.vector functions, the rampart-sql
vecdist()function and the faiss idx.addFp16() / idx.searchFp16() functions.Usage:
var ret = oemb.embedTextToFp16Buf(text[, isQuery]);Where:
textis a String, the text to be embedded.isQueryis an optional Boolean. Iftrue, thequeryPrefix(if configured) is applied; iffalseor omitted, thepassagePrefix(if configured) is applied.If the tokenized text does not fit the model’s token window, it is chunked; with the default
split: "auto"the chunking is structure-aware: text is split on paragraph boundaries (falling back to token windows for oversized paragraphs), so each vector corresponds to a semantically meaningful span of the input, and the spans are reported inchunks. rampart-llamacpp’s emb.embedTextToFp16Buf() chunks the same way — the two modules share the chunker.
- Return Value:
An Object with the following properties:
vecs- An Array of Buffers, one vector per chunk (2 * dimensionbytes each).avgVec- A Buffer. If only one chunk was produced, the same vector asvecs[0]; otherwise the re-normalized average of the chunk vectors, suitable as a single whole-document vector.coherence- A Number in[0, 1]: the average pairwise cosine similarity of the chunk vectors (1.0for a single-chunk document). A low value means the document spans several topics andavgVecis a blurrier summary of it.chunks- An Array of Objects, one per vector:
start,end- Numbers, the chunk’s byte span intext.tokens- A Number, the chunk’s token count.text- A String, the chunk text itself.oversized- Booleantruewhen this vector is one of several token windows over a single span that exceeded the model window (such sub-chunks share their span). Not set otherwise.For empty input, the return value is
{vecs: []}.
oemb.embedTextToFp32Buf() / oemb.embedTextToNumbers()¶
The same as oemb.embedTextToFp16Buf() except that each vector is packed as 32-bit floats (
4 * dimensionbytes), or returned as an Array of Numbers, respectively.Usage:
var ret = oemb.embedTextToFp32Buf(text[, isQuery]); var ret = oemb.embedTextToNumbers(text[, isQuery]);
oemb.embedTextsToNumbers()¶
Embed several short texts in one batched model run — the fast path for many small inputs (e.g. embedding a list of queries or titles). Each text produces exactly one vector; text longer than the token window is truncated, not chunked.
Usage:
var ret = oemb.embedTextsToNumbers(texts[, isQuery]);Where
textsis an Array of Strings andisQueryselects the prefix as in oemb.embedTextToFp16Buf().
- Return Value:
- An Array (same order as
texts) of Objects, each with a single propertyavgVec: the text’s vector as an Array of Numbers.
oemb.destroy()¶
Free the model session. Using the handle after calling
destroy()throws an error.Usage:
oemb.destroy();
onnx.initRerank¶
The
initRerankfunction loads an ONNX cross-encoder reranking model (such as bge-reranker-v2-m3 or ms-marco-MiniLM-L6-v2) and returns a handle used to score how well documents answer a query.Usage:
var onnx = require("rampart-onnx"); var orr = onnx.initRerank(modelPath[, options]);Where:
modelPathis a String: a HuggingFace-layout model directory (recommended; the model, tokenizer, specials, token window and pair template are discovered) or a.onnxfile path. For a file path the tokenizer is still discovered beside the model (the file’s directory, and its parent when the file is in anonnx/subdirectory), so naming a specific.onnxvariant works withoutoptions.tokenizer; pass it explicitly only if discovery fails.optionsis an optional Object accepting the session options of onnx.initSession and the tokenizer / special-token options of onnx.initEmbed, plus:
pairTemplate- A String,"bert"([CLS] query [SEP] document [SEP]with token types) or"roberta"([bos] query [eos eos] document [eos]). Default:"bert"when the tokenizer is WordPiece, else"roberta". Instruct-style rerankers (the Qwen3-Reranker family) are not supported here — use the rampart-llamacpp initRerank for those.sigmoid- A Boolean. Iftrue(the default), scores are passed through a sigmoid and lie in(0, 1); iffalse, raw model logits are returned (matching rampart-llamacpp’s rr.rerank()).
- Return Value:
- An Object (the reranker handle) with the functions orr.rerank() and
destroy(), plussession.- Note:
- The handle carries all of its state as object properties with native methods, so it survives being shared with other rampart threads — including a rampart-server
preThreadFuncglobal propagating to worker threads, like a rampart-llamacpp handle. Handles from onnx.initEmbed andinitSnacDecoderare thread-portable the same way.
orr.rerank()¶
Score one or more documents against a query. All documents are scored in one batched model run.
Usage:
var score = orr.rerank(query, document); var scored = orr.rerank(query, documents[, scoresOnly]);Where
queryis a String anddocument/documentsis a String or an Array of Strings, as in the llamacpp rr.rerank().
- Return Value:
- Given a single String document: a Number, the relevance score.
- Given an Array of documents: an Array of Objects, each
{document: String, score: Number, index: Number}, sorted best-first;indexis the document’s position in the input Array. (Note this differs from the llamacpp reranker, which returns results in input order.)- Given an Array and
scoresOnly=true: an Array of Numbers in input order (llamacpp parity).Example:
var onnx = require("rampart-onnx"); var orr = onnx.initRerank("./ms-marco-MiniLM-L6-v2"); var scored = orr.rerank("How tall is the Eiffel Tower?", [ "The Eiffel Tower is 330 metres tall.", "Gustave Eiffel also designed bridges.", "Paris is the capital of France." ]); /* [ { document: "The Eiffel Tower is ...", score: 0.98, index: 0 }, { document: "Paris is the capital ...", score: 0.03, index: 2 }, ... -- sorted best-first ] */ orr.destroy();
onnx.initSession¶
The
initSessionfunction loads any.onnxmodel and returns a low-level session handle: named input tensors go in, named output tensors come out. This is the layer beneath onnx.initEmbed / onnx.initRerank, exposed for models that are neither embedders nor rerankers (classifiers, taggers, audio and vision models, …).Usage:
var onnx = require("rampart-onnx"); var sess = onnx.initSession(path[, options]);Where:
pathis a String, the path of a.onnxmodel file.optionsis an optional Object with the following properties:
gpu- A Boolean. Whether to run the session on the GPU via the CUDA execution provider (see CPU and GPU (runtime selection)). Default: auto —truewhen the module selected a GPU runtime (a GPU build with a usable device), otherwisefalse. Passgpu: falseto force CPU even on a GPU build. A GPU session that cannot be created falls back to CPU with a one-line notice rather than throwing.provider: "cuda"/"cpu"is accepted as an alternative spelling (provider: "cpu"also forces CPU).device- A Number, the CUDA device id (withgpu: true). Default:0.intraOpThreads- A Number, threads used within an operator. Default:0= ONNX Runtime’s own thread pool (sized to the machine’s cores), so a single call uses the full CPU. Set1for a session with no background threads — see the fork note below.interOpThreads- A Number, threads used to run independent operators concurrently (only meaningful withexecutionMode: "parallel"). Default:1.executionMode- A String,"sequential"(default) or"parallel".graphOpt- A String, the graph optimization level:"disable","basic","extended"or"all". Default: ONNX Runtime’s default ("all").
- Note:
- Fork safety costs no performance. A session whose thread pool is broken by a
fork()is transparently rebuilt from its model source on first use in the child process; a GPU session (whose device context cannot be rebuilt in a forked child) throws a descriptive error there instead.intraOpThreads: 1(with sequential execution) creates a session with no background threads at all, which a forked child can keep using without any rebuild. Concurrency is also available across rampart threads: a session may be used from several threads at once (runis thread-safe).- Return Value:
- An Object (the session handle) with the functions sess.run(), sess.inputs(),
outputs(), sess.metadata() anddestroy().Example:
var onnx = require("rampart-onnx"); var sess = onnx.initSession("model.onnx"); /* what does it want? */ var ins = sess.inputs(); /* [ { name: "input_ids", type: "int64", shape: [-1,-1] }, { name: "attention_mask", type: "int64", shape: [-1,-1] } ] */ var out = sess.run({ input_ids: { data: [101, 7592, 102], shape: [1, 3], type: "int64" }, attention_mask: { data: [1, 1, 1], shape: [1, 3], type: "int64" } }); /* out.last_hidden_state.array is a Float32Array, out.last_hidden_state.shape e.g. [1, 3, 384] */ sess.destroy();
sess.run()¶
Run the model once.
Usage:
var outputs = sess.run(feeds);Where
feedsis an Object mapping each input’s name to a tensor Object:
data- A Buffer (raw little-endian element bytes) or an Array of Numbers (converted to the given type).shape- An Array of Numbers, the tensor dimensions. If omitted, the tensor is treated as 1-D.type- A String, the element type:"float32","float16","double","int64","int32","int16","int8","uint8"or"bool".
- Return Value:
An Object mapping each output’s name to a tensor Object:
data- An ArrayBuffer of the raw element bytes (so e.g.new Float32Array(t.data)reinterprets rather than copies).array- A ready-made typed-array view overdatamatching the element type (Float32Array,Int32Array, …). Omitted forint64outputs, which have no native typed array (usedata).shape- An Array of Numbers.type- A String, the element type.
sess.inputs()¶
Return the model’s declared inputs (
sess.outputs()likewise returns its outputs): an Array of Objects, each{name: String, type: String, shape: Array}. Dynamic dimensions appear as-1.Usage:
var ins = sess.inputs(); var outs = sess.outputs();
sess.metadata()¶
Return the model’s metadata: an Object with
producerName,graphName,domain,description(Strings, present when set in the model) andversion(a Number).Usage:
var meta = sess.metadata();
sess.destroy()¶
Free the session. Using the handle after calling
destroy()throws an error. Sessions are also freed automatically when garbage collected.Usage:
sess.destroy();
onnx.initSessionFromBuffer¶
The same as onnx.initSession except that the model is read from a Buffer of model bytes rather than a file.
Usage:
var sess = onnx.initSessionFromBuffer(buffer[, options]);
onnx.modelInfo¶
Return a model’s declared inputs and outputs without creating a full session.
Usage:
var onnx = require("rampart-onnx"); var info = onnx.modelInfo(path);Where
pathis a String, the path of a.onnxmodel file.
- Return Value:
- An Object with
inputsandoutputs, each an Array of{name, type, shape}Objects as returned by sess.inputs().
onnx.wordPieceTokenizer¶
Create a WordPiece (BERT-style) tokenizer from a
vocab.txtfile. onnx.initEmbed / onnx.initRerank create one of these automatically when the model directory contains a*vocab.txt; the function is exposed for custom pipelines.Usage:
var onnx = require("rampart-onnx"); var tok = onnx.wordPieceTokenizer(vocabPath[, options]);Where:
vocabPathis a String, the path of thevocab.txtfile.optionsis an optional Object with the Boolean propertieslowercase,stripAccentsandtokenizeChinese, each defaulting totrue.
- Return Value:
- An Object with the property
vocabSize(a Number) and the functionencodeIds(text), which returns an Array of Numbers — the content token ids oftext, without special tokens ([CLS]/[SEP]are added by the embed/rerank layers).
onnx.spTokenizer¶
Create a SentencePiece/BPE tokenizer from a HuggingFace
tokenizer.json. As with onnx.wordPieceTokenizer, this is created automatically for model directories that have atokenizer.json.Usage:
var onnx = require("rampart-onnx"); var tok = onnx.spTokenizer(modelDir);Where
modelDiris a String, the directory containingtokenizer.json.
- Return Value:
- An Object with the function
encodeIds(text), as in onnx.wordPieceTokenizer.
onnx.initSnacDecoder¶
Experimental. Load a SNAC audio-codec decoder model and return a handle that turns SNAC codes (as produced by speech models such as Orpheus TTS) into 24 kHz audio samples. The handle has the propertysampleRate(24000) and the functionsdecode(codes),framesToCodes(frames),decodeFrames(frames),decodeOrpheus(tokens)anddestroy(); decoded audio is returned as aFloat32Arrayof samples. The API may change.
onnx.onnxVersion¶
Return the ONNX Runtime version string (e.g.
"1.27.0").Usage:
var v = onnx.onnxVersion();
onnx.runtimeInfo¶
Return a String describing which runtime the selection ladder picked (see CPU and GPU (runtime selection)) — e.g.
"built-in CPU", or the CUDA runtime directory with the driver version and GPU compute capability.Usage:
rampart.utils.printf("%s\n", onnx.runtimeInfo()); /* "/usr/local/rampart/modules/onnx-cu12 (driver CUDA 12.2, sm 89)" */
onnx.getLog / onnx.clearLog¶
ONNX Runtime warnings and non-fatal errors are captured in an internal buffer rather than printed to stderr.
getLog()returns the captured log as a String;clearLog()empties it (resetLog()is an alias, for rampart-llamacpp naming parity).Usage:
var log = onnx.getLog(); onnx.clearLog();
Converting a model to float16 or int8¶
ONNX embedding and reranking models are usually exported in float32. For GPU inference a float16 copy roughly halves the weight size and uses the GPU’s tensor cores — usually the fastest ONNX option (the module auto-uses the GPU; see CPU and GPU (runtime selection)). An int8 (dynamically quantized) copy is smaller still but is CPU-oriented: ONNX Runtime’s CUDA execution provider has sparse int8 kernel coverage, so int8 tends to fall back to the CPU (with CPU⇄GPU copies) and is usually slower on the GPU than fp16 — prefer it with
gpu: false.Conversion is a one-time, offline step using Python’s ONNX tooling (not a runtime rampart operation): convert once and place the resulting
.onnxbeside the original, then load it like any other model. Install the tooling into a throwaway virtualenv matching yourpython3:python3 -m venv /tmp/onnxconv /tmp/onnxconv/bin/pip install onnxruntime onnx sympyfloat16 — use ONNX Runtime’s transformer optimizer, which inserts/repairs the
Castnodes that a bareonnxconverter_commonpass gets wrong.opt_level=0applies no graph fusions (only the precision change, so outputs do not drift);keep_io_typesleaves the int/float inputs and outputs unchanged; anduse_external_data_formathandles weights that exceed protobuf’s 2 GB single-file limit:from onnxruntime.transformers.optimizer import optimize_model m = optimize_model("model.onnx", model_type="bert", opt_level=0, use_gpu=False) m.convert_float_to_float16(keep_io_types=True) m.save_model_to_file("model_fp16.onnx", use_external_data_format=True)int8 — weight-only dynamic quantization (no calibration data needed):
from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic("model.onnx", "model_int8.onnx", weight_type=QuantType.QInt8)Then load the result as usual, e.g.
onnx.initRerank("<dir>/onnx/model_fp16.onnx", {tokenizer: ...}). A.onnxsaved with external data keeps its weights in a sidecar file (model_fp16.onnx.data); keep the two together.
The rampart-clip module¶
Experimental. rampart-clip is new in this release. It is under active development and its API may change.
Loading the module is a simple matter of using the require() function:
var clip = require("rampart-clip");
CLIP (Contrastive Language–Image Pre-training) maps images and text into a single shared vector space, so similar concepts land close together regardless of modality. That makes it possible to search a collection of images with a text query, find images similar to a reference image, or score how well a caption matches a picture — all with plain dot-product similarity on the vectors it returns.
The module is built on a CLIP implementation ported onto the same ggml tensor library rampart-llamacpp uses, so it links no second copy of ggml and — like the other langtools modules — a loaded model is shared across rampart threads: every thread copy of a model object shares one read-only set of weights and does its own computation, so a model may be handed to a worker thread and used there directly (see CLIP and threads).
Models¶
rampart-clip loads CLIP models in the GGUF format used by clip.cpp — standard OpenAI and LAION CLIP models
converted to GGUF, available pre-converted on HuggingFace (e.g. the mys/ggml_CLIP-ViT-*
repositories). The q8_0 quantization is a good default: nearly identical to f16 in quality at roughly half
the size.
The The rampart-models module helper can fetch one into the standard location for you:
var models = require("rampart-models");
var path = models.get("clip-vit-b-32-laion:q8_0"); // -> local .gguf path
clip.initEmbed¶
clip.initEmbed(modelPath[, options])(alias:clip.load)Load a CLIP GGUF model and return a model object carrying the image+text embedding methods below.
modelPathis the path to a.gguffile (typically from The rampart-models module). NamedinitEmbedto match the other langtools modules (initEmbed/initGen/onnx.initEmbed);clip.loadis a backward-compatible alias for the same call.The optional
optionsobject accepts:
nThreads– Number. CPU threads for inference.A given model file is loaded once per process and shared: calling
clip.initEmbedagain with the same path (on any thread) returns a handle onto the already-resident weights rather than reloading them.var model = clip.initEmbed(path); model.dimension; // 512 for ViT-B/32, 768 for ViT-L/14, ...
model.dimension¶
Read-only Number: the dimension of the shared embedding space (the length of every vector this model produces).
model.onGpu¶
Read-only Boolean:trueif this model’s weights and compute live on a GPU (CUDA, or Metal on macOS),falseif it runs on the CPU — a CPU-only build, no GPU present, or a GPU that was rejected (see CLIP and the GPU, andclip.errMsgfor the reason). Fixed when the model is loaded: thread copies compute on the same backend, so the value cannot go stale.
model.embedImageToNumbers¶
model.embedImageToNumbers(image)Embed an image and return the vector as a JavaScript
ArrayofNumber.imageis either aStringfile path or aBufferholding the image bytes — a JPEG/PNG/… blob such as areadFile()result, afetch()body, or a rampart-sqlvarbytecolumn. A String is always taken as a path and a Buffer always as image bytes (never the reverse). Any buffer flavor is accepted (a plain buffer,Uint8Array,ArrayBuffer, or a NodeBuffer). Either way the image is decoded, resized and center-cropped to the model’s input size automatically; supported formats include JPEG, PNG, BMP, GIF (first frame), TGA, PSD, HDR, PIC and PNM. The returned vector is L2-normalized. (Contrast theembedText*methods below, where a Buffer means text stored in a buffer, not an image.)
model.embedImageToFp16Buf / embedImageToFp32Buf¶
model.embedImageToFp16Buf(image)/model.embedImageToFp32Buf(image)As above —
imageis a pathStringor aBufferof image bytes — but return the vector as aBufferof fp16 (2 bytes per element) or fp32 (4 bytes per element) values — the compact forms to store in a The rampart-faiss module index or a rampart-sql vector column.
model.embedTextToNumbers / embedTextToFp16Buf / embedTextToFp32Buf¶
model.embedTextToNumbers(text)(and theFp16Buf/Fp32Bufvariants)Embed a text string into the same space as the image methods, so an image vector and a text vector can be compared directly. Return types match the image methods.
model.similarity¶
model.similarity(vec1, vec2)Cosine similarity (a
Numberfrom -1 to 1) between two vectors, each an fp16 or fp32Bufferas returned by the*Bufmethods (both must be the same type). Because the vectors are L2-normalized this is simply their dot product; higher means more similar.
model.destroy¶
Release this model object. The object must not be used afterward (any method then throws). The underlying weights stay resident in the process for reuse, exactly as rampart-onnx and rampart-llamacpp keep embedding models loaded — so a subsequentclip.initEmbedof the same path is instant.
CLIP and the GPU¶
On a CUDA build the model is loaded into the GPU and embedded there; on macOS the GPU is Metal. The choice is automatic and needs no option, but it is made only when the device can actually run the model:
- A CUDA device whose compute capability is not in this module’s build, or an NVIDIA driver older than the CUDA the module was built with, is rejected (the same guard the other langtools modules apply).
- A GPU that does not implement every operation CLIP’s graphs need is rejected. This is what rules out Metal on older Macs: ggml’s Metal backend implements matrix multiply, normalization and softmax only on
MTLGPUFamilyApple7or Metal 3 devices — i.e. Apple Silicon, or macOS 13 and later — so an Intel Mac, or any Mac running macOS 11/12, has no usable Metal path here.In either case the model quietly falls back to the CPU and the reason is placed in
clip.errMsg(see Errors, Warnings and Logs). Results are identical either way; only speed differs. model.onGpu reports which one a loaded model got:var model = clip.initEmbed(path); if (!model.onGpu && clip.errMsg) printf("CLIP on CPU: %s\n", clip.errMsg);Note that
clip.getLog()is not a reliable indicator here: the op check has to create the GPU device in order to ask it what it supports, so the log contains the backend’s start-up banner even when the model then went to the CPU.
CLIP and threads¶
A model object is thread-safe to copy: it may be passed into a
rampart.threadworker (or is copied automatically when a server hands a request to a worker) and used there. All copies share one read-only set of weights; each thread builds its own small compute context on first use. This is the recommended way to embed in parallel — the heavy weights are not duplicated.Warnings and errors follow the langtools convention described in Errors, Warnings and Logs: a failure throws a JavaScript
Error, a non-fatal problem is placed inerrMsg, and nothing is written tostdoutorstderr.
Example: search images by text¶
var clip = require("rampart-clip");
var models = require("rampart-models");
var printf = rampart.utils.printf;
var model = clip.initEmbed(models.get("clip-vit-b-32-laion:q8_0"));
// embed a set of images once
var images = ["cat.jpg", "dog.jpg", "beach.jpg"].map(function(f) {
return { name: f, vec: model.embedImageToFp32Buf(f) };
});
// rank them against a text query
var q = model.embedTextToFp32Buf("a dog playing outside");
images
.map(function(im) { return { name: im.name, score: model.similarity(im.vec, q) }; })
.sort(function(a, b) { return b.score - a.score; })
.forEach(function(r) { printf(" %-12s %.4f\n", r.name, r.score); });
model.destroy();
The rampart-faiss module¶
Loading the module is a simple matter of using the require() function:
var faiss = require("rampart-faiss");
Note that the rampart-sql module’s built-in Vector Indexes (the IVFPQ
backend of CREATE
VECTOR INDEX) are also FAISS indexes,
maintained automatically as table rows change. Use rampart-faiss directly when the vectors do not
live in a SQL table, or when a custom index type, metric or training regime is needed.
openFactory¶
The
openFactoryfunction creates a new (empty) vector index from a FAISS index factory description string. See also the FAISS guidelines for choosing an index.Usage:
var faiss = require("rampart-faiss"); var idx = faiss.openFactory(description, dimensions[, metricType]);Where:
descriptionis a String, the factory description (e.g."Flat","IDMap2,Flat","IDMap2,OPQ96,IVF262144,PQ48"). It is highly recommended to includeIDMaporIDMap2so that arbitrary ids can be stored with each vector; otherwise ids are assigned sequentially starting at0.dimensionsis a positive Number, the vector dimension (e.g.384for all-minilm-l6-v2 embeddings — see modelInfo to read it from the embedding model).metricTypeis an optional String, the distance metric (case-insensitive):
"innerProduct"or"ip"- inner (dot) product (the default). Equivalent to cosine similarity when vectors are L2-normalized, as the initEmbed vectors are."l2"- squared euclidean distance."l1","manhattan"or"cityBlock"- L1 distance."linf"or"infinity"- L-infinity distance."lp"- Lp distance."canberra","brayCurtis","jensenShannon"- additional metrics supported by FAISS.
- Return Value:
- An Object (the index handle) with the property idx.settings and the functions idx.addFp32(), idx.addFp16(), idx.searchFp32(), idx.searchFp16(), idx.save() and (on CUDA builds) idx.enableGpu(). If the index type requires training, the handle additionally has the idx.trainer() function;
idx.trainerisundefinedfor index types that need no training (such asFlat).Example:
var faiss = require("rampart-faiss"); /* a small exact-search index storing our own doc ids */ var idx = faiss.openFactory("IDMap2,Flat", 4); /* add fp32 vectors (Buffer of 4 floats = 16 bytes each) */ idx.addFp32(101, new Float32Array([1,0,0,0]).buffer); idx.addFp32(102, new Float32Array([0,1,0,0]).buffer); /* search: top 2 results for a query vector */ var res = idx.searchFp32(new Float32Array([0.9,0.1,0,0]).buffer, 2); /* res = [ { id: 101, distance: 0.8999999761581421 }, { id: 102, distance: 0.10000000149011612 } ] (distances come back as 32-bit floats widened to JavaScript doubles, so they carry fp32 rounding) */ idx.save("myindex.faiss");
openIndexFromFile¶
The
openIndexFromFilefunction loads an index previously written with idx.save().Usage:
var faiss = require("rampart-faiss"); var idx = faiss.openIndexFromFile(filename[, readOnly]);Where:
filenameis a String, the path of the saved index.readOnlyis an optional Boolean. Iftrue, the index is opened read-only and memory-mapped, serving searches directly from disk rather than loading the entire index into RAM. Default:false.
- Return Value:
- An index handle, as returned by openFactory.
Example:
var faiss = require("rampart-faiss"); var llamacpp = require("rampart-llamacpp"); var idx = faiss.openIndexFromFile("myindex.faiss", true); var emb = llamacpp.initEmbed("all-minilm-l6-v2_f16.gguf"); var v = emb.embedTextToFp16Buf("my search query"); var res = idx.searchFp16(v.avgVec, 10); res.forEach(function(r) { rampart.utils.printf("id=%s distance=%f\n", r.id, r.distance); });
idx.settings¶
A read-only Object describing the open index:
dimension- A Number, the vector dimension.count- A Number, the number of vectors currently in the index (updated by idx.addFp32() / idx.addFp16()).metricType- A String, the distance metric (see openFactory).type- A String, the detected index type (e.g."Flat","IVFFlat","IVFPQ","HNSWFlat","LSH").map- A String,"IDMap"or"IDMap2"if the index stores arbitrary ids. Not set otherwise.PQm,PQbits- Numbers, product-quantization parameters. Only set for PQ-based index types.onGpu,gpuDevice- set after a successful idx.enableGpu() call.
idx.addFp32()¶
Add one vector of 32-bit floats to the index.
Usage:
var id = idx.addFp32(id, buffer);Where:
idis a Number or String (for ids larger than the float precision of a Number), the 64-bit id to associate with the vector. Pass-1to have an id assigned sequentially (0,1,2, …).The two forms are mutually exclusive, and which one applies is decided by the index type:
- An
IDMap/IDMap2index stores your own ids, and so requires an explicitid; passing-1throws.- Any other index type (e.g. a plain
Flat) has no id map and so requires-1; passing an explicitidthrows.In other words, use
IDMap/IDMap2when you need arbitrary ids, and a bare index when sequential ids are sufficient — see openFactory.
bufferis a Buffer of4 * dimensionbytes: the vector packed as little-endian 32-bit floats (e.g. from emb.embedTextToFp32Buf() or aFloat32Array’sbuffer).If the index requires training (see idx.trainer()) it must be trained before vectors can be added.
- Return Value:
- A Number, the id under which the vector was stored (the passed
id, or the assigned sequential id when-1was passed).
idx.addFp16()¶
The same as idx.addFp32() except that
bufferis2 * dimensionbytes: the vector packed as little-endian 16-bit (half-precision) floats, e.g. from emb.embedTextToFp16Buf() or rampart.vector conversion functions. The vector is converted to 32-bit floats before insertion (FAISS indexes store fp32).Usage:
var id = idx.addFp16(id, buffer);
idx.searchFp32()¶
Search the index for the nearest vectors to a query vector of 32-bit floats.
Usage:
var results = idx.searchFp32(buffer[, nResults[, nProbe]]);Where:
bufferis a Buffer of4 * dimensionbytes, the query vector (see idx.addFp32()).nResultsis an optional positive Number, the maximum number of results to return. Default:10.nProbeis an optional positive Number. For IVF index types, how many inverted-list cells to probe (more = better recall, slower search). Ignored for non-IVF indexes.
- Return Value:
An Array of Objects, best match first:
id- A Number, the id stored with the vector.distance- A Number, the metric value (for the defaultinnerProductmetric, larger is more similar; forl2, smaller is more similar).Fewer than
nResultsentries are returned if the index holds fewer vectors.
idx.searchFp16()¶
The same as idx.searchFp32() except that
bufferis the query vector packed as 16-bit floats (2 * dimensionbytes).Usage:
var results = idx.searchFp16(buffer[, nResults[, nProbe]]);
idx.save()¶
Write the index to a file, which may later be loaded with openIndexFromFile. If the index has been moved to the GPU with idx.enableGpu(), it is converted back to a CPU index for saving (the in-memory index stays on the GPU).
Usage:
idx.save(filename);Where
filenameis a String, the path of the file to write.
idx.enableGpu()¶
Move the index to a GPU. Only available on CUDA builds of the module; on CPU builds the function does not exist (check with
if (idx.enableGpu)). Note that not every FAISS index type is supported on the GPU.Usage:
if (idx.enableGpu) idx.enableGpu(device);Where
deviceis an optional Number, the GPU device id. Default:0.
- Return Value:
trueupon success. After the call,idx.settings.onGpuistrueandidx.settings.gpuDeviceis set. An error is thrown on failure.
idx.trainer()¶
Index types that partition or compress the vector space (IVF, PQ, OPQ, LSH and similar) must be trained on a representative sample of vectors before any can be added. For such indexes the handle returned by openFactory includes a
trainerfunction; for index types that need no training (e.g.Flat)idx.trainerisundefined.The trainer accumulates training vectors in a file, so that millions of training vectors need not be held in memory, and so that an interrupted run can be resumed from the same file.
Usage:
if (idx.trainer) { var trainer = new idx.trainer(path); ... }Where
pathis an optional String:
- A directory: a new training file named
faisstrainingdata.<n>.<pid>is created there. Default:"/tmp".- An existing training file (from a previous run): its vectors are reloaded and
trainer.settings.loadedRowsis set to the number of vectors found. Newly added vectors are appended.- A non-existent path: it is created as a new (empty) training file.
The training file is not deleted automatically; it may be reused by a later run, or removed with rampart.utils.rmFile when no longer wanted.
- Return Value:
- An Object (the trainer handle) with the read-only property
trainFile(a String, the path of the training data file) and the functions trainer.addTrainingfp32(), trainer.addTrainingfp16() and trainer.train().Example:
var faiss = require("rampart-faiss"); /* an IVF index over 384-dim vectors: requires training */ var idx = faiss.openFactory("IDMap2,IVF4096,Flat", 384); if (idx.trainer) { var trainer = new idx.trainer("./tdata"); /* feed a representative sample of vectors */ sql.exec("select Vec from vecs", {maxRows: 1000000}, function(row) { trainer.addTrainingfp16(row.Vec); }); trainer.train(); /* may take a while */ } /* now vectors may be added */ sql.exec("select Id, Vec from vecs", {maxRows: -1}, function(row) { idx.addFp16(row.Id, row.Vec); }); idx.save("vecs-ivf4096.faiss");
trainer.addTrainingfp32()¶
Append one 32-bit float vector to the training file.
Usage:
trainer.addTrainingfp32(buffer);Where
bufferis a Buffer of4 * dimensionbytes (see idx.addFp32()).
trainer.addTrainingfp16()¶
Append one 16-bit float vector to the training file. The vector is converted to 32-bit floats before being written.
Usage:
trainer.addTrainingfp16(buffer);Where
bufferis a Buffer of2 * dimensionbytes (see idx.addFp16()).
trainer.train()¶
Train the index from all the vectors accumulated in the training file (including vectors reloaded from a previous run). Depending on the index type and the number of training vectors, training can take from seconds to many hours.
Usage:
trainer.train();An error is thrown if no vectors have been added, or if the training file’s size is not a multiple of the vector size.
The rampart-sentencepiece module¶
Loading the module is a simple matter of using the require() function:
var sp = require("rampart-sentencepiece");
init¶
The
initfunction loads a SentencePiece model file and returns a handle for encoding text into subword pieces.Usage:
var sp = require("rampart-sentencepiece"); var encoder = sp.init(path);Where
pathis a String, the path to a SentencePiece model (e.g. sentencepiece.bpe.model from the bge-m3 repository).
- Return Value:
- An Object (the encoder handle) with the function encoder.encode().
Example:
var sp = require("rampart-sentencepiece"); var encoder = sp.init("./sentencepiece.bpe.model"); var pieces = encoder.encode("hello there you goat"); /* [ "▁hell", "o", "▁there", "▁you", "▁go", "at" ] */ var text = sp.decode(pieces); /* "hello there you goat" */
encoder.encode()¶
Encode text into subword pieces using the loaded model. In the pieces, the character
▁(U+2581, “lower one eighth block”) marks the start of a word.Usage:
var pieces = encoder.encode(text[, asString]);Where:
textis a String, the text to encode.asStringis an optional Boolean. Iftrue, the pieces are returned as a single space-separated String instead of an Array. Default:false.
- Return Value:
- An Array of Strings (one per piece), or a single space-separated String if
asStringistrue. Either form is accepted by decode.
decode¶
Reassemble encoded pieces into text. Decoding is purely textual (pieces are concatenated and the
▁word-start markers become spaces), so no model handle is needed and the function lives on the module object itself.Usage:
var sp = require("rampart-sentencepiece"); var text = sp.decode(pieces);Where
piecesis an Array of Strings, or a space-separated String of pieces, as produced by encoder.encode().
- Return Value:
- A String, the decoded text.
The rampart-models module¶
Experimental. rampart-models is new in this release. It is under active development and its API may change.
Loading the module is a simple matter of using the require() function:
var models = require("rampart-models");
The module downloads and locates GGUF and ONNX models by short name, returning a local path that feeds initEmbed, initRerank, initGen, onnx.initEmbed / onnx.initRerank and the rampart-sql llamaEmbed / onnxEmbed properties directly:
var models = require("rampart-models");
var onnx = require("rampart-onnx");
var llamacpp = require("rampart-llamacpp");
var oemb = onnx.initEmbed( models.get("bge-m3") ); // onnx .onnx file
var emb = llamacpp.initEmbed( models.get("bge-m3:q8_0") ); // gguf FILE
var gen = llamacpp.initGen( models.get("qwen3-4b") ); // gen = gguf
Models live under ~/.rampart/models/<category>/ (categories: embed, rerank, gen, clip; plain URLs go to
other). If the model
is already on disk its path is returned immediately with no network access; otherwise it is
downloaded from HuggingFace with
resume, retries and a single-line progress display.
A short name is resolved in this order:
- Already on disk under
~/.rampart/models/. - The embedded catalog — currently 81 curated models (embedding, reranking, text-generation and
CLIP), each pinned to a specific repository revision. Embedding entries also record the model’s
vector dimension and its retrieval prompts, when it has them (see Retrieval prompt sidecars below).
models.list()(or--liston the command line) shows them; it returns an Object keyed by category, each value an Array of the model names in that category. - A name containing
/is used as an exact HuggingFaceorg/repo— no search. - A live HuggingFace search (exact-name match first, model-family organizations before
converter organizations, then quant coverage). Live resolutions are remembered in
~/.rampart/models/.resolved.jsonso the same name resolves the same way next time.
models.get()¶
Resolve (and, if needed, download) a model; return its local path.
Usage:
var path = models.get(name[, options]);Where:
nameis a String: a catalog short name ("bge-m3"), a short name with a quant suffix ("bge-m3:q8_0"— implies GGUF), an exact HuggingFace"org/repo", or a fullhttps://URL (downloaded as-is).optionsis an optional Object:
format- A String,"onnx"or"gguf". Default:"onnx"when the model has an ONNX form and is an embedding/reranking model, else"gguf"(text-generation models are GGUF-only).quant- A String, the GGUF quantization (e.g."Q4_K_M","Q8_0","F16"); same meaning as the:quantname suffix. When the exact quant isn’t available, the closest available one is chosen.precision- A String, the ONNX weight precision:"fp16"(the default — half precision, the GPU sweet spot),"fp32"(full precision, the reference),"int8"or"q4"(quantized — smaller and often faster on CPU). Since model authors rarely publish every precision in the original repository, the closest converter mirror (onnx-community/*,Xenova/*) is searched as well; if the requested precision isn’t found anywhere it falls back tofp16thenfp32, printing a notice to stderr. Ignored for GGUF (usequantthere).category- A String, the subdirectory under~/.rampart/models/(default: from the catalog, else"embed"; URLs default to"other").dest- A String, an exact destination file or directory, overriding the category layout.progress-false(silent), a file handle to write progress to, or a Function called with progress info. Default: single-line progress on stdout.force- A Boolean, re-download even if the model is already present. Default:false.token- A String, a HuggingFace access token for gated repositories. Default: theHF_TOKENenvironment variable.confirm- A Function, called only when a download is actually needed (the model isn’t already on disk), so the caller can prompt before a large fetch. It receives an info Object ({name, format, dest, size, bytes, precision|quant, repo}) and returns a Boolean; a falsy return skips the download and models.get() returnsnull. Omit it for the default silent fetch. The module never reads stdin itself — any prompt lives in this callback.revision- A String, the git revision to fetch. Default: the catalog-pinned revision, else"main".
- Return Value:
- A String: the local path — the
.onnxfile for ONNX models, a.gguffile for GGUF. Throws on resolution or download failure. Returnsnullwhen aconfirmcallback declines a needed download.
models.pull()is an alias ofmodels.get().
- Note:
- For an ONNX model the whole usable directory is fetched — the
.onnxfile (plus its.onnx_dataweights sidecar when present) alongside the tokenizer and configuration files (tokenizer.json/vocab.txt,config.json,1_Pooling/etc.) — but the returned path is the.onnxfile itself. onnx.initEmbed / onnx.initRerank accept that file directly and auto-discover the tokenizer and configuration from its directory, so it feeds them (and the onnxEmbed property) as-is.
models.ggufGet() / models.onnxGet()¶
Format-explicit variants of models.get() — the call site then reads as the engine it feeds:
var emb = llamacpp.initEmbed( models.ggufGet("bge-m3") ); var oemb = onnx.initEmbed( models.onnxGet("bge-m3") );
models.url()¶
Download a plain URL into the models directory (or
dest) with the same resume/retry/progress machinery; returns the file path.Usage:
var path = models.url(theUrl[, options]);
models.resolve()¶
Resolve a name to its catalog-shaped entry — repository, pinned revision, available quants, license, category, and (for embedding models) vector dimension and retrieval prompts — without downloading.
Usage:
var entry = models.resolve(name[, options]);
models.list()¶
Return the embedded catalog’s short names grouped by category (an Object of Arrays), each annotated with its available formats:
var l = models.list(); /* { embed: [ "all-minilm-l6-v2 [onnx+gguf]", "bge-m3 [onnx+gguf]", ... ], rerank: [ "ms-marco-minilm-l6-v2 [onnx+gguf]", ... ], gen: [ "qwen3-4b [gguf]", ... ] } */The module also exposes
models.catalog(the raw catalog Object) andmodels.modelsDir(the~/.rampart/modelspath).
Retrieval prompt sidecars¶
Many embedding models are asymmetric: they expect a short prefix on queries, on documents, or on both (e.g.
nomic-embed-text-v1.5’s"search_query: "/"search_document: ", or the e5 family’s"query: "/"passage: "). Using such a model without its prompts costs retrieval quality.The catalog records each model’s published prompts, and every download writes them into a small sidecar file next to the model:
<file>.gguf.prompts.jsonbeside a GGUF file, and<name>.prompts.jsonbeside an ONNX model directory. Fetching the path of an already-downloaded model refreshes the sidecar, so models downloaded before this feature gain one on their nextmodels.get().The rampart-sql llamaEmbed and onnxEmbed settings read the sidecar automatically —
likevqueries,chunkembed()andembed(?, 'query'|'document')then apply the right prompt with no further configuration. See Retrieval prompts for how the prompts are applied and how to override or disable them. Symmetric models (e.g.all-minilm-l6-v2) have no prompts and embed text verbatim.The module-level engines embed exactly the text they are given: when calling initEmbed / onnx.initEmbed directly, prepending a model’s prompts is the caller’s responsibility.
Command line¶
The module doubles as a downloader script:
rampart rampart-models.js bge-m3 # onnx .onnx file (fp16) rampart rampart-models.js bge-m3 onnx fp32 # onnx, full precision rampart rampart-models.js bge-m3 onnx q4 # onnx, 4-bit quantized rampart rampart-models.js bge-m3 gguf Q8_0 # gguf file, chosen quant rampart rampart-models.js qwen3-4b:q4_k_m # quant suffix rampart rampart-models.js --list # show the catalogThe third argument is the ONNX
precision(fp16|fp32|int8|q4, defaultfp16) when the format isonnx, or the GGUFquantotherwise. The resolved local path is printed on success.--listgroups the catalog by category, colorizes on a color terminal (plain when piped), and marks already-downloaded models — e.g.[installed (onnx fp32, gguf Q4_K_M)]— showing the on-disk precision/quant of each.Environment:
HF_TOKENsupplies the HuggingFace token for gated repositories;HF_ENDPOINToverrides the HuggingFace host (for mirrors). Only HuggingFace’s stable URL patterns are used (api/models,resolve/{revision}/), never CDN URLs.
Putting It Together¶
A compact end-to-end example: fetch a model, embed documents, index them, and serve semantic search queries. Runs as-is on a fresh install — models.get() downloads the model on first use and returns its local path immediately thereafter.
rampart.globalize(rampart.utils);
var models = require("rampart-models");
var llamacpp = require("rampart-llamacpp");
var faiss = require("rampart-faiss");
var mdl = models.ggufGet("all-minilm-l6-v2");
var emb = llamacpp.initEmbed(mdl);
var dim = llamacpp.modelInfo(mdl).embedDim;
var docs = [
"The Eiffel Tower is 330 metres tall.",
"Gustave Eiffel also designed bridges.",
"Semantic search finds meaning, not just words."
];
/* build the index */
var idx = faiss.openFactory("IDMap2,Flat", dim);
docs.forEach(function(text, i) {
var v = emb.embedTextToFp16Buf(text);
idx.addFp16(i, v.avgVec);
});
/* search it */
var q = emb.embedTextToFp16Buf("How high is the Eiffel Tower?");
var res = idx.searchFp16(q.avgVec, 2);
res.forEach(function(r) {
printf("%f %s\n", r.distance, docs[r.id]);
});
For a larger, trained index (tens of millions of vectors) see the idx.trainer() example above; for improving the final ordering of the top results, see initRerank.
Integration with rampart-sql¶
The rampart-sql module has this whole pipeline built in, so vectors can be generated, indexed and searched inside the SQL engine — the langtools modules are the engines under the hood, with no application glue required.
Embedding — the embed() SQL function. A value is turned into a vector by an
embedding engine loaded once per connection with sql.set():
- llamaEmbed runs a GGUF model through rampart-llamacpp (initEmbed) — the CPU / macOS default.
- onnxEmbed runs an ONNX model through rampart-onnx (onnx.initEmbed) — GPU acceleration is available only on NVIDIA.
- clipEmbed runs a CLIP model through rampart-clip (clip.initEmbed) — images and text in one shared space, so an image table can be searched by a text query (see below).
The text engines (llamaEmbed / onnxEmbed) use the structure-aware chunking and pooling described under
Generating embeddings; all three share one loaded model across the SQL
worker threads — the thread-copyable, refcounted handle design these modules use makes that safe.
Indexing and search — FAISS vector indexes. CREATE VECTOR
INDEX builds a compressed IVFPQ index and the LIKEV operator searches it by
vector similarity, both backed by rampart-faiss (The rampart-faiss module) inside the engine. See Vector
Indexes and Vector Search. The typical
flow embeds documents with embed(), stores the vectors in a column, indexes that column, and answers
… WHERE vec LIKEV 'a natural-language query'.
Cross-modal image search — clipEmbed. Because CLIP embeds images and text into
the same space, an image table is searched by a plain text query. An image is stored
with embed(?, 'image') — the bytes (a Buffer from a column, or a file
path) go through CLIP’s vision encoder — and found with an ordinary … WHERE
Vec LIKEV 'a
dog catching a
frisbee', where the query text goes through CLIP’s
text encoder. Image Buffer parameters are embedded in the calling process before the statement is
bound, so a multi-megabyte blob is never shipped to the SQL helper and back. See clipEmbed
for the details.
Reordering — reranking. For the highest-precision top results a cross-encoder reranker (initRerank / onnx.initRerank) can reorder a short candidate list after the vector search — a little more latency for a better final ordering.