You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

653 lines
26 KiB

{
"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
}