gglib_core/ports/benchmark.rs
1//! Benchmark repository port definition.
2//!
3//! Defines the interface for persisting benchmark runs and results.
4//! Implementations live in `gglib-db`; this trait contains only domain types.
5
6use async_trait::async_trait;
7
8use super::RepositoryError;
9use crate::domain::{
10 BenchmarkRun, BenchmarkRunType, ModelBenchmarkSummary, ModelCompareResult, ModelPerfResult,
11 TuneCandidateResult,
12};
13
14/// Repository interface for benchmark persistence.
15///
16/// Implementations are responsible for:
17/// - Creating and updating run records
18/// - Storing per-model compare and perf results
19/// - Upserting `model_benchmark_summaries` in the same transaction as each
20/// result save, so the model list query always has fresh summary data
21/// without extra round-trips
22#[async_trait]
23pub trait BenchmarkRepositoryPort: Send + Sync {
24 /// Create a new benchmark run record in `Running` status.
25 ///
26 /// Returns the auto-assigned run ID.
27 async fn create_run(
28 &self,
29 run_type: BenchmarkRunType,
30 model_ids: &[i64],
31 prompt_text: Option<&str>,
32 system_prompt: Option<&str>,
33 config_json: Option<&str>,
34 ) -> Result<i64, RepositoryError>;
35
36 /// Mark a run as `Complete` and record the completion timestamp.
37 async fn complete_run(&self, run_id: i64) -> Result<(), RepositoryError>;
38
39 /// Mark a run as `Failed` and record the error message.
40 async fn fail_run(&self, run_id: i64, error: &str) -> Result<(), RepositoryError>;
41
42 /// Persist a compare result and upsert the model's benchmark summary.
43 ///
44 /// Both the result INSERT and the summary upsert happen in the same
45 /// database transaction to keep the denormalised summary consistent.
46 ///
47 /// Returns the auto-assigned result ID.
48 async fn save_compare_result(
49 &self,
50 result: &ModelCompareResult,
51 run_id: i64,
52 ) -> Result<i64, RepositoryError>;
53
54 /// Persist a perf result and upsert the model's benchmark summary.
55 ///
56 /// Both the result INSERT and the summary upsert happen in the same
57 /// database transaction to keep the denormalised summary consistent.
58 ///
59 /// Returns the auto-assigned result ID.
60 async fn save_perf_result(
61 &self,
62 result: &ModelPerfResult,
63 run_id: i64,
64 ) -> Result<i64, RepositoryError>;
65
66 /// List benchmark runs, most recent first.
67 async fn list_runs(
68 &self,
69 limit: i64,
70 offset: i64,
71 ) -> Result<Vec<BenchmarkRun>, RepositoryError>;
72
73 /// Get a single benchmark run by ID.
74 async fn get_run(&self, run_id: i64) -> Result<Option<BenchmarkRun>, RepositoryError>;
75
76 /// Get compare results for one model, most recent first.
77 async fn get_model_compare_history(
78 &self,
79 model_id: i64,
80 limit: i64,
81 ) -> Result<Vec<ModelCompareResult>, RepositoryError>;
82
83 /// Get perf results for one model, most recent first.
84 async fn get_model_perf_history(
85 &self,
86 model_id: i64,
87 limit: i64,
88 ) -> Result<Vec<ModelPerfResult>, RepositoryError>;
89
90 /// Get the denormalised benchmark summary for one model.
91 ///
92 /// Returns `None` if no benchmark has been run for this model.
93 async fn get_model_summary(
94 &self,
95 model_id: i64,
96 ) -> Result<Option<ModelBenchmarkSummary>, RepositoryError>;
97
98 /// Persist one tune candidate's result.
99 ///
100 /// Unlike compare/perf, tune results do not upsert
101 /// `model_benchmark_summaries` — a candidate's `composite_score` is only
102 /// meaningful relative to the other candidates in the same run, so it is
103 /// not a useful cross-run "best" figure to denormalise.
104 ///
105 /// Returns the auto-assigned result ID.
106 async fn save_tune_result(
107 &self,
108 result: &TuneCandidateResult,
109 run_id: i64,
110 model_id: i64,
111 ) -> Result<i64, RepositoryError>;
112
113 /// Get tune candidate results for one model, most recent first.
114 async fn get_model_tune_history(
115 &self,
116 model_id: i64,
117 limit: i64,
118 ) -> Result<Vec<TuneCandidateResult>, RepositoryError>;
119}