# Les 12 — Huiswerk
## Write-tool + UI visualisatie + reflectie

**Vak:** AI-Assisted Development
**Opleiding:** NOVI Hogeschool Utrecht
**Deadline:** Voor de volgende les (Les 13 — Agents)
**Inleveren:** GitHub repo + `TOOLS.md` in root

---

## Doel

Bouwt voort op de **lesopdracht** (jouw thema-app met 3 read-tools). Hier voeg je een **write-tool** toe, visualiseert tool-calls in je UI, en documenteert in `TOOLS.md`.

> Niet klaar met de lesopdracht? Eerst die afmaken — het huiswerk heeft de 3 read-tools als startpunt nodig.

---

## Onderdeel A — Write-tool toevoegen (verplicht)

Voeg een tool toe waarmee de gebruiker iets **kan opslaan** in je database.

### Stappen

1. **Nieuwe tabel in Supabase** — voor user-acties. Voorbeelden:

   ```sql
   create table user_favorites (
     id          bigserial primary key,
     user_email  text not null,
     item_id     bigint not null references items(id) on delete cascade,
     created_at  timestamp default now(),
     unique(user_email, item_id)
   );
   ```

   Andere ideeën — afhankelijk van thema:
   - `user_notes` — eigen aantekeningen per item
   - `user_votes` — like/dislike per item
   - `user_watchlist` — bewaar voor later
   - `user_visited` — track wat al gezien is

2. **Write-tool schrijven**

   ```typescript
   const addToFavorites = tool({
     description:
       "Voeg een item toe aan de favorieten van de gebruiker. " +
       "Alleen gebruiken als de gebruiker expliciet vraagt om iets toe te voegen.",
     inputSchema: z.object({
       userEmail: z.string().email(),
       itemName: z.string(),
     }),
     execute: async ({ userEmail, itemName }) => {
       const { data: item } = await supabase
         .from("items").select("id").ilike("name", itemName).single();
       if (!item) return { error: `'${itemName}' niet gevonden.` };
       const { error } = await supabase
         .from("user_favorites")
         .insert({ user_email: userEmail, item_id: item.id });
       if (error) return { error: error.message };
       return { success: true, itemName };
     },
   });
   ```

3. **Lees-tool er bij** (om de favorieten weer terug te halen):

   ```typescript
   const listFavorites = tool({
     description: "Geef de favorieten van de gebruiker.",
     inputSchema: z.object({ userEmail: z.string().email() }),
     execute: async ({ userEmail }) => {
       const { data, error } = await supabase
         .from("user_favorites")
         .select("items(name, ...)")
         .eq("user_email", userEmail);
       if (error) return { error: error.message };
       return data?.map((r) => r.items) ?? [];
     },
   });
   ```

4. **Test in chat**:
   - "Voeg X toe aan mijn favorieten, mijn email is test@test.nl"
   - "Wat staat er nu in mijn favorieten?"

### Eisen

- [ ] Nieuwe tabel in Supabase (zelfde regels: RLS aan, policies voor demo)
- [ ] Write-tool werkt — favorieten worden opgeslagen
- [ ] List-tool werkt — favorieten worden teruggehaald
- [ ] System prompt aangepast — "alleen toevoegen op expliciete request"

---

## Onderdeel B — Tool-calls in UI tonen (verplicht)

Refactor je chat-UI om **tool-invocations zichtbaar te maken**.

### Hoe

`useChat` returnt `messages` met **parts** — een array van delen. Tekst-parts én tool-invocation-parts.

```tsx
{messages.map((m) => (
  <div key={m.id}>
    {m.parts?.map((part, i) => {
      if (part.type === "text") {
        return <div key={i}>{part.text}</div>;
      }
      // In AI SDK v6 zijn tool-parts genaamd `tool-<toolName>`
      if (part.type?.startsWith("tool-")) {
        const toolName = part.type.replace("tool-", "");
        return (
          <div key={i} className="bg-yellow-50 border border-yellow-300 p-2 rounded text-sm">
            🔧 <strong>{toolName}</strong>({JSON.stringify(part.input)})
            {part.state === "output-available" && (
              <details className="mt-1">
                <summary>Toon resultaat</summary>
                <pre>{JSON.stringify(part.output, null, 2)}</pre>
              </details>
            )}
          </div>
        );
      }
      return null;
    })}
  </div>
))}
```

### Eisen

- [ ] Tool-invocations zichtbaar als chip / badge / box
- [ ] Tool-naam + args getoond
- [ ] Resultaat (collapsed) zichtbaar bij open-klikken
- [ ] Multi-step vragen tonen meerdere chips

