From ecedb1c054d17cb43228d11dd58f7b19e5622866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Cipriani=20Bandarra?= Date: Sun, 28 Dec 2025 20:52:01 +0000 Subject: [PATCH] Add Options to generate and chat --- src/types/chat.rs | 16 +++++++ src/types/common.rs | 100 ++++++++++++++++++++++++++++++++++++++++++ src/types/generate.rs | 12 ++++- 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/types/chat.rs b/src/types/chat.rs index 5af6f1f..76806e9 100644 --- a/src/types/chat.rs +++ b/src/types/chat.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +use crate::types::common::Options; + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Role { @@ -17,8 +19,16 @@ pub struct Message { #[derive(Debug, Serialize, Deserialize)] pub struct ChatRequest { pub model: String, + + #[serde(skip_serializing_if = "Vec::is_empty")] pub messages: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] pub stream: Option, + + /// Runtime options that control text generation + #[serde(skip_serializing_if = "Option::is_none")] + pub options: Option, } impl ChatRequest { @@ -46,6 +56,7 @@ impl ChatRequestBuilder { model: model.into(), messages: vec![], stream: None, + options: None, }, } } @@ -55,6 +66,11 @@ impl ChatRequestBuilder { self } + pub fn options(mut self, options: Options) -> Self { + self.chat_request.options = Some(options); + self + } + pub fn build(self) -> ChatRequest { self.chat_request } diff --git a/src/types/common.rs b/src/types/common.rs index aba1d1f..39c7598 100644 --- a/src/types/common.rs +++ b/src/types/common.rs @@ -23,3 +23,103 @@ pub enum ThinkLevel { Medium, Low, } + +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Options { + /// Random seed used for reproducible outputs + #[serde(skip_serializing_if = "Option::is_none")] + pub seed: Option, + + /// Controls randomness in generation (higher = more random) + #[serde(skip_serializing_if = "Option::is_none")] + pub temperature: Option, + + /// Limits next token selection to the K most likely + #[serde(skip_serializing_if = "Option::is_none")] + pub top_k: Option, + + /// Cumulative probability threshold for nucleus sampling + #[serde(skip_serializing_if = "Option::is_none")] + pub top_p: Option, + + /// Minimum probability threshold for token selection + #[serde(skip_serializing_if = "Option::is_none")] + pub min_p: Option, + + /// Stop sequences that will halt generation + #[serde(skip_serializing_if = "Option::is_none")] + pub stop: Option, + + /// Context length size (number of tokens) + #[serde(skip_serializing_if = "Option::is_none")] + pub num_ctx: Option, + + /// Maximum number of tokens to generate + #[serde(skip_serializing_if = "Option::is_none")] + pub num_predict: Option, +} + +impl Options { + pub fn builder() -> OptionsBuilder { + OptionsBuilder { + options: Options::default(), + } + } +} + +pub struct OptionsBuilder { + options: Options, +} + +impl OptionsBuilder { + pub fn seed(mut self, seed: u64) -> Self { + self.options.seed = Some(seed); + self + } + + pub fn temperature(mut self, temperature: f32) -> Self { + self.options.temperature = Some(temperature); + self + } + + pub fn top_k(mut self, top_k: u32) -> Self { + self.options.top_k = Some(top_k); + self + } + + pub fn top_p(mut self, top_p: f32) -> Self { + self.options.top_p = Some(top_p); + self + } + + pub fn min_p(mut self, min_p: f32) -> Self { + self.options.min_p = Some(min_p); + self + } + + pub fn stop(mut self, stop: Stop) -> Self { + self.options.stop = Some(stop); + self + } + + pub fn num_ctx(mut self, num_ctx: u32) -> Self { + self.options.num_ctx = Some(num_ctx); + self + } + + pub fn num_predict(mut self, num_predict: u32) -> Self { + self.options.num_predict = Some(num_predict); + self + } + + pub fn build(self) -> Options { + self.options + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum Stop { + Single(String), + Multiple(Vec), +} diff --git a/src/types/generate.rs b/src/types/generate.rs index cab2afe..b2d9b1f 100644 --- a/src/types/generate.rs +++ b/src/types/generate.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::types::common::Think; +use crate::types::common::{Options, Think}; #[derive(Debug, Serialize, Deserialize)] pub struct GenerateRequest { @@ -38,6 +38,10 @@ pub struct GenerateRequest { /// (true/false) or a string ("high", "medium", "low") for supported models. #[serde(skip_serializing_if = "Option::is_none")] pub think: Option, + + /// Runtime options that control text generation + #[serde(skip_serializing_if = "Option::is_none")] + pub options: Option, } impl GenerateRequest { @@ -62,6 +66,7 @@ impl GenerateRequestBuilder { images: vec![], format: None, think: None, + options: None, }, } } @@ -101,6 +106,11 @@ impl GenerateRequestBuilder { self } + pub fn options(mut self, options: Options) -> Self { + self.generate_request.options = Some(options); + self + } + pub fn build(self) -> GenerateRequest { self.generate_request }