Add Insights Model Discovery and Fallback Handling
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
use actix_web::{HttpResponse, Responder, delete, get, post, web};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ai::InsightGenerator;
|
||||
use crate::ai::{InsightGenerator, OllamaClient};
|
||||
use crate::data::Claims;
|
||||
use crate::database::InsightDao;
|
||||
use crate::utils::normalize_path;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GeneratePhotoInsightRequest {
|
||||
pub file_path: String,
|
||||
#[serde(default)]
|
||||
pub model: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -25,6 +28,20 @@ pub struct PhotoInsightResponse {
|
||||
pub model_version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AvailableModelsResponse {
|
||||
pub primary: ServerModels,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fallback: Option<ServerModels>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ServerModels {
|
||||
pub url: String,
|
||||
pub models: Vec<String>,
|
||||
pub default_model: String,
|
||||
}
|
||||
|
||||
/// POST /insights/generate - Generate insight for a specific photo
|
||||
#[post("/insights/generate")]
|
||||
pub async fn generate_insight_handler(
|
||||
@@ -32,14 +49,17 @@ pub async fn generate_insight_handler(
|
||||
request: web::Json<GeneratePhotoInsightRequest>,
|
||||
insight_generator: web::Data<InsightGenerator>,
|
||||
) -> impl Responder {
|
||||
let normalized_path = normalize_path(&request.file_path);
|
||||
|
||||
log::info!(
|
||||
"Manual insight generation triggered for photo: {}",
|
||||
request.file_path
|
||||
"Manual insight generation triggered for photo: {} with model: {:?}",
|
||||
normalized_path,
|
||||
request.model
|
||||
);
|
||||
|
||||
// Generate insight
|
||||
// Generate insight with optional custom model
|
||||
match insight_generator
|
||||
.generate_insight_for_photo(&request.file_path)
|
||||
.generate_insight_for_photo_with_model(&normalized_path, request.model.clone())
|
||||
.await
|
||||
{
|
||||
Ok(()) => HttpResponse::Ok().json(serde_json::json!({
|
||||
@@ -62,12 +82,13 @@ pub async fn get_insight_handler(
|
||||
query: web::Query<GetPhotoInsightQuery>,
|
||||
insight_dao: web::Data<std::sync::Mutex<Box<dyn InsightDao>>>,
|
||||
) -> impl Responder {
|
||||
log::debug!("Fetching insight for {}", query.path);
|
||||
let normalized_path = normalize_path(&query.path);
|
||||
log::debug!("Fetching insight for {}", normalized_path);
|
||||
|
||||
let otel_context = opentelemetry::Context::new();
|
||||
let mut dao = insight_dao.lock().expect("Unable to lock InsightDao");
|
||||
|
||||
match dao.get_insight(&otel_context, &query.path) {
|
||||
match dao.get_insight(&otel_context, &normalized_path) {
|
||||
Ok(Some(insight)) => {
|
||||
let response = PhotoInsightResponse {
|
||||
id: insight.id,
|
||||
@@ -98,12 +119,13 @@ pub async fn delete_insight_handler(
|
||||
query: web::Query<GetPhotoInsightQuery>,
|
||||
insight_dao: web::Data<std::sync::Mutex<Box<dyn InsightDao>>>,
|
||||
) -> impl Responder {
|
||||
log::info!("Deleting insight for {}", query.path);
|
||||
let normalized_path = normalize_path(&query.path);
|
||||
log::info!("Deleting insight for {}", normalized_path);
|
||||
|
||||
let otel_context = opentelemetry::Context::new();
|
||||
let mut dao = insight_dao.lock().expect("Unable to lock InsightDao");
|
||||
|
||||
match dao.delete_insight(&otel_context, &query.path) {
|
||||
match dao.delete_insight(&otel_context, &normalized_path) {
|
||||
Ok(()) => HttpResponse::Ok().json(serde_json::json!({
|
||||
"success": true,
|
||||
"message": "Insight deleted successfully"
|
||||
@@ -152,3 +174,53 @@ pub async fn get_all_insights_handler(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GET /insights/models - List available models from both servers
|
||||
#[get("/insights/models")]
|
||||
pub async fn get_available_models_handler(
|
||||
_claims: Claims,
|
||||
app_state: web::Data<crate::state::AppState>,
|
||||
) -> impl Responder {
|
||||
log::debug!("Fetching available models");
|
||||
|
||||
let ollama_client = &app_state.ollama;
|
||||
|
||||
// Fetch models from primary server
|
||||
let primary_models = match OllamaClient::list_models(&ollama_client.primary_url).await {
|
||||
Ok(models) => models,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to fetch models from primary server: {:?}", e);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let primary = ServerModels {
|
||||
url: ollama_client.primary_url.clone(),
|
||||
models: primary_models,
|
||||
default_model: ollama_client.primary_model.clone(),
|
||||
};
|
||||
|
||||
// Fetch models from fallback server if configured
|
||||
let fallback = if let Some(fallback_url) = &ollama_client.fallback_url {
|
||||
match OllamaClient::list_models(fallback_url).await {
|
||||
Ok(models) => Some(ServerModels {
|
||||
url: fallback_url.clone(),
|
||||
models,
|
||||
default_model: ollama_client
|
||||
.fallback_model
|
||||
.clone()
|
||||
.unwrap_or_else(|| ollama_client.primary_model.clone()),
|
||||
}),
|
||||
Err(e) => {
|
||||
log::warn!("Failed to fetch models from fallback server: {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response = AvailableModelsResponse { primary, fallback };
|
||||
|
||||
HttpResponse::Ok().json(response)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user