ShelfPhotoAnalyzer / utils /ai_analyzer.py
Marek4321's picture
Update utils/ai_analyzer.py
242f216 verified
import openai
import streamlit as st
import json
from typing import Dict, Optional
from config import get_openai_api_key, ANALYSIS_PROMPT_TEMPLATE, AI_MODEL
def analyze_shelf_image(image_base64: str, product_name: str, analysis_depth: str = "Podstawowy") -> Optional[Dict]:
"""
Analyze shelf image using OpenAI GPT-4 Vision API
Args:
image_base64: Base64 encoded image
product_name: Name of product to search for
analysis_depth: Level of analysis detail
Returns:
Dictionary with analysis results or None if failed
"""
try:
# Get API key and validate
api_key = get_openai_api_key()
if not api_key:
st.error("⚠️ OpenAI API key not configured!")
return None
# Initialize OpenAI client
client = openai.OpenAI(api_key=api_key)
# Prepare the prompt
prompt = ANALYSIS_PROMPT_TEMPLATE.format(
product_name=product_name,
analysis_depth=analysis_depth
)
# Make API call
response = client.chat.completions.create(
model=AI_MODEL,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": prompt
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{image_base64}",
"detail": "high"
}
}
]
}
],
max_completion_tokens=1500
)
# Parse response
content = response.choices[0].message.content
# Try to extract JSON from response
try:
# Find JSON in the response
start_idx = content.find('{')
end_idx = content.rfind('}') + 1
if start_idx != -1 and end_idx != -1:
json_str = content[start_idx:end_idx]
analysis_results = json.loads(json_str)
# Validate required fields
required_fields = ['product_found', 'overall_score', 'confidence']
for field in required_fields:
if field not in analysis_results:
st.warning(f"Missing required field: {field}")
analysis_results[field] = get_default_value(field)
# Ensure proper data types
analysis_results = normalize_analysis_results(analysis_results)
return analysis_results
else:
st.error("Could not find JSON in AI response")
return create_fallback_result(product_name, content)
except json.JSONDecodeError as e:
st.error(f"Failed to parse AI response as JSON: {str(e)}")
return create_fallback_result(product_name, content)
except Exception as e:
st.error(f"Error during AI analysis: {str(e)}")
return None
def normalize_analysis_results(results: Dict) -> Dict:
"""Normalize analysis results to ensure proper data types"""
try:
# Ensure boolean fields
bool_fields = ['product_found', 'price_visible']
for field in bool_fields:
if field in results:
if isinstance(results[field], str):
results[field] = results[field].lower() in ['true', '1', 'yes', 'tak']
else:
results[field] = bool(results[field])
# Ensure numeric fields
if 'overall_score' in results:
try:
results['overall_score'] = max(1, min(10, int(float(results['overall_score']))))
except:
results['overall_score'] = 5
if 'confidence' in results:
try:
results['confidence'] = max(0.0, min(1.0, float(results['confidence'])))
except:
results['confidence'] = 0.5
if 'facing_count' in results:
try:
results['facing_count'] = max(0, int(results['facing_count']))
except:
results['facing_count'] = 0
if 'shelf_share' in results:
try:
results['shelf_share'] = max(0, min(100, float(results['shelf_share'])))
except:
results['shelf_share'] = 0
# Ensure string fields
string_fields = ['shelf_position', 'product_condition', 'description']
for field in string_fields:
if field in results and not isinstance(results[field], str):
results[field] = str(results[field])
# Ensure list fields
if 'competitors_nearby' in results and not isinstance(results['competitors_nearby'], list):
results['competitors_nearby'] = []
return results
except Exception as e:
st.warning(f"Error normalizing results: {str(e)}")
return results
def get_default_value(field: str):
"""Get default value for missing field"""
defaults = {
'product_found': False,
'facing_count': 0,
'shelf_position': 'unknown',
'price_visible': False,
'product_condition': 'unknown',
'overall_score': 5,
'confidence': 0.5,
'description': 'Analysis incomplete',
'competitors_nearby': [],
'shelf_share': 0
}
return defaults.get(field, None)
def create_fallback_result(product_name: str, ai_response: str) -> Dict:
"""Create fallback result when JSON parsing fails"""
# Try to extract basic information from text response
product_found = any(word in ai_response.lower() for word in ['found', 'visible', 'present', 'znaleziono', 'widoczny'])
return {
'product_found': product_found,
'facing_count': 1 if product_found else 0,
'shelf_position': 'unknown',
'price_visible': 'cena' in ai_response.lower() or 'price' in ai_response.lower(),
'product_condition': 'good',
'overall_score': 6 if product_found else 3,
'confidence': 0.6,
'description': f"Analysis of {product_name}: {ai_response[:200]}...",
'competitors_nearby': [],
'shelf_share': 10 if product_found else 0
}