leninqwerty03@gmail.com commited on
Commit
d435d93
·
1 Parent(s): 1d89a24

refactor: change assess_product_agentic to synchronous function and optimize execution flow

Browse files
Files changed (1) hide show
  1. app.py +314 -246
app.py CHANGED
@@ -312,8 +312,8 @@ def format_score(score) -> str:
312
 
313
 
314
  # ===== GRADIO 6 FEATURE: Progress Tracking =====
315
- async def assess_product_agentic(image: str, progress=gr.Progress()) -> str:
316
- """Main assessment with Gradio 6 Progress tracking - OPTIMIZED FOR SPEED"""
317
 
318
  try:
319
  progress(0, desc="Initializing agent...")
@@ -326,10 +326,7 @@ async def assess_product_agentic(image: str, progress=gr.Progress()) -> str:
326
 
327
  if image is not None:
328
  progress(0.1, desc="Analyzing image...")
329
- # Run image analysis in thread pool to avoid blocking
330
- loop = asyncio.get_running_loop()
331
- query, vision_data = await loop.run_in_executor(None, analyze_image_with_mcp, image)
332
-
333
  # Don't stop if image analysis fails, just log it
334
  if query and not query.startswith("Error"):
335
  source = f"**Detected:** {query}\n\n"
@@ -342,123 +339,78 @@ async def assess_product_agentic(image: str, progress=gr.Progress()) -> str:
342
  # Image analysis failed, clear query so text input can be used
343
  query = ""
344
 
 
345
 
346
  # Only return error if BOTH image and text are missing/invalid
347
  if not query:
348
  return "**Take a photo** or type a product name to get started!"
349
 
350
- # PHASE 1: PLANNING (Simplified for speed)
351
  progress(0.3, desc="Agent planning assessment...")
352
- # Skip slow LLM planning call, use static plan for speed
353
- agent.plan = ["Identify product", "Analyze lifecycle", "Retrieve data", "Generate report"]
354
-
355
- # PHASE 2 & 3: PARALLEL EXECUTION
356
- progress(0.5, desc="Running parallel analysis...")
357
-
358
- loop = asyncio.get_running_loop()
359
-
360
- async def run_reasoning():
361
- return await loop.run_in_executor(None, agent.reason_about_product, query)
362
-
363
- async def run_rag():
364
- # RAG doesn't strictly need reasoning, so we can run it in parallel
365
- return await loop.run_in_executor(None, retrieve_environmental_knowledge, query, "")
366
-
367
- async def run_lifecycle():
368
- product_data = {"name": query, "query": query}
369
- if vision_data and vision_data.get("status") == "success":
370
- analysis = vision_data.get("analysis", {})
371
- materials = analysis.get("materials", [])
372
- if materials: product_data["materials"] = materials
373
- packaging = analysis.get("packaging_materials", [])
374
- if packaging: product_data["packaging"] = packaging
375
- category = analysis.get("product_category", "")
376
- if category: product_data["category"] = category
377
-
378
- return await loop.run_in_executor(None, mcp_lifecycle_assessment, query, product_data)
379
-
380
- # Execute all three heavy tasks concurrently
381
- reasoning, rag_context, lifecycle_result = await asyncio.gather(
382
- run_reasoning(),
383
- run_rag(),
384
- run_lifecycle()
385
- )
386
 
387
- agent.execution_log.append("Parallel execution completed")
 
388
 
