修复拆类金样换机器就对不齐的问题,避免测试删掉共用排盘缓存。
指纹不再包含仓库绝对路径;采集时关闭缓存而不是删除目录。基线与本分支 8 个哈希一致,定向 pytest 11 通过。 Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
14ad076fe4
commit
a7fe7e9dd5
@@ -1,6 +1,7 @@
|
||||
"""Canonical hashes for the compute/HTTP class split.
|
||||
|
||||
Fictional smoke chart only. Clock fields are removed before hashing.
|
||||
Absolute repository paths in string values are replaced with <ROOT>.
|
||||
Payload values are not written; the golden file stores sha256 and key-path types.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -8,18 +9,20 @@ from __future__ import annotations
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SCRIPTS = ROOT / "scripts"
|
||||
GOLDEN_PATH = ROOT / "tests" / "golden" / "api_server_service_extract_v1.json"
|
||||
CACHE_DIR = ROOT / "scratch" / "local" / "api_chart_cache"
|
||||
CLOCK_KEYS = {"cache_created_at", "cache_expires_at"}
|
||||
THEMES = ("career", "wealth", "relationship")
|
||||
BASELINE_SHA = "fbf04f0b5f31cd8421bc4546840b1c833aaed810"
|
||||
CACHE_TTL_ENV = "JYOTISH_API_CHART_CACHE_TTL_SECONDS"
|
||||
ROOT_PLACEHOLDER = "<ROOT>"
|
||||
|
||||
# Synthetic positions reused from tests/test_offline_compute_mixins.py.
|
||||
# Not a birth record.
|
||||
@@ -42,9 +45,54 @@ def prepare_env() -> None:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
|
||||
def clear_chart_cache() -> None:
|
||||
if CACHE_DIR.exists():
|
||||
shutil.rmtree(CACHE_DIR)
|
||||
@contextmanager
|
||||
def chart_cache_disabled() -> Iterator[None]:
|
||||
"""Turn the shared chart cache off. Do not delete the repository directory."""
|
||||
previous = os.environ.get(CACHE_TTL_ENV)
|
||||
os.environ[CACHE_TTL_ENV] = "0"
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if previous is None:
|
||||
os.environ.pop(CACHE_TTL_ENV, None)
|
||||
else:
|
||||
os.environ[CACHE_TTL_ENV] = previous
|
||||
|
||||
|
||||
def root_spellings(root: Path = ROOT) -> tuple[str, ...]:
|
||||
native = os.path.normpath(str(root))
|
||||
forward = native.replace("\\", "/")
|
||||
spellings = {native, forward}
|
||||
if os.name == "nt":
|
||||
spellings.add("\\\\?\\" + native)
|
||||
return tuple(sorted(spellings, key=len, reverse=True))
|
||||
|
||||
|
||||
def neutralize_path_string(text: str, root: Path = ROOT) -> str:
|
||||
for spelling in root_spellings(root):
|
||||
text = text.replace(spelling, ROOT_PLACEHOLDER)
|
||||
if ROOT_PLACEHOLDER not in text:
|
||||
return text
|
||||
pieces = text.split(ROOT_PLACEHOLDER)
|
||||
rebuilt = [pieces[0]]
|
||||
for piece in pieces[1:]:
|
||||
if piece.startswith("\\") or piece.startswith("/"):
|
||||
end = 0
|
||||
while end < len(piece) and piece[end] not in " \t\r\n\"'":
|
||||
end += 1
|
||||
piece = piece[:end].replace("\\", "/") + piece[end:]
|
||||
rebuilt.append(piece)
|
||||
return ROOT_PLACEHOLDER.join(rebuilt)
|
||||
|
||||
|
||||
def neutralize_paths(value: Any, root: Path = ROOT) -> Any:
|
||||
if isinstance(value, str):
|
||||
return neutralize_path_string(value, root)
|
||||
if isinstance(value, dict):
|
||||
return {key: neutralize_paths(item, root) for key, item in value.items()}
|
||||
if isinstance(value, list):
|
||||
return [neutralize_paths(item, root) for item in value]
|
||||
return value
|
||||
|
||||
|
||||
def theme_body(theme: str) -> dict[str, Any]:
|
||||
@@ -110,7 +158,7 @@ def flatten_keypaths(value: Any, prefix: str = "", *, depth: int = 0, max_depth:
|
||||
|
||||
|
||||
def canonical_text(value: Any) -> str:
|
||||
return json.dumps(strip_clock(value), sort_keys=True, ensure_ascii=False)
|
||||
return json.dumps(neutralize_paths(strip_clock(value)), sort_keys=True, ensure_ascii=False)
|
||||
|
||||
|
||||
def digest(value: Any) -> dict[str, Any]:
|
||||
@@ -124,56 +172,55 @@ def digest(value: Any) -> dict[str, Any]:
|
||||
|
||||
def capture_mcp(theme: str) -> Any:
|
||||
prepare_env()
|
||||
clear_chart_cache()
|
||||
from consultation_workflow_service import execute_consultation_workflow
|
||||
with chart_cache_disabled():
|
||||
from consultation_workflow_service import execute_consultation_workflow
|
||||
|
||||
return execute_consultation_workflow(theme_body(theme), surface="skill_mcp")
|
||||
return execute_consultation_workflow(theme_body(theme), surface="skill_mcp")
|
||||
|
||||
|
||||
def capture_http(theme: str) -> Any:
|
||||
"""HTTP handler method, on the compute class after the split."""
|
||||
prepare_env()
|
||||
clear_chart_cache()
|
||||
from jyotish_api_server import JyotishComputeService
|
||||
with chart_cache_disabled():
|
||||
from jyotish_api_server import JyotishComputeService
|
||||
|
||||
return JyotishComputeService()._compute_consultation_workflow(theme_body(theme))
|
||||
return JyotishComputeService()._compute_consultation_workflow(theme_body(theme))
|
||||
|
||||
|
||||
def capture_repairs() -> dict[str, Any]:
|
||||
prepare_env()
|
||||
clear_chart_cache()
|
||||
from capture_report_blocked_repairs_golden import (
|
||||
BIRTH,
|
||||
MODULE_KEEP,
|
||||
THEMES as REPAIR_THEMES,
|
||||
VARGA_KEEP,
|
||||
_body,
|
||||
_handler,
|
||||
_trim_workflow,
|
||||
)
|
||||
with chart_cache_disabled():
|
||||
from capture_report_blocked_repairs_golden import (
|
||||
BIRTH,
|
||||
MODULE_KEEP,
|
||||
THEMES as REPAIR_THEMES,
|
||||
VARGA_KEEP,
|
||||
_body,
|
||||
_handler,
|
||||
_trim_workflow,
|
||||
)
|
||||
|
||||
payload: dict[str, Any] = {
|
||||
"source": "scripts/jyotish_api_server.py _compute_consultation_workflow",
|
||||
"smoke_birth": {**BIRTH, "note": "fictional capture identity; not a real person"},
|
||||
"trim": {
|
||||
"keep_varga_keys": list(VARGA_KEEP),
|
||||
"keep_modules": list(MODULE_KEEP),
|
||||
"drop": [
|
||||
"ai_prompt_pack",
|
||||
"vedastro",
|
||||
"varga_spectrum",
|
||||
"guided_topics",
|
||||
"western_spectrum",
|
||||
"technique_audit_table",
|
||||
],
|
||||
},
|
||||
"themes": {},
|
||||
}
|
||||
for theme in REPAIR_THEMES:
|
||||
clear_chart_cache()
|
||||
raw = _handler()._compute_consultation_workflow(_body(theme))
|
||||
payload["themes"][theme] = _trim_workflow(raw)
|
||||
return payload
|
||||
payload: dict[str, Any] = {
|
||||
"source": "scripts/jyotish_api_server.py _compute_consultation_workflow",
|
||||
"smoke_birth": {**BIRTH, "note": "fictional capture identity; not a real person"},
|
||||
"trim": {
|
||||
"keep_varga_keys": list(VARGA_KEEP),
|
||||
"keep_modules": list(MODULE_KEEP),
|
||||
"drop": [
|
||||
"ai_prompt_pack",
|
||||
"vedastro",
|
||||
"varga_spectrum",
|
||||
"guided_topics",
|
||||
"western_spectrum",
|
||||
"technique_audit_table",
|
||||
],
|
||||
},
|
||||
"themes": {},
|
||||
}
|
||||
for theme in REPAIR_THEMES:
|
||||
raw = _handler()._compute_consultation_workflow(_body(theme))
|
||||
payload["themes"][theme] = _trim_workflow(raw)
|
||||
return payload
|
||||
|
||||
|
||||
def capture_evidence() -> Any:
|
||||
@@ -207,10 +254,15 @@ def provenance() -> dict[str, Any]:
|
||||
"env": {
|
||||
"VEDASTRO_ENABLE_NETWORK": "0",
|
||||
"JYOTISH_SKIP_LOCAL_ENV": "1",
|
||||
"JYOTISH_API_CHART_CACHE_TTL_SECONDS": "0",
|
||||
},
|
||||
"clockFieldsRemoved": sorted(CLOCK_KEYS),
|
||||
"rootPlaceholder": ROOT_PLACEHOLDER,
|
||||
"mcpSurface": "skill_mcp",
|
||||
"note": "fictional smoke chart from the task brief; payload values are not stored",
|
||||
"note": (
|
||||
"fictional smoke chart from the task brief; payload values are not stored; "
|
||||
"repository absolute paths in string values are replaced with <ROOT>"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user