Add the ability to rate insights to curate training data

This commit is contained in:
Cameron
2026-04-13 09:23:40 -04:00
parent da16fddce3
commit c703a47f17
7 changed files with 204 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use base64::Engine as _;
use chrono::{DateTime, NaiveDate, Utc};
use chrono::{DateTime, Local, NaiveDate, Utc};
use image::ImageFormat;
use opentelemetry::KeyValue;
use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer};
@@ -1165,6 +1165,7 @@ impl InsightGenerator {
generated_at: Utc::now().timestamp(),
model_version: ollama_client.primary_model.clone(),
is_current: true,
training_messages: None,
};
let mut dao = self.insight_dao.lock().expect("Unable to lock InsightDao");
@@ -1367,6 +1368,7 @@ Return ONLY the summary, nothing else."#,
"recall_facts_for_photo" => self.tool_recall_facts_for_photo(arguments, cx).await,
"store_entity" => self.tool_store_entity(arguments, ollama, cx).await,
"store_fact" => self.tool_store_fact(arguments, file_path, cx).await,
"get_current_datetime" => Self::tool_get_current_datetime(),
unknown => format!("Unknown tool: {}", unknown),
};
if result.starts_with("Error") || result.starts_with("No ") {
@@ -2023,6 +2025,16 @@ Return ONLY the summary, nothing else."#,
)
}
/// Tool: get_current_datetime — returns the current local date and time
fn tool_get_current_datetime() -> String {
let now = Local::now();
format!(
"Current date/time: {} ({})",
now.format("%Y-%m-%d %H:%M:%S %Z"),
now.format("%A")
)
}
// ── Agentic insight generation ──────────────────────────────────────
/// Build the list of tool definitions for the agentic loop
@@ -2237,6 +2249,15 @@ Return ONLY the summary, nothing else."#,
}),
));
tools.push(Tool::function(
"get_current_datetime",
"Get the current date and time. Useful for understanding how long ago the photo was taken.",
serde_json::json!({
"type": "object",
"properties": {}
}),
));
if has_vision {
tools.push(Tool::function(
"describe_photo",
@@ -2630,10 +2651,13 @@ Return ONLY the summary, nothing else."#,
"Based on the context gathered, please write the final photo insight: a title and a detailed personal summary. Write in first person as Cameron.",
));
let (final_response, prompt_tokens, eval_tokens) =
ollama_client.chat_with_tools(messages, vec![]).await?;
ollama_client
.chat_with_tools(messages.clone(), vec![])
.await?;
last_prompt_eval_count = prompt_tokens;
last_eval_count = eval_tokens;
final_content = final_response.content;
final_content = final_response.content.clone();
messages.push(final_response);
}
loop_cx
@@ -2653,7 +2677,16 @@ Return ONLY the summary, nothing else."#,
&final_content[..final_content.len().min(200)]
);
// 14. Store insight (returns the persisted row including its new id)
// 14. Serialize the full message history for training data
let training_messages = match serde_json::to_string(&messages) {
Ok(json) => Some(json),
Err(e) => {
log::warn!("Failed to serialize training messages: {}", e);
None
}
};
// 15. Store insight (returns the persisted row including its new id)
let insight = InsertPhotoInsight {
file_path: file_path.to_string(),
title,
@@ -2661,6 +2694,7 @@ Return ONLY the summary, nothing else."#,
generated_at: Utc::now().timestamp(),
model_version: ollama_client.primary_model.clone(),
is_current: true,
training_messages,
};
let stored = {
@@ -2682,7 +2716,7 @@ Return ONLY the summary, nothing else."#,
let stored_insight = stored?;
// 15. Backfill source_insight_id on all facts recorded for this photo during the loop
// 16. Backfill source_insight_id on all facts recorded for this photo during the loop
{
let mut kdao = self
.knowledge_dao