#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Test P3: RAG semantico su file Gitea indicizzati.""" from __future__ import annotations import json import os import sys ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if ROOT not in sys.path: sys.path.insert(0, ROOT) def _load_dotenv() -> None: env_path = os.path.join(ROOT, ".env") if not os.path.isfile(env_path): return with open(env_path, encoding="utf-8") as fh: for line in fh: line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") key = key.strip() if key and key not in os.environ: os.environ[key] = value.strip().strip("'").strip('"') def _configure_paths() -> str: in_container = os.path.isfile("/.dockerenv") or ( os.path.isdir("/data") and os.access("/data", os.W_OK) ) if in_container: os.environ.setdefault("MCP_DB", "/data/loogle_mcp.db") os.environ.setdefault("MCP_VECTOR_FALLBACK", "/data/vector_fallback.db") return "container" os.environ["MCP_DB"] = "/tmp/loogle_mcp_p3_test.db" os.environ["MCP_VECTOR_FALLBACK"] = "/tmp/loogle_mcp_p3_vectors.db" return "host" def main() -> int: _load_dotenv() mode = _configure_paths() print(f"mode={mode} db={os.environ['MCP_DB']} vector={os.environ['MCP_VECTOR_FALLBACK']}") from app.db import get_conn, init_db from app.knowledge import gitea_indexer from app.mcp import tools init_db() if not get_conn().execute( "SELECT 1 FROM sqlite_master WHERE name='indexed_gitea_files'" ).fetchone(): raise RuntimeError("tabella indexed_gitea_files assente") if not os.environ.get("GITEA_API_TOKEN_DANIELE", "").strip(): print("SKIP: GITEA_API_TOKEN_DANIELE assente") return 1 repo = "daniele/rete" existing = gitea_indexer.list_indexed_files(limit=5, repo=repo) stats = gitea_indexer.index_stats() if len(existing) < 1 or stats.get("qdrant_points", 0) < 1: index_result = gitea_indexer.index_repo( repo, username="daniele", force=True, max_files=1, ) print("index", json.dumps(index_result, ensure_ascii=False)) else: print("index_skip", "already", len(existing), "files") files = gitea_indexer.list_indexed_files(limit=5, repo=repo) if not files: raise RuntimeError("indexed_gitea_files vuota") print("indexed_sample", [f["path"] for f in files[:3]]) claims = {"sub": "daniele", "scope": "knowledge:read gitea:read admin", "admin": "admin"} for query in ("failover tier-b", "runbook"): hits = gitea_indexer.search_gitea_knowledge("daniele", query, limit=5, is_admin=True) if not hits: raise RuntimeError(f"search_gitea_knowledge senza risultati per: {query}") top = hits[0] print( f"search_ok[{query}]", top.get("path") or top.get("title"), round(float(top.get("score", 0)), 3), ) tool_out = tools.call_tool( "search_gitea_knowledge", {"query": "failover", "limit": 5}, claims, ) if not json.loads(tool_out["content"][0]["text"]).get("results"): raise RuntimeError("tool search_gitea_knowledge vuoto") unified = tools.call_tool( "search_knowledge", {"query": "FAILOVER CENSIMENTO documenti servizi", "limit": 12}, claims, ) unified_payload = json.loads(unified["content"][0]["text"]) results = unified_payload.get("results") or [] sources = set() for r in results: if r.get("repo"): sources.add("gitea") elif r.get("source"): sources.add(r["source"]) else: sources.add("paperless") print("search_knowledge_sources", sorted(sources)) if "gitea" not in sources: raise RuntimeError("search_knowledge non include risultati Gitea") list_tool = tools.call_tool("list_gitea_indexed_files", {"repo": repo, "limit": 5}, claims) if json.loads(list_tool["content"][0]["text"]).get("count", 0) <= 0: raise RuntimeError("list_gitea_indexed_files vuoto") print("OK P3: search + unified + list (reindex opzionale via tool reindex_gitea_repo)") return 0 if __name__ == "__main__": raise SystemExit(main())