Files
LLM_Engineering_OLD/week2/community-contributions/day4-ecommerce-project-fullyCustomiz.ipynb

654 lines
26 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "markdown",
"id": "ddfa9ae6-69fe-444a-b994-8c4c5970a7ec",
"metadata": {},
"source": [
"# 🛠️ Project Art-Tech Store AI Assistant\n",
"\n",
"## 🛍️ Product Availability\n",
"- Check availability of **printers** and **printer papers** with:\n",
" - Product name, price, brand, type (e.g., laser/inkjet), and stock status.\n",
"- Alerts user if a product is out of stock.\n",
"\n",
"## 🧭 Guided Shopping Experience\n",
"- Guides users through:\n",
" 1. Choosing product category (printer or paper)\n",
" 2. Filtering options (brand, price range, type)\n",
" 3. Adding selected products to cart\n",
"- Ensures correct input for smooth shopping flow.\n",
"\n",
"## 🧾 Receipt Generation\n",
"- Creates a unique receipt file: `customerName_orderNumber.txt`\n",
"- Receipt includes:\n",
" - Customer name and contact\n",
" - Product details (name, price, quantity)\n",
" - Total cost and order summary\n",
"\n",
"## 📦 Generate Order Summary Report\n",
"- Summarizes all purchases into a single file: `order_summary.txt`\n",
"- Useful for inventory and sales review\n",
"\n",
"## 🎯 Product Recommendation\n",
"- Recommends:\n",
" - Printers based on paper type, usage (home/office), or brand preference\n",
" - Compatible paper based on selected printer\n",
"\n",
"## 💬 Interactive Chat Interface\n",
"- Real-time conversation via **Gradio**\n",
"- Polite, helpful answers to product-related questions\n",
"\n",
"## 🛠️ Modular Tool Support\n",
"- Integrated tools for:\n",
" - Checking product availability\n",
" - Adding to cart and generating receipts\n",
" - Creating summary reports\n",
"- Easily extendable for:\n",
" - Promotions\n",
" - Customer reviews\n",
" - Delivery tracking\n",
"\n",
"## 🛡️ Error Handling\n",
"- Validates user inputs (e.g., product name, quantity)\n",
"- Graceful messages to guide user and prevent mistakes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "747e8786-9da8-4342-b6c9-f5f69c2e22ae",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import random\n",
"from dotenv import load_dotenv\n",
"import gradio as gr\n",
"from openai import OpenAI\n",
"\n",
"load_dotenv()\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
"\n",
"# MODEL = \"gpt-4o-mini\"\n",
"MODEL = \"gpt-3.5-turbo\"\n",
"openai = OpenAI()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6072536-eee1-4f87-9f03-8dc88dc04f1a",
"metadata": {},
"outputs": [],
"source": [
"# Using local LLM (that can't even handle basic greeting like Hi!!\n",
"\n",
"# MODEL = \"llama3.2\"\n",
"# openai = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5fe5f5d7-0bd1-41a2-a654-59b587882f22",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 1) System Prompt\n",
"###############################################################################\n",
"system_message = (\n",
" \"You are a helpful assistant for an online store called art-tech.store that sells printers and printer papers.\\n\\n\"\n",
" \"When the user wants to purchase a product, follow these steps:\\n\"\n",
" \"1. Ask whether they are interested in printers or printer papers.\\n\"\n",
" \"2. Ask for filtering preferences (e.g., brand, price range, type).\\n\"\n",
" \"3. Call the function 'check_product_availability' with the selected category and filters.\\n\"\n",
" \" - If it returns an empty list, say: 'No products found for your selection.'\\n\"\n",
" \" - If it returns products, list them EXACTLY, in a numbered list, showing name, price, brand, and availability.\\n\"\n",
" \"4. Wait for the user to select a product by number and quantity.\\n\"\n",
" \"5. Ask for customer first name and contact info.\\n\"\n",
" \"6. Then call 'add_to_cart_and_generate_receipt' to confirm and show the user the receipt and order details.\\n\\n\"\n",
" \"You also have a tool 'generate_report' which summarizes ALL purchases in a single file.\\n\\n\"\n",
" \"IMPORTANT:\\n\"\n",
" \"- Always call 'check_product_availability' if user mentions a new category or changes filters.\\n\"\n",
" \"- Do not invent products or details. Use only what the function calls return.\\n\"\n",
" \"- Every time an order is placed, produce a new receipt file named customerName_orderNumber.txt.\\n\"\n",
" \"- If no matching products are found, say so.\\n\"\n",
" \"- If the user wants a full order summary, call 'generate_report' with no arguments.\\n\"\n",
" \"If you don't know something, say so.\\n\"\n",
" \"Keep answers short and courteous.\\n\"\n",
")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "61a2a15d-b559-4844-b377-6bd5cb4949f6",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 2) Mini Printer Availability with Price & Stock\n",
"###############################################################################\n",
"product_availability = {\n",
" \"mini_printers\": [\n",
" {\n",
" \"name\": \"Phomemo M110 מדפסת מדבקות חכמה\",\n",
" \"brand\": \"Phomemo\",\n",
" \"price\": \"₪300\", # Update if pricing is known\n",
" \"type\": \"Label Printer\",\n",
" \"availability\": \"360,745 in stock (24 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Niimbot B1 Label Printer\",\n",
" \"brand\": \"Niimbot\",\n",
" \"price\": \"₪350\",\n",
" \"type\": \"Portable Thermal Label Printer\",\n",
" \"availability\": \"13,029 in stock (18 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Niimbot B21 Mini Portable Thermal Label Printer\",\n",
" \"brand\": \"Niimbot\",\n",
" \"price\": \"₪500\",\n",
" \"type\": \"Adhesive Sticker Printer\",\n",
" \"availability\": \"141 in stock (12 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Dolewa D3 Portable Mini Printer\",\n",
" \"brand\": \"Dolewa\",\n",
" \"price\": \"₪450\",\n",
" \"type\": \"Thermal Photo & Label Printer\",\n",
" \"availability\": \"336 in stock (6 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"PrintPro Mini מדפסת כיס חכמה\",\n",
" \"brand\": \"PrintPro\",\n",
" \"price\": \"₪550\",\n",
" \"type\": \"Mini Pocket Printer\",\n",
" \"availability\": \"336 in stock (6 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"מיני מדפסת טרמית מעוצבת לילדים\",\n",
" \"brand\": \"Art-Tech\",\n",
" \"price\": \"₪200\",\n",
" \"type\": \"Kids Thermal Printer\",\n",
" \"availability\": \"62 in stock (11 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Children Digital Camera Instant Print\",\n",
" \"brand\": \"Art-Tech\",\n",
" \"price\": \"₪250\",\n",
" \"type\": \"Photo Printing Camera with 32G Memory Card\",\n",
" \"availability\": \"160 in stock (3 variants)\"\n",
" }\n",
" ],\n",
" \"mini_printer_papers\": [\n",
" {\n",
" \"name\": \"HP Printer Paper 8.5x11, 500 Sheets\", # example only\n",
" \"brand\": \"HP\",\n",
" \"price\": \"$9.99\",\n",
" \"type\": \"Standard\",\n",
" \"availability\": \"In stock\"\n",
" },\n",
" {\n",
" \"name\": \"Mini Printer Paper 57*25mm Color Sticker\",\n",
" \"brand\": \"Art-Tech\",\n",
" \"price\": \"₪70\",\n",
" \"type\": \"Self-adhesive Color Label Paper\",\n",
" \"availability\": \"71,996 in stock (9 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"מדבקות שקופות למדפסת טרמית\",\n",
" \"brand\": \"Art-Tech\",\n",
" \"price\": \"₪55\",\n",
" \"type\": \"Transparent Labels\",\n",
" \"availability\": \"11,762 in stock (12 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"גלילי נייר מדבקה\",\n",
" \"brand\": \"Art-Tech\",\n",
" \"price\": \"₪40\",\n",
" \"type\": \"Sticker Paper Rolls\",\n",
" \"availability\": \"42 in stock (4 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Niimbot B21/B1/B3S Thermal Label Sticker Paper\",\n",
" \"brand\": \"Niimbot\",\n",
" \"price\": \"₪55\",\n",
" \"type\": \"Printable White Label Paper 2050mm\",\n",
" \"availability\": \"1,342 in stock (14 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"Mini Printer Sticker Paper 25X57mm\",\n",
" \"brand\": \"Paperang-compatible\",\n",
" \"price\": \"₪65\",\n",
" \"type\": \"Color Self-Adhesive Thermal Rolls\",\n",
" \"availability\": \"3,023 in stock (20 variants)\"\n",
" },\n",
" {\n",
" \"name\": \"3/5/10 NiiMBOT White Label Paper Rolls\",\n",
" \"brand\": \"Niimbot\",\n",
" \"price\": \"₪40\",\n",
" \"type\": \"Waterproof Self-adhesive Rolls\",\n",
" \"availability\": \"1,400 in stock (9 variants)\"\n",
" }\n",
" ]\n",
"}\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0696acb1-0b05-4dc2-80d5-771be04f1fb2",
"metadata": {},
"outputs": [],
"source": [
"# A global list of flight bookings\n",
"flight_bookings = []\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "80ca4e09-6287-4d3f-997d-fa6afbcf6c85",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 3) Helper Functions for Art-Tech Store\n",
"###############################################################################\n",
"\n",
"product_orders = []\n",
"\n",
"def check_product_availability(category: str, filters: dict = None):\n",
" \"\"\"\n",
" Return list of products in the given category from 'product_availability'.\n",
" Optionally filter by brand, type, etc.\n",
" \"\"\"\n",
" print(f\"[TOOL] check_product_availability({category}, {filters=})\")\n",
" category = category.lower()\n",
" products = product_availability.get(category, [])\n",
" \n",
" if filters:\n",
" for key, val in filters.items():\n",
" products = [p for p in products if p.get(key, \"\").lower() == val.lower()]\n",
" return products\n",
"\n",
"\n",
"def add_to_cart_and_generate_receipt(customer_name: str, contact: str, product: dict, quantity: int, order_number: int):\n",
" \"\"\"\n",
" Create a text file: customerName_orderNumber.txt containing order details.\n",
" \"\"\"\n",
" safe_name = customer_name.replace(\" \", \"_\")\n",
" filename = f\"{safe_name}_{order_number}.txt\"\n",
"\n",
" content = (\n",
" \"Art-Tech Store Receipt\\n\"\n",
" \"=======================\\n\"\n",
" f\"Order # : {order_number}\\n\"\n",
" f\"Customer : {customer_name}\\n\"\n",
" f\"Contact : {contact}\\n\"\n",
" f\"Product : {product['name']}\\n\"\n",
" f\"Brand : {product['brand']}\\n\"\n",
" f\"Type : {product.get('type', 'N/A')}\\n\"\n",
" f\"Price : {product['price']}\\n\"\n",
" f\"Quantity : {quantity}\\n\"\n",
" f\"Availability: {product['availability']}\\n\"\n",
" )\n",
" with open(filename, \"w\") as f:\n",
" f.write(content)\n",
"\n",
" print(f\"[TOOL] Receipt file generated => {filename}\")\n",
" return filename\n",
"\n",
"\n",
"def place_order(category, product_index, quantity, customer_name, contact_info):\n",
" \"\"\"\n",
" Places an order for a product by index in the filtered list.\n",
" \"\"\"\n",
" print(f\"[TOOL] place_order({category=}, {product_index=}, {quantity=})\")\n",
"\n",
" try:\n",
" idx = int(product_index)\n",
" except ValueError:\n",
" return \"Error: Product option number is not a valid integer.\"\n",
"\n",
" products = product_availability.get(category.lower(), [])\n",
" if not products:\n",
" return f\"Error: No products found in category '{category}'.\"\n",
"\n",
" pick = idx - 1\n",
" if pick < 0 or pick >= len(products):\n",
" return f\"Error: Invalid product option #{idx} for category '{category}'.\"\n",
"\n",
" selected_product = products[pick]\n",
"\n",
" order = {\n",
" \"category\": category,\n",
" \"product\": selected_product[\"name\"],\n",
" \"brand\": selected_product[\"brand\"],\n",
" \"type\": selected_product.get(\"type\", \"\"),\n",
" \"price\": selected_product[\"price\"],\n",
" \"quantity\": quantity,\n",
" \"customer_name\": customer_name,\n",
" \"contact\": contact_info,\n",
" }\n",
" product_orders.append(order)\n",
"\n",
" order_number = len(product_orders)\n",
" receipt_filename = add_to_cart_and_generate_receipt(customer_name, contact_info, selected_product, quantity, order_number)\n",
"\n",
" confirmation = (\n",
" f\"Order #{order_number} confirmed for {customer_name}. \"\n",
" f\"{selected_product['name']} x{quantity}. Receipt saved to {receipt_filename}.\"\n",
" )\n",
" print(f\"[TOOL] {confirmation}\")\n",
" return confirmation\n",
"\n",
"\n",
"def generate_report():\n",
" \"\"\"\n",
" Summarize ALL orders in a single file called order_summary.txt.\n",
" \"\"\"\n",
" print(f\"[TOOL] generate_report called.\")\n",
"\n",
" report_content = \"Art-Tech Store Order Summary Report\\n\"\n",
" report_content += \"===================================\\n\"\n",
"\n",
" if not product_orders:\n",
" report_content += \"No orders found.\\n\"\n",
" else:\n",
" for i, order in enumerate(product_orders, start=1):\n",
" report_content += (\n",
" f\"Order # : {i}\\n\"\n",
" f\"Customer : {order['customer_name']}\\n\"\n",
" f\"Contact : {order['contact']}\\n\"\n",
" f\"Product : {order['product']}\\n\"\n",
" f\"Brand : {order['brand']}\\n\"\n",
" f\"Type : {order['type']}\\n\"\n",
" f\"Price : {order['price']}\\n\"\n",
" f\"Quantity : {order['quantity']}\\n\"\n",
" \"-------------------------\\n\"\n",
" )\n",
"\n",
" filename = \"order_summary.txt\"\n",
" with open(filename, \"w\") as f:\n",
" f.write(report_content)\n",
"\n",
" msg = f\"Summary report generated => {filename}\"\n",
" print(f\"[TOOL] {msg}\")\n",
" return msg\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "39fb9008",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 4) Tools JSON Schemas for Art-Tech Store\n",
"###############################################################################\n",
"price_function = {\n",
" \"name\": \"get_product_price\",\n",
" \"description\": \"Get the price of a product (not strictly needed now).\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"category\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Product category such as 'mini_printers' or 'mini_printer_papers'.\",\n",
" },\n",
" \"product_name\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Name of the product to check price for.\",\n",
" },\n",
" },\n",
" \"required\": [\"category\", \"product_name\"],\n",
" },\n",
"}\n",
"\n",
"availability_function = {\n",
" \"name\": \"check_product_availability\",\n",
" \"description\": (\n",
" \"Check availability of products in a category. \"\n",
" \"Returns a list of {name, brand, price, type, availability}.\"\n",
" ),\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"category\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Category of products to check (e.g., 'mini_printers').\",\n",
" },\n",
" \"filters\": {\n",
" \"type\": \"object\",\n",
" \"description\": \"Optional filters like brand or type.\",\n",
" },\n",
" },\n",
" \"required\": [\"category\"],\n",
" },\n",
"}\n",
"\n",
"book_function = {\n",
" \"name\": \"place_order\",\n",
" \"description\": (\n",
" \"Place an order using an index for the chosen product. \"\n",
" \"Generates a unique receipt file customerName_{orderNumber}.txt.\"\n",
" ),\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"category\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Product category (e.g., 'mini_printers').\",\n",
" },\n",
" \"product_index\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"1-based index of selected product from availability list.\",\n",
" },\n",
" \"quantity\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Quantity to order.\",\n",
" },\n",
" \"customer_name\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Customer's full name.\",\n",
" },\n",
" \"contact_info\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Customer's contact information (email or phone).\",\n",
" },\n",
" },\n",
" \"required\": [\"category\", \"product_index\", \"quantity\", \"customer_name\", \"contact_info\"],\n",
" },\n",
"}\n",
"\n",
"report_function = {\n",
" \"name\": \"generate_report\",\n",
" \"description\": (\n",
" \"Generates a summary report of ALL orders in order_summary.txt.\"\n",
" ),\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {},\n",
" \"required\": [],\n",
" },\n",
"}\n",
"\n",
"tools = [\n",
" {\"type\": \"function\", \"function\": price_function},\n",
" {\"type\": \"function\", \"function\": availability_function},\n",
" {\"type\": \"function\", \"function\": book_function},\n",
" {\"type\": \"function\", \"function\": report_function},\n",
"]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1f003836",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 5) Handle Tool Calls for Art-Tech Store\n",
"###############################################################################\n",
"def handle_tool_call(message):\n",
" \"\"\"\n",
" The LLM can request to call a function in 'tools'. We parse the JSON arguments\n",
" and run the corresponding Python function. Then we return a 'tool' message with the result.\n",
" \"\"\"\n",
" tool_call = message.tool_calls[0]\n",
" fn_name = tool_call.function.name\n",
" args = json.loads(tool_call.function.arguments)\n",
"\n",
" if fn_name == \"get_product_price\":\n",
" category = args.get(\"category\")\n",
" product_name = args.get(\"product_name\")\n",
" products = product_availability.get(category.lower(), [])\n",
" price = \"Not found\"\n",
" for p in products:\n",
" if p[\"name\"].lower() == product_name.lower():\n",
" price = p[\"price\"]\n",
" break\n",
" response_content = {\"category\": category, \"product_name\": product_name, \"price\": price}\n",
"\n",
" elif fn_name == \"check_product_availability\":\n",
" category = args.get(\"category\")\n",
" filters = args.get(\"filters\", {})\n",
" products = check_product_availability(category, filters)\n",
" response_content = {\"category\": category, \"availability\": products}\n",
"\n",
" elif fn_name == \"place_order\":\n",
" category = args.get(\"category\")\n",
" product_index = args.get(\"product_index\")\n",
" quantity = args.get(\"quantity\")\n",
" customer_name = args.get(\"customer_name\")\n",
" contact_info = args.get(\"contact_info\")\n",
"\n",
" confirmation = place_order(category, product_index, quantity, customer_name, contact_info)\n",
" response_content = {\n",
" \"category\": category,\n",
" \"product_index\": product_index,\n",
" \"quantity\": quantity,\n",
" \"customer_name\": customer_name,\n",
" \"contact_info\": contact_info,\n",
" \"confirmation\": confirmation,\n",
" }\n",
"\n",
" elif fn_name == \"generate_report\":\n",
" msg = generate_report()\n",
" response_content = {\"report\": msg}\n",
"\n",
" else:\n",
" response_content = {\"error\": f\"Unknown tool: {fn_name}\"}\n",
"\n",
" return {\n",
" \"role\": \"tool\",\n",
" \"content\": json.dumps(response_content),\n",
" \"tool_call_id\": tool_call.id,\n",
" }, args\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f6b34b32",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 6) Main Chat Function for Art-Tech Store\n",
"###############################################################################\n",
"def chat(message, history):\n",
" \"\"\"\n",
" The main chat loop that handles the conversation with the user,\n",
" passing 'tools' definitions to the LLM for function calling.\n",
" \"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": message}]\n",
"\n",
" try:\n",
" response = openai.chat.completions.create(\n",
" model=MODEL,\n",
" messages=messages,\n",
" tools=tools\n",
" )\n",
"\n",
" # If the LLM requests a function call, handle it\n",
" while response.choices[0].finish_reason == \"tool_calls\":\n",
" msg = response.choices[0].message\n",
" print(f\"[INFO] Tool call requested: {msg.tool_calls[0]}\")\n",
" tool_response, tool_args = handle_tool_call(msg)\n",
" print(f\"[INFO] Tool response: {tool_response}\")\n",
"\n",
" # Add both the LLM's request and our tool response to the conversation\n",
" messages.append(msg)\n",
" messages.append(tool_response)\n",
"\n",
" # Re-send updated conversation to get final or next step\n",
" response = openai.chat.completions.create(\n",
" model=MODEL,\n",
" messages=messages\n",
" )\n",
"\n",
" # Return normal text response (finish_reason = \"stop\")\n",
" return response.choices[0].message.content\n",
"\n",
" except Exception as e:\n",
" print(f\"[ERROR] {e}\")\n",
" return \"I'm sorry, something went wrong while processing your request.\"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cea4b097",
"metadata": {},
"outputs": [],
"source": [
"###############################################################################\n",
"# 7) Launch Gradio\n",
"###############################################################################\n",
"gr.ChatInterface(fn=chat, type=\"messages\").launch()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0b39d5a6",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}