| name | arc-dyn-llm |
| description | JOB-251 — Why Arc<dyn LLM> over LLMClone, migration pattern, and correct LLM ownership model. |
The LLMClone supertrait is an anti-pattern. Prefer Arc for shared ownership —
Arc is Clone by definition, no extra trait machinery needed.
## Current Problem (JOB-251)
LLMClone exists solely because dyn LLM is not Clone:
pub trait LLMClone {
fn clone_box(&self) -> Box<dyn LLM>;
}
pub trait LLM: Sync + Send + LLMClone { ... }
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;
let llm: Arc<dyn LLM> = Arc::new(Claude::new());
let llm2 = Arc::clone(&llm);
let stuff_chain = StuffDocumentBuilder::new().llm(Arc::clone(&llm));
let condense_chain = CondenseQuestionGeneratorChain::new(Arc::clone(&llm));
## Current Workaround (until JOB-251 is resolved)
clone_box() still works today. If you must clone an LLM to pass to two builders:
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.
## Migration Checklist (implementing JOB-251)
- Remove
LLMClone supertrait from LLM definition
- 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 with Arc::clone(&llm)
- Update builder
.llm() method signatures to accept impl Into<Arc<dyn LLM>>