Build Chatbot v4 : Frontend Integration

Business Scenario

Welcome!

Previously, ​you explored how agents differ from fixed RAG pipelines, defined five tools and built a ReAct loop with a step limit. You tested agent decisions and guardrails, then built a SmartCart agent with memory and visible reasoning

Today, your manager has assigned you a task to​ ​​build SmartCart AI v4 as a complete web-based chatbot. You will connect Kart's retrieval system, AI agent, shopping tools, and a real pincode lookup service to a user-friendly interface where shoppers can chat, track tool usage, and watch their cart update in real time.​​

Git Pull

git pull origin branchName

Pre-Lab Preparation

Topic : Build Chatbot v4 : Frontend Integration

1) Frontends

2) External APIs

3) End-to-end apps

Lab 18 : Lab 18 ipynb

Task 1: Setup — Bring In the Pieces from Earlier Labs

import os

needed = ["SmartCart_Return_Policy.pdf", "SmartCart_Delivery_FAQ.docx", "smartcart_products.csv"]

for name in needed:
    if os.path.exists(name):
        print("OK      ", name)
    else:
        print("MISSING ", name, "-- upload it with the folder icon on the left")

Upload data files

1

Output

Install the libraries

2

!pip install -q langchain langchain-groq langchain-ollama langchain-community \
    langchain-text-splitters langchain-chroma langchain-huggingface \
    sentence-transformers pypdf docx2txt beautifulsoup4 "gradio>=5,<6"
from google.colab import userdata
os.environ["GROQ_API_KEY"] = userdata.get("GROQ_API_KEY")
print("Key loaded:", os.environ["GROQ_API_KEY"][:4] + "...")

Load the API Key

3

Define RAG Data Layer (copied from Lab 15)

4

os.environ["USER_AGENT"] = "SmartCartLab/1.0"

from langchain_community.document_loaders import (
    PyPDFLoader, Docx2txtLoader, WebBaseLoader, CSVLoader)
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma

DB_FOLDER = "smartcart_db"
SOURCES = [
    ("SmartCart_Return_Policy.pdf", "policy"),
    ("SmartCart_Delivery_FAQ.docx", "faq"),
    ("https://en.wikipedia.org/wiki/Cash_on_delivery", "web"),
    ("smartcart_products.csv", "product"),]
embedder = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
os.environ["USER_AGENT"] = "SmartCartLab/1.0"

from langchain_community.document_loaders import (
    PyPDFLoader, Docx2txtLoader, WebBaseLoader, CSVLoader)
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma

DB_FOLDER = "smartcart_db"
SOURCES = [
    ("SmartCart_Return_Policy.pdf", "policy"),
    ("SmartCart_Delivery_FAQ.docx", "faq"),
    ("https://en.wikipedia.org/wiki/Cash_on_delivery", "web"),
    ("smartcart_products.csv", "product"),]
embedder = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

def load_documents():
    """Stage 1 -- load every source and tag it with its doc_type."""
    docs = []
    for source, doc_type in SOURCES:
        if source.endswith(".pdf"):
            loader = PyPDFLoader(source)
        elif source.endswith(".docx"):
            loader = Docx2txtLoader(source)
        elif source.endswith(".csv"):
            loader = CSVLoader(source)
        else:
            loader = WebBaseLoader(source)
        for doc in loader.load():
            doc.metadata["doc_type"] = doc_type
            docs.append(doc)
    return docs

def chunk_documents(docs):
    """Stage 2 -- split documents into overlapping chunks of up to 300 characters."""
    splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
    return splitter.split_documents(docs)

def build_vector_store(chunks):
    """Stage 3 -- empty the database, then embed and store all chunks."""
    store = Chroma(collection_name="smartcart", embedding_function=embedder,
                   persist_directory=DB_FOLDER)
    store.reset_collection()        # start empty -- safe to run again and again
    store.add_documents(chunks)
    return store
os.environ["USER_AGENT"] = "SmartCartLab/1.0"

from langchain_community.document_loaders import (
    PyPDFLoader, Docx2txtLoader, WebBaseLoader, CSVLoader)
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma

