from __future__ import annotations
import pathlib
import re
import sys
ROOT = pathlib.Path(__file__).resolve().parents[1]
DOCS = (
[ROOT / "README.md", ROOT / "README_EN.md"]
+ sorted((ROOT / "docs").rglob("*.md"))
)
SRC = ROOT / "src"
def src_text() -> str:
return "\n".join(
p.read_text(errors="replace")
for d in (ROOT / "src", ROOT / "benches", ROOT / "tests")
for p in d.rglob("*.rs")
)
def code_routes(src: str) -> set[str]:
routes: set[str] = set()
for m in re.finditer(r"#\[\s*forge\s*\((.*?)\)\s*\]", src, re.S):
attrs = m.group(1)
pm = re.search(r'path\s*=\s*"([^"]+)"', attrs)
if not pm:
continue
path = pm.group(1)
no_prefix = "no_prefix" in attrs and "true" in attrs
routes.add(path if no_prefix else f"/api/1{path}")
if 'route("/metrics"' in src:
routes.add("/metrics")
return routes
def code_grpc_methods(src: str) -> set[str]:
return set(re.findall(r'grpc_method\s*=\s*"(vecboost\.[a-z_]+)"', src))
def code_env_vars(src: str) -> set[str]:
return set(re.findall(r'"(VECBOOST_[A-Z0-9_]+)"', src))
def code_cli_subcommands(src: str) -> set[str]:
return set(re.findall(r'name\s*=\s*"(embed|embed_batch|compute_similarity|rerank|search)"\s*,\s*version', src))
CONFIG_SECTIONS: dict[str, set[str]] = {
"server": {"host", "port", "timeout", "grpc", "workers", "body", "request", "cors"},
"model": {"repo", "revision", "path", "gpu", "size", "dimension", "length", "batch", "quantized", "max", "resident"},
"embedding": set(),
"rerank": set(),
"monitoring": set(),
"auth": set(),
"rate": set(),
"audit": set(),
"logging": set(),
"semantic": set(),
"device": set(),
"database": set(),
"pipeline": set(),
"memory": set(),
}
EXPLICIT_ENV_OK = {
"VECBOOST_JWT_SECRET", "VECBOOST_ADMIN_PASSWORD", "VECBOOST_ENCRYPTION_KEY",
"VECBOOST_REQUIRE_ENCRYPTION", "VECBOOST_KEY_STORAGE_TYPE", "VECBOOST_KEY_FILE_PATH",
"VECBOOST_LANG", "HF_ENDPOINT", "VECBOOST_DATABASE_PASSWORD", "VECBOOST_MODEL_API_KEY",
"VECBOOST_LOG_LEVEL", "VECBOOST_ALLOW_INSECURE",
}
def env_var_resolvable(var: str, src: str) -> bool:
if var in code_env_vars(src):
return True
if var in EXPLICIT_ENV_OK:
return var in src
body = var[len("VECBOOST_"):].lower()
parts = body.split("_")
if len(parts) < 2:
return False
section = parts[0]
field = "_".join(parts[1:])
if section not in CONFIG_SECTIONS:
return False
fields = CONFIG_SECTIONS[section]
if not fields: return "_" not in field
return field.split("_")[0] in fields
def main() -> int:
src = src_text()
docs_text = "\n".join(p.read_text(errors="replace") for p in DOCS if p.exists())
issues: list[str] = []
routes = code_routes(src)
doc_eps = set(re.findall(r"`/(?:api/1|v1)/[a-z0-9/_-]*`", docs_text))
doc_eps = {e.strip("`") for e in doc_eps}
doc_eps = {e for e in doc_eps if not e.endswith("/api/1/") and len(e) > len("/api/1/")}
for ep in sorted(doc_eps):
if ep not in routes:
issues.append(f"[endpoint] 文档声明 {ep} 在代码路由中不存在(已注册: {sorted(routes)})")
methods = code_grpc_methods(src)
doc_methods = set(re.findall(r"`(vecboost\.[a-z_]+)`", docs_text))
doc_methods -= {"vecboost.db", "vecboost.log", "vecboost.git", "vecboost.png"}
for gm in sorted(doc_methods):
if gm not in methods:
issues.append(f"[grpc] 文档声明 {gm} 未在代码中注册(已注册: {sorted(methods)})")
env_vars = set(re.findall(r"VECBOOST_[A-Z0-9_]+", docs_text))
for var in sorted(env_vars):
if not env_var_resolvable(var, src):
issues.append(f"[env] 文档声明 {var} 无代码消费且无法经 confers 映射到 VecboostConfig 字段")
cli_cmds = code_cli_subcommands(src)
doc_cli = set(re.findall(r"vecboost\s+(embed_batch|compute_similarity|embed|rerank|search)\b", docs_text))
for cmd in sorted(doc_cli):
if cmd not in cli_cmds:
issues.append(f"[cli] 文档声明子命令 {cmd} 未注册")
print(f"代码事实: routes={len(routes)} grpc={len(methods)} cli={len(cli_cmds)}")
if issues:
print(f"\n发现 {len(issues)} 处不一致:")
for i in issues:
print(f" - {i}")
return 1
print("文档一致性核验通过:无差异")
return 0
if __name__ == "__main__":
sys.exit(main())