Enhanced Insights with daily summary embeddings
Bump to 0.5.0. Added daily summary generation job
This commit is contained in:
150
src/ai/ollama.rs
150
src/ai/ollama.rs
@@ -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>>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user