Embedding providers
Local ONNX presets or BYO remote embedding (OpenAI, Voyage, Ollama, TEI): switch providers, Matryoshka dims, cost and privacy tradeoffs, and the dim-mismatch reset flow.
Overview#
Memtrace embeds every indexed symbol so semantic search (find_code) can match by meaning, not just keywords. You have three choices for where that work happens:
- Local (default) — ONNX on your machine. Free, private, fast once warm. Uses RAM.
- Remote, hosted API — HTTP POST to OpenAI, Voyage, or any OpenAI-compatible embeddings endpoint. Costs credits; zero local compute.
- Remote, self-hosted — Same wire format pointed at Ollama, Infinity, TEI, vLLM, or LM Studio on your LAN. Free and private if you already run inference infra.
All three drive the same indexing and search pipelines. Switch with memtrace embed set — a dim change triggers a guarded MemDB reset rather than silently corrupting the HNSW index.
Config precedence: workspace .memtrace/embed.toml > ~/.memtrace/config.toml > env vars > built-in default (local jina-code, 768d). See memtrace embed for the full command reference.
Quick decision guide#
| If you… | Use |
|---|---|
| Have a Mac with ≥ 16 GB RAM and want it to just work | jina-code local (default) |
| Low RAM, Windows, or locked-down corp laptop | Remote — Voyage or OpenAI |
| Already run Ollama / Infinity / TEI on a workstation | Remote — point at your own server |
| Faster first-run, okay with lower retrieval quality | bge-small local (384d) |
| Top retrieval quality and have API credits | voyage-code-3 remote (1024d) |
| CI with no GPU | Remote — any provider with an API key |
Local presets#
Four local presets are built in. memtrace embed set <preset> always sets provider=local and clears any remote configuration.
| Preset | Dimensions | Notes |
|---|---|---|
jina-code | 768d | Default. Code-tuned embeddings — the model every fresh install starts on. |
bge-small | 384d | Smaller and faster; a good choice on memory-constrained hosts. |
bge-base | 768d | General-purpose alternative at the same dimension as the default. |
nomic | 768d | General-purpose alternative at the same dimension as the default. |
The first embedding run downloads the active model into ~/.memtrace/fastembed_cache and prints an explicit "First run — downloading embedding model" notice so the wait isn't mistaken for a hang. Local presets never leave the machine and never depend on your Memtrace session — an auth failure can never stop local embedding.
$ memtrace embed list # See all four presets and which config is currently active. $ memtrace embed set bge-small # Switch to the smaller/faster 384d local preset. # Prompts [r]eset/[c]ancel if your MemDB was built at a different dim.
Remote embedding#
memtrace embed set --remote openai-compat is the only remote kind. It speaks the standard POST /embeddings OpenAI-compatible spec — same request and response shape across hosted and self-hosted providers.
# Hosted: OpenAI export OPENAI_API_KEY='sk-...' # in ~/.zshrc — never written to config memtrace embed set --remote openai-compat \ --url https://api.openai.com/v1 \ --model text-embedding-3-small \ --api-key-env OPENAI_API_KEY # Hosted: Voyage (code-tuned) export VOYAGE_API_KEY='vk-...' memtrace embed set --remote openai-compat \ --url https://api.voyageai.com/v1 \ --model voyage-code-3 \ --api-key-env VOYAGE_API_KEY # Self-hosted: Ollama (no auth) memtrace embed set --remote openai-compat \ --url http://localhost:11434/v1 \ --model nomic-embed-text
Only the name of the API-key env var is stored in TOML — the secret is read at request time and never persisted. Remote auth is entirely separate from your Memtrace session token.
| Flag | Default | Purpose |
|---|---|---|
| --timeout-ms | 30000 | Per-request HTTP timeout |
| --max-batch | 64 | Max texts per request (provider limits still apply) |
| --dim | (probe) | Pin dim and request Matryoshka truncation |
| --api-key-env | (none) | Env var for Bearer Authorization; omit for no-auth servers |
Without --dim, embed set probes <url>/embeddings with a one-token request and records the returned vector length. Verify anytime with memtrace embed test and memtrace embed status — check memdb_dim matches dim.
Provider reference
| Provider | Base URL | Code-tuned model |
|---|---|---|
| OpenAI | https://api.openai.com/v1 | text-embedding-3-small (1536d), text-embedding-3-large (3072d) |
| Voyage AI | https://api.voyageai.com/v1 | voyage-code-3 (1024d), voyage-code-2 (1536d) |
| Ollama | http://localhost:11434/v1 | nomic-embed-text (768d), mxbai-embed-large (1024d) |
| LM Studio | http://localhost:1234/v1 | whichever embedding model is loaded |
| Infinity | http://localhost:7997/v1 | server-side configuration |
| TEI | http://localhost:8080/v1 | server-side configuration |
| vLLM (--task embed) | http://localhost:8000/v1 | served model |
Matryoshka truncation
OpenAI text-embedding-3-* and Voyage voyage-code-3 / voyage-4-* support smaller output dims — trade a small recall hit for less HNSW memory and faster search. Pass --dim N on embed set:
memtrace embed set --remote openai-compat \ --url https://api.openai.com/v1 \ --model text-embedding-3-small \ --api-key-env OPENAI_API_KEY --dim 512 memtrace embed set --remote openai-compat \ --url https://api.voyageai.com/v1 \ --model voyage-code-3 \ --api-key-env VOYAGE_API_KEY --dim 256
| Model | Sweet-spot dim |
|---|---|
| text-embedding-3-small | 768 or 1024 (native 1536) |
| text-embedding-3-large | 1024 (native up to 3072) |
| voyage-code-3 | 1024 default; also 256, 512, 2048 |
Indexing vs query embedding
| Phase | Local | Remote |
|---|---|---|
| Indexing | ONNX on CPU / NPU | HTTP POST per batch of symbol bodies |
| Query (every find_code) | Same ONNX model | One HTTP POST per query string |
| Vector search | In-process HNSW | In-process HNSW — provider never sees search |
High-frequency agents send one small POST per semantic query. Plan for provider rate limits and latency (~200–500 ms US-hosted from Europe). Switching providers updates both indexing and queries automatically — no separate query config.
Cost and latency
Reference: indexing ~1,500 embeddable symbols (MemFleet-scale repo).
| Provider | Warm index time | Cost | Local RAM |
|---|---|---|---|
| Local jina-code (768d) | ~3 min | $0 | ~500 MB |
| Local bge-small (384d) | ~50 s | $0 | ~250 MB |
| Voyage voyage-code-3 | ~80 s | ~$0.04 | minimal |
| OpenAI text-embedding-3-small | network-dependent | ~$0.02 | minimal |
| Self-hosted Ollama | hardware-dependent | $0 | minimal (work on server) |
Per-query find_code latency (single query, warm): local jina-code 10–50 ms; Voyage US from Europe 300–500 ms; LAN Ollama 5–30 ms.
Privacy
- Local — symbol bodies never leave your machine.
- Remote hosted — Memtrace sends code-symbol bodies (up to ~1,500 chars each) and query strings over HTTPS. The provider's retention policy applies — read it before using on confidential codebases.
- Self-hosted remote — traffic stays on your hardware; useful on air-gapped workstations that still want HTTP-based model swapping.
Failure modes
| Breaker reason | Cause | Fix |
|---|---|---|
| AuthFailed | HTTP 401/403 | Check --api-key-env resolves to a valid key |
| RateLimited | 3× HTTP 429 | Wait for Retry-After; reduce agent query frequency |
| NetworkUnavailable | Timeout / connection refused / 5xx | Verify URL; check self-hosted server |
| DimMismatch | Response dim ≠ configured dim | Drop --dim and let probe re-discover native dim |
After a trip: memtrace embed reset-breaker or restart the daemon. See Troubleshooting for the embed circuit breaker section.
Changing dimensions#
A MemDB HNSW index cannot mix vector dimensions. Every time you switch models or providers, Memtrace compares the new config's dimension against the live MemDB's dimension. If they differ, it will not silently corrupt the index — it walks you through a guarded reset-and-reindex flow instead.
- Interactive (TTY): a prompt offers
[r] Reset + reindex nowor[c] Cancel (config NOT written). - Non-interactive (CI, pipelines): the command errors with "dim mismatch detected and stdin is not a TTY" and exits 2 — pass
--yes-resetor--no-resetexplicitly. - No MemDB dim known yet: nothing to reconcile — the config is written directly.
Accepting the reset (r or --yes-reset) prints a loud red stderr banner naming the exact directory (! ABOUT TO WIPE MEMDB DATA DIR: <path>), then stops OS supervisors, kills the daemon, and wipes that local MemDB data dir — the exact dir that produced the measured dim, never a re-resolved path from env or workspace anchoring.
The reset wipes the MemDB data dir but does not automatically reindex, and it runs before the new config is written. Re-indexing happens on the next memtrace start or memtrace index . — a save failure right after a reset can leave you with a wiped MemDB and the old config still in place.
$ memtrace embed set bge-small --yes-reset # Scripts/CI: skips the prompt, wipes the MemDB data dir # (loud stderr warning shows the exact path), and writes the config. # Re-run `memtrace start` or `memtrace index .` afterward to reindex.
Hardware acceleration#
Embeddings run through ONNX Runtime. On CPU-only hosts this just works; on GPU-capable hosts Memtrace can accelerate the same models — but every accelerator is opt-in, not default behavior.
Apple Silicon: ANE / CoreML
On macOS, embeddings default to CPU-only even on Apple Silicon. The native CoreML execution provider (which can route work to the Apple Neural Engine) is explicit opt-in via MEMTRACE_ENABLE_COREML=1 — it is not part of the default first-run cost. When enabled, the first inference pays a CoreML graph compile that can take 60–300 seconds cold; run memtrace warmup ahead of time so memtrace start doesn't trip the embed-batch circuit breaker on that spike.
If you enable CoreML and route embeddings to the ANE, don't expect to see it in a GPU usage meter — Activity Monitor's GPU history does not reflect ANE work. Indexing itself is CPU-bound regardless; only the embedding step can move to the ANE.
Windows NVIDIA: CUDA / DirectML
On Windows, the engine probes execution providers in order: CUDA → DirectML → CPU. The CUDA execution provider is an optional ~400 MB DLL fetched from a GitHub release asset (not bundled in the npm package) — the installer downloads it automatically when it detects an NVIDIA GPU. Without it, NVIDIA hosts silently run embeddings on DirectML instead of falling all the way back to CPU.
$ memtrace gpu status # Shows whether the CUDA DLL is installed, whether an NVIDIA GPU is # detected, and the provider probe order. $ memtrace gpu install-cuda # Fetch + sha256-verify the CUDA provider DLL manually (offline installs, # GPUs added after install, or detection misses). --force re-downloads.
memtrace gpu is a no-op on macOS/Linux — CUDA only applies to Windows. The CUDA provider is an accelerator, never a requirement; the engine always has a working fallback path.
Performance env knobs#
These override the model/dimension resolved from config files for the current process — useful for scripting, one-off runs, or diagnosing a specific host without touching the persistent TOML config.
| Variable | Default | Description |
|---|---|---|
MEMTRACE_EMBED_MODEL | (unset) | Transient override for the embed model. Sits below workspace and home config files in precedence, above the built-in default — once a config file exists, this no longer affects resolution. Aliases are canonicalized (e.g. jinaai/jina-embeddings-v2-base-code → jina-code). |
MEMTRACE_VECTOR_DIMS | (unset; effective default 768) | Transient override for the vector dimensionality, paired with MEMTRACE_EMBED_MODEL. Also feeds the "live MemDB dim" that embed set/embed status compare against. |
MEMTRACE_EMBED_CACHE_CAP_MB | 2048 | Cap in MB for the on-disk embedding cache at ~/.memtrace/embed-cache/, reported by memtrace embed cache-status. 0 disables the cap. |
MEMTRACE_ENABLE_COREML | (unset — disabled) | Opt in to the CoreML execution provider on Apple Silicon. Pairs with memtrace warmup to pre-pay the graph compile. |
MEMTRACE_DISABLE_COREML | (unset) | Hard override that forces CoreML off even if MEMTRACE_ENABLE_COREML is set — a workaround if a CoreML compile crashes under memory pressure. |
MEMTRACE_ORT_DYLIB_PATH | (unset) | Point at a manually installed ONNX Runtime dylib if the bundled one fails the startup pre-flight probe. |
FASTEMBED_CACHE_DIR | ~/.memtrace/fastembed_cache | Overrides where downloaded ONNX model files are cached. |
MEMTRACE_SKIP_EMBED | (unset) | Disables embedding entirely for a structural-only graph (semantic search off, everything else works) — no model download, no ONNX dependency at all. |
memtrace warmup [--model <name>] loads a model and runs one dummy embedding to force the download, ONNX session init, and (if enabled) the CoreML graph compile — so the next real memtrace start doesn't pay that cost on the critical path. It never starts the daemon or touches the graph.
See also memtrace embed, Performance tuning, memtrace warmup, and Data directories for cache locations.