From 328faae953cd4cee1d54f409e512975360d9fe09 Mon Sep 17 00:00:00 2001 From: juanatsap Date: Thu, 9 Apr 2026 13:08:17 +0100 Subject: [PATCH] feat: chat module portability guide + fix mobile wave position - doc/30-CHAT-MODULE-PORTABILITY.md: step-by-step guide to port the chat agent to other Go apps (files, dependencies, customization) - Fix wave emoji position on mobile (follows button to bottom:5rem right:1rem) --- doc/30-CHAT-MODULE-PORTABILITY.md | 197 ++++++++++++++++++++++++++++ static/css/04-interactive/_chat.css | 6 + 2 files changed, 203 insertions(+) create mode 100644 doc/30-CHAT-MODULE-PORTABILITY.md diff --git a/doc/30-CHAT-MODULE-PORTABILITY.md b/doc/30-CHAT-MODULE-PORTABILITY.md new file mode 100644 index 0000000..76da2f1 --- /dev/null +++ b/doc/30-CHAT-MODULE-PORTABILITY.md @@ -0,0 +1,197 @@ +# Chat Module Portability Guide + +**Project:** CV Interactive Website +**Last Updated:** 2026-04-09 +**Tag Reference:** `v1.2.0` + +--- + +## Overview + +The AI chat agent is a self-contained module that can be ported to other Go web applications. It provides an embeddable chat widget powered by Google Gemini (production) with Ollama fallback (local development). + +--- + +## Module Files + +### Backend (Go) + +| File | Purpose | Dependencies | +|------|---------|-------------| +| `internal/chat/agent.go` | ADK agent definition, tools, prompt | `google.golang.org/adk`, your data model | +| `internal/chat/handler.go` | HTTP handlers, provider init, warmup, icon injection | `internal/chat/agent.go`, `internal/cache` | +| `internal/chat/ollama.go` | Ollama/OpenAI-compatible LLM adapter | None (standalone) | + +### Frontend (HTML + CSS + JS) + +| File | Purpose | +|------|---------| +| `templates/partials/widgets/chat-widget.html` | Complete widget: panel, header, messages, input, JS | +| `templates/partials/modals/chat-help-modal.html` | Help accordion with suggested questions | +| `static/css/04-interactive/_chat.css` | All styling: layout modes, responsive, dark mode, animations | + +### Configuration + +| File | Keys | +|------|------| +| `.env` | `GOOGLE_API_KEY`, `OLLAMA_MODEL`, `OLLAMA_HOST` | +| `config/systemd/cv.service` | `EnvironmentFile` for production secrets | + +--- + +## Go Dependencies + +Add to your `go.mod`: + +``` +google.golang.org/adk v1.0.0 +google.golang.org/genai v1.x.x +``` + +--- + +## Integration Steps + +### 1. Copy the backend files + +```bash +mkdir -p internal/chat +cp internal/chat/agent.go internal/chat/handler.go internal/chat/ollama.go YOUR_PROJECT/internal/chat/ +``` + +### 2. Adapt `agent.go` + +This is the only file that needs significant changes per project: + +- **`NewAgent()`**: Change the `Instruction` prompt to describe YOUR data, not a CV +- **`QueryCVArgs` / `QueryCVResult`**: Rename and adapt the tool to query YOUR data source +- **`newQueryCVTool()`**: Replace with a tool that queries your application's data +- **Filter functions**: Adapt `filterExperience()`, `filterProjects()`, etc. to your data model + +### 3. Adapt `handler.go` + +Minimal changes needed: + +- **`buildIconMap()`**: Remove or adapt for your icon system (sprite sheets, image files) +- **`formatResponse()`**: The markdown→HTML converter works generically. Icon injection is optional. +- **`NewHandler(dataCache)`**: Change the parameter type to your data source + +### 4. Keep `ollama.go` as-is + +This is a generic Ollama/OpenAI-compatible adapter. No changes needed — it implements `model.LLM` interface for any ADK agent. + +### 5. Copy frontend files + +```bash +cp templates/partials/widgets/chat-widget.html YOUR_PROJECT/templates/partials/widgets/ +cp templates/partials/modals/chat-help-modal.html YOUR_PROJECT/templates/partials/modals/ +cp static/css/04-interactive/_chat.css YOUR_PROJECT/static/css/ +``` + +### 6. Adapt the widget + +- Change suggested questions (chips) to match your domain +- Change help modal questions +- Update welcome message +- Adjust CSS variables to match your theme + +### 7. Register routes + +```go +// In your routes setup: +chatHandler := chat.NewHandler(yourDataSource) +mux.Handle("/api/chat", rateLimiter.Middleware(http.HandlerFunc(chatHandler.HandleChat))) +mux.HandleFunc("/api/chat/warmup", chatHandler.HandleWarmup) +mux.HandleFunc("/api/chat/status", chatHandler.HandleStatus) +``` + +### 8. Include in templates + +```html +{{template "chat-widget" .}} +{{template "chat-help-modal" .}} +``` + +Conditionally load CSS: +```html +{{if .ChatEnabled}} + +{{end}} +``` + +--- + +## Architecture Overview + +``` +User clicks chat → HTMX POST /api/chat + ↓ + handler.HandleChat() + ↓ + runAgent(primary) ← Gemini or Ollama + ↓ + ADK Runner loop: + 1. LLM sees prompt + user message + 2. LLM calls query tool (function calling) + 3. Tool queries your data source + 4. LLM generates response from tool results + ↓ + formatResponse() → HTML with icons + links + ↓ + HTMX appends to #chat-messages +``` + +--- + +## Features Included + +| Feature | What it does | +|---------|-------------| +| **Dual provider** | Gemini (prod) + Ollama (dev) with auto-fallback | +| **Auto-warmup** | Local model pre-loaded on startup in dev mode | +| **Status polling** | `/api/chat/status` → "Initializing AI model..." indicator | +| **4 layout modes** | Compact, Side Panel, Floating (draggable), Full Screen | +| **Mobile responsive** | Split mode on phones, desktop modes hidden | +| **User + bot avatars** | Teams-style bubble layout | +| **Inline icons** | Sprite + image fallback next to navigation links | +| **External links** | `[text](https://...)` rendered as clickable links | +| **Wave greeting** | 👋 animation to attract visitors | +| **Help modal** | Accordion with suggested questions | +| **Chip questions** | One-click with instant bubble rendering | +| **Rate limiting** | 30 req/hour per IP (configurable) | +| **Dark mode** | Lighter panel to contrast with dark backgrounds | +| **HTMX timeout** | 120s for slow local models | + +--- + +## Testing + +| Test file | Assertions | Covers | +|-----------|-----------|--------| +| `tests/mjs/84-chat-layout-modes.test.mjs` | 38 | Desktop layout modes, drag, switching, avatars | +| `tests/mjs/85-chat-mobile.test.mjs` | 79 | Mobile on 3 iPhone viewports + desktop sanity | +| `tests/mjs/83-chat-mascot.test.mjs` | 39 | Chat UX, chips, responses, navigation | + +--- + +## What to Customize Per Project + +| Component | What to change | +|-----------|---------------| +| Agent prompt | `agent.go` — describe YOUR domain, not a CV | +| Query tool | `agent.go` — query YOUR data source | +| Suggested questions | `chat-widget.html` — chips and help modal | +| Welcome message | `chat-widget.html` — greeting text | +| Icons/sprites | `handler.go` — `buildIconMap()` and CSS | +| CSS theme | `_chat.css` — colors, `--accent-green`, fonts | +| Rate limits | `routes.go` — requests per hour | + +--- + +## References + +- [Google ADK Go Documentation](https://google.github.io/adk-docs/) +- [Ollama API](https://github.com/ollama/ollama/blob/main/docs/api.md) +- [HTMX Documentation](https://htmx.org/docs/) +- [doc/28-AI-CHAT-AGENT.md](./28-AI-CHAT-AGENT.md) — Full technical documentation +- [doc/29-AI-CHAT-SHOWCASE.md](./29-AI-CHAT-SHOWCASE.md) — Public showcase writeup diff --git a/static/css/04-interactive/_chat.css b/static/css/04-interactive/_chat.css index 644fb32..a17d3c5 100644 --- a/static/css/04-interactive/_chat.css +++ b/static/css/04-interactive/_chat.css @@ -848,6 +848,12 @@ resize: none; } + /* Wave position follows mobile button */ + .chat-wave { + bottom: calc(5rem + 38px); + right: calc(1rem + 38px); + } + /* Tooltips: prevent overflow on mobile */ .chat-mode-btn[title]:hover::after { display: none;