LLMClone exists solely because dyn LLM is not Clone:
// CURRENT — anti-pattern
pub trait LLMClone {
fn clone_box(&self) -> Box<dyn LLM>;
}
pub trait LLM: Sync + Send + LLMClone { ... }
// Used only in two places:
let stuff_chain = StuffDocumentBuilder::new().llm(llm.clone_box());
let condense_chain = CondenseQuestionGeneratorChain::new(llm.clone_box());
clone_box() allocates a new Box on every call. Every backend must also derive(Clone).
Preferred Pattern: Arc
use std::sync::Arc;
use langchainx::language_models::llm::LLM;
// Cheap clone — just an atomic ref count increment
let llm: Arc<dyn LLM> = Arc::new(Claude::new());
let llm2 = Arc::clone(&llm); // no Box allocation, no clone_box()
// Both chains share the same LLM instance
let stuff_chain = StuffDocumentBuilder::new().llm(Arc::clone(&llm));
let condense_chain = CondenseQuestionGeneratorChain::new(Arc::clone(&llm));
clone_box() still works today. If you must clone an LLM to pass to two builders:
// Acceptable today — will be removed when JOB-251 is implemented
let llm: Box<dyn LLM> = Box::new(OpenAI::default());
let stuff = StuffDocumentBuilder::new().llm(llm.clone_box()).build()?;
let condense = CondenseQuestionGeneratorChain::new(llm.clone_box());
Do NOT add new code that introduces new clone_box() call sites. Prefer Arc.
- Remove
LLMClonesupertrait fromLLMdefinition - Remove blanket
impl<T: LLM + Clone> LLMClone for T - Change
Box<dyn LLM>fields →Arc<dyn LLM>in all chain structs - Change
From<L> for Box<dyn LLM>→From<L> for Arc<dyn LLM> - Replace all
llm.clone_box()call sites withArc::clone(&llm) - Update builder
.llm()method signatures to acceptimpl Into<Arc<dyn LLM>>