ferrum_engine/
embedding_engine.rs1use async_trait::async_trait;
9use ferrum_interfaces::engine::{EmbedEngine, InferenceEngine};
10use ferrum_models::ClipModelExecutor;
11use ferrum_types::{EngineConfig, EngineMetrics, EngineStatus, FerrumError, Result};
12use std::sync::Arc;
13
14pub struct EmbeddingEngine {
16 executor: Arc<ClipModelExecutor>,
17 tokenizer: Option<tokenizers::Tokenizer>,
18 config: EngineConfig,
19}
20
21impl EmbeddingEngine {
22 pub fn new(executor: ClipModelExecutor, config: EngineConfig) -> Self {
23 Self {
24 executor: Arc::new(executor),
25 tokenizer: None,
26 config,
27 }
28 }
29
30 pub fn with_tokenizer(mut self, tokenizer: tokenizers::Tokenizer) -> Self {
32 self.tokenizer = Some(tokenizer);
33 self
34 }
35}
36
37#[async_trait]
38impl InferenceEngine for EmbeddingEngine {
39 async fn status(&self) -> EngineStatus {
40 crate::modality_stubs::inert_status()
41 }
42
43 async fn shutdown(&self) -> Result<()> {
44 Ok(())
45 }
46
47 fn config(&self) -> &EngineConfig {
48 &self.config
49 }
50
51 fn metrics(&self) -> EngineMetrics {
52 crate::modality_stubs::inert_metrics()
53 }
54
55 async fn health_check(&self) -> ferrum_types::HealthStatus {
56 crate::modality_stubs::inert_health()
57 }
58}
59
60#[async_trait]
61impl EmbedEngine for EmbeddingEngine {
62 async fn embed_text(&self, text: &str) -> Result<Vec<f32>> {
63 let tokenizer = self
64 .tokenizer
65 .as_ref()
66 .ok_or_else(|| FerrumError::model("No tokenizer loaded for text embedding"))?;
67 let encoding = tokenizer
68 .encode(text, true)
69 .map_err(|e| FerrumError::model(format!("tokenize: {e}")))?;
70 let embedding = self.executor.embed_text(encoding.get_ids())?;
71 embedding
72 .squeeze(0)
73 .and_then(|t| t.to_dtype(candle_core::DType::F32))
74 .and_then(|t| t.to_vec1())
75 .map_err(|e| FerrumError::model(format!("embed_text tensor: {e}")))
76 }
77
78 async fn embed_image(&self, image: &str) -> Result<Vec<f32>> {
79 let embedding = if image.starts_with("data:") || image.len() > 1000 {
80 self.executor.embed_image_base64(image)?
81 } else {
82 self.executor.embed_image_path(image)?
83 };
84 embedding
85 .squeeze(0)
86 .and_then(|t| t.to_dtype(candle_core::DType::F32))
87 .and_then(|t| t.to_vec1())
88 .map_err(|e| FerrumError::model(format!("embed_image tensor: {e}")))
89 }
90
91 fn embedding_dim(&self) -> usize {
92 self.executor.projection_dim()
93 }
94}