perf: feature-gate smart deps, drop ONNX/qdrant from backend binary

wiring `smart` feature gates fastembed+qdrant; bootstrap no longer
links ONNX Runtime (~300 MiB RSS savings). prod compose stripped
to backend-only (no worker/qdrant).
This commit is contained in:
2026-08-08 15:23:39 +02:00
parent 425376874c
commit c7e1a867ed
5 changed files with 39 additions and 70 deletions

View File

@@ -3,6 +3,10 @@ name = "wiring"
version = "0.1.0"
edition = "2024"
[features]
default = []
smart = ["dep:fastembed-adapter", "dep:qdrant-adapter"]
[dependencies]
# domain + application
domain = { workspace = true }
@@ -13,8 +17,8 @@ sqlite = { workspace = true }
auth = { workspace = true }
event-publisher-memory = { workspace = true }
nats = { workspace = true }
fastembed-adapter = { workspace = true }
qdrant-adapter = { workspace = true }
fastembed-adapter = { workspace = true, optional = true }
qdrant-adapter = { workspace = true, optional = true }
# utilities
tokio = { workspace = true }

View File

@@ -12,7 +12,9 @@ use domain::{
type OptEmbedding = Option<Arc<dyn EmbeddingGenerator>>;
type OptVectorStore = Option<Arc<dyn VectorStore>>;
use event_publisher_memory::MemoryEventBus;
#[cfg(feature = "smart")]
use fastembed_adapter::{FastEmbedConfig, FastEmbedGenerator};
#[cfg(feature = "smart")]
use qdrant_adapter::{QdrantConfig, QdrantVectorStore};
use sqlite::{
db::{connect, run_migrations},
@@ -61,34 +63,38 @@ pub async fn build_context(cfg: &WiringConfig) -> anyhow::Result<AppContext> {
(bus.publisher(), bus.consumer())
};
// ── Smart features ────────────────────────────────────────────────────────
// EmbeddingGenerator: only load the fastembed model in the worker.
// The backend only needs VectorStore (for querying related notes).
// Loading the model in both processes wastes ~150 MB per process.
let embedding: OptEmbedding = if cfg.enable_embeddings && cfg.qdrant_url.is_some() {
tracing::info!("loading fastembed embedding model");
let embedder = FastEmbedGenerator::new(FastEmbedConfig::default())
.map_err(|e| anyhow::anyhow!("fastembed init failed: {e}"))?;
Some(Arc::new(embedder) as Arc<dyn EmbeddingGenerator>)
} else {
None
// ── Smart features (behind "smart" feature flag) ────────────────────────
#[cfg(feature = "smart")]
let (embedding, vector_store): (OptEmbedding, OptVectorStore) = {
let emb: OptEmbedding = if cfg.enable_embeddings && cfg.qdrant_url.is_some() {
tracing::info!("loading fastembed embedding model");
let embedder = FastEmbedGenerator::new(FastEmbedConfig::default())
.map_err(|e| anyhow::anyhow!("fastembed init failed: {e}"))?;
Some(Arc::new(embedder) as Arc<dyn EmbeddingGenerator>)
} else {
None
};
let vs: OptVectorStore = if let Some(ref url) = cfg.qdrant_url {
tracing::info!("connecting to qdrant at {url}");
let qdrant = QdrantVectorStore::new(QdrantConfig {
url: url.clone(),
collection: cfg.qdrant_collection.clone(),
vector_size: cfg.qdrant_vector_size,
})
.map_err(|e| anyhow::anyhow!("qdrant client init failed: {e}"))?;
qdrant.init(cfg.qdrant_vector_size).await?;
tracing::info!(collection = %cfg.qdrant_collection, "qdrant collection ready");
Some(Arc::new(qdrant) as Arc<dyn VectorStore>)
} else {
None
};
(emb, vs)
};
let vector_store: OptVectorStore = if let Some(ref url) = cfg.qdrant_url {
tracing::info!("connecting to qdrant at {url}");
let qdrant = QdrantVectorStore::new(QdrantConfig {
url: url.clone(),
collection: cfg.qdrant_collection.clone(),
vector_size: cfg.qdrant_vector_size,
})
.map_err(|e| anyhow::anyhow!("qdrant client init failed: {e}"))?;
qdrant.init(cfg.qdrant_vector_size).await?;
tracing::info!(collection = %cfg.qdrant_collection, "qdrant collection ready");
Some(Arc::new(qdrant) as Arc<dyn VectorStore>)
} else {
tracing::info!("no QDRANT_URL — smart features disabled");
None
};
#[cfg(not(feature = "smart"))]
let (embedding, vector_store): (OptEmbedding, OptVectorStore) = (None, None);
Ok(AppContext {
repos,

View File

@@ -10,7 +10,7 @@ path = "src/main.rs"
[dependencies]
domain = { workspace = true }
application = { workspace = true }
wiring = { workspace = true }
wiring = { workspace = true, features = ["smart"] }
async-trait = { workspace = true }
dotenvy = "0.15"
tokio = { workspace = true }