vantage / scripts /ingest_topics.py
sourabh gupta
refactor(agent): semantic topic index, label-free gaps, decluttered prompt
cf55da0
Raw
History Blame Contribute Delete
2.79 kB
#!/usr/bin/env python
"""One-time ingest of the topic vocabulary into the vantage_topics Qdrant collection.
Run once (or whenever seeds/topics.csv changes):
python scripts/ingest_topics.py
Each point stores:
vector β€” 384-dim MiniLM dense embedding of the keyword string
payload β€” {keyword: str, source: str}
The TopicIndex adapter then does a single search against this collection at runtime
instead of re-embedding 708 strings on every API cold start.
"""
from __future__ import annotations
import csv
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "packages" / "vantage_core"))
from qdrant_client import QdrantClient
from qdrant_client import models as qm
from vantage_core.adapters.embedder_st import STEmbedder
from vantage_core.config import get_settings
from vantage_core.constants import EMBEDDING_DIM, TOPICS_COLLECTION_NAME
from vantage_core.log import get_logger
log = get_logger(__name__)
_SEEDS = Path(__file__).parent.parent / "packages" / "vantage_core" / "seeds" / "topics.csv"
def main() -> None:
s = get_settings()
client = QdrantClient(url=s.qdrant_url, api_key=s.qdrant_api_key, timeout=60)
# Read topics
with _SEEDS.open() as fh:
rows = list(csv.DictReader(fh))
keywords = [r["keyword"] for r in rows]
sources = [r.get("source", "") for r in rows]
log.info("ingest_topics topics=%d file=%s", len(keywords), _SEEDS)
# Create collection (dense-only β€” BM25 overkill for short keyword strings)
if not client.collection_exists(TOPICS_COLLECTION_NAME):
client.create_collection(
collection_name = TOPICS_COLLECTION_NAME,
vectors_config = qm.VectorParams(size=EMBEDDING_DIM, distance=qm.Distance.COSINE),
)
log.info("ingest_topics created collection %r", TOPICS_COLLECTION_NAME)
else:
log.info("ingest_topics collection %r already exists β€” upserting", TOPICS_COLLECTION_NAME)
# Embed all keywords in one batch
embedder = STEmbedder()
log.info("ingest_topics embedding %d keywords ...", len(keywords))
vectors = embedder.embed(keywords)
# Upsert β€” point id = row index (stable, idempotent)
points = [
qm.PointStruct(
id = i,
vector = vec,
payload = {"keyword": kw, "source": src},
)
for i, (kw, src, vec) in enumerate(zip(keywords, sources, vectors))
]
client.upsert(collection_name=TOPICS_COLLECTION_NAME, points=points)
count = client.count(TOPICS_COLLECTION_NAME).count
log.info("ingest_topics done collection=%r points=%d", TOPICS_COLLECTION_NAME, count)
print(f"βœ“ {count} topics ingested into '{TOPICS_COLLECTION_NAME}'")
if __name__ == "__main__":
main()