Skip to main content

aimux_core/
reranking_model.rs

1//! The `RerankingModel` trait — the provider-facing interface for reranking.
2//!
3//! Aligned with Vercel AI SDK `RerankingModelV4`
4//! (`reference/ai/packages/provider/src/reranking-model/v4/`).
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use ts_rs::TS;
9
10use crate::error::AiMuxError;
11use crate::shared::{SharedHeaders, SharedProviderMetadata, SharedProviderOptions, Warning};
12use crate::{AbortSignal, retry, timeout};
13
14/// Documents to rerank: either a list of texts or a list of JSON objects.
15///
16/// Aligned with V4 `RerankingModelV4CallOptions.documents`.
17#[derive(Debug, Clone, Serialize, Deserialize, TS)]
18#[ts(export)]
19pub enum RerankingDocuments {
20    /// A list of plain-text documents.
21    Text { values: Vec<String> },
22    /// A list of JSON-object documents.
23    Object { values: Vec<serde_json::Value> },
24}
25
26/// Options passed to [`RerankingModel::do_rerank`].
27///
28/// Aligned with V4 `RerankingModelV4CallOptions`.
29#[derive(Debug, Clone, Serialize, Deserialize, TS)]
30#[ts(export)]
31pub struct RerankingCallOptions {
32    /// Documents to rerank.
33    pub documents: RerankingDocuments,
34
35    /// The query to rerank the documents against.
36    pub query: String,
37
38    /// Optional limit: return only the top `n` documents.
39    pub top_n: Option<u32>,
40
41    /// Abort signal for cancelling the operation.
42    #[serde(skip)]
43    #[ts(skip)]
44    pub abort_signal: Option<AbortSignal>,
45
46    /// Per-call retry override. `None` uses the model default.
47    pub max_retries: Option<u32>,
48
49    /// Per-call operation timeout.
50    pub timeout: Option<crate::options::TimeoutConfiguration>,
51
52    /// Additional provider-specific options, keyed by provider name.
53    pub provider_options: Option<SharedProviderOptions>,
54
55    /// Additional HTTP headers to send with the request.
56    pub headers: Option<SharedHeaders>,
57}
58
59impl RerankingCallOptions {
60    /// Create options with text documents and a query.
61    pub fn new(query: impl Into<String>, documents: RerankingDocuments) -> Self {
62        Self {
63            documents,
64            query: query.into(),
65            top_n: None,
66            abort_signal: None,
67            max_retries: None,
68            timeout: None,
69            provider_options: None,
70            headers: None,
71        }
72    }
73}
74
75/// A single reranked entry: the original index and its relevance score.
76#[derive(Debug, Clone, Serialize, Deserialize, TS)]
77#[ts(export)]
78pub struct RerankingRank {
79    /// The index of the document in the original list (before reranking).
80    pub index: u32,
81    /// The relevance score of the document after reranking.
82    pub relevance_score: f64,
83}
84
85/// The result of [`RerankingModel::do_rerank`].
86///
87/// Aligned with V4 `RerankingModelV4Result`.
88#[derive(Debug, Clone, Serialize, Deserialize, TS)]
89#[ts(export)]
90pub struct RerankingResult {
91    /// Ordered list of reranked documents, sorted by descending relevance
92    /// score. Each entry's `index` refers to the position in the original
93    /// `documents` list.
94    pub ranking: Vec<RerankingRank>,
95
96    /// Additional provider-specific metadata, keyed by provider name.
97    pub provider_metadata: Option<SharedProviderMetadata>,
98
99    /// Warnings for the call, e.g. unsupported settings.
100    pub warnings: Option<Vec<Warning>>,
101
102    /// Optional response information for debugging.
103    pub response: Option<RerankingResponse>,
104}
105
106/// Optional response information for a reranking call.
107#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
108#[ts(export)]
109pub struct RerankingResponse {
110    /// ID for the generated response, if the provider sends one.
111    pub id: Option<String>,
112    /// Timestamp for the start of the generated response (ISO 8601 string).
113    pub timestamp: Option<String>,
114    /// The ID of the model that was used to generate the response.
115    pub model_id: Option<String>,
116    /// Response headers.
117    pub headers: Option<SharedHeaders>,
118    /// Response body (opaque JSON).
119    pub body: Option<serde_json::Value>,
120}
121
122/// The unified reranking model trait (provider-facing).
123///
124/// Aligned with V4 `RerankingModelV4`.
125#[async_trait]
126pub trait RerankingModel: Send + Sync {
127    /// Specification version (always `"v4"`).
128    fn specification_version(&self) -> &'static str {
129        "v4"
130    }
131
132    /// Provider name, e.g. `"cohere"`.
133    fn provider(&self) -> &str;
134
135    /// Provider-specific model ID, e.g. `"rerank-english-v3.0"`.
136    fn model_id(&self) -> &str;
137
138    fn retry_config(&self) -> crate::retry::RetryConfig {
139        crate::retry::RetryConfig::default()
140    }
141
142    /// Rerank a list of documents using the query.
143    ///
144    /// Naming: the `do_` prefix prevents accidental direct usage by users.
145    async fn do_rerank(
146        &self,
147        options: &RerankingCallOptions,
148    ) -> Result<RerankingResult, AiMuxError>;
149}
150
151/// User-facing reranking with Core-owned retry and timeout.
152///
153/// # Errors
154///
155/// Returns the provider failure, retry exhaustion, timeout, or caller abort.
156pub async fn rerank(
157    model: &dyn RerankingModel,
158    options: RerankingCallOptions,
159) -> Result<RerankingResult, AiMuxError> {
160    let timeout = timeout::OperationTimeout::new(options.timeout.unwrap_or_default())?;
161    let abort_signal = options.abort_signal.clone();
162    let retries = retry::prepare_retries(
163        options.max_retries,
164        model.retry_config(),
165        abort_signal.clone(),
166    );
167    timeout::run(
168        retries.retry(|| model.do_rerank(&options)),
169        abort_signal.as_ref(),
170        timeout,
171    )
172    .await
173}