-
Notifications
You must be signed in to change notification settings - Fork 233
Add lua binding #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
karminski
wants to merge
2
commits into
asg017:main
Choose a base branch
from
karminski:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add lua binding #237
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
-- sqlite_vec.lua Lua 5.1 compatible version with JSON fallback | ||
local sqlite3 = require("lsqlite3") | ||
|
||
local M = {} | ||
|
||
-- Function to load extension | ||
function M.load(db) | ||
local possible_paths = { | ||
"../../sqlite-vec.so", -- Linux | ||
"../../sqlite-vec.dll", -- Windows | ||
"../../sqlite-vec.dylib", -- macOS | ||
"./sqlite-vec.so", | ||
"./sqlite-vec.dll", | ||
"./sqlite-vec.dylib", | ||
"../sqlite-vec.so", | ||
"../sqlite-vec.dll", | ||
"../sqlite-vec.dylib", | ||
"sqlite-vec", | ||
} | ||
|
||
local entry_point = "sqlite3_vec_init" | ||
|
||
if db.enable_load_extension then | ||
db:enable_load_extension(true) | ||
for _, path in ipairs(possible_paths) do | ||
local ok, result = pcall(function() | ||
return db:load_extension(path, entry_point) | ||
end) | ||
if ok then | ||
db:enable_load_extension(false) | ||
return result | ||
end | ||
end | ||
db:enable_load_extension(false) | ||
error("Failed to load extension from all paths") | ||
else | ||
for _, path in ipairs(possible_paths) do | ||
local ok, result = pcall(function() | ||
return db:load_extension(path, entry_point) | ||
end) | ||
if ok then | ||
return result | ||
else | ||
local ok2, result2 = pcall(function() | ||
return db:load_extension(path) | ||
end) | ||
if ok2 then | ||
return result2 | ||
end | ||
end | ||
end | ||
error("Failed to load extension from all paths") | ||
end | ||
end | ||
|
||
-- Lua 5.1 compatible float to binary conversion function | ||
local function float_to_bytes(f) | ||
if f == 0 then | ||
return string.char(0, 0, 0, 0) | ||
end | ||
|
||
local sign = 0 | ||
if f < 0 then | ||
sign = 1 | ||
f = -f | ||
end | ||
|
||
local mantissa, exponent = math.frexp(f) | ||
exponent = exponent - 1 | ||
|
||
if exponent < -126 then | ||
mantissa = mantissa * 2^(exponent + 126) | ||
exponent = -127 | ||
else | ||
mantissa = (mantissa - 0.5) * 2 | ||
end | ||
|
||
exponent = exponent + 127 | ||
mantissa = math.floor(mantissa * 2^23 + 0.5) | ||
|
||
local bytes = {} | ||
bytes[1] = mantissa % 256; mantissa = math.floor(mantissa / 256) | ||
bytes[2] = mantissa % 256; mantissa = math.floor(mantissa / 256) | ||
bytes[3] = mantissa % 256 + (exponent % 2) * 128; exponent = math.floor(exponent / 2) | ||
bytes[4] = exponent % 128 + sign * 128 | ||
|
||
return string.char(bytes[1], bytes[2], bytes[3], bytes[4]) | ||
end | ||
|
||
-- Helper function: serialize float vector to binary format (Lua 5.1 compatible) | ||
function M.serialize_f32(vector) | ||
local buffer = {} | ||
|
||
if string.pack then | ||
for _, v in ipairs(vector) do | ||
table.insert(buffer, string.pack("f", v)) | ||
end | ||
else | ||
for _, v in ipairs(vector) do | ||
table.insert(buffer, float_to_bytes(v)) | ||
end | ||
end | ||
|
||
return table.concat(buffer) | ||
end | ||
|
||
-- New: JSON format vector serialization (more reliable fallback) | ||
function M.serialize_json(vector) | ||
local values = {} | ||
for _, v in ipairs(vector) do | ||
table.insert(values, tostring(v)) | ||
end | ||
return "[" .. table.concat(values, ",") .. "]" | ||
end | ||
|
||
return M |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Lua specific | ||
*.luac | ||
|
||
# SQLite databases (if any test files are created) | ||
*.db | ||
*.sqlite | ||
*.sqlite3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# SQLite-Vec Simple Lua Example | ||
|
||
This example demonstrates how to use sqlite-vec with Lua and the lsqlite3 binding. | ||
|
||
## Prerequisites | ||
|
||
1. **Lua** (5.1 or later) - The example is compatible with Lua 5.1+ | ||
2. **lsqlite3** - Lua SQLite3 binding | ||
3. **sqlite-vec extension** - Compiled for your platform | ||
|
||
## Installation | ||
|
||
### Install lsqlite3 | ||
|
||
Using LuaRocks: | ||
```bash | ||
luarocks install lsqlite3 | ||
``` | ||
|
||
Or on Ubuntu/Debian: | ||
```bash | ||
apt-get install lua-sql-sqlite3 | ||
``` | ||
|
||
### Build sqlite-vec | ||
|
||
From the sqlite-vec root directory: | ||
```bash | ||
make | ||
``` | ||
|
||
This will create the appropriate extension file (.so, .dll, or .dylib) for your platform. | ||
|
||
### Setup sqlite_vec.lua | ||
|
||
You have two options: | ||
|
||
1. **Copy the binding file** (recommended): | ||
```bash | ||
cp ../../bindings/lua/sqlite_vec.lua ./ | ||
``` | ||
|
||
2. **Modify the require path** in `demo.lua` to point to the bindings directory. | ||
|
||
## Running the Example | ||
|
||
```bash | ||
lua demo.lua | ||
``` | ||
|
||
Expected output: | ||
``` | ||
=== SQLite-Vec Simple Lua Example === | ||
sqlite_version=3.x.x, vec_version=v0.x.x | ||
Inserting vector data... | ||
Executing KNN query... | ||
Results: | ||
rowid=3 distance=0.000000 | ||
rowid=2 distance=0.200000 | ||
rowid=1 distance=0.400000 | ||
✓ Demo completed successfully | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
#!/usr/bin/env lua | ||
|
||
-- Simple Lua example demonstrating sqlite-vec usage | ||
-- This example shows how to create vector tables, insert data, and perform KNN queries | ||
|
||
local sqlite3 = require("lsqlite3") | ||
|
||
-- You can either: | ||
-- 1. Copy sqlite_vec.lua from ../../bindings/lua/sqlite_vec.lua to this directory | ||
-- 2. Or modify the path below to point to the bindings directory | ||
local sqlite_vec = require("sqlite_vec") | ||
|
||
function main() | ||
print("=== SQLite-Vec Simple Lua Example ===") | ||
|
||
-- Create in-memory database | ||
local db = sqlite3.open_memory() | ||
if not db then | ||
error("Failed to create database") | ||
end | ||
|
||
-- Load sqlite-vec extension | ||
local load_success, load_error = pcall(function() | ||
sqlite_vec.load(db) | ||
end) | ||
|
||
if not load_success then | ||
error("Failed to load sqlite-vec extension: " .. tostring(load_error)) | ||
end | ||
|
||
-- Check versions - handle the case where vec_version() might not be available | ||
local sqlite_version = nil | ||
local vec_version = nil | ||
|
||
for row in db:nrows("SELECT sqlite_version()") do | ||
sqlite_version = row.sqlite_version | ||
break | ||
end | ||
|
||
-- Try to get vec_version, but don't fail if it's not available | ||
local success, _ = pcall(function() | ||
for row in db:nrows("SELECT vec_version()") do | ||
vec_version = row.vec_version | ||
break | ||
end | ||
end) | ||
|
||
if sqlite_version then | ||
if vec_version then | ||
print(string.format("sqlite_version=%s, vec_version=%s", sqlite_version, vec_version)) | ||
else | ||
print(string.format("sqlite_version=%s, vec_version=unknown", sqlite_version)) | ||
end | ||
end | ||
|
||
-- Verify extension is loaded by checking for vec0 module | ||
local vec0_available = false | ||
for row in db:nrows("SELECT name FROM pragma_module_list() WHERE name='vec0'") do | ||
vec0_available = true | ||
break | ||
end | ||
|
||
if vec0_available then | ||
print("✓ sqlite-vec extension loaded successfully") | ||
else | ||
error("sqlite-vec extension loaded but vec0 module not found") | ||
end | ||
|
||
-- Test data - same as other examples for consistency | ||
local items = { | ||
{1, {0.1, 0.1, 0.1, 0.1}}, | ||
{2, {0.2, 0.2, 0.2, 0.2}}, | ||
{3, {0.3, 0.3, 0.3, 0.3}}, | ||
{4, {0.4, 0.4, 0.4, 0.4}}, | ||
{5, {0.5, 0.5, 0.5, 0.5}}, | ||
} | ||
local query = {0.3, 0.3, 0.3, 0.3} | ||
|
||
-- Create virtual table | ||
local create_result = db:exec("CREATE VIRTUAL TABLE vec_items USING vec0(embedding float[4])") | ||
if create_result ~= sqlite3.OK then | ||
error("Failed to create virtual table: " .. db:errmsg()) | ||
end | ||
|
||
-- Insert data using JSON format (more compatible) | ||
print("Inserting vector data...") | ||
db:exec("BEGIN") | ||
|
||
for _, item in ipairs(items) do | ||
local rowid = math.floor(item[1]) | ||
local vector_json = sqlite_vec.serialize_json(item[2]) | ||
|
||
local sql = string.format("INSERT INTO vec_items(rowid, embedding) VALUES (%d, '%s')", | ||
rowid, vector_json) | ||
local result = db:exec(sql) | ||
if result ~= sqlite3.OK then | ||
error("Failed to insert item: " .. db:errmsg()) | ||
end | ||
end | ||
|
||
db:exec("COMMIT") | ||
|
||
-- Verify data was inserted | ||
local count = 0 | ||
for row in db:nrows("SELECT COUNT(*) as count FROM vec_items") do | ||
count = row.count | ||
break | ||
end | ||
print(string.format("✓ Inserted %d vector records", count)) | ||
|
||
-- Perform KNN query using JSON format | ||
print("Executing KNN query...") | ||
local query_json = sqlite_vec.serialize_json(query) | ||
|
||
local sql = string.format([[ | ||
SELECT | ||
rowid, | ||
distance | ||
FROM vec_items | ||
WHERE embedding MATCH '%s' | ||
ORDER BY distance | ||
LIMIT 3 | ||
]], query_json) | ||
|
||
print("Results:") | ||
for row in db:nrows(sql) do | ||
print(string.format("rowid=%d distance=%f", row.rowid, row.distance)) | ||
end | ||
|
||
db:close() | ||
print("✓ Demo completed successfully") | ||
end | ||
|
||
-- Run the demo with error handling | ||
local success, error_msg = pcall(main) | ||
if not success then | ||
print("Error: " .. tostring(error_msg)) | ||
os.exit(1) | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#!/bin/bash | ||
|
||
# Simple run script for the Lua example | ||
|
||
echo "=== SQLite-Vec Lua Example Runner ===" | ||
|
||
# Check if Lua is available | ||
if ! command -v lua &> /dev/null; then | ||
echo "Error: Lua is not installed or not in PATH" | ||
exit 1 | ||
fi | ||
|
||
# Check if lsqlite3 is available | ||
lua -e "require('lsqlite3')" 2>/dev/null | ||
if [ $? -ne 0 ]; then | ||
echo "Error: lsqlite3 module is not installed" | ||
echo "Install it with: luarocks install lsqlite3" | ||
exit 1 | ||
fi | ||
|
||
# Check if sqlite-vec extension exists | ||
if [ ! -f "../../sqlite-vec.so" ] && [ ! -f "../../sqlite-vec.dll" ] && [ ! -f "../../sqlite-vec.dylib" ]; then | ||
echo "Error: sqlite-vec extension not found" | ||
echo "Build it with: cd ../.. && make" | ||
exit 1 | ||
fi | ||
|
||
# Run the demo | ||
echo "Running demo..." | ||
lua demo.lua |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to print errors to stderr: