PGC-AI-Chatbot / scripts /test_embedding_singleton.py
Jacooo's picture
Deploy from GitHub: a6a371c
2c8b2df verified
"""
Smoke test: verify embedding model singleton is resilient to module-reload scenarios.
Simulates the exact failure mode reported in the evaluation:
1. First call to _get_embedding_model() succeeds (registers + instantiates)
2. Resetting the module-level global to None (simulating a module reload / failed-first-attempt)
3. Second call should NOT crash with "already registered" β€” the fix must handle
fastembed's class-level registry being pre-populated.
Usage:
cd AI Chatbot
python -m scripts.test_embedding_singleton
"""
import sys
import time
sys.stdout.reconfigure(encoding="utf-8") # Windows-safe emoji
print("=" * 60)
print("Smoke Test: Embedding Singleton Resilience")
print("=" * 60)
def step(label: str, ok: bool):
status = "PASS" if ok else "FAIL"
print(f" [{status}] {label}")
# ---- Step 1: first-time load (should succeed) ----
print("\n[Step 1] First-time load...")
start = time.perf_counter()
from app.vector_store import _get_embedding_model, embed_text
try:
model = _get_embedding_model()
step("_get_embedding_model() returned an instance", model is not None)
except Exception as e:
step(f"_get_embedding_model() raised: {e}", False)
sys.exit(1)
elapsed = time.perf_counter() - start
print(f" (took {elapsed:.1f}s β€” includes model download if first run)\n")
# ---- Step 2: second call with cached model (should return immediately) ----
print("[Step 2] Second call (cached)...")
try:
model2 = _get_embedding_model()
step("_get_embedding_model() returned the same instance", model2 is model)
except Exception as e:
step(f"_get_embedding_model() raised: {e}", False)
sys.exit(1)
print()
# ---- Step 3: embed a test query (should produce 1024-dim vector) ----
print("[Step 3] embed_text produces a valid 1024-dim vector...")
try:
vec = embed_text("berapa pH optimal untuk selada hidroponik?")
is_1024 = len(vec) == 1024
has_values = any(abs(v) > 1e-8 for v in vec[:10])
step(f"embed_text returned {len(vec)} floats", is_1024)
step("first 10 values are non-zero (valid embedding)", has_values)
except Exception as e:
step(f"embed_text raised: {e}", False)
sys.exit(1)
print()
# ---- Step 4: simulate module reload (reset global, keep class registry) ----
print("[Step 4] Simulating module reload β€” reset _embedding_model to None...")
import app.vector_store as vs
try:
old_model = vs._embedding_model
vs._embedding_model = None
step("_embedding_model reset to None", vs._embedding_model is None)
except Exception as e:
step(f"reset failed: {e}", False)
sys.exit(1)
print()
# ---- Step 5: the critical test β€” _get_embedding_model() with registry populated ----
print("[Step 5] Calling _get_embedding_model() after 'module reload'...")
print(" (fastembed class-level registry already has the model from Step 1)")
try:
model3 = _get_embedding_model()
step("_get_embedding_model() did NOT crash with 'already registered'", model3 is not None)
step("instance is valid", hasattr(model3, "query_embed"))
except Exception as e:
step(f"CRASHED: {e}", False)
sys.exit(1)
print()
# ---- Step 6: verify embed still works after reload simulation ----
print("[Step 6] embed_text works after 'module reload'...")
try:
vec2 = embed_text("bagaimana cara mengatasi defisiensi kalium pada tanaman cabai?")
is_1024 = len(vec2) == 1024
step(f"embed_text returned {len(vec2)} floats", is_1024)
# Quick sanity: same query should produce similar embedding
from app.vector_store import EMBEDDING_DIM
step(f"matches EMBEDDING_DIM={EMBEDDING_DIM}", len(vec2) == EMBEDDING_DIM)
except Exception as e:
step(f"embed_text raised: {e}", False)
sys.exit(1)
print()
# ---- Done ----
print("=" * 60)
print("ALL STEPS PASSED β€” singleton fix is working correctly")
print("=" * 60)