Documentation
¶
Overview ¶
Package embed provides text-to-vector embedding via Ollama's local HTTP API. Vector embeddings power the `similarity` CEL variable and the `search_semantic` MCP tool (PR for issue #151).
Ollama is the only embedding source supported in v1 — it's the canonical local-model runner, exposes a stable HTTP API on localhost:11434, and lets the user pick the model via one-time `ollama pull <name>`. No SDK, no CGo, no bundled model.
The Embedder interface is small enough that other embedding sources (OpenAI API, llama.cpp HTTP, etc.) can plug in later without rewiring callers — same Embed(ctx, text) shape.
Index ¶
- Variables
- func BareName(name string) string
- func Cosine(a, b []float32) float64
- func Dot(a, b []float32) float64
- func Normalize(v []float32)
- type CatalogEntry
- type Embedder
- type LocalModel
- type OllamaEmbedder
- func (o *OllamaEmbedder) Embed(ctx context.Context, text string) ([]float32, error)
- func (o *OllamaEmbedder) ListLocal(ctx context.Context) ([]LocalModel, error)
- func (o *OllamaEmbedder) Model() string
- func (o *OllamaEmbedder) Pull(ctx context.Context, name string, onProgress func(PullProgress)) error
- type PullProgress
Constants ¶
This section is empty.
Variables ¶
var Catalog = []CatalogEntry{
{
Name: "all-minilm",
Description: "Smallest. Great for memory-constrained machines; lower quality.",
Size: "~45 MB",
Dimensions: 384,
},
{
Name: "bge-m3",
Description: "Multilingual; supports dense + sparse + colbert outputs.",
Size: "~600 MB",
Dimensions: 1024,
},
{
Name: "mxbai-embed-large",
Description: "Higher dimensionality. Better recall on long-form prose; slower.",
Size: "~670 MB",
Dimensions: 1024,
},
{
Name: "nomic-embed-text",
Description: "Strong general-purpose. Good default for English documents.",
Size: "~270 MB",
Dimensions: 768,
},
{
Name: "snowflake-arctic-embed",
Description: "Snowflake's open embedding family. Solid for short technical text.",
Size: "~330 MB",
Dimensions: 1024,
},
}
Catalog is the curated list of recommended embedding models. Sorted alphabetically by Name so list output is deterministic.
var ErrNoModel = errors.New("no embedding model configured (pass --embedding-model or per-call 'model')")
ErrNoModel is returned by Embed when no model has been configured (neither at server startup nor per-call). Surfaces cleanly in MCP responses as "no embedding model configured".
var ErrNoServer = errors.New("no embedding server configured")
ErrNoServer is returned by Embed when no server URL has been configured. Practically rare since OllamaEmbedder defaults to localhost:11434, but defensive against an empty override.
Functions ¶
func BareName ¶ added in v0.68.0
BareName strips an Ollama tag suffix ("nomic-embed-text:latest" → "nomic-embed-text") so a tagged local model can match a catalog entry. Returns name unchanged when there's no colon.
func Cosine ¶
Cosine returns the cosine similarity of two vectors, in [-1, 1]. Returns 0 when either vector is empty OR their lengths disagree — defensive against partially-populated cache entries. Vectors don't need to be pre-normalised; the function divides by the L2 norms itself. For pre-normalised vectors, prefer Dot which skips the norm computation.
Types ¶
type CatalogEntry ¶ added in v0.68.0
type CatalogEntry struct {
Name string `json:"name"`
Description string `json:"description"`
Size string `json:"size"`
Dimensions int `json:"dimensions"`
}
CatalogEntry describes one well-known embedding model that file-search-on knows about by name. The catalog is hand-curated and intentionally small — five entries covering the obvious choices. Locally-pulled models that aren't in the catalog still surface in list output; they're just not annotated with description / dims.
Adding a new entry: append to Catalog in alphabetical order (the sanity test enforces ordering). Each entry needs Name (the bare `ollama pull` name, no tag), Description (one user-visible line), Size (human-readable, for display only), and Dimensions (the vector length the model emits — used to flag dimension mismatches before a dimension-validated query lands).
func CatalogLookup ¶ added in v0.68.0
func CatalogLookup(bareName string) *CatalogEntry
CatalogLookup returns the catalog entry for the given bare model name (without ":tag" suffix), or nil when the name isn't in the catalog. Used to annotate ListLocal results.
type Embedder ¶
type Embedder interface {
// Embed returns the embedding vector for the given text. Honours
// ctx; HTTP-backed implementations route ctx through
// http.NewRequestWithContext so cancellation propagates.
Embed(ctx context.Context, text string) ([]float32, error)
// Model returns the model identifier the embedder is configured
// to use. Used in error messages and telemetry.
Model() string
}
Embedder turns text into a fixed-size embedding vector. Implementations are expected to be safe for concurrent Embed calls (the OllamaEmbedder uses an *http.Client which is concurrency-safe).
type LocalModel ¶ added in v0.68.0
type LocalModel struct {
Name string `json:"name"` // e.g. "nomic-embed-text:latest"
Size int64 `json:"size_bytes"` // payload size in bytes
ModifiedAt time.Time `json:"modified_at"` // last-modified timestamp Ollama stamps
Digest string `json:"digest"` // sha256 of the manifest
}
LocalModel is one entry from Ollama's GET /api/tags response.
type OllamaEmbedder ¶
type OllamaEmbedder struct {
// contains filtered or unexported fields
}
OllamaEmbedder calls Ollama's local /api/embed endpoint. Lazy connect — the first Embed call performs the only HTTP round-trip needed to detect Ollama-not-running / model-not-pulled. Server construction does no network I/O.
Wire shape (Ollama 0.1.34+):
POST /api/embed
{"model": "<name>", "input": "..."}
200
{"embeddings": [[float32, ...]], ...}
func NewOllama ¶
func NewOllama(server, model string) *OllamaEmbedder
NewOllama returns an embedder pointed at the given Ollama server. server is the base URL (e.g. "http://localhost:11434"); model is the embedding model name (e.g. "nomic-embed-text"). Empty strings surface as errors on the first Embed call rather than at construction time — callers can pass partial config from MCP startup flags + per-call overrides without preflight checks.
func (*OllamaEmbedder) Embed ¶
Embed POSTs the text to Ollama's /api/embed endpoint and returns the embedding vector. The returned vector is NOT pre-normalised — callers should call Normalize before storing or comparing if they want cosine-similarity semantics.
Errors surface from three places: missing configuration (ErrNoModel / ErrNoServer), transport (the HTTP call itself — server unreachable, timeout, etc.), and protocol (non-200 response, empty embedding array). Each carries a wrapped underlying error so callers can errors.Is past the wrapper.
func (*OllamaEmbedder) ListLocal ¶ added in v0.68.0
func (o *OllamaEmbedder) ListLocal(ctx context.Context) ([]LocalModel, error)
ListLocal returns every model present on the Ollama server (chat AND embedding mixed — Ollama doesn't surface a classification, and we don't try to guess). The caller annotates which ones match the curated catalog via CatalogLookup.
Returns an empty slice (not nil) when Ollama responds with an empty model list. Returns an error wrapped from one of: network failure reaching Ollama, non-200 status, malformed JSON.
func (*OllamaEmbedder) Model ¶
func (o *OllamaEmbedder) Model() string
func (*OllamaEmbedder) Pull ¶ added in v0.68.0
func (o *OllamaEmbedder) Pull(ctx context.Context, name string, onProgress func(PullProgress)) error
Pull downloads the named model via POST /api/pull and streams Ollama's NDJSON progress events to onProgress (which may be nil to discard). Returns nil when Ollama emits the terminating "success" status, or an error from one of:
- missing config (ErrNoServer)
- network failure reaching Ollama
- non-200 HTTP status on the pull request
- an in-stream "error" field on a progress event (Ollama signals mid-pull failures this way, e.g. unknown model)
- ctx cancellation (returns ctx.Err() wrapped)
Cancellation aborts the streaming response read; the partial download is NOT lost because Ollama's pull is layer-based and resumable — a subsequent Pull call against the same name picks up where the cancelled one left off.
Uses a fresh *http.Client with no timeout (separate from the 60s-bounded Embed client) because pulls of large models can take many minutes. ctx is the only deadline.
type PullProgress ¶ added in v0.68.0
type PullProgress struct {
Status string `json:"status"`
Digest string `json:"digest,omitempty"`
Total int64 `json:"total,omitempty"`
Completed int64 `json:"completed,omitempty"`
}
PullProgress is one event from Ollama's POST /api/pull NDJSON stream. Ollama emits multiple events per pull: status lines ("pulling manifest", "downloading sha256:abc..."), layer-progress events with Completed/Total counters, and a terminating "status: success" event. Callers receive every event verbatim; throttling is Ollama's responsibility.