DB_FOLDER = "smartcart_db"
SOURCES = [
    ("SmartCart_Return_Policy.pdf", "policy"),
    ("SmartCart_Delivery_FAQ.docx", "faq"),
    ("https://en.wikipedia.org/wiki/Cash_on_delivery", "web"),
    ("smartcart_products.csv", "product"),]
embedder = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

def load_documents():
    """Stage 1 -- load every source and tag it with its doc_type."""
    docs = []
    for source, doc_type in SOURCES:
        if source.endswith(".pdf"):
            loader = PyPDFLoader(source)
        elif source.endswith(".docx"):
            loader = Docx2txtLoader(source)
        elif source.endswith(".csv"):
            loader = CSVLoader(source)
        else:
            loader = WebBaseLoader(source)
        for doc in loader.load():
            doc.metadata["doc_type"] = doc_type
            docs.append(doc)
    return docs

def chunk_documents(docs):
    """Stage 2 -- split documents into overlapping chunks of up to 300 characters."""
    splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
    return splitter.split_documents(docs)

def build_vector_store(chunks):
    """Stage 3 -- empty the database, then embed and store all chunks."""
    store = Chroma(collection_name="smartcart", embedding_function=embedder,
                   persist_directory=DB_FOLDER)
    store.reset_collection()        # start empty -- safe to run again and again
    store.add_documents(chunks)
    return store

def get_retriever(k=3, doc_type=None):
    """Stage 4 -- open the saved database and return a retriever."""
    store = Chroma(collection_name="smartcart", embedding_function=embedder,
                   persist_directory=DB_FOLDER)
    search_kwargs = {"k": k}
    if doc_type:
        search_kwargs["filter"] = {"doc_type": doc_type}
    return store.as_retriever(search_kwargs=search_kwargs)

5

Build the Database (copied from Lab 15)

docs = load_documents()
print("Loaded ", len(docs), "documents")

chunks = chunk_documents(docs)
print("Created", len(chunks), "chunks")

build_vector_store(chunks)
print("Stored in", DB_FOLDER)

Output

Define get_llm() from Lab 13, now with json_mode switch

6

from langchain_groq import ChatGroq
from langchain_ollama import ChatOllama

CLOUD_MODEL = "qwen/qwen3.8-27b"     # GroqCloud, same model since Lab 7
LOCAL_MODEL = "qwen2.5:3b"           # Ollama, Lab 13

def get_llm(backend="cloud", json_mode=True):
    """Return the chat model for 'cloud' (GroqCloud) or 'local' (Ollama)."""
    if backend == "local":
        if json_mode:
            return ChatOllama(model=LOCAL_MODEL, temperature=0.3, num_predict=800, format="json")
        return ChatOllama(model=LOCAL_MODEL, temperature=0.3, num_predict=800)
    extra = {"response_format": {"type": "json_object"}} if json_mode else {}
    return ChatGroq(model=CLOUD_MODEL, temperature=0.3, max_tokens=800,
                    model_kwargs=extra, reasoning_effort="none")

Define input checks, from lab 10

7

EXIT_WORDS = ["quit", "exit", "bye"]
MAX_CHARS = 500
FRIENDLY_ERROR = "Sorry, I'm having trouble right now. Please try again in a moment."

def check_input(text):
    """Return (cleaned_text, problem). problem is "" when the input is fine."""
    cleaned = " ".join(text.split())
    if cleaned == "":
        return cleaned, "Please type what you are shopping for."
    if len(cleaned) > MAX_CHARS:
        return cleaned, f"Please keep your request under {MAX_CHARS} characters."
    return cleaned, ""

Define the tools (copied from Lab 18)

8

import pandas as pd
from langchain_core.tools import tool

catalogue = pd.read_csv("smartcart_products.csv")
product_retriever = get_retriever(k=3, doc_type="product")
policy_retriever = get_retriever(k=3, doc_type="policy")
faq_retriever = get_retriever(k=2, doc_type="faq")

