{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "colab": {
   "provenance": [],
   "name": "session_5_lab.ipynb"
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lab 5 — Give It Hands\n",
    "**Session 5 · Tool use + choosing the approach · TCE**\n",
    "\n",
    "Today: fix Session 1's broken math with a real calculator, chain tools, then decide when tools are even the right answer. **File → Save a copy in Drive.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 1 — setup\n",
    "%pip install -q -U google-genai\n",
    "from getpass import getpass\n",
    "from google import genai\n",
    "from google.genai import types\n",
    "import time\n",
    "\n",
    "client = genai.Client(api_key=getpass(\"Gemini API key: \"))\n",
    "MODEL = \"gemini-flash-latest\"  # the free tier's current Flash (July 2026 → Gemini 3.5 Flash). 503 'high demand'? swap to \"gemini-flash-lite-latest\".\n",
    "print(\"ready ✓\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part A — The calculator tool\n",
    "\n",
    "The model never runs your function — it *asks* to, your code executes, the result goes back. The SDK reads your **docstring + type hints** to build the tool declaration, so write them like instructions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 2 — define a tool as a plain Python function\n",
    "def calculator(expression: str) -> float:\n",
    "    \"\"\"Evaluate a mathematical expression and return the exact result.\n",
    "    Use this for ANY arithmetic. Never compute numbers yourself.\n",
    "    Example expression: '2347 * 0.18'.\"\"\"\n",
    "    allowed = set(\"0123456789+-*/(). \")\n",
    "    if not set(expression) <= allowed:      # validate BEFORE eval — never trust raw args\n",
    "        raise ValueError(f\"unsafe expression: {expression!r}\")\n",
    "    return eval(expression)\n",
    "\n",
    "# hand the model the tool; SDK does automatic function calling\n",
    "config = types.GenerateContentConfig(tools=[calculator])\n",
    "\n",
    "r = client.models.generate_content(\n",
    "    model=MODEL,\n",
    "    contents=\"What is 18% GST on a bill of 2347 rupees, and the total?\",\n",
    "    config=config)\n",
    "print(r.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ask the same thing **without** `config` (no tool) and compare — watch it guess digits. That contrast is the whole point.\n",
    "\n",
    "### ✓ Checkpoint 1 — with-tool answer is exact; you saw the no-tool version wobble.\n",
    "\n",
    "---\n",
    "## Part B — A second tool + chaining"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 3 — add a tool; ask something needing BOTH\n",
    "def days_between(date1: str, date2: str) -> int:\n",
    "    \"\"\"Return the number of days between two dates in YYYY-MM-DD format.\"\"\"\n",
    "    from datetime import date\n",
    "    a = date.fromisoformat(date1); b = date.fromisoformat(date2)\n",
    "    return abs((b - a).days)\n",
    "\n",
    "config = types.GenerateContentConfig(tools=[calculator, days_between])\n",
    "\n",
    "r = client.models.generate_content(\n",
    "    model=MODEL,\n",
    "    contents=(\"My internship is from 2026-05-15 to 2026-07-20 and pays 25000 per month. \"\n",
    "              \"How many days is it, and roughly how much total if a month is 30 days?\"),\n",
    "    config=config)\n",
    "print(r.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ✓ Checkpoint 2 — it called BOTH tools to answer.\n",
    "\n",
    "---\n",
    "## Part C — See the machinery\n",
    "\n",
    "Turn OFF automatic calling to watch every function call the model requests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 4 — manual loop: see the raw function calls\n",
    "config = types.GenerateContentConfig(\n",
    "    tools=[calculator, days_between],\n",
    "    automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True))\n",
    "\n",
    "r = client.models.generate_content(\n",
    "    model=MODEL, contents=\"What is 15% of 8400 plus 200?\", config=config)\n",
    "\n",
    "for part in r.candidates[0].content.parts:\n",
    "    if part.function_call:\n",
    "        print(\"MODEL WANTS:\", part.function_call.name, dict(part.function_call.args))\n",
    "    elif part.text:\n",
    "        print(\"MODEL SAYS:\", part.text)\n",
    "# This is the request the model emits — YOUR code decides whether to run it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part D — Scenario cards (paper + pen, no code)\n",
    "\n",
    "For each, pick **Prompt / RAG / Tools / Fine-tune** and justify in 2 sentences. This is a mini design doc — and capstone rehearsal.\n",
    "\n",
    "1. A bot that answers questions about your college's 80-page attendance & exam rulebook, with citations.\n",
    "2. Replies are correct but too long and too formal; you want short and friendly.\n",
    "3. \"What's the weather in Madurai right now, and should I carry an umbrella to the exam?\"\n",
    "4. A model that must emit your exact 10-field JSON ticket format 50,000×/day on a small cheap model.\n",
    "\n",
    "### ✓ Checkpoint 3 — defend one choice to me out loud.\n",
    "\n",
    "---\n",
    "## Stretch goals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stretch 1 — plug your Session 4 RAG in as a TOOL (capstone move!)\n",
    "# Paste your search() + chunk_vecs setup from Lab 4 above this cell, then:\n",
    "\n",
    "def search_notes(query: str) -> str:\n",
    "    \"\"\"Search the student's personal course notes and return the most relevant passages.\n",
    "    Use this for any question about the student's specific courses, syllabus, or college.\"\"\"\n",
    "    hits = search(query, k=3)      # from your Lab 4 notebook\n",
    "    return \"\\n\\n\".join(c for s, c in hits)\n",
    "\n",
    "config = types.GenerateContentConfig(tools=[calculator, search_notes])\n",
    "r = client.models.generate_content(\n",
    "    model=MODEL,\n",
    "    contents=\"According to my notes, what's the pass mark — and if I have 12/25 internal, what % of the end-sem do I need?\",\n",
    "    config=config)\n",
    "print(r.text)\n",
    "# Your capstone is now: knowledge (RAG) + hands (tools). One assistant."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stretch 2 — break it, then fix it\n",
    "# Give the model a badly-described tool and watch it misfire:\n",
    "def mystery(x: str) -> str:\n",
    "    \"\"\"Does stuff.\"\"\"        # <- terrible docstring on purpose\n",
    "    return \"42\"\n",
    "# Ask something ambiguous with [calculator, mystery] as tools.\n",
    "# Then rewrite the docstring to be specific and watch behaviour change.\n",
    "# Lesson: the docstring IS the prompt."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Capstone: knowledge + hands + judge\n",
    "\n",
    "You now have RAG (S4) + tools (S5) + evals (S2). **Save the notebook.** Final session: we attack it, harden it, and you demo. Last break — then the finale."
   ]
  }
 ]
}