mod helpers;
mod request;
mod response;
mod streaming;
use std::sync::{Arc, Mutex};
use serde_json::{Map, Value};
use crate::clients::base::TokenUsage;
use crate::clients::sampling::get_sampling_defaults;
pub struct OllamaClient {
base_url: String,
model: String,
http_client: reqwest::Client,
temperature: Option<f64>,
top_p: Option<f64>,
top_k: Option<i64>,
min_p: Option<f64>,
repeat_penalty: Option<f64>,
presence_penalty: Option<f64>,
timeout_secs: f64,
think: Mutex<bool>,
think_resolved: Mutex<bool>,
num_ctx: Mutex<Option<i64>>,
last_usage: Arc<Mutex<Option<TokenUsage>>>,
sampling_defaults: Option<Map<String, Value>>,
}
impl OllamaClient {
pub fn new(model: impl Into<String>) -> Self {
let model_str = model.into();
let (think, think_resolved) = helpers::detect_think_mode(&model_str);
Self {
base_url: "http://localhost:11434".to_string(),
model: model_str,
http_client: reqwest::Client::new(),
temperature: None,
top_p: None,
top_k: None,
min_p: None,
repeat_penalty: None,
presence_penalty: None,
timeout_secs: 300.0,
think: Mutex::new(think),
think_resolved: Mutex::new(think_resolved),
num_ctx: Mutex::new(None),
last_usage: Arc::new(Mutex::new(None)),
sampling_defaults: None,
}
}
pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = url.into();
self
}
pub fn with_http_client(mut self, client: reqwest::Client) -> Self {
self.http_client = client;
self
}
pub fn with_temperature(mut self, t: f64) -> Self {
self.temperature = Some(t);
self
}
pub fn with_top_p(mut self, v: f64) -> Self {
self.top_p = Some(v);
self
}
pub fn with_top_k(mut self, v: i64) -> Self {
self.top_k = Some(v);
self
}
pub fn with_min_p(mut self, v: f64) -> Self {
self.min_p = Some(v);
self
}
pub fn with_repeat_penalty(mut self, v: f64) -> Self {
self.repeat_penalty = Some(v);
self
}
pub fn with_presence_penalty(mut self, v: f64) -> Self {
self.presence_penalty = Some(v);
self
}
pub fn with_timeout(mut self, s: f64) -> Self {
self.timeout_secs = s;
self
}
pub fn with_think(mut self, think: Option<bool>) -> Self {
match think {
Some(t) => {
self.think = Mutex::new(t);
self.think_resolved = Mutex::new(true);
}
None => {
let (d, r) = helpers::detect_think_mode(&self.model);
self.think = Mutex::new(d);
self.think_resolved = Mutex::new(r);
}
}
self
}
pub fn with_recommended_sampling(mut self, enabled: bool) -> Self {
if enabled {
let d = get_sampling_defaults(&self.model);
if !d.is_empty() {
self.sampling_defaults = Some(d);
}
}
self
}
pub fn is_think_enabled(&self) -> bool {
self.think.lock().map(|g| *g).unwrap_or(false)
}
pub fn is_think_resolved(&self) -> bool {
self.think_resolved.lock().map(|g| *g).unwrap_or(false)
}
pub fn set_num_ctx(&self, ctx: Option<i64>) {
if let Ok(mut g) = self.num_ctx.lock() {
*g = ctx;
}
}
}