Add AI cache clearing infrastructure

Add ClearAiCacheJob for async cache clearing with low priority. Extend Enrichable concern with clear_ai_cache methods to unlock AI-enriched attributes and delete AI enrichment records. Trigger automatic cache clearing when OpenAI model setting changes.
This commit is contained in:
eureka928
2026-01-26 09:46:20 +01:00
parent 8b3ebd7988
commit 46ab1d8373
3 changed files with 68 additions and 0 deletions

View File

@@ -15,6 +15,8 @@ module Enrichable
InvalidAttributeError = Class.new(StandardError)
included do
has_many :data_enrichments, as: :enrichable, dependent: :destroy
scope :enrichable, ->(attrs) {
attrs = Array(attrs).map(&:to_s)
json_condition = attrs.each_with_object({}) { |attr, hash| hash[attr] = true }
@@ -22,6 +24,28 @@ module Enrichable
}
end
class_methods do
def clear_ai_cache(family)
# Get all records that belong to this family
records = if respond_to?(:joins)
case name
when "Transaction"
joins(entry: :account).where(accounts: { family_id: family.id })
when "Entry"
joins(:account).where(accounts: { family_id: family.id })
else
none
end
else
none
end
records.find_each do |record|
record.clear_ai_cache
end
end
end
# Convenience method for a single attribute
def enrich_attribute(attr, value, source:, metadata: {})
enrich_attributes({ attr => value }, source:, metadata:)
@@ -124,6 +148,21 @@ module Enrichable
end
end
def clear_ai_cache
ActiveRecord::Base.transaction do
# Find attributes that were locked by AI enrichment
ai_enriched_attrs = data_enrichments.where(source: "ai").pluck(:attribute_name).uniq
# Remove locks for AI-enriched attributes
ai_enriched_attrs.each do |attr|
unlock_attr!(attr) if locked?(attr)
end
# Delete AI enrichment records
data_enrichments.where(source: "ai").delete_all
end
end
private
def log_enrichment(attribute_name:, attribute_value:, source:, metadata: {})
de = DataEnrichment.find_or_create_by(

View File

@@ -80,6 +80,8 @@ class Setting < RailsSettings::Base
class << self
alias_method :raw_onboarding_state, :onboarding_state
alias_method :raw_onboarding_state=, :onboarding_state=
alias_method :raw_openai_model, :openai_model
alias_method :raw_openai_model=, :openai_model=
def onboarding_state
value = raw_onboarding_state
@@ -94,6 +96,18 @@ class Setting < RailsSettings::Base
self.raw_onboarding_state = state
end
def openai_model=(value)
old_value = raw_openai_model
self.raw_openai_model = value
if old_value != value && old_value.present?
Rails.logger.info("OpenAI model changed from #{old_value} to #{value}, clearing AI cache for all families")
Family.find_each do |family|
ClearAiCacheJob.perform_later(family)
end
end
end
# Support dynamic field access via bracket notation
# First checks if it's a declared field, then falls back to individual dynamic entries
def [](key)