Skip to main content

objectiveai_api/chat/completions/upstream/openrouter/request/
chat_completion_create_params.rs

1//! Chat completion request parameters for OpenRouter.
2
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6/// Chat completion request parameters formatted for the OpenRouter API.
7///
8/// Combines parameters from both the Ensemble LLM configuration and the
9/// incoming request to create a complete request for OpenRouter.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ChatCompletionCreateParams {
12    /// Messages for the conversation, including any prefix/suffix from the Ensemble LLM.
13    pub messages: Vec<objectiveai::chat::completions::request::Message>,
14    /// Provider preferences merged from request and Ensemble LLM.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub provider: Option<super::Provider>,
17
18    /// The model identifier from the Ensemble LLM.
19    pub model: String,
20    /// Frequency penalty from Ensemble LLM.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub frequency_penalty: Option<f64>,
23    /// Logit bias from Ensemble LLM.
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub logit_bias: Option<IndexMap<String, i64>>,
26    /// Maximum completion tokens from Ensemble LLM.
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub max_completion_tokens: Option<u64>,
29    /// Presence penalty from Ensemble LLM.
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub presence_penalty: Option<f64>,
32    /// Stop sequences from Ensemble LLM.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub stop: Option<objectiveai::ensemble_llm::Stop>,
35    /// Temperature from Ensemble LLM.
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub temperature: Option<f64>,
38    /// Top-p (nucleus sampling) from Ensemble LLM.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub top_p: Option<f64>,
41    /// Maximum tokens (legacy) from Ensemble LLM.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub max_tokens: Option<u64>,
44    /// Min-p sampling from Ensemble LLM.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub min_p: Option<f64>,
47    /// Reasoning configuration from Ensemble LLM.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub reasoning: Option<objectiveai::ensemble_llm::Reasoning>,
50    /// Repetition penalty from Ensemble LLM.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub repetition_penalty: Option<f64>,
53    /// Top-a sampling from Ensemble LLM.
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub top_a: Option<f64>,
56    /// Top-k sampling from Ensemble LLM.
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub top_k: Option<u64>,
59    /// Verbosity setting from Ensemble LLM.
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub verbosity: Option<objectiveai::ensemble_llm::Verbosity>,
62
63    /// Whether to include log probabilities from request.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub logprobs: Option<bool>,
66    /// Number of top log probabilities to return from request.
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub top_logprobs: Option<u64>,
69    /// Response format specification from request.
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub response_format: Option<objectiveai::chat::completions::request::ResponseFormat>,
72    /// Random seed from request.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub seed: Option<i64>,
75    /// Tool choice configuration from request.
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub tool_choice: Option<objectiveai::chat::completions::request::ToolChoice>,
78    /// Available tools from request.
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub tools: Option<Vec<objectiveai::chat::completions::request::Tool>>,
81    /// Whether to allow parallel tool calls from request.
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub parallel_tool_calls: Option<bool>,
84    /// Prediction hints from request.
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub prediction: Option<objectiveai::chat::completions::request::Prediction>,
87
88    /// Always true for streaming requests.
89    pub stream: bool,
90    /// Stream options for usage inclusion.
91    pub stream_options: super::StreamOptions,
92    /// Usage reporting options.
93    pub usage: super::Usage,
94}
95
96impl ChatCompletionCreateParams {
97    /// Creates request parameters for a chat completion.
98    ///
99    /// Applies the Ensemble LLM's prefix/suffix messages and decoding parameters.
100    pub fn new_for_chat(
101        ensemble_llm: &objectiveai::ensemble_llm::EnsembleLlm,
102        request: &objectiveai::chat::completions::request::ChatCompletionCreateParams,
103    ) -> Self {
104        Self {
105            messages: super::prompt::new_for_chat(
106                ensemble_llm.base.prefix_messages.as_deref(),
107                &request.messages,
108                ensemble_llm.base.suffix_messages.as_deref(),
109            ),
110            provider: super::provider::Provider::new(
111                request.provider,
112                ensemble_llm.base.provider.as_ref(),
113            ),
114            model: ensemble_llm.base.model.clone(),
115            frequency_penalty: ensemble_llm.base.frequency_penalty,
116            logit_bias: ensemble_llm.base.logit_bias.clone(),
117            max_completion_tokens: ensemble_llm.base.max_completion_tokens,
118            presence_penalty: ensemble_llm.base.presence_penalty,
119            stop: ensemble_llm.base.stop.clone(),
120            temperature: ensemble_llm.base.temperature,
121            top_p: ensemble_llm.base.top_p,
122            max_tokens: ensemble_llm.base.max_tokens,
123            min_p: ensemble_llm.base.min_p,
124            reasoning: ensemble_llm.base.reasoning,
125            repetition_penalty: ensemble_llm.base.repetition_penalty,
126            top_a: ensemble_llm.base.top_a,
127            top_k: ensemble_llm.base.top_k,
128            verbosity: ensemble_llm.base.verbosity,
129            logprobs: if let Some(top_logprobs) = request.top_logprobs {
130                Some(top_logprobs > 0)
131            } else {
132                None
133            },
134            top_logprobs: request.top_logprobs,
135            response_format: request.response_format.clone(),
136            seed: request.seed,
137            tool_choice: request.tool_choice.clone(),
138            tools: request.tools.clone(),
139            parallel_tool_calls: request.parallel_tool_calls,
140            prediction: request.prediction.clone(),
141            stream: true,
142            stream_options: super::StreamOptions {
143                include_usage: Some(true),
144            },
145            usage: super::Usage { include: true },
146        }
147    }
148
149    /// Creates request parameters for a vector completion vote.
150    ///
151    /// Transforms the vector completion request into a chat completion that asks
152    /// the LLM to select from labeled response options.
153    pub fn new_for_vector(
154        vector_pfx_indices: &[(String, usize)],
155        ensemble_llm: &objectiveai::ensemble_llm::EnsembleLlm,
156        request: &objectiveai::vector::completions::request::VectorCompletionCreateParams,
157    ) -> Self {
158        Self {
159            messages: super::prompt::new_for_vector(
160                &request.responses,
161                vector_pfx_indices,
162                ensemble_llm.base.output_mode,
163                ensemble_llm.base.prefix_messages.as_deref(),
164                &request.messages,
165                ensemble_llm.base.suffix_messages.as_deref(),
166            ),
167            provider: super::provider::Provider::new(
168                request.provider,
169                ensemble_llm.base.provider.as_ref(),
170            ),
171            model: ensemble_llm.base.model.clone(),
172            frequency_penalty: ensemble_llm.base.frequency_penalty,
173            logit_bias: ensemble_llm.base.logit_bias.clone(),
174            max_completion_tokens: ensemble_llm.base.max_completion_tokens,
175            presence_penalty: ensemble_llm.base.presence_penalty,
176            stop: ensemble_llm.base.stop.clone(),
177            temperature: ensemble_llm.base.temperature,
178            top_p: ensemble_llm.base.top_p,
179            max_tokens: ensemble_llm.base.max_tokens,
180            min_p: ensemble_llm.base.min_p,
181            reasoning: ensemble_llm.base.reasoning,
182            repetition_penalty: ensemble_llm.base.repetition_penalty,
183            top_a: ensemble_llm.base.top_a,
184            top_k: ensemble_llm.base.top_k,
185            verbosity: ensemble_llm.base.verbosity,
186            logprobs: if let Some(top_logprobs) = ensemble_llm.base.top_logprobs {
187                Some(top_logprobs > 0)
188            } else {
189                None
190            },
191            top_logprobs: ensemble_llm.base.top_logprobs,
192            response_format: super::response_format::new_for_vector(
193                vector_pfx_indices,
194                ensemble_llm.base.output_mode,
195                ensemble_llm.base.synthetic_reasoning,
196            ),
197            seed: request.seed,
198            tool_choice: super::tool_choice::new_for_vector(
199                ensemble_llm.base.output_mode,
200                request.tools.as_deref(),
201            ),
202            tools: super::tools::new_for_vector(
203                vector_pfx_indices,
204                ensemble_llm.base.output_mode,
205                ensemble_llm.base.synthetic_reasoning,
206                request.tools.as_deref(),
207            ),
208            parallel_tool_calls: None,
209            prediction: None,
210            stream: true,
211            stream_options: super::StreamOptions {
212                include_usage: Some(true),
213            },
214            usage: super::Usage { include: true },
215        }
216    }
217}