389
- # PHASE 4: SYNTHESIS
390
- progress(0.8, desc="Synthesizing results...")
391
-
392
- # Context Engineering
393
- engineered_prompt = f"""You are an environmental assessment expert. Assess this product: {query}
394
-
395
- **AGENT REASONING:**
396
- {reasoning}
397
-
398
- **RETRIEVED KNOWLEDGE (RAG):**
399
- {rag_context}
400
-
401
- **MCP LIFECYCLE ANALYSIS:**
402
- {json.dumps(lifecycle_result, indent=2) if lifecycle_result.get("status") == "success" else "Pending detailed analysis"}
403
-
404
- **VISION ANALYSIS:**
405
- {json.dumps(vision_data.get("analysis", {}), indent=2) if vision_data else "No image data"}
406
-
407
- Using ALL the above context, provide a comprehensive assessment in this EXACT JSON format:
408
- {{
409
- "name": "{query}",
410
- "eco_score": 5.5,
411
- "carbon": "XX kg CO2e or XX g CO2e",
412
- "issues": "Main environmental concerns separated by commas",
413
- "alternative": "Best eco-friendly alternative with score (X.X/10) - Key benefit",
414
- "lifecycle_insights": "Key insights from lifecycle analysis",
415
- "materials_analysis": "Summary of materials and their impact"
416
- }}
417
-
418
- IMPORTANT:
419
- - name MUST be: {query}
420
- - eco_score: realistic number 1-10 for THIS SPECIFIC product
421
- - alternative: Recommend ONLY NEW sustainable products (NOT second-hand, vintage, used, refurbished, or pre-owned items). Focus on eco-friendly materials, sustainable manufacturing, certifications, or innovative green technologies
422
- - Be specific and accurate based on the context provided
423
- - Return ONLY valid JSON, no extra text.
424
- """
425
-
426
- # Final LLM Call
427
- message = await loop.run_in_executor(None, lambda: client.messages.create(
428
- model="claude-sonnet-4-20250514",
429
- max_tokens=2000,
430
- messages=[{"role": "user", "content": engineered_prompt}]
431
- ))
432
-
433
- response_text = message.content[0].text.strip()
434
-
435
- if "```json" in response_text:
436
- response_text = response_text.split("```json")[1].split("```")[0].strip()
437
- elif "```" in response_text:
438
- response_text = response_text.split("```")[1].split("```")[0].strip()
439
-
440
- product = json.loads(response_text)
441
- product["name"] = query.title()
442
-
443
- if isinstance(product.get("eco_score"), str):
444
- product["eco_score"] = float(product["eco_score"])
445
 
