Skip to content

Commit 67d2996

Browse files
committed
Refactor provider key validation in update_provider function
- Simplified the logic for checking if the current routing provider has a usable key by introducing a reusable closure. - Enhanced the handling of environment variable references for provider keys, ensuring that keys are validated correctly even when environment variables are not set. - Improved readability and maintainability of the code by consolidating key checks into a single function.
1 parent 3dae3f8 commit 67d2996

File tree

2 files changed

+67
-44
lines changed

2 files changed

+67
-44
lines changed

src/api/server.rs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,33 +2672,31 @@ async fn update_provider(
26722672
let current_provider =
26732673
crate::llm::routing::provider_from_model(current_channel);
26742674

2675-
// Check if the current routing provider has a key configured
2675+
// Check if the current routing provider has a usable key.
2676+
// Resolves "env:VAR_NAME" references — the boot script writes these
2677+
// for common providers even when the env var isn't actually set.
2678+
let has_provider_key = |toml_key: &str, env_var: &str| -> bool {
2679+
if let Some(s) = doc.get("llm").and_then(|l| l.get(toml_key)).and_then(|v| v.as_str()) {
2680+
if let Some(var_name) = s.strip_prefix("env:") {
2681+
return std::env::var(var_name).is_ok();
2682+
}
2683+
return !s.is_empty();
2684+
}
2685+
std::env::var(env_var).is_ok()
2686+
};
2687+
26762688
let has_key_for_current = match current_provider {
2677-
"anthropic" => doc
2678-
.get("llm")
2679-
.and_then(|l| l.get("anthropic_key"))
2680-
.and_then(|v| v.as_str())
2681-
.is_some_and(|s| !s.is_empty()),
2682-
"openai" => doc
2683-
.get("llm")
2684-
.and_then(|l| l.get("openai_key"))
2685-
.and_then(|v| v.as_str())
2686-
.is_some_and(|s| !s.is_empty()),
2687-
"openrouter" => doc
2688-
.get("llm")
2689-
.and_then(|l| l.get("openrouter_key"))
2690-
.and_then(|v| v.as_str())
2691-
.is_some_and(|s| !s.is_empty()),
2692-
"zhipu" => doc
2693-
.get("llm")
2694-
.and_then(|l| l.get("zhipu_key"))
2695-
.and_then(|v| v.as_str())
2696-
.is_some_and(|s| !s.is_empty()),
2697-
"opencode-zen" => doc
2698-
.get("llm")
2699-
.and_then(|l| l.get("opencode_zen_key"))
2700-
.and_then(|v| v.as_str())
2701-
.is_some_and(|s| !s.is_empty()),
2689+
"anthropic" => has_provider_key("anthropic_key", "ANTHROPIC_API_KEY"),
2690+
"openai" => has_provider_key("openai_key", "OPENAI_API_KEY"),
2691+
"openrouter" => has_provider_key("openrouter_key", "OPENROUTER_API_KEY"),
2692+
"zhipu" => has_provider_key("zhipu_key", "ZHIPU_API_KEY"),
2693+
"groq" => has_provider_key("groq_key", "GROQ_API_KEY"),
2694+
"together" => has_provider_key("together_key", "TOGETHER_API_KEY"),
2695+
"fireworks" => has_provider_key("fireworks_key", "FIREWORKS_API_KEY"),
2696+
"deepseek" => has_provider_key("deepseek_key", "DEEPSEEK_API_KEY"),
2697+
"xai" => has_provider_key("xai_key", "XAI_API_KEY"),
2698+
"mistral" => has_provider_key("mistral_key", "MISTRAL_API_KEY"),
2699+
"opencode-zen" => has_provider_key("opencode_zen_key", "OPENCODE_ZEN_API_KEY"),
27022700
_ => false,
27032701
};
27042702

src/memory/lance.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,54 @@ impl Clone for EmbeddingTable {
2626

2727
impl EmbeddingTable {
2828
/// Open existing table or create a new one.
29+
///
30+
/// If the table exists but is corrupted (e.g. process killed mid-write),
31+
/// it is dropped and recreated. Embeddings can be regenerated from SQLite.
2932
pub async fn open_or_create(connection: &lancedb::Connection) -> Result<Self> {
30-
// Try to open existing table first
33+
// Try to open existing table
3134
match connection.open_table(TABLE_NAME).execute().await {
32-
Ok(table) => Ok(Self { table }),
33-
Err(_) => {
34-
// Create new table with empty batch
35-
let schema = Self::schema();
36-
37-
// Create empty RecordBatchIterator
38-
let batches = RecordBatchIterator::new(
39-
vec![].into_iter().map(Ok),
40-
Arc::new(schema),
35+
Ok(table) => return Ok(Self { table }),
36+
Err(error) => {
37+
tracing::debug!(%error, "failed to open embeddings table, will create");
38+
}
39+
}
40+
41+
// Table doesn't exist or is unreadable — try creating it
42+
match Self::create_empty_table(connection).await {
43+
Ok(table) => return Ok(Self { table }),
44+
Err(error) => {
45+
tracing::warn!(
46+
%error,
47+
"failed to create embeddings table, attempting recovery from corrupted state"
4148
);
42-
43-
let table = connection
44-
.create_table(TABLE_NAME, Box::new(batches))
45-
.execute()
46-
.await
47-
.map_err(|e| DbError::LanceDb(e.to_string()))?;
48-
49-
Ok(Self { table })
5049
}
5150
}
51+
52+
// Both open and create failed — table data exists but is corrupted.
53+
// Drop it and recreate from scratch.
54+
if let Err(error) = connection.drop_table(TABLE_NAME, &[]).await {
55+
tracing::warn!(%error, "drop_table failed during recovery, proceeding anyway");
56+
}
57+
58+
let table = Self::create_empty_table(connection).await?;
59+
tracing::info!("embeddings table recovered — embeddings will be rebuilt from memory store");
60+
61+
Ok(Self { table })
62+
}
63+
64+
/// Create an empty embeddings table.
65+
async fn create_empty_table(connection: &lancedb::Connection) -> Result<lancedb::Table> {
66+
let schema = Self::schema();
67+
let batches = RecordBatchIterator::new(
68+
vec![].into_iter().map(Ok),
69+
Arc::new(schema),
70+
);
71+
72+
connection
73+
.create_table(TABLE_NAME, Box::new(batches))
74+
.execute()
75+
.await
76+
.map_err(|e| DbError::LanceDb(e.to_string()).into())
5277
}
5378

5479
/// Store an embedding with content for a memory.

0 commit comments

Comments
 (0)