Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PLUGIN = ROOT / "plugins" / "1c"
|
||||
DEFAULT_REPORT = ROOT / "reports" / "1c-plugin-health.json"
|
||||
|
||||
|
||||
def run(command: list[str], *, allow_fail: bool = False) -> dict:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
)
|
||||
status = "ok" if result.returncode == 0 else "failed"
|
||||
if allow_fail and result.returncode != 0:
|
||||
status = "blocked"
|
||||
return {
|
||||
"command": command,
|
||||
"status": status,
|
||||
"returncode": result.returncode,
|
||||
"stdout": result.stdout.strip(),
|
||||
"stderr": result.stderr.strip(),
|
||||
}
|
||||
|
||||
|
||||
def prepare_rag_smoke_index(temp_path: Path) -> list[dict]:
|
||||
rag_source = temp_path / "metadata.health.generated.md"
|
||||
corpus = temp_path / "rag_corpus.jsonl"
|
||||
manifest = temp_path / "rag_manifest.json"
|
||||
index = temp_path / "rag_index.json"
|
||||
steps = [
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/convert_1c_metadata_to_rag.py",
|
||||
"--input",
|
||||
"plugins/1c/metadata/examples/metadata.example.json",
|
||||
"--output",
|
||||
str(rag_source),
|
||||
],
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/convert_1c_bsl_modules_to_rag.py",
|
||||
"--input",
|
||||
"plugins/1c/metadata/examples/bsl-modules.example.json",
|
||||
"--output",
|
||||
str(temp_path / "bsl.health.generated.md"),
|
||||
],
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/validate_1c_rag_sources.py",
|
||||
"--source-dir",
|
||||
str(temp_path),
|
||||
],
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/prepare_1c_rag_corpus.py",
|
||||
"--source-dir",
|
||||
str(temp_path),
|
||||
"--output",
|
||||
str(corpus),
|
||||
"--manifest",
|
||||
str(manifest),
|
||||
],
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/build_1c_rag_index.py",
|
||||
"--corpus",
|
||||
str(corpus),
|
||||
"--output",
|
||||
str(index),
|
||||
],
|
||||
]
|
||||
return [run(step, allow_fail=True) for step in steps]
|
||||
|
||||
|
||||
def read_yaml(path: Path) -> dict:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
data = yaml.safe_load(handle)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"{path} must contain a YAML mapping")
|
||||
return data
|
||||
|
||||
|
||||
def check_required_files() -> dict:
|
||||
required = [
|
||||
PLUGIN / "plugin.yaml",
|
||||
PLUGIN / "prompts" / "system.md",
|
||||
PLUGIN / "prompts" / "rag-answer.md",
|
||||
PLUGIN / "rag" / "profiles.yaml",
|
||||
PLUGIN / "rag" / "quality-smoke.json",
|
||||
PLUGIN / "rag" / "profile-routing-smoke.json",
|
||||
PLUGIN / "tools" / "tool-contract.yaml",
|
||||
PLUGIN / "connector" / "contracts" / "openapi.yaml",
|
||||
PLUGIN / "connector" / "policies" / "read-only-query.yaml",
|
||||
PLUGIN / "connector" / "policies" / "change-workflow.yaml",
|
||||
PLUGIN / "connector" / "policies" / "config-layer-write-policy.yaml",
|
||||
PLUGIN / "connector" / "Dockerfile",
|
||||
PLUGIN / "connector" / "docker-compose.yml",
|
||||
PLUGIN / "connector" / ".env.example",
|
||||
PLUGIN / "connector" / "pyproject.toml",
|
||||
PLUGIN / "connector" / "service.yaml",
|
||||
PLUGIN / "metadata" / "schema.json",
|
||||
PLUGIN / "metadata" / "moxel-schema-registry.json",
|
||||
PLUGIN / "metadata" / "examples" / "metadata.example.json",
|
||||
PLUGIN / "metadata" / "examples" / "metadata-v2.example.json",
|
||||
PLUGIN / "metadata" / "examples" / "bsl-modules.example.json",
|
||||
PLUGIN / "schemas" / "metadata-snapshot-v2.schema.json",
|
||||
PLUGIN / "schemas" / "bsl-module-snapshot.schema.json",
|
||||
PLUGIN / "schemas" / "moxel-schema-registry.schema.json",
|
||||
PLUGIN / "training" / "examples" / "instruction.examples.jsonl",
|
||||
PLUGIN / "training" / "configs" / "qwen3-coder-30b-a3b-lora.yaml",
|
||||
PLUGIN / "evals" / "smoke.yaml",
|
||||
]
|
||||
missing = [str(path.relative_to(ROOT)) for path in required if not path.exists()]
|
||||
return {
|
||||
"status": "ok" if not missing else "failed",
|
||||
"missing": missing,
|
||||
}
|
||||
|
||||
|
||||
def summarize_manifest() -> dict:
|
||||
manifest = read_yaml(PLUGIN / "plugin.yaml")
|
||||
return {
|
||||
"id": manifest.get("id"),
|
||||
"version": manifest.get("version"),
|
||||
"status": manifest.get("status"),
|
||||
"tasks": manifest.get("tasks") or [],
|
||||
"entrypoints": sorted((manifest.get("entrypoints") or {}).keys()),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Run 1C plugin health checks.")
|
||||
parser.add_argument("--report", type=Path, default=DEFAULT_REPORT)
|
||||
parser.add_argument("--print", action="store_true")
|
||||
parser.add_argument(
|
||||
"--no-report",
|
||||
action="store_true",
|
||||
help="Do not write a health report file; useful for clean local checks.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="llm-1c-health-") as temp_dir:
|
||||
temp_index = Path(temp_dir) / "rag_index.json"
|
||||
commands = {
|
||||
"metadata_example": [
|
||||
sys.executable,
|
||||
"scripts/validate_1c_metadata_snapshot.py",
|
||||
"plugins/1c/metadata/examples/metadata.example.json",
|
||||
],
|
||||
"metadata_v2_example": [
|
||||
sys.executable,
|
||||
"scripts/validate_1c_metadata_snapshot.py",
|
||||
"plugins/1c/metadata/examples/metadata-v2.example.json",
|
||||
],
|
||||
"bsl_modules_example": [
|
||||
sys.executable,
|
||||
"scripts/validate_1c_bsl_modules.py",
|
||||
"plugins/1c/metadata/examples/bsl-modules.example.json",
|
||||
],
|
||||
"readonly_query_allowed": [
|
||||
sys.executable,
|
||||
"scripts/validate_1c_readonly_query.py",
|
||||
"--query",
|
||||
"ВЫБРАТЬ Первые 10 Ссылка ИЗ Справочник.Номенклатура",
|
||||
],
|
||||
"training_examples": [
|
||||
sys.executable,
|
||||
"scripts/validate_1c_training_data.py",
|
||||
"plugins/1c/training/examples/instruction.examples.jsonl",
|
||||
],
|
||||
"evals": [sys.executable, "scripts/validate_evals.py", "plugins/1c/evals/smoke.yaml"],
|
||||
"connector_standalone_check": [sys.executable, "scripts/check_1c_connector_standalone.py"],
|
||||
"write_plan_contract": [sys.executable, "scripts/check_1c_write_plan_contract.py"],
|
||||
"bsl_symbol_check": [sys.executable, "scripts/check_1c_bsl_symbol_resolver.py"],
|
||||
"code_symbol_contract": [sys.executable, "scripts/check_1c_code_symbol_contract.py"],
|
||||
"module_origin_contract": [sys.executable, "scripts/check_1c_module_origin_contract.py"],
|
||||
"extension_action_contract": [sys.executable, "scripts/check_1c_extension_action_contract.py"],
|
||||
"moxel_schema_registry_contract": [
|
||||
sys.executable,
|
||||
"scripts/check_1c_moxel_schema_registry.py",
|
||||
"--registry",
|
||||
"plugins/1c/metadata/moxel-schema-registry.json",
|
||||
"--output",
|
||||
str(Path(temp_dir) / "moxel-schema-registry-check.json"),
|
||||
],
|
||||
"moxel_status": [
|
||||
sys.executable,
|
||||
"scripts/status_1c_moxel.py",
|
||||
"--output-json",
|
||||
str(Path(temp_dir) / "moxel-status.json"),
|
||||
"--output-markdown",
|
||||
str(Path(temp_dir) / "moxel-status.md"),
|
||||
],
|
||||
"rag_prompt_guardrails": [
|
||||
sys.executable,
|
||||
"scripts/check_1c_rag_prompt.py",
|
||||
"--index",
|
||||
str(temp_index),
|
||||
],
|
||||
"rag_quality": [
|
||||
sys.executable,
|
||||
"scripts/check_1c_rag_quality.py",
|
||||
"--index",
|
||||
str(temp_index),
|
||||
],
|
||||
"rag_profile_routing": [
|
||||
sys.executable,
|
||||
"scripts/check_1c_rag_profiles.py",
|
||||
],
|
||||
"training_preflight": [sys.executable, "scripts/preflight_1c_training.py"],
|
||||
}
|
||||
|
||||
checks = {
|
||||
"manifest": summarize_manifest(),
|
||||
"required_files": check_required_files(),
|
||||
"rag_smoke_prepare": prepare_rag_smoke_index(Path(temp_dir)),
|
||||
"commands": {},
|
||||
}
|
||||
|
||||
for name, command in commands.items():
|
||||
checks["commands"][name] = run(
|
||||
command,
|
||||
allow_fail=name in {"training_preflight"},
|
||||
)
|
||||
|
||||
failed = []
|
||||
for name, result in checks["commands"].items():
|
||||
if result["status"] == "failed":
|
||||
failed.append(name)
|
||||
if checks["required_files"]["status"] == "failed":
|
||||
failed.append("required_files")
|
||||
|
||||
report = {
|
||||
"plugin": "1c",
|
||||
"status": "failed" if failed else "ok",
|
||||
"failed_checks": failed,
|
||||
"checks": checks,
|
||||
}
|
||||
|
||||
if not args.no_report:
|
||||
args.report.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.report.write_text(
|
||||
json.dumps(report, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
if args.print:
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
if not args.no_report:
|
||||
print(f"Wrote 1C plugin health report to {args.report}")
|
||||
print(f"Status: {report['status']}")
|
||||
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user