#!/usr/bin/env python3 import os import requests import base64 from pathlib import Path from backend.config import HF_API_KEY, HF_IMAGE_MODEL_ID from backend.database.connection import get_connection from backend.ingestion.image_loader import get_pending_image_jobs, mark_job_completed, mark_job_failed def get_hf_api_url(): model_id = HF_IMAGE_MODEL_ID or "Salesforce/blip-image-captioning-large" return f"https://api-inference.huggingface.co/models/{model_id}" def caption_single_image_hf(image_path: str) -> str: """ Generates a caption for a single image using Hugging Face Inference API. Zero local VRAM usage. """ if not HF_API_KEY: print("[Captioner] Error: HF_API_KEY not set in backend/.env") return "Caption generation failed: No API Key." headers = {"Authorization": f"Bearer {HF_API_KEY}"} api_url = get_hf_api_url() try: with open(image_path, "rb") as f: data = f.read() print(f"[Captioner] Calling HF API ({api_url}) for {Path(image_path).name}...") response = requests.post(api_url, headers=headers, data=data) if response.status_code == 200: result = response.json() if isinstance(result, list) and len(result) > 0 and "generated_text" in result[0]: caption = result[0]["generated_text"].strip() return caption elif "error" in result: print(f"[Captioner] HF API Error: {result['error']}") return "Caption generation failed." else: print(f"[Captioner] HF API failed with status {response.status_code}: {response.text}") return "Caption generation failed." except Exception as e: print(f"[Captioner] Error processing {image_path}: {e}") return "Caption generation failed." return "Caption generation failed." def run_caption_pipeline(): """ Main pipeline to process pending image jobs via Hugging Face API. """ print("[Captioner] Starting HF Image Captioning pipeline") jobs = get_pending_image_jobs() print(f"[Captioner] Found {len(jobs)} pending jobs") if not jobs: print("No jobs. Exiting.") return for job in jobs: print(f"[Captioner] Processing: {job['image_path']}") caption = caption_single_image_hf(job['image_path']) if caption and not caption.startswith("Caption generation failed"): mark_job_completed(job['id'], caption) print(f"[Captioner] Job {job['id']} completed: {caption}") else: mark_job_failed(job['id'], caption) print(f"[Captioner] Job {job['id']} failed.") print("[Captioner] Pipeline finished.") if __name__ == "__main__": run_caption_pipeline()