skeg-py
Python client for skeg, an SSD-primary KV+vector store designed for Personal AI Inference machines.
pip install skeg # pure-Python
pip install 'skeg[fast]' # PyO3-backed (binary wheels for macOS arm64, Linux x86_64, Linux aarch64)
RESP3 is the supported protocol. It carries the whole command surface, and it is the one to reach for unless you have a specific reason not to. The native binary client covers a smaller set of operations and is kept for existing callers.
import skeg
with skeg.connect() as c: # RESP3 on 6379, HELLO already negotiated
c.set(b"hello", b"world")
Two entry points, and they are not interchangeable:
| Call | Wire | Port | Surface |
|---|---|---|---|
skeg.connect() |
RESP3 | 6379 | Everything |
skeg.client() |
native binary | 7379 | KV + basic vectors |
What's in the package
Two synchronous clients sharing one error hierarchy:
| Module | Wire | Server binary | Default port | Use case |
|---|---|---|---|---|
skeg.RespClient |
RESP2/3 (Redis) | skeg-resp3 |
6379 | Recommended. Full surface: KV, vectors, tenancy, quotas. Also a drop-in for redis-cli / redis-py |
skeg.BinaryClient |
skeg-proto (native) | skeg |
7379 | Lower framing overhead, smaller surface. No payloads, filters or tenancy |
What only RESP3 can do: vector payloads (WITHPAYLOAD), search filters,
VMSET bulk insert, index consolidation, and every tenancy, quota and
QoS command. The native protocol has no equivalent for any of them.
Three backends behind one selector (skeg.client(...)):
- Pure-Python (default, zero deps) - just
pip install skeg - PyO3 / Rust (optional, faster framing) -
pip install skeg[fast](requires Rust toolchain at install time, or a pre-built wheel)
The PyO3 path mirrors BinaryClient's public API, so you can swap with
no code changes:
import skeg
c = skeg.client(prefer_native=True) # uses PyO3 if available
c = skeg.client(prefer_native=False) # forces pure-Python
Both backends speak native protocol v1 and v2, including
native_hello(), supports_kind() and the TurboQuant kinds:
import skeg
from skeg import _wire as wire
c = skeg.client(prefer_native=True, version=wire.VERSION_V2)
if c.supports_kind("tq2"):
c.vindex_create("notes", dim=1024, kind="tq2", backend="disk")
tests/test_backend_parity.py compares the two surfaces and fails both
ways: when the compiled backend loses a method, and when a method listed
as missing comes back. So the two cannot drift apart quietly.
Install
pip install skeg # pure-Python, zero dependencies
pip install 'skeg[fast]' # PyO3-backed; binary wheels for macOS arm64, Linux x86_64, Linux aarch64
To build from source (e.g. on Windows or another arch), pip install
will compile the PyO3 backend; set SKEG_PY_PURE=1 to skip it.
Quick start
KV
import skeg
with skeg.connect("127.0.0.1", 6379) as c:
c.set(b"hello", b"world")
print(c.get(b"hello")) # b"world"
print(c.mget([b"hello", b"nope"])) # [b"world", None]
print(c.append(b"hello", b"!")) # 6 - the new length
print(c.incr(b"counter")) # 1
c.delete(b"hello")
Vectors
import skeg
with skeg.connect() as c:
# kind: f32 | int8 | binary | tq1 | tq2 | tq4. Omit it for the
# server default, tq2 - near-f32 recall at a fraction of the RAM.
c.vindex_create("notes", dim=1024, backend="flat")
c.vset("notes", 1, my_embedding_1024d)
c.vset("notes", 2, another_embedding, payload=b"chapter-3")
for hit in c.vsearch("notes", query_embedding, k=10):
print(hit.id, hit.score)
# Payloads come back only when asked for, and can be filtered on.
for hit in c.vsearch(
"notes", query_embedding, k=10, with_payload=True, filter="tenant=alice"
):
print(hit.id, hit.score, hit.payload)
Bulk insert in one round trip:
c.vmset("notes", [(1, vec_a, b"payload-a"), (2, vec_b, None)])
c.vindex_consolidate("notes") # fold the disk delta into the graph
Vectors (on-disk Vamana)
Build the index offline (one-shot), then point the client at the served copy:
skeg-cli build --input embeddings.npy --output ./data --name notes
skeg --mode serve --data-dir ./data --tier pq:128:256
# Same client code as above; the server handles the disk-backed index.
hits = c.vsearch("notes", query, k=10)
Multi-tenant
c.hello(3, auth=("alice", "hunter2"))
print(c.skeg_whoami()) # "tenant=<hex> mode=tenant-aware"
# All subsequent GET/SET are auto-scoped to alice's namespace.
c.subject_erase(b"user:42:") # erase one data subject, returns a count
c.quota_set("alice", max_vectors=1_000_000, max_disk_bytes=None) # None = unlimited
c.qos_set("alice", rate=500, burst=1000, max_concurrent=8)
c.reclaim() # admin: physically free erased bytes
Erasure is logical: the bytes leave the disk on a later reclaim().
Native binary protocol
Smaller surface, less framing overhead. Two wire versions:
from skeg import BinaryClient, VectorKindV2
from skeg import _wire as wire
# v1 (default): f32, int8 and binary kinds only.
with BinaryClient.connect("127.0.0.1", 7379) as c:
c.vindex_create("notes", dim=1024, kind="int8")
# v2: adds the TurboQuant kinds and capability negotiation.
with BinaryClient.connect("127.0.0.1", 7379, version=wire.VERSION_V2) as c:
protocol_version, kind_mask = c.native_hello()
if c.supports_kind("tq2"):
c.vindex_create("notes", dim=1024, kind="tq2")
Kind byte 3 means different things in the two versions: PQ in v1,
TQ1 in v2. That collision is why v2 exists, and why VectorKindV2 is a
separate enum from VectorKind rather than extra variants on it. A v1
connection asking for byte 3 is refused by the server rather than
silently given the wrong index.
Testing
brew tap skegdb/tap
brew install skeg
git clone https://github.com/skegdb/skeg-py
cd skeg-py
pip install -e '.[test]'
SKEG_BIN=$(which skeg) SKEG_RESP3_BIN=$(which skeg-resp3) pytest
The fixture spawns one server per session and tears it down at the end. Both env vars are required: skeg-py used to live inside the skeg repo, where the binaries could be found by relative path, and no longer does. Tests that need a server skip without them.
Conformance suite
Command coverage is not asserted by hand here. tests/test_conformance_resp3.py
and tests/test_conformance_native.py run the shared case files that
every skeg client is checked against, driving this SDK's public methods:
SKEG_CONFORMANCE_DIR=<skeg-internal>/conformance \
SKEG_BIN=$(which skeg) SKEG_RESP3_BIN=$(which skeg-resp3) pytest
Without SKEG_CONFORMANCE_DIR those tests skip. Cases marked wire_only
are skipped by design: they are things a typed SDK cannot express - a
GET with no key, an unknown command - and the case files ship
standalone validators that cover them against the raw wire.
Test-suite safety vs your data
The pytest suite is designed for a server it owns. It writes keys
under names like doc:N, counter:N, sk-N, and creates/drops
VINDEX entries prefixed with pytst-. If you ever override the
default fixture to point the suite at a server that already holds
real data:
- KV: keys with the names above will be overwritten or deleted.
- VINDEX: only entries that match the
pytst-prefix are touched by the cleanup loop. Unrelated VINDEX are left alone.
In short: never point pytest at a server you can't afford to lose
KV state on. The pytest-spawned fixture is the safe default.
Compatibility
- Python 3.10+ (one abi3 wheel covers 3.10, 3.11, 3.12, 3.13).
- macOS arm64, Linux x86_64, Linux aarch64 (wheels). Other targets
build from sdist; set
SKEG_PY_PURE=1to skip the PyO3 backend. - RESP2 and RESP3, and native protocol versions 1 and 2. All stable.
License
Apache-2.0. See LICENSE.
Release files for skeg 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| skeg-0.2.0.tar.gz | 33.6 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| skeg-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| skeg-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| skeg-0.2.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
Total release size: 1.4 MB
Release files / skeg-0.2.0.tar.gz
| Download URL | skeg-0.2.0.tar.gz |
|---|---|
| Size | 33.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e3795bf8c1628e85459f6dbaf02aa29d86bd29d167ae50115aba97b8c6c1c49
|
|
BLAKE2b-256 checksum How to use checksums |
66d36bb6db052e3803def98b216ba93226618562a47a97892ad5638b1f4bfd8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / skeg-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | skeg-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 495.3 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
f2bbdf3c0854eee958df7bf83ec6530a55102fb956e6cc69bbea5f0f34d81dd9
|
|
BLAKE2b-256 checksum How to use checksums |
bc26f3cc70a59c416fe969e3d4ac47b87c888841f161c0e758503d22d404967c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / skeg-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | skeg-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 489.4 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
2d800fd7a2622980e644d999d9d807d79d612b0a77d9089ad1e0bc50418d76ad
|
|
BLAKE2b-256 checksum How to use checksums |
1a38b9d631a2a9a9dd6d3fe29ddebea1093a797ddbc6d31a30271bca4b46cb50
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / skeg-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | skeg-0.2.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 424.8 kB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
a65a16e1a2dbeec065f71694191e6e7d9f161e5526e9ad3f4320631c14151e21
|
|
BLAKE2b-256 checksum How to use checksums |
ef95cb0667ad34935c10e367f6afc023048b635402158d82f857fe2959eb4906
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|