feat: add consultation and product domain registries
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Canonical consultation-domain registry shared by Python entry points."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
CANONICAL_DOMAINS = (
|
||||
"career",
|
||||
"marriage",
|
||||
"wealth",
|
||||
"health",
|
||||
"education",
|
||||
"migration",
|
||||
"family",
|
||||
"annual",
|
||||
"timing",
|
||||
"general",
|
||||
)
|
||||
DEFAULT_THEMES = ("career", "marriage", "wealth")
|
||||
DEFAULT_DOMAINS = DEFAULT_THEMES
|
||||
|
||||
DOMAIN_ALIASES = {
|
||||
"relationship": "marriage",
|
||||
"finance": "wealth",
|
||||
"money": "wealth",
|
||||
"health-pressure": "health",
|
||||
"health_pressure": "health",
|
||||
"home": "migration",
|
||||
"relocation": "migration",
|
||||
"foreign": "migration",
|
||||
"study": "education",
|
||||
"children": "family",
|
||||
"yearly": "annual",
|
||||
"varshaphala": "annual",
|
||||
"事业": "career",
|
||||
"婚恋": "marriage",
|
||||
"婚姻": "marriage",
|
||||
"感情": "marriage",
|
||||
"财富": "wealth",
|
||||
"财运": "wealth",
|
||||
"健康": "health",
|
||||
"迁移": "migration",
|
||||
"海外": "migration",
|
||||
"教育": "education",
|
||||
"学习": "education",
|
||||
"家庭": "family",
|
||||
"子女": "family",
|
||||
"年度": "annual",
|
||||
"流年": "annual",
|
||||
}
|
||||
|
||||
_CANONICAL_DOMAIN_SET = frozenset(CANONICAL_DOMAINS)
|
||||
|
||||
|
||||
def normalize_domain(value: Any) -> str:
|
||||
"""Return one canonical domain or reject the value as unknown."""
|
||||
key = str(value).strip().lower()
|
||||
canonical = DOMAIN_ALIASES.get(key, key)
|
||||
if canonical not in _CANONICAL_DOMAIN_SET:
|
||||
raise ValueError(f"Unknown theme: {value}")
|
||||
return canonical
|
||||
|
||||
|
||||
def normalize_themes(raw: Any) -> list[str]:
|
||||
"""Normalize themes, preserving first-seen order and failing closed."""
|
||||
if raw is None or raw == "" or (isinstance(raw, str) and raw.strip().lower() == "all"):
|
||||
values = list(DEFAULT_THEMES)
|
||||
elif isinstance(raw, str):
|
||||
values = [raw]
|
||||
elif isinstance(raw, list):
|
||||
values = raw
|
||||
else:
|
||||
raise ValueError("theme/themes must be a string, list, or all")
|
||||
|
||||
if not values:
|
||||
return list(DEFAULT_THEMES)
|
||||
|
||||
normalized: list[str] = []
|
||||
for value in values:
|
||||
canonical = normalize_domain(value)
|
||||
if canonical not in normalized:
|
||||
normalized.append(canonical)
|
||||
return normalized
|
||||
+112
-19
@@ -65,6 +65,10 @@ try:
|
||||
from scripts.unified_consultation_orchestrator import UnifiedConsultationOrchestrator
|
||||
except ModuleNotFoundError: # pragma: no cover - script execution path
|
||||
from unified_consultation_orchestrator import UnifiedConsultationOrchestrator
|
||||
try:
|
||||
from scripts.consultation_domain_registry import CANONICAL_DOMAINS
|
||||
except ModuleNotFoundError: # pragma: no cover - script execution path
|
||||
from consultation_domain_registry import CANONICAL_DOMAINS
|
||||
try:
|
||||
from scripts.skill_experience import (
|
||||
build_rectification_questionnaire,
|
||||
@@ -1085,7 +1089,7 @@ def execute_consultation_workflow(
|
||||
'mode': 'vedastro_official_first_existing_modules_reused',
|
||||
'entry_mode': entry_mode,
|
||||
'question': question,
|
||||
'routes': ['career', 'relationship', 'finance'],
|
||||
'routes': list(CANONICAL_DOMAINS),
|
||||
'themes': themes,
|
||||
'routing': route_packet,
|
||||
'unified_orchestrator': unified_contract,
|
||||
@@ -2861,6 +2865,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
|
||||
custom_evidence = body.get('evidence')
|
||||
has_custom_evidence = isinstance(custom_evidence, dict) and bool(custom_evidence)
|
||||
adapter_evidence = {}
|
||||
derived_context = None
|
||||
strict_workflow_contracts = upstream_contract.get('strict_workflow_contracts') if isinstance(upstream_contract.get('strict_workflow_contracts'), dict) else {}
|
||||
upstream_guided_topics = upstream_contract.get('guided_topics') if isinstance(upstream_contract.get('guided_topics'), list) else []
|
||||
@@ -2874,10 +2879,14 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
orchestrator = report_orchestrator.ThematicReportOrchestrator(chart)
|
||||
|
||||
if has_custom_evidence:
|
||||
self._inject_thematic_evidence(orchestrator, custom_evidence, report_orchestrator)
|
||||
adapter_evidence = self._inject_thematic_evidence(orchestrator, custom_evidence, report_orchestrator)
|
||||
mode = 'custom_evidence'
|
||||
elif derived_context and derived_context.get('evidence'):
|
||||
self._inject_thematic_evidence(orchestrator, derived_context['evidence'], report_orchestrator)
|
||||
adapter_evidence = self._inject_thematic_evidence(
|
||||
orchestrator,
|
||||
derived_context['evidence'],
|
||||
report_orchestrator,
|
||||
)
|
||||
mode = 'derived_chart_evidence'
|
||||
elif strict_workflow_contracts:
|
||||
mode = 'upstream_contract_reuse'
|
||||
@@ -2887,11 +2896,49 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
|
||||
theme_values = self._requested_thematic_report_themes(body, report_orchestrator)
|
||||
reports = {}
|
||||
for theme in theme_values:
|
||||
report = orchestrator.generate_report(theme)
|
||||
reports[theme.value] = self._apply_monthly_adjudication_to_theme_report(
|
||||
theme.value,
|
||||
report.to_dict(),
|
||||
native_theme_map = {theme.value: theme for theme in report_orchestrator.ThemeName}
|
||||
consultation_capabilities = report_orchestrator.consultation_thematic_capabilities()
|
||||
for theme_name in theme_values:
|
||||
adapter = consultation_capabilities.get(theme_name)
|
||||
native_theme = native_theme_map.get(theme_name)
|
||||
if adapter and adapter.get('report_theme'):
|
||||
native_theme = native_theme_map[adapter['report_theme']]
|
||||
if native_theme is not None:
|
||||
report = orchestrator.generate_report(native_theme)
|
||||
report_payload = self._apply_monthly_adjudication_to_theme_report(
|
||||
theme_name,
|
||||
report.to_dict(),
|
||||
)
|
||||
report_payload['theme'] = theme_name
|
||||
report_payload['status'] = 'supported'
|
||||
report_payload['thematic_adapter'] = (
|
||||
{
|
||||
**adapter,
|
||||
'status': 'supported',
|
||||
'scope': 'canonical_consultation',
|
||||
}
|
||||
if adapter
|
||||
else {
|
||||
'domain': theme_name,
|
||||
'capability_status': 'supported',
|
||||
'report_theme': native_theme.value,
|
||||
'reason': None,
|
||||
'fallback_theme': None,
|
||||
'status': 'supported',
|
||||
'scope': 'independent_report',
|
||||
}
|
||||
)
|
||||
reports[theme_name] = report_payload
|
||||
continue
|
||||
|
||||
reports[theme_name] = report_orchestrator.build_unavailable_consultation_theme_report(
|
||||
theme_name,
|
||||
evidence=adapter_evidence.get(theme_name, []),
|
||||
upstream_contract_available=self._thematic_upstream_contract_available(
|
||||
strict_workflow_contracts,
|
||||
theme_name,
|
||||
report_orchestrator,
|
||||
),
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -2923,7 +2970,12 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
),
|
||||
'themes': reports,
|
||||
'theme_count': len(reports),
|
||||
'available_themes': [theme.value for theme in report_orchestrator.ThemeName],
|
||||
'available_themes': list(CANONICAL_DOMAINS),
|
||||
'consultation_thematic_capabilities': consultation_capabilities,
|
||||
'native_report_themes': [theme.value for theme in report_orchestrator.ThemeName],
|
||||
'independent_report_themes': [
|
||||
theme.value for theme in report_orchestrator.INDEPENDENT_REPORT_THEMES
|
||||
],
|
||||
'boundary': '主题化报告用于组织证据、裁决冲突和生成叙事;具体预测仍需本命承诺、Dasha、Transit 与案例验证共同收敛。',
|
||||
}
|
||||
|
||||
@@ -3104,7 +3156,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'success': True,
|
||||
'endpoint': 'high_rigor_workflow',
|
||||
'mode': 'plan_only_no_external_calls',
|
||||
'routes': ['career', 'relationship', 'finance'],
|
||||
'routes': list(CANONICAL_DOMAINS),
|
||||
'themes': themes,
|
||||
'event_count': len(events),
|
||||
'source_priority': {
|
||||
@@ -3658,13 +3710,18 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'label': theme.name,
|
||||
})
|
||||
report_themes = [theme.value for theme in getattr(report_orchestrator, 'ThemeName')]
|
||||
selected = [theme.value for theme in theme_values]
|
||||
selected = list(theme_values)
|
||||
return {
|
||||
'stage': 'report_pipeline_bridge',
|
||||
'reading_theme_count': len(reading_themes),
|
||||
'reading_themes': reading_themes,
|
||||
'report_themes': report_themes,
|
||||
'selected_report_themes': selected,
|
||||
'canonical_consultation_domains': list(CANONICAL_DOMAINS),
|
||||
'consultation_thematic_capabilities': report_orchestrator.consultation_thematic_capabilities(),
|
||||
'independent_report_themes': [
|
||||
theme.value for theme in report_orchestrator.INDEPENDENT_REPORT_THEMES
|
||||
],
|
||||
'bridge': {
|
||||
'class': getattr(orchestrator_bridge, 'OrchestratorBridge').__name__,
|
||||
'capabilities': [
|
||||
@@ -3686,14 +3743,35 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
values = raw
|
||||
else:
|
||||
raise BadRequest('theme/themes must be a string, list, or all')
|
||||
mapping = {theme.value: theme for theme in report_orchestrator.ThemeName}
|
||||
native_theme_names = {theme.value for theme in report_orchestrator.ThemeName}
|
||||
themes = []
|
||||
for value in values:
|
||||
key = str(value).strip().lower()
|
||||
if key not in mapping:
|
||||
raise BadRequest(f'Unknown thematic report theme: {value}')
|
||||
themes.append(mapping[key])
|
||||
return themes or list(report_orchestrator.ThemeName)
|
||||
if key not in native_theme_names:
|
||||
try:
|
||||
key = report_orchestrator.consultation_thematic_adapter(key).domain
|
||||
except ValueError as exc:
|
||||
raise BadRequest(f'Unknown thematic report theme: {value}') from exc
|
||||
if key not in themes:
|
||||
themes.append(key)
|
||||
return themes or [theme.value for theme in report_orchestrator.ThemeName]
|
||||
|
||||
def _thematic_upstream_contract_available(
|
||||
self,
|
||||
strict_workflow_contracts,
|
||||
domain,
|
||||
report_orchestrator,
|
||||
):
|
||||
if not isinstance(strict_workflow_contracts, dict):
|
||||
return False
|
||||
for raw_domain, contract in strict_workflow_contracts.items():
|
||||
try:
|
||||
canonical = report_orchestrator.consultation_thematic_adapter(raw_domain).domain
|
||||
except ValueError:
|
||||
continue
|
||||
if canonical == domain and bool(contract):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _build_thematic_chart_data(self, raw, report_orchestrator):
|
||||
if not isinstance(raw, dict) or not raw:
|
||||
@@ -3775,10 +3853,18 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
def _inject_thematic_evidence(self, orchestrator, evidence, report_orchestrator):
|
||||
theme_map = {theme.value: theme for theme in report_orchestrator.ThemeName}
|
||||
strength_map = {level.value: level for level in report_orchestrator.StrengthLevel}
|
||||
adapter_evidence = {}
|
||||
for theme_name, items in evidence.items():
|
||||
theme = theme_map.get(str(theme_name).strip().lower())
|
||||
key = str(theme_name).strip().lower()
|
||||
theme = theme_map.get(key)
|
||||
canonical_domain = None
|
||||
if not theme:
|
||||
raise BadRequest(f'Unknown evidence theme: {theme_name}')
|
||||
try:
|
||||
adapter = report_orchestrator.consultation_thematic_adapter(key)
|
||||
except ValueError as exc:
|
||||
raise BadRequest(f'Unknown evidence theme: {theme_name}') from exc
|
||||
canonical_domain = adapter.domain
|
||||
theme = adapter.report_theme
|
||||
if not isinstance(items, list):
|
||||
raise BadRequest('evidence theme values must be arrays')
|
||||
results = []
|
||||
@@ -3796,7 +3882,14 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
strength=strength,
|
||||
details=item.get('details') if isinstance(item.get('details'), dict) else {},
|
||||
))
|
||||
orchestrator.add_techniques(theme, results)
|
||||
if theme is not None:
|
||||
orchestrator.add_techniques(theme, results)
|
||||
else:
|
||||
adapter_evidence[canonical_domain] = [
|
||||
orchestrator._technique_to_dict(result)
|
||||
for result in results
|
||||
]
|
||||
return adapter_evidence
|
||||
|
||||
def _can_derive_thematic_evidence(self, raw):
|
||||
if not isinstance(raw, dict) or not raw:
|
||||
|
||||
@@ -23,6 +23,11 @@ from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Callable
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from consultation_domain_registry import CANONICAL_DOMAINS, normalize_domain
|
||||
except ImportError: # pragma: no cover - package import path
|
||||
from scripts.consultation_domain_registry import CANONICAL_DOMAINS, normalize_domain
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 核心枚举与常量
|
||||
@@ -44,6 +49,96 @@ class ThemeName(str, Enum):
|
||||
SPIRITUALITY = "spirituality"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ConsultationThematicAdapter:
|
||||
"""Explicit bridge from a canonical consultation domain to this legacy report enum."""
|
||||
|
||||
domain: str
|
||||
capability_status: str
|
||||
report_theme: Optional[ThemeName]
|
||||
reason: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"domain": self.domain,
|
||||
"capability_status": self.capability_status,
|
||||
"report_theme": self.report_theme.value if self.report_theme else None,
|
||||
"reason": self.reason,
|
||||
"fallback_theme": None,
|
||||
}
|
||||
|
||||
|
||||
_NATIVE_CONSULTATION_REPORT_THEMES = {
|
||||
"career": ThemeName.CAREER,
|
||||
"marriage": ThemeName.MARRIAGE,
|
||||
"wealth": ThemeName.WEALTH,
|
||||
"health": ThemeName.HEALTH,
|
||||
}
|
||||
|
||||
CONSULTATION_THEMATIC_ADAPTERS: Dict[str, ConsultationThematicAdapter] = {
|
||||
domain: ConsultationThematicAdapter(
|
||||
domain=domain,
|
||||
capability_status="supported" if domain in _NATIVE_CONSULTATION_REPORT_THEMES else "blocked",
|
||||
report_theme=_NATIVE_CONSULTATION_REPORT_THEMES.get(domain),
|
||||
reason=(
|
||||
None
|
||||
if domain in _NATIVE_CONSULTATION_REPORT_THEMES
|
||||
else "dedicated_thematic_report_enum_unavailable"
|
||||
),
|
||||
)
|
||||
for domain in CANONICAL_DOMAINS
|
||||
}
|
||||
|
||||
INDEPENDENT_REPORT_THEMES = (ThemeName.SPIRITUALITY,)
|
||||
|
||||
|
||||
def consultation_thematic_adapter(value: Any) -> ConsultationThematicAdapter:
|
||||
"""Resolve a canonical consultation domain without falling back to ``general``."""
|
||||
return CONSULTATION_THEMATIC_ADAPTERS[normalize_domain(value)]
|
||||
|
||||
|
||||
def consultation_thematic_capabilities() -> Dict[str, Dict[str, Any]]:
|
||||
"""Return the ordered consultation adapter catalog for API capability surfaces."""
|
||||
return {
|
||||
domain: CONSULTATION_THEMATIC_ADAPTERS[domain].to_dict()
|
||||
for domain in CANONICAL_DOMAINS
|
||||
}
|
||||
|
||||
|
||||
def build_unavailable_consultation_theme_report(
|
||||
domain: Any,
|
||||
*,
|
||||
evidence: Optional[List[Dict[str, Any]]] = None,
|
||||
upstream_contract_available: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
"""Build an explicit degraded/blocked result instead of impersonating another theme."""
|
||||
adapter = consultation_thematic_adapter(domain)
|
||||
if adapter.report_theme is not None:
|
||||
raise ValueError(f"Native thematic report is available for: {adapter.domain}")
|
||||
normalized_evidence = list(evidence or [])
|
||||
runtime_status = "degraded" if normalized_evidence or upstream_contract_available else "blocked"
|
||||
return {
|
||||
"theme": adapter.domain,
|
||||
"status": runtime_status,
|
||||
"summary": "该咨询域暂无独立 thematic report 适配器。",
|
||||
"narrative": "",
|
||||
"evidence": normalized_evidence,
|
||||
"strength": "weak",
|
||||
"timing": None,
|
||||
"conflicts": [],
|
||||
"recommendations": [],
|
||||
"thematic_adapter": {
|
||||
**adapter.to_dict(),
|
||||
"status": runtime_status,
|
||||
"upstream_contract_available": bool(upstream_contract_available),
|
||||
},
|
||||
"boundary": (
|
||||
"The canonical consultation domain remains available in the unified workflow, but this legacy thematic "
|
||||
"report enum has no dedicated implementation. No general-theme fallback was used."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# 经典矛盾裁决优先级:D9 > D1(婚姻),D10 > D1(事业),D30 > D1(健康)
|
||||
# 数值越大优先级越高
|
||||
CHART_PRIORITY = {
|
||||
|
||||
@@ -9,6 +9,27 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
from consultation_domain_registry import (
|
||||
CANONICAL_DOMAINS,
|
||||
DEFAULT_THEMES,
|
||||
DOMAIN_ALIASES,
|
||||
normalize_domain,
|
||||
)
|
||||
from consultation_domain_registry import (
|
||||
normalize_themes as normalize_consultation_themes,
|
||||
)
|
||||
except ImportError: # pragma: no cover - import path varies in tests/CLI
|
||||
from scripts.consultation_domain_registry import (
|
||||
CANONICAL_DOMAINS,
|
||||
DEFAULT_THEMES,
|
||||
DOMAIN_ALIASES,
|
||||
normalize_domain,
|
||||
)
|
||||
from scripts.consultation_domain_registry import (
|
||||
normalize_themes as normalize_consultation_themes,
|
||||
)
|
||||
|
||||
|
||||
def _formal_divisions() -> tuple[int, ...]:
|
||||
registry = Path(__file__).resolve().parents[1] / "references/oracle/d1_d60_varga_mapping_registry_2026_07_19.json"
|
||||
@@ -88,52 +109,9 @@ class UnifiedConsultationOrchestrator:
|
||||
"vedastro_official_raw_response",
|
||||
"vedastro_official_raw_archive_manifest",
|
||||
]
|
||||
_THEME_ALIASES = {
|
||||
"relationship": "marriage",
|
||||
"marriage": "marriage",
|
||||
"finance": "wealth",
|
||||
"money": "wealth",
|
||||
"wealth": "wealth",
|
||||
"career": "career",
|
||||
"health": "health",
|
||||
"migration": "migration",
|
||||
"foreign": "migration",
|
||||
"education": "education",
|
||||
"study": "education",
|
||||
"family": "family",
|
||||
"children": "family",
|
||||
"annual": "annual",
|
||||
"yearly": "annual",
|
||||
"spirituality": "spirituality",
|
||||
"事业": "career",
|
||||
"婚恋": "marriage",
|
||||
"婚姻": "marriage",
|
||||
"感情": "marriage",
|
||||
"财富": "wealth",
|
||||
"财运": "wealth",
|
||||
"健康": "health",
|
||||
"迁移": "migration",
|
||||
"海外": "migration",
|
||||
"教育": "education",
|
||||
"学习": "education",
|
||||
"家庭": "family",
|
||||
"子女": "family",
|
||||
"年度": "annual",
|
||||
"流年": "annual",
|
||||
"灵性": "spirituality",
|
||||
}
|
||||
_DEFAULT_THEMES = ["career", "marriage", "wealth"]
|
||||
_ALLOWED_THEMES = {
|
||||
"annual",
|
||||
"career",
|
||||
"education",
|
||||
"family",
|
||||
"health",
|
||||
"marriage",
|
||||
"migration",
|
||||
"spirituality",
|
||||
"wealth",
|
||||
}
|
||||
_THEME_ALIASES = DOMAIN_ALIASES
|
||||
_DEFAULT_THEMES = list(DEFAULT_THEMES)
|
||||
_ALLOWED_THEMES = set(CANONICAL_DOMAINS)
|
||||
_ROUTE_DEFINITIONS = {
|
||||
"career": RouteDefinition(
|
||||
question_type="career",
|
||||
@@ -141,17 +119,17 @@ class UnifiedConsultationOrchestrator:
|
||||
focus_techniques=["D10", "Dasha", "Shadbala", "Transit", "Narayana Dasha"],
|
||||
display_label="career",
|
||||
),
|
||||
"relationship": RouteDefinition(
|
||||
question_type="relationship",
|
||||
"marriage": RouteDefinition(
|
||||
question_type="marriage",
|
||||
primary_theme="marriage",
|
||||
focus_techniques=["D9", "UL Upapada", "Dasha", "Nakshatra", "Vivah Saham"],
|
||||
display_label="relationship",
|
||||
display_label="marriage",
|
||||
),
|
||||
"finance": RouteDefinition(
|
||||
question_type="finance",
|
||||
"wealth": RouteDefinition(
|
||||
question_type="wealth",
|
||||
primary_theme="wealth",
|
||||
focus_techniques=["D2", "D11", "Dasha", "Shadbala", "Ashtakavarga"],
|
||||
display_label="finance",
|
||||
display_label="wealth",
|
||||
),
|
||||
"health": RouteDefinition(
|
||||
question_type="health",
|
||||
@@ -159,6 +137,12 @@ class UnifiedConsultationOrchestrator:
|
||||
focus_techniques=["D1", "D6", "D8", "Dasha", "Shadbala", "non-medical boundary"],
|
||||
display_label="health",
|
||||
),
|
||||
"education": RouteDefinition(
|
||||
question_type="education",
|
||||
primary_theme="education",
|
||||
focus_techniques=["D5", "D24", "5th house", "9th house", "Dasha"],
|
||||
display_label="education",
|
||||
),
|
||||
"migration": RouteDefinition(
|
||||
question_type="migration",
|
||||
primary_theme="migration",
|
||||
@@ -171,12 +155,6 @@ class UnifiedConsultationOrchestrator:
|
||||
focus_techniques=["D7", "D12", "4th house", "5th house", "9th house", "Dasha"],
|
||||
display_label="family",
|
||||
),
|
||||
"education": RouteDefinition(
|
||||
question_type="education",
|
||||
primary_theme="education",
|
||||
focus_techniques=["D5", "D24", "5th house", "9th house", "Dasha"],
|
||||
display_label="education",
|
||||
),
|
||||
"annual": RouteDefinition(
|
||||
question_type="annual",
|
||||
primary_theme="annual",
|
||||
@@ -185,21 +163,21 @@ class UnifiedConsultationOrchestrator:
|
||||
),
|
||||
"timing": RouteDefinition(
|
||||
question_type="timing",
|
||||
primary_theme="career",
|
||||
primary_theme="timing",
|
||||
focus_techniques=["Dasha", "Transit", "Double Transit", "Gochara"],
|
||||
display_label="timing",
|
||||
),
|
||||
"general": RouteDefinition(
|
||||
question_type="general",
|
||||
primary_theme="career",
|
||||
primary_theme="general",
|
||||
focus_techniques=["D1", "D9", "Dasha", "Yoga", "Shadbala", "Ashtakavarga"],
|
||||
display_label="general",
|
||||
),
|
||||
}
|
||||
_SYNC_STEPS_BY_ROUTE = {
|
||||
"career": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"relationship": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"finance": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"marriage": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"wealth": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"health": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"migration": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
"family": ["compute_chart", "run_rectification_gate", "run_thematic_report"],
|
||||
@@ -214,9 +192,9 @@ class UnifiedConsultationOrchestrator:
|
||||
"extended_prompt_pack_refresh",
|
||||
]
|
||||
_DOMAIN_PROFILE_SECTIONS = {
|
||||
"relationship": ["core_partner_profile", "temperament_and_compatibility", "timing_windows", "red_flags", "verification_questions"],
|
||||
"marriage": ["core_partner_profile", "temperament_and_compatibility", "timing_windows", "red_flags", "verification_questions"],
|
||||
"career": ["career_direction", "role_and_responsibility", "income_and_recognition", "opportunity_windows", "risks_and_verification_questions"],
|
||||
"finance": ["wealth_path", "income_structure", "asset_and_cashflow_pattern", "opportunity_windows", "verification_questions"],
|
||||
"wealth": ["wealth_path", "income_structure", "asset_and_cashflow_pattern", "opportunity_windows", "verification_questions"],
|
||||
"health": ["non_medical_pattern", "pressure_factors", "protective_factors", "verification_questions"],
|
||||
"migration": ["relocation_pattern", "foreign_link", "candidate_windows", "verification_questions"],
|
||||
"family": ["family_structure", "home_and_care", "children_boundary", "verification_questions"],
|
||||
@@ -234,7 +212,8 @@ class UnifiedConsultationOrchestrator:
|
||||
"family": ("D1", "D7", "D12"),
|
||||
"education": ("D1", "D5", "D24"),
|
||||
"annual": ("D1", "D9", "D10"),
|
||||
"spirituality": ("D1", "D20", "D24", "D60"),
|
||||
"timing": ("D1", "D9", "D10"),
|
||||
"general": ("D1", "D9"),
|
||||
}
|
||||
_THEME_TECHNIQUE_IDENTIFIERS = {
|
||||
"career": {"D10", "A10"},
|
||||
@@ -245,13 +224,17 @@ class UnifiedConsultationOrchestrator:
|
||||
"family": {"D7", "D12"},
|
||||
"education": {"D5", "D24"},
|
||||
"annual": {"DASHA", "TRANSIT", "TAJIKA"},
|
||||
"spirituality": {"D20", "D60"},
|
||||
"timing": {"DASHA", "TRANSIT", "GOCHARA"},
|
||||
"general": {"D1", "D9"},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def route_profile_contract(cls, route_name: str) -> dict[str, Any]:
|
||||
"""Expose reader sections without replacing runtime technique audit."""
|
||||
route = route_name if route_name in cls._DOMAIN_PROFILE_SECTIONS else "general"
|
||||
try:
|
||||
route = normalize_domain(route_name)
|
||||
except ValueError:
|
||||
route = "general"
|
||||
return {
|
||||
"version": "domain_profile_v1",
|
||||
"route": route,
|
||||
@@ -264,36 +247,20 @@ class UnifiedConsultationOrchestrator:
|
||||
}
|
||||
|
||||
def normalize_themes(self, raw: Any) -> list[str]:
|
||||
if raw in (None, "", "all"):
|
||||
values = list(self._DEFAULT_THEMES)
|
||||
elif isinstance(raw, str):
|
||||
values = [raw]
|
||||
elif isinstance(raw, list):
|
||||
values = raw
|
||||
else:
|
||||
raise ValueError("theme/themes must be a string, list, or all")
|
||||
|
||||
normalized: list[str] = []
|
||||
for value in values:
|
||||
key = self._THEME_ALIASES.get(str(value).strip().lower(), str(value).strip().lower())
|
||||
if key not in self._ALLOWED_THEMES:
|
||||
raise ValueError(f"Unknown theme: {value}")
|
||||
if key not in normalized:
|
||||
normalized.append(key)
|
||||
return normalized or list(self._DEFAULT_THEMES)
|
||||
return normalize_consultation_themes(raw)
|
||||
|
||||
def resolve_route(self, question: str, themes: list[str] | None = None) -> dict[str, Any]:
|
||||
text = (question or "").lower()
|
||||
normalized_themes = themes or list(self._DEFAULT_THEMES)
|
||||
normalized_themes = self.normalize_themes(themes)
|
||||
explicit_timing_tokens = ("when", "timing", "何时", "什么时候", "应期", "几月", "哪月", "哪天", "日期")
|
||||
|
||||
domain_tokens = {
|
||||
"career": ("career", "job", "work", "promotion", "business", "profession", "事业", "工作", "升职", "生意"),
|
||||
"relationship": ("marriage", "married", "wedding", "relationship", "love", "spouse", "partner", "divorce", "婚恋", "婚姻", "感情", "配偶", "恋爱", "结婚", "marry"),
|
||||
"finance": ("money", "wealth", "finance", "investment", "property", "income", "财务", "财富", "投资", "房产", "收入"),
|
||||
"marriage": ("marriage", "married", "wedding", "relationship", "love", "spouse", "partner", "divorce", "婚恋", "婚姻", "感情", "配偶", "恋爱", "结婚", "marry"),
|
||||
"wealth": ("money", "wealth", "finance", "investment", "property", "income", "财务", "财富", "投资", "房产", "收入"),
|
||||
"health": ("health", "illness", "medical", "disease", "vitality", "健康", "疾病", "病", "体力", "医疗"),
|
||||
"migration": ("migration", "foreign", "abroad", "overseas", "relocation", "迁移", "海外", "出国", "搬迁", "远方"),
|
||||
"family": ("family", "children", "home", "mother", "father", "家庭", "子女", "孩子", "父母", "家宅"),
|
||||
"migration": ("migration", "foreign", "abroad", "overseas", "relocation", "home", "迁移", "海外", "出国", "搬迁", "远方"),
|
||||
"family": ("family", "children", "mother", "father", "家庭", "子女", "孩子", "父母", "家宅"),
|
||||
"education": ("education", "study", "learning", "school", "degree", "学习", "教育", "学历", "学校", "考试"),
|
||||
"annual": ("annual", "yearly", "this year", "next year", "年度", "流年", "今年", "明年", "年运"),
|
||||
}
|
||||
@@ -316,9 +283,9 @@ class UnifiedConsultationOrchestrator:
|
||||
elif "career" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["career"]
|
||||
elif "marriage" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["relationship"]
|
||||
route = self._ROUTE_DEFINITIONS["marriage"]
|
||||
elif "wealth" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["finance"]
|
||||
route = self._ROUTE_DEFINITIONS["wealth"]
|
||||
elif "health" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["health"]
|
||||
elif "migration" in normalized_themes:
|
||||
@@ -329,6 +296,8 @@ class UnifiedConsultationOrchestrator:
|
||||
route = self._ROUTE_DEFINITIONS["education"]
|
||||
elif "annual" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["annual"]
|
||||
elif "timing" in normalized_themes:
|
||||
route = self._ROUTE_DEFINITIONS["timing"]
|
||||
else:
|
||||
route = self._ROUTE_DEFINITIONS["general"]
|
||||
|
||||
@@ -756,23 +725,21 @@ class UnifiedConsultationOrchestrator:
|
||||
route_packet: dict[str, Any],
|
||||
machine_evidence_packet: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
route = route_packet.get("question_type") or route_packet.get("primary_theme") or "general"
|
||||
if route == "marriage":
|
||||
route = "relationship"
|
||||
route = normalize_domain(route_packet.get("question_type") or route_packet.get("primary_theme") or "general")
|
||||
case_index_by_domain = {
|
||||
"career": ["references/real_case_studies/vedicka/career-success-poverty-prosperity.md"],
|
||||
"finance": ["references/real_case_studies/vedicka/career-success-poverty-prosperity.md"],
|
||||
"relationship": ["docs/benchmark/legacy-marriage-v6.1/verify-results-v6.1.json"],
|
||||
"wealth": ["references/real_case_studies/vedicka/career-success-poverty-prosperity.md"],
|
||||
"marriage": ["docs/benchmark/legacy-marriage-v6.1/verify-results-v6.1.json"],
|
||||
}
|
||||
case_profiles = {
|
||||
"references/real_case_studies/vedicka/career-success-poverty-prosperity.md": {
|
||||
"domains": ["career", "finance"],
|
||||
"domains": ["career", "wealth"],
|
||||
"evidence_sections": ["D1", "D10", "dasha_boundaries", "yogas"],
|
||||
"recorded_outcome": "poverty_to_prosperity_global_recognition",
|
||||
"event_trigger_keywords": ["Saturn dasha poverty", "Mercury dasha breakthrough", "Ketu dasha consolidation"],
|
||||
},
|
||||
"docs/benchmark/legacy-marriage-v6.1/verify-results-v6.1.json": {
|
||||
"domains": ["relationship"],
|
||||
"domains": ["marriage"],
|
||||
"evidence_sections": ["D1", "D9", "UL", "dasha_boundaries"],
|
||||
"recorded_outcome": "relationship_structure_validation_dataset",
|
||||
"event_trigger_keywords": ["UL", "Darapada", "7th lord", "DK"],
|
||||
|
||||
Reference in New Issue
Block a user