feature/insights #46

Merged
cameron merged 25 commits from feature/insights into master 2026-01-15 01:07:59 +00:00
5 changed files with 54 additions and 30 deletions
Showing only changes of commit f65f4efde8 - Show all commits

View File

@@ -222,13 +222,14 @@ pub async fn get_available_models_handler(
let ollama_client = &app_state.ollama; let ollama_client = &app_state.ollama;
// Fetch models with capabilities from primary server // Fetch models with capabilities from primary server
let primary_models = match OllamaClient::list_models_with_capabilities(&ollama_client.primary_url).await { let primary_models =
Ok(models) => models, match OllamaClient::list_models_with_capabilities(&ollama_client.primary_url).await {
Err(e) => { Ok(models) => models,
log::warn!("Failed to fetch models from primary server: {:?}", e); Err(e) => {
vec![] log::warn!("Failed to fetch models from primary server: {:?}", e);
} vec![]
}; }
};
let primary = ServerModels { let primary = ServerModels {
url: ollama_client.primary_url.clone(), url: ollama_client.primary_url.clone(),

View File

@@ -995,7 +995,10 @@ impl InsightGenerator {
let image_base64 = if has_vision { let image_base64 = if has_vision {
match self.load_image_as_base64(&file_path) { match self.load_image_as_base64(&file_path) {
Ok(b64) => { Ok(b64) => {
log::info!("Successfully loaded image for vision-capable model '{}'", model_to_check); log::info!(
"Successfully loaded image for vision-capable model '{}'",
model_to_check
);
Some(b64) Some(b64)
} }
Err(e) => { Err(e) => {

View File

@@ -63,7 +63,10 @@ impl OllamaClient {
} }
/// Check if a model has vision capabilities using the /api/show endpoint /// Check if a model has vision capabilities using the /api/show endpoint
pub async fn check_model_capabilities(url: &str, model_name: &str) -> Result<ModelCapabilities> { pub async fn check_model_capabilities(
url: &str,
model_name: &str,
) -> Result<ModelCapabilities> {
let client = Client::builder() let client = Client::builder()
.connect_timeout(Duration::from_secs(5)) .connect_timeout(Duration::from_secs(5))
.timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(10))

View File

@@ -1,3 +1,6 @@
use ::anyhow;
use actix::{Handler, Message};
use anyhow::{Context, anyhow};
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Debug; use std::fmt::Debug;
use std::fs::read_dir; use std::fs::read_dir;
@@ -5,10 +8,7 @@ use std::io;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Mutex; use std::sync::Mutex;
use std::time::SystemTime;
use ::anyhow;
use actix::{Handler, Message};
use anyhow::{Context, anyhow};
use crate::data::{Claims, FilesRequest, FilterMode, MediaType, PhotosResponse, SortType}; use crate::data::{Claims, FilesRequest, FilterMode, MediaType, PhotosResponse, SortType};
use crate::database::ExifDao; use crate::database::ExifDao;
@@ -22,6 +22,7 @@ use actix_web::{
HttpRequest, HttpResponse, HttpRequest, HttpResponse,
web::{self, Query}, web::{self, Query},
}; };
use chrono::{DateTime, Utc};
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use opentelemetry::KeyValue; use opentelemetry::KeyValue;
use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer}; use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer};
@@ -73,18 +74,24 @@ fn apply_sorting_with_exif(
.into_iter() .into_iter()
.map(|f| { .map(|f| {
// Try EXIF date first // Try EXIF date first
let date_taken = exif_map.get(&f.file_name).copied().or_else(|| { let date_taken = exif_map
// Fallback to filename extraction .get(&f.file_name)
extract_date_from_filename(&f.file_name).map(|dt| dt.timestamp()) .copied()
}).or_else(|| { .or_else(|| {
// Fallback to filesystem metadata creation date // Fallback to filename extraction
let full_path = base_path.join(&f.file_name); extract_date_from_filename(&f.file_name).map(|dt| dt.timestamp())
std::fs::metadata(full_path) })
.and_then(|md| md.created()) .or_else(|| {
.ok() // Fallback to filesystem metadata creation date
.and_then(|ct| ct.duration_since(std::time::UNIX_EPOCH).ok()) let full_path = base_path.join(&f.file_name);
.map(|d| d.as_secs() as i64) std::fs::metadata(full_path)
}); .and_then(|md| md.created().or(md.modified()))
.ok()
.map(|system_time| {
<SystemTime as Into<DateTime<Utc>>>::into(system_time)
.timestamp()
})
});
FileWithMetadata { FileWithMetadata {
file_name: f.file_name, file_name: f.file_name,
@@ -332,8 +339,13 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
// Handle sorting - use helper function that supports EXIF date sorting // Handle sorting - use helper function that supports EXIF date sorting
let sort_type = req.sort.unwrap_or(NameAsc); let sort_type = req.sort.unwrap_or(NameAsc);
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao"); let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
let result = let result = apply_sorting_with_exif(
apply_sorting_with_exif(files, sort_type, &mut exif_dao_guard, &span_context, (&app_state.base_path).as_ref()); files,
sort_type,
&mut exif_dao_guard,
&span_context,
(&app_state.base_path).as_ref(),
);
drop(exif_dao_guard); drop(exif_dao_guard);
result result
}) })
@@ -476,8 +488,13 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
// Handle sorting - use helper function that supports EXIF date sorting // Handle sorting - use helper function that supports EXIF date sorting
let response_files = if let Some(sort_type) = req.sort { let response_files = if let Some(sort_type) = req.sort {
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao"); let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
let result = let result = apply_sorting_with_exif(
apply_sorting_with_exif(photos, sort_type, &mut exif_dao_guard, &span_context, (&app_state.base_path).as_ref()); photos,
sort_type,
&mut exif_dao_guard,
&span_context,
(&app_state.base_path).as_ref(),
);
drop(exif_dao_guard); drop(exif_dao_guard);
result result
} else { } else {

View File

@@ -28,9 +28,9 @@ use actix_web::{
web::{self, BufMut, BytesMut}, web::{self, BufMut, BytesMut},
}; };
use chrono::Utc; use chrono::Utc;
use urlencoding::decode;
use diesel::sqlite::Sqlite; use diesel::sqlite::Sqlite;
use rayon::prelude::*; use rayon::prelude::*;
use urlencoding::decode;
use crate::ai::InsightGenerator; use crate::ai::InsightGenerator;
use crate::auth::login; use crate::auth::login;