semantic bufo search find-bufo.com
bufo
at fix-query-length-error-handling 87 lines 2.3 kB view raw
1use anyhow::{Context, Result}; 2use reqwest::Client; 3use serde::{Deserialize, Serialize}; 4 5#[derive(Debug, Serialize)] 6struct VoyageEmbeddingRequest { 7 inputs: Vec<MultimodalInput>, 8 model: String, 9 #[serde(skip_serializing_if = "Option::is_none")] 10 input_type: Option<String>, 11} 12 13#[derive(Debug, Serialize)] 14struct MultimodalInput { 15 content: Vec<ContentSegment>, 16} 17 18#[derive(Debug, Serialize)] 19#[serde(tag = "type", rename_all = "snake_case")] 20enum ContentSegment { 21 Text { text: String }, 22} 23 24#[derive(Debug, Deserialize)] 25struct VoyageEmbeddingResponse { 26 data: Vec<VoyageEmbeddingData>, 27} 28 29#[derive(Debug, Deserialize)] 30struct VoyageEmbeddingData { 31 embedding: Vec<f32>, 32} 33 34pub struct EmbeddingClient { 35 client: Client, 36 api_key: String, 37} 38 39impl EmbeddingClient { 40 pub fn new(api_key: String) -> Self { 41 Self { 42 client: Client::new(), 43 api_key, 44 } 45 } 46 47 pub async fn embed_text(&self, text: &str) -> Result<Vec<f32>> { 48 let request = VoyageEmbeddingRequest { 49 inputs: vec![MultimodalInput { 50 content: vec![ContentSegment::Text { 51 text: text.to_string(), 52 }], 53 }], 54 model: "voyage-multimodal-3".to_string(), 55 input_type: Some("query".to_string()), 56 }; 57 58 let response = self 59 .client 60 .post("https://api.voyageai.com/v1/multimodalembeddings") 61 .header("Authorization", format!("Bearer {}", self.api_key)) 62 .json(&request) 63 .send() 64 .await 65 .context("failed to send embedding request")?; 66 67 if !response.status().is_success() { 68 let status = response.status(); 69 let body = response.text().await.unwrap_or_default(); 70 anyhow::bail!("voyage api error ({}): {}", status, body); 71 } 72 73 let embedding_response: VoyageEmbeddingResponse = response 74 .json() 75 .await 76 .context("failed to parse embedding response")?; 77 78 let embedding = embedding_response 79 .data 80 .into_iter() 81 .next() 82 .map(|d| d.embedding) 83 .context("no embedding returned")?; 84 85 Ok(embedding) 86 } 87}