fix for stats and model selection
Some checks failed
Weekly OSV dependency audit / dependency-audit (push) Failing after 5s

This commit is contained in:
Georg Bauer
2026-09-05 08:17:07 +02:00
parent bd6353804b
commit b99ce2aa10
5 changed files with 116 additions and 24 deletions

View File

@@ -0,0 +1,13 @@
ALTER TABLE sessions ADD COLUMN model_without_qwen TEXT
CHECK (model_without_qwen IS NULL OR model_without_qwen IN (
'deepseek-v4-flash-0731',
'deepseek-v4-pro',
'glm-5.2',
'glm-5.3-flash'
));
UPDATE sessions SET model_without_qwen = CASE
WHEN model = 'qwen3.8-flash-next' THEN NULL
ELSE model
END;
ALTER TABLE sessions DROP COLUMN model;
ALTER TABLE sessions RENAME COLUMN model_without_qwen TO model;

View File

@@ -0,0 +1,11 @@
ALTER TABLE sessions ADD COLUMN model_with_qwen TEXT
CHECK (model_with_qwen IS NULL OR model_with_qwen IN (
'deepseek-v4-flash-0731',
'deepseek-v4-pro',
'glm-5.2',
'glm-5.3-flash',
'qwen3.8-flash-next'
));
UPDATE sessions SET model_with_qwen = model;
ALTER TABLE sessions DROP COLUMN model;
ALTER TABLE sessions RENAME COLUMN model_with_qwen TO model;

View File

