Class: HTM::EmbeddingService

Inherits:
Object
  • Object
show all
Defined in:
lib/htm/embedding_service.rb

Overview

Embedding Service - Processes and validates vector embeddings

This service wraps the configured embedding generator and provides:

  • Response validation

  • Dimension handling (padding/truncation)

  • Error handling and logging

  • Storage formatting

  • Circuit breaker protection for external LLM failures

The actual LLM call is delegated to HTM.configuration.embedding_generator

Class Method Summary collapse

Class Method Details

.circuit_breakerHTM::CircuitBreaker

Get or create the circuit breaker for embedding service

Returns:



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/htm/embedding_service.rb', line 35

def circuit_breaker
  config = HTM.configuration
  @circuit_breaker_mutex.synchronize do
    @circuit_breaker ||= HTM::CircuitBreaker.new(
      name: 'embedding_service',
      failure_threshold: config.circuit_breaker_failure_threshold,
      reset_timeout: config.circuit_breaker_reset_timeout,
      half_open_max_calls: config.circuit_breaker_half_open_max_calls
    )
  end
end

.format_for_storage(embedding) ⇒ String

Format embedding for database storage

Parameters:

  • embedding (Array<Float>)

    Padded embedding

Returns:

  • (String)

    PostgreSQL array format



149
150
151
# File 'lib/htm/embedding_service.rb', line 149

def self.format_for_storage(embedding)
  "[#{embedding.join(',')}]"
end

.generate(text) ⇒ Hash

Generate embedding with validation and processing

Parameters:

  • text (String)

    Text to embed

Returns:

  • (Hash)

    Processed embedding with metadata

    embedding: Array<Float>,           # Original embedding
    dimension: Integer,                # Original dimension
    storage_embedding: String,         # Formatted for database storage
    storage_dimension: Integer         # Padded dimension (2000)
    

Raises:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/htm/embedding_service.rb', line 70

def self.generate(text)
  # Use circuit breaker to protect against cascading failures
  raw_embedding = circuit_breaker.call do
    HTM.configuration.embedding_generator.call(text)
  end

  # Validate response
  validate_embedding!(raw_embedding)

  # Get actual dimension
  actual_dimension = raw_embedding.length

  # Check dimension limit
  max_dim = max_dimension
  if actual_dimension > max_dim
    HTM.logger.warn "EmbeddingService: Embedding dimension #{actual_dimension} exceeds max #{max_dim}, truncating"
    raw_embedding = raw_embedding[0...max_dim]
    actual_dimension = max_dim
  end

  # Pad to max dimensions for consistent storage
  storage_embedding = pad_embedding(raw_embedding)

  # Format for database storage
  storage_string = format_for_storage(storage_embedding)

  {
    embedding: raw_embedding,
    dimension: actual_dimension,
    storage_embedding: storage_string,
    storage_dimension: max_dim
  }
rescue HTM::CircuitBreakerOpenError, HTM::EmbeddingError
  raise
rescue StandardError => e
  HTM.logger.error "EmbeddingService: Failed to generate embedding: #{e.message}"
  raise HTM::EmbeddingError, "Embedding generation failed: #{e.message}"
end

.max_dimensionInteger

Maximum embedding dimension (configurable, default 2000)

Returns:

  • (Integer)

    Max dimensions for pgvector HNSW index



27
28
29
# File 'lib/htm/embedding_service.rb', line 27

def max_dimension
  HTM.configuration.max_embedding_dimension
end

.pad_embedding(embedding) ⇒ Array<Float>

Pad embedding to max_dimension with zeros

Parameters:

  • embedding (Array<Float>)

    Original embedding

Returns:

  • (Array<Float>)

    Padded embedding



137
138
139
140
141
142
# File 'lib/htm/embedding_service.rb', line 137

def self.pad_embedding(embedding)
  max_dim = max_dimension
  return embedding if embedding.length >= max_dim

  embedding + Array.new(max_dim - embedding.length, 0.0)
end

.reset_circuit_breaker!void

This method returns an undefined value.

Reset the circuit breaker (useful for testing)



51
52
53
54
55
# File 'lib/htm/embedding_service.rb', line 51

def reset_circuit_breaker!
  @circuit_breaker_mutex.synchronize do
    @circuit_breaker&.reset!
  end
end

.validate_embedding!(embedding) ⇒ Object

Validate embedding response format

Parameters:

  • embedding (Object)

    Raw embedding from generator

Raises:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/htm/embedding_service.rb', line 114

def self.validate_embedding!(embedding)
  unless embedding.is_a?(Array)
    raise HTM::EmbeddingError, "Embedding must be an Array, got #{embedding.class}"
  end

  if embedding.empty?
    raise HTM::EmbeddingError, "Embedding array is empty"
  end

  unless embedding.all?(Numeric)
    raise HTM::EmbeddingError, "Embedding must contain only numeric values"
  end

  # Check for NaN or Infinity
  return unless embedding.any? { |v| (v.respond_to?(:nan?) && v.nan?) || (v.respond_to?(:infinite?) && v.infinite?) }
  raise HTM::EmbeddingError, "Embedding contains NaN or Infinity values"
end