feat(rag): offer the storage driver when an agent initializes its RAG
Agent startup and graph rag nodes both run an interactive wizard when their knowledge base has not been built, but neither offered the driver choice that interactive named-RAG creation has, so both silently produced a yaml store. A plain agent was the worse of the two: AgentConfig carries only documents, so there was no way to get a duckdb RAG for one, interactively or declaratively. A graph node could at least declare driver: in the workflow. Agent startup now passes prompt_for_driver, and a rag node whose wizard runs is asked too. The prompt is skipped when the node already declares a driver, and sits inside the not-fully-specified branch after the non-interactive bail, so declarative workflows and headless runs are unchanged. Temp RAGs still pass false: they are deleted on the next run, so a persistent store would only leave a sidecar behind. The prompt moves to select_rag_driver rather than being duplicated.
This commit is contained in:
+6
-2
@@ -185,7 +185,7 @@ impl Agent {
|
||||
&rag_path_clone,
|
||||
&document_paths,
|
||||
abort,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
})
|
||||
@@ -1025,7 +1025,7 @@ async fn init_graph_rags(
|
||||
{
|
||||
bail!("rag node '{node_id}': {message}");
|
||||
}
|
||||
let config = rag_init_config(rag_node);
|
||||
let mut config = rag_init_config(rag_node);
|
||||
let fully_specified = config.embedding_model.is_some()
|
||||
&& config.chunk_size.is_some()
|
||||
&& config.chunk_overlap.is_some();
|
||||
@@ -1051,6 +1051,10 @@ async fn init_graph_rags(
|
||||
initialized. RAG initialization is required for this agent."
|
||||
);
|
||||
}
|
||||
|
||||
if config.driver.is_none() {
|
||||
config.driver = Some(crate::rag::select_rag_driver()?);
|
||||
}
|
||||
}
|
||||
|
||||
let document_paths =
|
||||
|
||||
+24
-24
@@ -267,31 +267,10 @@ impl Rag {
|
||||
}
|
||||
println!("⚙ Initializing RAG...");
|
||||
let (embedding_model, chunk_size, chunk_overlap) = Self::create_config(app)?;
|
||||
// Only interactive named-RAG creation offers a driver choice. Temp RAGs and
|
||||
// agent startup pass `false`; an explicit flag is used rather than inferring
|
||||
// from the name because the agent path passes the literal name "rag", which is
|
||||
// indistinguishable from a user creating a RAG genuinely named `rag`.
|
||||
let driver = if prompt_for_driver {
|
||||
let options = vec![
|
||||
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
|
||||
"duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.",
|
||||
];
|
||||
let sel = Select::new("RAG storage driver:", options)
|
||||
.with_starting_cursor(0)
|
||||
.prompt()?;
|
||||
if sel.starts_with("duckdb") {
|
||||
println!(
|
||||
"Note: several Coyote processes can query a duckdb RAG at the same time, \
|
||||
but while one process is ingesting or rebuilding it the others cannot \
|
||||
read it until that finishes. Changing its driver later means deleting \
|
||||
and recreating the RAG."
|
||||
);
|
||||
"duckdb"
|
||||
select_rag_driver()?
|
||||
} else {
|
||||
"yaml"
|
||||
}
|
||||
} else {
|
||||
"yaml"
|
||||
"yaml".to_string()
|
||||
};
|
||||
let reranker_model = app.rag_reranker_model.clone();
|
||||
let top_k = app.rag_top_k;
|
||||
@@ -318,7 +297,7 @@ impl Rag {
|
||||
graph_hops: Some(graph_hops),
|
||||
},
|
||||
);
|
||||
data.driver = driver.to_string();
|
||||
data.driver = driver;
|
||||
let mut rag = Self::create(app, name, save_path, data)?;
|
||||
let mut paths = doc_paths.to_vec();
|
||||
if paths.is_empty() {
|
||||
@@ -1867,6 +1846,27 @@ fn select_embedding_model(models: &[&Model]) -> Result<String> {
|
||||
Ok(result.value)
|
||||
}
|
||||
|
||||
pub(crate) fn select_rag_driver() -> Result<String> {
|
||||
let options = vec![
|
||||
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
|
||||
"duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.",
|
||||
];
|
||||
let sel = Select::new("RAG storage driver:", options)
|
||||
.with_starting_cursor(0)
|
||||
.prompt()?;
|
||||
if sel.starts_with("duckdb") {
|
||||
println!(
|
||||
"Note: several Coyote processes can query a duckdb RAG at the same time, \
|
||||
but while one process is ingesting or rebuilding it the others cannot \
|
||||
read it until that finishes. Changing its driver later means deleting \
|
||||
and recreating the RAG."
|
||||
);
|
||||
Ok("duckdb".to_string())
|
||||
} else {
|
||||
Ok("yaml".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
const EXTRACTOR_SKIP: &str = "Skip";
|
||||
|
||||
fn select_extractor_model(app: &AppConfig) -> Result<Option<String>> {
|
||||
|
||||
Reference in New Issue
Block a user