Class: Teri::AIIntegration
- Inherits:
-
Object
- Object
- Teri::AIIntegration
- Defined in:
- lib/teri/ai_integration.rb
Overview
Handles AI integration with OpenAI
Instance Attribute Summary collapse
-
#counterparty_hints ⇒ Object
readonly
Returns the value of attribute counterparty_hints.
-
#openai_client ⇒ Object
readonly
Returns the value of attribute openai_client.
-
#previous_codings ⇒ Object
readonly
Returns the value of attribute previous_codings.
Instance Method Summary collapse
-
#initialize(options, logger, log_file) ⇒ AIIntegration
constructor
A new instance of AIIntegration.
-
#load_previous_codings(file_adapter) ⇒ Object
Load previous codings from coding.ledger for AI suggestions.
-
#suggest_category(transaction, available_categories) ⇒ Object
Delegate suggest_category to OpenAI client.
-
#update_previous_codings(description, category, counterparty, hints) ⇒ Object
Update previous codings with a new transaction.
Constructor Details
#initialize(options, logger, log_file) ⇒ AIIntegration
Returns a new instance of AIIntegration.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/teri/ai_integration.rb', line 6 def initialize(, logger, log_file) @options = @logger = logger @log_file = log_file @previous_codings = {} @counterparty_hints = {} # Initialize OpenAI client if API key is provided if @options[:use_ai_suggestions] && (@options[:openai_api_key] || ENV.fetch('OPENAI_API_KEY', nil)) begin @openai_client = OpenAIClient.new(api_key: @options[:openai_api_key], log_file: @log_file) @logger&.info('OpenAI client initialized') rescue StandardError => e @logger&.error("Failed to initialize OpenAI client: #{e.}") puts "Warning: Failed to initialize OpenAI client: #{e.}" @openai_client = nil end end end |
Instance Attribute Details
#counterparty_hints ⇒ Object (readonly)
Returns the value of attribute counterparty_hints.
4 5 6 |
# File 'lib/teri/ai_integration.rb', line 4 def counterparty_hints @counterparty_hints end |
#openai_client ⇒ Object (readonly)
Returns the value of attribute openai_client.
4 5 6 |
# File 'lib/teri/ai_integration.rb', line 4 def openai_client @openai_client end |
#previous_codings ⇒ Object (readonly)
Returns the value of attribute previous_codings.
4 5 6 |
# File 'lib/teri/ai_integration.rb', line 4 def previous_codings @previous_codings end |
Instance Method Details
#load_previous_codings(file_adapter) ⇒ Object
Load previous codings from coding.ledger for AI suggestions
65 66 67 68 69 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/teri/ai_integration.rb', line 65 def load_previous_codings(file_adapter) @previous_codings = {} @counterparty_hints = {} # Check if coding.ledger exists return unless file_adapter.exist?('coding.ledger') # Parse coding.ledger to extract transaction descriptions and categories begin ledger = Ledger.parse('coding.ledger', file_adapter) # Process each transaction ledger.transactions.each do |transaction| next unless transaction[:description] && transaction[:entries] && !transaction[:entries].empty? # Find entries that are not Assets or Liabilities (likely the categorization) categorization_entries = transaction[:entries].reject do |entry| entry[:account].start_with?('Assets:', 'Liabilities:') end # Use the first categorization entry as the category next if categorization_entries.empty? counterparty = transaction[:counterparty] # Get hints if available hints = transaction[:metadata]&.select { |m| m[:key] == 'Hint' }&.map { |m| m[:value] } || [] # Update previous codings with this transaction update_previous_codings( transaction[:description], categorization_entries.first[:account], counterparty, hints ) end @logger&.info("Loaded #{@previous_codings.size - (@previous_codings[:by_counterparty] ? 1 : 0)} previous codings with hints for #{@counterparty_hints.size} counterparties") rescue StandardError => e @logger&.error("Failed to load previous codings: #{e.}") file_adapter.warning("Failed to load previous codings: #{e.}") if file_adapter.respond_to?(:warning) end end |
#suggest_category(transaction, available_categories) ⇒ Object
Delegate suggest_category to OpenAI client
27 28 29 30 31 32 |
# File 'lib/teri/ai_integration.rb', line 27 def suggest_category(transaction, available_categories) return nil unless @openai_client # Make self respond to previous_codings and counterparty_hints @openai_client.suggest_category(transaction, self) end |
#update_previous_codings(description, category, counterparty, hints) ⇒ Object
Update previous codings with a new transaction
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/teri/ai_integration.rb', line 35 def update_previous_codings(description, category, counterparty, hints) # Store by description for backward compatibility with tests @previous_codings[description] = { category: category, counterparty: counterparty, hints: hints, } # Also store by counterparty for the new functionality return unless counterparty @previous_codings[:by_counterparty] ||= {} @previous_codings[:by_counterparty][counterparty] ||= { transactions: [], hints: @counterparty_hints[counterparty] || [], } @previous_codings[:by_counterparty][counterparty][:transactions] << { description: description, category: category, } # Store hints by counterparty if counterparty && !hints.empty? @counterparty_hints[counterparty] ||= [] @counterparty_hints[counterparty].concat(hints) end end |