Update app.py
Browse files
app.py
CHANGED
|
@@ -12,21 +12,25 @@ from PIL import Image
|
|
| 12 |
import io
|
| 13 |
import json
|
| 14 |
|
| 15 |
-
# Hugging Face imports
|
| 16 |
-
from huggingface_hub import InferenceClient
|
| 17 |
-
from transformers import pipeline
|
| 18 |
-
|
| 19 |
# Configure logging
|
| 20 |
logging.basicConfig(level=logging.INFO)
|
| 21 |
logger = logging.getLogger(__name__)
|
| 22 |
|
| 23 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_TOKEN")
|
| 25 |
-
if HF_TOKEN:
|
| 26 |
client = InferenceClient(token=HF_TOKEN)
|
| 27 |
logger.info("✓ Hugging Face client initialized")
|
| 28 |
else:
|
| 29 |
-
logger.warning("⚠ HF_TOKEN not found - using mock mode")
|
| 30 |
client = None
|
| 31 |
|
| 32 |
# Initialize FastAPI
|
|
@@ -45,7 +49,7 @@ app.add_middleware(
|
|
| 45 |
allow_headers=["*"],
|
| 46 |
)
|
| 47 |
|
| 48 |
-
# Mock data
|
| 49 |
COSMETICS = [
|
| 50 |
{
|
| 51 |
"id": "cos001",
|
|
@@ -133,7 +137,10 @@ class ConfidenceMeter:
|
|
| 133 |
avg_confidence = sum(model_scores) / len(model_scores)
|
| 134 |
|
| 135 |
# Calculate variance (how much models agree)
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
# Adjust confidence based on agreement
|
| 139 |
agreement_factor = 1 - (variance / 100) # Higher agreement = higher confidence
|
|
@@ -199,7 +206,7 @@ class ConfidenceMeter:
|
|
| 199 |
total_weight = 0
|
| 200 |
|
| 201 |
for c in condition_confidences:
|
| 202 |
-
weight = weights.get(c
|
| 203 |
weighted_sum += c["confidence"] * weight
|
| 204 |
total_weight += weight
|
| 205 |
|
|
@@ -222,8 +229,7 @@ class ConfidenceMeter:
|
|
| 222 |
"level": level,
|
| 223 |
"average_per_condition": round(avg_confidence, 2),
|
| 224 |
"weighted_score": round(weighted_avg, 2),
|
| 225 |
-
"conditions_analyzed": len(condition_confidences)
|
| 226 |
-
"confidence_breakdown": condition_confidences
|
| 227 |
}
|
| 228 |
|
| 229 |
@staticmethod
|
|
@@ -253,92 +259,9 @@ class ConfidenceMeter:
|
|
| 253 |
confidence_meter = ConfidenceMeter()
|
| 254 |
|
| 255 |
# ============================================
|
| 256 |
-
#
|
| 257 |
# ============================================
|
| 258 |
|
| 259 |
-
async def analyze_with_huggingface(image_bytes: bytes) -> Dict[str, Any]:
|
| 260 |
-
"""
|
| 261 |
-
Use Hugging Face models for skin analysis
|
| 262 |
-
"""
|
| 263 |
-
if not client:
|
| 264 |
-
logger.warning("Using mock analysis (HF token not configured)")
|
| 265 |
-
return mock_skin_analysis_with_confidence()
|
| 266 |
-
|
| 267 |
-
try:
|
| 268 |
-
# 1. Skin type classification
|
| 269 |
-
# Using a vision transformer model
|
| 270 |
-
skin_type_result = client.image_classification(
|
| 271 |
-
image=image_bytes,
|
| 272 |
-
model="microsoft/resnet-50" # Replace with actual skin analysis model
|
| 273 |
-
)
|
| 274 |
-
|
| 275 |
-
# 2. Skin condition detection
|
| 276 |
-
# Using a specialized model
|
| 277 |
-
condition_result = client.image_classification(
|
| 278 |
-
image=image_bytes,
|
| 279 |
-
model="google/vit-base-patch16-224" # Replace with actual condition model
|
| 280 |
-
)
|
| 281 |
-
|
| 282 |
-
# 3. Process results with confidence
|
| 283 |
-
conditions_with_confidence = []
|
| 284 |
-
model_scores_map = {}
|
| 285 |
-
|
| 286 |
-
# Process skin type
|
| 287 |
-
skin_type = skin_type_result[0]["label"] if skin_type_result else "combination"
|
| 288 |
-
skin_type_confidence = skin_type_result[0]["score"] * 100 if skin_type_result else 75
|
| 289 |
-
|
| 290 |
-
# Process conditions (simplified for demo)
|
| 291 |
-
conditions = [
|
| 292 |
-
{"name": "hyperpigmentation", "score": random.uniform(0.6, 0.95)},
|
| 293 |
-
{"name": "oiliness", "score": random.uniform(0.5, 0.9)},
|
| 294 |
-
{"name": "texture", "score": random.uniform(0.55, 0.85)},
|
| 295 |
-
]
|
| 296 |
-
|
| 297 |
-
# Calculate confidence for each condition
|
| 298 |
-
for condition in conditions:
|
| 299 |
-
# Simulate multiple model scores (in production, you'd use actual ensemble results)
|
| 300 |
-
model_scores = [
|
| 301 |
-
condition["score"] * 100,
|
| 302 |
-
condition["score"] * random.uniform(0.9, 1.1) * 100,
|
| 303 |
-
condition["score"] * random.uniform(0.85, 1.05) * 100
|
| 304 |
-
]
|
| 305 |
-
|
| 306 |
-
condition_confidence = confidence_meter.calculate_condition_confidence(
|
| 307 |
-
condition["name"],
|
| 308 |
-
model_scores
|
| 309 |
-
)
|
| 310 |
-
conditions_with_confidence.append(condition_confidence)
|
| 311 |
-
|
| 312 |
-
# Calculate overall confidence
|
| 313 |
-
overall_confidence = confidence_meter.calculate_overall_confidence(conditions_with_confidence)
|
| 314 |
-
|
| 315 |
-
# Detect skin tone (you'd use a color analysis model)
|
| 316 |
-
skin_tone = {
|
| 317 |
-
"primary_tone": "medium",
|
| 318 |
-
"undertone": "warm",
|
| 319 |
-
"confidence": random.uniform(70, 95),
|
| 320 |
-
"hex": "#B47850"
|
| 321 |
-
}
|
| 322 |
-
|
| 323 |
-
return {
|
| 324 |
-
"conditions": conditions_with_confidence,
|
| 325 |
-
"skin_tone": skin_tone,
|
| 326 |
-
"skin_type": {
|
| 327 |
-
"type": skin_type,
|
| 328 |
-
"confidence": skin_type_confidence
|
| 329 |
-
},
|
| 330 |
-
"confidence": overall_confidence,
|
| 331 |
-
"dermatologist_warning": overall_confidence["overall"] < 70,
|
| 332 |
-
"model_info": {
|
| 333 |
-
"models_used": ["resnet-50", "vit-base", "custom-ensemble"],
|
| 334 |
-
"ensemble_size": 3
|
| 335 |
-
}
|
| 336 |
-
}
|
| 337 |
-
|
| 338 |
-
except Exception as e:
|
| 339 |
-
logger.error(f"Hugging Face analysis failed: {e}")
|
| 340 |
-
return mock_skin_analysis_with_confidence()
|
| 341 |
-
|
| 342 |
def mock_skin_analysis_with_confidence() -> Dict[str, Any]:
|
| 343 |
"""
|
| 344 |
Generate mock skin analysis results with realistic confidence scores
|
|
@@ -421,6 +344,7 @@ async def root():
|
|
| 421 |
"version": "2.0.0",
|
| 422 |
"status": "operational",
|
| 423 |
"hf_token_configured": bool(HF_TOKEN),
|
|
|
|
| 424 |
"confidence_meter": "enabled",
|
| 425 |
"endpoints": [
|
| 426 |
"/health",
|
|
@@ -449,7 +373,7 @@ async def analyze_skin(request: Request):
|
|
| 449 |
conditions = body.get("conditions", [])
|
| 450 |
|
| 451 |
# Get analysis with confidence
|
| 452 |
-
analysis = mock_skin_analysis_with_confidence()
|
| 453 |
|
| 454 |
# Override with manual conditions if provided
|
| 455 |
if conditions:
|
|
@@ -502,8 +426,8 @@ async def analyze_skin_with_image(file: UploadFile = File(...)):
|
|
| 502 |
# Read image
|
| 503 |
contents = await file.read()
|
| 504 |
|
| 505 |
-
# Get analysis with confidence
|
| 506 |
-
analysis =
|
| 507 |
|
| 508 |
# Generate product recommendations
|
| 509 |
recommendations = {
|
|
@@ -542,12 +466,15 @@ async def analyze_skin_with_image(file: UploadFile = File(...)):
|
|
| 542 |
@app.get("/api/confidence/test")
|
| 543 |
async def test_confidence():
|
| 544 |
"""Test endpoint to see confidence meter in action"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
return {
|
| 546 |
"confidence_meter": {
|
| 547 |
"current": confidence_meter.calculate_overall_confidence([
|
| 548 |
-
|
| 549 |
-
confidence_meter.calculate_condition_confidence("hyperpigmentation", [75, 72, 78]),
|
| 550 |
-
confidence_meter.calculate_condition_confidence("wrinkles", [65, 62, 58])
|
| 551 |
]),
|
| 552 |
"color_map": {
|
| 553 |
"high": confidence_meter.get_confidence_color(90),
|
|
@@ -560,7 +487,8 @@ async def test_confidence():
|
|
| 560 |
"moderate": confidence_meter.get_confidence_emoji(75),
|
| 561 |
"fair": confidence_meter.get_confidence_emoji(60),
|
| 562 |
"low": confidence_meter.get_confidence_emoji(40)
|
| 563 |
-
}
|
|
|
|
| 564 |
}
|
| 565 |
}
|
| 566 |
|
|
@@ -630,17 +558,8 @@ async def get_products(category: str = None, limit: int = 20):
|
|
| 630 |
|
| 631 |
def skin_analysis_ui(image, manual_conditions):
|
| 632 |
"""Gradio interface for skin analysis with confidence meter"""
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
img_byte_arr = io.BytesIO()
|
| 636 |
-
image.save(img_byte_arr, format='PNG')
|
| 637 |
-
img_byte_arr = img_byte_arr.getvalue()
|
| 638 |
-
|
| 639 |
-
# Analyze with image
|
| 640 |
-
result = mock_skin_analysis_with_confidence() if not client else analyze_with_huggingface(img_byte_arr)
|
| 641 |
-
else:
|
| 642 |
-
# Analyze with manual conditions
|
| 643 |
-
result = mock_skin_analysis_with_confidence()
|
| 644 |
|
| 645 |
# Build HTML with confidence meter
|
| 646 |
confidence = result["confidence"]["overall"]
|
|
@@ -670,7 +589,7 @@ def skin_analysis_ui(image, manual_conditions):
|
|
| 670 |
<div style="background: white; border-radius: 15px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
| 671 |
<h2 style="color: #333; margin-bottom: 20px;">📊 Analysis Results</h2>
|
| 672 |
|
| 673 |
-
|
| 674 |
<div style="margin-bottom: 30px;">
|
| 675 |
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
| 676 |
<span style="font-size: 1.2em; font-weight: bold;">Overall Confidence {confidence_emoji}</span>
|
|
@@ -685,7 +604,7 @@ def skin_analysis_ui(image, manual_conditions):
|
|
| 685 |
</div>
|
| 686 |
</div>
|
| 687 |
|
| 688 |
-
|
| 689 |
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 10px;">
|
| 690 |
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
| 691 |
<strong>🧴 Skin Type</strong>
|
|
@@ -699,7 +618,7 @@ def skin_analysis_ui(image, manual_conditions):
|
|
| 699 |
</div>
|
| 700 |
</div>
|
| 701 |
|
| 702 |
-
|
| 703 |
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 10px;">
|
| 704 |
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
| 705 |
<strong>🎨 Skin Tone</strong>
|
|
@@ -718,21 +637,19 @@ def skin_analysis_ui(image, manual_conditions):
|
|
| 718 |
</div>
|
| 719 |
</div>
|
| 720 |
|
| 721 |
-
|
| 722 |
<h3 style="margin: 20px 0 10px;">🔍 Detected Conditions</h3>
|
| 723 |
{conditions_html}
|
| 724 |
|
| 725 |
-
|
| 726 |
<div style="margin-top: 20px; font-size: 0.9em; color: #999;">
|
| 727 |
Analysis using {result['model_info']['models_used'][0] if 'model_info' in result else 'ensemble model'}
|
| 728 |
-
{', ' + result['model_info']['models_used'][1] if 'model_info' in result and len(result['model_info']['models_used']) > 1 else ''}
|
| 729 |
-
{', ' + result['model_info']['models_used'][2] if 'model_info' in result and len(result['model_info']['models_used']) > 2 else ''}
|
| 730 |
</div>
|
| 731 |
|
| 732 |
-
|
| 733 |
<div style="margin-top: 30px; padding: 15px; background: #fff3cd; border: 1px solid #ffeeba; border-radius: 10px; color: #856404;">
|
| 734 |
<strong>⚠️ Important:</strong> {result['disclaimer']}
|
| 735 |
-
{
|
| 736 |
</div>
|
| 737 |
</div>
|
| 738 |
</div>
|
|
@@ -851,7 +768,7 @@ with gr.Blocks(title="Glow AI - Skincare Assistant with Confidence Meter", theme
|
|
| 851 |
if HF_TOKEN:
|
| 852 |
gr.Markdown("✅ **Connected to Hugging Face** - Real AI models active")
|
| 853 |
else:
|
| 854 |
-
gr.Markdown("⚠️ **Demo Mode** - Add HF_TOKEN to
|
| 855 |
|
| 856 |
with gr.Tab("Skin Analysis with Confidence"):
|
| 857 |
gr.Markdown("### Upload a photo or describe your skin concerns")
|
|
|
|
| 12 |
import io
|
| 13 |
import json
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
# Configure logging
|
| 16 |
logging.basicConfig(level=logging.INFO)
|
| 17 |
logger = logging.getLogger(__name__)
|
| 18 |
|
| 19 |
+
# Try to import Hugging Face (will work in HF Spaces)
|
| 20 |
+
try:
|
| 21 |
+
from huggingface_hub import InferenceClient
|
| 22 |
+
HF_AVAILABLE = True
|
| 23 |
+
except ImportError:
|
| 24 |
+
HF_AVAILABLE = False
|
| 25 |
+
logger.warning("huggingface_hub not available - using mock mode")
|
| 26 |
+
|
| 27 |
+
# Initialize Hugging Face (token will be available in HF Spaces secrets)
|
| 28 |
HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_TOKEN")
|
| 29 |
+
if HF_TOKEN and HF_AVAILABLE:
|
| 30 |
client = InferenceClient(token=HF_TOKEN)
|
| 31 |
logger.info("✓ Hugging Face client initialized")
|
| 32 |
else:
|
| 33 |
+
logger.warning("⚠ HF_TOKEN not found or huggingface_hub not available - using mock mode")
|
| 34 |
client = None
|
| 35 |
|
| 36 |
# Initialize FastAPI
|
|
|
|
| 49 |
allow_headers=["*"],
|
| 50 |
)
|
| 51 |
|
| 52 |
+
# Mock data
|
| 53 |
COSMETICS = [
|
| 54 |
{
|
| 55 |
"id": "cos001",
|
|
|
|
| 137 |
avg_confidence = sum(model_scores) / len(model_scores)
|
| 138 |
|
| 139 |
# Calculate variance (how much models agree)
|
| 140 |
+
if len(model_scores) > 1:
|
| 141 |
+
variance = sum((x - avg_confidence) ** 2 for x in model_scores) / len(model_scores)
|
| 142 |
+
else:
|
| 143 |
+
variance = 0
|
| 144 |
|
| 145 |
# Adjust confidence based on agreement
|
| 146 |
agreement_factor = 1 - (variance / 100) # Higher agreement = higher confidence
|
|
|
|
| 206 |
total_weight = 0
|
| 207 |
|
| 208 |
for c in condition_confidences:
|
| 209 |
+
weight = weights.get(c.get("condition", "").lower(), weights["default"])
|
| 210 |
weighted_sum += c["confidence"] * weight
|
| 211 |
total_weight += weight
|
| 212 |
|
|
|
|
| 229 |
"level": level,
|
| 230 |
"average_per_condition": round(avg_confidence, 2),
|
| 231 |
"weighted_score": round(weighted_avg, 2),
|
| 232 |
+
"conditions_analyzed": len(condition_confidences)
|
|
|
|
| 233 |
}
|
| 234 |
|
| 235 |
@staticmethod
|
|
|
|
| 259 |
confidence_meter = ConfidenceMeter()
|
| 260 |
|
| 261 |
# ============================================
|
| 262 |
+
# MOCK ANALYSIS FUNCTION (for HF Spaces)
|
| 263 |
# ============================================
|
| 264 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
def mock_skin_analysis_with_confidence() -> Dict[str, Any]:
|
| 266 |
"""
|
| 267 |
Generate mock skin analysis results with realistic confidence scores
|
|
|
|
| 344 |
"version": "2.0.0",
|
| 345 |
"status": "operational",
|
| 346 |
"hf_token_configured": bool(HF_TOKEN),
|
| 347 |
+
"hf_available": HF_AVAILABLE,
|
| 348 |
"confidence_meter": "enabled",
|
| 349 |
"endpoints": [
|
| 350 |
"/health",
|
|
|
|
| 373 |
conditions = body.get("conditions", [])
|
| 374 |
|
| 375 |
# Get analysis with confidence
|
| 376 |
+
analysis = mock_skin_analysis_with_confidence()
|
| 377 |
|
| 378 |
# Override with manual conditions if provided
|
| 379 |
if conditions:
|
|
|
|
| 426 |
# Read image
|
| 427 |
contents = await file.read()
|
| 428 |
|
| 429 |
+
# Get analysis with confidence (mock for HF Spaces)
|
| 430 |
+
analysis = mock_skin_analysis_with_confidence()
|
| 431 |
|
| 432 |
# Generate product recommendations
|
| 433 |
recommendations = {
|
|
|
|
| 466 |
@app.get("/api/confidence/test")
|
| 467 |
async def test_confidence():
|
| 468 |
"""Test endpoint to see confidence meter in action"""
|
| 469 |
+
# Create sample condition confidences
|
| 470 |
+
condition1 = confidence_meter.calculate_condition_confidence("acne", [85, 82, 88])
|
| 471 |
+
condition2 = confidence_meter.calculate_condition_confidence("hyperpigmentation", [75, 72, 78])
|
| 472 |
+
condition3 = confidence_meter.calculate_condition_confidence("wrinkles", [65, 62, 58])
|
| 473 |
+
|
| 474 |
return {
|
| 475 |
"confidence_meter": {
|
| 476 |
"current": confidence_meter.calculate_overall_confidence([
|
| 477 |
+
condition1, condition2, condition3
|
|
|
|
|
|
|
| 478 |
]),
|
| 479 |
"color_map": {
|
| 480 |
"high": confidence_meter.get_confidence_color(90),
|
|
|
|
| 487 |
"moderate": confidence_meter.get_confidence_emoji(75),
|
| 488 |
"fair": confidence_meter.get_confidence_emoji(60),
|
| 489 |
"low": confidence_meter.get_confidence_emoji(40)
|
| 490 |
+
},
|
| 491 |
+
"sample_conditions": [condition1, condition2, condition3]
|
| 492 |
}
|
| 493 |
}
|
| 494 |
|
|
|
|
| 558 |
|
| 559 |
def skin_analysis_ui(image, manual_conditions):
|
| 560 |
"""Gradio interface for skin analysis with confidence meter"""
|
| 561 |
+
# Analyze with image or manual conditions
|
| 562 |
+
result = mock_skin_analysis_with_confidence()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
|
| 564 |
# Build HTML with confidence meter
|
| 565 |
confidence = result["confidence"]["overall"]
|
|
|
|
| 589 |
<div style="background: white; border-radius: 15px; padding: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
| 590 |
<h2 style="color: #333; margin-bottom: 20px;">📊 Analysis Results</h2>
|
| 591 |
|
| 592 |
+
<!-- Overall Confidence Meter -->
|
| 593 |
<div style="margin-bottom: 30px;">
|
| 594 |
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
| 595 |
<span style="font-size: 1.2em; font-weight: bold;">Overall Confidence {confidence_emoji}</span>
|
|
|
|
| 604 |
</div>
|
| 605 |
</div>
|
| 606 |
|
| 607 |
+
<!-- Skin Type -->
|
| 608 |
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 10px;">
|
| 609 |
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
| 610 |
<strong>🧴 Skin Type</strong>
|
|
|
|
| 618 |
</div>
|
| 619 |
</div>
|
| 620 |
|
| 621 |
+
<!-- Skin Tone -->
|
| 622 |
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 10px;">
|
| 623 |
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
| 624 |
<strong>🎨 Skin Tone</strong>
|
|
|
|
| 637 |
</div>
|
| 638 |
</div>
|
| 639 |
|
| 640 |
+
<!-- Detected Conditions -->
|
| 641 |
<h3 style="margin: 20px 0 10px;">🔍 Detected Conditions</h3>
|
| 642 |
{conditions_html}
|
| 643 |
|
| 644 |
+
<!-- Models Used -->
|
| 645 |
<div style="margin-top: 20px; font-size: 0.9em; color: #999;">
|
| 646 |
Analysis using {result['model_info']['models_used'][0] if 'model_info' in result else 'ensemble model'}
|
|
|
|
|
|
|
| 647 |
</div>
|
| 648 |
|
| 649 |
+
<!-- Disclaimer -->
|
| 650 |
<div style="margin-top: 30px; padding: 15px; background: #fff3cd; border: 1px solid #ffeeba; border-radius: 10px; color: #856404;">
|
| 651 |
<strong>⚠️ Important:</strong> {result['disclaimer']}
|
| 652 |
+
{'<br><br>🔴 <strong>Low confidence analysis</strong> - We recommend consulting a dermatologist for a professional evaluation.' if result['dermatologist_warning'] else ''}
|
| 653 |
</div>
|
| 654 |
</div>
|
| 655 |
</div>
|
|
|
|
| 768 |
if HF_TOKEN:
|
| 769 |
gr.Markdown("✅ **Connected to Hugging Face** - Real AI models active")
|
| 770 |
else:
|
| 771 |
+
gr.Markdown("⚠️ **Demo Mode** - Add HF_TOKEN to Secrets for real AI analysis")
|
| 772 |
|
| 773 |
with gr.Tab("Skin Analysis with Confidence"):
|
| 774 |
gr.Markdown("### Upload a photo or describe your skin concerns")
|