COMPETITOR_PRICES = {            # simulated price-comparison service
    "P01": {"MegaMart": 1349, "QuickBuy": 1279},
    "P02": {"MegaMart": 1149, "QuickBuy": 1099},
    "P03": {"MegaMart": 3799, "QuickBuy": 4199},
    "P05": {"MegaMart": 15499, "QuickBuy": 14799},
    "P11": {"MegaMart": 325, "QuickBuy": 299},
    "P13": {"MegaMart": 1299, "QuickBuy": 1149},}

ORDERS = {                       # simulated order-tracking service
    "SC1001": {"item": "Tata Tea Gold 500 g", "status": "Delivered", "when": "on 6 October 2026"},
    "SC1002": {"item": "boAt Airdopes 141", "status": "Out for delivery", "when": "today by 8 pm"},
    "SC1003": {"item": "Levi's 511 Slim Jeans", "status": "Shipped", "when": 
    "expected 12 October 2026"},}

CART = {}                        # product_id -> quantity

def find_product(product_id):
    """Return the catalogue row for a product ID, or None (plain helper, not a tool)."""
    rows = catalogue[catalogue["product_id"] == product_id.strip().upper()]
    return None if rows.empty else rows.iloc[0]
    
    
import pandas as pd
from langchain_core.tools import tool

catalogue = pd.read_csv("smartcart_products.csv")
product_retriever = get_retriever(k=3, doc_type="product")
policy_retriever = get_retriever(k=3, doc_type="policy")
faq_retriever = get_retriever(k=2, doc_type="faq")

COMPETITOR_PRICES = {            # simulated price-comparison service
    "P01": {"MegaMart": 1349, "QuickBuy": 1279},
    "P02": {"MegaMart": 1149, "QuickBuy": 1099},
    "P03": {"MegaMart": 3799, "QuickBuy": 4199},
    "P05": {"MegaMart": 15499, "QuickBuy": 14799},
    "P11": {"MegaMart": 325, "QuickBuy": 299},
    "P13": {"MegaMart": 1299, "QuickBuy": 1149},}

ORDERS = {                       # simulated order-tracking service
    "SC1001": {"item": "Tata Tea Gold 500 g", "status": "Delivered", "when": "on 6 October 2026"},
    "SC1002": {"item": "boAt Airdopes 141", "status": "Out for delivery", "when": "today by 8 pm"},
    "SC1003": {"item": "Levi's 511 Slim Jeans", "status": "Shipped", "when": 
    "expected 12 October 2026"},}

CART = {}                        # product_id -> quantity

def find_product(product_id):
    """Return the catalogue row for a product ID, or None (plain helper, not a tool)."""
    rows = catalogue[catalogue["product_id"] == product_id.strip().upper()]
    return None if rows.empty else rows.iloc[0]


def cart_summary():
    """Return the cart as readable text (plain helper, not a tool)."""
    if not CART:
        return "empty"
    items, total = [], 0
    for product_id, quantity in CART.items():
        product = find_product(product_id)
        total += quantity * int(product["price_rs"])
        items.append(f"{quantity} x {product['name']}")
    return ", ".join(items) + f" | Total Rs. {total:,}"

@tool
def search_products(query: str) -> str:
    """Search SmartCart's product catalogue by meaning. Use it for product
    recommendations, prices and product IDs (like P01)."""
    docs = product_retriever.invoke(query)
    return "\n\n".join(doc.page_content for doc in docs)

@tool
def search_policies(question: str) -> str:
    """Search SmartCart's return, refund, delivery and payment policies and FAQ."""
    docs = policy_retriever.invoke(question) + faq_retriever.invoke(question)
    return "\n\n".join(doc.page_content for doc in docs)


import pandas as pd
from langchain_core.tools import tool

catalogue = pd.read_csv("smartcart_products.csv")
product_retriever = get_retriever(k=3, doc_type="product")
policy_retriever = get_retriever(k=3, doc_type="policy")
faq_retriever = get_retriever(k=2, doc_type="faq")

COMPETITOR_PRICES = {            # simulated price-comparison service
    "P01": {"MegaMart": 1349, "QuickBuy": 1279},
    "P02": {"MegaMart": 1149, "QuickBuy": 1099},
    "P03": {"MegaMart": 3799, "QuickBuy": 4199},
    "P05": {"MegaMart": 15499, "QuickBuy": 14799},
    "P11": {"MegaMart": 325, "QuickBuy": 299},
    "P13": {"MegaMart": 1299, "QuickBuy": 1149},}

