LOT 1 - Optimizare script build -Instalare mono comanda

This commit is contained in:
Dezvoltari Evotech 2026-06-27 06:42:02 -07:00
parent 5380c3fc63
commit 42ff22bf85
127 changed files with 16163 additions and 532 deletions

View file

@ -0,0 +1,271 @@
#!/usr/bin/env python3
"""DiDi LOT 1 — runner automat de teste end-to-end.
Rulează un test funcțional pe fiecare componentă și scrie rezultatele într-un
raport JSON cu timestamp (reports/test_report_<data>.json). Fiecare intrare
conține: componentă, endpoint, http_code, ok (verdict funcțional), latență,
extras din răspuns + request-ul folosit ca dovadă vizibilă.
Rulare: python3 run_tests.py (HOST_IP din .env sau 10.11.10.18)
HOST_IP=1.2.3.4 python3 run_tests.py
"""
from __future__ import annotations
import json, os, subprocess, sys, time, datetime, pathlib
HERE = pathlib.Path(__file__).resolve().parent
ASSETS = HERE / "test_assets"
REPORTS = HERE / "reports"
# --- config din .env (fallback la defaults) ---------------------------------
def load_env() -> dict:
env = {}
f = HERE / ".env"
if f.exists():
for line in f.read_text().splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, v = line.split("=", 1)
env[k.strip()] = v.strip()
return env
ENV = load_env()
H = os.environ.get("HOST_IP") or ENV.get("HOST_IP") or "10.11.10.18"
TOKEN = ENV.get("GATEWAY_API_TOKEN", "didi-local-dev-token-123")
WHISPER = ENV.get("WHISPER_MODEL", "large-v3-turbo")
# --- docker shim (pt pregatirea asset-urilor; merge si prin sg docker) -------
def docker(args: list[str]) -> subprocess.CompletedProcess:
if subprocess.run(["docker", "ps"], capture_output=True).returncode == 0:
return subprocess.run(["docker", *args], capture_output=True, text=True)
return subprocess.run(["sg", "docker", "-c", "docker " + " ".join(args)],
capture_output=True, text=True)
def ensure_assets() -> None:
ASSETS.mkdir(exist_ok=True)
mp4 = ASSETS / "sample.mp4"
if not mp4.exists():
import glob
cands = sorted(glob.glob(str(HERE.parent.parent / "artefacte_lot1/runs_deepfake/**/*.mp4"), recursive=True),
key=lambda p: os.path.getsize(p))
if cands:
subprocess.run(["cp", cands[0], str(mp4)])
if not (ASSETS / "frame.jpg").exists() or not (ASSETS / "audio.wav").exists():
# genereaza un cadru + audio din containerul extractors (are ffmpeg)
docker(["cp", str(mp4), "didiAI-extractors:/tmp/s.mp4"])
docker(["exec", "didiAI-extractors", "sh", "-c",
"ffmpeg -y -i /tmp/s.mp4 -frames:v 1 /tmp/frame.jpg -vn -ar 16000 -ac 1 -t 8 /tmp/audio.wav"])
docker(["cp", "didiAI-extractors:/tmp/frame.jpg", str(ASSETS / "frame.jpg")])
docker(["cp", "didiAI-extractors:/tmp/audio.wav", str(ASSETS / "audio.wav")])
# --- helper request prin curl -----------------------------------------------
def run_curl(args: list[str], timeout: int) -> tuple[int, str, float]:
"""Return (http_code, body, latency_ms)."""
full = ["curl", "-s", "-m", str(timeout), "-w", "\n%{http_code}", *args]
t0 = time.monotonic()
try:
p = subprocess.run(full, capture_output=True, text=True, timeout=timeout + 10)
out = p.stdout
except subprocess.TimeoutExpired:
return (0, "TIMEOUT", round((time.monotonic() - t0) * 1000, 1))
lat = round((time.monotonic() - t0) * 1000, 1)
code = 0; body = out
if "\n" in out:
body, _, last = out.rpartition("\n")
try: code = int(last.strip())
except ValueError: code = 0
return (code, body, lat)
def jparse(body: str):
try: return json.loads(body)
except Exception: return None
# --- definitia testelor ------------------------------------------------------
def t_llm():
c, b, l = run_curl([f"http://{H}:14011/v1/chat/completions", "-H", "Content-Type: application/json",
"-d", json.dumps({"model":"qwen3.5","messages":[{"role":"user","content":"Ce este un deepfake? O propozitie."}],"max_tokens":120})], 90)
d = jparse(b); txt = (d or {}).get("choices",[{}])[0].get("message",{}).get("content","") if d else ""
return c, l, bool(txt) and not txt.lower().startswith("thinking"), {"reply": txt[:300]}
def t_embeddings():
c,b,l = run_curl([f"http://{H}:14100/v1/embeddings","-H","Content-Type: application/json","-d",json.dumps({"model":"bge-m3","input":"test"})],30)
d=jparse(b); dim=len((d or {}).get("data",[{}])[0].get("embedding",[])) if d else 0
return c,l,dim==1024,{"dim":dim}
def t_rerank():
c,b,l=run_curl([f"http://{H}:14200/v1/rerank","-H","Content-Type: application/json","-d",json.dumps({"model":"bge-reranker-v2-m3","query":"deepfake detection","documents":["o pisica","sistem detectie deepfake video"]})],30)
d=jparse(b); r=(d or {}).get("results",[]) ; top=r[0] if r else {}
return c,l,top.get("index")==1,{"top_index":top.get("index"),"score":round(top.get("relevance_score",0),4)}
def t_audio():
c,b,l=run_curl([f"http://{H}:54300/v1/audio/transcriptions","-F",f"file=@{ASSETS}/audio.wav","-F",f"model={WHISPER}"],120)
d=jparse(b); txt=(d or {}).get("text","") if d else ""
return c,l,bool(txt.strip()),{"text":txt[:200],"language":(d or {}).get("language")}
def t_video():
c,b,l=run_curl([f"http://{H}:54600/analyze/video","-F",f"file=@{ASSETS}/sample.mp4"],180)
d=jparse(b); v=(d or {}).get("verdict")
return c,l,v in ("REAL","FAKE","UNCERTAIN"),{"verdict":v,"frames_analyzed":(d or {}).get("frames_analyzed")}
def t_metadata():
c,b,l=run_curl([f"http://{H}:54400/v1/metadata","-F",f"file=@{ASSETS}/sample.mp4"],40)
d=jparse(b); ok=bool((d or {}).get("analyses",{}).get("integrity",{}).get("ok"))
return c,l,ok,{"media_type":(d or {}).get("media_type"),"sha256":(d or {}).get("sha256","")[:16]}
def t_ner():
c,b,l=run_curl([f"http://{H}:54400/v1/ner","-H","Content-Type: application/json","-d",json.dumps({"text":"Klaus Iohannis s-a intalnit cu Emmanuel Macron la Bucuresti."})],60)
d=jparse(b); ents=(d or {}).get("results",{}).get("entities",[])
return c,l,len(ents)>=2,{"entities":[e.get("text")+":"+e.get("label","") for e in ents][:5]}
def t_detect():
c,b,l=run_curl([f"http://{H}:54400/v1/detect","-F",f"file=@{ASSETS}/frame.jpg"],90)
d=jparse(b); det=(d or {}).get("results",{}).get("detections",[])
return c,l,len(det)>=1,{"objects":len(det),"labels":[x.get("label") for x in det][:5]}
def t_ocr():
c,b,l=run_curl([f"http://{H}:54400/v1/ocr","-F",f"file=@{ASSETS}/frame.jpg"],90)
d=jparse(b); ok=bool((d or {}).get("ok")); txt=(d or {}).get("results",{}).get("text","")
return c,l,ok,{"text":txt[:150]}
def t_sentiment():
c,b,l=run_curl([f"http://{H}:54400/v1/sentiment","-H","Content-Type: application/json","-d",json.dumps({"text":"Produsul este excelent, sunt foarte multumit!"})],60)
d=jparse(b); ok=bool((d or {}).get("ok"))
return c,l,ok,{"label":(d or {}).get("results",{}).get("label"),"score":(d or {}).get("results",{}).get("score")}
def t_forensic():
c,b,l=run_curl([f"http://{H}:8085/api/forensic-modules"],15)
d=jparse(b); m=(d or {}).get("available_modules",[])
return c,l,len(m)>=3,{"modules":[x.get("id") for x in m]}
def t_web():
c,b,l=run_curl([f"http://{H}:51100/v1/search","-H","Content-Type: application/json","-d",json.dumps({"queries":["deepfake detection"],"max_results":3})],40)
d=jparse(b); r=(d or {}).get("results",[])
return c,l,len(r)>0,{"results":len(r),"first":(r[0].get("title","")[:60] if r else None)}
def t_catalog():
c,b,l=run_curl([f"http://{H}:11000/catalog/v1/status","-H",f"Authorization: Bearer {TOKEN}"],15)
d=jparse(b); st=(d or {}).get("status")
return c,l,st in ("healthy","degraded"),{"status":st,"components":[x.get("component_id") for x in (d or {}).get("components",[])]}
def t_gateway():
c,b,l=run_curl([f"http://{H}:11000/health"],10)
c2,_,_=run_curl([f"http://{H}:11000/web/v1/info"],10) # fara token -> 401
return c,l,(c==200 and c2==401),{"health":c,"web_no_auth":c2}
def t_brain_atoms():
c,b,l=run_curl([f"http://{H}:8090/v1/analysis_atom/stats/extended"],15)
d=jparse(b)
return c,l,(d is not None and "total_atoms" in d),{"total_atoms":(d or {}).get("total_atoms"),"by_tier":(d or {}).get("by_tier")}
def t_brain_facts():
c,b,l=run_curl([f"http://{H}:8090/v1/fact_status/list?page=1&page_size=10"],15)
d=jparse(b)
return c,l,(d is not None and "total" in d),{"total":(d or {}).get("total")}
def t_brain_vcache():
c,b,l=run_curl([f"http://{H}:8090/v1/verification_cache/list?page=1&page_size=10"],15)
d=jparse(b)
return c,l,(d is not None and "total" in d),{"total":(d or {}).get("total")}
def t_llm_completions():
c,b,l=run_curl([f"http://{H}:14011/v1/completions","-H","Content-Type: application/json","-d",json.dumps({"model":"qwen3.5","prompt":"Capitala Romaniei este","max_tokens":10})],60)
d=jparse(b); txt=(d or {}).get("choices",[{}])[0].get("text","") if d else ""
return c,l,bool(txt.strip()),{"text":txt[:120]}
def t_llm_info():
c,b,l=run_curl([f"http://{H}:14011/v1/info"],15); d=jparse(b)
return c,l,isinstance(d,dict) and len(d)>0,{"keys":list(d.keys())[:6] if isinstance(d,dict) else None}
def t_video_semantic():
c,b,l=run_curl([f"http://{H}:54600/analyze/video/semantic","-F",f"file=@{ASSETS}/sample.mp4"],180)
d=jparse(b); ok=bool(d) and ("final_summary" in d or "chunk_results" in d)
return c,l,ok,{"num_chunks":(d or {}).get("num_chunks"),"summary":(str((d or {}).get("final_summary",""))[:120])}
def t_web_fetch():
c,b,l=run_curl([f"http://{H}:51100/v1/fetch","-H","Content-Type: application/json","-d",json.dumps({"urls":["https://en.wikipedia.org/wiki/Deepfake"]})],60)
ok=(c==200 and len(b)>1500 and ("title" in b or "content" in b or "text" in b))
return c,l,ok,{"bytes":len(b)}
def t_web_gather():
c,b,l=run_curl([f"http://{H}:51100/v1/gather","-H","Content-Type: application/json","-d",json.dumps({"claim":"Deepfakes can be detected by AI","max_search_results":5})],150)
d=jparse(b); ev=(d or {}).get("evidence",[]); n=(d or {}).get("total_evidence_items",len(ev) if isinstance(ev,list) else 0)
return c,l,bool(d) and (n>0 or (isinstance(ev,list) and len(ev)>0)),{"evidence_items":n,"urls_found":(d or {}).get("total_urls_found")}
def t_forensic_evidence():
c,b,l=run_curl([f"http://{H}:8085/api/forensic-evidence","-F",f"video=@{ASSETS}/sample.mp4","-F","modules=m29","-F","every_n_frames=30"],150)
d=jparse(b); mr=(d or {}).get("modules_run",[])
return c,l,bool(mr),{"modules_run":mr,"frames":(d or {}).get("n_frames_extracted")}
def t_catalog_components():
c,b,l=run_curl([f"http://{H}:11000/catalog/v1/components","-H",f"Authorization: Bearer {TOKEN}"],15)
d=jparse(b); comp=(d or {}).get("components",d if isinstance(d,list) else [])
return c,l,c==200 and len(comp)>0,{"components":len(comp)}
def t_catalog_models():
c,b,l=run_curl([f"http://{H}:11000/catalog/v1/models","-H",f"Authorization: Bearer {TOKEN}"],15)
d=jparse(b)
return c,l,c==200 and d is not None,{"models":len((d or {}).get("models",d if isinstance(d,list) else []))}
def t_brain_search():
c,b,l=run_curl([f"http://{H}:8090/v1/search","-H","Content-Type: application/json","-d",json.dumps({"queries":["deepfake"],"max_results":2})],60)
d=jparse(b)
return c,l,bool(d) and "results" in d,{"total_results":(d or {}).get("total_results"),"cache":(d or {}).get("brain_meta",{}).get("cache_status")}
def t_brain_gather():
c,b,l=run_curl([f"http://{H}:8090/v1/gather","-H","Content-Type: application/json","-d",json.dumps({"claim":"Deepfakes can be detected by AI"})],120)
d=jparse(b)
return c,l,bool(d) and "evidence" in d,{"evidence_items":(d or {}).get("total_evidence_items"),"urls":(d or {}).get("total_urls_found")}
def t_dashboard_health():
c,b,l=run_curl([f"http://{H}:51300/health"],10); d=jparse(b)
return c,l,(d or {}).get("status") in ("healthy","ok"),{"status":(d or {}).get("status"),"db":(d or {}).get("db")}
def t_dashboard_monitoring():
c,b,l=run_curl([f"http://{H}:51300/api/monitoring/services"],20); d=jparse(b)
s=d if isinstance(d,list) else (d or {}).get("services",[])
h=sum(1 for x in s if x.get("status")=="healthy")
return c,l,len(s)>0 and h==len(s),{"healthy":f"{h}/{len(s)}"}
def t_cloak():
c,b,l=run_curl([f"http://{H}:8770/v1/search","-H","Content-Type: application/json","-d",json.dumps({"queries":["deepfake"],"engines":["ddg"],"max_results_per_engine":3})],45)
d=jparse(b); r=(d or {}).get("results",[])
return c,l,len(r)>0,{"results":len(r),"first":(r[0].get("title","")[:55] if r else None)}
TESTS = [
("llm","LLM (Qwen3.5-35B-A3B)","POST /v1/chat/completions",t_llm),
("llm_completions","LLM · completions","POST /v1/completions",t_llm_completions),
("llm_info","LLM · info","GET /v1/info",t_llm_info),
("embeddings","Embeddings (bge-m3)","POST /v1/embeddings",t_embeddings),
("rerank","Rerank (bge-reranker-v2-m3)","POST /v1/rerank",t_rerank),
("audio","Audio (Whisper large-v3-turbo)","POST /v1/audio/transcriptions",t_audio),
("video","Video/BusterX (deepfake)","POST /analyze/video",t_video),
("video_semantic","Video · semantic","POST /analyze/video/semantic",t_video_semantic),
("extractors_metadata","Extractors · metadata","POST /v1/metadata",t_metadata),
("extractors_ner","Extractors · NER (GLiNER)","POST /v1/ner",t_ner),
("extractors_detect","Extractors · detect (YOLOv8)","POST /v1/detect",t_detect),
("extractors_ocr","Extractors · OCR (LLM vision)","POST /v1/ocr",t_ocr),
("extractors_sentiment","Extractors · sentiment (LLM)","POST /v1/sentiment",t_sentiment),
("forensic","Forensic · module list","GET /api/forensic-modules",t_forensic),
("forensic_evidence","Forensic · analiza reala","POST /api/forensic-evidence",t_forensic_evidence),
("web","Web · search (SearXNG)","POST /v1/search",t_web),
("web_fetch","Web · fetch","POST /v1/fetch",t_web_fetch),
("web_gather","Web · gather (evidence)","POST /v1/gather",t_web_gather),
("catalog","Catalog · status","GET /catalog/v1/status",t_catalog),
("catalog_components","Catalog · components","GET /catalog/v1/components",t_catalog_components),
("catalog_models","Catalog · models","GET /catalog/v1/models",t_catalog_models),
("gateway","Gateway (auth+rutare)","GET /health + 401",t_gateway),
("brain_atoms","Brain · analysis_atom","GET /v1/analysis_atom/stats/extended",t_brain_atoms),
("brain_facts","Brain · fact_status","GET /v1/fact_status/list",t_brain_facts),
("brain_vcache","Brain · verification_cache","GET /v1/verification_cache/list",t_brain_vcache),
("brain_search","Brain · search (RAG)","POST /v1/search",t_brain_search),
("brain_gather","Brain · gather (RAG)","POST /v1/gather",t_brain_gather),
("dashboard","Dashboard · health","GET /health",t_dashboard_health),
("dashboard_monitoring","Dashboard · monitoring 11/11","GET /api/monitoring/services",t_dashboard_monitoring),
("cloak","Cloak · SERP scraper","POST /v1/search",t_cloak),
]
def main() -> int:
ensure_assets()
ts = datetime.datetime.now().astimezone()
stamp = ts.strftime("%Y%m%d_%H%M%S")
results = []
print(f"\n DiDi LOT 1 — test run @ {ts.isoformat()} (host {H})\n")
for tid, name, ep, fn in TESTS:
try:
code, lat, ok, extra = fn()
except Exception as e: # noqa: BLE001
code, lat, ok, extra = 0, 0.0, False, {"error": str(e)}
mark = "\033[32mPASS\033[0m" if ok else "\033[31mFAIL\033[0m"
print(f" [{mark}] {name:34} {ep:38} HTTP {code:>3} {lat:>7.0f}ms {json.dumps(extra, ensure_ascii=False)[:80]}")
results.append({"id":tid,"name":name,"endpoint":ep,"http_code":code,
"ok":ok,"latency_ms":lat,"result":extra,"checked_at":ts.isoformat()})
passed = sum(1 for r in results if r["ok"])
report = {
"generated_at": ts.isoformat(),
"host": H,
"summary": {"total": len(results), "passed": passed, "failed": len(results) - passed},
"tests": results,
}
REPORTS.mkdir(exist_ok=True)
out = REPORTS / f"test_report_{stamp}.json"
out.write_text(json.dumps(report, indent=2, ensure_ascii=False))
latest = REPORTS / "latest.json"
latest.write_text(json.dumps(report, indent=2, ensure_ascii=False))
print(f"\n === {passed}/{len(results)} PASS ===")
print(f" Raport JSON: {out}")
print(f" (si copie: {latest})\n")
return 0 if passed == len(results) else 1
if __name__ == "__main__":
sys.exit(main())