feat: make birth-time rectification conversational

This commit is contained in:
Jesse_Chen
2026-07-23 13:12:09 +08:00
parent 200c39d22b
commit f9010c8797
34 changed files with 1720 additions and 177 deletions
+8 -1
View File
@@ -43,7 +43,7 @@ import varga # noqa: E402
AYANAMSA: Final = "lahiri"
NODE_MODE: Final = "mean"
INPUT_CONTRACT_VERSION: Final = "rectification-candidate-input-v1"
INPUT_CONTRACT_VERSION: Final = "rectification-candidate-input-v2"
DomainConfig = tuple[tuple[str, ...], tuple[int, ...]]
DOMAIN_CONFIG: Final[dict[EventDomain, DomainConfig]] = {
@@ -426,6 +426,13 @@ def _canonical_input_contract(request: RectificationEventRequest) -> tuple[dict,
"timezone_offset": float(request["tz"]),
"timezone_source": "explicit_offset",
},
"events": [{
"id": event["id"],
"domain": event["domain"],
"date": event["date"],
"precision": event["precision"],
"summary": event.get("summary", ""),
} for event in request["events"]],
"calculation": {
"ayanamsa": AYANAMSA,
"node_mode": NODE_MODE,
+2 -1
View File
@@ -9,7 +9,7 @@
from __future__ import annotations
from collections.abc import Sequence
from typing import Any, Final, Literal, TypedDict, assert_never
from typing import Any, Final, Literal, NotRequired, TypedDict, assert_never
from uuid import NAMESPACE_URL, uuid5
EventPrecision = Literal["year", "month", "day"]
@@ -44,6 +44,7 @@ class LifeEvent(TypedDict):
domain: EventDomain
date: str
precision: EventPrecision
summary: NotRequired[str]
class RectificationEventRequest(TypedDict):
+9 -2
View File
@@ -6944,8 +6944,11 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
allowed_domains = {'education', 'relocation', 'relationship', 'career', 'finance', 'health_pressure'}
formats = {'year': '%Y', 'month': '%Y-%m', 'day': '%Y-%m-%d'}
for raw_event in events:
if not isinstance(raw_event, dict) or set(raw_event) != {'id', 'domain', 'date', 'precision'}:
raise BadRequest('each event must contain only id, domain, date, and precision')
if not isinstance(raw_event, dict) or set(raw_event) not in (
{'id', 'domain', 'date', 'precision'},
{'id', 'domain', 'date', 'precision', 'summary'},
):
raise BadRequest('each event must contain id, domain, date, precision, and optional summary')
try:
uuid.UUID(str(raw_event['id']))
except (ValueError, TypeError, AttributeError) as exc:
@@ -6957,6 +6960,9 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
raise BadRequest('event domain is unsupported')
if precision not in formats or not isinstance(event_date, str):
raise BadRequest('event precision is unsupported')
summary = raw_event.get('summary', '')
if not isinstance(summary, str) or len(summary.strip()) > 1000:
raise BadRequest('event summary must be text up to 1000 characters')
try:
parsed_event_date = datetime.strptime(event_date, formats[precision])
except ValueError as exc:
@@ -6968,6 +6974,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
'domain': domain,
'date': event_date,
'precision': precision,
'summary': summary.strip(),
})
module = _load_local_module('active_rectification_events')
result = module.score_life_events({