| |
| const https = require('https'); |
|
|
| class AIHandler { |
| constructor(apiKey, memoryHandler = null) { |
| this.apiKey = apiKey; |
| this.memoryHandler = memoryHandler; |
| console.log('AI Handler initialized with' + (memoryHandler ? ' conversation memory' : 'out memory')); |
| } |
|
|
| |
| |
| |
| |
| |
| async getAIResponse(userMessage, username = null) { |
| return new Promise((resolve, reject) => { |
| try { |
| console.log(`AI processing message from ${username || 'unknown'}: ${userMessage}`); |
| |
| |
| let historyContext = ''; |
| |
| |
| if (this.memoryHandler && username) { |
| historyContext = this.memoryHandler.getFormattedHistory(username); |
| if (historyContext) { |
| console.log(`Including conversation history for ${username}`); |
| } |
| } |
| |
| |
| const prompt = `You are a helpful assistant in a music chat room. You can speak both English and Korean. |
| |
| ${historyContext} |
| |
| User message: "${userMessage}" |
| |
| Your helpful response: `; |
| |
| |
| |
| const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${this.apiKey}`; |
| |
| |
| const body = JSON.stringify({ |
| contents: [{ |
| parts: [{ |
| text: prompt |
| }] |
| }], |
| generationConfig: { |
| temperature: 0.7, |
| maxOutputTokens: 5000, |
| topP: 0.95, |
| topK: 40 |
| }, |
| safetySettings: [ |
| { |
| category: "HARM_CATEGORY_HARASSMENT", |
| threshold: "BLOCK_MEDIUM_AND_ABOVE" |
| }, |
| { |
| category: "HARM_CATEGORY_HATE_SPEECH", |
| threshold: "BLOCK_MEDIUM_AND_ABOVE" |
| } |
| ] |
| }); |
| |
| |
| console.log(`Sending request to: ${url.replace(this.apiKey, 'API_KEY')}`); |
| |
| |
| const urlObj = new URL(url); |
| |
| |
| const options = { |
| hostname: urlObj.hostname, |
| path: urlObj.pathname + urlObj.search, |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Content-Length': Buffer.byteLength(body) |
| } |
| }; |
| |
| const req = https.request(options, (res) => { |
| let responseBody = ''; |
| |
| |
| res.on('data', (chunk) => { |
| responseBody += chunk; |
| }); |
| |
| |
| res.on('end', () => { |
| console.log(`API Response status: ${res.statusCode}`); |
| |
| |
| if (res.statusCode !== 200) { |
| console.error(`API Error ${res.statusCode}: ${responseBody}`); |
| return resolve('Sorry, I had a problem connecting to my AI brain.'); |
| } |
| |
| try { |
| const response = JSON.parse(responseBody); |
| |
| |
| console.log('Full API response:', JSON.stringify(response)); |
| |
| |
| let text = null; |
| |
| |
| if (response.candidates && response.candidates[0]) { |
| const candidate = response.candidates[0]; |
| |
| |
| if (candidate.content && |
| candidate.content.parts && |
| candidate.content.parts[0] && |
| candidate.content.parts[0].text) { |
| |
| text = candidate.content.parts[0].text.trim(); |
| } |
| |
| else if (candidate.content && candidate.content.text) { |
| text = candidate.content.text.trim(); |
| } |
| |
| else if (candidate.text) { |
| text = candidate.text.trim(); |
| } |
| |
| else if (candidate.content && candidate.finishReason) { |
| if (candidate.finishReason === 'STOP') { |
| text = 'I understand what you mean.'; |
| } else if (candidate.finishReason === 'MAX_TOKENS') { |
| text = 'I have thoughts about that, but let me keep it brief.'; |
| } |
| } |
| } |
| |
| if (text) { |
| |
| console.log(`AI generated: ${text.substring(0, 50)}...`); |
| |
| |
| text = text.replace(/^("|'|`)/, ''); |
| text = text.replace(/("|'|`)$/, ''); |
| text = text.replace(/^Your helpful, concise response: ?/i, ''); |
| |
| |
| |
| const maxLength = 180; |
| |
| |
| if (text.length > maxLength) { |
| |
| let breakPoint = text.substring(0, maxLength).lastIndexOf('.'); |
| if (breakPoint === -1 || breakPoint < maxLength / 2) { |
| breakPoint = text.substring(0, maxLength).lastIndexOf('?'); |
| } |
| if (breakPoint === -1 || breakPoint < maxLength / 2) { |
| breakPoint = text.substring(0, maxLength).lastIndexOf('!'); |
| } |
| if (breakPoint === -1 || breakPoint < maxLength / 2) { |
| breakPoint = text.substring(0, maxLength - 3).lastIndexOf(' '); |
| } |
| |
| |
| if (breakPoint !== -1 && breakPoint >= maxLength / 2) { |
| text = text.substring(0, breakPoint + 1); |
| } else { |
| text = text.substring(0, maxLength - 3) + '...'; |
| } |
| } |
| |
| resolve(text); |
| } else { |
| console.error('Could not extract text from response:', JSON.stringify(response)); |
| |
| |
| const fallbacks = [ |
| 'Interesting thoughts about the music! What else are you enjoying in the queue?', |
| 'That is a good point! Music brings people together in so many ways.', |
| 'I like how you think about music. What is your favorite genre?', |
| 'Thanks for sharing that with me! The song selection here is always interesting.', |
| 'I appreciate your perspective on this. Music is such a universal language.' |
| ]; |
| |
| |
| resolve(fallbacks[Math.floor(Math.random() * fallbacks.length)]); |
| } |
| } catch (error) { |
| console.error('Error parsing response:', error); |
| console.error('Raw response:', responseBody.substring(0, 200)); |
| resolve('Sorry, I had trouble understanding the response.'); |
| } |
| }); |
| }); |
| |
| |
| req.on('error', (error) => { |
| console.error('Request error:', error); |
| resolve('Sorry, I had trouble connecting to my AI brain. Try again later?'); |
| }); |
| |
| |
| req.write(body); |
| req.end(); |
| |
| } catch (error) { |
| console.error('Unexpected error in getAIResponse:', error); |
| resolve('Sorry, something unexpected happened with my AI processing.'); |
| } |
| }); |
| } |
| } |
|
|
| module.exports = AIHandler; |
|
|