feat: add AI chat widget powered by ADK Go 1.0

Visitors can ask questions about the CV via a floating chat panel.
The agent uses Gemini to answer questions about experience, projects,
skills, and education by querying the cached CV JSON data.

- internal/chat/agent.go: LLM agent with query_cv tool that searches
  CV data by section (experience, projects, skills, etc.) with keyword filtering
- internal/chat/handler.go: POST /api/chat endpoint with session management,
  graceful degradation when GOOGLE_API_KEY is not set
- chat-widget.html: HTMX-powered floating chat panel with Hyperscript toggle
- _chat.css: Responsive chat UI with dark theme support
- Wired into existing architecture via dependency injection (CVHandler,
  routes, main.go) — zero breaking changes, all existing tests pass
This commit is contained in:
juanatsap
2026-04-08 00:20:48 +01:00
parent 2ac4fbcd92
commit f5276431ea
13 changed files with 900 additions and 22 deletions
@@ -0,0 +1,59 @@
{{define "chat-widget"}}
{{if .ChatEnabled}}
<!-- AI Chat Widget -->
<button
id="chat-toggle-btn"
class="fixed-btn chat-toggle-btn no-print has-tooltip"
aria-label="Ask about this CV"
data-tooltip="Ask AI about this CV"
_="on click toggle .chat-open on #chat-panel
then if #chat-panel matches .chat-open
then set #chat-input.focus to true
then call #chat-input.focus()
end">
<iconify-icon icon="mdi:chat-outline" id="chat-icon-open"></iconify-icon>
<iconify-icon icon="mdi:close" id="chat-icon-close" style="display:none"></iconify-icon>
</button>
<div id="chat-panel" class="chat-panel no-print">
<div class="chat-header">
<iconify-icon icon="mdi:robot-outline"></iconify-icon>
<span>{{if eq .Lang "es"}}Pregunta sobre este CV{{else}}Ask about this CV{{end}}</span>
<button class="chat-close-btn"
_="on click remove .chat-open from #chat-panel">
<iconify-icon icon="mdi:close"></iconify-icon>
</button>
</div>
<div id="chat-messages" class="chat-messages">
<div class="chat-message chat-agent">
{{if eq .Lang "es"}}¡Hola! Puedo responder preguntas sobre este currículum. Prueba a preguntar sobre experiencia, proyectos, tecnologías o formación.{{else}}Hi! I can answer questions about this CV. Try asking about experience, projects, technologies, or education.{{end}}
</div>
</div>
<form id="chat-form" class="chat-input-area"
hx-post="/api/chat"
hx-target="#chat-messages"
hx-swap="beforeend scroll:#chat-messages:bottom"
hx-indicator="#chat-spinner"
_="on htmx:afterRequest set #chat-input.value to ''">
<input type="hidden" name="session_id" value="">
<input type="hidden" name="lang" value="{{.Lang}}">
<input
type="text"
id="chat-input"
name="message"
class="chat-input"
placeholder="{{if eq .Lang "es"}}Escribe una pregunta...{{else}}Type a question...{{end}}"
autocomplete="off"
required>
<button type="submit" class="chat-send-btn" aria-label="Send">
<iconify-icon icon="mdi:send"></iconify-icon>
</button>
<div id="chat-spinner" class="chat-spinner htmx-indicator">
<iconify-icon icon="mdi:loading" class="spin"></iconify-icon>
</div>
</form>
</div>
{{end}}
{{end}}