Class: Embedding::Gemini
- Inherits:
-
Object
- Object
- Embedding::Gemini
- Defined in:
- app/services/embedding/gemini.rb
Overview
Gemini Embedding 2 service for multimodal embeddings.
Natively embeds images, text, and interleaved image+text into a unified
vector space via the Gemini API embedContent endpoint.
== Architecture
Image Analysis: pHash → Gemini Embedding 2 (image + metadata)
Vision Desc: Gemini Flash (independent, on-demand)
== Embedding Dimensions
Gemini Embedding 2 supports Matryoshka Representation Learning:
- 3072: Full quality (default from API)
- 1536: Used here for HNSW compatibility (pgvector 2000-dim limit)
- 768: For constrained environments
== Rate Limiting
Redis-based sliding window rate limiter.
Configurable via GEMINI_EMBED_REQUESTS_PER_MINUTE (default: 300).
Defined Under Namespace
Classes: ApiError, ConfigurationError, Error, PermanentError, RateLimitError
Constant Summary collapse
- BASE_URL =
URL for base.
'https://generativelanguage.googleapis.com'- API_VERSION =
Api version.
'v1beta'- EMBED_MODEL =
Embed model — sourced from the canonical registry
(config/initializers/ai_model_constants.rb). AiModelConstants.id(:unified_embedding)
- MODEL_NAME =
Model name (same model; kept as a distinct public constant for callers).
EMBED_MODEL- DEFAULT_DIMENSIONS =
Default dimensions.
1536- DEFAULT_TEXT_DIMENSIONS =
Default text dimensions.
1536- DEFAULT_VISUAL_DIMENSIONS =
Default visual dimensions.
1536- MAX_BATCH_SIZE =
Maximum items per batchEmbedContents request. Google caps batch size;
larger inputs are chunked into multiple requests transparently. 100- TIMEOUT =
Timeout.
120- RATE_LIMIT_KEY =
Key used for rate limit.
'gemini_embed:rate_limit'- REQUESTS_PER_MINUTE =
Requests per minute.
ENV.fetch('GEMINI_EMBED_REQUESTS_PER_MINUTE', 300).to_i
- RATE_LIMIT_WINDOW =
seconds
60- MAX_RETRIES =
Maximum retries.
5- BASE_RETRY_DELAY =
Base retry delay.
2- MIME_TYPES =
Recognised mime types.
{ '.jpg' => 'image/jpeg', '.jpeg' => 'image/jpeg', '.png' => 'image/png' }.freeze
- RETRYABLE_EXCEPTIONS =
Retryable exceptions.
[ RateLimitError, Faraday::TimeoutError, Faraday::ConnectionFailed ].freeze
Class Method Summary collapse
-
.available? ⇒ Boolean
Check if the API is configured.
-
.embed_image(image_url, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed an image, optionally with accompanying text metadata.
-
.embed_image_file(path, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed a local image file.
-
.embed_image_url(url, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Object
Alias for embed_image.
-
.embed_query(text, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Float>
Embed text for a search query.
-
.embed_text(text, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Float>
Embed text for storage/indexing.
-
.embed_texts(texts, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Array<Float>>
Embed many texts in batched requests via the batchEmbedContents endpoint.
-
.embed_visual_query(text, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed a text query for visual search (cross-modal text → image).
-
.model_name ⇒ String
Model name for database storage.
Class Method Details
.available? ⇒ Boolean
Check if the API is configured
190 191 192 193 194 |
# File 'app/services/embedding/gemini.rb', line 190 def available? api_key.present? rescue ConfigurationError false end |
.embed_image(image_url, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed an image, optionally with accompanying text metadata.
Sends the image as inlineData (base64) with optional text parts.
117 118 119 120 121 122 123 |
# File 'app/services/embedding/gemini.rb', line 117 def (image_url, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) parts = [] parts << { text: text } if text.present? parts << build_image_part(image_url) (parts, dimensions: dimensions) end |
.embed_image_file(path, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed a local image file
131 132 133 134 135 136 137 138 139 |
# File 'app/services/embedding/gemini.rb', line 131 def (path, text: nil, dimensions: DEFAULT_VISUAL_DIMENSIONS) raise Error, "Image file not found: #{path}" unless File.exist?(path) parts = [] parts << { text: text } if text.present? parts << build_file_image_part(path) (parts, dimensions: dimensions) end |
.embed_image_url(url, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Object
Alias for embed_image
202 203 204 |
# File 'app/services/embedding/gemini.rb', line 202 def (url, dimensions: DEFAULT_VISUAL_DIMENSIONS) (url, dimensions: dimensions) end |
.embed_query(text, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Float>
Embed text for a search query. (gemini-embedding-2 ignores task_type, so
this is currently equivalent to embed_text; kept as the query-side seam
for a future task-prefix optimization.)
148 149 150 |
# File 'app/services/embedding/gemini.rb', line 148 def (text, dimensions: DEFAULT_TEXT_DIMENSIONS) ([{ text: text }], dimensions: dimensions) end |
.embed_text(text, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Float>
Embed text for storage/indexing
157 158 159 |
# File 'app/services/embedding/gemini.rb', line 157 def (text, dimensions: DEFAULT_TEXT_DIMENSIONS) ([{ text: text }], dimensions: dimensions) end |
.embed_texts(texts, dimensions: DEFAULT_TEXT_DIMENSIONS) ⇒ Array<Array<Float>>
Embed many texts in batched requests via the batchEmbedContents endpoint.
One HTTP round-trip (and one rate-limit slot) per MAX_BATCH_SIZE items
instead of one per text — the recommended path for backfills.
168 169 170 171 172 173 174 175 176 |
# File 'app/services/embedding/gemini.rb', line 168 def (texts, dimensions: DEFAULT_TEXT_DIMENSIONS) texts = Array(texts) return [] if texts.empty? texts.each_slice(MAX_BATCH_SIZE).flat_map do |slice| parts_list = slice.map { |text| [{ text: text.to_s }] } (parts_list, dimensions: dimensions) end end |
.embed_visual_query(text, dimensions: DEFAULT_VISUAL_DIMENSIONS) ⇒ Array<Float>
Embed a text query for visual search (cross-modal text → image)
183 184 185 |
# File 'app/services/embedding/gemini.rb', line 183 def (text, dimensions: DEFAULT_VISUAL_DIMENSIONS) (text, dimensions: dimensions) end |
.model_name ⇒ String
Returns Model name for database storage.
197 198 199 |
# File 'app/services/embedding/gemini.rb', line 197 def model_name MODEL_NAME end |