mirror of
https://github.com/we-promise/sure.git
synced 2026-05-31 08:19:03 +00:00
* feat(assistant): add get_budget function for budget tracking Exposes the existing Budget / BudgetCategory pacing data to the AI assistant as a `get_budget` function. Supports a target month and an optional `prior_months` window for trend comparison, with the response shape matching the budget UI (totals, income, per-category status, suggested daily spend on the current month). Honors custom month_start_day by matching `Budget.param_to_date` semantics for explicit slug input, so `month` round-trips with the response's `month` field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(assistant): use fixture reference for Food & Drink lookup Replace fragile string match on `bc.category.name == "Food & Drink"` with the `categories(:food_and_drink)` fixture so the test setup isn't sensitive to category-name translations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(assistant): enforce strict month format in get_budget `Date.strptime` is lenient about trailing characters, so inputs like `"2026-05-01"` or `"may-2026foo"` were parsing successfully and being silently truncated to May 2026. Pre-validate the raw string with anchored regex patterns for the documented YYYY-MM and MMM-YYYY shapes so malformed tool arguments raise Assistant::Error instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(budgets): suggested_daily_spending handles custom-month periods The helper compared `budget.start_date.month/year` against `Date.current.month/year` and returned nil whenever the current period straddled two calendar months — common for families with `month_start_day != 1` (e.g., May 15–Jun 14 viewed on Jun 1). Replace the calendar-month check with `budget.current?` and compute remaining days from `budget.end_date` so the helper works for both standard and custom periods. This also restores the daily pacing row in the budget UI for custom-month families. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(assistant): make get_budget read-only for prior months `prior_months: N` was calling `Budget.find_or_bootstrap` for every month, which created empty `Budget` rows (and synced `BudgetCategory` children) as a side effect of an AI query. Only the explicit target month now bootstraps; prior months use `Budget.find_by` and are dropped from the response if they don't exist. The response now includes `months_unavailable: N` so the LLM can phrase a sensible answer when fewer months come back than requested. Extract `Budget.period_for(date, family:)` to share the date-bracket math between `find_or_bootstrap`, `budget_date_valid?`, and the new read-only path in `get_budget`. Adds two tests covering the no-bootstrap behavior for prior months and the `prior_months` clamp at `MAX_PRIOR_MONTHS`. Updates the existing N+1 sorted-months test to seed prior budgets explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: wolstad <wesleyolstad@protonmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.0 KiB
Ruby
45 lines
1.0 KiB
Ruby
module Assistant
|
|
Error = Class.new(StandardError)
|
|
|
|
REGISTRY = {
|
|
"builtin" => Assistant::Builtin,
|
|
"external" => Assistant::External
|
|
}.freeze
|
|
|
|
class << self
|
|
def for_chat(chat)
|
|
implementation_for(chat).for_chat(chat)
|
|
end
|
|
|
|
def config_for(chat)
|
|
raise Error, "chat is required" if chat.blank?
|
|
Assistant::Builtin.config_for(chat)
|
|
end
|
|
|
|
def available_types
|
|
REGISTRY.keys
|
|
end
|
|
|
|
def function_classes
|
|
[
|
|
Function::GetTransactions,
|
|
Function::GetAccounts,
|
|
Function::GetHoldings,
|
|
Function::GetBalanceSheet,
|
|
Function::GetIncomeStatement,
|
|
Function::GetBudget,
|
|
Function::ImportBankStatement,
|
|
Function::SearchFamilyFiles
|
|
]
|
|
end
|
|
|
|
private
|
|
|
|
def implementation_for(chat)
|
|
raise Error, "chat is required" if chat.blank?
|
|
type = ENV["ASSISTANT_TYPE"].presence || chat.user&.family&.assistant_type.presence || "builtin"
|
|
REGISTRY.fetch(type) { REGISTRY["builtin"] }
|
|
end
|
|
end
|
|
end
|