comic-panel-search / src /pinecone /build_documents.py
lukkawolff's picture
streamlit app
830fbdc
Raw
History Blame Contribute Delete
2.92 kB
"""Join manifest, dense vectors, and sparse vectors into Pinecone documents."""
from pathlib import Path
import json
import pandas as pd
PROCESSED_DIR = Path("data/comics/processed")
MANIFEST_PATH = PROCESSED_DIR / "panels_manifest.parquet"
DENSE_PATH = PROCESSED_DIR / "image_dense_vectors.parquet"
SPARSE_PATH = PROCESSED_DIR / "text_sparse_vectors.parquet"
OUTPUT_PATH = PROCESSED_DIR / "pinecone_documents.jsonl"
def to_list(val) -> list:
"""Convert numpy array or other sequence to a plain Python list."""
import numpy as np
if isinstance(val, np.ndarray):
return val.tolist()
return list(val)
def valid_sparse(sparse) -> bool:
if not isinstance(sparse, dict):
return False
indices = sparse.get("indices")
values = sparse.get("values")
return indices is not None and len(indices) > 0 and values is not None and len(values) > 0
def main():
manifest = pd.read_parquet(MANIFEST_PATH)
dense = pd.read_parquet(DENSE_PATH)
sparse = pd.read_parquet(SPARSE_PATH)
# Inner join on dense — only panels that were successfully embedded
df = manifest.merge(dense, on="panel_id", how="inner")
# Left join on sparse — panels without text keep their document slot
df = df.merge(sparse, on="panel_id", how="left")
dropped = len(manifest) - len(df)
if dropped:
print(f"Warning: {dropped} panels dropped (missing dense vector)")
count = 0
with open(OUTPUT_PATH, "w") as f:
for row in df.itertuples(index=False):
doc: dict = {
"_id": row.panel_id,
"image_dense": to_list(row.image_dense),
"ocr_text": row.ocr_text_clean or "",
"search_text": row.search_text or "",
"comic_id": row.comic_id,
"book_id": row.book_id,
"page_id": row.page_id,
"page_num": int(row.page_num) if row.page_num is not None and not pd.isna(row.page_num) else None,
"panel_num": int(row.panel_num) if row.panel_num is not None and not pd.isna(row.panel_num) else None,
"image_path": row.image_path,
"source": "COMICS",
"is_ad_page": bool(row.is_ad_page),
}
sparse_vec = getattr(row, "text_sparse", None)
if valid_sparse(sparse_vec):
doc["text_sparse"] = {
"indices": to_list(sparse_vec["indices"]),
"values": to_list(sparse_vec["values"]),
}
f.write(json.dumps(doc) + "\n")
count += 1
print(f"Wrote {count} documents → {OUTPUT_PATH}")
with_sparse = sum(1 for row in df.itertuples(index=False) if valid_sparse(getattr(row, "text_sparse", None)))
print(f" With sparse vector: {with_sparse}")
print(f" Dense only: {count - with_sparse}")
if __name__ == "__main__":
main()