---

## Onderdeel C — `TOOLS.md` documentatie (verplicht)

Schrijf in je repo-root een markdown-bestand met de volgende secties.

### Sectie 1 — Mijn tools

Tabel met al je tools:

| Tool | Wat doet 't | Read / Write |
|------|-------------|--------------|
| searchItems | Filter op X, Y, Z | Read |
| getItemById | Detail van één item | Read |
| getStats | Verdeling per groep | Read |
| addToFavorites | User favoriet opslaan | **Write** |
| listFavorites | User favorieten ophalen | Read |

### Sectie 2 — Voorbeeld-vragen

**3 vragen die 1 tool gebruiken:**

1. *Vraag*: "..."
   *Tool die triggered*: `searchItems({ ... })`
   *Antwoord (samenvatting)*: ...

2. (idem)

3. (idem)

**1 vraag die 2+ tools combineert (multi-step):**

*Vraag*: "..."
*Tools die triggeren in volgorde*:
1. `searchItems(...)` — krijgt 5 items
2. `getItemById(...)` — detail van item #3
*Antwoord*: ...

### Sectie 3 — Eén edge-case die AI goed afhandelde

*Wat gebeurde er*: ...
*Welke tool was 't*: ...
*Hoe handelde AI 't af*: ...

Voorbeelden van edge cases:
- Ongeldige enum-value
- Lege resultaten
- Database error
- Vraag waar geen tool voor bestaat

### Vorm

- Max 500 woorden totaal
- Concrete voorbeelden
- Mag wat informeel

---

## Bonus (optioneel)

- **Loading indicator** per tool-execute (spinner naast tool-naam tijdens running)
- **Mooie cards** voor tool-results in plaats van JSON-dump
- **Confirmation UI** voor write-tools (bevestig vóór insert)
- **Tool failure recovery** — als tool faalt, AI probeert andere tool

---

## Inleveren

1. **GitHub repo URL** in Brightspace
2. **`TOOLS.md`** in repo-root
3. **Schema-update** in `schema.sql` (met nieuwe tabel)
4. **Updated `app/api/chat/route.ts`** met write-tool
5. **Updated `app/chat/page.tsx`** met tool-call rendering

---

## Beoordeling

| Criterium | Punten |
|-----------|--------|
| A — Write-tool werkt + list-tool werkt | 3 |
| B — Tool-calls in UI zichtbaar | 2 |
| C — TOOLS.md aanwezig met 3 secties | 3 |
| Chat werkt end-to-end (geen broken pages) | 1 |
| Documentatie sectie 3 (edge case) is concreet | 1 |
| **Totaal** | **10** |

Voldoende = 6+. Bonus telt mee bij twijfelgevallen.

---

## Tijd-indicatie

| Onderdeel | Tijd |
|-----------|------|
| A — Write-tool (tabel + tool + test) | 40 min |
| B — UI tool-call rendering | 30 min |
| C — TOOLS.md schrijven | 20 min |
| **Totaal** | **~1,5 uur** |

---

## Veelvoorkomende valkuilen

- **Write-tool zonder permissies** — werkt voor demo, maar in productie crash je als anon-user iets probeert te schrijven. Voor nu: RLS-policies open zetten.
- **Confusion AI tussen read en write** — sterke description: "alleen gebruiken als gebruiker expliciet vraagt om..."
- **UI part-types niet ondersteund** — check welke versie AI SDK je hebt. In v6 zijn tool-parts genaamd `tool-<toolName>` (niet meer `tool-invocation`). Oudere versies hadden `toolInvocations` array.
- **`details` collapsed in dev maar uitgeklapt in prod** — Tailwind / browser-default. Test in productie.
- **`useChat` returnt parts undefined** — check je AI SDK versie, run `npm update ai`

---

## Tips

- **Schrijf TOOLS.md gaandeweg** — niet aan eind. Sla goede voorbeelden + screenshots op zodra ze werken.
- **Test write-tool eerst los** — voordat je 'm via chat triggert, run de execute-functie handmatig. Sneller debuggen.
- **UI parts is nieuw** — als je oude tutorial volgt, kan API anders zijn. Check `ai-sdk.dev/docs` voor de actuele syntax.
- **Edge case 'goed' afhandelen** is subjectief — schrijf in TOOLS.md wat **jouw** definitie van 'goed' was.

Volgende les: Agents. Met je tools werkend gaan we dan zien hoe AI 30+ tool-calls in één request kan plannen. Tot dan!
