SQLite AI Extension
repository·main·Indexed 40 days ago
https://github.com/sqliteai/sqlite-aiAn extension that brings on-device LLM inference, embeddings, audio transcription (Whisper), and vision capabilities directly into SQLite. It allows developers to run AI models via GGUF locally and privately using standard SQL queries. Supports Node.js (@sqliteai/sqlite-ai v0.7.56), Python (sqlite-ai), Flutter/Dart (sqlite_ai), Android, and Swift.
What's inside SQLite AI
- SQLite-AI is an extension for SQLite that brings artificial intelligence capabilities directly into the database. It allows developers to run, fine-tune, and serve AI models using standard SQL queries. This is specifically designed for on-device and edge applications where low-latency and offline inference are required.
Understand the SQLite-AI lifecycle and resource management
mainA SQLite-AI connection manages four independent resources: a model, an inference context (which owns the KV cache), a sampler, and a chat (the message history). Releasing one does not necessarily release the others, allowing for granular control over memory.
Resource Release Mapping
Function Call Model Context / KV Cache Sampler Chat History llm_model_load()(again)replaced freed freed kept llm_model_free()freed freed freed kept llm_context_free()kept freed kept kept llm_chat_free()kept cache cleared kept freed closing the connection freed freed freed freed Key Behaviors
- Conversations survive model switches: Because chat history is not tied to a model, loading a new model via
llm_model_load()will cause the nextllm_chat_respond()to replay the entire transcript through the new model's template. To start a fresh conversation when switching models, callllm_chat_free()first. - Samplers are tied to models: A sampler cannot outlive a model. If you load a new model, you must rebuild your sampler chain. If you do not configure one, a default is created automatically.
-- Example: Switching models while clearing old history and cache SELECT llm_chat_save('before switching model'); -- optional: keep a copy SELECT llm_chat_free(); -- drop history and clear the cache SELECT llm_model_load('/path/to/other-model.gguf'); SELECT llm_context_create_chat('context_size=4096'); SELECT llm_sampler_create(); -- rebuild sampler after model load SELECT llm_sampler_init_temp(0.8); SELECT llm_sampler_init_dist(42); SELECT llm_chat_respond('Hello');- Conversations survive model switches: Because chat history is not tied to a model, loading a new model via
Understand the difference between token-level and sentence-level embeddings
mainThe number and type of embeddings returned depend on the
pooling_typeconfigured inllama_context_params:Token-level embeddings (
LLAMA_POOLING_TYPE_NONE):- Returns an embedding for every token in the input.
- Retrieve using
llama_get_embeddings(ctx). - Resulting shape is effectively a matrix of (tokens × dimension).
Sentence-level (pooled) embeddings (
LLAMA_POOLING_TYPE_MEAN,LLAMA_POOLING_TYPE_CLS, orLLAMA_POOLING_TYPE_LAST):- Returns exactly one embedding per sequence ID (
seq_id). - Retrieve using
llama_get_embeddings_seq(ctx, seq_id). - Useful for semantic search and sentence similarity tasks.
- Returns exactly one embedding per sequence ID (
Load the sqlite-ai extension in Python
mainTo use AI capabilities in your SQL queries, you must load the
sqlite-aiextension into your SQLite connection. This involves enabling extension loading, locating the binary viaimportlib.resources, and then loading it.Note: If you encounter issues, some operating systems disable extension loading by default. You may need to follow platform-specific instructions to enable it.
import importlib.resources import sqlite3 # Connect to your SQLite database conn = sqlite3.connect("example.db") # Load the sqlite-ai extension # Use 'sqliteai.binaries.cpu' or 'sqliteai.binaries.gpu' ext_path = importlib.resources.files("sqliteai.binaries.cpu") / "ai" conn.enable_load_extension(True) conn.load_extension(str(ext_path)) conn.enable_load_extension(False) # Verify installation print(conn.execute("SELECT ai_version();").fetchone())Load the SQLite AI extension in Node.js
mainTo use the AI capabilities, you must load the extension into your SQLite database instance. Use
getExtensionPath()from@sqliteai/sqlite-aito retrieve the correct binary path for your current platform, then pass it to your SQLite driver's extension loading method (e.g.,db.loadExtension()inbetter-sqlite3).import { getExtensionPath } from '@sqliteai/sqlite-ai'; import Database from 'better-sqlite3'; const db = new Database(':memory:'); db.loadExtension(getExtensionPath()); // Verify installation by checking the AI extension version const version = db.prepare('SELECT ai_version()').pluck().get(); console.log('AI extension version:', version);Install the sqlite-ai Python package
mainThe
sqlite-aiPython package provides prebuilt binaries for the SQLite-AI extension across multiple platforms and architectures. When you install the package viapip, it automatically selects the correct binary package for your platform and architecture. You can choose between the CPU or GPU variant depending on your hardware requirements.pip install sqlite-aiInstall @sqliteai/sqlite-ai
mainInstall the SQLite AI extension for Node.js using npm. The package automatically detects your platform and downloads the appropriate native binary (e.g.,
.so,.dylib, or.dll) during installation.npm install @sqliteai/sqlite-aiInstall the sqlite_ai package
mainAdd the
sqlite_aipackage to your Flutter or Dart project using the following command. This package requires Dart 3.10+ or Flutter 3.38+.dart pub add sqlite_aiRecover from a full context error
mainWhen a chat fills its context,
llm_chat_respond()returnsContext size exceeded. The conversation remains usable, but you must make room to continue. You have three options:- Start over: Call
llm_chat_free()to drop the history and clear the cache. - Enlarge the context: Call
llm_context_free()followed byllm_context_create_chat('context_size=...')with a larger window. The history is preserved and replayed into the new context. - Compact the history: Since history is stored in standard tables, you can use SQL to trim old messages and then restore the history.
Compaction Example
To keep only the most recent 8 messages:
SELECT llm_chat_save('before compaction'); DELETE FROM ai_chat_messages WHERE id NOT IN (SELECT id FROM ai_chat_messages ORDER BY id DESC LIMIT 8); SELECT llm_chat_restore((SELECT uuid FROM ai_chat_history ORDER BY id DESC LIMIT 1));- Start over: Call
Install SQLite-AI
mainSQLite-AI can be installed via pre-built binaries or platform-specific packages:
Pre-built Binaries
Download binaries for Linux (x86/ARM), macOS (x86/ARM), Windows (x86), Android, or iOS from the official Releases page.
Platform-Specific Installation
- Python: Install via PyPI using
pip install sqlite-ai. - Flutter/Dart: Use
flutter pub add sqlite_aiordart pub add sqlite_ai. - Android: Add
ai.sqlite:ai:0.7.55to your Gradle dependencies. - Swift: Add the repository as a package dependency in Xcode.
pip install sqlite-ai- Python: Install via PyPI using
Load the SQLite-AI extension
mainOnce the binary is available, you must load the extension into your SQLite session. You can do this via the SQLite CLI dot-command or via a SQL statement.
-- In SQLite CLI .load ./ai -- In SQL SELECT load_extension('./ai');How to generate sentence-level embeddings in llama.cpp
mainBy default, llama.cpp can produce embeddings per token. To force the generation of a single embedding vector for an entire text input (sentence embedding), you must enable a pooling mode in the
llama_context_paramswhen creating the context.When pooling is enabled, you must use
llama_get_embeddings_seq(ctx, seq_id)to retrieve the resulting vector instead ofllama_get_embeddings(ctx).struct llama_context_params cparams = llama_context_default_params(); // Enable pooling to get one embedding per sequence cparams.pooling_type = LLAMA_POOLING_TYPE_MEAN; struct llama_context *ctx = llama_new_context_with_model(model, cparams); // ... after llama_encode() or llama_decode() ... // Retrieve the single sentence-level embedding const float *embedding = llama_get_embeddings_seq(ctx, 0);