use crate::constants::{DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE};
use crate::prompts;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelConfig {
pub model: String,
#[serde(default = "default_temperature")]
pub temperature: f32,
#[serde(default = "default_max_tokens")]
pub max_tokens: usize,
pub system_prompt: Option<String>,
#[serde(default = "default_thinking_enabled")]
pub thinking_enabled: Option<bool>,
#[serde(skip)]
pub is_subagent: bool,
#[serde(default)]
pub backend_options: HashMap<String, HashMap<String, String>>,
#[serde(skip)]
pub mcp_tools: Vec<serde_json::Value>,
}
impl Default for ModelConfig {
fn default() -> Self {
Self {
model: "ollama/tinyllama".to_string(),
temperature: default_temperature(),
max_tokens: default_max_tokens(),
system_prompt: Some(prompts::get_system_prompt()),
thinking_enabled: Some(true),
is_subagent: false,
backend_options: HashMap::new(),
mcp_tools: Vec::new(),
}
}
}
impl ModelConfig {
pub fn get_backend_option(&self, backend: &str, key: &str) -> Option<&String> {
self.backend_options.get(backend)?.get(key)
}
pub fn get_backend_option_i32(&self, backend: &str, key: &str) -> Option<i32> {
self.get_backend_option(backend, key)?.parse::<i32>().ok()
}
pub fn get_backend_option_bool(&self, backend: &str, key: &str) -> Option<bool> {
self.get_backend_option(backend, key)?.parse::<bool>().ok()
}
pub fn set_backend_option(&mut self, backend: String, key: String, value: String) {
self.backend_options
.entry(backend)
.or_default()
.insert(key, value);
}
pub fn from_app_config(config: &crate::app::Config, model_id: &str) -> Self {
let mut mc = Self {
model: model_id.to_string(),
temperature: config.default_model.temperature,
max_tokens: config.default_model.max_tokens,
..Self::default()
};
if let Some(v) = config.ollama.num_gpu {
mc.set_backend_option("ollama".into(), "num_gpu".into(), v.to_string());
}
if let Some(v) = config.ollama.num_ctx {
mc.set_backend_option("ollama".into(), "num_ctx".into(), v.to_string());
}
if let Some(v) = config.ollama.num_thread {
mc.set_backend_option("ollama".into(), "num_thread".into(), v.to_string());
}
if let Some(v) = config.ollama.numa {
mc.set_backend_option("ollama".into(), "numa".into(), v.to_string());
}
mc
}
pub fn ollama_options(&self) -> OllamaOptions {
OllamaOptions {
num_gpu: self.get_backend_option_i32("ollama", "num_gpu"),
num_thread: self.get_backend_option_i32("ollama", "num_thread"),
num_ctx: self.get_backend_option_i32("ollama", "num_ctx"),
numa: self.get_backend_option_bool("ollama", "numa"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct OllamaOptions {
pub num_gpu: Option<i32>,
pub num_thread: Option<i32>,
pub num_ctx: Option<i32>,
pub numa: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackendConfig {
#[serde(default = "default_ollama_url")]
pub ollama_url: String,
#[serde(default = "default_timeout")]
pub timeout_secs: u64,
#[serde(default = "default_max_idle")]
pub max_idle_per_host: usize,
}
impl Default for BackendConfig {
fn default() -> Self {
Self {
ollama_url: default_ollama_url(),
timeout_secs: default_timeout(),
max_idle_per_host: default_max_idle(),
}
}
}
fn default_temperature() -> f32 {
DEFAULT_TEMPERATURE
}
fn default_max_tokens() -> usize {
DEFAULT_MAX_TOKENS
}
fn default_ollama_url() -> String {
std::env::var("OLLAMA_HOST").unwrap_or_else(|_| "http://localhost:11434".to_string())
}
fn default_timeout() -> u64 {
10
}
fn default_max_idle() -> usize {
10
}
fn default_thinking_enabled() -> Option<bool> {
Some(true)
}