ORDERS = {                       # simulated order-tracking service
    "SC1001": {"item": "Tata Tea Gold 500 g", "status": "Delivered", "when": "on 6 October 2026"},
    "SC1002": {"item": "boAt Airdopes 141", "status": "Out for delivery", "when": "today by 8 pm"},
    "SC1003": {"item": "Levi's 511 Slim Jeans", "status": "Shipped", "when": 
    "expected 12 October 2026"},}

CART = {}                        # product_id -> quantity

def find_product(product_id):
    """Return the catalogue row for a product ID, or None (plain helper, not a tool)."""
    rows = catalogue[catalogue["product_id"] == product_id.strip().upper()]
    return None if rows.empty else rows.iloc[0]


def cart_summary():
    """Return the cart as readable text (plain helper, not a tool)."""
    if not CART:
        return "empty"
    items, total = [], 0
    for product_id, quantity in CART.items():
        product = find_product(product_id)
        total += quantity * int(product["price_rs"])
        items.append(f"{quantity} x {product['name']}")
    return ", ".join(items) + f" | Total Rs. {total:,}"


@tool
def search_products(query: str) -> str:
    """Search SmartCart's product catalogue by meaning. Use it for product
    recommendations, prices and product IDs (like P01)."""
    docs = product_retriever.invoke(query)
    return "\n\n".join(doc.page_content for doc in docs)

@tool
def search_policies(question: str) -> str:
    """Search SmartCart's return, refund, delivery and payment policies and FAQ."""
    docs = policy_retriever.invoke(question) + faq_retriever.invoke(question)
    return "\n\n".join(doc.page_content for doc in docs)

@tool
def compare_prices(product_id: str) -> str:
    """Compare SmartCart's price for a product with other online stores.
    Needs a product_id like P01 -- use search_products first to find it."""
    product = find_product(product_id)
    if product is None:
        return f"No product with ID {product_id}. Use search_products to find the right ID."
    lines = [f"{product['name']} -- SmartCart: Rs. {int(product['price_rs'])}"]
    others = COMPETITOR_PRICES.get(product["product_id"], {})
    for store, price in others.items():
        lines.append(f"{store}: Rs. {price}")
    if not others:
        lines.append("No prices from other stores are available for this product.")
    return "\n".join(lines)
@tool
def track_order(order_id: str) -> str:
    """Get the delivery status of a SmartCart order. Needs an order ID like SC1002."""
    order = ORDERS.get(order_id.strip().upper())
    if order is None:
        return f"No order found with ID {order_id}. Order IDs look like SC1001."
    return f"Order {order_id.upper()}: {order['item']} -- {order['status']} ({order['when']})."
@tool
def add_to_cart(product_id: str, quantity: int = 1) -> str:
    """Add a product to the shopper's cart. Needs a product_id like P01 and a
    quantity from 1 to 5 -- use search_products first to find the ID."""
    product = find_product(product_id)
    if product is None:
        return f"No product with ID {product_id}. Use search_products to find the right ID."
    if not 1 <= quantity <= 5:
        return "Quantity must be between 1 and 5."
    CART[product["product_id"]] = CART.get(product["product_id"], 0) + quantity
    return f"Added {quantity} x {product['name']}. Cart: {cart_summary()}"
ALL_TOOLS = [search_products, search_policies, compare_prices, track_order, add_to_cart]

Define the agent (copied from Lab 18)

9

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage

llm_with_tools = get_llm("cloud", json_mode=False).bind_tools(ALL_TOOLS)
TOOLS_BY_NAME = {t.name: t for t in ALL_TOOLS}
MAX_STEPS = 5

