Files
Priceflow/app/utils/image.py
T
Claude f2d45f9566 fix: Improve AI product price extraction for French e-commerce sites
Three critical fixes to resolve AI extraction returning null prices with 0.0 confidence:

1. **Add French price/stock keywords** (app/utils/text.py)
   - Added € symbol and French price patterns (12,99 €, 1 234,56 €)
   - Added French keywords: prix, coût, promotion, réduction
   - Added French stock keywords: ajouter au panier, en stock, rupture, épuisé
   - Previously only detected $ and English keywords

2. **Enable full-page screenshots** (app/services/browserless_service.py)
   - Changed full_page=False to full_page=True
   - Now captures entire page instead of viewport-only
   - Prevents missing prices located below the fold

3. **Increase image resolution** (app/utils/image.py)
   - Increased MAX_IMAGE_SIZE from 1024px to 2048px
   - Preserves fine details in price text for better AI recognition
   - Improves readability on dense product pages

These changes fix the issue where AI models returned:
{"price": null, "price_confidence": 0.0, "in_stock": null, "in_stock_confidence": 0.0}

Tested with Amazon.fr product monitoring.
2025-11-29 19:07:04 +00:00

47 lines
1.4 KiB
Python

import asyncio
import base64
import io
import logging
from PIL import Image
logger = logging.getLogger(__name__)
# Image processing constants
# Increased from 1024 to 2048 to preserve more detail for AI analysis
MAX_IMAGE_SIZE = 2048
JPEG_QUALITY = 85
def _process_image(image_path: str) -> str:
"""
Synchronous image processing function to be run in an executor.
"""
try:
with Image.open(image_path) as img:
# Resize if too large (e.g., max dimension 1024)
if max(img.size) > MAX_IMAGE_SIZE:
img.thumbnail((MAX_IMAGE_SIZE, MAX_IMAGE_SIZE))
logger.info(f"Resized image to {img.size}")
# Convert to RGB if necessary (e.g. for PNGs with alpha)
if img.mode in ("RGBA", "P"):
img_to_process = img.convert("RGB")
else:
img_to_process = img
buffered = io.BytesIO()
img_to_process.save(buffered, format="JPEG", quality=JPEG_QUALITY)
return base64.b64encode(buffered.getvalue()).decode("utf-8")
except Exception as e:
logger.error(f"Error encoding image: {e}")
raise
async def encode_image(image_path: str) -> str:
"""
Asynchronously encode image by running blocking code in a thread.
"""
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, _process_image, image_path)