Skip to main content

sqlite_graphrag/
backend_choice.rs

1//! LLM / embedding backend CLI choices (Wave C1).
2
3/// LLM backend for embedding. Accepts `openrouter` (OpenRouter REST) or
4/// `none` (skips embedding; useful for tests).
5#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
6pub enum LlmBackendChoice {
7    /// Open router variant.
8    OpenRouter,
9    /// None variant.
10    None,
11}
12
13/// v1.0.93: embedding backend selector. Separate from `--llm-backend` which
14/// controls enrichment (entity extraction, body enrichment).
15/// `auto` uses OpenRouter when a client is initialised.
16/// `openrouter` requires API key (exit 78 if absent).
17#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
18pub enum EmbeddingBackendChoice {
19    /// Auto variant.
20    Auto,
21    /// Openrouter variant.
22    Openrouter,
23}
24
25/// The `llm` + `embedding` pair, named once.
26///
27/// GAP-SG-265: these two selectors are resolved together in `main`, travel
28/// together down every write path, and are consumed together by
29/// `embed_passage_with_embedding_choice`. Passing them as two positional
30/// parameters cost one argument slot in each of the signatures that carry them,
31/// and several of those signatures sat one slot over the
32/// `clippy::too_many_arguments` threshold for exactly that reason.
33///
34/// This is a plain aggregate on purpose: the two fields keep their own types,
35/// so nothing that reads them changes, and the struct is `Copy` so threading it
36/// through a call chain stays as cheap as threading the two enums was.
37#[derive(Copy, Clone, Debug, PartialEq, Eq)]
38pub struct BackendChoice {
39    /// Which LLM backend answers enrichment calls.
40    pub llm: LlmBackendChoice,
41    /// Which backend computes embeddings.
42    pub embedding: EmbeddingBackendChoice,
43}
44
45impl BackendChoice {
46    /// Builds the pair from the two CLI selectors.
47    pub fn new(llm: LlmBackendChoice, embedding: EmbeddingBackendChoice) -> Self {
48        Self { llm, embedding }
49    }
50}
51
52impl EmbeddingBackendChoice {
53    /// v1.0.93: produces a fallback chain that prepends OpenRouter when
54    /// the client is initialised.
55    pub fn to_chain(self, llm_choice: LlmBackendChoice) -> Vec<crate::embedder::LlmBackendKind> {
56        use crate::embedder::LlmBackendKind;
57        match self {
58            EmbeddingBackendChoice::Openrouter => vec![LlmBackendKind::OpenRouter],
59            EmbeddingBackendChoice::Auto => {
60                if crate::embedder::is_openrouter_initialized() {
61                    let mut chain = vec![LlmBackendKind::OpenRouter];
62                    chain.extend(llm_choice.to_chain());
63                    chain
64                } else {
65                    llm_choice.to_chain()
66                }
67            }
68        }
69    }
70}
71
72impl LlmBackendChoice {
73    /// Converts the CLI choice into an ordered chain of backends that
74    /// `embedder::embed_with_fallback` iterates. The first element of the
75    /// chain is the preferred backend; subsequent elements are fallbacks
76    /// used when the preferred one fails with `LlmBackendError`.
77    pub fn to_chain(self) -> Vec<crate::embedder::LlmBackendKind> {
78        use crate::embedder::LlmBackendKind;
79        match self {
80            LlmBackendChoice::OpenRouter => {
81                vec![LlmBackendKind::OpenRouter, LlmBackendKind::None]
82            }
83            LlmBackendChoice::None => vec![LlmBackendKind::None],
84        }
85    }
86}