Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/commands/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ pub fn run_watch(
verbose: bool,
) -> Result<()> {
let conn = crate::index::open_global_index()?;
// Load skip patterns from config
let skip_patterns: Option<Vec<String>> =
crate::config::loader::load_config(config_path, None, None, None, None, false)
.ok()
.map(|c| c.rules_config.index_skip_files);
// Load skip patterns + brain embedding backend from config
let config =
crate::config::loader::load_config(config_path, None, None, None, None, false).ok();
let skip_patterns: Option<Vec<String>> = config
.as_ref()
.map(|c| c.rules_config.index_skip_files.clone());

// Resolve embedding backend
let brain_mode = config
.as_ref()
.map(|c| c.brain.embedding.to_string())
.unwrap_or_else(|| "auto".to_string());
crate::embed::resolve_backend(&brain_mode);

let skip_ref: Option<&[String]> = skip_patterns.as_deref();

Expand Down
60 changes: 60 additions & 0 deletions src/config/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub struct Config {
/// Analysis configuration — dead-code detection, entry-point patterns.
#[serde(default, skip_serializing_if = "is_default")]
pub analysis: AnalysisConfig,
/// Brain Mode configuration — embedding backend selection.
#[serde(default, skip_serializing_if = "is_default")]
pub brain: BrainConfig,
}

/// Provider configuration.
Expand Down Expand Up @@ -141,6 +144,7 @@ impl Default for Config {
debt: crate::engine::debt_tracker::DebtConfig::default(),
profile: None,
analysis: AnalysisConfig::default(),
brain: BrainConfig::default(),
}
}
}
Expand Down Expand Up @@ -414,6 +418,62 @@ pub struct AnalysisConfig {
pub entry_point_patterns: Vec<String>,
}

/// Brain Mode configuration — controls embedding backend for vector search.
///
/// By default (`auto`), cora selects the best available backend at runtime:
/// pretrained 768d (if compiled with `pretrained-embed` feature) → hashing 256d fallback.
/// Users can force a specific backend via `.cora.yaml`.
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct BrainConfig {
/// Embedding backend selection.
///
/// - `"auto"` (default) — best available: pretrained → hashing
/// - `"hashing"` — force 256d hashing trick (zero dependency)
/// - `"pretrained"` — force nomic 768d (requires `--features pretrained-embed`)
///
/// Invalid values fall back to `"auto"` with a warning.
#[serde(default, skip_serializing_if = "is_default")]
pub embedding: BrainEmbeddingMode,
}

/// Embedding backend mode for Brain Mode.
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum BrainEmbeddingMode {
/// Best available backend (pretrained if compiled, else hashing).
#[default]
Auto,
/// Force 256d hashing trick (zero dependency).
Hashing,
/// Force nomic 768d pretrained (requires feature flag).
Pretrained,
}

impl std::fmt::Display for BrainEmbeddingMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Auto => write!(f, "auto"),
Self::Hashing => write!(f, "hashing"),
Self::Pretrained => write!(f, "pretrained"),
}
}
}

impl std::str::FromStr for BrainEmbeddingMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"auto" => Ok(Self::Auto),
"hashing" => Ok(Self::Hashing),
"pretrained" => Ok(Self::Pretrained),
other => Err(format!(
"unknown brain.embedding value '{other}' — expected auto, hashing, or pretrained"
)),
}
}
}

fn is_default<T: Default + PartialEq>(val: &T) -> bool {
*val == T::default()
}
Expand Down
185 changes: 143 additions & 42 deletions src/embed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
//! compiled into the binary via `include_bytes!` / `include_str!`.
//! Higher quality at the cost of ~30 MB binary size.
//!
//! The [`embed_code_dispatch`] function selects the best available backend at
//! compile time: pretrained-embed (768d) → hashing trick (256d) → FTS5-only.
//! The active backend is selected at **runtime** via [`resolve_backend`],
//! which reads the `brain.embedding` config value. At compile time, only
//! the availability of the pretrained path is gated by the `pretrained-embed`
//! feature flag.

pub mod tokens;

Expand All @@ -27,63 +29,162 @@ pub use tokens::EMBEDDING_DIM;
#[cfg(feature = "pretrained-embed")]
pub use token_vocab::{PRETRAINED_DIM, embed_code_pretrained};

