feat(api): align routine context windows with recent skin history
This commit is contained in:
parent
1c457d62a3
commit
fecfa0b9e4
3 changed files with 273 additions and 71 deletions
|
|
@ -49,6 +49,9 @@ from innercontext.validators.routine_validator import RoutineValidationContext
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HISTORY_WINDOW_DAYS = 5
|
||||
SNAPSHOT_FALLBACK_DAYS = 14
|
||||
|
||||
|
||||
def _build_response_metadata(session: Session, log_id: Any) -> ResponseMetadata | None:
|
||||
"""Build ResponseMetadata from AICallLog for Phase 3 observability."""
|
||||
|
|
@ -284,12 +287,58 @@ def _ev(v: object) -> str:
|
|||
return str(v)
|
||||
|
||||
|
||||
def _build_skin_context(session: Session) -> str:
|
||||
def _get_recent_skin_snapshot(
|
||||
session: Session,
|
||||
reference_date: date,
|
||||
window_days: int = HISTORY_WINDOW_DAYS,
|
||||
fallback_days: int = SNAPSHOT_FALLBACK_DAYS,
|
||||
) -> SkinConditionSnapshot | None:
|
||||
window_cutoff = reference_date - timedelta(days=window_days)
|
||||
fallback_cutoff = reference_date - timedelta(days=fallback_days)
|
||||
|
||||
snapshot = session.exec(
|
||||
select(SkinConditionSnapshot).order_by(
|
||||
col(SkinConditionSnapshot.snapshot_date).desc()
|
||||
)
|
||||
select(SkinConditionSnapshot)
|
||||
.where(SkinConditionSnapshot.snapshot_date <= reference_date)
|
||||
.where(SkinConditionSnapshot.snapshot_date >= window_cutoff)
|
||||
.order_by(col(SkinConditionSnapshot.snapshot_date).desc())
|
||||
).first()
|
||||
if snapshot is not None:
|
||||
return snapshot
|
||||
|
||||
return session.exec(
|
||||
select(SkinConditionSnapshot)
|
||||
.where(SkinConditionSnapshot.snapshot_date <= reference_date)
|
||||
.where(SkinConditionSnapshot.snapshot_date >= fallback_cutoff)
|
||||
.order_by(col(SkinConditionSnapshot.snapshot_date).desc())
|
||||
).first()
|
||||
|
||||
|
||||
def _get_latest_skin_snapshot_within_days(
|
||||
session: Session,
|
||||
reference_date: date,
|
||||
max_age_days: int = SNAPSHOT_FALLBACK_DAYS,
|
||||
) -> SkinConditionSnapshot | None:
|
||||
cutoff = reference_date - timedelta(days=max_age_days)
|
||||
return session.exec(
|
||||
select(SkinConditionSnapshot)
|
||||
.where(SkinConditionSnapshot.snapshot_date <= reference_date)
|
||||
.where(SkinConditionSnapshot.snapshot_date >= cutoff)
|
||||
.order_by(col(SkinConditionSnapshot.snapshot_date).desc())
|
||||
).first()
|
||||
|
||||
|
||||
def _build_skin_context(
|
||||
session: Session,
|
||||
reference_date: date,
|
||||
window_days: int = HISTORY_WINDOW_DAYS,
|
||||
fallback_days: int = SNAPSHOT_FALLBACK_DAYS,
|
||||
) -> str:
|
||||
snapshot = _get_recent_skin_snapshot(
|
||||
session,
|
||||
reference_date=reference_date,
|
||||
window_days=window_days,
|
||||
fallback_days=fallback_days,
|
||||
)
|
||||
if snapshot is None:
|
||||
return "SKIN CONDITION: no data\n"
|
||||
ev = _ev
|
||||
|
|
@ -369,10 +418,15 @@ def _build_upcoming_grooming_context(
|
|||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def _build_recent_history(session: Session) -> str:
|
||||
cutoff = date.today() - timedelta(days=7)
|
||||
def _build_recent_history(
|
||||
session: Session,
|
||||
reference_date: date,
|
||||
window_days: int = HISTORY_WINDOW_DAYS,
|
||||
) -> str:
|
||||
cutoff = reference_date - timedelta(days=window_days)
|
||||
routines = session.exec(
|
||||
select(Routine)
|
||||
.where(Routine.routine_date <= reference_date)
|
||||
.where(Routine.routine_date >= cutoff)
|
||||
.order_by(col(Routine.routine_date).desc())
|
||||
).all()
|
||||
|
|
@ -672,14 +726,14 @@ def suggest_routine(
|
|||
session: Session = Depends(get_session),
|
||||
):
|
||||
weekday = data.routine_date.weekday()
|
||||
skin_ctx = _build_skin_context(session)
|
||||
skin_ctx = _build_skin_context(session, reference_date=data.routine_date)
|
||||
profile_ctx = build_user_profile_context(session, reference_date=data.routine_date)
|
||||
upcoming_grooming_ctx = _build_upcoming_grooming_context(
|
||||
session,
|
||||
start_date=data.routine_date,
|
||||
days=7,
|
||||
)
|
||||
history_ctx = _build_recent_history(session)
|
||||
history_ctx = _build_recent_history(session, reference_date=data.routine_date)
|
||||
day_ctx = _build_day_context(data.leaving_home)
|
||||
available_products = _get_available_products(
|
||||
session,
|
||||
|
|
@ -848,10 +902,10 @@ def suggest_routine(
|
|||
)
|
||||
|
||||
# Get skin snapshot for barrier state
|
||||
stmt = select(SkinConditionSnapshot).order_by(
|
||||
col(SkinConditionSnapshot.snapshot_date).desc()
|
||||
skin_snapshot = _get_latest_skin_snapshot_within_days(
|
||||
session,
|
||||
reference_date=data.routine_date,
|
||||
)
|
||||
skin_snapshot = session.exec(stmt).first()
|
||||
|
||||
# Build validation context
|
||||
products_by_id = {p.id: p for p in available_products}
|
||||
|
|
@ -923,9 +977,9 @@ def suggest_batch(
|
|||
{(data.from_date + timedelta(days=i)).weekday() for i in range(delta)}
|
||||
)
|
||||
profile_ctx = build_user_profile_context(session, reference_date=data.from_date)
|
||||
skin_ctx = _build_skin_context(session)
|
||||
skin_ctx = _build_skin_context(session, reference_date=data.from_date)
|
||||
grooming_ctx = _build_grooming_context(session, weekdays=weekdays)
|
||||
history_ctx = _build_recent_history(session)
|
||||
history_ctx = _build_recent_history(session, reference_date=data.from_date)
|
||||
batch_products = _get_available_products(
|
||||
session,
|
||||
include_minoxidil=data.include_minoxidil_beard,
|
||||
|
|
@ -1030,10 +1084,10 @@ def suggest_batch(
|
|||
)
|
||||
|
||||
# Get skin snapshot for barrier state
|
||||
stmt = select(SkinConditionSnapshot).order_by(
|
||||
col(SkinConditionSnapshot.snapshot_date).desc()
|
||||
skin_snapshot = _get_latest_skin_snapshot_within_days(
|
||||
session,
|
||||
reference_date=data.from_date,
|
||||
)
|
||||
skin_snapshot = session.exec(stmt).first()
|
||||
|
||||
# Build validation context
|
||||
products_by_id = {p.id: p for p in batch_products}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue