Module: Parse::Embeddings::BindingAudit

Defined in:
lib/parse/embeddings/binding_audit.rb

Overview

Checks that a :vector property's declared provider binding still matches the provider actually registered under that name.

A :vector property may declare provider:, model:, and dimensions:. Only provider: was ever enforced. dimensions: is verified — but only against the vector a provider already returned, i.e. after the call has been made and paid for. And model: was never checked at all, which is the dangerous one: two generations of the same model family usually share a width (voyage-3 and voyage-3.5 are both 1024), so swapping the registered provider's model silently mixes incompatible embeddings into one index. Nothing raises, recall just quietly degrades, and the damage is only repairable by re-embedding.

This module closes both gaps by comparing the declaration against the live provider BEFORE any request is issued.

Auditing cannot happen at class-definition time: providers are registered by name and, as Core::EmbedManaged documents, registration may legitimately happen any time before the first save. So the audit runs lazily on each use and is also exposed as BindingAudit.audit_all! for an explicit boot-time or CI check.

Defined Under Namespace

Classes: BindingMismatch, DiscoveryFailed

Class Method Summary collapse

Class Method Details

.audit_all!(classes: nil, strict: false) ⇒ Array<String>

Audit every declared binding whose provider is registered. Intended for boot or CI: it surfaces a drifted declaration before a single embedding is written, rather than on the first save that happens to touch it.

Parameters:

  • classes (Array<Class>, nil) (defaults to: nil)

    defaults to every Parse::Object subclass carrying :vector properties.

  • strict (Boolean) (defaults to: false)

    when true, an unregistered provider is itself a failure; otherwise those bindings are skipped (a provider may be registered later in boot).

Returns:

  • (Array<String>)

    human-readable problems, empty when clean.



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
# File 'lib/parse/embeddings/binding_audit.rb', line 74

def audit_all!(classes: nil, strict: false)
  problems = []
  begin
    bindings = collect_bindings(classes)
  rescue DiscoveryFailed => e
    return [e.message]
  end

  bindings.each do |klass, field, declared|
    provider_name = declared[:provider]
    next if provider_name.nil?

    begin
      provider = Parse::Embeddings.provider(provider_name)
    rescue Parse::Embeddings::ProviderNotRegistered => e
      problems << "#{klass}##{field}: #{e.message}" if strict
      next
    end

    begin
      check!(klass, field, provider, declared)
    rescue BindingMismatch => e
      problems << e.message
    end
  end
  problems
end

.audit_all_or_raise!(classes: nil, strict: false) ⇒ void

This method returns an undefined value.

audit_all! that raises instead of returning problems.

Raises:



106
107
108
109
110
111
112
# File 'lib/parse/embeddings/binding_audit.rb', line 106

def audit_all_or_raise!(classes: nil, strict: false)
  problems = audit_all!(classes: classes, strict: strict)
  return if problems.empty?

  raise BindingMismatch,
        "Parse::Embeddings binding audit failed:\n  - #{problems.join("\n  - ")}"
end

.reset!void

This method returns an undefined value.

Retained as a no-op for callers that invoked it when this module memoized verdicts.



117
118
119
# File 'lib/parse/embeddings/binding_audit.rb', line 117

def reset!
  nil
end

.verify!(klass, field, provider) ⇒ void

This method returns an undefined value.

Verify one property binding against a resolved provider.

Deliberately NOT memoized. The check is a pair of comparisons against values already in memory, so caching it saves nothing measurable — while any cache key cheap enough to be worth computing (class name, provider object id) can go stale when a class is unloaded and redefined with a changed declaration, or when object ids are recycled. A validator that silently skips after a reload is worse than no validator, so correctness wins over an optimization with no observable benefit.

Parameters:

Raises:



55
56
57
58
59
60
61
# File 'lib/parse/embeddings/binding_audit.rb', line 55

def verify!(klass, field, provider)
  declared = klass.vector_properties[field.to_sym]
  return if declared.nil?

  check!(klass, field, provider, declared)
  nil
end