446
  progress(0.9, desc="Formatting results...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
 
448
- # Format main output
449
- main_output = f"{source}# {product['name']}\n\n"
450
- main_output += f"## {format_score(product.get('eco_score', 5.0))}\n\n"
451
- main_output += f"**Carbon Footprint**\n{product.get('carbon', 'N/A')}\n\n"
452
- main_output += f"**Environmental Issues**\n{product.get('issues', 'N/A')}\n\n"
453
 
454
- if 'materials_analysis' in product:
455
- main_output += f"**Materials Analysis**\n{product['materials_analysis']}\n\n"
456
 
457
- if 'lifecycle_insights' in product:
458
- main_output += f"**Lifecycle Insights**\n{product['lifecycle_insights']}\n\n"
459
 
460
- main_output += f"---\n\n"
461
- main_output += f"## Better Alternative\n\n{product.get('alternative', 'N/A')}\n\n"
 
 
 
 
 
 
 
462
 
463
  progress(1.0, desc="Complete!")
464
  return main_output
@@ -467,221 +419,337 @@ IMPORTANT:
467
  import traceback
468
  return f"**Error:**\n\n{str(e)}\n\n```\n{traceback.format_exc()}\n```"
469
 
470
- # ===== ECO-FRIENDLY GREEN THEME - PROFESSIONAL & MODERN =====
471
- eco_green_css = """
472
- /* Professional Eco Theme - Clean, Modern, Sustainable */
473
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
 
 
 
 
474
 
475
  :root {
476
- --primary-color: #059669; /* Emerald 600 */
477
- --primary-dark: #047857; /* Emerald 700 */
478
- --primary-light: #d1fae5; /* Emerald 100 */
479
- --accent-color: #10b981; /* Emerald 500 */
480
- --text-main: #1e293b; /* Slate 800 */
481
- --text-muted: #64748b; /* Slate 500 */
482
- --bg-color: #f8fafc; /* Slate 50 */
483
- --card-bg: #ffffff;
484
- --border-color: #e2e8f0; /* Slate 200 */
485
  }
486
 
487
  body {
488
- background-color: var(--bg-color);
489
- color: var(--text-main);
 
490
  }
491
 
492
  .gradio-container {
493
- max-width: 1000px !important;
494
  margin: 0 auto !important;
495
  padding: 2rem 1rem !important;
496
- font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
497
  }
498
 
499
- /* Blocks/Cards - Clean & Professional */
 
 
 
 
 
 
 
 
 
 
500
  .block {
501
- background: var(--card-bg) !important;
502
- border: 1px solid var(--border-color) !important;
503
- border-radius: 12px !important;
504
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06) !important;
505
- padding: 0 !important;
506
- overflow: hidden !important;
507
  }
508
 
509
- /* Input Fields - Modern & Accessible */
510
- input, textarea, .gr-input {
511
  background: #ffffff !important;
512
- border: 1px solid #cbd5e1 !important;
513
- border-radius: 8px !important;
514
- padding: 12px 16px !important;
515
- font-size: 16px !important; /* Enforce 16px to prevent iOS zoom */
516
- color: var(--text-main) !important;
517
- transition: all 0.2s ease;
518
  }
519
 
520
  input:focus, textarea:focus {
521
- border-color: var(--primary-color) !important;
522
- box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.15) !important;
523
  outline: none !important;
 
524
  }
525
 
526
  input::placeholder, textarea::placeholder {
527
- color: #475569 !important; /* Slate 600 - Darker for better visibility */
528
- opacity: 1 !important;
529
  }
530
 
531
- /* Primary Action Button - TACTILE & 3D */
532
  #assess-btn {
533
- background: linear-gradient(180deg, #10b981 0%, #059669 100%) !important;
534
  color: white !important;
535
- border: 1px solid #047857 !important;
536
- border-radius: 8px !important;
537
- padding: 14px 28px !important;
538
- font-weight: 600 !important;
539
- font-size: 1.1rem !important;
540
- letter-spacing: 0.01em;
541
  cursor: pointer !important;
542
- transition: all 0.2s ease;
543
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.2) !important;
544
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
 
545
  }
546
 
547
  #assess-btn:hover {
548
- background: linear-gradient(180deg, #059669 0%, #047857 100%) !important;
549
- transform: translateY(-1px);
550
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1) !important;
551
  }
552
 
553
  #assess-btn:active {
554
- transform: translateY(1px);
555
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(0, 0, 0, 0.1) !important;
556
- background: #047857 !important;
557
  }
558
 
559
- /* Result Box - Typography & Spacing */
560
  #result-box {
561
- background: #ffffff !important;
562
- border: 1px solid var(--border-color) !important;
563
- border-radius: 12px !important;
564
- padding: 2.5rem !important;
565
  }
566
 
567
- #result-box .markdown-text {
568
- font-size: 1.05rem !important;
569
- line-height: 1.7 !important;
570
- color: var(--text-main) !important;
571
  }
572
 
573
- #result-box h1 {
574
- color: var(--primary-dark) !important;
575
- font-size: 2rem !important;
576
- font-weight: 700 !important;
577
- border-bottom: 2px solid var(--primary-light) !important;
578
- padding-bottom: 0.75rem !important;
579
- margin-bottom: 1.5rem !important;
580
- letter-spacing: -0.02em !important;
581
  }
582
 
583
- #result-box h2 {
584
- color: var(--primary-color) !important;
585
- font-size: 1.5rem !important;
586
- font-weight: 600 !important;
587
- margin-top: 2.5rem !important;
588
- margin-bottom: 1rem !important;
589
- letter-spacing: -0.01em !important;
590
  }
591
 
592
- #result-box strong {
593
- color: var(--primary-dark) !important;
594
- font-weight: 600 !important;
595
  }
596
 
597
- #result-box p {
598
- margin-bottom: 1rem !important;
599
- color: var(--text-main) !important; /* Force dark color */
 
600
  }
601
 
602
- /* Image Upload Area */
603
- .image-container {
604
- border: 2px dashed #cbd5e1 !important;
605
- border-radius: 12px !important;
606
- background: #f8fafc !important;
607
- transition: border-color 0.2s ease;
608
  }
609
 
610
- .image-container:hover {
611
- border-color: var(--primary-color) !important;
 
 
 
 
 
 
 
612
  }
613
 
614
  /* Labels */
615
- label span {
616
- color: var(--text-muted) !important;
617
- font-weight: 500 !important;
618
- font-size: 0.9rem !important;
619
- text-transform: uppercase !important;
620
- letter-spacing: 0.05em !important;
621
- margin-bottom: 0.5rem !important;
622
  }
623
 
624
  /* Progress Bar */
625
  .progress-bar {
626
- background: var(--primary-color) !important;
 
 
 
 
 
 
627
  }
628
 
629
  /* OR Divider */
630
- .or-text p {
631
- color: #475569 !important; /* Slate 600 - Darker */
632
- font-weight: 700 !important;
633
- text-align: center !important;
634
- margin: 1rem 0 !important;
635
- font-size: 1.1rem !important;
636
  position: relative;
637
  }
638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  /* Footer */
 
 
 
 
 
 
 
 
640
  footer p {
641
- color: var(--text-muted) !important;
 
642
  }
643
  """
644
 
645
- with gr.Blocks(title="Future Earth - Eco Advisor") as app:
646
 
647
  gr.HTML(f"""
648
  <style>
649
- {eco_green_css}
650
  </style>
651
- <div style="text-align: center; margin-bottom: 2.5rem; padding-top: 1rem;">
652
- <div style="display: inline-flex; align-items: center; justify-content: center; background: #ecfdf5; padding: 16px; border-radius: 20px; margin-bottom: 1.2rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);">
653
- <span style="font-size: 3rem;">🌍</span>
 
 
 
 
654
  </div>
655
- <h1 style="color: #064e3b; font-size: 2.5rem; font-weight: 800; margin: 0; letter-spacing: -0.03em; font-family: 'Inter', sans-serif;">Future Earth</h1>
656
- <p style="color: #64748b; font-size: 1.1rem; margin-top: 0.75rem; font-weight: 400;">AI-Powered Environmental Assessment & Advisor</p>
 
 
 
 
657
  </div>
658
  """)
659
 
660
- with gr.Row():
661
- with gr.Column():
662
- image_input = gr.Image(
663
- label="📸 Take Photo or Upload Product Image",
664
- type="pil",
665
- sources=["webcam", "upload"],
666
- height=300,
667
- elem_classes="image-container"
668
- )
669
-
670
- assess_btn = gr.Button("🌱 Check Environmental Impact", elem_id="assess-btn", size="lg")
671
-
672
- with gr.Row():
673
- with gr.Column():
674
- result_output = gr.Markdown(
675
- label="📊 Environmental Assessment",
676
- elem_id="result-box",
677
- value="**🌿 Ready to analyze your product...**\n\nTake a photo or type a product name to get started!",
678
- elem_classes="markdown-text"
679
- )
 
 
 
 
680
 
681
  gr.HTML("""
682
- <footer style="text-align: center; margin-top: 4rem; padding: 2.5rem 0; color: #94a3b8; border-top: 1px solid #e2e8f0;">
683
- <p style="margin: 0; font-weight: 500; color: #64748b;">Powered by Advanced Agentic AI • MCP • RAG</p>
684
- <p style="margin-top: 0.75rem; font-size: 0.9rem; opacity: 0.8;">Building a sustainable future through intelligent analysis</p>
 
 
 
 
 
685
  </footer>
686
  """)
687
 
@@ -697,4 +765,4 @@ with gr.Blocks(title="Future Earth - Eco Advisor") as app:
697
  )