@@ -148,8 +148,9 @@ pub(crate) struct App {
pub(super) kv_cache_report: KvCacheReport, pub(super) kv_cache_report: KvCacheReport,
last_cache_scan: Instant, last_cache_scan: Instant,
last_http_requests: u64, last_http_requests: u64,
last_ssd_selected_requests: u64, last_ssd_cache_misses: u64,
last_ssd_requested_bytes: u64, last_ssd_cache_evictions: u64,
last_ssd_pread_bytes: u64,
last_ssd_wait_ms: u64, last_ssd_wait_ms: u64,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
generation_service: Option<GenerationService>, generation_service: Option<GenerationService>,
@@ -344,8 +345,9 @@ pub(super) struct MetricsPoint {
pub(super) http_requests_per_second: f32, pub(super) http_requests_per_second: f32,
pub(super) kv_read_bytes_per_second: f32, pub(super) kv_read_bytes_per_second: f32,
pub(super) kv_write_bytes_per_second: f32, pub(super) kv_write_bytes_per_second: f32,
pub(super) ssd_requests_per_second: f32, pub(super) ssd_misses_per_second: f32,
pub(super) ssd_bytes_per_second: f32, pub(super) ssd_evictions_per_second: f32,
pub(super) ssd_pread_bytes_per_second: f32,
pub(super) ssd_wait_ms_per_second: f32, pub(super) ssd_wait_ms_per_second: f32,
} }
@@ -685,8 +687,9 @@ impl App {
kv_cache_report: KvCacheReport::default(), kv_cache_report: KvCacheReport::default(),
last_cache_scan: Instant::now() - CACHE_SCAN_INTERVAL, last_cache_scan: Instant::now() - CACHE_SCAN_INTERVAL,
last_http_requests: 0, last_http_requests: 0,
last_ssd_selected_requests: 0, last_ssd_cache_misses: 0,
last_ssd_requested_bytes: 0, last_ssd_cache_evictions: 0,
last_ssd_pread_bytes: 0,
last_ssd_wait_ms: 0, last_ssd_wait_ms: 0,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
generation_service, generation_service,
@@ -848,8 +851,9 @@ impl App {
kv_cache_report: KvCacheReport::default(), kv_cache_report: KvCacheReport::default(),
last_cache_scan: Instant::now() - CACHE_SCAN_INTERVAL, last_cache_scan: Instant::now() - CACHE_SCAN_INTERVAL,
last_http_requests: 0, last_http_requests: 0,
last_ssd_selected_requests: 0, last_ssd_cache_misses: 0,
last_ssd_requested_bytes: 0, last_ssd_cache_evictions: 0,
last_ssd_pread_bytes: 0,
last_ssd_wait_ms: 0, last_ssd_wait_ms: 0,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
generation_service, generation_service,
@@ -2280,21 +2284,26 @@ impl App {
let kv_write_bytes_per_second = let kv_write_bytes_per_second =
kv_write_bytes as f32 / METRICS_SAMPLE_INTERVAL.as_secs_f32(); kv_write_bytes as f32 / METRICS_SAMPLE_INTERVAL.as_secs_f32();
let sample_seconds = METRICS_SAMPLE_INTERVAL.as_secs_f32(); let sample_seconds = METRICS_SAMPLE_INTERVAL.as_secs_f32();
let ssd_requests_per_second = snapshot let ssd_misses_per_second = snapshot
.ssd_selected_requests .ssd_cache_misses
.saturating_sub(self.last_ssd_selected_requests) .saturating_sub(self.last_ssd_cache_misses) as f32
/ sample_seconds;
let ssd_evictions_per_second = snapshot
.ssd_cache_evictions
.saturating_sub(self.last_ssd_cache_evictions)
as f32 as f32
/ sample_seconds; / sample_seconds;
let ssd_bytes_per_second = snapshot let ssd_pread_bytes_per_second = snapshot
.ssd_requested_bytes .ssd_pread_bytes
.saturating_sub(self.last_ssd_requested_bytes) .saturating_sub(self.last_ssd_pread_bytes)
as f32 as f32
/ sample_seconds; / sample_seconds;
let ssd_wait_ms_per_second = let ssd_wait_ms_per_second =
snapshot.ssd_wait_ms.saturating_sub(self.last_ssd_wait_ms) as f32 / sample_seconds; snapshot.ssd_wait_ms.saturating_sub(self.last_ssd_wait_ms) as f32 / sample_seconds;
self.last_http_requests = snapshot.http_requests; self.last_http_requests = snapshot.http_requests;
self.last_ssd_selected_requests = snapshot.ssd_selected_requests; self.last_ssd_cache_misses = snapshot.ssd_cache_misses;
self.last_ssd_requested_bytes = snapshot.ssd_requested_bytes; self.last_ssd_cache_evictions = snapshot.ssd_cache_evictions;
self.last_ssd_pread_bytes = snapshot.ssd_pread_bytes;
self.last_ssd_wait_ms = snapshot.ssd_wait_ms; self.last_ssd_wait_ms = snapshot.ssd_wait_ms;
self.metrics_history.push_back(MetricsPoint { self.metrics_history.push_back(MetricsPoint {
decode_tokens_per_second: if snapshot.phase == crate::metrics::RuntimePhase::Generating decode_tokens_per_second: if snapshot.phase == crate::metrics::RuntimePhase::Generating
@@ -2307,8 +2316,9 @@ impl App {
http_requests_per_second, http_requests_per_second,
kv_read_bytes_per_second, kv_read_bytes_per_second,
kv_write_bytes_per_second, kv_write_bytes_per_second,
ssd_requests_per_second, ssd_misses_per_second,
ssd_bytes_per_second, ssd_evictions_per_second,
ssd_pread_bytes_per_second,
ssd_wait_ms_per_second, ssd_wait_ms_per_second,
}); });
if self.metrics_history.len() > 120 { if self.metrics_history.len() > 120 {

View File

@@ -250,29 +250,43 @@ impl App {
column![ column![
mini_chart( mini_chart(
&self.metrics_history, &self.metrics_history,
|point| point.ssd_requests_per_second, |point| point.ssd_misses_per_second,
Color::from_rgb8(240, 180, 70), Color::from_rgb8(240, 180, 70),
), ),
row![ row![
text("Selected loads") text("Cache misses")
.size(12) .size(12)
.color(Color::from_rgb8(240, 180, 70)), .color(Color::from_rgb8(240, 180, 70)),
Space::new().width(Length::Fill), Space::new().width(Length::Fill),
text(format!("{:.1}/s", latest.ssd_requests_per_second)) text(format!("{:.1}/s", latest.ssd_misses_per_second))
.size(12) .size(12)
.color(muted_text()), .color(muted_text()),
], ],
mini_chart( mini_chart(
&self.metrics_history, &self.metrics_history,
|point| point.ssd_bytes_per_second, |point| point.ssd_pread_bytes_per_second,
Color::from_rgb8(67, 194, 203), Color::from_rgb8(67, 194, 203),
), ),
row![ row![
text("Requested expert data") text("Direct SSD reads")
.size(12) .size(12)
.color(Color::from_rgb8(67, 194, 203)), .color(Color::from_rgb8(67, 194, 203)),
Space::new().width(Length::Fill), Space::new().width(Length::Fill),
text(format_rate(latest.ssd_bytes_per_second)) text(format_rate(latest.ssd_pread_bytes_per_second))
.size(12)
.color(muted_text()),
],
mini_chart(
&self.metrics_history,
|point| point.ssd_evictions_per_second,
Color::from_rgb8(157, 119, 255),
),
row![
text("Cache evictions")
.size(12)
.color(Color::from_rgb8(157, 119, 255)),
Space::new().width(Length::Fill),
text(format!("{:.1}/s", latest.ssd_evictions_per_second))
.size(12) .size(12)
.color(muted_text()), .color(muted_text()),
], ],

View File

@@ -945,6 +945,50 @@ mod tests {
fs::remove_file(path).unwrap(); fs::remove_file(path).unwrap();
} }
#[test]
fn session_model_migration_preserves_data_and_accepts_catalog() {
let id = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let path = std::env::temp_dir().join(format!("ds4-session-models-{id}.sqlite3"));
let mut database = Database::open(&path).unwrap();
let project = database
.create_project("DS4", "/tmp/ds4-session-models")
.unwrap();
let existing = database
.create_session(
project.id,
"Existing",
PermissionMode::Ai,
crate::model::ModelChoice::DeepSeekV4Flash0731,
)
.unwrap();
database
.connection
.revert_last_migration(MIGRATIONS)
.unwrap();
database
.connection
.run_pending_migrations(MIGRATIONS)
.unwrap();
assert_eq!(
database.load_projects().unwrap()[0].sessions[0].model(),
existing.model()
);
for model in crate::model::MODEL_CHOICES {
let session = database
.create_session(project.id, model.id(), PermissionMode::Ai, model)
.unwrap();
assert_eq!(session.model(), Some(model));
}
drop(database);
fs::remove_file(path).unwrap();
}
#[test] #[test]
fn sessions_are_ordered_by_recent_use_within_sidebar_groups() { fn sessions_are_ordered_by_recent_use_within_sidebar_groups() {
let id = SystemTime::now() let id = SystemTime::now()