local-embed
Standalone local embedding server — loads one GGUF (llama-cpp) or HF
transformer embedding model once and exposes it as an
OpenAI-compatible
/v1/embeddings HTTP endpoint plus FastMCP tools, on a single port.
Built for slife (its memdb and memfiles plugins both call this
service over HTTP, so the model is never loaded twice in one process
tree), but it is a fully standalone package — any OpenAI-compatible
client can use it.
POST http://127.0.0.1:8000/v1/embeddings
slife memdb ─────────────────────────┐
slife memfiles ──────────────────────┤
any OpenAI client ───────────────────┤
▼
┌────────────────────────────┐
│ local-embed │
│ /v1/embeddings (HTTP) │
│ /v1/models (HTTP) │
│ /health (HTTP) │
│ /mcp (FastMCP tools) │
│ ONE loaded model │
└────────────────────────────┘
Install
# GGUF backend (llama-cpp-python)
uv tool install 'local-embed[gguf]' # or: uv pip install 'local-embed[gguf]'
# Transformer backend (sentence-transformers)
uv tool install 'local-embed[transformer]' # or: uv pip install 'local-embed[transformer]'
Python 3.13+. The server core only depends on fastmcp + starlette; the
heavy model backends are optional extras.
Run
# GGUF model (recommended — offline, no HF download)
local-embed --backend gguf --model bge-m3 --gguf-path /path/to/bge-m3-q4_k_m.gguf --port 8000
# HF transformer model (downloads from HF hub on first load)
local-embed --backend transformer --model BAAI/bge-m3 --port 8000
By default it binds 127.0.0.1:8000 (local only — never exposed to the
network).
Transformer models & the env: section
A transformer model is referenced by its HF repo name (BAAI/bge-m3).
The HuggingFace hub resolves that name against its cache (default
~/.cache/huggingface), downloading on first load if missing. To keep the
server self-contained — point it at an existing local cache, or force
offline — put the env vars in local_embed.json5's env: section (injected
into this process before any model loads; an existing shell env var wins):
{
env: {
HF_HUB_CACHE: "C:\\Users\\me\\HuggingFace\\hub", // existing cache
HF_HUB_OFFLINE: "1" // never hit the network
},
models: {
"bge-m3-transformer": { backend: "transformer", model: "BAAI/bge-m3", device: "" }
}
}
Without HF_HUB_CACHE the model resolves against the default cache — a
model already downloaded elsewhere would be silently re-fetched.
Use
curl http://127.0.0.1:8000/v1/embeddings \
-H 'Content-Type: application/json' \
-d '{"model": "bge-m3", "input": ["hello world", "another text"]}'
Returns the standard OpenAI shape:
{
"object": "list",
"data": [
{"object": "embedding", "index": 0, "embedding": [0.012, ...]},
{"object": "embedding", "index": 1, "embedding": [...]}
],
"model": "bge-m3",
"usage": {"prompt_tokens": 3, "total_tokens": 3}
}
Any OpenAI-compatible client works — point base_url at
http://127.0.0.1:8000/v1 (e.g. slife's memdb.embedding api backend, or
the openai Python package):
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="local")
vecs = client.embeddings.create(model="bge-m3", input=["hello"])
Other endpoints:
GET /v1/models— the loaded model + its real embedding dimension.GET /health—{status, backend, model, dimension, loaded}.POST /mcp— FastMCP streamable-HTTP endpoint (toolsembed_statusandembed).
Dimension
The real output width is only known once the model is loaded (n_embd /
get_sentence_embedding_dimension). GET /v1/models reports the real
dimension, so a client can size its vector table correctly — a wrong width
silently drops every mis-sized embedding.
As a slife external plugin
slife treats every embedding model as a remote OpenAI-compatible
endpoint — local-embed is just one such endpoint. The model is
determined by the plugin's active model: slife discovers it from
GET /v1/models (the entry flagged active: true) on load.
Register local-embed as an external plugin (like mcp-plugin) so slife
manages the process, and point slife's embedding config at it with the
unified OpenAI format:
plugins: {
external: [
{ name: "local-embed", module: "local_embed.server" }
]
},
memdb: {
embedding: {
base_url: "http://127.0.0.1:8000/v1", // stable port from local_embed.json5
api_key: "local",
}
}
The plugin binds the stable port from local_embed.json5 (default
8000), so slife's base_url is fixed whether local-embed runs as the
plugin or standalone. When the service is unreachable, slife degrades
gracefully to keyword search.
CLI
local-embed --help
--host/--port— bind address (default127.0.0.1:8000)--backend gguf|transformer— model backend (defaultgguf)--model— model name/id (for metadata and dim guessing)--gguf-path— path to the GGUF file (required forbackend=gguf)--device cpu|cuda— transformer device (default auto)--log-level— logging level (defaultINFO)
License
MIT — see the repository root LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file local_embed-0.1.0.tar.gz.
File metadata
- Download URL: local_embed-0.1.0.tar.gz
- Upload date:
- Size: 28.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb4cf301db8bbf377a811b0a448482bf33ec71aa7d343626a86e490fd6ade1e4
|
|
| MD5 |
90a0262ea2efea6804d38ff8b1085bc6
|
|
| BLAKE2b-256 |
c602ba72bc5356ad1b7bfcf499143113cb5df9703677d7941655dae1a5cae50e
|
File details
Details for the file local_embed-0.1.0-py3-none-any.whl.
File metadata
- Download URL: local_embed-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d69f8957b2d047cdb6b1e3f6f017f731d9f47b51d82c365485ebb5d1d4ab068
|
|
| MD5 |
e91198ce5a409d4d7e97d37fa6ea6774
|
|
| BLAKE2b-256 |
6a3876486af1a948c789a0f1e899d8015516437547351480b2baee0376a79369
|