openai_protocol/
rerank.rs1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use validator::Validate;
6
7use super::common::{default_true, GenerationRequest, StringOrArray, UsageInfo};
8
9fn default_rerank_object() -> String {
10 "rerank".to_string()
11}
12
13fn current_timestamp() -> i64 {
14 std::time::SystemTime::now()
15 .duration_since(std::time::UNIX_EPOCH)
16 .unwrap_or_else(|_| std::time::Duration::from_secs(0))
17 .as_secs() as i64
18}
19
20#[derive(Debug, Clone, Deserialize, Serialize, Validate, schemars::JsonSchema)]
25#[validate(schema(function = "validate_rerank_request"))]
26pub struct RerankRequest {
27 #[validate(custom(function = "validate_query"))]
29 pub query: String,
30
31 #[validate(custom(function = "validate_documents"))]
33 pub documents: Vec<String>,
34
35 pub model: String,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 #[validate(range(min = 1))]
41 pub top_k: Option<usize>,
42
43 #[serde(default = "default_true")]
45 pub return_documents: bool,
46
47 pub rid: Option<StringOrArray>,
50
51 pub user: Option<String>,
53}
54
55impl GenerationRequest for RerankRequest {
56 fn rid(&self) -> Option<&str> {
57 self.rid.as_ref().and_then(StringOrArray::first)
58 }
59
60 fn get_model(&self) -> Option<&str> {
61 Some(&self.model)
62 }
63
64 fn is_stream(&self) -> bool {
65 false }
67
68 fn extract_text_for_routing(&self) -> String {
69 self.query.clone()
70 }
71}
72
73impl super::validated::Normalizable for RerankRequest {
74 }
76
77fn validate_query(query: &str) -> Result<(), validator::ValidationError> {
83 if query.trim().is_empty() {
84 return Err(validator::ValidationError::new("query cannot be empty"));
85 }
86 Ok(())
87}
88
89fn validate_documents(documents: &[String]) -> Result<(), validator::ValidationError> {
91 if documents.is_empty() {
92 return Err(validator::ValidationError::new(
93 "documents list cannot be empty",
94 ));
95 }
96 Ok(())
97}
98
99#[expect(
101 clippy::unnecessary_wraps,
102 reason = "validator crate requires Result return type"
103)]
104fn validate_rerank_request(req: &RerankRequest) -> Result<(), validator::ValidationError> {
105 if let Some(k) = req.top_k {
107 if k > req.documents.len() {
108 tracing::warn!(
109 "top_k ({}) is greater than number of documents ({})",
110 k,
111 req.documents.len()
112 );
113 }
114 }
115 Ok(())
116}
117
118impl RerankRequest {
119 pub fn effective_top_k(&self) -> usize {
121 self.top_k.unwrap_or(self.documents.len())
122 }
123}
124
125#[serde_with::skip_serializing_none]
127#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
128pub struct RerankResult {
129 pub score: f32,
131
132 pub document: Option<String>,
134
135 pub index: usize,
137
138 pub meta_info: Option<HashMap<String, Value>>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
144pub struct RerankResponse {
145 pub results: Vec<RerankResult>,
147
148 pub model: String,
150
151 pub usage: Option<UsageInfo>,
153
154 #[serde(default = "default_rerank_object")]
156 pub object: String,
157
158 pub id: Option<StringOrArray>,
160
161 pub created: i64,
163}
164
165impl RerankResponse {
166 pub fn new(
168 results: Vec<RerankResult>,
169 model: String,
170 request_id: Option<StringOrArray>,
171 ) -> Self {
172 RerankResponse {
173 results,
174 model,
175 usage: None,
176 object: default_rerank_object(),
177 id: request_id,
178 created: current_timestamp(),
179 }
180 }
181
182 pub fn apply_top_k(&mut self, k: usize) {
184 self.results.truncate(k);
185 }
186
187 pub fn drop_documents(&mut self) {
189 for result in &mut self.results {
190 result.document = None;
191 }
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
198pub struct V1RerankReqInput {
199 pub query: String,
200 pub documents: Vec<String>,
201}
202
203impl From<V1RerankReqInput> for RerankRequest {
205 fn from(v1: V1RerankReqInput) -> Self {
206 RerankRequest {
207 query: v1.query,
208 documents: v1.documents,
209 model: super::UNKNOWN_MODEL_ID.to_string(),
210 top_k: None,
211 return_documents: true,
212 rid: None,
213 user: None,
214 }
215 }
216}