-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_batch.py
More file actions
87 lines (67 loc) · 2.95 KB
/
Copy pathrun_batch.py
File metadata and controls
87 lines (67 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
"""
Runs the MenuAI pipeline against every photo in a folder, one at a time,
and saves all results to a single JSON file for later review/labeling.
Usage:
python run_batch.py path/to/photos_folder
"""
from __future__ import annotations
import json
import sys
import time
from pathlib import Path
from menuai_pipeline import generate_menu_entry
# Free-tier APIs limit how many requests you can make per minute.
# Waiting a couple seconds between photos avoids hitting that limit.
SECONDS_BETWEEN_PHOTOS = 3
# Which file extensions count as a photo we should process
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"}
def find_photos(folder: Path) -> list[Path]:
"""Return every image file directly inside the given folder, sorted by name."""
return sorted(
p for p in folder.iterdir()
if p.is_file() and p.suffix.lower() in IMAGE_EXTENSIONS
)
def run_batch(folder_path: str, output_path: str = "batch_results.json") -> None:
folder = Path(folder_path)
photos = find_photos(folder)
if not photos:
print(f"No photos found in {folder}. Looking for: {IMAGE_EXTENSIONS}")
return
print(f"Found {len(photos)} photos. Starting batch run...\n")
results = []
for i, photo_path in enumerate(photos, start=1):
print(f"[{i}/{len(photos)}] Processing {photo_path.name}...", end=" ", flush=True)
try:
comparison = generate_menu_entry(str(photo_path))
results.append({
"filename": photo_path.name,
"result": json.loads(comparison.model_dump_json()),
})
status = "NEEDS REVIEW" if comparison.needs_review else "auto-approved"
print(f"done — agreement {comparison.agreement_score:.2f} ({status})")
except Exception as e:
# One bad photo (corrupted file, API error, etc.) should not stop the batch.
print(f"FAILED — {e}")
results.append({
"filename": photo_path.name,
"result": None,
"error": str(e),
})
# Don't sleep after the very last photo — no point waiting once we're done
if i < len(photos):
time.sleep(SECONDS_BETWEEN_PHOTOS)
with open(output_path, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
# Quick summary so you don't have to open the JSON file to see how it went
succeeded = [r for r in results if r["result"] is not None]
needs_review_count = sum(
1 for r in succeeded if r["result"]["needs_review"]
)
print(f"\nDone. {len(succeeded)}/{len(photos)} photos processed successfully.")
print(f"{needs_review_count} flagged for review, {len(succeeded) - needs_review_count} auto-approved.")
print(f"Full results saved to {output_path}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python run_batch.py path/to/photos_folder")
sys.exit(1)
run_batch(sys.argv[1])