{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dfd434e5",
   "metadata": {},
   "source": [
    "# Finding the Cheapest Airbnb with AI-Powered Web Automation\n",
    "\n",
    "This notebook demonstrates how to build an intelligent web automation agent that searches Airbnb, extracts prices, and finds the cheapest listing in Stockholm. You'll learn how to integrate **Playwright** with **Browser-Use** for powerful AI-driven automation.\n",
    "\n",
    "## What You'll Learn:\n",
    "1. **Playwright + Browser-Use Integration**: Combining browser management with AI automation\n",
    "2. **Vision-Based Price Extraction**: Let AI \"see\" and read prices from web pages\n",
    "3. **Structured Data Extraction**: Extract listing data with type-safe Pydantic models\n",
    "4. **Price Comparison Logic**: Find the cheapest option from multiple listings\n",
    "5. **Real-world Application**: Practical price comparison automation\n",
    "\n",
    "## Prerequisites:\n",
    "- Azure OpenAI deployment configured\n",
    "- Playwright installed (`pip install playwright`)\n",
    "- Understanding of async Python\n",
    "- Basic web automation concepts"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6340fa32",
   "metadata": {},
   "source": [
    "## Understanding the Playwright + Browser-Use Architecture\n",
    "\n",
    "This notebook uses the **official Playwright integration** pattern from Browser-Use documentation.\n",
    "\n",
    "### Architecture Flow:\n",
    "```\n",
    "┌──────────────────┐\n",
    "│   Playwright     │ ◄─── Manages browser lifecycle\n",
    "│  Browser Manager │      Handles CDP connection\n",
    "└────────┬─────────┘      Provides browser instance\n",
    "         │\n",
    "         │ playwright_browser parameter\n",
    "         ▼\n",
    "┌──────────────────┐\n",
    "│  Browser-Use     │ ◄─── AI-powered automation\n",
    "│  Browser Object  │      Wraps Playwright browser\n",
    "└────────┬─────────┘      Provides Agent interface\n",
    "         │\n",
    "         │ uses\n",
    "         ▼\n",
    "┌──────────────────┐\n",
    "│   Agent          │ ◄─── Vision + Decision Making\n",
    "│ (with LLM)       │      Structured output extraction\n",
    "└──────────────────┘      Natural language tasks\n",
    "         │\n",
    "         │ powered by\n",
    "         ▼\n",
    "┌──────────────────┐\n",
    "│  Azure OpenAI    │ ◄─── GPT-4 Vision\n",
    "│  (LLM + Vision)  │      Analyzes screenshots\n",
    "└──────────────────┘      Extracts structured data\n",
    "```\n",
    "\n",
    "### Why This Approach?\n",
    "\n",
    "**Playwright provides:**\n",
    "- ✅ Robust browser lifecycle management\n",
    "- ✅ Full Chrome DevTools Protocol control\n",
    "- ✅ Stable page and context handling\n",
    "- ✅ Built-in waiting and synchronization\n",
    "\n",
    "**Browser-Use adds:**\n",
    "- ✅ AI-powered element finding (no CSS selectors needed!)\n",
    "- ✅ Vision-based page understanding\n",
    "- ✅ Structured output extraction with Pydantic\n",
    "- ✅ Natural language task execution\n",
    "\n",
    "**Together they enable:**\n",
    "- 🎯 \"Search for Stockholm Airbnb\" → Agent navigates\n",
    "- 👁️ Vision reads all prices on the page\n",
    "- 📊 Structured extraction → clean Python objects\n",
    "- 💰 Price comparison logic → find cheapest\n",
    "\n",
    "### Our Task Flow:\n",
    "1. **Playwright** launches Chrome browser\n",
    "2. **Browser-Use Agent** navigates to Airbnb.com\n",
    "3. **Agent searches** for \"Stockholm, Sweden\"\n",
    "4. **Vision model** reads and extracts all listing prices\n",
    "5. **Structured output** returns typed data (Pydantic models)\n",
    "6. **Python code** compares prices and finds cheapest\n",
    "7. **Display results** with rich formatting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1449801",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install browser_use langchain-openai playwright "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "290efc2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "!playwright install chromium"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ed8648b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ All packages imported successfully\n"
     ]
    }
   ],
   "source": [
    "import asyncio\n",
    "import os\n",
    "import re\n",
    "from typing import Optional, List\n",
    "from IPython.display import display, HTML, Markdown\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# Playwright imports\n",
    "from playwright.async_api import async_playwright\n",
    "\n",
    "# Browser-Use imports - USE BROWSER-USE'S AZURE OPENAI!\n",
    "# Changed from langchain_openai\n",
    "from browser_use import Agent, Browser, ChatAzureOpenAI\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "print(\"✅ All packages imported successfully\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "26f7e392",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Azure OpenAI Configuration:\n",
      "   Endpoint: https://foundry-aiteam2510.cognitiveservices.azure.com/\n",
      "   Deployment: gpt-5-mini\n",
      "   API Version: 2024-12-01-preview\n"
     ]
    }
   ],
   "source": [
    "# Load environment variables\n",
    "load_dotenv()\n",
    "\n",
    "# Azure OpenAI Configuration\n",
    "azure_openai_deployment = os.getenv(\"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME\")\n",
    "azure_openai_endpoint = os.getenv(\"AZURE_OPENAI_ENDPOINT\")\n",
    "azure_openai_api_key = os.getenv(\"AZURE_OPENAI_API_KEY\")\n",
    "api_version = os.getenv(\"AZURE_OPENAI_API_VERSION\")\n",
    "\n",
    "# Verify configuration\n",
    "print(\"✅ Azure OpenAI Configuration:\")\n",
    "print(f\"   Endpoint: {azure_openai_endpoint}\")\n",
    "print(f\"   Deployment: {azure_openai_deployment}\")\n",
    "print(f\"   API Version: {api_version}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70b80842",
   "metadata": {},
   "source": [
    "## Initialize Azure OpenAI LLM\n",
    "\n",
    "The LLM powers the Agent's decision-making and vision capabilities. We use:\n",
    "- **Temperature: 0.3** for consistent, predictable automation\n",
    "- **Vision capabilities** to \"see\" and understand page content\n",
    "- **Structured output** to extract data into Pydantic models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ce29377",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ LLM initialized successfully\n",
      "   Model: gpt-5-mini\n",
      "   Endpoint: https://foundry-aiteam2510.cognitiveservices.azure.com/\n",
      "   Integration: Browser-Use ChatAzureOpenAI\n"
     ]
    }
   ],
   "source": [
    "# Initialize Azure OpenAI with Browser-Use's ChatAzureOpenAI\n",
    "llm = ChatAzureOpenAI(\n",
    "    # Your deployment name (e.g., 'gpt-5-mini')\n",
    "    model=azure_openai_deployment,\n",
    "    # Browser-Use reads these from environment variables automatically:\n",
    "    # AZURE_OPENAI_ENDPOINT\n",
    "    # AZURE_OPENAI_API_KEY\n",
    "    # AZURE_OPENAI_API_VERSION (optional, defaults to latest)\n",
    ")\n",
    "\n",
    "print(\"✅ LLM initialized successfully\")\n",
    "print(f\"   Model: {azure_openai_deployment}\")\n",
    "print(f\"   Endpoint: {azure_openai_endpoint}\")\n",
    "print(f\"   Integration: Browser-Use ChatAzureOpenAI\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "846bf74e",
   "metadata": {},
   "source": [
    "## Define Structured Output Models\n",
    "\n",
    "We use Pydantic models to extract structured data from Airbnb search results. The Agent will use GPT-4 Vision to read the page and extract data into these models automatically.\n",
    "\n",
    "This ensures type safety and validation of all extracted data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "5ff0e177",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Structured output models defined\n",
      "   AirbnbListing: Individual listing data with clickable URLs\n",
      "   SearchResult: Complete search results with price analysis\n"
     ]
    }
   ],
   "source": [
    "# UPDATE THIS CELL - Add URL field to AirbnbListing\n",
    "class AirbnbListing(BaseModel):\n",
    "    \"\"\"Single Airbnb listing with price information\"\"\"\n",
    "    title: str = Field(description=\"Name/title of the listing\")\n",
    "    price_per_night: float = Field(\n",
    "        description=\"Price per night as a number (extract just the numeric value, ignore currency symbols)\")\n",
    "    currency: str = Field(\n",
    "        default=\"SEK\", description=\"Currency code (SEK for Swedish Krona)\")\n",
    "    rating: Optional[float] = Field(\n",
    "        default=None, description=\"Rating score if visible\")\n",
    "    url: Optional[str] = Field(\n",
    "        default=None, description=\"Full URL link to the listing page\")  # ✅ NEW!\n",
    "\n",
    "\n",
    "class SearchResult(BaseModel):\n",
    "    \"\"\"Complete search results from Airbnb\"\"\"\n",
    "    location: str = Field(description=\"Search location (Stockholm, Sweden)\")\n",
    "    total_listings_found: int = Field(\n",
    "        description=\"Number of listings found on the page\")\n",
    "    listings: List[AirbnbListing] = Field(\n",
    "        description=\"List of all listings with prices extracted from the page\")\n",
    "    cheapest_listing: AirbnbListing = Field(\n",
    "        description=\"The listing with the lowest price per night\")\n",
    "    average_price: float = Field(\n",
    "        description=\"Average price per night across all listings\")\n",
    "    price_range: str = Field(description=\"Price range as 'min - max SEK'\")\n",
    "\n",
    "\n",
    "print(\"✅ Structured output models defined\")\n",
    "print(\"   AirbnbListing: Individual listing data with clickable URLs\")\n",
    "print(\"   SearchResult: Complete search results with price analysis\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c50e9241",
   "metadata": {},
   "source": [
    "## Helper Functions for Display\n",
    "\n",
    "These functions provide rich, educational output in the notebook with formatted HTML."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61e58afe",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "86977630",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Structured output models defined\n",
      "   ListingInfo: Extract listing details\n",
      "   BookingDates: Capture selected dates\n",
      "   BookingResult: Final booking status\n"
     ]
    }
   ],
   "source": [
    "class ListingInfo(BaseModel):\n",
    "    \"\"\"Information about the Airbnb listing\"\"\"\n",
    "    title: str = Field(description=\"The name/title of the listing\")\n",
    "    location: str = Field(description=\"City and country of the listing\")\n",
    "    price_per_night: Optional[str] = Field(\n",
    "        description=\"Price per night if visible\")\n",
    "    rating: Optional[str] = Field(description=\"Rating score if visible\")\n",
    "\n",
    "\n",
    "class BookingDates(BaseModel):\n",
    "    \"\"\"Selected booking dates\"\"\"\n",
    "    check_in: str = Field(\n",
    "        description=\"Check-in date in format: Month DD, YYYY\")\n",
    "    check_out: str = Field(\n",
    "        description=\"Check-out date in format: Month DD, YYYY\")\n",
    "    nights: int = Field(description=\"Number of nights\")\n",
    "\n",
    "\n",
    "class BookingResult(BaseModel):\n",
    "    \"\"\"Complete booking result information\"\"\"\n",
    "    success: bool = Field(description=\"Whether the booking flow was completed\")\n",
    "    listing_info: Optional[ListingInfo] = Field(\n",
    "        description=\"Details about the listing\")\n",
    "    booking_dates: Optional[BookingDates] = Field(description=\"Selected dates\")\n",
    "    total_price: Optional[str] = Field(description=\"Total price if shown\")\n",
    "    message: str = Field(description=\"Status message or error description\")\n",
    "\n",
    "\n",
    "print(\"✅ Structured output models defined\")\n",
    "print(\"   ListingInfo: Extract listing details\")\n",
    "print(\"   BookingDates: Capture selected dates\")\n",
    "print(\"   BookingResult: Final booking status\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd44b2e8",
   "metadata": {},
   "source": [
    "## Helper Functions for Display\n",
    "\n",
    "These functions provide rich, educational output in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "cc83f640",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Helper functions loaded\n"
     ]
    }
   ],
   "source": [
    "def display_step(step_number: int, title: str, description: str, color: str = \"#2E8B57\"):\n",
    "    \"\"\"Display a workflow step with formatting\"\"\"\n",
    "    html = f\"\"\"\n",
    "    <div style='\n",
    "        margin: 20px 0; \n",
    "        padding: 20px; \n",
    "        border-left: 5px solid {color}; \n",
    "        background: linear-gradient(to right, rgba(46, 139, 87, 0.05), transparent); \n",
    "        border-radius: 8px;\n",
    "    '>\n",
    "        <h3 style='color: {color}; margin: 0 0 10px 0;'>\n",
    "            Step {step_number}: {title}\n",
    "        </h3>\n",
    "        <p style='margin: 0; line-height: 1.6; color: #333;'>{description}</p>\n",
    "    </div>\n",
    "    \"\"\"\n",
    "    display(HTML(html))\n",
    "\n",
    "\n",
    "def display_action(action_type: str, details: str, emoji: str = \"⚙️\"):\n",
    "    \"\"\"Display an action being performed\"\"\"\n",
    "    html = f\"\"\"\n",
    "    <div style='\n",
    "        margin: 10px 20px;\n",
    "        padding: 12px 16px;\n",
    "        background: rgba(0, 123, 255, 0.05);\n",
    "        border: 1px solid #007BFF;\n",
    "        border-radius: 6px;\n",
    "        font-family: monospace;\n",
    "        font-size: 14px;\n",
    "    '>\n",
    "        <strong style='color: #007BFF;'>{emoji} {action_type}:</strong>\n",
    "        <span style='color: #555; margin-left: 10px;'>{details}</span>\n",
    "    </div>\n",
    "    \"\"\"\n",
    "    display(HTML(html))\n",
    "\n",
    "\n",
    "def display_result(success: bool, message: str):\n",
    "    \"\"\"Display a result with success/failure indication\"\"\"\n",
    "    color = \"#28a745\" if success else \"#dc3545\"\n",
    "    emoji = \"✅\" if success else \"❌\"\n",
    "    html = f\"\"\"\n",
    "    <div style='\n",
    "        margin: 20px 0;\n",
    "        padding: 15px 20px;\n",
    "        border-left: 5px solid {color};\n",
    "        background: rgba({\"40, 167, 69\" if success else \"220, 53, 69\"}, 0.1);\n",
    "        border-radius: 8px;\n",
    "    '>\n",
    "        <strong style='color: {color}; font-size: 16px;'>{emoji} {message}</strong>\n",
    "    </div>\n",
    "    \"\"\"\n",
    "    display(HTML(html))\n",
    "\n",
    "\n",
    "def display_screenshot(screenshot_base64: str, caption: str = \"\"):\n",
    "    \"\"\"Display a screenshot in the notebook\"\"\"\n",
    "    html = f\"\"\"\n",
    "    <div style='margin: 20px 0; text-align: center;'>\n",
    "        <img src='data:image/png;base64,{screenshot_base64}' \n",
    "             style='max-width: 100%; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);'/>\n",
    "        {f\"<p style='margin-top: 10px; color: #666; font-style: italic;'>{caption}</p>\" if caption else \"\"}\n",
    "    </div>\n",
    "    \"\"\"\n",
    "    display(HTML(html))\n",
    "\n",
    "\n",
    "print(\"✅ Helper functions loaded\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2267dd74",
   "metadata": {},
   "source": [
    "## The Airbnb Booking Agent Class\n",
    "\n",
    "This class orchestrates the entire booking workflow, combining Agent and Actor approaches strategically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "9eb624c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ AirbnbSearchAgent class defined\n",
      "   Integration: Playwright browser + Browser-Use Agent\n",
      "   Capabilities: Vision-based price extraction with structured output\n"
     ]
    }
   ],
   "source": [
    "class AirbnbSearchAgent:\n",
    "    \"\"\"\n",
    "    Intelligent Airbnb search agent using Playwright + Browser-Use integration.\n",
    "    \n",
    "    This agent:\n",
    "    1. Navigates to Airbnb.com\n",
    "    2. Searches for listings in Stockholm\n",
    "    3. Extracts all visible prices using AI vision\n",
    "    4. Compares prices and finds the cheapest listing\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, llm, playwright_browser):\n",
    "        self.llm = llm\n",
    "        # Browser-Use wraps the Playwright browser\n",
    "        # Reference: https://docs.browser-use.com/examples/templates/playwright-integration\n",
    "        self.browser = Browser(playwright_browser=playwright_browser)\n",
    "        \n",
    "    async def take_screenshot(self, caption: str = \"\"):\n",
    "        \"\"\"Take and display a screenshot of current page\"\"\"\n",
    "        try:\n",
    "            page = await self.browser.get_current_page()\n",
    "            screenshot_base64 = await page.screenshot(format='png')\n",
    "            display_screenshot(screenshot_base64, caption)\n",
    "        except Exception as e:\n",
    "            print(f\"⚠️  Could not capture screenshot: {str(e)}\")\n",
    "    \n",
    "    async def search_stockholm(self) -> SearchResult:\n",
    "        \"\"\"\n",
    "        Main workflow: Search Airbnb for Stockholm and find cheapest listing.\n",
    "        \n",
    "        Returns:\n",
    "            SearchResult: Structured data with all listings and price analysis\n",
    "        \"\"\"\n",
    "        \n",
    "        # Step 1: Navigate and search\n",
    "        display_step(\n",
    "            1, \n",
    "            \"Navigate & Search (AI Agent)\", \n",
    "            \"Using AI agent with vision to navigate Airbnb and search for Stockholm listings. \"\n",
    "            \"The agent will handle pop-ups, cookie banners, and search automatically.\"\n",
    "        )\n",
    "        \n",
    "        try:\n",
    "            # Agent navigates to Airbnb and searches\n",
    "            search_agent = Agent(\n",
    "                task=(\n",
    "                    \"Navigate to https://www.airbnb.com. \"\n",
    "                    \"Close any pop-ups, cookie banners, or login prompts if they appear. \"\n",
    "                    \"Search for 'Stockholm, Sweden' in the search box. \"\n",
    "                    \"Wait for the search results page to fully load with listing cards visible.\"\n",
    "                ),\n",
    "                llm=self.llm,\n",
    "                browser=self.browser,\n",
    "                use_vision=True  # Critical: enables screenshot analysis\n",
    "            )\n",
    "            \n",
    "            display_action(\"Agent\", \"Navigating to Airbnb and searching for Stockholm...\")\n",
    "            await search_agent.run()\n",
    "            \n",
    "            # Wait for results to load\n",
    "            await asyncio.sleep(3)\n",
    "            await self.take_screenshot(\"Search results page loaded\")\n",
    "            \n",
    "            display_result(True, \"Successfully loaded Stockholm search results\")\n",
    "            \n",
    "        except Exception as e:\n",
    "            display_result(False, f\"Search failed: {str(e)}\")\n",
    "            raise\n",
    "        \n",
    "        # Step 2: Extract prices with vision\n",
    "        display_step(\n",
    "            2,\n",
    "            \"Extract Prices (Vision + LLM)\",\n",
    "            \"Using GPT-4 Vision to read all listing prices from the page and extract \"\n",
    "            \"structured data into Pydantic models. The AI 'sees' the page like a human.\"\n",
    "        )\n",
    "        \n",
    "        try:\n",
    "            page = await self.browser.get_current_page()\n",
    "            \n",
    "            display_action(\"Vision\", \"AI is analyzing the page and reading prices...\")\n",
    "            \n",
    "            # Use page.extract_content with structured output\n",
    "            # Reference: https://docs.browser-use.com/customize/actor/all-parameters\n",
    "            # This uses LLM to parse the visible page content\n",
    "            extraction_prompt = \"\"\"\n",
    "            Extract ALL Airbnb listings visible on this page.\n",
    "            \n",
    "            For each listing, extract:\n",
    "            - Title/name of the property\n",
    "            - Price per night (numeric value only, without currency symbols)\n",
    "            - Currency (SEK for Swedish Krona)\n",
    "            - Rating if visible\n",
    "            \n",
    "            After extracting all listings:\n",
    "            - Identify which listing has the LOWEST price\n",
    "            - Calculate the average price across all listings\n",
    "            - Determine the price range (min to max)\n",
    "            \n",
    "            Focus on the listing cards on the search results page.\n",
    "            Only include listings where you can clearly see the price.\n",
    "            \"\"\"\n",
    "            \n",
    "            # Extract structured data using LLM vision\n",
    "            search_results = await page.extract_content(\n",
    "                prompt=extraction_prompt,\n",
    "                structured_output=SearchResult,\n",
    "                llm=self.llm\n",
    "            )\n",
    "            \n",
    "            display_action(\n",
    "                \"Extracted\", \n",
    "                f\"Found {search_results.total_listings_found} listings with prices\"\n",
    "            )\n",
    "            \n",
    "            # Display some sample prices\n",
    "            if len(search_results.listings) > 0:\n",
    "                sample_prices = [f\"{l.price_per_night:.0f} SEK\" for l in search_results.listings[:5]]\n",
    "                display_action(\n",
    "                    \"Sample Prices\", \n",
    "                    f\"{', '.join(sample_prices)}{'...' if len(search_results.listings) > 5 else ''}\"\n",
    "                )\n",
    "            \n",
    "            display_result(True, \"Price extraction completed successfully\")\n",
    "            \n",
    "            return search_results\n",
    "            \n",
    "        except Exception as e:\n",
    "            display_result(False, f\"Price extraction failed: {str(e)}\")\n",
    "            raise\n",
    "\n",
    "print(\"✅ AirbnbSearchAgent class defined\")\n",
    "print(\"   Integration: Playwright browser + Browser-Use Agent\")\n",
    "print(\"   Capabilities: Vision-based price extraction with structured output\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f097a3de",
   "metadata": {},
   "source": [
    "## Execute the Search\n",
    "\n",
    "Now let's run the complete workflow and find the cheapest Airbnb in Stockholm!\n",
    "\n",
    "This will:\n",
    "1. Launch a real Chrome browser (visible)\n",
    "2. Use AI to navigate and search\n",
    "3. Extract all prices using vision\n",
    "4. Display the cheapest option"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "75fcdd98",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ AirbnbSearchAgent class defined\n",
      "   Integration: CDP-based connection for Playwright + Browser-Use\n",
      "   Capabilities: Vision-based price extraction with structured output\n",
      "   Browser Mode: keep_alive=True (prevents auto-close)\n"
     ]
    }
   ],
   "source": [
    "# UPDATED AirbnbSearchAgent class - Add keep_alive parameter\n",
    "class AirbnbSearchAgent:\n",
    "    \"\"\"\n",
    "    Intelligent Airbnb search agent using Browser-Use integration via CDP.\n",
    "    \n",
    "    This agent:\n",
    "    1. Connects to Chrome via CDP (Chrome DevTools Protocol)\n",
    "    2. Both Playwright and Browser-Use share the same browser instance\n",
    "    3. Searches for listings in Stockholm\n",
    "    4. Extracts prices using AI vision\n",
    "    5. Finds the cheapest listing\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, llm, cdp_url: str):\n",
    "        \"\"\"\n",
    "        Initialize agent with LLM and CDP connection.\n",
    "        \n",
    "        Args:\n",
    "            llm: Language model for AI decisions\n",
    "            cdp_url: Chrome DevTools Protocol URL (e.g., 'http://localhost:9222')\n",
    "        \"\"\"\n",
    "        self.llm = llm\n",
    "        # Browser-Use connects to Chrome via CDP\n",
    "        # IMPORTANT: keep_alive=True prevents browser from closing after Agent completes\n",
    "        self.browser = Browser(\n",
    "            cdp_url=cdp_url,\n",
    "            keep_alive=True  # ✅ This keeps the browser open!\n",
    "        )\n",
    "\n",
    "    async def take_screenshot(self, caption: str = \"\"):\n",
    "        \"\"\"Take and display a screenshot of current page\"\"\"\n",
    "        try:\n",
    "            # Get pages and use the active one\n",
    "            pages = await self.browser.get_pages()\n",
    "            if not pages:\n",
    "                print(\"⚠️  No pages available for screenshot\")\n",
    "                return\n",
    "\n",
    "            page = pages[0]  # Use first page (active page)\n",
    "            screenshot_bytes = await page.screenshot()\n",
    "            import base64\n",
    "            screenshot_base64 = base64.b64encode(screenshot_bytes).decode()\n",
    "            display_screenshot(screenshot_base64, caption)\n",
    "        except Exception as e:\n",
    "            print(f\"⚠️  Could not capture screenshot: {str(e)}\")\n",
    "\n",
    "    async def search_stockholm(self) -> SearchResult:\n",
    "        \"\"\"\n",
    "        Main workflow: Search Airbnb for Stockholm and find cheapest listing.\n",
    "        \n",
    "        Returns:\n",
    "            SearchResult: Structured data with all listings and price analysis\n",
    "        \"\"\"\n",
    "\n",
    "        # Step 1: Navigate and search\n",
    "        display_step(\n",
    "            1,\n",
    "            \"Navigate & Search (AI Agent)\",\n",
    "            \"Using AI agent with vision to navigate Airbnb and search for Stockholm listings. \"\n",
    "            \"The agent will handle pop-ups, cookie banners, and search automatically.\"\n",
    "        )\n",
    "\n",
    "        try:\n",
    "            # Agent navigates to Airbnb and searches\n",
    "            search_agent = Agent(\n",
    "                task=(\n",
    "                    \"Navigate to https://www.airbnb.com. \"\n",
    "                    \"Close any pop-ups, cookie banners, or login prompts if they appear. \"\n",
    "                    \"Search for 'Stockholm, Sweden' in the search box. \"\n",
    "                    \"Wait for the search results page to fully load with listing cards visible.\"\n",
    "                ),\n",
    "                llm=self.llm,\n",
    "                browser=self.browser,\n",
    "                use_vision=True  # Critical: enables screenshot analysis\n",
    "            )\n",
    "\n",
    "            display_action(\n",
    "                \"Agent\", \"Navigating to Airbnb and searching for Stockholm...\")\n",
    "            await search_agent.run()\n",
    "\n",
    "            # Wait for results to load\n",
    "            await asyncio.sleep(3)\n",
    "            await self.take_screenshot(\"Search results page loaded\")\n",
    "\n",
    "            display_result(\n",
    "                True, \"Successfully loaded Stockholm search results\")\n",
    "\n",
    "        except Exception as e:\n",
    "            display_result(False, f\"Search failed: {str(e)}\")\n",
    "            raise\n",
    "\n",
    "        # Step 2: Extract prices with vision\n",
    "        display_step(\n",
    "            2,\n",
    "            \"Extract Prices (Vision + LLM)\",\n",
    "            \"Using GPT-4 Vision to read all listing prices from the page and extract \"\n",
    "            \"structured data into Pydantic models. The AI 'sees' the page like a human.\"\n",
    "        )\n",
    "\n",
    "        try:\n",
    "            # Get the pages created by the Agent\n",
    "            pages = await self.browser.get_pages()\n",
    "\n",
    "            if not pages:\n",
    "                raise RuntimeError(\n",
    "                    \"No pages available after Agent run. Browser might have closed.\")\n",
    "\n",
    "            # Use the first (active) page\n",
    "            page = pages[0]\n",
    "\n",
    "            display_action(\n",
    "                \"Vision\", \"AI is analyzing the page and reading prices...\")\n",
    "\n",
    "            # Extract structured data using LLM vision\n",
    "            extraction_prompt = \"\"\"\n",
    "Extract ALL Airbnb Home listings visible on this page. DO NOT include \"Experiences\" or other non-home listings.\n",
    "\n",
    "For each listing, extract:\n",
    "- Title/name of the property\n",
    "- Price per night (numeric value only, without currency symbols)\n",
    "- Currency (SEK for Swedish Krona)\n",
    "- Rating if visible\n",
    "- URL: The full link to the listing detail page (should start with https://www.airbnb.com/rooms/)\n",
    "\n",
    "After extracting all listings:\n",
    "- Identify which listing has the LOWEST price\n",
    "- Calculate the average price across all listings\n",
    "- Determine the price range (min to max)\n",
    "\n",
    "Focus on the listing cards on the search results page.\n",
    "Only include listings where you can clearly see the price.\n",
    "IMPORTANT: Extract the actual URL/link for each listing so users can click on it.\n",
    "\"\"\"\n",
    "\n",
    "            search_results = await page.extract_content(\n",
    "                prompt=extraction_prompt,\n",
    "                structured_output=SearchResult,\n",
    "                llm=self.llm\n",
    "            )\n",
    "\n",
    "            display_action(\n",
    "                \"Extracted\",\n",
    "                f\"Found {search_results.total_listings_found} listings with prices\"\n",
    "            )\n",
    "\n",
    "            # Display some sample prices\n",
    "            if len(search_results.listings) > 0:\n",
    "                sample_prices = [\n",
    "                    f\"{l.price_per_night:.0f} SEK\" for l in search_results.listings[:5]]\n",
    "                display_action(\n",
    "                    \"Sample Prices\",\n",
    "                    f\"{', '.join(sample_prices)}{'...' if len(search_results.listings) > 5 else ''}\"\n",
    "                )\n",
    "\n",
    "            display_result(True, \"Price extraction completed successfully\")\n",
    "\n",
    "            return search_results\n",
    "\n",
    "        except Exception as e:\n",
    "            display_result(False, f\"Price extraction failed: {str(e)}\")\n",
    "            raise\n",
    "\n",
    "\n",
    "print(\"✅ AirbnbSearchAgent class defined\")\n",
    "print(\"   Integration: CDP-based connection for Playwright + Browser-Use\")\n",
    "print(\"   Capabilities: Vision-based price extraction with structured output\")\n",
    "print(\"   Browser Mode: keep_alive=True (prevents auto-close)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "2ea963e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        padding: 30px;\n",
       "        background: linear-gradient(135deg, #FF5A5F 0%, #FF385C 100%);\n",
       "        color: white;\n",
       "        border-radius: 12px;\n",
       "        margin: 30px 0;\n",
       "        box-shadow: 0 10px 30px rgba(0,0,0,0.2);\n",
       "    '>\n",
       "        <h2 style='margin: 0 0 15px 0;'>🏠 Find the Cheapest Airbnb in Stockholm</h2>\n",
       "        <p style='margin: 0; font-size: 16px; line-height: 1.8;'>\n",
       "            This demo uses <strong>CDP (Chrome DevTools Protocol)</strong> integration:<br>\n",
       "            • <strong>Chrome</strong> runs with remote debugging enabled<br>\n",
       "            • <strong>Playwright</strong> connects to Chrome via CDP<br>\n",
       "            • <strong>Browser-Use</strong> connects to same Chrome via CDP<br>\n",
       "            • <strong>GPT-4 Vision</strong> reads and extracts prices<br>\n",
       "            • <strong>Structured Output</strong> returns type-safe data\n",
       "        </p>\n",
       "        <p style='margin: 15px 0 0 0; font-size: 14px; opacity: 0.9;'>\n",
       "            📊 Watch the browser as the AI agent searches and analyzes prices!\n",
       "        </p>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Chrome:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Starting Chrome with CDP (remote debugging)...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Chrome started with CDP on port 9222\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Playwright:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Connecting Playwright to Chrome via CDP...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Playwright connected successfully</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Browser-Use:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Creating Browser-Use agent with CDP connection...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Agent initialized with CDP integration</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0; \n",
       "        padding: 20px; \n",
       "        border-left: 5px solid #2E8B57; \n",
       "        background: linear-gradient(to right, rgba(46, 139, 87, 0.05), transparent); \n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <h3 style='color: #2E8B57; margin: 0 0 10px 0;'>\n",
       "            Step 1: Navigate & Search (AI Agent)\n",
       "        </h3>\n",
       "        <p style='margin: 0; line-height: 1.6; color: #333;'>Using AI agent with vision to navigate Airbnb and search for Stockholm listings. The agent will handle pop-ups, cookie banners, and search automatically.</p>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO     [Agent] 🔗 Found URL in task: https://www.airbnb.com, adding as initial action...\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Agent:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Navigating to Airbnb and searching for Stockholm...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO     [Agent] \u001b[34m🚀 Task: Navigate to https://www.airbnb.com. Close any pop-ups, cookie banners, or login prompts if they appear. Search for 'Stockholm, Sweden' in the search box. Wait for the search results page to fully load with listing cards visible.\u001b[0m\n",
      "INFO     [service] ────────────────────────────────────────\n",
      "INFO     [service] 🔐 To view this run in Browser Use Cloud, authenticate with:\n",
      "INFO     [service]     👉  browser-use auth\n",
      "INFO     [service]     or: python -m browser_use.cli auth\n",
      "INFO     [service] ────────────────────────────────────────\n",
      "\n",
      "INFO     [Agent]   🦾 \u001b[34m[ACTION 1/1]\u001b[0m go_to_url: url: https://www.airbnb.com, new_tab: False\n",
      "INFO     [tools] 🔗 Navigated to https://www.airbnb.com\n",
      "INFO     [Agent] \n",
      "\n",
      "INFO     [Agent] 📍 Step 1:\n",
      "ERROR    [Agent] ❌ Result failed 1/4 times:\n",
      " LLM call timed out after 60 seconds. Keep your thinking and output short.\n",
      "INFO     [Agent] \n",
      "\n",
      "INFO     [Agent] 📍 Step 2:\n",
      "INFO     [Agent]   \u001b[32m👍 Eval: Successfully loaded Airbnb homepage and identified the search input box for destination.\u001b[0m\n",
      "INFO     [Agent]   \u001b[34m🎯 Next goal: Input 'Stockholm, Sweden' into the destination search box and click the search button to load results.\u001b[0m\n",
      "INFO     [Agent]   🦾 \u001b[34m[ACTION 1/2]\u001b[0m input_text: index: 27, text: Stockholm, Sweden, clear_existing: True\n",
      "WARNING  [BrowserSession] ⚠️ Some framework events may have failed to trigger\n",
      "INFO     [BrowserSession] ⌨️ Typed \"Stockholm, Sweden\" into element with index 27\n",
      "INFO     [service] Page changed after action: actions click_element_by_index are not yet executed\n",
      "INFO     [Agent] \n",
      "\n",
      "INFO     [Agent] 📍 Step 3:\n",
      "INFO     [Agent]   \u001b[32m👍 Eval: Successfully input 'Stockholm, Sweden' into the destination search box but did not yet click the search button to load results.\u001b[0m\n",
      "INFO     [Agent]   \u001b[34m🎯 Next goal: Click the search button to perform the search and wait for the listings page to load with visible listing cards.\u001b[0m\n",
      "INFO     [Agent]   🦾 \u001b[34m[ACTION 1/1]\u001b[0m click_element_by_index: index: 34, while_holding_ctrl: False\n",
      "INFO     [tools] 🖱️ Clicked element\n",
      "INFO     [Agent] \n",
      "\n",
      "INFO     [Agent] 📍 Step 4:\n",
      "INFO     [Agent]   \u001b[32m👍 Eval: Successfully clicked the search button and loaded the Airbnb search results page with visible listing cards for Stockholm.\u001b[0m\n",
      "INFO     [Agent]   \u001b[34m🎯 Next goal: Confirm no pop-ups or cookie banners are present and finalize task as completed since listings are visible.\u001b[0m\n",
      "INFO     [Agent]   🦾 \u001b[34m[ACTION 1/1]\u001b[0m done: text: I have navigated to Airbnb.com, searched for Stockholm, Sweden, and confirmed that the search results page is fully loa\n",
      "INFO     [Agent] \n",
      "📄 \u001b[32m Final Result:\u001b[0m \n",
      "I have navigated to Airbnb.com, searched for 'Stockholm, Sweden', and confirmed that the search results page is fully loaded with listing cards visible. There were no pop-ups or cookie banners to close. Task completed successfully.\n",
      "\n",
      "\n",
      "INFO     [Agent] ✅ Task completed successfully\n",
      "⚠️  Could not capture screenshot: a bytes-like object is required, not 'str'\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Successfully loaded Stockholm search results</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0; \n",
       "        padding: 20px; \n",
       "        border-left: 5px solid #2E8B57; \n",
       "        background: linear-gradient(to right, rgba(46, 139, 87, 0.05), transparent); \n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <h3 style='color: #2E8B57; margin: 0 0 10px 0;'>\n",
       "            Step 2: Extract Prices (Vision + LLM)\n",
       "        </h3>\n",
       "        <p style='margin: 0; line-height: 1.6; color: #333;'>Using GPT-4 Vision to read all listing prices from the page and extract structured data into Pydantic models. The AI 'sees' the page like a human.</p>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Vision:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>AI is analyzing the page and reading prices...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Extracted:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Found 18 listings with prices</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Sample Prices:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>1066 SEK, 294 SEK, 294 SEK, 1008 SEK, 368 SEK...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Price extraction completed successfully</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<hr style='margin: 40px 0; border: none; border-top: 2px solid #ddd;'>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <div style='padding: 20px; background: #f8f9fa; border-radius: 8px; margin: 20px 0;'>\n",
       "            <h3 style='margin: 0 0 15px 0; color: #333;'>📊 Search Results</h3>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <div style='margin: 20px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'>\n",
       "            <h4 style='margin: 0 0 15px 0; color: #FF5A5F;'>📈 Price Analysis</h4>\n",
       "            <table style='width: 100%; border-collapse: collapse;'>\n",
       "                <tr>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Location:</td>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>Stockholm, Sweden</td>\n",
       "                </tr>\n",
       "                <tr>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Total Listings Found:</td>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>18</td>\n",
       "                </tr>\n",
       "                <tr>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Average Price:</td>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>572.72 SEK/night</td>\n",
       "                </tr>\n",
       "                <tr>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Price Range:</td>\n",
       "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>257 - 1066 SEK</td>\n",
       "                </tr>\n",
       "            </table>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <div style='\n",
       "            margin: 20px; \n",
       "            padding: 25px; \n",
       "            background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);\n",
       "            border-radius: 12px; \n",
       "            box-shadow: 0 4px 12px rgba(255,165,0,0.3);\n",
       "            border: 3px solid #FFD700;\n",
       "        '>\n",
       "            <h3 style='margin: 0 0 15px 0; color: #333; font-size: 24px;'>\n",
       "                🏆 CHEAPEST AIRBNB IN STOCKHOLM\n",
       "            </h3>\n",
       "            <div style='background: white; padding: 20px; border-radius: 8px; margin-top: 15px;'>\n",
       "                <h4 style='margin: 0 0 10px 0; color: #FF5A5F;'>Room in Nacka</h4>\n",
       "                <p style='margin: 10px 0; font-size: 28px; font-weight: bold; color: #28a745;'>\n",
       "                    257.00 SEK/night\n",
       "                </p>\n",
       "                <p style='margin: 10px 0; color: #666;'>⭐ Rating: 4.84/5.0</p>\n",
       "                <p style='margin: 15px 0 0 0; color: #666; font-size: 14px;'>\n",
       "                    💰 Saves you <strong>315.72 SEK</strong> compared to average price!\n",
       "                </p>\n",
       "                \n",
       "            <a href=\"https://www.airbnb.com/rooms/528377\" target=\"_blank\" style=\"\n",
       "                display: inline-block;\n",
       "                margin-top: 15px;\n",
       "                padding: 12px 24px;\n",
       "                background: #FF5A5F;\n",
       "                color: white;\n",
       "                text-decoration: none;\n",
       "                border-radius: 8px;\n",
       "                font-weight: bold;\n",
       "                transition: background 0.3s;\n",
       "            \" onmouseover=\"this.style.background='#E00007'\" onmouseout=\"this.style.background='#FF5A5F'\">\n",
       "                🔗 View Listing on Airbnb\n",
       "            </a>\n",
       "            \n",
       "            </div>\n",
       "        </div>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "            <div style='margin: 20px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'>\n",
       "                <h4 style='margin: 0 0 15px 0; color: #FF5A5F;'>📋 All Listings Found (Click to View)</h4>\n",
       "                <table style='width: 100%; border-collapse: collapse;'>\n",
       "                    <thead>\n",
       "                        <tr style='background: #f8f9fa;'>\n",
       "                            <th style='padding: 12px; text-align: left; border-bottom: 2px solid #dee2e6;'>Rank</th>\n",
       "                            <th style='padding: 12px; text-align: left; border-bottom: 2px solid #dee2e6;'>Listing</th>\n",
       "                            <th style='padding: 12px; text-align: right; border-bottom: 2px solid #dee2e6;'>Price/Night</th>\n",
       "                            <th style='padding: 12px; text-align: center; border-bottom: 2px solid #dee2e6;'>Rating</th>\n",
       "                            <th style='padding: 12px; text-align: center; border-bottom: 2px solid #dee2e6;'>Link</th>\n",
       "                        </tr>\n",
       "                    </thead>\n",
       "                    <tbody>\n",
       "            \n",
       "                    <tr style='background: #fff3cd;'>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>🏆 1</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/528377\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Room in Nacka...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            257.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.84\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/528377\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>2</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1325170162426570770\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared hotel room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            258.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.42\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1325170162426570770\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>3</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1076079717724966711\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            274.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.68\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1076079717724966711\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>4</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1325174269976835966\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared hotel room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            282.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.6\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1325174269976835966\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>5</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1273965753087063640\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            294.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.76\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1273965753087063640\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>6</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1433962995093313189\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared hotel room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            294.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.62\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1433962995093313189\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>7</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/664399747581611456\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            317.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.93\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/664399747581611456\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>8</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1497178259494952182\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Shared hotel room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            352.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            N/A\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1497178259494952182\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>9</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1458105060876881238\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Room in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            353.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 5.0\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1458105060876881238\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>10</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1295414809162007319\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Rooms in Enskede - Årsta - Vantör...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            365.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.76\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1295414809162007319\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>11</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/36783661\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Hostel in Norrmalm...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            368.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.46\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/36783661\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>12</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/939866565076342944\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Room in Helenelund...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            383.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.87\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/939866565076342944\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>13</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/48073646\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Sjöberg...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            851.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.29\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/48073646\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>14</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1025922981385700870\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Solna kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            929.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.89\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1025922981385700870\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>15</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1515443925860201423\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            939.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            N/A\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1515443925860201423\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>16</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1460063798046056638\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            966.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.37\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1460063798046056638\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>17</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1485494362111814492\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            1008.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.79\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1485494362111814492\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    <tr style=''>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>18</td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'><a href=\"https://www.airbnb.com/rooms/1429518738216103790\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color='#FF5A5F'\" onmouseout=\"this.style.color='#333'\">Apartment in Stockholms kommun...</a></td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
       "                            1066.00 SEK\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            ⭐ 4.77\n",
       "                        </td>\n",
       "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
       "                            <a href=\"https://www.airbnb.com/rooms/1429518738216103790\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background='#FF5A5F'; this.style.color='white'\" onmouseout=\"this.style.background='transparent'; this.style.color='#FF5A5F'\">🔗 View</a>\n",
       "                        </td>\n",
       "                    </tr>\n",
       "                \n",
       "                    </tbody>\n",
       "                </table>\n",
       "                <p style='margin-top: 15px; color: #666; font-size: 13px; font-style: italic;'>\n",
       "                    💡 Tip: Click on any listing title or the \"View\" button to open it in a new tab\n",
       "                </p>\n",
       "            </div>\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Price comparison completed successfully!</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 10px 20px;\n",
       "        padding: 12px 16px;\n",
       "        background: rgba(0, 123, 255, 0.05);\n",
       "        border: 1px solid #007BFF;\n",
       "        border-radius: 6px;\n",
       "        font-family: monospace;\n",
       "        font-size: 14px;\n",
       "    '>\n",
       "        <strong style='color: #007BFF;'>⚙️ Cleanup:</strong>\n",
       "        <span style='color: #555; margin-left: 10px;'>Closing browser and cleaning up...</span>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING  [cdp_use.client] WebSocket connection closed: no close frame received or sent\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 20px 0;\n",
       "        padding: 15px 20px;\n",
       "        border-left: 5px solid #28a745;\n",
       "        background: rgba(40, 167, 69, 0.1);\n",
       "        border-radius: 8px;\n",
       "    '>\n",
       "        <strong style='color: #28a745; font-size: 16px;'>✅ Cleanup complete</strong>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div style='\n",
       "        margin: 40px 0 20px 0;\n",
       "        padding: 25px;\n",
       "        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n",
       "        color: white;\n",
       "        border-radius: 12px;\n",
       "    '>\n",
       "        <h3 style='margin: 0 0 15px 0;'>🎓 What You Just Learned</h3>\n",
       "        <ul style='margin: 0; padding-left: 20px; line-height: 2;'>\n",
       "            <li><strong>CDP Integration:</strong> Chrome DevTools Protocol connects Playwright + Browser-Use</li>\n",
       "            <li><strong>Vision-Based Extraction:</strong> GPT-4 Vision reads prices like a human</li>\n",
       "            <li><strong>Structured Output:</strong> Type-safe data extraction with Pydantic models</li>\n",
       "            <li><strong>Price Comparison:</strong> Automated analysis to find best deals</li>\n",
       "            <li><strong>Clickable URLs:</strong> Direct links to Airbnb listings for easy viewing</li>\n",
       "            <li><strong>Real-World Application:</strong> Practical web scraping for price monitoring</li>\n",
       "        </ul>\n",
       "    </div>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import subprocess\n",
    "import tempfile\n",
    "\n",
    "\n",
    "async def start_chrome_with_cdp(port: int = 9222):\n",
    "    \"\"\"\n",
    "    Start Chrome with CDP (Chrome DevTools Protocol) enabled.\n",
    "    Returns the Chrome process.\n",
    "    \"\"\"\n",
    "    # Create temporary directory for Chrome user data\n",
    "    user_data_dir = tempfile.mkdtemp(prefix='chrome_cdp_')\n",
    "\n",
    "    # Chrome paths for different platforms\n",
    "    chrome_paths = [\n",
    "        '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',  # macOS\n",
    "        '/usr/bin/google-chrome',  # Linux\n",
    "        '/usr/bin/chromium-browser',  # Linux Chromium\n",
    "        'chrome',  # Windows/PATH\n",
    "        'chromium',  # Generic\n",
    "    ]\n",
    "\n",
    "    chrome_exe = None\n",
    "    for path in chrome_paths:\n",
    "        if os.path.exists(path) or path in ['chrome', 'chromium']:\n",
    "            try:\n",
    "                test_proc = await asyncio.create_subprocess_exec(\n",
    "                    path, '--version',\n",
    "                    stdout=subprocess.DEVNULL,\n",
    "                    stderr=subprocess.DEVNULL\n",
    "                )\n",
    "                await test_proc.wait()\n",
    "                chrome_exe = path\n",
    "                break\n",
    "            except Exception:\n",
    "                continue\n",
    "\n",
    "    if not chrome_exe:\n",
    "        raise RuntimeError(\n",
    "            '❌ Chrome not found. Please install Chrome or Chromium.')\n",
    "\n",
    "    # Chrome command arguments\n",
    "    cmd = [\n",
    "        chrome_exe,\n",
    "        f'--remote-debugging-port={port}',\n",
    "        f'--user-data-dir={user_data_dir}',\n",
    "        '--no-first-run',\n",
    "        '--no-default-browser-check',\n",
    "        'about:blank',\n",
    "    ]\n",
    "\n",
    "    # Start Chrome process\n",
    "    process = await asyncio.create_subprocess_exec(\n",
    "        *cmd,\n",
    "        stdout=subprocess.DEVNULL,\n",
    "        stderr=subprocess.DEVNULL\n",
    "    )\n",
    "\n",
    "    # Wait for Chrome to start and CDP to be ready\n",
    "    import aiohttp\n",
    "    cdp_ready = False\n",
    "    for _ in range(20):  # 20 second timeout\n",
    "        try:\n",
    "            async with aiohttp.ClientSession() as session:\n",
    "                async with session.get(\n",
    "                    f'http://localhost:{port}/json/version',\n",
    "                    timeout=aiohttp.ClientTimeout(total=1)\n",
    "                ) as response:\n",
    "                    if response.status == 200:\n",
    "                        cdp_ready = True\n",
    "                        break\n",
    "        except Exception:\n",
    "            pass\n",
    "        await asyncio.sleep(1)\n",
    "\n",
    "    if not cdp_ready:\n",
    "        process.terminate()\n",
    "        raise RuntimeError('❌ Chrome failed to start with CDP')\n",
    "\n",
    "    print(f\"✅ Chrome started with CDP on port {port}\")\n",
    "    return process\n",
    "\n",
    "\n",
    "async def main():\n",
    "    \"\"\"\n",
    "    Main execution function for Airbnb price comparison.\n",
    "    \n",
    "    Uses CDP to connect both Playwright and Browser-Use to the same Chrome instance.\n",
    "    \"\"\"\n",
    "\n",
    "    display(HTML(\"\"\"\n",
    "    <div style='\n",
    "        padding: 30px;\n",
    "        background: linear-gradient(135deg, #FF5A5F 0%, #FF385C 100%);\n",
    "        color: white;\n",
    "        border-radius: 12px;\n",
    "        margin: 30px 0;\n",
    "        box-shadow: 0 10px 30px rgba(0,0,0,0.2);\n",
    "    '>\n",
    "        <h2 style='margin: 0 0 15px 0;'>🏠 Find the Cheapest Airbnb in Stockholm</h2>\n",
    "        <p style='margin: 0; font-size: 16px; line-height: 1.8;'>\n",
    "            This demo uses <strong>CDP (Chrome DevTools Protocol)</strong> integration:<br>\n",
    "            • <strong>Chrome</strong> runs with remote debugging enabled<br>\n",
    "            • <strong>Playwright</strong> connects to Chrome via CDP<br>\n",
    "            • <strong>Browser-Use</strong> connects to same Chrome via CDP<br>\n",
    "            • <strong>GPT-4 Vision</strong> reads and extracts prices<br>\n",
    "            • <strong>Structured Output</strong> returns type-safe data\n",
    "        </p>\n",
    "        <p style='margin: 15px 0 0 0; font-size: 14px; opacity: 0.9;'>\n",
    "            📊 Watch the browser as the AI agent searches and analyzes prices!\n",
    "        </p>\n",
    "    </div>\n",
    "    \"\"\"))\n",
    "\n",
    "    chrome_process = None\n",
    "    playwright_browser = None\n",
    "\n",
    "    try:\n",
    "        # Step 1: Start Chrome with CDP\n",
    "        display_action(\n",
    "            \"Chrome\", \"Starting Chrome with CDP (remote debugging)...\")\n",
    "        chrome_process = await start_chrome_with_cdp(port=9222)\n",
    "        cdp_url = 'http://localhost:9222'\n",
    "\n",
    "        # Step 2: Connect Playwright to CDP (optional - for custom Playwright actions)\n",
    "        display_action(\n",
    "            \"Playwright\", \"Connecting Playwright to Chrome via CDP...\")\n",
    "        playwright = await async_playwright().start()\n",
    "        playwright_browser = await playwright.chromium.connect_over_cdp(cdp_url)\n",
    "        display_result(True, \"Playwright connected successfully\")\n",
    "\n",
    "        # Step 3: Create Browser-Use agent with CDP connection\n",
    "        display_action(\n",
    "            \"Browser-Use\", \"Creating Browser-Use agent with CDP connection...\")\n",
    "        agent = AirbnbSearchAgent(llm=llm, cdp_url=cdp_url)\n",
    "        display_result(True, \"Agent initialized with CDP integration\")\n",
    "\n",
    "        # Step 4: Search and extract prices\n",
    "        result = await agent.search_stockholm()\n",
    "\n",
    "        # Step 5: Display results\n",
    "        display(\n",
    "            HTML(\"<hr style='margin: 40px 0; border: none; border-top: 2px solid #ddd;'>\"))\n",
    "\n",
    "        display(HTML(\"\"\"\n",
    "        <div style='padding: 20px; background: #f8f9fa; border-radius: 8px; margin: 20px 0;'>\n",
    "            <h3 style='margin: 0 0 15px 0; color: #333;'>📊 Search Results</h3>\n",
    "        </div>\n",
    "        \"\"\"))\n",
    "\n",
    "        # Display summary stats\n",
    "        display(HTML(f\"\"\"\n",
    "        <div style='margin: 20px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'>\n",
    "            <h4 style='margin: 0 0 15px 0; color: #FF5A5F;'>📈 Price Analysis</h4>\n",
    "            <table style='width: 100%; border-collapse: collapse;'>\n",
    "                <tr>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Location:</td>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>{result.location}</td>\n",
    "                </tr>\n",
    "                <tr>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Total Listings Found:</td>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>{result.total_listings_found}</td>\n",
    "                </tr>\n",
    "                <tr>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Average Price:</td>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>{result.average_price:.2f} SEK/night</td>\n",
    "                </tr>\n",
    "                <tr>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee; font-weight: bold;'>Price Range:</td>\n",
    "                    <td style='padding: 10px; border-bottom: 1px solid #eee;'>{result.price_range}</td>\n",
    "                </tr>\n",
    "            </table>\n",
    "        </div>\n",
    "        \"\"\"))\n",
    "\n",
    "        # Display the CHEAPEST listing with clickable link\n",
    "        cheapest = result.cheapest_listing\n",
    "\n",
    "        # Create View Listing button if URL exists\n",
    "        view_button = \"\"\n",
    "        if cheapest.url:\n",
    "            view_button = f\"\"\"\n",
    "            <a href=\"{cheapest.url}\" target=\"_blank\" style=\"\n",
    "                display: inline-block;\n",
    "                margin-top: 15px;\n",
    "                padding: 12px 24px;\n",
    "                background: #FF5A5F;\n",
    "                color: white;\n",
    "                text-decoration: none;\n",
    "                border-radius: 8px;\n",
    "                font-weight: bold;\n",
    "                transition: background 0.3s;\n",
    "            \" onmouseover=\"this.style.background='#E00007'\" onmouseout=\"this.style.background='#FF5A5F'\">\n",
    "                🔗 View Listing on Airbnb\n",
    "            </a>\n",
    "            \"\"\"\n",
    "\n",
    "        display(HTML(f\"\"\"\n",
    "        <div style='\n",
    "            margin: 20px; \n",
    "            padding: 25px; \n",
    "            background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);\n",
    "            border-radius: 12px; \n",
    "            box-shadow: 0 4px 12px rgba(255,165,0,0.3);\n",
    "            border: 3px solid #FFD700;\n",
    "        '>\n",
    "            <h3 style='margin: 0 0 15px 0; color: #333; font-size: 24px;'>\n",
    "                🏆 CHEAPEST AIRBNB IN STOCKHOLM\n",
    "            </h3>\n",
    "            <div style='background: white; padding: 20px; border-radius: 8px; margin-top: 15px;'>\n",
    "                <h4 style='margin: 0 0 10px 0; color: #FF5A5F;'>{cheapest.title}</h4>\n",
    "                <p style='margin: 10px 0; font-size: 28px; font-weight: bold; color: #28a745;'>\n",
    "                    {cheapest.price_per_night:.2f} {cheapest.currency}/night\n",
    "                </p>\n",
    "                {f\"<p style='margin: 10px 0; color: #666;'>⭐ Rating: {cheapest.rating}/5.0</p>\" if cheapest.rating else \"\"}\n",
    "                <p style='margin: 15px 0 0 0; color: #666; font-size: 14px;'>\n",
    "                    💰 Saves you <strong>{(result.average_price - cheapest.price_per_night):.2f} SEK</strong> compared to average price!\n",
    "                </p>\n",
    "                {view_button}\n",
    "            </div>\n",
    "        </div>\n",
    "        \"\"\"))\n",
    "\n",
    "        # Display all listings in a clickable table\n",
    "        if len(result.listings) > 1:\n",
    "            listings_html = \"\"\"\n",
    "            <div style='margin: 20px; padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'>\n",
    "                <h4 style='margin: 0 0 15px 0; color: #FF5A5F;'>📋 All Listings Found (Click to View)</h4>\n",
    "                <table style='width: 100%; border-collapse: collapse;'>\n",
    "                    <thead>\n",
    "                        <tr style='background: #f8f9fa;'>\n",
    "                            <th style='padding: 12px; text-align: left; border-bottom: 2px solid #dee2e6;'>Rank</th>\n",
    "                            <th style='padding: 12px; text-align: left; border-bottom: 2px solid #dee2e6;'>Listing</th>\n",
    "                            <th style='padding: 12px; text-align: right; border-bottom: 2px solid #dee2e6;'>Price/Night</th>\n",
    "                            <th style='padding: 12px; text-align: center; border-bottom: 2px solid #dee2e6;'>Rating</th>\n",
    "                            <th style='padding: 12px; text-align: center; border-bottom: 2px solid #dee2e6;'>Link</th>\n",
    "                        </tr>\n",
    "                    </thead>\n",
    "                    <tbody>\n",
    "            \"\"\"\n",
    "\n",
    "            sorted_listings = sorted(\n",
    "                result.listings, key=lambda x: x.price_per_night)\n",
    "\n",
    "            for idx, listing in enumerate(sorted_listings, 1):\n",
    "                is_cheapest = listing.price_per_night == cheapest.price_per_night\n",
    "                row_style = \"background: #fff3cd;\" if is_cheapest else \"\"\n",
    "                badge = \"🏆 \" if is_cheapest else \"\"\n",
    "\n",
    "                # Create clickable link if URL exists\n",
    "                if listing.url:\n",
    "                    link_html = f'<a href=\"{listing.url}\" target=\"_blank\" style=\"color: #FF5A5F; text-decoration: none; font-weight: bold; padding: 6px 12px; border: 1px solid #FF5A5F; border-radius: 4px; transition: all 0.3s;\" onmouseover=\"this.style.background=\\'#FF5A5F\\'; this.style.color=\\'white\\'\" onmouseout=\"this.style.background=\\'transparent\\'; this.style.color=\\'#FF5A5F\\'\">🔗 View</a>'\n",
    "                    title_html = f'<a href=\"{listing.url}\" target=\"_blank\" style=\"color: #333; text-decoration: none; font-weight: 500;\" onmouseover=\"this.style.color=\\'#FF5A5F\\'\" onmouseout=\"this.style.color=\\'#333\\'\">{listing.title[:50]}...</a>'\n",
    "                else:\n",
    "                    link_html = '<span style=\"color: #999;\">N/A</span>'\n",
    "                    title_html = f'{listing.title[:50]}...'\n",
    "\n",
    "                listings_html += f\"\"\"\n",
    "                    <tr style='{row_style}'>\n",
    "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>{badge}{idx}</td>\n",
    "                        <td style='padding: 10px; border-bottom: 1px solid #eee;'>{title_html}</td>\n",
    "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: right; font-weight: bold;'>\n",
    "                            {listing.price_per_night:.2f} {listing.currency}\n",
    "                        </td>\n",
    "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
    "                            {f\"⭐ {listing.rating}\" if listing.rating else \"N/A\"}\n",
    "                        </td>\n",
    "                        <td style='padding: 10px; border-bottom: 1px solid #eee; text-align: center;'>\n",
    "                            {link_html}\n",
    "                        </td>\n",
    "                    </tr>\n",
    "                \"\"\"\n",
    "\n",
    "            listings_html += \"\"\"\n",
    "                    </tbody>\n",
    "                </table>\n",
    "                <p style='margin-top: 15px; color: #666; font-size: 13px; font-style: italic;'>\n",
    "                    💡 Tip: Click on any listing title or the \"View\" button to open it in a new tab\n",
    "                </p>\n",
    "            </div>\n",
    "            \"\"\"\n",
    "\n",
    "            display(HTML(listings_html))\n",
    "\n",
    "        display_result(True, \"Price comparison completed successfully!\")\n",
    "\n",
    "    except Exception as e:\n",
    "        display_result(False, f\"Error during search: {str(e)}\")\n",
    "        import traceback\n",
    "        print(traceback.format_exc())\n",
    "\n",
    "    finally:\n",
    "        # Cleanup\n",
    "        display_action(\"Cleanup\", \"Closing browser and cleaning up...\")\n",
    "\n",
    "        if playwright_browser:\n",
    "            await playwright_browser.close()\n",
    "\n",
    "        if chrome_process:\n",
    "            chrome_process.terminate()\n",
    "            try:\n",
    "                await asyncio.wait_for(chrome_process.wait(), 5)\n",
    "            except TimeoutError:\n",
    "                chrome_process.kill()\n",
    "\n",
    "        display_result(True, \"Cleanup complete\")\n",
    "\n",
    "    # Display educational summary\n",
    "    display(HTML(\"\"\"\n",
    "    <div style='\n",
    "        margin: 40px 0 20px 0;\n",
    "        padding: 25px;\n",
    "        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n",
    "        color: white;\n",
    "        border-radius: 12px;\n",
    "    '>\n",
    "        <h3 style='margin: 0 0 15px 0;'>🎓 What You Just Learned</h3>\n",
    "        <ul style='margin: 0; padding-left: 20px; line-height: 2;'>\n",
    "            <li><strong>CDP Integration:</strong> Chrome DevTools Protocol connects Playwright + Browser-Use</li>\n",
    "            <li><strong>Vision-Based Extraction:</strong> GPT-4 Vision reads prices like a human</li>\n",
    "            <li><strong>Structured Output:</strong> Type-safe data extraction with Pydantic models</li>\n",
    "            <li><strong>Price Comparison:</strong> Automated analysis to find best deals</li>\n",
    "            <li><strong>Clickable URLs:</strong> Direct links to Airbnb listings for easy viewing</li>\n",
    "            <li><strong>Real-World Application:</strong> Practical web scraping for price monitoring</li>\n",
    "        </ul>\n",
    "    </div>\n",
    "    \"\"\"))\n",
    "\n",
    "# Run the demo\n",
    "await main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "644c431e",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "1ef2945b",
   "metadata": {},
   "source": [
    "## Key Takeaways and Best Practices\n",
    "\n",
    "### When to Use Agent vs Actor\n",
    "\n",
    "| Scenario | Use Agent | Use Actor |\n",
    "|----------|-----------|-----------|\n",
    "| **Dynamic layouts** | ✅ AI adapts to changes | ❌ CSS selectors break |\n",
    "| **Known structure** | ❌ Slower than direct control | ✅ Fast and precise |\n",
    "| **Finding elements** | ✅ Natural language queries | ❌ Need exact selectors |\n",
    "| **Timing control** | ❌ Less predictable | ✅ Full timing control |\n",
    "| **Complex workflows** | ✅ Handles unexpected UI | ❌ Requires explicit code |\n",
    "\n",
    "### Browser-Use Best Practices\n",
    "\n",
    "1. **Start with Agent for exploration**: Let AI navigate complex sites first\n",
    "2. **Switch to Actor for precision**: Use CSS selectors for predictable elements\n",
    "3. **Always handle errors**: Websites change—build fallback strategies\n",
    "4. **Use structured output**: Pydantic models ensure type-safe data\n",
    "5. **Add delays strategically**: Use `asyncio.sleep()` after actions that trigger changes\n",
    "6. **Take screenshots**: Visual debugging is invaluable\n",
    "7. **Combine approaches**: Hybrid workflows leverage strengths of both paradigms\n",
    "\n",
    "### Real-World Applications\n",
    "\n",
    "- **Travel Booking**: Monitor prices, auto-book deals, compare options\n",
    "- **E-commerce**: Track inventory, compare prices, automated purchasing\n",
    "- **Data Collection**: Scrape dynamic sites, extract structured data\n",
    "- **Testing**: Automated UI testing with vision-based verification\n",
    "- **Monitoring**: Check website changes, alert on specific conditions\n",
    "- **Form Automation**: Fill complex multi-step forms intelligently"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv (3.12.11)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
