Skip to main content

vv_agent/
model_settings.rs

1use std::collections::BTreeMap;
2use std::time::Duration;
3
4use serde::{Deserialize, Serialize};
5use serde_json::{Map, Value};
6
7#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
8pub struct ModelSettings {
9    pub temperature: Option<f32>,
10    pub top_p: Option<f32>,
11    pub max_output_tokens: Option<u32>,
12    pub tool_choice: Option<ToolChoice>,
13    pub parallel_tool_calls: Option<bool>,
14    pub reasoning: Option<Value>,
15    pub response_format: Option<ResponseFormat>,
16    #[serde(skip)]
17    pub timeout: Option<Duration>,
18    pub retry: Option<RetryPolicy>,
19    pub extra_headers: BTreeMap<String, String>,
20    pub extra_body: Map<String, Value>,
21}
22
23impl ModelSettings {
24    pub fn builder() -> ModelSettingsBuilder {
25        ModelSettingsBuilder::default()
26    }
27
28    pub fn merge(&self, override_settings: &ModelSettings) -> ModelSettings {
29        let mut merged = self.clone();
30        if override_settings.temperature.is_some() {
31            merged.temperature = override_settings.temperature;
32        }
33        if override_settings.top_p.is_some() {
34            merged.top_p = override_settings.top_p;
35        }
36        if override_settings.max_output_tokens.is_some() {
37            merged.max_output_tokens = override_settings.max_output_tokens;
38        }
39        if override_settings.tool_choice.is_some() {
40            merged.tool_choice = override_settings.tool_choice.clone();
41        }
42        if override_settings.parallel_tool_calls.is_some() {
43            merged.parallel_tool_calls = override_settings.parallel_tool_calls;
44        }
45        if override_settings.reasoning.is_some() {
46            merged.reasoning = override_settings.reasoning.clone();
47        }
48        if override_settings.response_format.is_some() {
49            merged.response_format = override_settings.response_format.clone();
50        }
51        if override_settings.timeout.is_some() {
52            merged.timeout = override_settings.timeout;
53        }
54        if override_settings.retry.is_some() {
55            merged.retry = override_settings.retry.clone();
56        }
57        merged
58            .extra_headers
59            .extend(override_settings.extra_headers.clone());
60        merged
61            .extra_body
62            .extend(override_settings.extra_body.clone());
63        merged
64    }
65
66    pub fn to_value(&self) -> Value {
67        serde_json::to_value(self).unwrap_or(Value::Null)
68    }
69}
70
71#[derive(Debug, Clone, Default)]
72pub struct ModelSettingsBuilder {
73    settings: ModelSettings,
74}
75
76impl ModelSettingsBuilder {
77    pub fn temperature(mut self, temperature: f32) -> Self {
78        self.settings.temperature = Some(temperature);
79        self
80    }
81
82    pub fn top_p(mut self, top_p: f32) -> Self {
83        self.settings.top_p = Some(top_p);
84        self
85    }
86
87    pub fn max_output_tokens(mut self, max_output_tokens: u32) -> Self {
88        self.settings.max_output_tokens = Some(max_output_tokens);
89        self
90    }
91
92    pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
93        self.settings.tool_choice = Some(tool_choice);
94        self
95    }
96
97    pub fn parallel_tool_calls(mut self, parallel_tool_calls: bool) -> Self {
98        self.settings.parallel_tool_calls = Some(parallel_tool_calls);
99        self
100    }
101
102    pub fn reasoning(mut self, reasoning: Value) -> Self {
103        self.settings.reasoning = Some(reasoning);
104        self
105    }
106
107    pub fn response_format(mut self, response_format: ResponseFormat) -> Self {
108        self.settings.response_format = Some(response_format);
109        self
110    }
111
112    pub fn timeout(mut self, timeout: Duration) -> Self {
113        self.settings.timeout = Some(timeout);
114        self
115    }
116
117    pub fn retry(mut self, retry: RetryPolicy) -> Self {
118        self.settings.retry = Some(retry);
119        self
120    }
121
122    pub fn extra_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
123        self.settings.extra_headers.insert(key.into(), value.into());
124        self
125    }
126
127    pub fn extra_body(mut self, key: impl Into<String>, value: Value) -> Self {
128        self.settings.extra_body.insert(key.into(), value);
129        self
130    }
131
132    pub fn build(self) -> ModelSettings {
133        self.settings
134    }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138#[serde(rename_all = "snake_case")]
139pub enum ToolChoice {
140    Auto,
141    None,
142    Required,
143    Tool(String),
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(tag = "type", rename_all = "snake_case")]
148pub enum ResponseFormat {
149    Text,
150    JsonObject,
151    JsonSchema { schema: Value },
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
155pub struct RetryPolicy {
156    pub max_retries: u32,
157}
158
159impl RetryPolicy {
160    pub fn new(max_retries: u32) -> Self {
161        Self { max_retries }
162    }
163}