Spaces:
Running
Running
| // Front-end JavaScript - app.js | |
| console.log("app.js loaded") | |
| // Auto- fill and load session ID if exists (for now, just from global variable, can be extended to localStorage or URL param) | |
| window.onload = () => { | |
| if (window.session_id) { | |
| document.getElementById("session_display").value = window.session_id | |
| } | |
| } | |
| // ------------------------------- | |
| // GLOBAL STATE | |
| // ------------------------------- | |
| window.conversationStarted = false | |
| // Global memory store for agents | |
| window.agentMemory = {} | |
| // --------------------------------------------------------- | |
| // INITIAL TRIGGER - Fire (Start Conversation; Run Analysis) | |
| // --------------------------------------------------------- | |
| //async function runAnalysis(){ | |
| //async function fire() { | |
| window.fire = async function () { | |
| //const inputBox = document.getElementById("initial_input") | |
| const inputBox = document.getElementById("main_input") | |
| //const text = inputBox.value | |
| //console.log("π Run Analysis:", text) | |
| // safety check to prevent silent errrors if element isn't found | |
| if (!inputBox) { | |
| console.error("β main_input not found") | |
| return | |
| } | |
| const text = inputBox.value | |
| if (!text) return | |
| // Create session once | |
| if (!window.session_id) { | |
| window.session_id = Math.random().toString(36).substring(7) | |
| // π₯ Display it on UI | |
| document.getElementById("session_display").value = window.session_id | |
| } | |
| // Mark conversation started | |
| window.conversationStarted = true | |
| // UI changes | |
| switchToConversationMode() | |
| // Show user message | |
| appendMessage("user", text) | |
| // Show "Thinking..." | |
| showThinking() | |
| inputBox.value = "" | |
| // Disable Run Analysis permanently after first use | |
| //document.querySelector('button[onclick="runAnalysis()"]').disabled = true | |
| await sendToBackend(text) | |
| } | |
| // ----------------------------------------------------- | |
| // FUEL - FOLLOW-UP (Continue Conversation; Send button) | |
| // ----------------------------------------------------- | |
| //async function sendFollowup(){ | |
| //async function fuel() { | |
| window.fuel = async function () { | |
| //const inputBox = document.getElementById("followup_input") | |
| const inputBox = document.getElementById("main_input") | |
| //const text = inputBox.value | |
| if (!inputBox) { | |
| console.error("β main_input not found") | |
| return | |
| } | |
| const text = inputBox.value | |
| //console.log("π¨ Follow-up:", text) | |
| if (!text) return | |
| // Show user message | |
| appendMessage("user", text) | |
| // Show "Thinking..." | |
| showThinking() | |
| inputBox.value = "" | |
| await sendToBackend(text) | |
| } | |
| // ----------------- | |
| // UI Mode Switcher | |
| // ----------------- | |
| function switchToConversationMode() { | |
| const inputBox = document.getElementById("main_input") | |
| // Change placeholder | |
| inputBox.placeholder = "Continue..." | |
| // Replace Fire β Fuel | |
| const fireBtn = document.getElementById("fire_btn") | |
| const fuelBtn = document.createElement("button") | |
| fuelBtn.innerText = "Fuel" | |
| fuelBtn.id = "fuel_btn" | |
| //fuelBtn.onclick = fuel | |
| fuelBtn.onclick = window.fuel | |
| fireBtn.replaceWith(fuelBtn) | |
| // Show End Session button | |
| document.getElementById("end_btn").style.display = "inline-block" | |
| } | |
| // -------------------------------------------------------------------------------------------------------- | |
| // LOAD SESSION - Allow loading previous session by ID (for now, no authentication, just input ID and load) | |
| // -------------------------------------------------------------------------------------------------------- | |
| //function loadSession() { | |
| async function loadSession() { | |
| const input = document.getElementById("session_input").value | |
| if (!input) return | |
| window.session_id = input | |
| const res = await fetch(`/session/${input}`) | |
| const data = await res.json() | |
| if (data.error) { | |
| alert("Session not found") | |
| return | |
| } | |
| // Set session display | |
| document.getElementById("session_display").value = input | |
| // Switch UI mode | |
| window.conversationStarted = true | |
| switchToConversationMode() | |
| // Clear chat - Restore chat visually | |
| const chatBox = document.getElementById("chat") | |
| chatBox.innerHTML = "" | |
| // Restore messages | |
| data.history.forEach(msg => { | |
| if (msg.role === "client") { | |
| appendMessage("user", msg.content) | |
| } else { | |
| updateThinking(msg.content, "jung") | |
| } | |
| }) | |
| console.log("β»οΈ Session loaded:", input) | |
| } | |
| // ----------------------------------------------------------- | |
| // BACKEND CALL (SHARED) - PASS AGENT FROM BACKEND TO FRONTEND | |
| // ----------------------------------------------------------- | |
| async function sendToBackend(text) { | |
| const API_URL = window.location.origin + "/chat" | |
| console.log("π‘ Sending to backend:", text) | |
| try { | |
| const res = await fetch(API_URL, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json" | |
| }, | |
| body: JSON.stringify({ | |
| session_id: window.session_id, | |
| message: text | |
| }) | |
| }) | |
| const data = await res.json() | |
| //removeThinking() | |
| // Format text (single line breaks) | |
| const formatted = data.content.replace(/\n+/g, "<br>") | |
| // Update thinking, prevents duplicate messages | |
| //updateThinking(formatted) | |
| updateThinking(data.content, data.agent) | |
| // π₯ HANDLE CHOICE TYPE | |
| if (data.type === "choice") { | |
| renderChoices(data.choices) | |
| } | |
| // π₯ Store agent memory if present | |
| if (data.agent_log) { | |
| const agent = data.agent_log.agent | |
| window.agentMemory[agent] = data.agent_log.history | |
| updateAgentBox(agent, data.agent_log.history) | |
| } | |
| // π§ HANDLE EPISTEMIC (PASSIVE OBSERVER) | |
| if (data.witness_updates) { | |
| for (const agent in data.witness_updates) { | |
| const content = data.witness_updates[agent] | |
| // Epistemic is not conversational β single block | |
| updateAgentBox(agent, [ | |
| { | |
| role: "agent", | |
| text: content | |
| } | |
| ]) | |
| } | |
| } | |
| } catch (err) { | |
| console.error("β ERROR:", err) | |
| removeThinking() | |
| appendMessage("assistant", "Error: server not reachable") | |
| } | |
| } | |
| // ------------------------------- | |
| // NEXT AGENT SELECTION | |
| // ------------------------------- | |
| function renderChoices(choices) { | |
| const chatBox = document.getElementById("chat") | |
| const container = document.createElement("div") | |
| container.className = "choice-container" | |
| choices.forEach(choice => { | |
| const btn = document.createElement("button") | |
| btn.innerText = choice.label | |
| btn.className = "choice-btn" | |
| btn.onclick = () => { | |
| handleAgentSelection(choice.id) | |
| container.remove() // remove after click | |
| } | |
| container.appendChild(btn) | |
| }) | |
| chatBox.appendChild(container) | |
| chatBox.scrollTop = chatBox.scrollHeight | |
| } | |
| // ------------------------------- | |
| // HANDLE NEXT AGENT SELECTION | |
| // ------------------------------- | |
| function handleAgentSelection(agentId) { | |
| console.log("π§ Selected agent:", agentId) | |
| // Send agent selection as special message | |
| sendToBackend(`__agent__:${agentId}`) | |
| showThinking() | |
| } | |
| // ------------------------------- | |
| // SHOW "THINKING..." | |
| // ------------------------------- | |
| function showThinking() { | |
| const chatBox = document.getElementById("chat") | |
| const thinking = document.createElement("div") | |
| thinking.id = "thinking" | |
| thinking.className = "ai-msg" | |
| thinking.innerHTML = ` | |
| <span class="label-agent">Agent Jung:</span> | |
| <br><br> | |
| Thinking... | |
| ` | |
| chatBox.appendChild(thinking) | |
| } | |
| // ------------------------------- | |
| // REPLACE "THINKING..." WITH RESPONSE | |
| // ------------------------------- | |
| function updateThinking(text, agent = "jung") { | |
| let thinking = document.getElementById("thinking") | |
| const formatted = text.replace(/\n+/g, "<br>") | |
| // Convert agent id β display name | |
| const agentName = formatAgentName(agent) | |
| if (!thinking) { | |
| const chatBox = document.getElementById("chat") | |
| thinking = document.createElement("div") | |
| thinking.className = "ai-msg" | |
| chatBox.appendChild(thinking) | |
| } | |
| thinking.innerHTML = ` | |
| <span class="label-agent">Agent ${agentName}:</span> | |
| <br><br> | |
| ${formatted} | |
| ` | |
| thinking.id = "" | |
| } | |
| // ------------------------------- | |
| // REMOVE THINKING (fallback) | |
| // ------------------------------- | |
| function removeThinking() { | |
| const thinking = document.getElementById("thinking") | |
| if (thinking) thinking.remove() | |
| } | |
| // ------------------------------- | |
| // RENDER USER MESSAGE ONLY | |
| // ------------------------------- | |
| function appendMessage(role, text) { | |
| const chatBox = document.getElementById("chat") | |
| const msg = document.createElement("div") | |
| if (role === "user") { | |
| msg.innerHTML = ` | |
| <span class="label-client">Client:</span> | |
| <br><br> | |
| ${text} | |
| ` | |
| msg.className = "user-msg" | |
| } | |
| // β IMPORTANT: | |
| // We DO NOT render assistant here anymore | |
| // Assistant is handled ONLY via updateThinking() | |
| chatBox.appendChild(msg) | |
| chatBox.scrollTop = chatBox.scrollHeight | |
| } | |
| // ------------------------------- | |
| // FORMAT AGENT NAME | |
| // ------------------------------- | |
| function formatAgentName(agent) { | |
| const map = { | |
| jung: "Jung", | |
| dream: "Dream", | |
| shadow: "Shadow", | |
| myth: "Myth", | |
| epistemic: "Epistemic", | |
| bpsy: "BPsy", | |
| jred: "JRed", | |
| synthesis: "Synthesis" | |
| } | |
| return map[agent] || agent | |
| } | |
| // ------------------------------- | |
| // UPDATE AGENT BOX | |
| // ------------------------------- | |
| function updateAgentBox(agent, history) { | |
| const box = document.getElementById(agent) | |
| if (!box) return | |
| let html = "" | |
| history.forEach(entry => { | |
| if (agent === "epistemic") { | |
| html += formatEpistemic(entry.text) | |
| } else { | |
| if (entry.role === "client") { | |
| html += `<b>Client:</b> ${entry.text}<br><br>` | |
| } else { | |
| html += `<b>Agent:</b> ${entry.text}<br><br>` | |
| } | |
| } | |
| }) | |
| box.innerHTML = html | |
| } | |
| // ----------------------------------------------------- | |
| // HELPER FOR FORMATTING EPISTEMIC INSIGHTS WITH HEADERS | |
| // ----------------------------------------------------- | |
| function formatEpistemic(text) { | |
| return text | |
| .replace("SUMMARY β CLIENT:", "<span class='insight-header'>Summary β Client</span>") | |
| .replace("CROSS-ANALYSIS β CLIENT:", "<span class='insight-header'>Cross-Analysis β Client</span>") | |
| .replace("SUMMARY β AGENT JUNG:", "<span class='insight-header'>Summary β Agent Jung</span>") | |
| .replace("CROSS-ANALYSIS β AGENT JUNG:", "<span class='insight-header'>Cross-Analysis β Agent Jung</span>") | |
| .replace(/\n/g, "<br>") | |
| } | |
| // ------------ | |
| // END SESSION | |
| // ------------ | |
| async function endSession() { | |
| console.log("π Ending session") | |
| const sessionId = window.session_id | |
| await fetch(window.location.origin + "/chat", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ | |
| session_id: sessionId, | |
| message: "end session" | |
| }) | |
| }) | |
| // π₯ POPUP MESSAGE | |
| if (sessionId) { | |
| alert(`Session ended.\n\nUse Session ID: ${sessionId} to resume later.`) | |
| } | |
| window.session_id = null | |
| window.conversationStarted = false | |
| document.getElementById("chat").innerHTML = "" | |
| document.getElementById("session_display").value = "" | |
| } | |
| // --------------------------------------------------------- | |
| // ENTER(SEND ACTION) & SHIFT+ENTER(INSERT NEW LINE) CONTROL | |
| // --------------------------------------------------------- | |
| window.addEventListener("DOMContentLoaded", () => { | |
| const input = document.getElementById("main_input") | |
| if (input) { | |
| input.addEventListener("keydown", function (e) { | |
| if (e.key === "Enter" && !e.shiftKey) { | |
| e.preventDefault() | |
| if (window.conversationStarted) { | |
| fuel() | |
| } else { | |
| fire() | |
| } | |
| } | |
| }) | |
| } | |
| }) |