{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 🧪 LearnAI Workshop — Session 1: Foundations\n",
    "### Practical notebook · runs on **Google Colab** · powered by **Google Gemini (free)**\n",
    "\n",
    "You'll talk to a real AI three ways today and **test each step before moving on**:\n",
    "\n",
    "| Station | You'll do | ✅ You're done when |\n",
    "|---|---|---|\n",
    "| **1 · What is this?** | Make Gemini predict + catch a hallucination | You printed a reply *and* caught a made-up fact |\n",
    "| **2 · Meet the models** | Ask one question to two models | You compared quality *and* speed |\n",
    "| **3 · Talk with code** | Send prompts, count tokens, turn dials | Same prompt, two temperatures, different replies |\n",
    "\n",
    "> **How to use this:** click each grey **code cell** and press **▶️ (or Shift+Enter)**. Read the note above it first. Run them **in order** from the top."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🔑 Step 0 — Get your free Gemini key (once)\n",
    "1. Go to **https://aistudio.google.com/apikey** (sign in with a Google account).\n",
    "2. Click **Create API key** → copy it.\n",
    "3. Come back here and run the cells below. When asked, paste the key and press **Enter**.\n",
    "\n",
    "🔒 **Safety:** an API key is a password. Don't paste it into a public doc, a group chat, or GitHub."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Install the Gemini SDK (takes ~15s the first time)\n",
    "!pip -q install google-genai"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Connect to Gemini using your key\n",
    "from google import genai\n",
    "from google.genai import types\n",
    "import getpass, time\n",
    "\n",
    "API_KEY = getpass.getpass('Paste your Gemini API key and press Enter: ').strip()\n",
    "client = genai.Client(api_key=API_KEY)\n",
    "print('Client ready ✅')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pick a model automatically\n",
    "Model names change over time, so instead of hard-coding one, we **ask your key which models it can use** and grab a fast one. (This also means the notebook keeps working even after Google renames things.)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# List the models your key can see, then auto-pick a fast one\n",
    "model_names = [m.name for m in client.models.list()]\n",
    "flash = [m for m in model_names if 'flash' in m and 'embedding' not in m]\n",
    "FAST_MODEL = flash[0] if flash else 'models/gemini-2.5-flash'\n",
    "print('Using FAST_MODEL =', FAST_MODEL)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ✅ TEST: does everything work? You should see a short reply, then a checkmark.\n",
    "resp = client.models.generate_content(model=FAST_MODEL, contents='Say hello in exactly 5 words.')\n",
    "print(resp.text)\n",
    "assert resp.text, 'No text came back — re-check your API key and the model name above.'\n",
    "print('\\n✅ Setup works — you are talking to a real AI!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟦 Station 1 — Watch AI *predict*\n",
    "An LLM doesn't look up answers. It **predicts the most likely next chunk of text**, over and over. Let's see it happen."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Ask it to finish one sentence five different ways\n",
    "prompt = \"Finish this sentence 5 different ways, one per line: 'The best part of summer is going to the ...'\"\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=prompt).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** you should see 5 believable endings. There's no single 'right' answer — each is just a *likely* next word. That **is** the prediction loop."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Now the flip side of prediction: it will confidently make things up.\n",
    "fake_book = 'The Blue Lantern of Cairo'\n",
    "q = f\"How many pages exactly is the book '{fake_book}'? Give one specific number.\"\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=q).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "⚠️ **That book does not exist.** If the AI gave a confident page count, you just caught a **hallucination** — a plausible-sounding but false answer. This happens *because* it predicts likely text instead of looking up truth. (We fix this in Session 3.)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: change fake_thing to anything invented and see if the AI plays along.\n",
    "fake_thing = 'the 2019 Nobel Prize in Skateboarding'  # <-- edit me\n",
    "print(client.models.generate_content(model=FAST_MODEL, contents=f'Tell me about {fake_thing}.').text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> ✅ **Station 1 complete** when you've (1) seen 5 predicted endings and (2) caught at least one made-up 'fact'."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟨 Station 2 — Same question, two models\n",
    "There isn't one 'best' model — there are trade-offs: **smarts vs speed vs price**. Small *flash* models are fast and cheap; bigger *pro* models are smarter but slower. Let's feel the difference."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# See every model your key can use\n",
    "for m in client.models.list():\n",
    "    print(m.name)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Auto-pick a stronger 'pro' model too (falls back to fast if none)\n",
    "pro = [m for m in model_names if 'pro' in m and 'embedding' not in m and 'vision' not in m]\n",
    "STRONG_MODEL = pro[0] if pro else FAST_MODEL\n",
    "print('FAST  =', FAST_MODEL)\n",
    "print('STRONG=', STRONG_MODEL)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Ask BOTH the same tricky question and time each one.\n",
    "# (Classic trap: the quick 'obvious' answer is 10 cents — but it's wrong!)\n",
    "question = ('A bat and a ball cost $1.10 together. '\n",
    "            'The bat costs $1.00 more than the ball. '\n",
    "            'How much does the ball cost? Show your reasoning, then give the final number.')\n",
    "\n",
    "for label, model in [('FAST', FAST_MODEL), ('STRONG', STRONG_MODEL)]:\n",
    "    t0 = time.time()\n",
    "    answer = client.models.generate_content(model=model, contents=question).text\n",
    "    secs = time.time() - t0\n",
    "    print(f'\\n===== {label}  ({model})  —  {secs:.1f} seconds =====')\n",
    "    print(answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** both answers printed. Now compare:\n",
    "- Did each get it right? (The correct answer is **5 cents**.)\n",
    "- Which was faster? Was the slower one *worth* the wait?\n",
    "\n",
    "That judgment — *is the extra quality worth the extra time/cost?* — is real builder thinking."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: put your own hard question here and compare the two models.\n",
    "my_question = 'Explain why the sky is blue to a 6-year-old, in 3 sentences.'  # <-- edit me\n",
    "print('FAST:\\n', client.models.generate_content(model=FAST_MODEL, contents=my_question).text)\n",
    "print('\\nSTRONG:\\n', client.models.generate_content(model=STRONG_MODEL, contents=my_question).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> ✅ **Station 2 complete** when you've compared both models' answers *and* their speed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "# 🟩 Station 3 — Talk with code: tokens & dials\n",
    "Everything above was the **API loop**: send text → get text. Now the two dials every builder should know — **tokens** (what you pay for) and **temperature** (creativity)."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Models read text in 'tokens' (~3/4 of a word). You pay per token.\n",
    "text = 'Write a haiku about the ocean at night.'\n",
    "n = client.models.count_tokens(model=FAST_MODEL, contents=text).total_tokens\n",
    "print(f'Your prompt is {n} tokens.')\n",
    "print('Reply:\\n', client.models.generate_content(model=FAST_MODEL, contents=text).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** you saw a token count. Longer prompts = more tokens = more cost and slower replies. That's why builders keep prompts tight."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# temperature = creativity dial. 0.0 = safe & repeatable, 1.0 = wild & varied.\n",
    "# Run this cell 2-3 times and watch how each temperature behaves.\n",
    "for temp in [0.0, 1.0]:\n",
    "    cfg = types.GenerateContentConfig(temperature=temp, max_output_tokens=40)\n",
    "    out = client.models.generate_content(\n",
    "        model=FAST_MODEL,\n",
    "        contents='Invent a fun name for a new ice cream shop.',\n",
    "        config=cfg,\n",
    "    ).text\n",
    "    print(f'----- temperature = {temp} -----')\n",
    "    print(out, '\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🧪 **Test:** run it a few times. At **temperature 0** the name barely changes (it picks the safe favorite). At **temperature 1** it jumps around. *When would a builder want each?* (0 for consistent tasks like data cleanup; 1 for brainstorming.)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# 🎯 YOUR TURN: force a ONE-WORD answer. Try tightening the instruction and the max tokens.\n",
    "cfg = types.GenerateContentConfig(max_output_tokens=5, temperature=0)\n",
    "out = client.models.generate_content(\n",
    "    model=FAST_MODEL,\n",
    "    contents='In ONE word only, what is the capital of France?',\n",
    "    config=cfg,\n",
    ").text\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> ✅ **Station 3 complete** when the same prompt gave visibly different replies at temperature 0 vs 1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 🎉 You finished Session 1!\n",
    "You just:\n",
    "- Talked to a real AI and watched it **predict** (and hallucinate).\n",
    "- Compared **two models** on quality and speed.\n",
    "- Sent prompts **from code**, counted **tokens**, and turned the **temperature** dial.\n",
    "\n",
    "**Next up — Session 2: Building.** You'll write great prompts, give the AI *your own* notes to read (RAG), and build an AI **agent** that takes actions.\n",
    "\n",
    "_LearnAI · part of MillionRoots. Keep this notebook — you can reuse the setup cells any time._"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "LearnAI Session 1 — Foundations",
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}