use super::Difficulty;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VlmBenchConfig {
pub endpoint: String,
pub model: String,
pub max_concurrent: usize,
pub max_tokens: usize,
pub temperature: f32,
pub timeout_secs: u64,
pub levels: Vec<Difficulty>,
pub fixtures_dir: PathBuf,
pub output_dir: PathBuf,
}
impl Default for VlmBenchConfig {
fn default() -> Self {
Self {
endpoint: "http://192.168.1.99:1234/v1".into(),
model: "qwen/qwen3.5-9b".into(),
max_concurrent: 4,
max_tokens: 4096,
temperature: 0.2,
timeout_secs: 120,
levels: vec![
Difficulty::Easy,
Difficulty::Medium,
Difficulty::Hard,
Difficulty::VeryHard,
Difficulty::Extreme,
Difficulty::Mega,
],
fixtures_dir: PathBuf::from("vlm_fixtures"),
output_dir: PathBuf::from("vlm_results"),
}
}
}
impl VlmBenchConfig {
pub fn new(endpoint: impl Into<String>, model: impl Into<String>) -> Self {
Self {
endpoint: endpoint.into(),
model: model.into(),
..Default::default()
}
}
pub fn with_max_difficulty(mut self, max: Difficulty) -> Self {
self.levels.retain(|d| *d <= max);
self
}
pub fn with_concurrency(mut self, n: usize) -> Self {
self.max_concurrent = n.max(1);
self
}
pub fn validate(&self) -> Result<(), String> {
if self.endpoint.is_empty() {
return Err("endpoint must not be empty".into());
}
if self.model.is_empty() {
return Err("model must not be empty".into());
}
if self.max_concurrent == 0 {
return Err("max_concurrent must be >= 1".into());
}
if self.max_tokens == 0 {
return Err("max_tokens must be >= 1".into());
}
if self.timeout_secs == 0 {
return Err("timeout_secs must be >= 1".into());
}
if self.levels.is_empty() {
return Err("at least one difficulty level must be selected".into());
}
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::field_reassign_with_default)]
#[path = "../../tests/unit/vlm_bench/config/config_test.rs"]
mod tests;