use super::*;
use crate::errors::AppError;
use std::path::Path;
pub fn should_skip_embedding_on_failure() -> bool {
crate::runtime_config::skip_embedding_on_failure()
}
pub fn embed_passage_or_skip(
models_dir: &Path,
text: &str,
choice: Option<crate::cli::LlmBackendChoice>,
) -> Result<Option<Vec<f32>>, AppError> {
match embed_passage_with_choice(models_dir, text, choice) {
Ok((v, _backend)) => Ok(Some(v)),
Err(AppError::Validation(msg)) => Err(AppError::Validation(msg)),
Err(e) => {
if should_skip_embedding_on_failure() {
tracing::warn!(
error = %e,
"embedding failed but --skip-embedding-on-failure is active; persisting with NULL embedding"
);
Ok(None)
} else {
Err(e)
}
}
}
}
pub fn embed_passage_with_choice(
models_dir: &Path,
text: &str,
choice: Option<crate::cli::LlmBackendChoice>,
) -> Result<(Vec<f32>, LlmBackendKind), AppError> {
let _slot_guard = acquire_llm_slot_for_embedding()?;
let chain = choice
.unwrap_or(crate::cli::LlmBackendChoice::OpenRouter)
.to_chain();
embed_with_fallback(models_dir, text, &chain, false)
}
pub fn embed_passage_with_embedding_choice(
models_dir: &Path,
text: &str,
backends: crate::cli::BackendChoice,
) -> Result<(Vec<f32>, LlmBackendKind), AppError> {
let crate::cli::BackendChoice {
llm: llm_backend,
embedding: embedding_backend,
} = backends;
let _slot_guard = acquire_llm_slot_for_embedding()?;
let chain = embedding_backend.to_chain(llm_backend);
embed_with_fallback(models_dir, text, &chain, false)
}
pub fn try_embed_query_with_choice(
models_dir: &Path,
text: &str,
choice: Option<crate::cli::LlmBackendChoice>,
) -> Result<(Vec<f32>, LlmBackendKind), FallbackReason> {
match embed_passage_with_choice(models_dir, text, choice) {
Ok((v, _backend)) if v.is_empty() => Err(FallbackReason::DimZero),
Ok((v, backend)) => Ok((v, backend)),
Err(e) => Err(classify_embedding_error(e)),
}
}
pub fn try_embed_query_with_embedding_choice(
models_dir: &Path,
text: &str,
backends: crate::cli::BackendChoice,
) -> Result<(Vec<f32>, LlmBackendKind), FallbackReason> {
match embed_passage_with_embedding_choice(models_dir, text, backends) {
Ok((v, _backend)) if v.is_empty() => Err(FallbackReason::DimZero),
Ok((v, backend)) => Ok((v, backend)),
Err(e) => Err(classify_embedding_error(e)),
}
}
pub(crate) fn acquire_llm_slot_for_embedding() -> Result<crate::llm_slots::LlmSlotGuard, AppError> {
use crate::constants::{CLI_LOCK_DEFAULT_WAIT_SECS, LLM_WORKER_RSS_MB};
let default_max = crate::llm_slots::default_max_concurrency() as usize;
let max = crate::runtime_config::llm_max_host_concurrency(default_max).max(1) as u32;
let wait_secs = if crate::runtime_config::llm_slot_no_wait() {
0
} else {
crate::runtime_config::llm_slot_wait_secs(CLI_LOCK_DEFAULT_WAIT_SECS)
};
let _ = LLM_WORKER_RSS_MB; match crate::llm_slots::acquire_llm_slot(max, wait_secs) {
Ok(guard) => Ok(guard),
Err(e @ AppError::LockBusy { .. }) if wait_secs > 0 => Err(AppError::Embedding(
crate::i18n::validation::embedding_slot_exhausted(&e),
)),
Err(e) => Err(e),
}
}