anam_code / anam /index.html
Vaibhav Kathait
Add en/it/fr/es/de language support; default to our own agent LLM instead of Anam's
7f73287
Raw History Blame Contribute Delete
15.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Anam AI Demo</title>
<link rel="stylesheet" href="/static/css/style.css" />
<script src="/static/js/anam-sdk.js"></script>
</head>
<body>
<header>
<div class="header-row">
<h1>Anam AI Demo</h1>
<div id="lang-switcher">
<label for="lang-select" data-i18n="lang.label">Language</label>
<select id="lang-select">
<option value="en">English</option>
<option value="it">Italiano</option>
<option value="fr">FranΓ§ais</option>
<option value="es">EspaΓ±ol</option>
<option value="de">Deutsch</option>
</select>
</div>
</div>
<p data-i18n="header.subtitle">Real-time conversation with an Anam AI persona β€” with document Q&amp;A</p>
</header>
<!-- Video -->
<div id="video-container">
<div id="placeholder">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
<circle cx="12" cy="8" r="4"/>
<path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
</svg>
<p data-i18n="placeholder.text">Avatar will appear here</p>
<p id="placeholder-hint" style="font-size:11px;margin-top:2px;" data-i18n="placeholder.hint">Click Connect to start</p>
<div id="placeholder-spinner"></div>
<p id="placeholder-status"></p>
</div>
<video id="avatar-video" autoplay playsinline></video>
<div id="rag-active-badge">πŸ“š RAG Active</div>
<div id="mic-indicator">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="2" width="6" height="12" rx="3"/>
<path d="M5 10a7 7 0 0 0 14 0M12 19v3M9 22h6"/>
</svg>
</div>
</div>
<!-- Status -->
<div id="status-bar">
<div id="status-dot"></div>
<span id="status-text" data-i18n="status.notConnected">Not connected</span>
<span id="call-timer"></span>
</div>
<!-- Plan toggle (Free = 2 min cap) -->
<div id="plan-section">
<div id="plan-row">
<span class="plan-label" id="plan-free-label" data-i18n="plan.free">Free Β· 2 min limit</span>
<label class="switch">
<input type="checkbox" id="plan-switch" checked />
<span class="knob"></span>
</label>
<span class="plan-label active" id="plan-pro-label" data-i18n="plan.pro">Pro Β· unlimited</span>
</div>
<div id="plan-banner">
⏱ <span data-i18n="plan.banner">Free plan: the avatar will tell you up front that you have 2 minutes together, show a live countdown, and the session will disconnect automatically when time runs out.</span>
</div>
</div>
<!-- Controls -->
<div class="controls">
<select id="persona-select"><option value="" data-i18n="persona.default">β€” Default ephemeral β€”</option></select>
<button id="connect-btn" data-i18n="controls.connect">Connect</button>
<button id="disconnect-btn" disabled data-i18n="controls.disconnect">Disconnect</button>
</div>
<div id="preset-row">
<button id="advanced-toggle" data-i18n="advanced.toggle">βš™ Advanced (system prompt)</button>
<button id="clinical-preset-btn" data-i18n="advanced.clinicalPreset">🩺 Clinical Assistant preset</button>
</div>
<div id="advanced-panel">
<label for="system-prompt-input" data-i18n="advanced.systemPromptLabel">System prompt (used when no persona selected and RAG is off)</label>
<textarea id="system-prompt-input" rows="3">You are a helpful, friendly AI assistant. Keep responses concise and conversational. Always respond in plain spoken language β€” never use bullet points, asterisks, pipe characters, markdown headers, or any formatting symbols.</textarea>
<!-- ── LLM selector ───────────────────────────────────────────────────── -->
<div style="margin-top:10px;display:flex;flex-direction:column;gap:4px">
<label for="llm-select" style="font-size:11px;color:#666;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">LLM</label>
<select id="llm-select" style="padding:8px 10px;border-radius:6px;border:1px solid #2a2a2a;background:#1a1a1a;color:#e0e0e0;font-size:13px">
<optgroup label="Ours (default)">
<option value="__agent__" selected>Our Agent LLM β€” tools &amp; notepad</option>
<option value="__custom__">Custom Anam LLM ID…</option>
</optgroup>
<optgroup label="Anam built-in">
<option value="a7cf662c-2ace-4de1-a21e-ef0fbf144bb7">GPT OSS 120B</option>
<option value="b4f89001-9638-4879-a9c3-02cc9f9f2004">GPT 4.1 β€” most capable</option>
<option value="ANAM_LLAMA_v3_3_70B_V1">Llama 3.3 70B β€” fastest (no tools/RAG)</option>
</optgroup>
</select>
</div>
<!-- shown only when Agent LLM is selected -->
<div id="agent-model-row" style="display:none;margin-top:6px;display:flex;flex-direction:column;gap:4px">
<label for="agent-model-input" style="font-size:11px;color:#666;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">OpenAI Model</label>
<input id="agent-model-input" list="agent-model-list" type="text" placeholder="gpt-4o-mini (default)"
style="padding:8px 10px;border-radius:6px;border:1px solid #2a2a2a;background:#1a1a1a;color:#e0e0e0;font-size:12px" />
<datalist id="agent-model-list">
<option value="gpt-4o-mini">
<option value="gpt-4o">
<option value="gpt-4-turbo">
<option value="gpt-4">
<option value="gpt-3.5-turbo">
<option value="o1-mini">
<option value="o1-preview">
</datalist>
</div>
<!-- shown only when Custom LLM ID is selected -->
<div id="custom-id-row" style="display:none;margin-top:6px;display:flex;flex-direction:column;gap:4px">
<label for="custom-llm-id-input" style="font-size:11px;color:#666;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Custom LLM ID</label>
<input id="custom-llm-id-input" type="text" placeholder="Paste your Anam LLM ID here"
style="padding:8px 10px;border-radius:6px;border:1px solid #2a2a2a;background:#1a1a1a;color:#e0e0e0;font-size:12px;font-family:monospace" />
</div>
<!-- Voice ID -->
<div style="margin-top:6px;display:flex;flex-direction:column;gap:4px">
<label for="voice-id-input" style="font-size:11px;color:#666;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Voice ID</label>
<input id="voice-id-input" type="text" placeholder="Leave blank for default"
style="padding:8px 10px;border-radius:6px;border:1px solid #2a2a2a;background:#1a1a1a;color:#e0e0e0;font-size:12px" />
</div>
<!-- hidden β€” driven by llm-select, kept for legacy JS compatibility -->
<input type="checkbox" id="custom-llm-checkbox" style="display:none" />
</div>
<!-- Mode tabs (shown after connect) -->
<div class="mode-tabs" id="mode-tabs" style="display:none">
<button class="mode-tab active" data-mode="voice" data-i18n="mode.voice">πŸŽ™ Voice</button>
<button class="mode-tab" data-mode="text" data-i18n="mode.text">⌨️ Text</button>
</div>
<!-- Input area -->
<div class="input-area">
<div id="voice-mode-row">
<div class="voice-btn-row">
<button id="mic-btn" disabled title="Toggle microphone">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="2" width="6" height="12" rx="3"/>
<path d="M5 10a7 7 0 0 0 14 0M12 19v3M9 22h6"/>
</svg>
</button>
<button id="interrupt-btn" disabled>⏹ Interrupt</button>
</div>
<span id="voice-hint" data-i18n="voice.hint">Microphone is active β€” speak to the avatar</span>
</div>
<div id="text-mode-row">
<input id="message-input" type="text" placeholder="Type a message to the avatar…" disabled data-i18n-placeholder="text.placeholder" />
<button id="send-btn" disabled data-i18n="text.send">Send</button>
</div>
</div>
<!-- Knowledge Base -->
<div id="kb-section">
<div class="kb-header">
<h3 data-i18n="kb.title">πŸ“š Knowledge Base</h3>
<div class="kb-header-right">
<!-- RAG mode tabs -->
<div class="rag-mode-tabs" id="rag-mode-tabs">
<button class="rag-mode-tab active" data-mode="anam" data-i18n="kb.cloudTab">☁ Anam Cloud</button>
<button class="rag-mode-tab" data-mode="local" data-i18n="kb.localTab">πŸ’» Local</button>
</div>
</div>
</div>
<!-- ── Anam Cloud RAG panel ─────────────────────────────────────────────── -->
<div id="anam-rag-panel" style="display:flex;flex-direction:column;gap:14px">
<div class="rag-mode-note" data-i18n="kb.cloudNote">Documents indexed in Anam's cloud β€” avatar searches per turn</div>
<div class="kb-header" style="margin-top:4px">
<div></div>
<div class="kb-header-right">
<span id="kb-setup-badge"></span>
<button class="kb-reset-btn" id="kb-reset-btn" title="Clear RAG state (does not delete from Anam)" data-i18n="kb.reset">Reset</button>
</div>
</div>
<div id="drop-zone">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p data-i18n="kb.dropHint">Drop documents here</p>
<span data-i18n="kb.dropSubtext">PDF Β· TXT Β· MD Β· DOCX Β· CSV β€” max 50 MB</span>
<button id="browse-btn" data-i18n="kb.browse">Browse files</button>
</div>
<input type="file" id="file-input" multiple accept=".pdf,.txt,.md,.docx,.csv" hidden />
<div id="upload-status">
<div class="spinner"></div>
<span id="upload-status-text" data-i18n="kb.uploading">Uploading…</span>
</div>
<div id="doc-list"></div>
<label id="use-rag-row" for="use-rag-checkbox">
<input type="checkbox" id="use-rag-checkbox" checked />
<span id="use-rag-label" data-i18n="kb.useRagLabel">Use knowledge base β€” avatar will search your docs when answering</span>
<span id="rag-doc-count"></span>
</label>
<div id="rag-prompt-section">
<button id="rag-prompt-toggle" data-i18n="kb.editPrompt">✏️ Edit RAG assistant behaviour (system prompt)</button>
<div id="rag-prompt-body" style="display:none;flex-direction:column;gap:8px">
<label for="rag-system-prompt" data-i18n="kb.promptLabel">
System prompt β€” controls how the avatar uses the knowledge base and answers general questions
</label>
<textarea id="rag-system-prompt" rows="6"></textarea>
<div class="rag-prompt-actions">
<button id="apply-rag-prompt-btn" data-i18n="kb.applyPrompt">Apply to persona</button>
<span id="rag-prompt-hint" data-i18n="kb.promptHint">Changes apply to the next session you start.</span>
</div>
</div>
</div>
</div>
<!-- ── Local RAG panel ──────────────────────────────────────────────────── -->
<div id="local-rag-panel" style="display:none;flex-direction:column;gap:14px">
<div class="rag-mode-note"><span data-i18n="kb.localNote">Documents chunked &amp; embedded locally β€” only the most relevant passages are retrieved per question.</span>
<br><span style="color:#555" data-i18n="kb.localNoteSub">Ideal for clinical guidelines, protocols &amp; reference PDFs. Embeddings persist across restarts.</span>
</div>
<div id="local-drop-zone">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p data-i18n="kb.localDropHint">Drop reference documents here</p>
<span data-i18n="kb.localDropSubtext">PDF Β· DOCX Β· TXT Β· MD</span>
<button id="local-browse-btn" data-i18n="kb.localBrowse">Browse files</button>
</div>
<input type="file" id="local-file-input" multiple accept=".pdf,.docx,.txt,.md" hidden />
<div id="local-upload-status" style="display:none">
<div class="spinner"></div>
<span id="local-upload-status-text">Parsing…</span>
</div>
<div id="local-doc-list"></div>
<label id="use-local-rag-row" for="use-local-rag-checkbox" style="display:none">
<input type="checkbox" id="use-local-rag-checkbox" checked />
<span data-i18n="kb.localInjectLabel">Inject documents into avatar's context when connecting</span>
<span id="local-doc-count"></span>
</label>
</div>
</div>
<!-- Blood Test Report -->
<div id="clinical-section">
<div class="kb-header">
<h3 data-i18n="clinical.title">🩺 Blood Test Report</h3>
<div class="kb-header-right">
<span id="report-status-badge"></span>
<button class="kb-reset-btn" id="report-reset-btn" title="Clear uploaded report" data-i18n="kb.reset">Reset</button>
</div>
</div>
<div id="report-drop-zone">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p data-i18n="clinical.dropHint">Drop your blood test report here</p>
<span data-i18n="clinical.dropSubtext">Text-based PDF β€” max 50 MB</span>
<button id="report-browse-btn" data-i18n="clinical.browse">Browse files</button>
</div>
<input type="file" id="report-file-input" accept=".pdf" hidden />
<div id="report-upload-status">
<div class="spinner"></div>
<span id="report-upload-status-text" data-i18n="clinical.analysing">Analysing report…</span>
</div>
<div id="report-summary"></div>
</div>
<!-- Usage & cost telemetry -->
<div id="telemetry-panel">
<div class="tm-header">
<h3 data-i18n="telemetry.title">πŸ“Š Session Cost</h3>
<button class="kb-reset-btn" id="telemetry-reset-btn" data-i18n="kb.reset">Reset</button>
</div>
<div id="telemetry-stats">
<div class="tm-stat">
<span class="tm-stat-value" id="tm-total-cost">$0.00</span>
<span class="tm-stat-label" data-i18n="telemetry.cost">Estimated cost</span>
</div>
<div class="tm-stat">
<span class="tm-stat-value" id="tm-total-minutes">0</span>
<span class="tm-stat-label" data-i18n="telemetry.minutes">Minutes billed</span>
</div>
<div class="tm-stat">
<span class="tm-stat-value" id="tm-session-count">0</span>
<span class="tm-stat-label" data-i18n="telemetry.sessions">Sessions</span>
</div>
<div class="tm-stat live" id="tm-live-stat" style="display:none">
<span class="tm-stat-value" id="tm-live-cost">$0.00</span>
<span class="tm-stat-label" data-i18n="telemetry.live">Live session</span>
</div>
</div>
<p id="telemetry-rate-note"></p>
<div id="telemetry-sessions"></div>
</div>
<div id="log"></div>
<script type="module" src="/static/js/app.js"></script>
</body>
</html>