AGENT_PROMPT = """You are Kart, SmartCart's friendly shopping assistant for shoppers in India.
You can use tools. For each message, decide what to do:
- Greetings, thanks or general shopping advice: answer directly without a tool.
- Products, prices or recommendations: use search_products.
- Returns, refunds, delivery or payment questions: use search_policies.
- "Is it cheaper elsewhere?": find the product_id with search_products, then use compare_prices.
- Order status: use track_order.
- Adding to the cart: find the product_id with search_products, then use add_to_cart.

Rules:
1. Never make up prices, policies, product IDs or order details -- get them from a tool.
2. Before each tool call, write one short sentence saying why you need it.
3. Only help with shopping. Reply in the shopper's style (English or Hinglish).
4. Keep the final answer under 80 words.
5. When you mention a product, add its ID in brackets, like Noise Buds VS104 (P02), 
so you can use it later."""

agent_history = []     # earlier questions and final answers
MEMORY_SIZE = 10
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage

llm_with_tools = get_llm("cloud", json_mode=False).bind_tools(ALL_TOOLS)
TOOLS_BY_NAME = {t.name: t for t in ALL_TOOLS}
MAX_STEPS = 5

AGENT_PROMPT = """You are Kart, SmartCart's friendly shopping assistant for shoppers in India.
You can use tools. For each message, decide what to do:
- Greetings, thanks or general shopping advice: answer directly without a tool.
- Products, prices or recommendations: use search_products.
- Returns, refunds, delivery or payment questions: use search_policies.
- "Is it cheaper elsewhere?": find the product_id with search_products, then use compare_prices.
- Order status: use track_order.
- Adding to the cart: find the product_id with search_products, then use add_to_cart.

Rules:
1. Never make up prices, policies, product IDs or order details -- get them from a tool.
2. Before each tool call, write one short sentence saying why you need it.
3. Only help with shopping. Reply in the shopper's style (English or Hinglish).
4. Keep the final answer under 80 words.
5. When you mention a product, add its ID in brackets, like Noise Buds VS104 (P02), 
so you can use it later."""

agent_history = []     # earlier questions and final answers
MEMORY_SIZE = 10

def ask_agent(question, verbose=False):
    """Run the ReAct loop for one message. Returns (answer, tools_used)."""
    messages = ([SystemMessage(content=AGENT_PROMPT)]
                + agent_history[-MEMORY_SIZE:]
                + [HumanMessage(content=question)])
    tools_used = []
    answer = "Sorry, that took too many steps. Please ask in a simpler way."
    for step in range(MAX_STEPS):
        response = llm_with_tools.invoke(messages)        # Reason
        messages.append(response)
        if not response.tool_calls:
            answer = response.content                      # final answer
            break
        if verbose and response.content:
            print("   Thought:", response.content)
        for call in response.tool_calls:
            try:
                result = TOOLS_BY_NAME[call["name"]].invoke(call["args"])  # Act
            except Exception as error:
                result = f"Tool error: {error}"            # never crash
            tools_used.append(call["name"])
            if verbose:
                short = result[:70].replace("\n", " | ")
                print(f"   Action : {call['name']}({call['args']})")
                print(f"   Observe: {short}")
            messages.append(ToolMessage(content=result, tool_call_id=call["id"])) #observe
    agent_history.append(HumanMessage(content=question))
    agent_history.append(AIMessage(content=answer))
    return answer, tools_used
import requests
import time

url = "https://api.postalpincode.in/pincode/411001"

for attempt in range(3):
    try:
        response = requests.get(url, timeout=15,
                                headers={"User-Agent": "Mozilla/5.0"})
        response.raise_for_status()
        data = response.json()
        break
    except requests.exceptions.RequestException as e:
        print(f"Attempt {attempt+1} failed: {e}")
        if attempt < 2:
            time.sleep(3)
else:
    data = None

if data and data[0]["Status"] == "Success":
    office = data[0]["PostOffice"][0]
    print("HTTP status :", response.status_code)
    print("API status :", data[0]["Status"])
    print("First office:", office["Name"], "|",
          office["District"], "|", office["State"])

Task 2 : Integrate an External API

Call the API directly and look at the reply

1

import requests
import time

url = "https://api.postalpincode.in/pincode/411001"

