mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 09:08:46 +02:00
Agent: add get_daily_spending for day-scoped questions
Prod showed the failure mode after the date fix: get_current_date returned the right day, but the model answered "¿cuánto he gastado hoy?" from cycle-level aggregates (the only spend tools it had) and concluded ₡0. Give it a day-grained tool — per-day CRC totals mirroring /analytics/daily-spending (COMPRA only, no installment anchors, no future cuotas) — and a prompt rule to match tool to time grain. Verified: fresh thread now calls get_current_date → get_daily_spending(2026-07-04, 2026-07-05) and answers correctly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,10 @@ Context you can rely on:
|
||||
|
||||
How to answer:
|
||||
- ALWAYS call a tool to get data. Do not invent balances, dates or merchants.
|
||||
- Match the tool to the question's time grain: day-scoped questions ("hoy",
|
||||
"ayer", "el martes", a specific date) need get_daily_spending with that
|
||||
day's bounds — cycle or category summaries have NO per-day data, so never
|
||||
answer a day question from them.
|
||||
- Call multiple tools in parallel when the question spans domains
|
||||
(e.g. net worth + recent transactions).
|
||||
- Format currency with the appropriate symbol: ₡ for CRC (no decimals), $ for
|
||||
|
||||
@@ -35,6 +35,7 @@ from app.models.models import (
|
||||
from app.services.budget_projection import (
|
||||
MAX_YEAR,
|
||||
MIN_YEAR,
|
||||
NOT_INSTALLMENT_ANCHOR,
|
||||
compute_monthly_projection,
|
||||
compute_yearly_projection_with_cumulative,
|
||||
get_cycle_range,
|
||||
@@ -496,6 +497,39 @@ def list_categories() -> list[dict]:
|
||||
|
||||
|
||||
# Registered with the agent in agent.py
|
||||
def get_daily_spending(
|
||||
start_date: Annotated[str, Field(description="ISO date lower bound, inclusive")],
|
||||
end_date: Annotated[str, Field(description="ISO date upper bound, exclusive")],
|
||||
) -> list[dict]:
|
||||
"""Spending per day in CRC (converted), COMPRA only, excluding future
|
||||
Tasa Cero cuotas and installment anchors. THE tool for day-scoped
|
||||
questions: 'cuánto gasté hoy / ayer / el martes'. Days with no spending
|
||||
are simply absent from the result."""
|
||||
session = _s()
|
||||
amount_crc = get_converted_amount_expr(session)
|
||||
rows = session.exec(
|
||||
select(
|
||||
func.date(Transaction.date).label("day"),
|
||||
func.coalesce(func.sum(amount_crc), 0),
|
||||
func.count(),
|
||||
)
|
||||
.where(
|
||||
Transaction.transaction_type == TransactionType.COMPRA,
|
||||
NOT_INSTALLMENT_ANCHOR,
|
||||
Transaction.date >= datetime.fromisoformat(start_date),
|
||||
Transaction.date < datetime.fromisoformat(end_date),
|
||||
# Future-dated Tasa Cero cuotas are not money already spent.
|
||||
Transaction.date < datetime.combine(today_cr() + timedelta(days=1), time.min),
|
||||
)
|
||||
.group_by(func.date(Transaction.date))
|
||||
.order_by(func.date(Transaction.date))
|
||||
).all()
|
||||
return [
|
||||
{"date": str(day), "total_crc": float(total), "count": count}
|
||||
for day, total, count in rows
|
||||
]
|
||||
|
||||
|
||||
def get_current_date() -> dict:
|
||||
"""Today's date in the user's timezone (Costa Rica, UTC-6) and the
|
||||
active credit-card billing cycle. ALWAYS call this before resolving any
|
||||
@@ -523,6 +557,7 @@ def get_current_date() -> dict:
|
||||
|
||||
TOOLS = [
|
||||
get_current_date,
|
||||
get_daily_spending,
|
||||
get_accounts,
|
||||
get_net_worth,
|
||||
get_recent_transactions,
|
||||
|
||||
@@ -183,3 +183,34 @@ def test_get_current_date_is_costa_rica_and_cycle_consistent():
|
||||
start, end = out["cycle_range"]
|
||||
assert start <= out["date"] < end
|
||||
assert start.endswith("-18") and end.endswith("-18")
|
||||
|
||||
|
||||
def test_daily_spending_day_scoped(bound_session):
|
||||
from datetime import timedelta
|
||||
|
||||
from app.timeutil import today_cr
|
||||
|
||||
today = datetime.combine(today_cr(), datetime.min.time())
|
||||
bound_session.add(
|
||||
Transaction(amount=Decimal("100"), merchant="TODAY-A", date=today.replace(hour=9))
|
||||
)
|
||||
bound_session.add(
|
||||
Transaction(amount=Decimal("50"), merchant="TODAY-B", date=today.replace(hour=15))
|
||||
)
|
||||
bound_session.add(
|
||||
Transaction(
|
||||
amount=Decimal("999"),
|
||||
merchant="FUTURE CUOTA",
|
||||
date=today + timedelta(days=40),
|
||||
)
|
||||
)
|
||||
bound_session.commit()
|
||||
|
||||
out = tools.get_daily_spending(
|
||||
start_date=today.date().isoformat(),
|
||||
end_date=(today.date() + timedelta(days=60)).isoformat(),
|
||||
)
|
||||
assert len(out) == 1
|
||||
assert out[0]["date"] == today.date().isoformat()
|
||||
assert out[0]["total_crc"] == 150.0
|
||||
assert out[0]["count"] == 2
|
||||
|
||||
Reference in New Issue
Block a user