{ "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 20โ€“50mm\",\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 }