UNPKG

adk-typescript

Version:

TypeScript port of Google's Agent Development Kit (ADK)

303 lines (256 loc) 9.23 kB
/** * ADK Web UI - Main Client Script */ class AdkChatApp { constructor() { // Initialize properties this.socket = null; this.agents = []; this.selectedAgent = null; this.currentSession = null; this.isConnected = false; this.isThinking = false; // Get DOM elements this.agentSelect = document.getElementById('agent-select'); this.connectBtn = document.getElementById('connect-btn'); this.messagesContainer = document.getElementById('messages'); this.userInput = document.getElementById('user-input'); this.sendBtn = document.getElementById('send-btn'); this.newSessionBtn = document.getElementById('new-session-btn'); this.sessionDetails = document.getElementById('session-details'); this.agentDetails = document.getElementById('agent-details'); // Set up event listeners this.connectBtn.addEventListener('click', () => this.connectToAgent()); this.sendBtn.addEventListener('click', () => this.sendMessage()); this.newSessionBtn.addEventListener('click', () => this.createNewSession()); this.userInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // Initialize this.initialize(); } async initialize() { try { // Fetch available agents const response = await fetch('/list-apps'); if (response.ok) { const data = await response.json(); this.agents = data.agents || []; // Handle the agents array structure from the API this.populateAgentDropdown(); } else { this.showError('Failed to load agents'); } } catch (error) { this.showError('Error initializing: ' + error.message); } } populateAgentDropdown() { this.agentSelect.innerHTML = ''; this.agents.forEach(agent => { const option = document.createElement('option'); option.value = agent.name; option.textContent = agent.name; this.agentSelect.appendChild(option); }); } async connectToAgent() { const agentName = this.agentSelect.value; if (!agentName) { this.showError('Please select an agent'); return; } this.selectedAgent = { name: agentName }; this.connectBtn.disabled = true; this.connectBtn.textContent = 'Connecting...'; try { // Create a new session const response = await fetch(`/apps/${agentName}/users/web-user/sessions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ state: {} }) }); if (response.ok) { this.currentSession = await response.json(); this.setupSocketConnection(); this.updateSessionInfo(); this.updateAgentInfo(); this.clearMessages(); this.sendBtn.disabled = false; this.isConnected = true; this.addSystemMessage(`Connected to agent: ${agentName}`); } else { throw new Error('Failed to create session'); } } catch (error) { this.showError('Connection error: ' + error.message); this.connectBtn.disabled = false; this.connectBtn.textContent = 'Connect'; } } setupSocketConnection() { if (this.socket) { this.socket.disconnect(); } // Connect to the socket server this.socket = io(); this.socket.on('connect', () => { console.log('Socket connected'); this.connectBtn.textContent = 'Connected'; }); this.socket.on('error', (error) => { this.showError('Socket error: ' + error.message); }); this.socket.on('thinking', () => { this.isThinking = true; this.addThinkingIndicator(); }); this.socket.on('response_chunk', (data) => { this.isThinking = false; this.removeThinkingIndicator(); this.updateOrAddAgentMessage(data.text, data.author, data.partial); }); this.socket.on('response_complete', () => { this.isThinking = false; this.removeThinkingIndicator(); }); this.socket.on('function_calls', (data) => { console.log('Function calls:', data); // Display function calls in the UI if needed }); this.socket.on('function_responses', (data) => { console.log('Function responses:', data); // Display function responses in the UI if needed }); this.socket.on('disconnect', () => { console.log('Socket disconnected'); this.isConnected = false; this.sendBtn.disabled = true; this.connectBtn.disabled = false; this.connectBtn.textContent = 'Connect'; }); // Initialize the agent by sending information to the socket this.socket.emit('initialize_agent', { agentPath: this.selectedAgent.name }); } async sendMessage() { if (!this.isConnected || !this.currentSession || this.isThinking) return; const message = this.userInput.value.trim(); if (!message) return; this.addUserMessage(message); this.userInput.value = ''; if (this.socket) { this.socket.emit('message', { message }); } } async createNewSession() { if (!this.selectedAgent) { this.showError('Please select and connect to an agent first'); return; } try { const response = await fetch(`/apps/${this.selectedAgent.name}/users/web-user/sessions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ state: {} }) }); if (response.ok) { this.currentSession = await response.json(); this.updateSessionInfo(); this.clearMessages(); this.addSystemMessage('New session created'); } else { throw new Error('Failed to create new session'); } } catch (error) { this.showError('Error creating new session: ' + error.message); } } addUserMessage(text) { const messageDiv = document.createElement('div'); messageDiv.className = 'message user-message'; messageDiv.textContent = text; this.messagesContainer.appendChild(messageDiv); this.scrollToBottom(); } addAgentMessage(text, agent) { const messageDiv = document.createElement('div'); messageDiv.className = 'message agent-message'; messageDiv.dataset.agent = agent; messageDiv.textContent = text; this.messagesContainer.appendChild(messageDiv); this.scrollToBottom(); return messageDiv; } updateOrAddAgentMessage(text, agent, isPartial) { const existingPartialMessage = this.messagesContainer.querySelector(`.message.agent-message[data-partial="true"][data-agent="${agent}"]`); if (existingPartialMessage) { existingPartialMessage.textContent = text; if (!isPartial) { existingPartialMessage.dataset.partial = 'false'; } } else { const messageDiv = this.addAgentMessage(text, agent); messageDiv.dataset.partial = isPartial ? 'true' : 'false'; } this.scrollToBottom(); } addSystemMessage(text) { const messageDiv = document.createElement('div'); messageDiv.className = 'message system-message'; messageDiv.textContent = text; this.messagesContainer.appendChild(messageDiv); this.scrollToBottom(); } addThinkingIndicator() { // Remove any existing thinking indicators this.removeThinkingIndicator(); // Add new thinking indicator const thinkingDiv = document.createElement('div'); thinkingDiv.className = 'message agent-message thinking-message'; const loadingSpan = document.createElement('span'); loadingSpan.className = 'loading'; thinkingDiv.appendChild(loadingSpan); const textSpan = document.createElement('span'); textSpan.textContent = 'Thinking...'; thinkingDiv.appendChild(textSpan); this.messagesContainer.appendChild(thinkingDiv); this.scrollToBottom(); } removeThinkingIndicator() { const thinkingMessage = this.messagesContainer.querySelector('.thinking-message'); if (thinkingMessage) { thinkingMessage.remove(); } } updateSessionInfo() { if (!this.currentSession) return; this.sessionDetails.innerHTML = ` <p><strong>Session ID:</strong> ${this.currentSession.id}</p> <p><strong>User ID:</strong> ${this.currentSession.userId}</p> `; } updateAgentInfo() { if (!this.selectedAgent) return; this.agentDetails.innerHTML = ` <p><strong>Agent:</strong> ${this.selectedAgent.name}</p> `; } clearMessages() { this.messagesContainer.innerHTML = ''; } scrollToBottom() { this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight; } showError(message) { console.error(message); this.addSystemMessage(`Error: ${message}`); } } // Initialize the app when the DOM is fully loaded document.addEventListener('DOMContentLoaded', () => { new AdkChatApp(); });