for attempt in range(3):
    try:
        response = requests.get(url, timeout=15,
                                headers={"User-Agent": "Mozilla/5.0"})
        response.raise_for_status()
        data = response.json()
        break
    except requests.exceptions.RequestException as e:
        print(f"Attempt {attempt+1} failed: {e}")
        if attempt < 2:
            time.sleep(3)
else:
    data = None

if data and data[0]["Status"] == "Success":
    office = data[0]["PostOffice"][0]
    print("HTTP status :", response.status_code)
    print("API status :", data[0]["Status"])
    print("First office:", office["Name"], "|",
          office["District"], "|", office["State"])
elif data:
    print("API status:", data[0]["Status"])
    print("Pincode not found.")
else:
    print("India Post API is currently unreachable.")
    print("Try again later or use another network.")

Output

Wrap the API in a tool and add it to the tool list

2

# ============================================================
# PURPOSE: DELIVERY TOOL -- wraps the India Post API with input
# checks, a timeout and a fallback. Delivery times come from
# SmartCart_Delivery_FAQ.docx.
# ============================================================
METRO_CITIES = ["Mumbai", "Delhi", "Bengaluru", "Bangalore", "Chennai",
                "Kolkata", "Hyderabad", "Pune"]

# ============================================================
# PURPOSE: DELIVERY TOOL -- wraps the India Post API with input
# checks, a timeout and a fallback. Delivery times come from
# SmartCart_Delivery_FAQ.docx.
# ============================================================
METRO_CITIES = ["Mumbai", "Delhi", "Bengaluru", "Bangalore", "Chennai",
                "Kolkata", "Hyderabad", "Pune"]

@tool
def check_delivery(pincode: str) -> str:
    """Check where a 6-digit Indian pincode is and how long SmartCart delivery
    takes there. Use it when the shopper asks about delivery to a pincode."""
    pincode = pincode.strip()
    if not (pincode.isdigit() and len(pincode) == 6):
        return "A pincode must be exactly 6 digits, for example 411001."
    try:
        response = requests.get(f"https://api.postalpincode.in/pincode/{pincode}", timeout=5)
        data = response.json()[0]
    except Exception:
        return "The pincode service is not responding. Standard delivery takes 3 to 5 days."
    if data["Status"] != "Success":
        return f"Pincode {pincode} was not found. Please check it."
    office = data["PostOffice"][0]
    district, state = office["District"], office["State"]
    is_metro = any(city in district for city in METRO_CITIES)
    days = "1 to 2 days" if is_metro else "3 to 5 days"
    return f"Pincode {pincode} is in {district}, {state}. SmartCart delivery takes {days}."

# add the new tool (written so that running this cell twice does not add it twice)
ALL_TOOLS = [t for t in ALL_TOOLS if t.name != "check_delivery"] + [check_delivery]
TOOLS_BY_NAME = {t.name: t for t in ALL_TOOLS}

Test the tool on a Metro code, non Metro code, wrong format and nonexistant pincode

3

for pincode in ["560001", "302001", "12345", "999999"]:
    print(f"{pincode:<7} -> {check_delivery.invoke({'pincode': pincode})}")
print("\nTools available:", [t.name for t in ALL_TOOLS])

Output

Update the prompt and bind the toola again

1

if "check_delivery" not in AGENT_PROMPT:
    AGENT_PROMPT = AGENT_PROMPT.replace(
        "- Order status: use track_order.",
        "- Order status: use track_order.\n- Delivery time to a pincode: use check_delivery.")

llm_with_tools = get_llm("cloud", json_mode=False).bind_tools(ALL_TOOLS)

print(AGENT_PROMPT.split("Rules:")[0])

Task 3 : Connect the New Tool to the Agent

Output :

Test the agent with a delivery question and a message that needs three tools

2

for question in ["Can you deliver to 560001? How fast?",
                 "Add the Noise Buds VS104 to my cart and tell me how long delivery to 302001 takes"]:
    print("You :", question)
    answer, tools_used = ask_agent(question, verbose=True)
    print("Kart:", answer)
    print("Tools:", tools_used, "\n")

Output :

Build and launch the chat page

1

Task 4 : Your first Frontend

import gradio as gr

