use super::binary::resolve_real_binary;
use super::models::{claude_embed_model, codex_embed_model, opencode_embed_model};
use super::types::EmbeddingFlavour;
use super::LlmEmbedding;
use crate::errors::AppError;
#[derive(Clone, Debug)]
pub struct LlmEmbeddingBuilder {
pub(crate) flavour: EmbeddingFlavour,
pub(crate) binary_override: Option<std::path::PathBuf>,
pub(crate) model_override: Option<String>,
pub(crate) timeout_override: Option<std::time::Duration>,
}
impl LlmEmbeddingBuilder {
pub fn claude_default() -> Self {
Self {
flavour: EmbeddingFlavour::Claude,
binary_override: None,
model_override: None,
timeout_override: None,
}
}
pub fn codex_default() -> Self {
Self {
flavour: EmbeddingFlavour::Codex,
binary_override: None,
model_override: None,
timeout_override: None,
}
}
pub fn opencode_default() -> Self {
Self {
flavour: EmbeddingFlavour::Opencode,
binary_override: None,
model_override: None,
timeout_override: None,
}
}
pub fn override_binary(mut self, binary: std::path::PathBuf) -> Self {
self.binary_override = Some(binary);
self
}
pub fn override_model(mut self, model: String) -> Self {
self.model_override = Some(model);
self
}
pub fn override_timeout(mut self, secs: u64) -> Self {
let clamped = secs.clamp(1, 3_600);
self.timeout_override = Some(std::time::Duration::from_secs(clamped));
self
}
pub fn build(self) -> Result<LlmEmbedding, AppError> {
LlmEmbedding::oauth_only_enforce()?;
let binary = match self.binary_override {
Some(path) => resolve_real_binary(&path),
None => {
let (xdg_bin, which_name) = match self.flavour {
EmbeddingFlavour::Codex => (
crate::runtime_config::codex_binary(),
"codex",
),
EmbeddingFlavour::Claude => (
crate::runtime_config::claude_binary(),
"claude",
),
EmbeddingFlavour::Opencode => (
crate::runtime_config::opencode_binary(),
"opencode",
),
};
let path = xdg_bin
.map(std::path::PathBuf::from)
.or_else(|| which::which(which_name).ok())
.ok_or_else(|| {
AppError::Embedding(
crate::i18n::validation::embedding_binary_not_found_on_path(which_name),
)
})?;
resolve_real_binary(&path)
}
};
let model = match self.model_override {
Some(m) => m,
None => match self.flavour {
EmbeddingFlavour::Codex => codex_embed_model(),
EmbeddingFlavour::Claude => claude_embed_model(),
EmbeddingFlavour::Opencode => opencode_embed_model(),
},
};
Ok(LlmEmbedding::from_parts(
self.flavour,
binary,
model,
self.timeout_override,
))
}
}