Quickstart — Semvec REST API for semantic memory (5 min)¶
The lowest-friction way to try Semvec is the REST API: one
semvec serve command, then call HTTP from any client. No Python
integration required, every endpoint shape and JSON schema is fixed.
For tighter per-turn latency, in-process state sharing, or custom embedders inside the host process, drop into the Python library quickstart further down.
Prerequisites¶
- Python 3.10 or newer (
python --version) - An embedder. Cheapest:
pip install sentence-transformers
REST API quickstart¶
Install¶
Start the server¶
Server is ready when it logs Uvicorn running on http://0.0.0.0:8001.
Call it¶
# 1. Health check
curl -s http://localhost:8001/v1/health
# → {"status":"ok","active_sessions":0,"version":"1.0.0"}
# 2. Create a session under your own id (0.7.3+; alternatively omit
# session_id on /v1/run and reuse the id from its response)
curl -s -X POST http://localhost:8001/v1/session/create \
-H 'Content-Type: application/json' \
-d '{"session_id": "demo"}'
# → {"session_id":"demo","created":true}
# 3. Run a turn
curl -s -X POST http://localhost:8001/v1/run \
-H 'Content-Type: application/json' \
-d '{
"session_id": "demo",
"message": "We currently support SEPA payments but not iDEAL."
}'
# → {"session_id":"demo","context":"[Semvec Context | Turn 0 | 0 memories]",
# "top_similarity":0.0,"short_circuit":false,"drift_score":0.0,
# "drift_detected":false,"drift_phase":"stable","dedup_signal":null,
# "retrieval_error":false}
# (empty context — nothing stored yet)
# 4. Store the answer your LLM produced for that turn
curl -s -X POST http://localhost:8001/v1/store \
-H 'Content-Type: application/json' \
-d '{
"session_id": "demo",
"response": "SEPA covers the eurozone; iDEAL is the dominant method in NL."
}'
# → {"session_id":"demo"}
# 5. Ask a follow-up — the context block is built automatically
curl -s -X POST http://localhost:8001/v1/run \
-H 'Content-Type: application/json' \
-d '{
"session_id": "demo",
"message": "Which markets does iDEAL unlock for us?"
}'
# → {"session_id":"demo",
# "context":"[Semvec Context | Turn 1 | 1 memories]\nRelevant context:\n 1. [1.00] Q: We currently support SEPA payments but not iDEAL. A: SEPA covers the eurozone; iDEAL is the dominant method in NL.",
# "top_similarity":0.3517,"short_circuit":false,"drift_score":0.3241,
# "drift_detected":false,"drift_phase":"shifting","dedup_signal":null,
# "retrieval_error":false}
# (the stored answer was paired with turn 3's buffered question, then retrieved)
Two shortcuts: /v1/run without a session_id creates a fresh session and
returns its id; and /v1/run also accepts a response field carrying the
previous turn's LLM answer, storing it inline before retrieval. A /v1/run
with a session_id that does not exist returns 404 — create it first
(step 2) or omit the field.
The context field returned by /v1/run is the constant-cost
input you pass to your LLM, instead of the growing transcript. Whether
the conversation has run for 3 turns or 30 000, the size stays bounded.
See the REST API reference for the full
endpoint catalogue, the CLI reference for
semvec serve flags and SEMVEC_* environment variables.
You are productive
If /v1/health returns 200 and /v1/run returns a context block,
you have a working Semvec. Move on:
- Choose your path — when to graduate to the in-process library
- Full tour (15 min) — every surface end-to-end
- Concepts & Glossary — phases, tiers, anchors, triggers
- Embedders — pick a different model
Python library quickstart¶
For applications that need tighter latency than the REST round-trip, or in-process control over embedders and state, use the library directly.
Install¶
Minimal example¶
from semvec import SemvecState, SemvecConfig
from sentence_transformers import SentenceTransformer
# 1. Set up Semvec + your embedder
model = SentenceTransformer("all-MiniLM-L6-v2")
# dimension MUST match your embedder's output size.
# all-MiniLM-L6-v2 = 384, all-mpnet-base-v2 = 768,
# OpenAI text-embedding-3-small = 1536.
# Mismatch raises EmbeddingError at the first state.update().
state = SemvecState(config=SemvecConfig(dimension=384))
# 2. Feed it a conversation, turn by turn
conversation = [
"I want to discuss our European customer onboarding flow.",
"We currently support SEPA payments but not iDEAL.",
"Which markets does iDEAL unlock for us?",
]
for text in conversation:
result = state.update(model.encode(text), text)
print(f"phase={result['phase']:14} fsm={result['fsm']:.3f}")
# phase: conversation stage (initialization / exploration / convergence / resonance / stability / instability)
# fsm: stability score in [0, 1] — gate expensive actions on fsm > 0.7
# See concepts-glossary.md for the full list of metrics returned by update().
# 3. Get a compact context block for the next LLM call
from semvec.token_reduction import SemvecStateSerializer
context = SemvecStateSerializer().serialize(state, query_text="Should we add iDEAL?")
print(context[:400], "...")
The context string above is a compact block ready to inject into your next
LLM call — produced in-process instead of over HTTP.
This minimal path is dense-cosine only
SemvecStateSerializer().serialize() retrieves by dense cosine
similarity only — no BM25 fusion, no cross-encoder reranking. The
documented benchmark accuracy uses hybrid BM25
+ cross-encoder rerank, which live in SemvecSession.run(), not the raw
serializer. For retrieval that matches those numbers, use the session
facade with make_cross_encoder_reranker + enable_bm25=True — see
Retrieval quality.
Recommended in-process path: SemvecSession¶
For real applications, use the turn facade SemvecSession. It runs the same
composition the REST /v1/run endpoint does (retrieve → optional BM25 → optional
rerank → MMR → drift → context block) and returns a TurnResult. This is the
path the hackathon starter should copy:
import numpy as np
from semvec import SemvecConfig, SemvecState, SemvecSession
from sentence_transformers import SentenceTransformer
# Bring your own embedder (BYOE): any object with get_dimension()/get_embedding().
class STEmbedder:
def __init__(self, name="all-MiniLM-L6-v2"):
self._m = SentenceTransformer(name)
self._dim = int(self._m.get_sentence_embedding_dimension() or 384)
def get_dimension(self) -> int:
return self._dim
def get_embedding(self, text: str) -> np.ndarray:
if not text.strip():
return np.zeros(self._dim, dtype=np.float64)
v = self._m.encode(text, normalize_embeddings=True, convert_to_numpy=True,
show_progress_bar=False)
return np.asarray(v, dtype=np.float64)
embedder = STEmbedder()
config = SemvecConfig(dimension=embedder.get_dimension())
session = SemvecSession(SemvecState(config=config), embedder, config)
# One turn = your message + (optionally) the previous LLM answer to store.
result = session.run_sync(
"Which markets does iDEAL unlock for us?",
response="SEPA covers the eurozone; iDEAL is the dominant method in NL.",
)
print(result.context) # constant-cost block for your next LLM call
print(result.drift_phase) # "stable" | "shifting" | "drifted"
# Real output (captured against all-MiniLM-L6-v2):
# [Semvec Context | Turn 1 | 1 memories]
# Relevant context:
# 1. [1.00] SEPA covers the eurozone; iDEAL is the dominant method in NL.
# shifting
To match the published benchmark recall, add BM25 + a reranker (needs
pip install "semvec[hybrid]" sentence-transformers) — see
Retrieval quality.
Every REST SEMVEC_* knob has a library parameter here — see the
env → parameter map.
Common stumbles¶
| Error | Fix |
|---|---|
ImportError: No module named 'sentence_transformers' |
pip install sentence-transformers |
RuntimeError: Explicit embedder required |
You called a surface that needs embedder= — pass one in, or install sentence-transformers to let Semvec auto-load the default. |
EmbeddingError: dimension mismatch |
Your model and SemvecConfig.dimension disagree. Set dimension= to match (384 for MiniLM, 1536 for OpenAI text-embedding-3-small). |
REST /v1/run returns 503 |
Embedder not ready. Wait for the Uvicorn running on log line, or pre-warm with /v1/health. |
REST /v1/run returns 401 |
Endpoint requires a licence JWT. Set SEMVEC_LICENSE_KEY="eyJ..." (Pro / Enterprise key, or a Community evaluation key issued via vertrieb@versino.de) before starting semvec serve. |
More symptoms and fixes in Troubleshooting.