rig_core/providers/ollama/
embedding.rs1use crate::error::EncodeError;
10use crate::operation::Embedding;
11use crate::wire::{Body, Capabilities, Descriptor, DocumentDecoder, Encoded, Framing, Mode, Wire};
12use serde::{Deserialize, Serialize};
13
14use super::{OllamaConfig, PROVIDER_NAME};
15
16pub const ALL_MINILM: &str = "all-minilm";
18pub const NOMIC_EMBED_TEXT: &str = "nomic-embed-text";
20pub const MXBAI_EMBED_LARGE: &str = "mxbai-embed-large";
22pub const BGE_M3: &str = "bge-m3";
24pub const EMBEDDINGGEMMA: &str = "embeddinggemma";
26pub const QWEN3_EMBEDDING: &str = "qwen3-embedding";
28
29fn model_dimensions_from_identifier(identifier: &str) -> Option<usize> {
30 match identifier {
31 ALL_MINILM => Some(384),
32 NOMIC_EMBED_TEXT => Some(768),
33 MXBAI_EMBED_LARGE => Some(1024),
34 BGE_M3 => Some(1024),
35 EMBEDDINGGEMMA => Some(768),
36 _ => None,
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct EmbeddingResponse {
42 pub model: String,
43 pub embeddings: Vec<Vec<f64>>,
44 #[serde(default)]
45 pub total_duration: Option<u64>,
46 #[serde(default)]
47 pub load_duration: Option<u64>,
48 #[serde(default)]
49 pub prompt_eval_count: Option<u64>,
50}
51
52const MAX_DOCUMENTS: usize = 1024;
54
55impl OllamaConfig {
56 pub(crate) fn embedding(&self, model: impl Into<String>, ndims: Option<usize>) -> Embeddings {
59 let model = model.into();
60 let ndims = ndims
61 .or_else(|| model_dimensions_from_identifier(&model))
62 .unwrap_or_default();
63 Embeddings {
64 provider: self.clone(),
65 model,
66 ndims,
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct Embeddings {
74 pub provider: OllamaConfig,
76 pub model: String,
78 pub ndims: usize,
81}
82
83impl Wire for Embeddings {
84 type Op = Embedding;
85 type Payload = crate::wire::Encoded;
86 type Frame = crate::wire::WireFrame;
87 type Event = EmbeddingResponse;
88 type Decoder<'id> = DocumentDecoder<Embedding, Self::Event>;
89 type Reassembler = crate::wire::document::Unreassembled;
90
91 fn describe(&self) -> Descriptor<'_> {
92 Descriptor::new(PROVIDER_NAME)
93 .model(self.model.as_str())
94 .capabilities(Capabilities::embedding(MAX_DOCUMENTS, self.ndims))
95 }
96
97 fn encode(&self, texts: Vec<String>, _mode: Mode) -> Result<Encoded, EncodeError> {
98 let body = serde_json::json!({ "model": self.model, "input": texts });
99 let request = self
100 .provider
101 .request(http::Method::POST, "/api/embed")
102 .body(Body::Bytes(serde_json::to_vec(&body)?))?;
103 Ok(Encoded::new(request, Framing::Whole))
104 }
105
106 fn decoder<'id>(&self) -> Self::Decoder<'id> {
108 DocumentDecoder::new(
109 |body| {
110 crate::providers::internal::wire::classify_marker_keyed_frame(body, &["embeddings"])
111 },
112 |reply| {
113 let usage = crate::completion::Usage {
116 input_tokens: reply.prompt_eval_count,
117 total_tokens: reply.prompt_eval_count,
118 ..Default::default()
119 };
120 Ok(crate::embeddings::EmbeddingResponse {
121 model: Some(reply.model),
122 usage,
123 ..crate::embeddings::EmbeddingResponse::from_vectors(reply.embeddings)
124 })
125 },
126 )
127 }
128}
129
130#[cfg(test)]
131mod tests;