mod private
{
use crate::
{
client::Client,
components::
{
embeddings::{ EmbeddingRequest, EmbeddingResponse, EmbeddingOptions },
},
error::Result,
validation::{ validate_input_text, validate_model_identifier, validate_batch_inputs },
};
#[ cfg( feature = "env-config" ) ]
use crate::environment::{ HuggingFaceEnvironment, EnvironmentInterface };
const HF_SERVERLESS_API_BASE : &str = "https://router.huggingface.co/hf-inference";
#[ derive( Debug ) ]
pub struct Embeddings< E >
where
E : Clone,
{
client : Client< E >,
}
#[ cfg( feature = "env-config" ) ]
impl< E > Embeddings< E >
where
E : HuggingFaceEnvironment + EnvironmentInterface + Send + Sync + 'static + Clone,
{
#[ inline ]
#[ must_use ]
pub fn new( client : &Client< E > ) -> Self
{
Self
{
client : client.clone(),
}
}
#[ inline ]
pub async fn create(
&self,
input : impl Into< String >,
model : impl AsRef< str >
) -> Result< EmbeddingResponse >
{
let input_text = input.into();
let model_id = model.as_ref();
validate_input_text( &input_text )?;
validate_model_identifier( model_id )?;
let request = EmbeddingRequest::new_batch( vec![ input_text ] );
let url = format!( "{HF_SERVERLESS_API_BASE}/models/{model_id}" );
self.client.post( &url, &request ).await
}
#[ inline ]
pub async fn create_batch(
&self,
inputs : Vec< String >,
model : impl AsRef< str >
) -> Result< EmbeddingResponse >
{
let model_id = model.as_ref();
validate_batch_inputs( &inputs )?;
validate_model_identifier( model_id )?;
let request = EmbeddingRequest::new_batch( inputs );
let url = format!( "{HF_SERVERLESS_API_BASE}/models/{model_id}" );
self.client.post( &url, &request ).await
}
#[ inline ]
pub async fn create_with_options(
&self,
input : impl Into< String >,
model : impl AsRef< str >,
options : EmbeddingOptions
) -> Result< EmbeddingResponse >
{
let input_text = input.into();
let model_id = model.as_ref();
validate_input_text( &input_text )?;
validate_model_identifier( model_id )?;
let request = EmbeddingRequest::new_batch( vec![ input_text ] ).with_options( options );
let url = format!( "{HF_SERVERLESS_API_BASE}/models/{model_id}" );
self.client.post( &url, &request ).await
}
#[ inline ]
pub async fn feature_extraction(
&self,
inputs : Vec< String >,
model : impl AsRef< str >
) -> Result< Vec< Vec< f32 > > >
{
let model_id = model.as_ref();
validate_batch_inputs( &inputs )?;
validate_model_identifier( model_id )?;
let request = serde_json::json!
({
"inputs": inputs,
"parameters": {
"task": "feature-extraction"
}
});
let url = format!( "{HF_SERVERLESS_API_BASE}/models/{model_id}" );
self.client.post( &url, &request ).await
}
#[ inline ]
pub async fn similarity(
&self,
text1 : impl Into< String >,
text2 : impl Into< String >,
model : impl AsRef< str >
) -> Result< f32 >
{
let first_text = text1.into();
let second_text = text2.into();
let model_id = model.as_ref();
validate_input_text( &first_text )?;
validate_input_text( &second_text )?;
validate_model_identifier( model_id )?;
let text_inputs = vec![ first_text, second_text ];
let embeddings : Vec< Vec< f32 > > = self.feature_extraction( text_inputs, model_id ).await?;
if embeddings.len() != 2
{
return Err( crate::error::HuggingFaceError::Generic(
"Expected exactly 2 embeddings for similarity calculation".to_string()
) );
}
let first_embedding = &embeddings[ 0 ];
let second_embedding = &embeddings[ 1 ];
let similarity = cosine_similarity( first_embedding, second_embedding )?;
Ok( similarity )
}
}
#[ cfg( not( feature = "env-config" ) ) ]
impl< E > Embeddings< E >
where
E : Clone,
{
#[ inline ]
#[ must_use ]
pub fn new( client : &Client< E > ) -> Self
{
Self
{
client : (*client).clone(),
}
}
}
#[ inline ]
fn cosine_similarity( a : &[ f32 ], b : &[ f32 ] ) -> Result< f32 >
{
if a.len() != b.len()
{
return Err( crate::error::HuggingFaceError::InvalidArgument(
"Vectors must have the same dimension".to_string()
) );
}
let dot_product : f32 = a.iter().zip( b.iter() ).map( | ( x, y ) | x * y ).sum();
let magnitude_a : f32 = a.iter().map( | x | x * x ).sum::< f32 >().sqrt();
let magnitude_b : f32 = b.iter().map( | x | x * x ).sum::< f32 >().sqrt();
if magnitude_a == 0.0 || magnitude_b == 0.0
{
return Err( crate::error::HuggingFaceError::Generic(
"Cannot compute similarity with zero magnitude vector".to_string()
) );
}
Ok( ( dot_product / ( magnitude_a * magnitude_b ) ).clamp( -1.0, 1.0 ) )
}
}
crate::mod_interface!
{
exposed use
{
private::Embeddings,
};
}