个人长报告同盘同日重试改为秒回

完整数据版在版本号明确时复用当天的结果。渲染快捷算法留下但默认关闭,本机只快了大约两成。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-14 06:37:40 +08:00
co-authored by Grok (agent loop)
parent 66cd60081f
commit 69c739fbee
12 changed files with 1334 additions and 35 deletions
+76 -9
View File
@@ -486,15 +486,26 @@ def _drop_dotted_path(payload: Any, path: tuple[str, ...]) -> Any:
return cloned
def result_payload_for_binding(
def _render_fast() -> bool:
try:
from scripts.report_perf_flags import report_render_fast_enabled
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
from report_perf_flags import report_render_fast_enabled
return report_render_fast_enabled()
def _excluded_binding_top(binding_scope: dict[str, Any]) -> set[str]:
return RESULT_BINDING_ENVELOPE_KEYS | {
str(key) for key in (binding_scope.get("excluded_top_keys") or ())
}
def _result_payload_for_binding_legacy(
result: dict[str, Any],
scope: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Business payload that participates in result_hash."""
binding_scope = scope if isinstance(scope, dict) else default_result_binding_scope()
excluded_top = RESULT_BINDING_ENVELOPE_KEYS | {
str(key) for key in (binding_scope.get("excluded_top_keys") or ())
}
excluded_top = _excluded_binding_top(binding_scope)
payload = {key: value for key, value in result.items() if key not in excluded_top}
for raw_path in binding_scope.get("excluded_paths") or ():
parts = tuple(part for part in str(raw_path).split(".") if part)
@@ -503,17 +514,73 @@ def result_payload_for_binding(
return payload
def _drop_star_keys_and_canonicalize(payload: Any, star_keys: frozenset[str]) -> Any:
"""Drop ``**.key`` names and canonicalize keys in one walk.
String keys use the same inequality as ``_drop_key_recursive``. Non-string
keys are kept, then renamed with the same rule as ``canonicalize_result_payload``.
"""
if isinstance(payload, dict):
normalized: dict[str, Any] = {}
for key, child in payload.items():
if isinstance(key, str):
if key in star_keys:
continue
normalized_key = key
else:
normalized_key = f"__{type(key).__name__}__:{key}"
normalized[normalized_key] = _drop_star_keys_and_canonicalize(child, star_keys)
return normalized
if isinstance(payload, (list, tuple)):
return [_drop_star_keys_and_canonicalize(item, star_keys) for item in payload]
return payload
def _result_payload_for_binding_fast(
result: dict[str, Any],
scope: dict[str, Any] | None = None,
) -> dict[str, Any]:
binding_scope = scope if isinstance(scope, dict) else default_result_binding_scope()
excluded_top = _excluded_binding_top(binding_scope)
payload = {key: value for key, value in result.items() if key not in excluded_top}
star_keys: set[str] = set()
for raw_path in binding_scope.get("excluded_paths") or ():
parts = tuple(part for part in str(raw_path).split(".") if part)
if len(parts) == 2 and parts[0] == "**":
star_keys.add(parts[1])
continue
if parts:
payload = _drop_dotted_path(payload, parts)
return _drop_star_keys_and_canonicalize(payload, frozenset(star_keys))
def result_payload_for_binding(
result: dict[str, Any],
scope: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""Business payload that participates in result_hash."""
if _render_fast():
return _result_payload_for_binding_fast(result, scope)
return _result_payload_for_binding_legacy(result, scope)
def hash_bound_result(
result: dict[str, Any],
input_hash: str,
scope: dict[str, Any] | None = None,
) -> str:
"""SHA-256 of the canonical bound payload. Does not mutate ``result``."""
encoded = json.dumps(
canonicalize_result_payload({
payload = result_payload_for_binding(result, scope)
if _render_fast():
# The fast payload is already canonical, including the star-key drop.
canonical = {"input_hash": input_hash, "result": payload}
else:
canonical = canonicalize_result_payload({
"input_hash": input_hash,
"result": result_payload_for_binding(result, scope),
}),
"result": payload,
})
encoded = json.dumps(
canonical,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
+20 -11
View File
@@ -57,8 +57,8 @@ def _release_warm_slot() -> None:
_warm_inflight -= 1
def cache_dir() -> Path:
path = ROOT / "scratch" / "local" / "consult_full_data_cache"
def cache_dir(name: str = "consult_full_data_cache") -> Path:
path = ROOT / "scratch" / "local" / name
path.mkdir(parents=True, exist_ok=True)
return path
@@ -118,15 +118,15 @@ def cache_key(identity: dict[str, Any]) -> str:
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def _cache_path(identity: dict[str, Any]) -> Path:
return cache_dir() / f"{cache_key(identity)}.json"
def _cache_path(identity: dict[str, Any], name: str = "consult_full_data_cache") -> Path:
return cache_dir(name) / f"{cache_key(identity)}.json"
def prune_cache(now: float | None = None) -> int:
def prune_cache(now: float | None = None, *, name: str = "consult_full_data_cache") -> int:
"""Drop cache files last written more than two days ago."""
cutoff = (time.time() if now is None else now) - KEEP_SECONDS
removed = 0
folder = cache_dir()
folder = cache_dir(name)
for path in folder.glob("*.json"):
try:
if path.stat().st_mtime < cutoff:
@@ -147,15 +147,21 @@ def get_or_build(
builder: PacketBuilder | None = None,
*,
clock: Callable[[], float] | None = None,
cache_name: str = "consult_full_data_cache",
json_default: Callable[..., Any] | None = None,
) -> dict[str, Any]:
"""Return the packet. Concurrent callers for one key share one build."""
prune_cache()
"""Return the packet. Concurrent callers for one key share one build.
``cache_name`` selects the scratch subdirectory. The consult card keeps the
default. A non-default name does not change the consult key or its files.
"""
prune_cache(name=cache_name)
now = clock or time.perf_counter
started = now()
path = _cache_path(identity)
path = _cache_path(identity, cache_name)
if path.is_file():
return _read_cached(path, started, now)
key = cache_key(identity)
key = f"{cache_name}\n{cache_key(identity)}"
with _flights_guard:
flight = _flights.get(key)
owner = flight is None
@@ -177,7 +183,10 @@ def get_or_build(
builder = build_full_data_packet
packet = builder(identity)
temporary = path.with_suffix(".json.tmp")
temporary.write_text(json.dumps(packet, ensure_ascii=False, separators=(",", ":")), encoding="utf-8")
dump_kwargs: dict[str, Any] = {"ensure_ascii": False, "separators": (",", ":")}
if json_default is not None:
dump_kwargs["default"] = json_default
temporary.write_text(json.dumps(packet, **dump_kwargs), encoding="utf-8")
temporary.replace(path)
elapsed = now() - started
return {
+160 -1
View File
@@ -7,9 +7,15 @@ It only changes reader-facing labels; it does not alter calculation payloads.
"""
from __future__ import annotations
import functools
import re
from typing import Any
try:
from scripts.report_perf_flags import report_render_fast_enabled
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
from report_perf_flags import report_render_fast_enabled
# Bilingual planet tokens from yoga combination text, longest first.
# Dignity is glued on with no space ("木星Jupiterexalted"), so the long form
@@ -131,6 +137,11 @@ def translate_zh_glued_phrases(text: str) -> str:
return text
@functools.lru_cache(maxsize=512)
def _natural_language_term_pattern(raw: str, plural: str) -> re.Pattern:
return re.compile(rf"(?<![A-Za-z0-9_]){re.escape(raw)}{plural}(?![A-Za-z0-9_])")
def replace_natural_language_term(text: str, raw: str, public: str) -> str:
"""Replace one glossary term in prose only.
@@ -141,7 +152,14 @@ def replace_natural_language_term(text: str, raw: str, public: str) -> str:
if not text or not raw:
return text
plural = "s?" if re.search(r"[A-Za-z]$", raw) and not raw.endswith("s") else ""
pattern = re.compile(rf"(?<![A-Za-z0-9_]){re.escape(raw)}{plural}(?![A-Za-z0-9_])")
if report_render_fast_enabled():
# Case-sensitive literal. If it is absent, split-and-join would return
# the same bytes, so the compile and the scan can be skipped.
if raw not in text:
return text
pattern = _natural_language_term_pattern(raw, plural)
else:
pattern = re.compile(rf"(?<![A-Za-z0-9_]){re.escape(raw)}{plural}(?![A-Za-z0-9_])")
parts = re.split(r"(`[^`]*`)", text)
return "".join(
part if index % 2 else pattern.sub(public, part)
@@ -555,12 +573,98 @@ def _restore_zh_nakshatra_glosses(markdown: str) -> str:
)
# Every AD/PD hit contains these literals. Patterns stay case-sensitive.
_ZH_AD_DENSITY_RE = re.compile(
r"(.+?)主大运中的(.+?)子运,会把(.+?)的自然主题带入当前阶段。"
)
_ZH_PD_DENSITY_RE = re.compile(
r"(.+?)子运中的(.+?)次子运,会在(.+?)背景下呈现(.+?)的短周期结果。"
)
_ZH_AD_ANCHORS = ("主大运中的", "子运,会把", "的自然主题带入当前阶段。")
_ZH_PD_ANCHORS = ("次子运,会在", "背景下呈现", "的短周期结果。")
_EN_MD_HEADING_RE = re.compile(r"^#{2,4}\s+(.+?\bMaha Dasha\b.*)$")
_EN_AD_DENSITY_RE = re.compile(
r"The Antar Dasha of (.+?) activates (.+?)'s natural themes inside the broader Maha Dasha of (.+?)\."
)
_EN_PD_DENSITY_RE = re.compile(
r"In the Pratyantar Dasha of (.+?) in the Antar Dasha of (.+?), (.+?)'s significations give the short-period result inside the (.+?) background\."
)
_EN_AD_ANCHORS = (
"The Antar Dasha of ",
" activates ",
"'s natural themes inside the broader Maha Dasha of ",
)
_EN_PD_ANCHORS = (
"In the Pratyantar Dasha of ",
" in the Antar Dasha of ",
"'s significations give the short-period result inside the ",
" background.",
)
def _line_has_anchors(line: str, anchors: tuple[str, ...]) -> bool:
return all(anchor in line for anchor in anchors)
def _thicken_zh_dasha_density_fast(markdown: str) -> str:
"""Same AD/PD inserts as the legacy scan, without searching inert lines."""
lines = markdown.splitlines()
out: list[str] = []
for index, line in enumerate(lines):
out.append(line)
# Both patterns are case-sensitive and every hit contains these literals.
ad_possible = _line_has_anchors(line, _ZH_AD_ANCHORS)
pd_possible = _line_has_anchors(line, _ZH_PD_ANCHORS)
if not ad_possible and not pd_possible:
continue
lookahead = "\n".join(lines[index + 1:index + 6])
if "这一子运不是单独结论" in lookahead or "这个次子运只承担短周期细化" in lookahead:
continue
if ad_possible:
ad_match = _ZH_AD_DENSITY_RE.search(line)
if ad_match:
md_lord, ad_lord, theme_lord = ad_match.groups()
out.extend([
"",
(
f"这一子运不是单独结论,而是把{theme_lord}的自然象征放进{md_lord}主大运的背景里筛选。"
f"阅读时先看{ad_lord}本身的落宫、守护宫、尊贵状态与星宿,再看它和主大运星之间是否互相支持。"
),
"",
(
"若同一宫位或同一行星在分盘、Ashtakavarga、年度盘和行运中重复出现,"
"该主题的可见度会提高;若这些层彼此冲突,则应把结果视为阶段性倾向,而不是孤立断语。"
),
])
continue
if pd_possible:
pd_match = _ZH_PD_DENSITY_RE.search(line)
if pd_match:
ad_lord, pd_lord, md_lord, theme_lord = pd_match.groups()
out.extend([
"",
(
f"这个次子运只承担短周期细化:{pd_lord}会把{theme_lord}的具体征象带入{ad_lord}子运,"
f"并受{md_lord}主背景限制。"
),
"",
(
"因此这里优先用于判断事情推进的节奏、触发点和轻重缓急;实际领域仍需回到本命落宫、"
"分盘重复、年度盘和当前行运共同核对。"
),
])
continue
return "\n".join(out)
def _thicken_zh_dasha_density(markdown: str) -> str:
"""Restore reader-facing density for translated AD/PD prose.
This adds reading instructions tied to already-present fields. It does not
introduce new predictions or alter dates.
"""
if report_render_fast_enabled():
return _thicken_zh_dasha_density_fast(markdown)
lines = markdown.splitlines()
out: list[str] = []
for index, line in enumerate(lines):
@@ -941,9 +1045,64 @@ def normalize_en_markdown_terms(markdown: str) -> str:
return apply_en_residual_han(_thicken_en_dasha_density(text))
def _thicken_en_dasha_density_fast(markdown: str) -> str:
"""Same English AD inserts as the legacy scan, without searching inert lines."""
lines = markdown.splitlines()
out: list[str] = []
current_md = None
ad_context_added = False
for index, line in enumerate(lines):
md_heading = _EN_MD_HEADING_RE.match(line)
if md_heading:
next_md = md_heading.group(1)
if next_md != current_md:
current_md = next_md
ad_context_added = False
out.append(line)
# Both patterns are case-sensitive and every hit contains these literals.
ad_possible = _line_has_anchors(line, _EN_AD_ANCHORS)
pd_possible = _line_has_anchors(line, _EN_PD_ANCHORS)
if not ad_possible and not pd_possible:
continue
lookahead = "\n".join(lines[index + 1:index + 6])
if (
"This Antar Dasha is not read as a standalone verdict" in lookahead
or "This Pratyantar Dasha is the short-cycle refinement" in lookahead
):
continue
if ad_possible:
ad_match = _EN_AD_DENSITY_RE.search(line)
if ad_match and not ad_context_added:
ad_lord, theme_lord, md_lord = ad_match.groups()
out.extend([
"",
(
f"This Antar Dasha is not read as a standalone verdict. It places {theme_lord}'s "
f"natural significations inside the background of the {md_lord} Maha Dasha, then "
f"filters them through {ad_lord}'s house placement, house ownership, dignity, "
"nakshatra lord, conjunctions, and divisional-chart repetition."
),
"",
(
"When the same planet or house repeats through divisional charts, Ashtakavarga, "
"annual factors, and transits, the theme becomes more visible. When those layers "
"conflict, the result should be read as a staged tendency rather than an isolated statement."
),
])
ad_context_added = True
continue
if pd_possible and _EN_PD_DENSITY_RE.search(line):
# The row already contains the concrete PD lord and timing context.
# Keep it, but do not append the same methodology paragraph per node.
continue
return "\n".join(out)
def _thicken_en_dasha_density(markdown: str) -> str:
"""Add chapter-level AD context without repeating boilerplate on every PD row."""
if report_render_fast_enabled():
return _thicken_en_dasha_density_fast(markdown)
lines = markdown.splitlines()
out: list[str] = []
current_md = None
+98 -13
View File
@@ -8,6 +8,9 @@ calculation, and delegates packet assembly/rendering to ``jyotish_engine``.
from __future__ import annotations
import copy
import os
import sys
import time
from datetime import datetime
from importlib import import_module
from types import SimpleNamespace
@@ -32,6 +35,11 @@ except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution path
from reader_dasha_applicability import render_dasha_applicability
from report_density_packet import report_density_packet
try:
from scripts.report_perf_flags import report_reference_cache_enabled
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution path
from report_perf_flags import report_reference_cache_enabled
class ProfessionalReportReferenceInputError(ValueError):
"""The professional-reference request is outside the public contract."""
@@ -187,18 +195,49 @@ def _export_args(birth: dict[str, Any]) -> SimpleNamespace:
return SimpleNamespace(**payload)
def build_professional_report_reference(handler, body: dict[str, Any], *, engine=None) -> dict[str, Any]:
"""Build one JSON or Markdown response from one reused full-reading result."""
if not isinstance(body, dict):
raise ProfessionalReportReferenceInputError("request body must be an object")
output_format = _normalize_format(body.get("format"))
edition = _normalize_edition(body.get("edition"))
packs = _normalize_packs(body.get("packs"))
languages = _normalize_languages(body.get("languages"))
if languages and (output_format != "markdown" or edition != FULL_DATA_EDITION):
raise ProfessionalReportReferenceInputError("languages requires format markdown and edition full_data")
def _load_report_cache():
try:
from scripts import consult_full_data_cache as cache
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution path
import consult_full_data_cache as cache
return cache
birth = handler._high_rigor_birth_payload(body)
def _report_cache_sha() -> str:
return os.environ.get("GITHUB_SHA") or ""
def _report_cache_identity(
birth: dict[str, Any],
output_format: str,
edition: str,
packs: list[str],
languages: list[str],
include_fact_tables: bool,
sha: str,
) -> dict[str, Any]:
return {
"birth": birth,
"format": output_format,
"edition": edition,
"packs": list(packs),
"languages": list(languages),
"include_fact_tables": include_fact_tables,
"report_version": REPORT_VERSION_FULL,
"github_sha": sha,
}
def _assemble_professional_report_reference(
handler,
birth: dict[str, Any],
output_format: str,
edition: str,
packs: list[str],
languages: list[str],
include_fact_tables: bool,
engine,
) -> dict[str, Any]:
full_reading = handler._compute_full_reading_for_thematic(birth)
resolved_engine = engine or _load_engine()
try:
@@ -217,7 +256,7 @@ def build_professional_report_reference(handler, body: dict[str, Any], *, engine
if edition == FULL_DATA_EDITION:
# English renders from its own deep copy first, so nothing the Chinese
# renderer does to the packet can reach it (and vice versa).
english = _english_edition(packet, body.get("include_fact_tables") is True) if "en" in languages else {}
english = _english_edition(packet, include_fact_tables) if "en" in languages else {}
markdown = render_full_data_markdown(packet)
else:
markdown = resolved_engine.render_pl9_markdown(packet)
@@ -228,7 +267,7 @@ def build_professional_report_reference(handler, body: dict[str, Any], *, engine
"markdown": markdown,
**({"fact_table_packet": report_density_packet(packet),
"reader_dasha_applicability": clean_reader_appendix_markdown(render_dasha_applicability(packet))}
if body.get("include_fact_tables") is True else {}),
if include_fact_tables else {}),
**(english if edition == FULL_DATA_EDITION and "en" in languages else {}),
}
return {
@@ -236,3 +275,49 @@ def build_professional_report_reference(handler, body: dict[str, Any], *, engine
"edition": edition,
"report": packet,
}
def build_professional_report_reference(handler, body: dict[str, Any], *, engine=None) -> dict[str, Any]:
"""Build one JSON or Markdown response from one reused full-reading result."""
if not isinstance(body, dict):
raise ProfessionalReportReferenceInputError("request body must be an object")
output_format = _normalize_format(body.get("format"))
edition = _normalize_edition(body.get("edition"))
packs = _normalize_packs(body.get("packs"))
languages = _normalize_languages(body.get("languages"))
if languages and (output_format != "markdown" or edition != FULL_DATA_EDITION):
raise ProfessionalReportReferenceInputError("languages requires format markdown and edition full_data")
birth = handler._high_rigor_birth_payload(body)
include_fact_tables = body.get("include_fact_tables") is True
sha = _report_cache_sha()
use_cache = (
report_reference_cache_enabled()
and output_format == "markdown"
and edition == FULL_DATA_EDITION
and sha not in ("", "unknown")
)
if not use_cache:
return _assemble_professional_report_reference(
handler, birth, output_format, edition, packs, languages, include_fact_tables, engine,
)
identity = _report_cache_identity(
birth, output_format, edition, packs, languages, include_fact_tables, sha,
)
started = time.perf_counter()
def builder(_identity: dict[str, Any]) -> dict[str, Any]:
return _assemble_professional_report_reference(
handler, birth, output_format, edition, packs, languages, include_fact_tables, engine,
)
cached = _load_report_cache().get_or_build(
identity,
builder,
cache_name="report_reference_cache",
json_default=str,
)
elapsed_ms = int(round((time.perf_counter() - started) * 1000))
state = "hit" if cached["hit"] else "miss"
print(f"[report-reference-cache] {state} elapsed_ms={elapsed_ms}", file=sys.stderr)
return cached["packet"]
+40
View File
@@ -0,0 +1,40 @@
"""Call-time switches for long-report rendering and same-chart reuse.
Values are read on every call so one process can measure the old path and the
new path back to back. The old path stays in place when a switch is off.
"""
from __future__ import annotations
import os
# Masked Windows A/B on 2026-10-14. Render shortcuts are byte-identical and use
# less memory, but the end-to-end drop was 20–23%, short of the 35% gate, so
# the fast path stays off. Cache hits matched the miss bytes and returned in
# about 0.06s, under the 1.5s gate. A missing or unknown GITHUB_SHA still
# disables the cache at the call site.
RENDER_FAST_DEFAULT = False
REFERENCE_CACHE_DEFAULT = True
_ON = {"1", "true", "yes", "on"}
def _enabled(name: str, default: bool) -> bool:
raw = os.environ.get(name)
if raw is None or not raw.strip():
return default
return raw.strip().lower() in _ON
def report_render_fast_enabled() -> bool:
"""True when the equivalent render and hash shortcuts may run."""
return _enabled("JYOTISH_REPORT_RENDER_FAST", RENDER_FAST_DEFAULT)
def report_reference_cache_enabled() -> bool:
"""True when a full_data markdown response may be reused from disk.
A missing or ``unknown`` ``GITHUB_SHA`` still disables the cache at the
call site. This function only reads the switch.
"""
return _enabled("JYOTISH_REPORT_REFERENCE_CACHE", REFERENCE_CACHE_DEFAULT)
@@ -0,0 +1,428 @@
"""A/B timing for the professional long report.
A is both switches off. B is both on. Same process, fictional charts only.
A field sha mismatch exits 1 and prints a short diff context, not the report.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import platform
import sys
import tempfile
import time
import tracemalloc
from pathlib import Path
from types import SimpleNamespace
ROOT = Path(__file__).resolve().parents[2]
for _folder in (ROOT / "scripts", ROOT / "jyotish_vedic", ROOT):
_text = str(_folder)
if _text not in sys.path:
sys.path.insert(0, _text)
PRODUCTION_BODY = {
"format": "markdown",
"edition": "full_data",
"include_fact_tables": True,
"languages": ["zh", "en"],
}
SECTION2_CHART = {
"year": 1990,
"month": 1,
"day": 1,
"hour": 12,
"minute": 0,
"second": 0,
"lat": 28.6139,
"lon": 77.2090,
"tz": 5.5,
"node_mode": "mean",
"ayanamsa": "lahiri",
"today": "2026-10-13",
"transit_date": "2026-10-13",
"target_year": 2026,
"age": 36,
"birth_time_accuracy": "confirmed",
}
FIELDS = (
"response",
"markdown",
"markdown_en",
"fact_table_packet",
"reader_dasha_applicability",
)
def _mask_pyjhora() -> str:
root = Path(tempfile.mkdtemp(prefix="nojhora-"))
package = root / "jhora"
package.mkdir()
(package / "__init__.py").write_text('raise ImportError("masked")\n', encoding="utf-8")
sys.path.insert(0, str(root))
prior = os.environ.get("PYTHONPATH", "")
os.environ["PYTHONPATH"] = str(root) + (os.pathsep + prior if prior else "")
for name in list(sys.modules):
if name == "jhora" or name.startswith("jhora."):
del sys.modules[name]
return str(root)
def _sha(text: str) -> str:
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def _canonical(value) -> str:
if isinstance(value, str):
return value
return json.dumps(value, ensure_ascii=False, default=str)
def _field_texts(result: dict) -> dict[str, str]:
whole = json.dumps(result, ensure_ascii=False, default=str)
return {
"response": whole,
"markdown": _canonical(result.get("markdown", "")),
"markdown_en": _canonical(result.get("markdown_en", "")),
"fact_table_packet": _canonical(result.get("fact_table_packet")),
"reader_dasha_applicability": _canonical(result.get("reader_dasha_applicability", "")),
}
def _field_shas(result: dict) -> dict[str, str]:
return {name: _sha(text) for name, text in _field_texts(result).items()}
def _diff_context(left: str, right: str) -> str:
limit = min(len(left), len(right))
index = next((i for i in range(limit) if left[i] != right[i]), limit)
start = max(0, index - 40)
stop = index + 40
return (
f"at {index} len {len(left)} vs {len(right)} "
f"A={left[start:stop]!r} B={right[start:stop]!r}"
)
def _median(values: list[float]) -> float:
ordered = sorted(values)
return ordered[len(ordered) // 2]
class _Bucket:
def __init__(self) -> None:
self.cmd = 0.0
self.packet = 0.0
self.zh = 0.0
self.en = 0.0
self.hash = 0.0
def reset(self) -> None:
self.cmd = self.packet = self.zh = self.en = self.hash = 0.0
def as_dict(self) -> dict[str, float]:
return {
"cmd_full_reading_s": round(self.cmd, 4),
"packet_s": round(self.packet, 4),
"zh_render_s": round(self.zh, 4),
"en_render_s": round(self.en, 4),
"hash_s": round(self.hash, 4),
}
def _install_timers(bucket: _Bucket) -> None:
import scripts.calculation_profile_contract as contract
import scripts.full_report_quality_gate as gate
import scripts.jyotish_engine as engine
import scripts.professional_report_reference as reference
orig_packet = engine.build_professional_report_reference_packet
def timed_packet(*args, **kwargs):
started = time.perf_counter()
try:
return orig_packet(*args, **kwargs)
finally:
bucket.packet += time.perf_counter() - started
engine.build_professional_report_reference_packet = timed_packet
orig_render = reference.render_full_data_markdown
def timed_render(packet, *args, **kwargs):
started = time.perf_counter()
try:
return orig_render(packet, *args, **kwargs)
finally:
elapsed = time.perf_counter() - started
if isinstance(packet, dict) and packet.get("report_language") == "en":
bucket.en += elapsed
else:
bucket.zh += elapsed
reference.render_full_data_markdown = timed_render
orig_hash = contract.hash_bound_result
def timed_hash(*args, **kwargs):
started = time.perf_counter()
try:
return orig_hash(*args, **kwargs)
finally:
bucket.hash += time.perf_counter() - started
contract.hash_bound_result = timed_hash
gate.hash_bound_result = timed_hash
def _set_flags(fast: bool, cache: bool) -> None:
os.environ["JYOTISH_REPORT_RENDER_FAST"] = "1" if fast else "0"
os.environ["JYOTISH_REPORT_REFERENCE_CACHE"] = "1" if cache else "0"
class _Handler:
def __init__(self, birth: dict, bucket: _Bucket) -> None:
self.birth = birth
self.bucket = bucket
def _high_rigor_birth_payload(self, _body: dict) -> dict:
return dict(self.birth)
def _compute_full_reading_for_thematic(self, birth: dict):
from scripts.jyotish_engine import cmd_full_reading
started = time.perf_counter()
try:
return cmd_full_reading(SimpleNamespace(**birth))
finally:
self.bucket.cmd += time.perf_counter() - started
def _run_once(birth: dict, body: dict, bucket: _Bucket, handler=None) -> dict:
from scripts.professional_report_reference import build_professional_report_reference
bucket.reset()
tracemalloc.start()
started = time.perf_counter()
result = build_professional_report_reference(handler or _Handler(birth, bucket), body)
elapsed = time.perf_counter() - started
_current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
texts = _field_texts(result)
return {
"elapsed_s": round(elapsed, 4),
"peak_bytes": peak,
"response_bytes": len(texts["response"].encode("utf-8")),
"segments": bucket.as_dict(),
"sha": _field_shas(result),
"_texts": texts,
}
def _repeat(birth: dict, body: dict, bucket: _Bucket, times: int) -> list[dict]:
return [_run_once(birth, body, bucket) for _ in range(times)]
def _public_row(row: dict) -> dict:
return {key: value for key, value in row.items() if key != "_texts"}
def _compare(label: str, left: dict, right: dict, problems: list[str]) -> None:
left_texts = left["_texts"]
right_texts = right["_texts"]
for name in FIELDS:
if left["sha"][name] != right["sha"][name]:
problems.append(f"{label} {name} {_diff_context(left_texts[name], right_texts[name])}")
def _freeze_clocks():
"""Pin datetime.now / utcnow in every already imported module.
``format=json`` and the reference edition embed wall-clock fields. Freezing
only the engine module leaves other imports on the real clock.
"""
import datetime as dt_mod
real = dt_mod.datetime
fixed_utc = real(2026, 10, 13, 0, 0, 0)
fixed_local = real(2026, 10, 13, 8, 0, 0)
class Frozen(real):
@classmethod
def now(cls, tz=None):
if tz is not None:
return fixed_utc.replace(tzinfo=tz)
return fixed_local
@classmethod
def utcnow(cls):
return fixed_utc
saved = []
for module in list(sys.modules.values()):
if module is None:
continue
current = getattr(module, "datetime", None)
if isinstance(current, type) and current.__name__ == "datetime":
saved.append((module, current))
module.datetime = Frozen
return saved
def _restore_clocks(saved) -> None:
for module, current in saved:
module.datetime = current
def _strip_generated_at(value):
if isinstance(value, dict):
return {key: _strip_generated_at(item) for key, item in value.items() if key != "generated_at"}
if isinstance(value, list):
return [_strip_generated_at(item) for item in value]
return value
def _sha_pair(birth: dict, body: dict, bucket: _Bucket) -> dict:
"""Compare one frozen clock on one reused full reading.
A second ``cmd_full_reading`` appends onto shared trait lists, so two live
engine calls are not a byte test of the render switch.
"""
import copy
from scripts.jyotish_engine import cmd_full_reading
_set_flags(False, False)
reading = cmd_full_reading(SimpleNamespace(**birth))
class _Reuse:
def _high_rigor_birth_payload(self, _body: dict) -> dict:
return dict(birth)
def _compute_full_reading_for_thematic(self, _birth: dict):
return copy.deepcopy(reading)
_set_flags(False, False)
left = _run_once(birth, body, bucket, _Reuse())
_set_flags(True, False)
right = _run_once(birth, body, bucket, _Reuse())
problems = []
_compare("side", left, right, problems)
compared = "full"
if problems:
left_stripped = _sha(_canonical(_strip_generated_at(json.loads(left["_texts"]["response"]))))
right_stripped = _sha(_canonical(_strip_generated_at(json.loads(right["_texts"]["response"]))))
if left_stripped == right_stripped:
problems.clear()
compared = "without_generated_at"
else:
compared = "mismatch"
return {
"compared": compared,
"sha_a": left["sha"]["response"],
"sha_b": right["sha"]["response"],
"problems": problems,
}
def _charts() -> dict[str, dict]:
from tests.test_report_english_edition import CASES
return {
"day": {key: value for key, value in CASES["day"].items() if key != "name"},
"night": {key: value for key, value in CASES["night"].items() if key != "name"},
"south": {key: value for key, value in CASES["south"].items() if key != "name"},
"section2": dict(SECTION2_CHART),
}
def _clear_report_cache() -> None:
folder = ROOT / "scratch" / "local" / "report_reference_cache"
if not folder.is_dir():
return
for path in folder.glob("*.json"):
path.unlink(missing_ok=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--mask-pyjhora", action="store_true")
parser.add_argument("--only-a", action="store_true")
parser.add_argument("--side-only", action="store_true")
args = parser.parse_args()
mask_path = _mask_pyjhora() if args.mask_pyjhora else ""
bucket = _Bucket()
_install_timers(bucket)
os.environ.pop("GITHUB_SHA", None)
report: dict = {
"python": platform.python_version(),
"platform": platform.platform(),
"mask_pyjhora": bool(args.mask_pyjhora),
"mask_path": mask_path,
"only_a": args.only_a,
"charts": {},
}
problems: list[str] = []
chart_items = () if args.side_only else _charts().items()
for label, birth in chart_items:
_set_flags(False, False)
arm_a = _repeat(birth, PRODUCTION_BODY, bucket, 3)
entry: dict = {"A": [_public_row(row) for row in arm_a]}
if not args.only_a:
_set_flags(True, False)
arm_b = _repeat(birth, PRODUCTION_BODY, bucket, 3)
for row in arm_b:
_compare(f"{label} B-miss", arm_a[0], row, problems)
os.environ["GITHUB_SHA"] = "report-reference-perf-ab"
_set_flags(True, True)
_clear_report_cache()
miss = _run_once(birth, PRODUCTION_BODY, bucket)
_compare(f"{label} cache-miss", arm_a[0], miss, problems)
hits = _repeat(birth, PRODUCTION_BODY, bucket, 3)
for row in hits:
_compare(f"{label} cache-hit", miss, row, problems)
entry["B_miss"] = [_public_row(row) for row in arm_b]
entry["cache_miss"] = _public_row(miss)
entry["B_hit"] = [_public_row(row) for row in hits]
os.environ.pop("GITHUB_SHA", None)
report["charts"][label] = entry
print(f"{label} done", file=sys.stderr)
if not args.only_a:
saved_clocks = _freeze_clocks()
try:
sample = _charts()["section2"]
report["format_json"] = _sha_pair(sample, {"format": "json", "edition": "full_data"}, bucket)
report["edition_reference"] = _sha_pair(
sample, {"format": "markdown", "edition": "reference"}, bucket,
)
finally:
_restore_clocks(saved_clocks)
problems.extend(report["format_json"]["problems"])
problems.extend(report["edition_reference"]["problems"])
_set_flags(False, False)
from scripts.calculation_profile_contract import hash_bound_result
from scripts.jyotish_engine import build_professional_report_reference_packet, cmd_full_reading
from scripts.professional_report_reference import _export_args
reading = cmd_full_reading(SimpleNamespace(**sample))
packet = build_professional_report_reference_packet(reading, _export_args(sample), [])
input_hash = packet["calculation_profile"]["input_hash"]
def _time_hash(fast: bool) -> float:
_set_flags(fast, False)
started = time.perf_counter()
hash_bound_result(packet, input_hash)
return time.perf_counter() - started
off = [_time_hash(False) for _ in range(3)]
on = [_time_hash(True) for _ in range(3)]
report["hash_once_s"] = {"off": [round(item, 4) for item in off], "on": [round(item, 4) for item in on]}
report["problems"] = problems
print(json.dumps(report, ensure_ascii=False))
return 1 if problems else 0
if __name__ == "__main__":
raise SystemExit(main())