| | |
| | 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. you should use 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', () => { |
| | try { |
| | |
| | if (res.statusCode !== 200) { |
| | console.error(`API returned status ${res.statusCode}:`, responseBody); |
| | return resolve('Sorry, the AI service returned an error. Please try again later.'); |
| | } |
| | |
| | |
| | const response = JSON.parse(responseBody); |
| | console.log('API response received, status:', res.statusCode); |
| | |
| | |
| | console.log('Full API response:', responseBody); |
| | |
| | |
| | let text = null; |
| | |
| | |
| | if (response.candidates && response.candidates[0]) { |
| | const candidate = response.candidates[0]; |
| | |
| | if (candidate.content && candidate.content.parts) { |
| | |
| | const parts = candidate.content.parts; |
| | const allText = []; |
| | |
| | |
| | for (const part of parts) { |
| | if (part.text) { |
| | allText.push(part.text); |
| | } |
| | } |
| | |
| | if (allText.length > 0) { |
| | |
| | text = allText.join('\n'); |
| | } |
| | } else if (candidate.text) { |
| | |
| | text = candidate.text; |
| | } |
| | } |
| | |
| | |
| | if (text) { |
| | |
| | console.log(`AI generated text (first 50 chars): ${text.substring(0, 50)}...`); |
| | |
| | |
| | text = text.replace(/^(\"|'|`)/, ''); |
| | text = text.replace(/(\"|'|`)$/, ''); |
| | text = text.replace(/^Your helpful response: ?/i, ''); |
| | |
| | |
| | text = text.replace(/\*\s+/g, 'β’ '); |
| | |
| | |
| | |
| | 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 = [ |
| | 'μ£μ‘ν©λλ€, λ§μνμ λ΄μ©μ μ λλ‘ μ΄ν΄νμ§ λͺ»νμ΄μ. λ€μ μ§λ¬Έν΄ μ£Όμκ² μ΄μ?', |
| | 'ν₯λ―Έλ‘μ΄ μ΄μΌκΈ°λ€μ! λ μμΈν μ€λͺ
ν΄ μ£Όμ€λμ?', |
| | 'μ§λ¬Έμ λν λ΅λ³μ μ°Ύκ³ μμλλ° λ¬Έμ κ° μκ²Όμ΄μ. λ€μ μλν΄ λ³ΌκΉμ?', |
| | 'λνμ μ°Έμ¬ν΄ μ£Όμ
μ κ°μ¬ν©λλ€. λ€λ₯Έ μ§λ¬Έμ΄ μμΌμ κ°μ?', |
| | 'μ£μ‘ν©λλ€λ§, μ κ° μ λλ‘ μ²λ¦¬νμ§ λͺ»νμ΅λλ€. λ€λ₯Έ λ°©μμΌλ‘ λ¬Όμ΄λ΄ μ£Όμ€λμ?' |
| | ]; |
| | |
| | |
| | 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('μ£μ‘ν©λλ€, μλ΅μ μ²λ¦¬νλ μ€μ λ¬Έμ κ° λ°μνμ΅λλ€. λ€μ μλν΄ μ£ΌμΈμ.'); |
| | } |
| | }); |
| | }); |
| | |
| | |
| | req.on('error', (error) => { |
| | console.error('Request error:', error); |
| | resolve('μ£μ‘ν©λλ€, AI μλΉμ€μ μ°κ²°νλ λ° λ¬Έμ κ° μμμ΅λλ€. λμ€μ λ€μ μλν΄ μ£ΌμΈμ.'); |
| | }); |
| | |
| | |
| | req.write(body); |
| | req.end(); |
| | |
| | } catch (error) { |
| | console.error('Unexpected error in getAIResponse:', error); |
| | resolve('μ£μ‘ν©λλ€, AI μ²λ¦¬ μ€μ μμμΉ λͺ»ν λ¬Έμ κ° λ°μνμ΅λλ€.'); |
| | } |
| | }); |
| | } |
| | } |
| |
|
| | module.exports = AIHandler; |
| |
|