Spaces:
Running
Running
fix
Browse files
py_backend/app/routers/caption.py
CHANGED
|
@@ -44,7 +44,11 @@ if settings.HF_API_KEY:
|
|
| 44 |
try:
|
| 45 |
models = crud.get_models(db)
|
| 46 |
for model in models:
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
try:
|
| 49 |
service = ProvidersGenericVLMService(
|
| 50 |
api_key=settings.HF_API_KEY,
|
|
@@ -89,8 +93,6 @@ async def create_caption(
|
|
| 89 |
db: Session = Depends(get_db),
|
| 90 |
):
|
| 91 |
print(f"π Caption Router: Starting caption generation for image {image_id}")
|
| 92 |
-
print(f"π Caption Router: Title: {title}")
|
| 93 |
-
print(f"π Caption Router: Prompt: {prompt}")
|
| 94 |
print(f"π Caption Router: Requested model: {model_name}")
|
| 95 |
|
| 96 |
# Get the image
|
|
@@ -99,16 +101,12 @@ async def create_caption(
|
|
| 99 |
print(f"β Caption Router: Image {image_id} not found")
|
| 100 |
raise HTTPException(404, f"Image {image_id} not found")
|
| 101 |
|
| 102 |
-
print(f"π Caption Router: Image found: {img.file_key}, type: {img.image_type}")
|
| 103 |
-
|
| 104 |
# Get the prompt object
|
| 105 |
prompt_obj = crud.get_prompt(db, prompt)
|
| 106 |
if not prompt_obj:
|
| 107 |
print(f"β Caption Router: Prompt '{prompt}' not found")
|
| 108 |
raise HTTPException(400, f"Prompt '{prompt}' not found")
|
| 109 |
|
| 110 |
-
print(f"π Caption Router: Prompt found: {prompt_obj.p_code}")
|
| 111 |
-
|
| 112 |
# Get image bytes
|
| 113 |
try:
|
| 114 |
if hasattr(storage, 's3') and settings.STORAGE_PROVIDER != "local":
|
|
@@ -122,7 +120,6 @@ async def create_caption(
|
|
| 122 |
file_path = os.path.join(settings.STORAGE_DIR, img.file_key)
|
| 123 |
with open(file_path, 'rb') as f:
|
| 124 |
img_bytes = f.read()
|
| 125 |
-
print(f"π Caption Router: Image bytes retrieved: {len(img_bytes)} bytes")
|
| 126 |
except Exception as e:
|
| 127 |
print(f"β Caption Router: Failed to get image bytes: {e}")
|
| 128 |
raise HTTPException(500, f"Failed to get image: {e}")
|
|
@@ -131,76 +128,63 @@ async def create_caption(
|
|
| 131 |
metadata_instructions = ""
|
| 132 |
if img.image_type == "drone_image":
|
| 133 |
metadata_instructions = f"Image type: drone image. Center coordinates: {img.center_lon}, {img.center_lat}. Altitude: {img.amsl_m}m AMSL, {img.agl_m}m AGL. Heading: {img.heading_deg}Β°, Yaw: {img.yaw_deg}Β°, Pitch: {img.pitch_deg}Β°, Roll: {img.roll_deg}Β°. RTK fix: {img.rtk_fix}. Standard deviations: H={img.std_h_m}m, V={img.std_v_m}m."
|
| 134 |
-
print(f"π Caption Router: Drone metadata instructions prepared")
|
| 135 |
else:
|
| 136 |
metadata_instructions = f"Image type: crisis map. Source: {img.source}. Event type: {img.event_type}. EPSG: {img.epsg}. Countries: {img.countries}."
|
| 137 |
-
print(f"π Caption Router: Crisis map metadata instructions prepared")
|
| 138 |
|
| 139 |
print(f"π Caption Router: Calling VLM manager...")
|
| 140 |
|
| 141 |
-
|
| 142 |
try:
|
| 143 |
result = await vlm_manager.generate_caption(
|
| 144 |
-
image_bytes=img_bytes,
|
| 145 |
prompt=prompt_obj.label,
|
| 146 |
metadata_instructions=metadata_instructions,
|
| 147 |
model_name=model_name,
|
| 148 |
db_session=db,
|
| 149 |
)
|
| 150 |
-
|
| 151 |
-
print(f"π Caption Router: VLM manager returned result")
|
| 152 |
-
print(f"π Caption Router: Result keys: {list(result.keys())}")
|
| 153 |
-
print(f"π Caption Router: Model used: {result.get('model')}")
|
| 154 |
-
print(f"π Caption Router: Fallback used: {result.get('fallback_used')}")
|
| 155 |
-
|
| 156 |
-
# Get the raw response for validation
|
| 157 |
-
raw = result.get("raw_response", {})
|
| 158 |
-
|
| 159 |
-
# Validate and clean the data using schema validation
|
| 160 |
-
image_type = img.image_type
|
| 161 |
-
print(f"π Caption Router: Validating data for image type: {image_type}")
|
| 162 |
-
print(f"π Caption Router: Raw data structure: {list(raw.keys()) if isinstance(raw, dict) else 'Not a dict'}")
|
| 163 |
-
|
| 164 |
-
cleaned_data, is_valid, validation_error = schema_validator.clean_and_validate_data(raw, image_type)
|
| 165 |
-
|
| 166 |
-
if is_valid:
|
| 167 |
-
print(f"β
Caption Router: Schema validation passed for {image_type}")
|
| 168 |
-
text = cleaned_data.get("analysis", "")
|
| 169 |
-
metadata = cleaned_data.get("metadata", {})
|
| 170 |
-
else:
|
| 171 |
-
print(f"β οΈ Caption Router: Schema validation failed for {image_type}: {validation_error}")
|
| 172 |
-
# Use fallback but log the validation error
|
| 173 |
-
text = result.get("caption", "This is a fallback caption due to schema validation error.")
|
| 174 |
-
metadata = result.get("metadata", {})
|
| 175 |
-
raw["validation_error"] = validation_error
|
| 176 |
-
raw["validation_failed"] = True
|
| 177 |
-
|
| 178 |
-
# Use the actual model that was used, not the requested model_name
|
| 179 |
-
used_model = result.get("model", model_name) or "STUB_MODEL"
|
| 180 |
-
|
| 181 |
-
# Check if fallback was used
|
| 182 |
-
fallback_used = result.get("fallback_used", False)
|
| 183 |
-
original_model = result.get("original_model", None)
|
| 184 |
-
fallback_reason = result.get("fallback_reason", None)
|
| 185 |
-
|
| 186 |
-
if fallback_used:
|
| 187 |
-
print(f"β οΈ Caption Router: Model fallback occurred: {original_model} -> {used_model} (reason: {fallback_reason})")
|
| 188 |
-
# Add fallback info to raw response for frontend
|
| 189 |
-
raw["fallback_info"] = {
|
| 190 |
-
"original_model": original_model,
|
| 191 |
-
"fallback_model": used_model,
|
| 192 |
-
"reason": fallback_reason
|
| 193 |
-
}
|
| 194 |
-
else:
|
| 195 |
-
print(f"β
Caption Router: No fallback used, primary model {used_model} succeeded")
|
| 196 |
-
|
| 197 |
except Exception as e:
|
| 198 |
-
print(f"β Caption Router: VLM
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
|
| 205 |
print(f"π Caption Router: Creating caption in database...")
|
| 206 |
|
|
@@ -218,20 +202,14 @@ async def create_caption(
|
|
| 218 |
db.refresh(c)
|
| 219 |
|
| 220 |
print(f"π Caption Router: Caption created successfully")
|
| 221 |
-
print(f"π Caption Router: Caption ID: {c.image_id}")
|
| 222 |
-
print(f"π Caption Router: Model used: {c.model}")
|
| 223 |
|
| 224 |
from .upload import convert_image_to_dict
|
| 225 |
try:
|
| 226 |
url = storage.get_object_url(c.file_key)
|
| 227 |
-
print(f"π Caption Router: Generated URL: {url}")
|
| 228 |
if url.startswith('/') and settings.STORAGE_PROVIDER == "local":
|
| 229 |
url = f"http://localhost:8000{url}"
|
| 230 |
-
print(f"π Caption Router: Local URL adjusted to: {url}")
|
| 231 |
except Exception as e:
|
| 232 |
-
print(f"β οΈ Caption Router: URL generation failed: {e}")
|
| 233 |
url = f"/api/images/{c.image_id}/file"
|
| 234 |
-
print(f"π Caption Router: Using fallback URL: {url}")
|
| 235 |
|
| 236 |
img_dict = convert_image_to_dict(c, url)
|
| 237 |
print(f"π Caption Router: Caption generation completed successfully")
|
|
|
|
| 44 |
try:
|
| 45 |
models = crud.get_models(db)
|
| 46 |
for model in models:
|
| 47 |
+
|
| 48 |
+
if (model.provider == "huggingface" and
|
| 49 |
+
model.model_id and
|
| 50 |
+
model.m_code != "STUB_MODEL" and
|
| 51 |
+
model.m_code not in ["GPT-4O", "GEMINI15"]):
|
| 52 |
try:
|
| 53 |
service = ProvidersGenericVLMService(
|
| 54 |
api_key=settings.HF_API_KEY,
|
|
|
|
| 93 |
db: Session = Depends(get_db),
|
| 94 |
):
|
| 95 |
print(f"π Caption Router: Starting caption generation for image {image_id}")
|
|
|
|
|
|
|
| 96 |
print(f"π Caption Router: Requested model: {model_name}")
|
| 97 |
|
| 98 |
# Get the image
|
|
|
|
| 101 |
print(f"β Caption Router: Image {image_id} not found")
|
| 102 |
raise HTTPException(404, f"Image {image_id} not found")
|
| 103 |
|
|
|
|
|
|
|
| 104 |
# Get the prompt object
|
| 105 |
prompt_obj = crud.get_prompt(db, prompt)
|
| 106 |
if not prompt_obj:
|
| 107 |
print(f"β Caption Router: Prompt '{prompt}' not found")
|
| 108 |
raise HTTPException(400, f"Prompt '{prompt}' not found")
|
| 109 |
|
|
|
|
|
|
|
| 110 |
# Get image bytes
|
| 111 |
try:
|
| 112 |
if hasattr(storage, 's3') and settings.STORAGE_PROVIDER != "local":
|
|
|
|
| 120 |
file_path = os.path.join(settings.STORAGE_DIR, img.file_key)
|
| 121 |
with open(file_path, 'rb') as f:
|
| 122 |
img_bytes = f.read()
|
|
|
|
| 123 |
except Exception as e:
|
| 124 |
print(f"β Caption Router: Failed to get image bytes: {e}")
|
| 125 |
raise HTTPException(500, f"Failed to get image: {e}")
|
|
|
|
| 128 |
metadata_instructions = ""
|
| 129 |
if img.image_type == "drone_image":
|
| 130 |
metadata_instructions = f"Image type: drone image. Center coordinates: {img.center_lon}, {img.center_lat}. Altitude: {img.amsl_m}m AMSL, {img.agl_m}m AGL. Heading: {img.heading_deg}Β°, Yaw: {img.yaw_deg}Β°, Pitch: {img.pitch_deg}Β°, Roll: {img.roll_deg}Β°. RTK fix: {img.rtk_fix}. Standard deviations: H={img.std_h_m}m, V={img.std_v_m}m."
|
|
|
|
| 131 |
else:
|
| 132 |
metadata_instructions = f"Image type: crisis map. Source: {img.source}. Event type: {img.event_type}. EPSG: {img.epsg}. Countries: {img.countries}."
|
|
|
|
| 133 |
|
| 134 |
print(f"π Caption Router: Calling VLM manager...")
|
| 135 |
|
| 136 |
+
# Call VLM manager
|
| 137 |
try:
|
| 138 |
result = await vlm_manager.generate_caption(
|
| 139 |
+
image_bytes=img_bytes,
|
| 140 |
prompt=prompt_obj.label,
|
| 141 |
metadata_instructions=metadata_instructions,
|
| 142 |
model_name=model_name,
|
| 143 |
db_session=db,
|
| 144 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
except Exception as e:
|
| 146 |
+
print(f"β Caption Router: VLM manager failed: {e}")
|
| 147 |
+
raise HTTPException(500, f"Caption generation failed: {e}")
|
| 148 |
+
|
| 149 |
+
print(f"π Caption Router: VLM manager returned result")
|
| 150 |
+
|
| 151 |
+
# Get the raw response for validation
|
| 152 |
+
raw = result.get("raw_response", {})
|
| 153 |
+
|
| 154 |
+
# Validate and clean the data using schema validation
|
| 155 |
+
image_type = img.image_type
|
| 156 |
+
print(f"π Caption Router: Validating data for image type: {image_type}")
|
| 157 |
+
|
| 158 |
+
cleaned_data, is_valid, validation_error = schema_validator.clean_and_validate_data(raw, image_type)
|
| 159 |
+
|
| 160 |
+
if is_valid:
|
| 161 |
+
print(f"β
Caption Router: Schema validation passed for {image_type}")
|
| 162 |
+
text = cleaned_data.get("analysis", "")
|
| 163 |
+
metadata = cleaned_data.get("metadata", {})
|
| 164 |
+
else:
|
| 165 |
+
print(f"β οΈ Caption Router: Schema validation failed for {image_type}: {validation_error}")
|
| 166 |
+
# Use fallback but log the validation error
|
| 167 |
+
text = result.get("caption", "This is a fallback caption due to schema validation error.")
|
| 168 |
+
metadata = result.get("metadata", {})
|
| 169 |
+
raw["validation_error"] = validation_error
|
| 170 |
+
raw["validation_failed"] = True
|
| 171 |
+
|
| 172 |
+
# Use the actual model that was used, not the requested model_name
|
| 173 |
+
used_model = result.get("model", model_name) or "STUB_MODEL"
|
| 174 |
+
|
| 175 |
+
# Check if fallback was used
|
| 176 |
+
fallback_used = result.get("fallback_used", False)
|
| 177 |
+
original_model = result.get("original_model", None)
|
| 178 |
+
fallback_reason = result.get("fallback_reason", None)
|
| 179 |
+
|
| 180 |
+
if fallback_used:
|
| 181 |
+
print(f"β οΈ Caption Router: Model fallback occurred: {original_model} -> {used_model} (reason: {fallback_reason})")
|
| 182 |
+
# Add fallback info to raw response for frontend
|
| 183 |
+
raw["fallback_info"] = {
|
| 184 |
+
"original_model": original_model,
|
| 185 |
+
"fallback_model": used_model,
|
| 186 |
+
"reason": fallback_reason
|
| 187 |
+
}
|
| 188 |
|
| 189 |
print(f"π Caption Router: Creating caption in database...")
|
| 190 |
|
|
|
|
| 202 |
db.refresh(c)
|
| 203 |
|
| 204 |
print(f"π Caption Router: Caption created successfully")
|
|
|
|
|
|
|
| 205 |
|
| 206 |
from .upload import convert_image_to_dict
|
| 207 |
try:
|
| 208 |
url = storage.get_object_url(c.file_key)
|
|
|
|
| 209 |
if url.startswith('/') and settings.STORAGE_PROVIDER == "local":
|
| 210 |
url = f"http://localhost:8000{url}"
|
|
|
|
| 211 |
except Exception as e:
|
|
|
|
| 212 |
url = f"/api/images/{c.image_id}/file"
|
|
|
|
| 213 |
|
| 214 |
img_dict = convert_image_to_dict(c, url)
|
| 215 |
print(f"π Caption Router: Caption generation completed successfully")
|
py_backend/app/services/gemini_service.py
CHANGED
|
@@ -84,53 +84,18 @@ class GeminiService(VLMService):
|
|
| 84 |
print(f"β Gemini: Error type: {error_type}")
|
| 85 |
print(f"β Gemini: Error message: {error_msg}")
|
| 86 |
|
| 87 |
-
# Capture Google Gemini API specific error details
|
| 88 |
-
provider_error_details = {}
|
| 89 |
-
|
| 90 |
-
# Check for Google API specific errors
|
| 91 |
-
if hasattr(e, 'status_code'):
|
| 92 |
-
provider_error_details = {
|
| 93 |
-
"provider": "google",
|
| 94 |
-
"status_code": e.status_code,
|
| 95 |
-
"error_type": error_type,
|
| 96 |
-
"error_message": error_msg
|
| 97 |
-
}
|
| 98 |
-
print(f"β Gemini: Google API Error Details: {provider_error_details}")
|
| 99 |
-
elif hasattr(e, 'details'):
|
| 100 |
-
try:
|
| 101 |
-
provider_error_details = {
|
| 102 |
-
"provider": "google",
|
| 103 |
-
"error_details": str(e.details),
|
| 104 |
-
"error_type": error_type,
|
| 105 |
-
"error_message": error_msg
|
| 106 |
-
}
|
| 107 |
-
print(f"β Gemini: Google API Error Details: {provider_error_details}")
|
| 108 |
-
except Exception as parse_error:
|
| 109 |
-
print(f"β οΈ Gemini: Could not parse error details: {parse_error}")
|
| 110 |
-
provider_error_details = {
|
| 111 |
-
"provider": "google",
|
| 112 |
-
"raw_error": error_msg,
|
| 113 |
-
"error_type": error_type
|
| 114 |
-
}
|
| 115 |
-
else:
|
| 116 |
-
provider_error_details = {
|
| 117 |
-
"provider": "google",
|
| 118 |
-
"raw_error": error_msg,
|
| 119 |
-
"error_type": error_type
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
# Check for specific error types
|
| 123 |
if "quota" in error_msg.lower() or "limit" in error_msg.lower():
|
| 124 |
print(f"β Gemini: Quota or rate limit exceeded detected")
|
| 125 |
-
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (quota/rate limit exceeded). Switching to another model.
|
| 126 |
elif "authentication" in error_msg.lower() or "invalid" in error_msg.lower() or "api_key" in error_msg.lower():
|
| 127 |
print(f"β Gemini: Authentication or API key error detected")
|
| 128 |
-
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (authentication error). Switching to another model.
|
| 129 |
elif "timeout" in error_msg.lower() or "connection" in error_msg.lower():
|
| 130 |
print(f"β Gemini: Network timeout or connection error detected")
|
| 131 |
-
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (network error). Switching to another model.
|
| 132 |
else:
|
| 133 |
print(f"β Gemini: Generic error, converting to MODEL_UNAVAILABLE")
|
| 134 |
-
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable ({error_type}: {error_msg}). Switching to another model.
|
| 135 |
|
| 136 |
|
|
|
|
| 84 |
print(f"β Gemini: Error type: {error_type}")
|
| 85 |
print(f"β Gemini: Error message: {error_msg}")
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
# Check for specific error types
|
| 88 |
if "quota" in error_msg.lower() or "limit" in error_msg.lower():
|
| 89 |
print(f"β Gemini: Quota or rate limit exceeded detected")
|
| 90 |
+
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (quota/rate limit exceeded). Switching to another model.")
|
| 91 |
elif "authentication" in error_msg.lower() or "invalid" in error_msg.lower() or "api_key" in error_msg.lower():
|
| 92 |
print(f"β Gemini: Authentication or API key error detected")
|
| 93 |
+
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (authentication error). Switching to another model.")
|
| 94 |
elif "timeout" in error_msg.lower() or "connection" in error_msg.lower():
|
| 95 |
print(f"β Gemini: Network timeout or connection error detected")
|
| 96 |
+
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable (network error). Switching to another model.")
|
| 97 |
else:
|
| 98 |
print(f"β Gemini: Generic error, converting to MODEL_UNAVAILABLE")
|
| 99 |
+
raise Exception(f"MODEL_UNAVAILABLE: GEMINI15 is currently unavailable ({error_type}: {error_msg}). Switching to another model.")
|
| 100 |
|
| 101 |
|
py_backend/app/services/gpt4v_service.py
CHANGED
|
@@ -102,41 +102,16 @@ class GPT4VService(VLMService):
|
|
| 102 |
print(f"β GPT-4V: Error type: {error_type}")
|
| 103 |
print(f"β GPT-4V: Error message: {error_msg}")
|
| 104 |
|
| 105 |
-
# Capture OpenAI API specific error details
|
| 106 |
-
provider_error_details = {}
|
| 107 |
-
|
| 108 |
-
# Check for OpenAI API specific errors
|
| 109 |
-
if hasattr(e, 'response') and e.response:
|
| 110 |
-
try:
|
| 111 |
-
error_response = e.response.json()
|
| 112 |
-
print(f"β GPT-4V: OpenAI API Error Response: {error_response}")
|
| 113 |
-
provider_error_details = {
|
| 114 |
-
"provider": "openai",
|
| 115 |
-
"error_code": error_response.get("error", {}).get("code"),
|
| 116 |
-
"error_type": error_response.get("error", {}).get("type"),
|
| 117 |
-
"error_message": error_response.get("error", {}).get("message"),
|
| 118 |
-
"error_param": error_response.get("error", {}).get("param"),
|
| 119 |
-
"status_code": e.response.status_code
|
| 120 |
-
}
|
| 121 |
-
print(f"β GPT-4V: Parsed Error Details: {provider_error_details}")
|
| 122 |
-
except Exception as parse_error:
|
| 123 |
-
print(f"β οΈ GPT-4V: Could not parse error response: {parse_error}")
|
| 124 |
-
provider_error_details = {
|
| 125 |
-
"provider": "openai",
|
| 126 |
-
"raw_error": error_msg,
|
| 127 |
-
"status_code": getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
# Check for specific error types
|
| 131 |
if "rate_limit" in error_msg.lower() or "quota" in error_msg.lower():
|
| 132 |
print(f"β GPT-4V: Rate limit or quota exceeded detected")
|
| 133 |
-
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (rate limit/quota exceeded). Switching to another model.
|
| 134 |
elif "authentication" in error_msg.lower() or "invalid" in error_msg.lower() or "api_key" in error_msg.lower():
|
| 135 |
print(f"β GPT-4V: Authentication or API key error detected")
|
| 136 |
-
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (authentication error). Switching to another model.
|
| 137 |
elif "timeout" in error_msg.lower() or "connection" in error_msg.lower():
|
| 138 |
print(f"β GPT-4V: Network timeout or connection error detected")
|
| 139 |
-
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (network error). Switching to another model.
|
| 140 |
else:
|
| 141 |
print(f"β GPT-4V: Generic error, converting to MODEL_UNAVAILABLE")
|
| 142 |
-
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable ({error_type}: {error_msg}). Switching to another model.
|
|
|
|
| 102 |
print(f"β GPT-4V: Error type: {error_type}")
|
| 103 |
print(f"β GPT-4V: Error message: {error_msg}")
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
# Check for specific error types
|
| 106 |
if "rate_limit" in error_msg.lower() or "quota" in error_msg.lower():
|
| 107 |
print(f"β GPT-4V: Rate limit or quota exceeded detected")
|
| 108 |
+
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (rate limit/quota exceeded). Switching to another model.")
|
| 109 |
elif "authentication" in error_msg.lower() or "invalid" in error_msg.lower() or "api_key" in error_msg.lower():
|
| 110 |
print(f"β GPT-4V: Authentication or API key error detected")
|
| 111 |
+
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (authentication error). Switching to another model.")
|
| 112 |
elif "timeout" in error_msg.lower() or "connection" in error_msg.lower():
|
| 113 |
print(f"β GPT-4V: Network timeout or connection error detected")
|
| 114 |
+
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable (network error). Switching to another model.")
|
| 115 |
else:
|
| 116 |
print(f"β GPT-4V: Generic error, converting to MODEL_UNAVAILABLE")
|
| 117 |
+
raise Exception(f"MODEL_UNAVAILABLE: GPT-4O is currently unavailable ({error_type}: {error_msg}). Switching to another model.")
|
py_backend/app/services/huggingface_service.py
CHANGED
|
@@ -81,43 +81,8 @@ class HuggingFaceService(VLMService):
|
|
| 81 |
) as resp:
|
| 82 |
raw_text = await resp.text()
|
| 83 |
if resp.status != 200:
|
| 84 |
-
#
|
| 85 |
-
|
| 86 |
-
error_response = await resp.json()
|
| 87 |
-
print(f"β HuggingFace: API Error Response (HTTP {resp.status}): {error_response}")
|
| 88 |
-
|
| 89 |
-
# Extract specific error details
|
| 90 |
-
provider_error_details = {
|
| 91 |
-
"provider": "huggingface",
|
| 92 |
-
"status_code": resp.status,
|
| 93 |
-
"error_response": error_response,
|
| 94 |
-
"model_id": self.model_id,
|
| 95 |
-
"model_name": self.model_name
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
# Check for specific error types
|
| 99 |
-
if "error" in error_response:
|
| 100 |
-
error_info = error_response["error"]
|
| 101 |
-
provider_error_details.update({
|
| 102 |
-
"error_type": error_info.get("type"),
|
| 103 |
-
"error_message": error_info.get("message"),
|
| 104 |
-
"error_code": error_info.get("code")
|
| 105 |
-
})
|
| 106 |
-
|
| 107 |
-
print(f"β HuggingFace: Parsed Error Details: {provider_error_details}")
|
| 108 |
-
|
| 109 |
-
# Any non-200 status - throw generic error for fallback handling
|
| 110 |
-
raise Exception(f"MODEL_UNAVAILABLE: {self.model_name} is currently unavailable (HTTP {resp.status}). Switching to another model. Provider details: {provider_error_details}")
|
| 111 |
-
except Exception as parse_error:
|
| 112 |
-
print(f"β οΈ HuggingFace: Could not parse error response: {parse_error}")
|
| 113 |
-
provider_error_details = {
|
| 114 |
-
"provider": "huggingface",
|
| 115 |
-
"status_code": resp.status,
|
| 116 |
-
"raw_response": raw_text,
|
| 117 |
-
"model_id": self.model_id,
|
| 118 |
-
"model_name": self.model_name
|
| 119 |
-
}
|
| 120 |
-
raise Exception(f"MODEL_UNAVAILABLE: {self.model_name} is currently unavailable (HTTP {resp.status}). Switching to another model. Provider details: {provider_error_details}")
|
| 121 |
result = await resp.json()
|
| 122 |
except Exception as e:
|
| 123 |
if "MODEL_UNAVAILABLE" in str(e):
|
|
|
|
| 81 |
) as resp:
|
| 82 |
raw_text = await resp.text()
|
| 83 |
if resp.status != 200:
|
| 84 |
+
# Any non-200 status - throw generic error for fallback handling
|
| 85 |
+
raise Exception(f"MODEL_UNAVAILABLE: {self.model_name} is currently unavailable (HTTP {resp.status}). Switching to another model.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
result = await resp.json()
|
| 87 |
except Exception as e:
|
| 88 |
if "MODEL_UNAVAILABLE" in str(e):
|
py_backend/app/services/vlm_service.py
CHANGED
|
@@ -66,27 +66,20 @@ class VLMServiceManager:
|
|
| 66 |
"""Generate caption using available VLM services with fallback"""
|
| 67 |
print(f"π VLM Manager: Starting caption generation")
|
| 68 |
print(f"π VLM Manager: Requested model: {model_name}")
|
| 69 |
-
print(f"π VLM Manager: Available services: {[s.model_name for s in self.services.values()]}")
|
| 70 |
-
print(f"π VLM Manager: Total services: {len(self.services)}")
|
| 71 |
|
| 72 |
# Select initial service
|
| 73 |
service = None
|
| 74 |
if model_name:
|
| 75 |
-
print(f"π VLM Manager: Looking for requested model: {model_name}")
|
| 76 |
service = self.services.get(model_name)
|
| 77 |
-
if service:
|
| 78 |
-
print(f"π VLM Manager: Found requested model: {service.model_name}")
|
| 79 |
-
else:
|
| 80 |
print(f"β οΈ VLM Manager: Requested model {model_name} not found in services")
|
| 81 |
|
| 82 |
if not service:
|
| 83 |
-
print(f"π VLM Manager: No specific model requested, selecting from available services")
|
| 84 |
if db_session:
|
| 85 |
try:
|
| 86 |
from .. import crud
|
| 87 |
available_models = crud.get_models(db_session)
|
| 88 |
available_model_codes = [m.m_code for m in available_models if m.is_available]
|
| 89 |
-
print(f"π VLM Manager: Available models from DB: {available_model_codes}")
|
| 90 |
|
| 91 |
available_services = [s for s in self.services.values() if s.model_name in available_model_codes]
|
| 92 |
if available_services:
|
|
@@ -94,34 +87,22 @@ class VLMServiceManager:
|
|
| 94 |
shuffled_services = available_services.copy()
|
| 95 |
random.shuffle(shuffled_services)
|
| 96 |
service = shuffled_services[0]
|
| 97 |
-
print(f"π VLM Manager: Randomly selected service: {service.model_name} (from {len(available_services)} available)")
|
| 98 |
-
print(f"π VLM Manager: All available services were: {[s.model_name for s in available_services]}")
|
| 99 |
-
print(f"π VLM Manager: Shuffled order: {[s.model_name for s in shuffled_services]}")
|
| 100 |
else:
|
| 101 |
-
print(f"β οΈ VLM Manager: No available services from DB, using fallback")
|
| 102 |
service = next(iter(self.services.values()))
|
| 103 |
-
print(f"π VLM Manager: Using fallback service: {service.model_name}")
|
| 104 |
except Exception as e:
|
| 105 |
print(f"β VLM Manager: Error checking database availability: {e}, using fallback")
|
| 106 |
service = next(iter(self.services.values()))
|
| 107 |
-
print(f"π VLM Manager: Using fallback service: {service.model_name}")
|
| 108 |
else:
|
| 109 |
-
print(f"π VLM Manager: No database session, using service property")
|
| 110 |
available_services = [s for s in self.services.values() if s.is_available]
|
| 111 |
if available_services:
|
| 112 |
import random
|
| 113 |
service = random.choice(available_services)
|
| 114 |
-
print(f"π VLM Manager: Randomly selected service: {service.model_name}")
|
| 115 |
else:
|
| 116 |
-
print(f"β οΈ VLM Manager: No available services, using fallback")
|
| 117 |
service = next(iter(self.services.values()))
|
| 118 |
-
print(f"π VLM Manager: Using fallback service: {service.model_name}")
|
| 119 |
|
| 120 |
if not service:
|
| 121 |
raise ValueError("No VLM services available")
|
| 122 |
|
| 123 |
-
print(f"π VLM Manager: Initial service selected: {service.model_name}")
|
| 124 |
-
|
| 125 |
# Track attempts to avoid infinite loops
|
| 126 |
attempted_services = set()
|
| 127 |
max_attempts = len(self.services)
|
|
@@ -129,7 +110,6 @@ class VLMServiceManager:
|
|
| 129 |
while len(attempted_services) < max_attempts:
|
| 130 |
try:
|
| 131 |
print(f"π VLM Manager: Attempting with service: {service.model_name}")
|
| 132 |
-
print(f"π VLM Manager: Attempt #{len(attempted_services) + 1}/{max_attempts}")
|
| 133 |
|
| 134 |
result = await service.generate_caption(image_bytes, prompt, metadata_instructions)
|
| 135 |
if isinstance(result, dict):
|
|
@@ -150,45 +130,6 @@ class VLMServiceManager:
|
|
| 150 |
if "MODEL_UNAVAILABLE" in error_str:
|
| 151 |
attempted_services.add(service.model_name)
|
| 152 |
print(f"π VLM Manager: Model {service.model_name} is unavailable, trying another service...")
|
| 153 |
-
print(f"π VLM Manager: Attempted services so far: {attempted_services}")
|
| 154 |
-
|
| 155 |
-
# Extract provider error details if available
|
| 156 |
-
provider_error_details = {}
|
| 157 |
-
if "Provider details:" in error_str:
|
| 158 |
-
try:
|
| 159 |
-
# Extract the provider details section
|
| 160 |
-
details_start = error_str.find("Provider details:") + len("Provider details:")
|
| 161 |
-
details_str = error_str[details_start:].strip()
|
| 162 |
-
if details_str.startswith("{") and details_str.endswith("}"):
|
| 163 |
-
import json
|
| 164 |
-
provider_error_details = json.loads(details_str)
|
| 165 |
-
print(f"π VLM Manager: Provider Error Details: {provider_error_details}")
|
| 166 |
-
except Exception as parse_error:
|
| 167 |
-
print(f"β οΈ VLM Manager: Could not parse provider error details: {parse_error}")
|
| 168 |
-
|
| 169 |
-
# Log specific error information
|
| 170 |
-
if provider_error_details:
|
| 171 |
-
provider = provider_error_details.get("provider", "unknown")
|
| 172 |
-
status_code = provider_error_details.get("status_code")
|
| 173 |
-
error_type = provider_error_details.get("error_type")
|
| 174 |
-
error_message = provider_error_details.get("error_message")
|
| 175 |
-
|
| 176 |
-
print(f"π VLM Manager: {service.model_name} failed with {provider} error:")
|
| 177 |
-
print(f" Status Code: {status_code}")
|
| 178 |
-
print(f" Error Type: {error_type}")
|
| 179 |
-
print(f" Error Message: {error_message}")
|
| 180 |
-
|
| 181 |
-
# Log specific error patterns
|
| 182 |
-
if status_code == 400:
|
| 183 |
-
print(f"π VLM Manager: HTTP 400 detected - likely request format issue")
|
| 184 |
-
elif status_code == 401:
|
| 185 |
-
print(f"π VLM Manager: HTTP 401 detected - authentication issue")
|
| 186 |
-
elif status_code == 403:
|
| 187 |
-
print(f"π VLM Manager: HTTP 403 detected - access forbidden")
|
| 188 |
-
elif status_code == 429:
|
| 189 |
-
print(f"π VLM Manager: HTTP 429 detected - rate limit exceeded")
|
| 190 |
-
elif status_code == 500:
|
| 191 |
-
print(f"π VLM Manager: HTTP 500 detected - server error")
|
| 192 |
|
| 193 |
# Try to find another available service
|
| 194 |
if db_session:
|
|
@@ -196,7 +137,6 @@ class VLMServiceManager:
|
|
| 196 |
from .. import crud
|
| 197 |
available_models = crud.get_models(db_session)
|
| 198 |
available_model_codes = [m.m_code for m in available_models if m.is_available]
|
| 199 |
-
print(f"π VLM Manager: Available models from DB: {available_model_codes}")
|
| 200 |
|
| 201 |
# Find next available service that hasn't been attempted
|
| 202 |
for next_service in self.services.values():
|
|
@@ -206,12 +146,10 @@ class VLMServiceManager:
|
|
| 206 |
print(f"π VLM Manager: Switching to fallback service: {service.model_name}")
|
| 207 |
break
|
| 208 |
else:
|
| 209 |
-
print(f"β οΈ VLM Manager: No more available services from DB, using any untried service")
|
| 210 |
# No more available services, use any untried service
|
| 211 |
for next_service in self.services.values():
|
| 212 |
if next_service.model_name not in attempted_services:
|
| 213 |
service = next_service
|
| 214 |
-
print(f"π VLM Manager: Using untried service as fallback: {service.model_name}")
|
| 215 |
break
|
| 216 |
except Exception as db_error:
|
| 217 |
print(f"β VLM Manager: Error checking database availability: {db_error}")
|
|
@@ -219,15 +157,12 @@ class VLMServiceManager:
|
|
| 219 |
for next_service in self.services.values():
|
| 220 |
if next_service.model_name not in attempted_services:
|
| 221 |
service = next_service
|
| 222 |
-
print(f"π VLM Manager: Using untried service as fallback: {service.model_name}")
|
| 223 |
break
|
| 224 |
else:
|
| 225 |
-
print(f"π VLM Manager: No database session, using any untried service")
|
| 226 |
# No database session, use any untried service
|
| 227 |
for next_service in self.services.values():
|
| 228 |
if next_service.model_name not in attempted_services:
|
| 229 |
service = next_service
|
| 230 |
-
print(f"π VLM Manager: Using untried service as fallback: {service.model_name}")
|
| 231 |
break
|
| 232 |
|
| 233 |
if not service:
|
|
@@ -242,7 +177,6 @@ class VLMServiceManager:
|
|
| 242 |
|
| 243 |
# If we get here, we've tried all services
|
| 244 |
print(f"β VLM Manager: All VLM services failed due to model unavailability")
|
| 245 |
-
print(f"β VLM Manager: Attempted services: {attempted_services}")
|
| 246 |
raise ValueError("All VLM services failed due to model unavailability")
|
| 247 |
|
| 248 |
vlm_manager = VLMServiceManager()
|
|
|
|
| 66 |
"""Generate caption using available VLM services with fallback"""
|
| 67 |
print(f"π VLM Manager: Starting caption generation")
|
| 68 |
print(f"π VLM Manager: Requested model: {model_name}")
|
|
|
|
|
|
|
| 69 |
|
| 70 |
# Select initial service
|
| 71 |
service = None
|
| 72 |
if model_name:
|
|
|
|
| 73 |
service = self.services.get(model_name)
|
| 74 |
+
if not service:
|
|
|
|
|
|
|
| 75 |
print(f"β οΈ VLM Manager: Requested model {model_name} not found in services")
|
| 76 |
|
| 77 |
if not service:
|
|
|
|
| 78 |
if db_session:
|
| 79 |
try:
|
| 80 |
from .. import crud
|
| 81 |
available_models = crud.get_models(db_session)
|
| 82 |
available_model_codes = [m.m_code for m in available_models if m.is_available]
|
|
|
|
| 83 |
|
| 84 |
available_services = [s for s in self.services.values() if s.model_name in available_model_codes]
|
| 85 |
if available_services:
|
|
|
|
| 87 |
shuffled_services = available_services.copy()
|
| 88 |
random.shuffle(shuffled_services)
|
| 89 |
service = shuffled_services[0]
|
|
|
|
|
|
|
|
|
|
| 90 |
else:
|
|
|
|
| 91 |
service = next(iter(self.services.values()))
|
|
|
|
| 92 |
except Exception as e:
|
| 93 |
print(f"β VLM Manager: Error checking database availability: {e}, using fallback")
|
| 94 |
service = next(iter(self.services.values()))
|
|
|
|
| 95 |
else:
|
|
|
|
| 96 |
available_services = [s for s in self.services.values() if s.is_available]
|
| 97 |
if available_services:
|
| 98 |
import random
|
| 99 |
service = random.choice(available_services)
|
|
|
|
| 100 |
else:
|
|
|
|
| 101 |
service = next(iter(self.services.values()))
|
|
|
|
| 102 |
|
| 103 |
if not service:
|
| 104 |
raise ValueError("No VLM services available")
|
| 105 |
|
|
|
|
|
|
|
| 106 |
# Track attempts to avoid infinite loops
|
| 107 |
attempted_services = set()
|
| 108 |
max_attempts = len(self.services)
|
|
|
|
| 110 |
while len(attempted_services) < max_attempts:
|
| 111 |
try:
|
| 112 |
print(f"π VLM Manager: Attempting with service: {service.model_name}")
|
|
|
|
| 113 |
|
| 114 |
result = await service.generate_caption(image_bytes, prompt, metadata_instructions)
|
| 115 |
if isinstance(result, dict):
|
|
|
|
| 130 |
if "MODEL_UNAVAILABLE" in error_str:
|
| 131 |
attempted_services.add(service.model_name)
|
| 132 |
print(f"π VLM Manager: Model {service.model_name} is unavailable, trying another service...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
# Try to find another available service
|
| 135 |
if db_session:
|
|
|
|
| 137 |
from .. import crud
|
| 138 |
available_models = crud.get_models(db_session)
|
| 139 |
available_model_codes = [m.m_code for m in available_models if m.is_available]
|
|
|
|
| 140 |
|
| 141 |
# Find next available service that hasn't been attempted
|
| 142 |
for next_service in self.services.values():
|
|
|
|
| 146 |
print(f"π VLM Manager: Switching to fallback service: {service.model_name}")
|
| 147 |
break
|
| 148 |
else:
|
|
|
|
| 149 |
# No more available services, use any untried service
|
| 150 |
for next_service in self.services.values():
|
| 151 |
if next_service.model_name not in attempted_services:
|
| 152 |
service = next_service
|
|
|
|
| 153 |
break
|
| 154 |
except Exception as db_error:
|
| 155 |
print(f"β VLM Manager: Error checking database availability: {db_error}")
|
|
|
|
| 157 |
for next_service in self.services.values():
|
| 158 |
if next_service.model_name not in attempted_services:
|
| 159 |
service = next_service
|
|
|
|
| 160 |
break
|
| 161 |
else:
|
|
|
|
| 162 |
# No database session, use any untried service
|
| 163 |
for next_service in self.services.values():
|
| 164 |
if next_service.model_name not in attempted_services:
|
| 165 |
service = next_service
|
|
|
|
| 166 |
break
|
| 167 |
|
| 168 |
if not service:
|
|
|
|
| 177 |
|
| 178 |
# If we get here, we've tried all services
|
| 179 |
print(f"β VLM Manager: All VLM services failed due to model unavailability")
|
|
|
|
| 180 |
raise ValueError("All VLM services failed due to model unavailability")
|
| 181 |
|
| 182 |
vlm_manager = VLMServiceManager()
|