1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
//! Generation options and tool definitions
use super::Headers;
use super::cache::CacheControl;
use super::cache_strategy::CacheStrategy;
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// Options for generation requests
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GenerateOptions {
/// Sampling temperature (0.0 to 2.0)
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
/// Maximum tokens to generate
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
/// Nucleus sampling parameter (0.0 to 1.0)
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
/// Sequences where generation should stop
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_sequences: Option<Vec<String>>,
/// Available tools/functions
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Tool>>,
/// How the model should choose tools
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
/// Frequency penalty (-2.0 to 2.0)
#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f32>,
/// Presence penalty (-2.0 to 2.0)
#[serde(skip_serializing_if = "Option::is_none")]
pub presence_penalty: Option<f32>,
/// Custom HTTP headers
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<Headers>,
/// Session ID for request correlation and caching
///
/// For OpenAI: Used as `prompt_cache_key` to improve cache routing
/// For other providers: Used for logging/telemetry correlation
///
/// # Example
///
/// ```rust
/// use stakai::GenerateOptions;
///
/// let options = GenerateOptions::default()
/// .with_session_id("session-12345");
/// ```
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
/// Caching strategy for this request
///
/// Controls how cache breakpoints are applied. Defaults to `CacheStrategy::Auto`
/// which applies provider-optimized caching.
///
/// # Example
///
/// ```rust
/// use stakai::{GenerateOptions, CacheStrategy};
///
/// // Disable caching for this request
/// let options = GenerateOptions::default()
/// .with_cache_strategy(CacheStrategy::None);
/// ```
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_strategy: Option<CacheStrategy>,
}
impl GenerateOptions {
/// Create new default options
pub fn new() -> Self {
Self::default()
}
/// Set temperature
pub fn temperature(mut self, temperature: f32) -> Self {
self.temperature = Some(temperature);
self
}
/// Set max tokens
pub fn max_tokens(mut self, max_tokens: u32) -> Self {
self.max_tokens = Some(max_tokens);
self
}
/// Set top_p
pub fn top_p(mut self, top_p: f32) -> Self {
self.top_p = Some(top_p);
self
}
/// Add stop sequence
pub fn add_stop_sequence(mut self, sequence: impl Into<String>) -> Self {
self.stop_sequences
.get_or_insert_with(Vec::new)
.push(sequence.into());
self
}
/// Add tool
pub fn add_tool(mut self, tool: Tool) -> Self {
self.tools.get_or_insert_with(Vec::new).push(tool);
self
}
/// Set tool choice
pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
self.tool_choice = Some(choice);
self
}
/// Set custom headers
pub fn headers(mut self, headers: Headers) -> Self {
self.headers = Some(headers);
self
}
/// Add a single header
pub fn add_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers
.get_or_insert_with(Headers::new)
.insert(key, value);
self
}
/// Set session ID for caching and correlation
///
/// For OpenAI, this can be used as the `prompt_cache_key` which improves
/// cache hit rates by influencing request routing.
pub fn with_session_id(mut self, session_id: impl Into<String>) -> Self {
self.session_id = Some(session_id.into());
self
}
/// Set caching strategy
///
/// Override the default automatic caching behavior.
///
/// # Example
///
/// ```rust
/// use stakai::{GenerateOptions, CacheStrategy};
///
/// // Custom Anthropic caching: only cache system, 3 tail messages
/// let options = GenerateOptions::default()
/// .with_cache_strategy(CacheStrategy::anthropic(false, true, 3));
/// ```
pub fn with_cache_strategy(mut self, strategy: CacheStrategy) -> Self {
self.cache_strategy = Some(strategy);
self
}
/// Get the effective cache strategy (defaults to Auto if not set)
pub fn effective_cache_strategy(&self) -> CacheStrategy {
self.cache_strategy.clone().unwrap_or_default()
}
}
/// Provider-specific options for tool definitions
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ToolProviderOptions {
/// Anthropic-specific tool options
#[serde(skip_serializing_if = "Option::is_none")]
pub anthropic: Option<AnthropicToolOptions>,
}
impl ToolProviderOptions {
/// Create Anthropic-specific options with cache control
pub fn anthropic_cache(cache_control: CacheControl) -> Self {
Self {
anthropic: Some(AnthropicToolOptions {
cache_control: Some(cache_control),
}),
}
}
}
/// Anthropic-specific tool options
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AnthropicToolOptions {
/// Cache control for this tool (Anthropic prompt caching)
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_control: Option<CacheControl>,
}
/// A tool/function definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
/// Tool type (currently only "function")
#[serde(rename = "type")]
pub tool_type: String,
/// Function definition
pub function: ToolFunction,
/// Provider-specific options (e.g., cache control for Anthropic)
#[serde(skip_serializing_if = "Option::is_none")]
pub provider_options: Option<ToolProviderOptions>,
}
impl Tool {
/// Create a new function tool
pub fn function(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
tool_type: "function".to_string(),
function: ToolFunction {
name: name.into(),
description: description.into(),
parameters: Value::Object(Default::default()),
},
provider_options: None,
}
}
/// Set function parameters (JSON Schema)
pub fn parameters(mut self, parameters: Value) -> Self {
self.function.parameters = parameters;
self
}
/// Add Anthropic cache control to this tool
///
/// # Example
///
/// ```rust
/// use stakai::{Tool, CacheControl};
/// use serde_json::json;
///
/// let tool = Tool::function("search", "Search documents")
/// .parameters(json!({"type": "object", "properties": {}}))
/// .with_cache_control(CacheControl::ephemeral());
/// ```
pub fn with_cache_control(mut self, cache_control: CacheControl) -> Self {
self.provider_options = Some(ToolProviderOptions::anthropic_cache(cache_control));
self
}
/// Add provider-specific options to this tool
pub fn with_provider_options(mut self, options: ToolProviderOptions) -> Self {
self.provider_options = Some(options);
self
}
/// Get the cache control from provider options (if set for Anthropic)
pub fn cache_control(&self) -> Option<&CacheControl> {
self.provider_options
.as_ref()
.and_then(|opts| opts.anthropic.as_ref())
.and_then(|anthropic| anthropic.cache_control.as_ref())
}
}
/// Function definition for a tool
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolFunction {
/// Function name
pub name: String,
/// Function description
pub description: String,
/// Function parameters (JSON Schema)
pub parameters: Value,
}
/// How the model should choose tools
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ToolChoice {
/// Auto-select tools
Auto,
/// Never use tools
None,
/// Always use a specific tool
Required { name: String },
}