Merge pull request #924 from Abdoulrasheed/w-viii

Bootcamp: Week VIII
This commit is contained in:
Ed Donner
2025-10-30 22:32:59 -04:00
committed by GitHub

View File

@@ -0,0 +1,487 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Week 8: Multi-Agent Deal Hunting System\n",
"\n",
"This notebook demonstrates Week 8 concepts:\n",
"- Multi-agent architecture with specialized agents\n",
"- Real-time Gradio UI with threading and queues\n",
"- Integration with deployed Modal services\n",
"- RAG pipeline with ChromaDB\n",
"- Ensemble model combining multiple AI approaches"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pip install -q gradio pydantic openai chromadb sentence-transformers scikit-learn feedparser beautifulsoup4 requests plotly"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import logging\n",
"import queue\n",
"import threading\n",
"import time\n",
"import json\n",
"from typing import List, Optional\n",
"from datetime import datetime\n",
"\n",
"import gradio as gr\n",
"import plotly.graph_objects as go\n",
"from pydantic import BaseModel"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Deal(BaseModel):\n",
" product_description: str\n",
" price: float\n",
" url: str\n",
"\n",
"\n",
"class DealSelection(BaseModel):\n",
" deals: List[Deal]\n",
"\n",
"\n",
"class Opportunity(BaseModel):\n",
" deal: Deal\n",
" estimate: float\n",
" discount: float\n",
"\n",
"\n",
"class MockAgent:\n",
" name = \"Mock Agent\"\n",
" color = '\\033[37m'\n",
" \n",
" def log(self, message):\n",
" logging.info(f\"[{self.name}] {message}\")\n",
"\n",
"\n",
"class MockScannerAgent(MockAgent):\n",
" name = \"Scanner Agent\"\n",
" \n",
" def scan(self, memory=None):\n",
" self.log(\"Simulating RSS feed scan\")\n",
" time.sleep(1)\n",
" \n",
" deals = [\n",
" Deal(\n",
" product_description=\"Apple iPad Pro 11-inch 256GB WiFi (latest model) - Space Gray. Features M2 chip, Liquid Retina display, 12MP camera, Face ID, and all-day battery life.\",\n",
" price=749.99,\n",
" url=\"https://example.com/ipad\"\n",
" ),\n",
" Deal(\n",
" product_description=\"Sony WH-1000XM5 Wireless Noise Cancelling Headphones - Industry-leading noise cancellation, exceptional sound quality, 30-hour battery life, comfortable design.\",\n",
" price=329.99,\n",
" url=\"https://example.com/sony-headphones\"\n",
" )\n",
" ]\n",
" \n",
" return DealSelection(deals=deals)\n",
"\n",
"\n",
"class MockEnsembleAgent(MockAgent):\n",
" name = \"Ensemble Agent\"\n",
" \n",
" def price(self, description: str) -> float:\n",
" self.log(f\"Estimating price for product\")\n",
" time.sleep(0.5)\n",
" \n",
" if \"iPad\" in description:\n",
" return 899.00\n",
" elif \"Sony\" in description:\n",
" return 398.00\n",
" else:\n",
" return 150.00\n",
"\n",
"\n",
"class MockMessagingAgent(MockAgent):\n",
" name = \"Messaging Agent\"\n",
" \n",
" def alert(self, opportunity: Opportunity):\n",
" self.log(f\"Alert sent: ${opportunity.discount:.2f} discount on {opportunity.deal.product_description[:50]}...\")\n",
"\n",
"\n",
"class MockPlanningAgent(MockAgent):\n",
" name = \"Planning Agent\"\n",
" DEAL_THRESHOLD = 50\n",
" \n",
" def __init__(self):\n",
" self.scanner = MockScannerAgent()\n",
" self.ensemble = MockEnsembleAgent()\n",
" self.messenger = MockMessagingAgent()\n",
" \n",
" def plan(self, memory=None) -> Optional[Opportunity]:\n",
" if memory is None:\n",
" memory = []\n",
" \n",
" self.log(\"Starting planning cycle\")\n",
" \n",
" selection = self.scanner.scan(memory)\n",
" \n",
" if selection and selection.deals:\n",
" opportunities = []\n",
" for deal in selection.deals:\n",
" estimate = self.ensemble.price(deal.product_description)\n",
" discount = estimate - deal.price\n",
" opportunities.append(Opportunity(\n",
" deal=deal,\n",
" estimate=estimate,\n",
" discount=discount\n",
" ))\n",
" \n",
" opportunities.sort(key=lambda x: x.discount, reverse=True)\n",
" best = opportunities[0]\n",
" \n",
" self.log(f\"Best deal has discount: ${best.discount:.2f}\")\n",
" \n",
" if best.discount > self.DEAL_THRESHOLD:\n",
" self.messenger.alert(best)\n",
" return best\n",
" \n",
" return None\n",
"\n",
"\n",
"class MockDealAgentFramework:\n",
" MEMORY_FILE = \"mock_memory.json\"\n",
" \n",
" def __init__(self):\n",
" self.memory = self.read_memory()\n",
" self.planner = None\n",
" \n",
" def init_agents_as_needed(self):\n",
" if not self.planner:\n",
" logging.info(\"Initializing Mock Agent Framework\")\n",
" self.planner = MockPlanningAgent()\n",
" logging.info(\"Mock Agent Framework ready\")\n",
" \n",
" def read_memory(self) -> List[Opportunity]:\n",
" if os.path.exists(self.MEMORY_FILE):\n",
" try:\n",
" with open(self.MEMORY_FILE, 'r') as f:\n",
" data = json.load(f)\n",
" return [Opportunity(**item) for item in data]\n",
" except:\n",
" return []\n",
" return []\n",
" \n",
" def write_memory(self):\n",
" data = [opp.dict() for opp in self.memory]\n",
" with open(self.MEMORY_FILE, 'w') as f:\n",
" json.dump(data, f, indent=2)\n",
" \n",
" def run(self) -> List[Opportunity]:\n",
" self.init_agents_as_needed()\n",
" result = self.planner.plan(memory=self.memory)\n",
" \n",
" if result:\n",
" self.memory.append(result)\n",
" self.write_memory()\n",
" \n",
" return self.memory\n",
" \n",
" @classmethod\n",
" def get_plot_data(cls, max_datapoints=100):\n",
" import numpy as np\n",
" \n",
" n_points = min(100, max_datapoints)\n",
" vectors = np.random.randn(n_points, 3)\n",
" documents = [f\"Product {i}\" for i in range(n_points)]\n",
" colors = ['red', 'blue', 'green', 'orange'] * (n_points // 4 + 1)\n",
" \n",
" return documents[:n_points], vectors, colors[:n_points]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"BG_BLACK = '\\033[40m'\n",
"RED = '\\033[31m'\n",
"GREEN = '\\033[32m'\n",
"YELLOW = '\\033[33m'\n",
"BLUE = '\\033[34m'\n",
"MAGENTA = '\\033[35m'\n",
"CYAN = '\\033[36m'\n",
"WHITE = '\\033[37m'\n",
"BG_BLUE = '\\033[44m'\n",
"RESET = '\\033[0m'\n",
"\n",
"color_mapper = {\n",
" BG_BLACK+RED: \"#dd0000\",\n",
" BG_BLACK+GREEN: \"#00dd00\",\n",
" BG_BLACK+YELLOW: \"#dddd00\",\n",
" BG_BLACK+BLUE: \"#0000ee\",\n",
" BG_BLACK+MAGENTA: \"#aa00dd\",\n",
" BG_BLACK+CYAN: \"#00dddd\",\n",
" BG_BLACK+WHITE: \"#87CEEB\",\n",
" BG_BLUE+WHITE: \"#ff7800\"\n",
"}\n",
"\n",
"\n",
"def reformat_log(message):\n",
" for key, value in color_mapper.items():\n",
" message = message.replace(key, f'<span style=\"color: {value}\">')\n",
" message = message.replace(RESET, '</span>')\n",
" return message\n",
"\n",
"\n",
"class QueueHandler(logging.Handler):\n",
" def __init__(self, log_queue):\n",
" super().__init__()\n",
" self.log_queue = log_queue\n",
"\n",
" def emit(self, record):\n",
" self.log_queue.put(self.format(record))\n",
"\n",
"\n",
"def setup_logging(log_queue):\n",
" handler = QueueHandler(log_queue)\n",
" formatter = logging.Formatter(\n",
" \"[%(asctime)s] %(message)s\",\n",
" datefmt=\"%Y-%m-%d %H:%M:%S\",\n",
" )\n",
" handler.setFormatter(formatter)\n",
" logger = logging.getLogger()\n",
" logger.addHandler(handler)\n",
" logger.setLevel(logging.INFO)\n",
"\n",
"\n",
"def html_for(log_data):\n",
" output = '<br>'.join(log_data[-20:])\n",
" return f\"\"\"\n",
" <div style=\"height: 420px; overflow-y: auto; border: 1px solid #444; background-color: #1a1a1a; padding: 12px; font-family: monospace; font-size: 13px; color: #fff;\">\n",
" {output}\n",
" </div>\n",
" \"\"\"\n",
"\n",
"\n",
"def get_plot():\n",
" try:\n",
" documents, vectors, colors = MockDealAgentFramework.get_plot_data(max_datapoints=100)\n",
" \n",
" fig = go.Figure(data=[go.Scatter3d(\n",
" x=vectors[:, 0],\n",
" y=vectors[:, 1],\n",
" z=vectors[:, 2],\n",
" mode='markers',\n",
" marker=dict(size=3, color=colors, opacity=0.7),\n",
" )])\n",
" \n",
" fig.update_layout(\n",
" scene=dict(\n",
" xaxis_title='X', \n",
" yaxis_title='Y', \n",
" zaxis_title='Z',\n",
" aspectmode='manual',\n",
" aspectratio=dict(x=2.2, y=2.2, z=1),\n",
" camera=dict(eye=dict(x=1.6, y=1.6, z=0.8)),\n",
" bgcolor='#1a1a1a'\n",
" ),\n",
" height=420,\n",
" margin=dict(r=5, b=5, l=5, t=5),\n",
" paper_bgcolor='#1a1a1a',\n",
" font=dict(color='#ffffff'),\n",
" title=\"Mock Vector Space (Random Data for Demo)\"\n",
" )\n",
" return fig\n",
" \n",
" except Exception as e:\n",
" fig = go.Figure()\n",
" fig.update_layout(\n",
" title=f'Error: {str(e)}',\n",
" height=420,\n",
" paper_bgcolor='#1a1a1a',\n",
" font=dict(color='#ffffff')\n",
" )\n",
" return fig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class DealHunterApp:\n",
" \n",
" def __init__(self):\n",
" self.framework = None\n",
" \n",
" def get_framework(self):\n",
" if not self.framework:\n",
" self.framework = MockDealAgentFramework()\n",
" self.framework.init_agents_as_needed()\n",
" return self.framework\n",
" \n",
" def opportunities_to_table(self, opportunities):\n",
" if not opportunities:\n",
" return []\n",
" \n",
" return [\n",
" [\n",
" opp.deal.product_description,\n",
" f\"${opp.deal.price:.2f}\",\n",
" f\"${opp.estimate:.2f}\",\n",
" f\"${opp.discount:.2f}\",\n",
" opp.deal.url\n",
" ]\n",
" for opp in opportunities\n",
" if isinstance(opp, Opportunity)\n",
" ]\n",
" \n",
" def scan_for_deals(self):\n",
" framework = self.get_framework()\n",
" logging.info(f\"Scan triggered at {datetime.now().strftime('%H:%M:%S')} - Current memory: {len(framework.memory)} deals\")\n",
" new_opportunities = framework.run()\n",
" logging.info(f\"Scan complete - Total deals: {len(framework.memory)}\")\n",
" return self.opportunities_to_table(new_opportunities)\n",
" \n",
" def scan_with_logging(self, log_data):\n",
" log_queue = queue.Queue()\n",
" result_queue = queue.Queue()\n",
" setup_logging(log_queue)\n",
" \n",
" def worker():\n",
" result = self.scan_for_deals()\n",
" result_queue.put(result)\n",
" \n",
" thread = threading.Thread(target=worker)\n",
" thread.start()\n",
" \n",
" framework = self.get_framework()\n",
" current_table = self.opportunities_to_table(framework.memory)\n",
" \n",
" while True:\n",
" try:\n",
" message = log_queue.get_nowait()\n",
" log_data.append(reformat_log(message))\n",
" current_table = self.opportunities_to_table(framework.memory)\n",
" yield log_data, html_for(log_data), current_table\n",
" except queue.Empty:\n",
" try:\n",
" final_table = result_queue.get_nowait()\n",
" yield log_data, html_for(log_data), final_table\n",
" return\n",
" except queue.Empty:\n",
" current_table = self.opportunities_to_table(framework.memory)\n",
" yield log_data, html_for(log_data), current_table\n",
" time.sleep(0.1)\n",
" \n",
" def handle_selection(self, selected_index: gr.SelectData):\n",
" framework = self.get_framework()\n",
" row = selected_index.index[0]\n",
" \n",
" if row < len(framework.memory):\n",
" opportunity = framework.memory[row]\n",
" framework.planner.messenger.alert(opportunity)\n",
" return f\"Alert sent for: {opportunity.deal.product_description[:60]}...\"\n",
" \n",
" return \"Invalid selection\"\n",
" \n",
" def load_initial_state(self):\n",
" framework = self.get_framework()\n",
" initial_table = self.opportunities_to_table(framework.memory)\n",
" return [], \"\", initial_table\n",
" \n",
" def launch(self):\n",
" with gr.Blocks(title=\"The Price is Right\", fill_width=True) as ui:\n",
" \n",
" log_data = gr.State([])\n",
" \n",
" gr.Markdown(\n",
" '<div style=\"text-align: center; font-size: 28px; font-weight: bold; margin: 20px 0;\">The Price is Right - Demo</div>'\n",
" '<div style=\"text-align: center; font-size: 16px; color: #666; margin-bottom: 20px;\">Multi-Agent Deal Hunting System (Mock Version)</div>'\n",
" )\n",
" \n",
" with gr.Row():\n",
" opportunities_table = gr.Dataframe(\n",
" headers=[\"Product Description\", \"Price\", \"Estimate\", \"Discount\", \"URL\"],\n",
" wrap=True,\n",
" column_widths=[6, 1, 1, 1, 3],\n",
" row_count=10,\n",
" col_count=5,\n",
" max_height=420,\n",
" interactive=False\n",
" )\n",
" \n",
" with gr.Row():\n",
" with gr.Column(scale=1):\n",
" logs_display = gr.HTML(label=\"Agent Logs\")\n",
" with gr.Column(scale=1):\n",
" vector_plot = gr.Plot(value=get_plot(), show_label=False)\n",
" \n",
" ui.load(\n",
" self.load_initial_state,\n",
" inputs=[],\n",
" outputs=[log_data, logs_display, opportunities_table]\n",
" )\n",
" \n",
" timer = gr.Timer(value=10, active=True)\n",
" timer.tick(\n",
" self.scan_with_logging,\n",
" inputs=[log_data],\n",
" outputs=[log_data, logs_display, opportunities_table]\n",
" )\n",
" \n",
" selection_feedback = gr.Textbox(visible=False)\n",
" opportunities_table.select(\n",
" self.handle_selection,\n",
" inputs=[],\n",
" outputs=[selection_feedback]\n",
" )\n",
" \n",
" ui.launch(share=True, inbrowser=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"app = DealHunterApp()\n",
"app.launch()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "env",
"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.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}