def reply(message, chat_history):
    """Called by Gradio for every message the shopper sends."""
    answer, tools_used = ask_agent(message)
    return answer

simple_app = gr.ChatInterface(fn=reply, type="messages", title="SmartCart AI")
simple_app.launch(share=True)

Output :

Close the first app before building the full one

2

simple_app.close()

Output :

Define the handlers and the page

1

Task 5 : Build the SmartCart v4

def respond(message, chat_history):
    """Runs when the shopper sends a message."""
    cleaned, problem = check_input(message)
    if problem:
        answer, tools_used = problem, []
    else:
        try:
            answer, tools_used = ask_agent(cleaned)
        except Exception as error:
            print("[log]", type(error).__name__, "-", str(error)[:80])
            answer, tools_used = FRIENDLY_ERROR, []
    chat_history = chat_history + [
        {"role": "user", "content": message},
        {"role": "assistant", "content": answer},]
    tools_text = ", ".join(tools_used) if tools_used else "None - answered directly"
    return "", chat_history, cart_summary(), tools_text

def new_chat():
    """Runs when the shopper clicks New chat."""
    agent_history.clear()
    CART.clear()
    return [], cart_summary(), "None yet"
    
    
def respond(message, chat_history):
    """Runs when the shopper sends a message."""
    cleaned, problem = check_input(message)
    if problem:
        answer, tools_used = problem, []
    else:
        try:
            answer, tools_used = ask_agent(cleaned)
        except Exception as error:
            print("[log]", type(error).__name__, "-", str(error)[:80])
            answer, tools_used = FRIENDLY_ERROR, []
    chat_history = chat_history + [
        {"role": "user", "content": message},
        {"role": "assistant", "content": answer},]
    tools_text = ", ".join(tools_used) if tools_used else "None - answered directly"
    return "", chat_history, cart_summary(), tools_text

def new_chat():
    """Runs when the shopper clicks New chat."""
    agent_history.clear()
    CART.clear()
    return [], cart_summary(), "None yet"

def switch_backend(backend):
    """Runs when the shopper picks cloud or local."""
    global llm_with_tools
    llm_with_tools = get_llm(backend, json_mode=False).bind_tools(ALL_TOOLS)
    return f"Using the **{backend}** model"

with gr.Blocks(title="SmartCart AI v4") as demo:
    gr.Markdown("# SmartCart AI v4\nAsk Kart about products, prices, policies, 
    orders or delivery.")
    with gr.Row():
        with gr.Column(scale=3):
            chatbot = gr.Chatbot(type="messages", height=450, label="Kart")
            message = gr.Textbox(label="Your message",
                                 placeholder="e.g. Suggest gym earbuds under Rs. 1,500")
            with gr.Row():
                send_button = gr.Button("Send", variant="primary")
                new_button = gr.Button("New chat")
        with gr.Column(scale=1):
            cart_box = gr.Textbox(label="Cart", value=cart_summary(), interactive=False)
            tools_box = gr.Textbox(label="Tools Kart used", value="None yet", interactive=False)
            backend_choice = gr.Radio(["cloud", "local"], value="cloud", label="Model backend")
            backend_status = gr.Markdown("Using the **cloud** model")

    # Wiring: event -> handler(inputs) -> outputs
    send_button.click(respond, [message, chatbot], [message, chatbot, cart_box, tools_box])
    message.submit(respond, [message, chatbot], [message, chatbot, cart_box, tools_box])
    new_button.click(new_chat, None, [chatbot, cart_box, tools_box])
    backend_choice.change(switch_backend, backend_choice, backend_status)

Launch app

1

Task 6 : Launch and use SmartCart v4

demo.launch(share=True)

Output

First click on NEW CHAT, then send these messages one by one & observe the output

2

Output :

 

Great job!

You have successfully ​​​​​integrated an external API as an agent tool, built the chat interface using Gradio (gr.ChatInterface and gr.Blocks), connected the frontend with the agent, and tested a complete shopping journey in the browser along with an end-to-end test.

Checkpoint

   Git Push

git push origin branchName

Lab 19 : Lab 18 ipynb

GenAI - 19

By Content ITV

GenAI - 19

  • 192