Enhanced Insights with daily summary embeddings

Bump to 0.5.0. Added daily summary generation job
This commit is contained in:
Cameron
2026-01-05 09:13:16 -05:00
parent 43b7c2b8ec
commit 11e725c443
18 changed files with 2348 additions and 61 deletions

View File

@@ -226,7 +226,7 @@ Return ONLY the title, nothing else."#,
let sms_str = sms_summary.unwrap_or("No messages");
let prompt = format!(
r#"Write a brief 1-2 paragraph description of this moment based on the available information:
r#"Write a 1-3 paragraph description of this moment based on the available information:
Date: {}
Location: {}
@@ -238,10 +238,139 @@ Use only the specific details provided above. Mention people's names, places, or
sms_str
);
let system = "You are a memory refreshing assistant. Use only the information provided. Do not invent details. Help me remember this day.";
let system = "You are a memory refreshing assistant who is able to provide insights through analyzing past conversations. Use only the information provided. Do not invent details.";
self.generate(&prompt, Some(system)).await
}
/// Generate an embedding vector for text using nomic-embed-text:v1.5
/// Returns a 768-dimensional vector as Vec<f32>
pub async fn generate_embedding(&self, text: &str) -> Result<Vec<f32>> {
let embeddings = self.generate_embeddings(&[text]).await?;
embeddings.into_iter().next()
.ok_or_else(|| anyhow::anyhow!("No embedding returned"))
}
/// Generate embeddings for multiple texts in a single API call (batch mode)
/// Returns a vector of 768-dimensional vectors
/// This is much more efficient than calling generate_embedding multiple times
pub async fn generate_embeddings(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>> {
let embedding_model = "nomic-embed-text:v1.5";
log::debug!("=== Ollama Batch Embedding Request ===");
log::debug!("Model: {}", embedding_model);
log::debug!("Batch size: {} texts", texts.len());
log::debug!("======================================");
// Try primary server first
log::debug!(
"Attempting to generate {} embeddings with primary server: {} (model: {})",
texts.len(),
self.primary_url,
embedding_model
);
let primary_result = self
.try_generate_embeddings(&self.primary_url, embedding_model, texts)
.await;
let embeddings = match primary_result {
Ok(embeddings) => {
log::debug!("Successfully generated {} embeddings from primary server", embeddings.len());
embeddings
}
Err(e) => {
log::warn!("Primary server batch embedding failed: {}", e);
// Try fallback server if available
if let Some(fallback_url) = &self.fallback_url {
log::info!(
"Attempting to generate {} embeddings with fallback server: {} (model: {})",
texts.len(),
fallback_url,
embedding_model
);
match self
.try_generate_embeddings(fallback_url, embedding_model, texts)
.await
{
Ok(embeddings) => {
log::info!("Successfully generated {} embeddings from fallback server", embeddings.len());
embeddings
}
Err(fallback_e) => {
log::error!("Fallback server batch embedding also failed: {}", fallback_e);
return Err(anyhow::anyhow!(
"Both primary and fallback servers failed. Primary: {}, Fallback: {}",
e,
fallback_e
));
}
}
} else {
log::error!("No fallback server configured");
return Err(e);
}
}
};
// Validate embedding dimensions (should be 768 for nomic-embed-text:v1.5)
for (i, embedding) in embeddings.iter().enumerate() {
if embedding.len() != 768 {
log::warn!(
"Unexpected embedding dimensions for item {}: {} (expected 768)",
i,
embedding.len()
);
}
}
Ok(embeddings)
}
/// Internal helper to try generating an embedding from a specific server
async fn try_generate_embedding(
&self,
url: &str,
model: &str,
text: &str,
) -> Result<Vec<f32>> {
let embeddings = self.try_generate_embeddings(url, model, &[text]).await?;
embeddings.into_iter().next()
.ok_or_else(|| anyhow::anyhow!("No embedding returned from Ollama"))
}
/// Internal helper to try generating embeddings for multiple texts from a specific server
async fn try_generate_embeddings(
&self,
url: &str,
model: &str,
texts: &[&str],
) -> Result<Vec<Vec<f32>>> {
let request = OllamaBatchEmbedRequest {
model: model.to_string(),
input: texts.iter().map(|s| s.to_string()).collect(),
};
let response = self
.client
.post(&format!("{}/api/embed", url))
.json(&request)
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let error_body = response.text().await.unwrap_or_default();
return Err(anyhow::anyhow!(
"Ollama batch embedding request failed: {} - {}",
status,
error_body
));
}
let result: OllamaEmbedResponse = response.json().await?;
Ok(result.embeddings)
}
}
#[derive(Serialize)]
@@ -267,3 +396,20 @@ struct OllamaTagsResponse {
struct OllamaModel {
name: String,
}
#[derive(Serialize)]
struct OllamaEmbedRequest {
model: String,
input: String,
}
#[derive(Serialize)]
struct OllamaBatchEmbedRequest {
model: String,
input: Vec<String>,
}
#[derive(Deserialize)]
struct OllamaEmbedResponse {
embeddings: Vec<Vec<f32>>,
}