Problem
embed_query (crates/rag-rat-core/src/index/ai/helpers.rs) resolves its embedder through active_embedder → embedder_for_spec → FastEmbedEmbedder::for_model_id → TextEmbedding::try_new (crates/rag-rat-llm/src/fastembed.rs) on every call. Nothing holds the embedder between queries, so each query embedding — every semantic_search, and every other path that embeds a query — loads the ONNX model from disk, builds a fresh session, embeds one short string, and drops the session when the query returns.
That costs, per query:
- Latency: model load and session init run before any search work.
- Transient memory: a full model session per in-flight query. Concurrent searches in one server, or several servers on one machine, each hold their own.
- Threads: the query path passes
intra_threads = None, so the runtime sizes its pool to the machine for a single embedding. For the prebuilt, OpenMP-based ONNX Runtime that fastembed downloads, the effective lever is OMP_NUM_THREADS (see omp_threads and docs/config/embedding.md), not intra_threads.
Proposal
- Keep one embedder per process, keyed by what
active_embedder resolves from: the active model id/version and the remote config. Rebuild only when those change (install, switch, remote config edit). FastEmbedEmbedder::embed_batch already serializes on an internal Mutex, so a shared instance is safe across queries.
- Cap the query path's thread pool: a query is one short text, and a machine-sized pool per query buys nothing.
- Keep the remote (Ollama / OpenAI-compatible) path's behavior unchanged;
OpenAiEmbedder construction does not connect and is cheap, but caching it through the same key is harmless.
Verification
- A test that runs two query embeddings against one store and asserts the embedder was constructed once, and that switching the active model rebuilds it.
- Measure per-query latency and peak RSS before and after with the default model.
Problem
embed_query(crates/rag-rat-core/src/index/ai/helpers.rs) resolves its embedder throughactive_embedder→embedder_for_spec→FastEmbedEmbedder::for_model_id→TextEmbedding::try_new(crates/rag-rat-llm/src/fastembed.rs) on every call. Nothing holds the embedder between queries, so each query embedding — everysemantic_search, and every other path that embeds a query — loads the ONNX model from disk, builds a fresh session, embeds one short string, and drops the session when the query returns.That costs, per query:
intra_threads = None, so the runtime sizes its pool to the machine for a single embedding. For the prebuilt, OpenMP-based ONNX Runtime that fastembed downloads, the effective lever isOMP_NUM_THREADS(seeomp_threadsanddocs/config/embedding.md), notintra_threads.Proposal
active_embedderresolves from: the active model id/version and the remote config. Rebuild only when those change (install, switch, remote config edit).FastEmbedEmbedder::embed_batchalready serializes on an internalMutex, so a shared instance is safe across queries.OpenAiEmbedderconstruction does not connect and is cheap, but caching it through the same key is harmless.Verification