ForcePilot/test/compare_kb_metadata_with_db.py

159 lines
5.2 KiB
Python
Raw Normal View History

import asyncio
import glob
import json
import os
import sys
from dataclasses import dataclass
from typing import Any
from sqlalchemy import func, select
os.environ.setdefault("YUXI_SKIP_APP_INIT", "1")
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from src.config import config
from src.storage.postgres.manager import pg_manager
from src.storage.postgres.models_knowledge import (
EvaluationBenchmark,
EvaluationResult,
EvaluationResultDetail,
KnowledgeBase,
KnowledgeFile,
)
def _load_json(path: str) -> dict[str, Any]:
if not os.path.exists(path):
return {}
with open(path, encoding="utf-8") as f:
return json.load(f)
@dataclass(frozen=True)
class JsonState:
kb_ids: set[str]
file_ids: set[str]
benchmark_ids: set[str]
result_task_ids: set[str]
result_detail_count: int
@dataclass(frozen=True)
class DbState:
kb_ids: set[str]
file_ids: set[str]
benchmark_ids: set[str]
result_task_ids: set[str]
result_detail_count: int
def load_json_state() -> JsonState:
base_dir = os.path.join(config.save_dir, "knowledge_base_data")
global_meta = _load_json(os.path.join(base_dir, "global_metadata.json")).get("databases", {}) or {}
kb_ids: set[str] = set(global_meta.keys())
file_ids: set[str] = set()
benchmark_ids: set[str] = set()
result_task_ids: set[str] = set()
result_detail_count = 0
kb_type_dirs = [
p for p in glob.glob(os.path.join(base_dir, "*_data")) if os.path.isdir(p) and os.path.basename(p) != "uploads"
]
for kb_dir in kb_type_dirs:
kb_type = os.path.basename(kb_dir)[: -len("_data")]
meta_file = os.path.join(kb_dir, f"metadata_{kb_type}.json")
meta = _load_json(meta_file)
databases_meta: dict[str, Any] = meta.get("databases", {}) or {}
files_meta: dict[str, Any] = meta.get("files", {}) or {}
benchmarks_meta: dict[str, Any] = meta.get("benchmarks", {}) or {}
kb_ids.update(databases_meta.keys())
file_ids.update(files_meta.keys())
for _db_id, bmap in benchmarks_meta.items():
if not isinstance(bmap, dict):
continue
benchmark_ids.update(bmap.keys())
for db_id in databases_meta.keys():
result_dir = os.path.join(kb_dir, db_id, "results")
if not os.path.isdir(result_dir):
continue
for result_path in glob.glob(os.path.join(result_dir, "*.json")):
try:
data = _load_json(result_path)
except Exception:
continue
task_id = data.get("task_id") or os.path.splitext(os.path.basename(result_path))[0]
result_task_ids.add(task_id)
interim = data.get("interim_results") or data.get("results") or []
result_detail_count += len(interim)
return JsonState(
kb_ids=kb_ids,
file_ids=file_ids,
benchmark_ids=benchmark_ids,
result_task_ids=result_task_ids,
result_detail_count=result_detail_count,
)
async def load_db_state() -> DbState:
async with pg_manager.get_async_session_context() as session:
kb_ids = set((await session.execute(select(KnowledgeBase.db_id))).scalars().all())
file_ids = set((await session.execute(select(KnowledgeFile.file_id))).scalars().all())
benchmark_ids = set((await session.execute(select(EvaluationBenchmark.benchmark_id))).scalars().all())
result_task_ids = set((await session.execute(select(EvaluationResult.task_id))).scalars().all())
detail_count = (await session.execute(select(func.count(EvaluationResultDetail.id)))).scalar_one()
return DbState(
kb_ids=kb_ids,
file_ids=file_ids,
benchmark_ids=benchmark_ids,
result_task_ids=result_task_ids,
result_detail_count=int(detail_count or 0),
)
def _diff(name: str, json_set: set[str], db_set: set[str], limit: int = 30) -> list[str]:
missing = sorted(json_set - db_set)
extra = sorted(db_set - json_set)
lines: list[str] = []
lines.append(f"{name}: json={len(json_set)} db={len(db_set)}")
if missing:
preview = ", ".join(missing[:limit])
lines.append(f" missing_in_db({len(missing)}): {preview}")
if extra:
preview = ", ".join(extra[:limit])
lines.append(f" extra_in_db({len(extra)}): {preview}")
return lines
async def main() -> None:
engine_url = pg_manager.async_engine.url.render_as_string(hide_password=True)
print(f"db_url={engine_url}")
json_state = load_json_state()
db_state = await load_db_state()
for line in _diff("knowledge_bases.db_id", json_state.kb_ids, db_state.kb_ids):
print(line)
for line in _diff("knowledge_files.file_id", json_state.file_ids, db_state.file_ids):
print(line)
for line in _diff("evaluation_benchmarks.benchmark_id", json_state.benchmark_ids, db_state.benchmark_ids):
print(line)
for line in _diff("evaluation_results.task_id", json_state.result_task_ids, db_state.result_task_ids):
print(line)
print(
"evaluation_result_details: "
f"json_count={json_state.result_detail_count} db_count={db_state.result_detail_count}"
)
if __name__ == "__main__":
asyncio.run(main())