698
 
699
  if __name__ == "__main__":
700
- app.launch()
 
312
 
313
 
314
  # ===== GRADIO 6 FEATURE: Progress Tracking =====
315
+ def assess_product_agentic(image: str, progress=gr.Progress()) -> str:
316
+ """Main assessment with Gradio 6 Progress tracking"""
317
 
318
  try:
319
  progress(0, desc="Initializing agent...")
 
326
 
327
  if image is not None:
328
  progress(0.1, desc="Analyzing image...")
329
+ query, vision_data = analyze_image_with_mcp(image)
 
 
 
330
  # Don't stop if image analysis fails, just log it
331
  if query and not query.startswith("Error"):
332
  source = f"**Detected:** {query}\n\n"
 
339
  # Image analysis failed, clear query so text input can be used
340
  query = ""
341
 
342
+
343
 
344
  # Only return error if BOTH image and text are missing/invalid
345
  if not query:
346
  return "**Take a photo** or type a product name to get started!"
347
 
348
+ # PHASE 1: PLANNING
349
  progress(0.3, desc="Agent planning assessment...")
350
+ plan = agent.plan_assessment(query)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
 
352
+ # PHASE 2: REASONING
353
+ progress(0.5, desc="Agent reasoning...")
354
 
355
+ # PHASE 3: EXECUTION
356
+ progress(0.7, desc="Executing assessment...")
357
+ result = execute_assessment(agent, query, vision_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
  progress(0.9, desc="Formatting results...")
360
+ product = result["assessment"]
361
+
362
+ # Get score for color coding
363
+ eco_score = float(product.get('eco_score', 5.0))
364
+
365
+ # Determine score color based on value
366
+ if eco_score < 5:
367
+ score_color = "#ef4444" # Red
368
+ score_label = "Poor"
369
+ score_bg = "#fee2e2"
370
+ elif eco_score < 7:
371
+ score_color = "#f59e0b" # Amber
372
+ score_label = "Moderate"
373
+ score_bg = "#fef3c7"
374
+ else:
375
+ score_color = "#10b981" # Green
376
+ score_label = "Good"
377
+ score_bg = "#d1fae5"
378
+
379
+ # Format main output with styled HTML
380
+ main_output = f"""
381
+ <div style="background: white; border-radius: 16px; padding: 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
382
+ <div style="text-align: center; margin-bottom: 2rem;">
383
+ <div style="display: inline-block; background: {score_bg}; padding: 1.5rem 2.5rem; border-radius: 16px; margin-bottom: 1rem;">
384
+ <div style="font-size: 3.5rem; font-weight: 800; color: {score_color}; margin-bottom: 0.5rem;">{eco_score}/10</div>
385
+ <div style="font-size: 1.2rem; font-weight: 600; color: {score_color}; text-transform: uppercase; letter-spacing: 0.05em;">{score_label}</div>
386
+ </div>
387
+ <h1 style="font-size: 2rem; font-weight: 700; color: #1e293b; margin: 1rem 0;">{product['name']}</h1>
388
+ </div>
389
+
390
+ <div style="display: grid; gap: 1.5rem;">
391
+ <div style="background: #f8fafc; border-left: 4px solid #10b981; padding: 1.5rem; border-radius: 8px;">
392
+ <h3 style="font-size: 1.1rem; font-weight: 600; color: #059669; margin: 0 0 0.75rem 0;">🌱 Carbon Footprint</h3>
393
+ <p style="font-size: 1rem; color: #475569; margin: 0; line-height: 1.6;">{product.get('carbon', 'N/A')}</p>
394
+ </div>
395
 
396
+ <div style="background: #f8fafc; border-left: 4px solid #ef4444; padding: 1.5rem; border-radius: 8px;">
397
+ <h3 style="font-size: 1.1rem; font-weight: 600; color: #dc2626; margin: 0 0 0.75rem 0;">⚠️ Environmental Issues</h3>
398
+ <p style="font-size: 1rem; color: #475569; margin: 0; line-height: 1.6;">{product.get('issues', 'N/A')}</p>
399
+ </div>
 
400
 
401
+ {"<div style='background: #f8fafc; border-left: 4px solid #3b82f6; padding: 1.5rem; border-radius: 8px;'><h3 style='font-size: 1.1rem; font-weight: 600; color: #2563eb; margin: 0 0 0.75rem 0;'>🔬 Materials Analysis</h3><p style='font-size: 1rem; color: #475569; margin: 0; line-height: 1.6;'>" + product['materials_analysis'] + "</p></div>" if 'materials_analysis' in product else ""}
 
402
 
403
+ {"<div style='background: #f8fafc; border-left: 4px solid #8b5cf6; padding: 1.5rem; border-radius: 8px;'><h3 style='font-size: 1.1rem; font-weight: 600; color: #7c3aed; margin: 0 0 0.75rem 0;'>♻️ Lifecycle Insights</h3><p style='font-size: 1rem; color: #475569; margin: 0; line-height: 1.6;'>" + product['lifecycle_insights'] + "</p></div>" if 'lifecycle_insights' in product else ""}
 
404
 
405
+ <div style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 2rem; border-radius: 12px; margin-top: 1rem;">
406
+ <h3 style="font-size: 1.3rem; font-weight: 700; color: white; margin: 0 0 1rem 0; display: flex; align-items: center; gap: 0.5rem;">
407
+ <span style="font-size: 1.5rem;">✨</span> Better Alternative
408
+ </h3>
409
+ <p style="font-size: 1.05rem; color: white; margin: 0; line-height: 1.7; font-weight: 500;">{product.get('alternative', 'N/A')}</p>
410
+ </div>
411
+ </div>
412
+ </div>
413
+ """
414
 
415
  progress(1.0, desc="Complete!")
416
  return main_output
 
419
  import traceback
420
  return f"**Error:**\n\n{str(e)}\n\n```\n{traceback.format_exc()}\n```"
421
 
422
+ # ===== ECO SAGE ADVISOR INSPIRED DESIGN =====
423
+ eco_sage_css = """
424
+ /* Eco Sage Advisor Inspired Design */
425
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
426
+
427
+ * {
428
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
429
+ }
430
 
431
  :root {
432
+ --primary: #10b981;
433
+ --primary-dark: #059669;
434
+ --danger: #ef4444;
435
+ --warning: #f59e0b;
436
+ --info: #3b82f6;
 
 
 
 
437
  }
438
 
439
  body {
440
+ background: linear-gradient(135deg, #0f766e 0%, #059669 100%);
441
+ margin: 0;
442
+ padding: 0;
443
  }
444
 
445
  .gradio-container {
446
+ max-width: 1200px !important;
447
  margin: 0 auto !important;
448
  padding: 2rem 1rem !important;
 
449
  }
450
 
451
+ /* Hero Section */
452
+ .hero-section {
453
+ text-align: center;
454
+ padding: 3rem 1rem;
455
+ background: rgba(255, 255, 255, 0.95);
456
+ border-radius: 24px;
457
+ margin-bottom: 2rem;
458
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
459
+ }
460
+
461
+ /* Blocks/Cards */
462
  .block {
463
+ background: rgba(255, 255, 255, 0.98) !important;
464
+ border: none !important;
465
+ border-radius: 20px !important;
466
+ padding: 2rem !important;
467
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) !important;
468
+ backdrop-filter: blur(10px);
469
  }
470
 
471
+ /* Input Fields */
472
+ input, textarea {
473
  background: #ffffff !important;
474
+ border: 2px solid #e5e7eb !important;
475
+ border-radius: 12px !important;
476
+ padding: 14px 18px !important;
477
+ font-size: 16px !important;
478
+ color: #1f2937 !important;
479
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
480
  }
481
 
482
  input:focus, textarea:focus {
483
+ border-color: var(--primary) !important;
484
+ box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.1) !important;
485
  outline: none !important;
486
+ transform: translateY(-1px);
487
  }
488
 
489
  input::placeholder, textarea::placeholder {
490
+ color: #9ca3af !important;
 
491
  }
492
 
493
+ /* Primary Button - Hero Style */
494
  #assess-btn {
495
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
496
  color: white !important;
497
+ border: none !important;
498
+ border-radius: 16px !important;
499
+ padding: 18px 36px !important;
500
+ font-weight: 700 !important;
501
+ font-size: 1.15rem !important;
502
+ letter-spacing: 0.02em;
503
  cursor: pointer !important;
504
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
505
+ box-shadow: 0 10px 25px -5px rgba(16, 185, 129, 0.4), 0 10px 10px -5px rgba(16, 185, 129, 0.04) !important;
506
+ text-transform: none !important;
507
+ width: 100% !important;
508
  }
509
 
510
  #assess-btn:hover {
511
+ transform: translateY(-3px) scale(1.02);
512
+ box-shadow: 0 20px 35px -5px rgba(16, 185, 129, 0.5), 0 10px 10px -5px rgba(16, 185, 129, 0.08) !important;
513
+ background: linear-gradient(135deg, #059669 0%, #047857 100%) !important;
514
  }
515
 
516
  #assess-btn:active {
517
+ transform: translateY(-1px) scale(0.98);
 
 
518
  }
519
 
520
+ /* Result Box */
521
  #result-box {
522
+ background: transparent !important;
523
+ border: none !important;
524
+ padding: 0 !important;
525
+ box-shadow: none !important;
526
  }
527
 
528
+ #result-box > div {
529
+ background: transparent !important;
 
 
530
  }
531
 
532
+ /* Image Upload Area - FIXED SIZE */
533
+ .image-container {
534
+ border: 3px dashed #d1d5db !important;
535
+ border-radius: 20px !important;
536
+ background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%) !important;
537
+ transition: all 0.3s ease;
 
 
538
  }
539
 
540
+ .image-container:hover {
541
+ border-color: var(--primary) !important;
542
+ background: linear-gradient(135deg, #ecfdf5 0%, #f0fdf4 100%) !important;
543
+ transform: scale(1.01);
 
 
 
544
  }
545
 
546
+ /* Hide large upload icon */
547
+ .image-container .upload-icon {
548
+ display: none !important;
549
  }
550
 
551
+ /* Resize upload text and icon */
552
+ .image-container button {
553
+ font-size: 0.95rem !important;
554
+ padding: 0.75rem 1.5rem !important;
555
  }
556
 
557
+ /* Make upload area more compact */
558
+ .image-container > div {
559
+ padding: 1rem !important;
 
 
 
560
  }
561
 
562
+ /* Hide the huge center icon in Gradio Image component */
563
+ .image-container svg {
564
+ width: 40px !important;
565
+ height: 40px !important;
566
+ opacity: 0.5;
567
+ }
568
+
569
+ .image-container .wrap {
570
+ min-height: 250px !important;
571
  }
572
 
573
  /* Labels */
574
+ label {
575
+ color: #374151 !important;
576
+ font-weight: 600 !important;
577
+ font-size: 1rem !important;
578
+ margin-bottom: 0.75rem !important;
579
+ display: block !important;
 
580
  }
581
 
582
  /* Progress Bar */
583
  .progress-bar {
584
+ background: linear-gradient(90deg, #10b981 0%, #059669 100%) !important;
585
+ border-radius: 9999px !important;
586
+ }
587
+
588
+ .progress-bar-wrap {
589
+ background: rgba(16, 185, 129, 0.1) !important;
590
+ border-radius: 9999px !important;
591
  }
592
 
593
  /* OR Divider */
594
+ .or-divider {
595
+ text-align: center;
596
+ margin: 1.5rem 0;
 
 
 
597
  position: relative;
598
  }
599
 
600
+ .or-divider::before,
601
+ .or-divider::after {
602
+ content: '';
603
+ position: absolute;
604
+ top: 50%;
605
+ width: 40%;
606
+ height: 2px;
607
+ background: linear-gradient(to right, transparent, #d1d5db, transparent);
608
+ }
609
+
610
+ .or-divider::before {
611
+ left: 0;
612
+ }
613
+
614
+ .or-divider::after {
615
+ right: 0;
616
+ }
617
+
618
+ /* Toast/Info Messages */
619
+ .toast {
620
+ background: white !important;
621
+ border-left: 4px solid var(--primary) !important;
622
+ border-radius: 12px !important;
623
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1) !important;
624
+ }
625
+
626
+ /* Scrollbar */
627
+ ::-webkit-scrollbar {
628
+ width: 10px;
629
+ }
630
+
631
+ ::-webkit-scrollbar-track {
632
+ background: #f1f5f9;
633
+ border-radius: 10px;
634
+ }
635
+
636
+ ::-webkit-scrollbar-thumb {
637
+ background: linear-gradient(180deg, #10b981, #059669);
638
+ border-radius: 10px;
639
+ }
640
+
641
+ ::-webkit-scrollbar-thumb:hover {
642
+ background: linear-gradient(180deg, #059669, #047857);
643
+ }
644
+
645
+ /* Mobile Responsive */
646
+ @media (max-width: 768px) {
647
+ .gradio-container {
648
+ padding: 1rem 0.5rem !important;
649
+ }
650
+
651
+ .hero-section {
652
+ padding: 2rem 1rem;
653
+ }
654
+
655
+ .block {
656
+ padding: 1.5rem !important;
657
+ }
658
+
659
+ #assess-btn {
660
+ padding: 16px 28px !important;
661
+ font-size: 1rem !important;
662
+ }
663
+ }
664
+
665
+ /* Animations */
666
+ @keyframes fadeIn {
667
+ from {
668
+ opacity: 0;
669
+ transform: translateY(10px);
670
+ }
671
+ to {
672
+ opacity: 1;
673
+ transform: translateY(0);
674
+ }
675
+ }
676
+
677
+ .block {
678
+ animation: fadeIn 0.5s ease-out;
679
+ }
680
+
681
  /* Footer */
682
+ footer {
683
+ background: rgba(255, 255, 255, 0.1) !important;
684
+ backdrop-filter: blur(10px);
685
+ border-radius: 16px;
686
+ margin-top: 3rem;
687
+ padding: 2rem;
688
+ }
689
+
690
  footer p {
691
+ color: white !important;
692
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
693
  }
694
  """
695
 
696
+ with gr.Blocks(title="Future Earth") as app:
697
 
698
  gr.HTML(f"""
699
  <style>
700
+ {eco_sage_css}
701
  </style>
702
+
703
+ <div class="hero-section">
704
+ <!-- Logo and Title Row -->
705
+ <div style="display: flex; align-items: center; justify-content: center; gap: 1rem; margin-bottom: 1.5rem;">
706
+ <div style="display: flex; align-items: baseline; gap: 0.5rem;">
707
+ <h1 style="font-size: 2.5rem; font-weight: 800; color: #047857; margin: 0; line-height: 1;">Future Earth</h1>
708
+ </div>
709
  </div>
710
+
711
+ <!-- Main Heading -->
712
+ <h2 style="font-size: 2.25rem; font-weight: 700; color: #111827; margin: 0 0 1rem 0; line-height: 1.2;">Agentic Eco Advisor</h2>
713
+
714
+ <!-- Subtitle -->
715
+ <p style="font-size: 1.125rem; color: #6b7280; margin: 0; font-weight: 400; max-width: 800px; margin-left: auto; margin-right: auto;">Analyze products instantly and discover their environmental footprint</p>
716
  </div>
717
  """)
718
 
719
+ with gr.Column():
720
+ image_input = gr.Image(
721
+ label="📸 Upload Product Image",
722
+ type="pil",
723
+ sources=["webcam", "upload"],
724
+ height=300,
725
+ elem_classes="image-container",
726
+ show_label=False
727
+ )
728
+
729
+
730
+ assess_btn = gr.Button("🌱 Analyze Environmental Impact", elem_id="assess-btn", size="lg")
731
+
732
+ result_output = gr.HTML(
733
+ label="",
734
+ value="""
735
+ <div style="background: white; border-radius: 20px; padding: 3rem; text-align: center; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.1);">
736
+ <div style="font-size: 4rem; margin-bottom: 1rem;">🌿</div>
737
+ <h2 style="font-size: 1.5rem; font-weight: 700; color: #1e293b; margin-bottom: 1rem;">Ready to Analyze</h2>
738
+ <p style="font-size: 1.1rem; color: #64748b; margin: 0;">Upload a product image or type a product name to get started with your environmental assessment</p>
739
+ </div>
740
+ """,
741
+ elem_id="result-box"
742
+ )
743
 
744
  gr.HTML("""
745
+ <footer style="text-align: center;">
746
+ <p style="font-size: 0.85rem; font-weight: 600; margin: 0; color: #1f2937 !important; text-shadow: none !important;">♻️ Powered by Advanced AI • MCP • RAG Technology</p>
747
+ <p style="font-size: 0.85rem; margin-top: 0.75rem; opacity: 0.9; color: #374151 !important; text-shadow: none !important;">Building a sustainable future through intelligent environmental analysis</p>
748
+ <div style="margin-top: 1.5rem; display: flex; justify-content: center; gap: 2rem; flex-wrap: wrap;">
749
+ <span style="font-size: 0.85rem; color: #1f2937 !important;">🌱 Sustainable Products</span>
750
+ <span style="font-size: 0.85rem; color: #1f2937 !important;">📊 Data-Driven Insights</span>
751
+ <span style="font-size: 0.85rem; color: #1f2937 !important;">🔬 Scientific Analysis</span>
752
+ </div>
753
  </footer>
754
  """)
755
 
 
765
  )
766
 
767
  if __name__ == "__main__":
768
+ app.launch()