mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 11:08:47 +02:00
Agent: resolve "today" in the browser's timezone, not hardcoded CR
The CR-hardcoded today broke two scenarios: the owner traveling, and any future non-CR user. Now the provider sends the browser's IANA timezone (X-Client-Timezone) with every CopilotKit request — the runtime forwards x-* headers to the agent on its own — and the backend binds it to a per-request ContextVar next to the DB session. get_current_date and the future-cuota bounds use it; the tool also reports the timezone so the model can echo it. Unknown or absent zones (n8n posts, tests) fall back to Costa Rica; comma-joined duplicate header values are tolerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,9 +21,10 @@ Context you can rely on:
|
||||
qualifiers, assume they mean the calendar month unless they mention
|
||||
"cycle", "corte", or their credit card.
|
||||
- You do NOT have a reliable clock, and the server runs in UTC while the
|
||||
user lives in Costa Rica (UTC-6). Whenever the question involves a
|
||||
relative date — "hoy", "ayer", "este mes", "este ciclo", "el año
|
||||
pasado" — call get_current_date FIRST and derive ranges from its answer.
|
||||
user may be anywhere (their browser reports their timezone per request).
|
||||
Whenever the question involves a relative date — "hoy", "ayer", "este
|
||||
mes", "este ciclo", "el año pasado" — call get_current_date FIRST and
|
||||
derive ranges from its answer.
|
||||
- Amounts are stored as raw numbers in their native currency (see `currency`
|
||||
field on transactions/accounts). Tools that return `total_crc` are already
|
||||
converted; tools that return per-transaction amounts are NOT.
|
||||
|
||||
@@ -41,7 +41,7 @@ from app.services.budget_projection import (
|
||||
get_cycle_range,
|
||||
)
|
||||
from app.services import exchange_rate as fx
|
||||
from app.timeutil import today_cr
|
||||
from app.timeutil import client_tz, today_client
|
||||
from app.services.exchange_rate import (
|
||||
get_converted_amount_expr,
|
||||
get_current_rate,
|
||||
@@ -145,8 +145,8 @@ def get_recent_transactions(
|
||||
),
|
||||
# Tasa Cero generates future-dated cuotas; "recent" means already
|
||||
# billed (same rule as /transactions/recent). Bound is end of the
|
||||
# user's today, Costa Rica time.
|
||||
Transaction.date < datetime.combine(today_cr() + timedelta(days=1), time.min),
|
||||
# user's today in their request timezone (CR fallback).
|
||||
Transaction.date < datetime.combine(today_client() + timedelta(days=1), time.min),
|
||||
)
|
||||
if source:
|
||||
q = q.where(Transaction.source == TransactionSource(source))
|
||||
@@ -519,7 +519,7 @@ def get_daily_spending(
|
||||
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),
|
||||
Transaction.date < datetime.combine(today_client() + timedelta(days=1), time.min),
|
||||
)
|
||||
.group_by(func.date(Transaction.date))
|
||||
.order_by(func.date(Transaction.date))
|
||||
@@ -531,12 +531,12 @@ def get_daily_spending(
|
||||
|
||||
|
||||
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
|
||||
relative date reference — 'hoy', 'ayer', 'este mes', 'este ciclo',
|
||||
'el ciclo pasado' — the server clock and your own assumptions about
|
||||
today are unreliable."""
|
||||
today = today_cr()
|
||||
"""Today's date in the user's own timezone (sent by their browser;
|
||||
Costa Rica fallback) and the active credit-card billing cycle. ALWAYS
|
||||
call this before resolving any relative date reference — 'hoy', 'ayer',
|
||||
'este mes', 'este ciclo', 'el ciclo pasado' — the server clock and your
|
||||
own assumptions about today are unreliable."""
|
||||
today = today_client()
|
||||
# get_cycle_range(y, m) is the cycle STARTING on the 18th of m; before
|
||||
# the 18th we are still in the cycle that started last month.
|
||||
if today.day >= 18:
|
||||
@@ -549,6 +549,7 @@ def get_current_date() -> dict:
|
||||
return {
|
||||
"date": today.isoformat(),
|
||||
"weekday": today.strftime("%A"),
|
||||
"timezone": str(client_tz()),
|
||||
"cycle_year": cycle_year,
|
||||
"cycle_month": cycle_month,
|
||||
"cycle_range": [start.date().isoformat(), end.date().isoformat()],
|
||||
|
||||
Reference in New Issue
Block a user