fix: make conversational rectification converge
This commit is contained in:
@@ -12,6 +12,21 @@ from collections.abc import Sequence
|
||||
from typing import Any, Final, Literal, NotRequired, TypedDict, assert_never
|
||||
from uuid import NAMESPACE_URL, uuid5
|
||||
|
||||
try:
|
||||
from scripts.rectification_policy import (
|
||||
MAX_CONFIRMATION_WIDTH_MINUTES,
|
||||
MIN_CONFIRMATION_DOMAINS,
|
||||
MIN_CONFIRMATION_EVENTS,
|
||||
MIN_CONFIRMATION_MARGIN_PERCENT,
|
||||
)
|
||||
except ModuleNotFoundError: # pragma: no cover - direct script execution
|
||||
from rectification_policy import (
|
||||
MAX_CONFIRMATION_WIDTH_MINUTES,
|
||||
MIN_CONFIRMATION_DOMAINS,
|
||||
MIN_CONFIRMATION_EVENTS,
|
||||
MIN_CONFIRMATION_MARGIN_PERCENT,
|
||||
)
|
||||
|
||||
EventPrecision = Literal["year", "month", "day"]
|
||||
EventDomain = Literal[
|
||||
"education",
|
||||
@@ -253,11 +268,11 @@ def adjudicate_candidate_rows(
|
||||
if reasons:
|
||||
confidence: Confidence = "low"
|
||||
elif (
|
||||
event_count >= 4
|
||||
and domain_count >= 3
|
||||
event_count >= MIN_CONFIRMATION_EVENTS
|
||||
and domain_count >= MIN_CONFIRMATION_DOMAINS
|
||||
and segment is not None
|
||||
and segment["width_minutes"] <= 5
|
||||
and margin >= 20
|
||||
and segment["width_minutes"] <= MAX_CONFIRMATION_WIDTH_MINUTES
|
||||
and margin >= MIN_CONFIRMATION_MARGIN_PERCENT
|
||||
):
|
||||
confidence = "high"
|
||||
else:
|
||||
|
||||
@@ -41,9 +41,25 @@ if SCRIPTS_DIR not in sys.path:
|
||||
|
||||
try:
|
||||
from scripts.local_env import load_local_env
|
||||
from scripts.rectification_policy import (
|
||||
MAX_CONFIRMATION_WIDTH_MINUTES,
|
||||
MAX_EXTERNAL_VALIDATION_WIDTH_MINUTES,
|
||||
MIN_CONFIRMATION_DOMAINS,
|
||||
MIN_CONFIRMATION_EVENTS,
|
||||
MIN_CONFIRMATION_MARGIN_PERCENT,
|
||||
MIN_SCORING_EVENTS,
|
||||
)
|
||||
from scripts.vedastro_runtime_context import temporary_timeout_seconds
|
||||
except ModuleNotFoundError: # pragma: no cover - script execution path
|
||||
from local_env import load_local_env
|
||||
from rectification_policy import (
|
||||
MAX_CONFIRMATION_WIDTH_MINUTES,
|
||||
MAX_EXTERNAL_VALIDATION_WIDTH_MINUTES,
|
||||
MIN_CONFIRMATION_DOMAINS,
|
||||
MIN_CONFIRMATION_EVENTS,
|
||||
MIN_CONFIRMATION_MARGIN_PERCENT,
|
||||
MIN_SCORING_EVENTS,
|
||||
)
|
||||
from vedastro_runtime_context import temporary_timeout_seconds
|
||||
try:
|
||||
from scripts.unified_consultation_orchestrator import UnifiedConsultationOrchestrator
|
||||
@@ -321,15 +337,39 @@ def _rectification_candidate_ready_for_external_validation(result):
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
return (
|
||||
int(result.get('event_count') or 0) >= 3
|
||||
and int(result.get('domain_count') or 0) >= 2
|
||||
and 1 <= width_minutes <= 15
|
||||
int(result.get('event_count') or 0) >= MIN_CONFIRMATION_EVENTS
|
||||
and int(result.get('domain_count') or 0) >= MIN_CONFIRMATION_DOMAINS
|
||||
and 1 <= width_minutes <= MAX_EXTERNAL_VALIDATION_WIDTH_MINUTES
|
||||
and bool(segment.get('representative_time'))
|
||||
and top_score > second_score
|
||||
and 'missing_mandatory_layers' not in (result.get('reasons') or [])
|
||||
)
|
||||
|
||||
|
||||
def _rectification_candidate_ready_for_confirmation(result):
|
||||
"""Keep final confirmation stricter than the external-validation entry gate."""
|
||||
segment = result.get('winning_segment')
|
||||
ranking = result.get('candidate_ranking_summary')
|
||||
if not isinstance(segment, dict) or not isinstance(ranking, list) or len(ranking) < 2:
|
||||
return False
|
||||
try:
|
||||
width_minutes = int(segment.get('width_minutes') or 0)
|
||||
top_score = float(result.get('top_score'))
|
||||
second_score = float(result.get('second_score'))
|
||||
margin_percent = float(result.get('margin_percent') or 0)
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
return (
|
||||
int(result.get('event_count') or 0) >= MIN_CONFIRMATION_EVENTS
|
||||
and int(result.get('domain_count') or 0) >= MIN_CONFIRMATION_DOMAINS
|
||||
and 1 <= width_minutes <= MAX_CONFIRMATION_WIDTH_MINUTES
|
||||
and bool(segment.get('representative_time'))
|
||||
and top_score > second_score
|
||||
and margin_percent >= MIN_CONFIRMATION_MARGIN_PERCENT
|
||||
and 'missing_mandatory_layers' not in (result.get('reasons') or [])
|
||||
)
|
||||
|
||||
|
||||
def build_evidence_packet_view(job_record: dict | None) -> dict:
|
||||
"""Public, token-protected job view. Excludes prompt internals and raw input."""
|
||||
job_record = job_record or {}
|
||||
@@ -7183,8 +7223,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
lon = self._get_float(body, 'lon', 0, -180, 180)
|
||||
tz = self._get_float(body, 'tz', 0, -14, 14)
|
||||
events = body.get('events')
|
||||
if not isinstance(events, list) or len(events) < 3:
|
||||
raise BadRequest('events must contain at least 3 items')
|
||||
if not isinstance(events, list) or len(events) < MIN_SCORING_EVENTS:
|
||||
raise BadRequest(f'events must contain at least {MIN_SCORING_EVENTS} item')
|
||||
normalized_events = []
|
||||
allowed_domains = {'education', 'relocation', 'relationship', 'career', 'finance', 'health_pressure'}
|
||||
formats = {'year': '%Y', 'month': '%Y-%m', 'day': '%Y-%m-%d'}
|
||||
@@ -7238,8 +7278,8 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'vedastro_reason': 'official_vedastro_runs_after_local_scoring_produces_a_narrow_candidate',
|
||||
'blockers': [],
|
||||
}
|
||||
local_candidate_ready = _rectification_candidate_ready_for_external_validation(result)
|
||||
if high_rigor and local_candidate_ready:
|
||||
external_validation_candidate_ready = _rectification_candidate_ready_for_external_validation(result)
|
||||
if high_rigor and external_validation_candidate_ready:
|
||||
from scripts.rectification_three_engine_packet import build_packet
|
||||
|
||||
representative_time = result['winning_segment']['representative_time']
|
||||
@@ -7430,6 +7470,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
}
|
||||
|
||||
from scripts.rectification_technique_contract import build_rectification_technique_contract
|
||||
local_candidate_ready = _rectification_candidate_ready_for_confirmation(result)
|
||||
result['technique_contract'] = build_rectification_technique_contract(
|
||||
event_count=result.get('event_count', 0),
|
||||
domain_count=result.get('domain_count', 0),
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Shared birth-time rectification convergence policy."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
_POLICY_PATH = Path(__file__).resolve().parents[1] / "references" / "rectification_policy.v1.json"
|
||||
POLICY: Final[dict[str, int | str]] = json.loads(_POLICY_PATH.read_text(encoding="utf-8"))
|
||||
|
||||
MIN_SCORING_EVENTS: Final = int(POLICY["minScoringEvents"])
|
||||
MIN_CONFIRMATION_EVENTS: Final = int(POLICY["minConfirmationEvents"])
|
||||
MIN_CONFIRMATION_DOMAINS: Final = int(POLICY["minConfirmationDomains"])
|
||||
MAX_EXTERNAL_VALIDATION_WIDTH_MINUTES: Final = int(POLICY["maxExternalValidationWidthMinutes"])
|
||||
MAX_CONFIRMATION_WIDTH_MINUTES: Final = int(POLICY["maxConfirmationWidthMinutes"])
|
||||
MIN_CONFIRMATION_MARGIN_PERCENT: Final = int(POLICY["minConfirmationMarginPercent"])
|
||||
MAX_PLATEAU_ROUNDS: Final = int(POLICY["maxPlateauRounds"])
|
||||
@@ -3,6 +3,14 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from scripts.rectification_policy import (
|
||||
MIN_CONFIRMATION_DOMAINS,
|
||||
MIN_CONFIRMATION_EVENTS,
|
||||
)
|
||||
except ModuleNotFoundError: # pragma: no cover - direct script execution
|
||||
from rectification_policy import MIN_CONFIRMATION_DOMAINS, MIN_CONFIRMATION_EVENTS
|
||||
|
||||
|
||||
def _gate(status: str, reason: str) -> dict[str, str]:
|
||||
return {"status": status, "reason": reason}
|
||||
@@ -21,9 +29,9 @@ def build_rectification_technique_contract(
|
||||
external_validation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
blockers: list[str] = []
|
||||
if event_count < 3:
|
||||
if event_count < MIN_CONFIRMATION_EVENTS:
|
||||
blockers.append("insufficient_events")
|
||||
if domain_count < 2:
|
||||
if domain_count < MIN_CONFIRMATION_DOMAINS:
|
||||
blockers.append("insufficient_domains")
|
||||
neighbor = (stability_diagnostics or {}).get("neighbor_stability") or {}
|
||||
leave_one_out = (stability_diagnostics or {}).get("leave_one_event_out") or {}
|
||||
@@ -38,17 +46,17 @@ def build_rectification_technique_contract(
|
||||
elif external_status != "pass":
|
||||
blockers.extend(external.get("blockers") or ["vedastro_validation_not_passed"])
|
||||
confirmation_allowed = (
|
||||
event_count >= 3
|
||||
and domain_count >= 2
|
||||
event_count >= MIN_CONFIRMATION_EVENTS
|
||||
and domain_count >= MIN_CONFIRMATION_DOMAINS
|
||||
and local_candidate_ready
|
||||
and required_layers_complete
|
||||
and high_rigor
|
||||
and external_status == "pass"
|
||||
)
|
||||
gates = {
|
||||
"event_quality": _gate("pass" if event_count >= 3 else "fail", "requires_at_least_three_dated_events"),
|
||||
"cross_domain_coverage": _gate("pass" if domain_count >= 2 else "fail", "requires_at_least_two_event_domains"),
|
||||
"local_candidate": _gate("pass" if local_candidate_ready else "fail", "requires_a_unique_leading_candidate_range_no_wider_than_fifteen_minutes"),
|
||||
"event_quality": _gate("pass" if event_count >= MIN_CONFIRMATION_EVENTS else "fail", "requires_confirmation_event_count"),
|
||||
"cross_domain_coverage": _gate("pass" if domain_count >= MIN_CONFIRMATION_DOMAINS else "fail", "requires_confirmation_domain_count"),
|
||||
"local_candidate": _gate("pass" if local_candidate_ready else "fail", "requires_final_confirmation_width_and_margin_policy"),
|
||||
"required_layers": _gate("pass" if required_layers_complete else "fail", "all_event_required_layers_must_compute"),
|
||||
"neighbor_stability": _gate("pass" if neighbor.get("all_required_passed") else "diagnostic_fail", "diagnostic_only_unique_lead_at_plus_minus_1_2_5_minutes"),
|
||||
"leave_one_event_out": _gate("pass" if leave_one_out.get("status") == "pass" else "diagnostic_fail", "diagnostic_only_leader_survival_after_removing_each_event"),
|
||||
|
||||
Reference in New Issue
Block a user