Class: Vangrail::Embeddings
- Inherits:
-
Object
- Object
- Vangrail::Embeddings
- Defined in:
- lib/vangrail/embeddings.rb
Overview
One OpenAI-compatible embeddings call.
The sibling of Chat, and it exists for one reason: the lexicon rails read words, and a synonym nobody listed is a miss. An embedding is the only cheap way to compare two sentences by what they mean rather than by what they spell, and every local proxy that serves chat can usually serve this too.
Local first, exactly as with Chat. Sending every retrieved document to a third party to be embedded is a data-flow decision an application should make deliberately, and on a loopback proxy it is not one at all.
Batched, because the cost that matters is round trips rather than tokens: a page has a few dozen clauses, and thirty small requests to score one page is what makes a rail too slow to leave on.
Constant Summary collapse
- PATH =
'/embeddings'
Instance Attribute Summary collapse
-
#http ⇒ Object
readonly
Returns the value of attribute http.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
Class Method Summary collapse
-
.cosine(left, right) ⇒ Object
Cosine similarity, which is what an embedding comparison is.
Instance Method Summary collapse
-
#embed(texts) ⇒ Object
Vectors for each input, in the order given.
-
#initialize(model:, base_url: nil, api_key: nil, http: nil, open_timeout: HTTP::DEFAULT_OPEN_TIMEOUT, read_timeout: 20) ⇒ Embeddings
constructor
A new instance of Embeddings.
Constructor Details
#initialize(model:, base_url: nil, api_key: nil, http: nil, open_timeout: HTTP::DEFAULT_OPEN_TIMEOUT, read_timeout: 20) ⇒ Embeddings
Returns a new instance of Embeddings.
26 27 28 29 30 31 32 33 |
# File 'lib/vangrail/embeddings.rb', line 26 def initialize(model:, base_url: nil, api_key: nil, http: nil, open_timeout: HTTP::DEFAULT_OPEN_TIMEOUT, read_timeout: 20) raise ArgumentError, 'an Embeddings needs a base_url or an http client' if http.nil? && base_url.to_s.strip.empty? @model = model @http = http || HTTP.new(base_url: base_url, api_key: api_key, open_timeout: open_timeout, read_timeout: read_timeout) end |
Instance Attribute Details
#http ⇒ Object (readonly)
Returns the value of attribute http.
24 25 26 |
# File 'lib/vangrail/embeddings.rb', line 24 def http @http end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
24 25 26 |
# File 'lib/vangrail/embeddings.rb', line 24 def model @model end |
Class Method Details
.cosine(left, right) ⇒ Object
Cosine similarity, which is what an embedding comparison is. Two vectors of different length is a provider that changed model mid-call, and it is an error rather than a zero.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/vangrail/embeddings.rb', line 55 def self.cosine(left, right) raise ProtocolError, 'vectors of different lengths' unless left.size == right.size dot = 0.0 left_norm = 0.0 right_norm = 0.0 left.each_with_index do |value, i| other = right[i] dot += value * other left_norm += value * value right_norm += other * other end return 0.0 if left_norm.zero? || right_norm.zero? dot / (Math.sqrt(left_norm) * Math.sqrt(right_norm)) end |
Instance Method Details
#embed(texts) ⇒ Object
Vectors for each input, in the order given.
The index is read rather than trusted to arrive in order: the API says each datum carries one, and a provider batching internally is entitled to answer out of order.
40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/vangrail/embeddings.rb', line 40 def (texts) inputs = Array(texts).map(&:to_s) return [] if inputs.empty? body = http.post_json(PATH, { 'model' => model, 'input' => inputs }) vectors = vectors_in(body) raise ProtocolError, "embeddings endpoint returned #{vectors.size} vectors for #{inputs.size} inputs" \ unless vectors.size == inputs.size vectors end |