/// Returns the embedding dimensionality used by the active backend.
/// Runtime embedding backend selector.
///
/// - `pretrained-embed` feature → 768
/// - default (hashing trick) → 256
pub const fn active_dims() -> usize {
#[cfg(feature = "pretrained-embed")]
{
PRETRAINED_DIM
/// Resolved from `brain.embedding` config value at call time.
/// Falls back gracefully when a requested backend is not compiled.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Backend {
/// 256-dim hashing trick (always available, zero dependency).
Hashing,
/// 768-dim nomic distilled pretrained (requires `pretrained-embed` feature).
#[allow(dead_code)]
Pretrained,
}

impl Backend {
/// Returns the embedding dimensionality for this backend.
pub const fn dims(self) -> usize {
match self {
Self::Hashing => EMBEDDING_DIM,
#[cfg(feature = "pretrained-embed")]
Self::Pretrained => PRETRAINED_DIM,
#[cfg(not(feature = "pretrained-embed"))]
Self::Pretrained => EMBEDDING_DIM, // unreachable — resolve never returns Pretrained without feature
}
}
#[cfg(not(feature = "pretrained-embed"))]
{
EMBEDDING_DIM

/// Returns a human-readable label for this backend.
pub const fn provider_name(self) -> &'static str {
match self {
Self::Hashing => "hashing-trick (256d, static)",
#[cfg(feature = "pretrained-embed")]
Self::Pretrained => "nomic-embed-code (768d, pretrained)",
#[cfg(not(feature = "pretrained-embed"))]
Self::Pretrained => "hashing-trick (256d, pretrained not compiled)",
}
}
}

/// Returns a human-readable label for the active embedding provider.
pub const fn active_provider_name() -> &'static str {
#[cfg(feature = "pretrained-embed")]
{
"nomic-embed-code (768d, pretrained)"
}
#[cfg(not(feature = "pretrained-embed"))]
{
"hashing-trick (256d, static)"
/// Thread-local active backend — set once at index/brain-search time.
static ACTIVE_BACKEND: std::sync::OnceLock<Backend> = std::sync::OnceLock::new();

/// Resolve the runtime backend from config string.
///
/// Logic:
/// - `"auto"` → pretrained if compiled, else hashing
/// - `"hashing"` → always hashing
/// - `"pretrained"` → pretrained if compiled, else hashing + warning
///
/// The result is cached process-wide via `OnceLock`.
pub fn resolve_backend(config_embedding: &str) -> Backend {
// Already resolved? Return cached value.
if let Some(&b) = ACTIVE_BACKEND.get() {
return b;
}

let backend = match config_embedding {
"hashing" => Backend::Hashing,
"pretrained" => {
#[cfg(feature = "pretrained-embed")]
{
Backend::Pretrained
}
#[cfg(not(feature = "pretrained-embed"))]
{
tracing::warn!(
"brain.embedding=pretrained but cora was not compiled with --features pretrained-embed; \
falling back to hashing-trick 256d"
);
Backend::Hashing
}
}
// "auto" or any unknown value
_ => {
#[cfg(feature = "pretrained-embed")]
{
Backend::Pretrained
}
#[cfg(not(feature = "pretrained-embed"))]
{
Backend::Hashing
}
}
};

let _ = ACTIVE_BACKEND.set(backend);
tracing::debug!(
config = config_embedding,
backend = ?backend,
"resolved embedding backend"
);
backend
}

/// Returns the embedding dimensionality used by the active backend.
///
/// Convenience wrapper around `resolve_backend().dims()`.
pub fn active_dims() -> usize {
// Use a sensible default if resolve_backend hasn't been called yet.
ACTIVE_BACKEND.get().map(|b| b.dims()).unwrap_or_else(|| {
#[cfg(feature = "pretrained-embed")]
{
PRETRAINED_DIM
}
#[cfg(not(feature = "pretrained-embed"))]
{
EMBEDDING_DIM
}
})
}

/// Returns a human-readable label for the active embedding provider.
pub fn active_provider_name() -> &'static str {
ACTIVE_BACKEND
.get()
.map(|b| b.provider_name())
.unwrap_or_else(|| {
#[cfg(feature = "pretrained-embed")]
{
"nomic-embed-code (768d, pretrained)"
}
#[cfg(not(feature = "pretrained-embed"))]
{
"hashing-trick (256d, static)"
}
})
}

/// Embed a code snippet using the best available backend.
///
/// Returns an f32 vector that can be passed directly to usearch.
///
/// - **Pretrained path** (`pretrained-embed` feature): tokenises → looks up
/// each token in the nomic vocabulary → accumulates int8 vectors → L2-normalises.
/// Returns 768-dim vector.
///
/// - **Hashing-trick fallback**: tokenises → hashes each token into a
/// pseudo-random 256-dim vector → accumulates → L2-normalises.
/// Returns 256-dim vector.
///
/// Both paths share the same [`tokenize_code`] tokenizer.
/// Dispatches to the backend set by [`resolve_backend`]. If no backend has
/// been explicitly resolved, falls back to compile-time default.
pub fn embed_code_dispatch(code: &str) -> Vec<f32> {
#[cfg(feature = "pretrained-embed")]
{
embed_code_pretrained(code)
}
#[cfg(not(feature = "pretrained-embed"))]
{
let embedding = tokens::embed_code(code);
embedding.as_slice().iter().map(|&v| v as f32).collect()
let backend = ACTIVE_BACKEND.get().copied().unwrap_or_else(|| {
// Lazy resolve with "auto" if not yet set
resolve_backend("auto")
});

match backend {
Backend::Hashing => {
let embedding = tokens::embed_code(code);
embedding.as_slice().iter().map(|&v| v as f32).collect()
}
#[cfg(feature = "pretrained-embed")]
Backend::Pretrained => embed_code_pretrained(code),
#[cfg(not(feature = "pretrained-embed"))]
Backend::Pretrained => {
// Should never happen — resolve_backend never returns Pretrained without feature
let embedding = tokens::embed_code(code);
embedding.as_slice().iter().map(|&v| v as f32).collect()
}
}
}

/// Whether the pretrained embedding backend is available (compile-time).
#[expect(
dead_code,
reason = "used by Phase 3+ features; embed module not yet wired at call sites"
)]
#[allow(dead_code)]
pub const fn has_pretrained() -> bool {
cfg!(feature = "pretrained-embed")
}
Loading
Loading