Files
sure/app/models/rule/action_executor/auto_categorize.rb
soky srm bb364fab38 LLM cost estimation (#223)
* Password reset back button also after confirmation

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>

* Implement a filter for category (#215)

- Also implement an is empty/is null condition.

* Implement an LLM cost estimation page

Track costs across all the cost categories: auto categorization, auto merchant detection and chat.
Show warning with estimated cost when running a rule that contains AI.

* Update pricing

* Add google pricing

and fix inferred model everywhere.

* Update app/models/llm_usage.rb

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: soky srm <sokysrm@gmail.com>

* FIX address review

* Linter

* Address review

- Lowered log level
- extracted the duplicated record_usage method into a shared concern

* Update app/controllers/settings/llm_usages_controller.rb

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: soky srm <sokysrm@gmail.com>

* Moved attr_reader out of private

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Signed-off-by: soky srm <sokysrm@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-24 00:08:59 +02:00

46 lines
1.5 KiB
Ruby

class Rule::ActionExecutor::AutoCategorize < Rule::ActionExecutor
def label
base_label = "Auto-categorize transactions with AI"
if rule.family.self_hoster?
# Use the same provider determination logic as Family::AutoCategorizer
llm_provider = Provider::Registry.get_provider(:openai)
if llm_provider
# Estimate cost for typical batch of 20 transactions
selected_model = Provider::Openai.effective_model
estimated_cost = LlmUsage.estimate_auto_categorize_cost(
transaction_count: 20,
category_count: rule.family.categories.count,
model: selected_model
)
suffix =
if estimated_cost.nil?
" (cost: N/A)"
else
" (~$#{sprintf('%.4f', estimated_cost)} per 20 transactions)"
end
"#{base_label}#{suffix}"
else
"#{base_label} (no LLM provider configured)"
end
else
base_label
end
end
def execute(transaction_scope, value: nil, ignore_attribute_locks: false)
enrichable_transactions = transaction_scope.enrichable(:category_id)
if enrichable_transactions.empty?
Rails.logger.info("No transactions to auto-categorize for #{rule.id}")
return
end
enrichable_transactions.in_batches(of: 20).each_with_index do |transactions, idx|
Rails.logger.info("Scheduling auto-categorization for batch #{idx + 1} of #{enrichable_transactions.count}")
rule.family.auto_categorize_transactions_later(transactions)
end
end
end