fix(rag): address Copilot review findings on the driver abstraction

Five review comments, all real:

- hybrid_search ran its vector and keyword legs sequentially after the
  provider refactor; main ran them under tokio::join!. Restores the
  concurrency while keeping the degrade-on-error keyword behaviour, so a
  remote provider no longer pays two serial round trips per query.

- inject_rag_secrets derived a vault secret name by trimming braces, which
  leaves a literal key untouched. A RAG holding a plaintext api_key therefore
  looked the secret up by its own value and printed it to stderr on failure.
  Parsing is now strict and a non-placeholder is skipped with a warning that
  names no credential.

- validate() now refuses a driver_config.api_key that is not a {{NAME}}
  placeholder, so a plaintext key cannot reach the RAG YAML at all.

- Rag::create's catch-all arm treated any unrecognised driver as yaml. A typo
  built a yaml store, paid to embed the corpus, persisted the bad driver and
  only failed on the next run. Unknown drivers now fail immediately.

- The qdrant arm's error was written for a developer; it now tells the user
  that only attached collections are readable and points at .rag attach.
This commit is contained in:
2026-08-11 21:04:21 -06:00
parent 6d0a5550fe
commit 74bc613d94
2 changed files with 142 additions and 15 deletions
+15 -5
View File
@@ -19,7 +19,7 @@ use crate::config::AppConfig;
use crate::config::Config;
use crate::config::VAULT_DATA_FILE_NAME;
use crate::config::paths;
use crate::rag::RagData;
use crate::rag::{RagData, placeholder_secret_name};
use crate::sandbox::mcp_credentials::MCP_MIXIN_NAME;
use crate::sandbox::mixins::DiscoveredMixin;
use crate::utils::run_command_with_output;
@@ -344,10 +344,20 @@ fn inject_rag_secrets(vault: &Vault, registered: &HashSet<String>) -> Result<()>
if service_id.is_empty() || registered.contains(&service_id) {
continue;
}
let secret_name = placeholder
.trim_start_matches("{{")
.trim_end_matches("}}")
.trim();
// A literal key must NOT be mistaken for a secret NAME. The trims that
// used to stand here leave a non-placeholder value completely untouched,
// so the vault lookup below would run with the credential as the "name"
// and the warning would then print that credential to stderr.
let Some(secret_name) = placeholder_secret_name(placeholder) else {
eprintln!(
"Warning: RAG '{stem}' has a driver_config.api_key that is not a \
secret placeholder, so no credential can be provisioned to the \
sandbox and queries to this RAG will fail inside it. Store the \
key with `coyote --add-secret <NAME>`, then set api_key to the \
matching placeholder in the RAG YAML."
);
continue;
};
match vault.get_secret(secret_name, false) {
Ok(secret_value) => {