Add the ability to rate insights to curate training data
This commit is contained in:
@@ -25,6 +25,18 @@ pub struct GetPhotoInsightQuery {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RateInsightRequest {
|
||||
pub file_path: String,
|
||||
pub approved: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ExportTrainingDataQuery {
|
||||
#[serde(default)]
|
||||
pub approved_only: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct PhotoInsightResponse {
|
||||
pub id: i32,
|
||||
@@ -37,6 +49,8 @@ pub struct PhotoInsightResponse {
|
||||
pub prompt_eval_count: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub eval_count: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub approved: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -139,6 +153,7 @@ pub async fn get_insight_handler(
|
||||
model_version: insight.model_version,
|
||||
prompt_eval_count: None,
|
||||
eval_count: None,
|
||||
approved: insight.approved,
|
||||
};
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
@@ -205,6 +220,7 @@ pub async fn get_all_insights_handler(
|
||||
model_version: insight.model_version,
|
||||
prompt_eval_count: None,
|
||||
eval_count: None,
|
||||
approved: insight.approved,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -287,6 +303,7 @@ pub async fn generate_agentic_insight_handler(
|
||||
model_version: insight.model_version,
|
||||
prompt_eval_count,
|
||||
eval_count,
|
||||
approved: insight.approved,
|
||||
};
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
@@ -377,3 +394,86 @@ pub async fn get_available_models_handler(
|
||||
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
|
||||
/// POST /insights/rate - Rate an insight (thumbs up/down for training data)
|
||||
#[post("/insights/rate")]
|
||||
pub async fn rate_insight_handler(
|
||||
_claims: Claims,
|
||||
request: web::Json<RateInsightRequest>,
|
||||
insight_dao: web::Data<std::sync::Mutex<Box<dyn InsightDao>>>,
|
||||
) -> impl Responder {
|
||||
let normalized_path = normalize_path(&request.file_path);
|
||||
log::info!(
|
||||
"Rating insight for {}: approved={}",
|
||||
normalized_path,
|
||||
request.approved
|
||||
);
|
||||
|
||||
let otel_context = opentelemetry::Context::new();
|
||||
let mut dao = insight_dao.lock().expect("Unable to lock InsightDao");
|
||||
|
||||
match dao.rate_insight(&otel_context, &normalized_path, request.approved) {
|
||||
Ok(()) => HttpResponse::Ok().json(serde_json::json!({
|
||||
"success": true,
|
||||
"message": "Insight rated successfully"
|
||||
})),
|
||||
Err(e) => {
|
||||
log::error!("Failed to rate insight: {:?}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": format!("Failed to rate insight: {:?}", e)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GET /insights/training-data - Export approved training data as JSONL
|
||||
#[get("/insights/training-data")]
|
||||
pub async fn export_training_data_handler(
|
||||
_claims: Claims,
|
||||
query: web::Query<ExportTrainingDataQuery>,
|
||||
insight_dao: web::Data<std::sync::Mutex<Box<dyn InsightDao>>>,
|
||||
) -> impl Responder {
|
||||
let approved_only = query.approved_only.unwrap_or(true);
|
||||
log::info!("Exporting training data (approved_only={})", approved_only);
|
||||
|
||||
let otel_context = opentelemetry::Context::new();
|
||||
let mut dao = insight_dao.lock().expect("Unable to lock InsightDao");
|
||||
|
||||
let insights = if approved_only {
|
||||
dao.get_approved_insights(&otel_context)
|
||||
} else {
|
||||
dao.get_all_insights(&otel_context)
|
||||
};
|
||||
|
||||
match insights {
|
||||
Ok(insights) => {
|
||||
let mut jsonl = String::new();
|
||||
for insight in &insights {
|
||||
if let Some(ref messages) = insight.training_messages {
|
||||
let entry = serde_json::json!({
|
||||
"file_path": insight.file_path,
|
||||
"model_version": insight.model_version,
|
||||
"generated_at": insight.generated_at,
|
||||
"title": insight.title,
|
||||
"summary": insight.summary,
|
||||
"messages": serde_json::from_str::<serde_json::Value>(messages)
|
||||
.unwrap_or(serde_json::Value::Null),
|
||||
});
|
||||
jsonl.push_str(&entry.to_string());
|
||||
jsonl.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/jsonl")
|
||||
.insert_header(("Content-Disposition", "attachment; filename=\"training_data.jsonl\""))
|
||||
.body(jsonl)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to export training data: {:?}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": format!("Failed to export training data: {:?}", e)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user