Skip to main content

oar_ocr_core/core/traits/
adapter.rs

1//! Model adapter trait definitions for the OCR pipeline.
2//!
3//! This module defines the `ModelAdapter` trait and related types that adapt
4//! various model implementations to conform to task interfaces. Adapters handle
5//! preprocessing, inference, and postprocessing for specific models.
6
7use super::task::{Task, TaskSchema, TaskType};
8use crate::core::OCRError;
9use std::fmt::Debug;
10
11/// Information about a model adapter.
12#[derive(Debug, Clone)]
13pub struct AdapterInfo {
14    /// Name of the model (e.g., "DB", "CRNN", "RT-DETR")
15    pub model_name: String,
16    /// Task type this adapter supports
17    pub task_type: TaskType,
18    /// Description of the model
19    pub description: String,
20}
21
22impl AdapterInfo {
23    /// Creates a new adapter info.
24    pub fn new(
25        model_name: impl Into<String>,
26        task_type: TaskType,
27        description: impl Into<String>,
28    ) -> Self {
29        Self {
30            model_name: model_name.into(),
31            task_type,
32            description: description.into(),
33        }
34    }
35}
36
37/// Core trait for model adapters.
38///
39/// Adapters bridge the gap between task interfaces and concrete model implementations.
40/// They handle model-specific preprocessing, inference, and postprocessing while
41/// conforming to the task's input/output schema.
42pub trait ModelAdapter: Send + Sync + Debug {
43    /// The task type this adapter executes
44    type Task: Task;
45
46    /// Returns information about this adapter.
47    fn info(&self) -> AdapterInfo;
48
49    /// Returns the schema that this adapter conforms to.
50    fn schema(&self) -> TaskSchema {
51        TaskSchema::new(
52            self.info().task_type,
53            vec!["image".to_string()], // Most adapters work with images
54            vec!["result".to_string()],
55        )
56    }
57
58    /// Executes the model on the given input.
59    ///
60    /// This method handles the complete pipeline:
61    /// 1. Validate input
62    /// 2. Preprocess
63    /// 3. Run inference
64    /// 4. Postprocess
65    /// 5. Validate output
66    ///
67    /// # Arguments
68    ///
69    /// * `input` - The task input to process
70    /// * `config` - Optional configuration for execution
71    ///
72    /// # Returns
73    ///
74    /// The task output or an error
75    fn execute(
76        &self,
77        input: <Self::Task as Task>::Input,
78        config: Option<&<Self::Task as Task>::Config>,
79    ) -> Result<<Self::Task as Task>::Output, OCRError>;
80
81    /// Validates that this adapter is compatible with the given task schema.
82    ///
83    /// # Arguments
84    ///
85    /// * `schema` - The schema to check compatibility with
86    ///
87    /// # Returns
88    ///
89    /// Result indicating success or incompatibility error
90    fn validate_compatibility(&self, schema: &TaskSchema) -> Result<(), OCRError> {
91        let adapter_schema = self.schema();
92        if adapter_schema.task_type != schema.task_type {
93            return Err(OCRError::ConfigError {
94                message: format!(
95                    "Adapter task type {:?} does not match required task type {:?}",
96                    adapter_schema.task_type, schema.task_type
97                ),
98            });
99        }
100        Ok(())
101    }
102
103    /// Returns whether this adapter can handle batched inputs efficiently.
104    fn supports_batching(&self) -> bool {
105        true // Most models support batching
106    }
107
108    /// Returns the recommended batch size for this adapter.
109    fn recommended_batch_size(&self) -> usize {
110        6 // Default from constants
111    }
112}
113
114/// Builder trait for creating model adapters.
115///
116/// This trait defines the interface for building adapters with specific configurations.
117pub trait AdapterBuilder: Sized {
118    /// The configuration type for this builder
119    type Config: Send + Sync + Debug + Clone;
120
121    /// The adapter type that this builder creates
122    type Adapter: ModelAdapter;
123
124    /// Builds an adapter from a model source.
125    ///
126    /// # Arguments
127    ///
128    /// * `model_source` - Model file path or in-memory model bytes
129    ///
130    /// # Returns
131    ///
132    /// The built adapter or an error
133    fn build(
134        self,
135        model_source: impl Into<crate::core::inference::ModelSource>,
136    ) -> Result<Self::Adapter, OCRError>;
137
138    /// Configures the builder with the given configuration.
139    ///
140    /// # Arguments
141    ///
142    /// * `config` - The configuration to use
143    ///
144    /// # Returns
145    ///
146    /// The configured builder
147    fn with_config(self, config: Self::Config) -> Self;
148
149    /// Returns the adapter type identifier.
150    fn adapter_type(&self) -> &str;
151}
152
153/// Trait for adapter builders that support ONNX Runtime session configuration.
154///
155/// This trait is implemented by builders that can be configured with ORT session
156/// settings like execution providers, thread count, and memory optimization.
157pub trait OrtConfigurable: Sized {
158    /// Configures the builder with ONNX Runtime session settings.
159    fn with_ort_config(self, config: crate::core::config::OrtSessionConfig) -> Self;
160}
161
162/// A wrapper that implements Task for an adapter's task type.
163///
164/// This allows adapters to be used polymorphically through the Task trait.
165#[derive(Debug)]
166pub struct AdapterTask<A: ModelAdapter> {
167    adapter: A,
168}
169
170impl<A: ModelAdapter> AdapterTask<A> {
171    /// Creates a new adapter task.
172    pub fn new(adapter: A) -> Self {
173        Self { adapter }
174    }
175
176    /// Returns a reference to the adapter.
177    pub fn adapter(&self) -> &A {
178        &self.adapter
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_adapter_info_creation() {
188        let info = AdapterInfo::new(
189            "DB",
190            TaskType::TextDetection,
191            "Differentiable Binarization text detector",
192        );
193
194        assert_eq!(info.model_name, "DB");
195        assert_eq!(info.task_type, TaskType::TextDetection);
196    }
197
198    #[test]
199    fn test_schema_validation() {
200        // This is a conceptual test - actual validation would be done with real adapters
201        let schema = TaskSchema::new(
202            TaskType::TextDetection,
203            vec!["image".to_string()],
204            vec!["text_boxes".to_string()],
205        );
206
207        assert_eq!(schema.task_type, TaskType::TextDetection);
208        assert_eq!(schema.input_types, vec!["image".to_string()]);
209    }
210}