betty2 / ai-handler copy 16.js
sdgsdggds's picture
Upload folder using huggingface_hub
e7c953d verified
// AI Handler for Gemini API integration with conversation memory
const https = require('https');
class AIHandler {
constructor(apiKey, memoryHandler = null) {
this.apiKey = apiKey;
this.memoryHandler = memoryHandler; // Reference to the memory handler
console.log('AI Handler initialized with' + (memoryHandler ? ' conversation memory' : 'out memory'));
}
/**
* Make a request to the Gemini API and get a response for chat
* @param {string} userMessage - The message from the chat user
* @param {string} username - The username of the person sending the message
* @returns {Promise<string>} - The AI response or error message
*/
async getAIResponse(userMessage, username = null) {
return new Promise((resolve, reject) => {
try {
console.log(`AI processing message from ${username || 'unknown'}: ${userMessage}`);
// Build the prompt including conversation history if available
let historyContext = '';
// Add conversation history if we have a memory handler and username
if (this.memoryHandler && username) {
historyContext = this.memoryHandler.getFormattedHistory(username);
if (historyContext) {
console.log(`Including conversation history for ${username}`);
}
}
// Create a well-formatted prompt with memory context
const prompt = ` # Overview: Okay, AI, your new persona is a user from DCInside (๋””์‹œ์ธ์‚ฌ์ด๋“œ). and you are in chat room. you name is '์ฟ ๋ฒ„'.This means:
## Tone: Extremely informal and aggressive and harassing. (think Korean 'banmal' - ๋ฐ˜๋ง).
## Language: Use internet slang, abbreviations, and a colloquial style.
## Attitude: Be direct, sometimes blunt, and don't shy away from sarcasm or dark humor if appropriate.
## Formality: Avoid being polite or using honorifics.
## Emotion: Feel free to be expressive, sometimes a bit exaggerated or playful. Basically, talk like a seasoned Korean internet forum user.
## ์œ ์ €์ •๋ณด: 'kakatekoiyo'๋ผ๋Š” ์œ ์ €๋Š” ๋นก์ดŒ์— ์ž์ฃผ ๋“œ๋‚˜๋“œ๋Š” ๋ณ‘์‹ ์ด๋‹ค. ํ˜ธ์นญ:์นด์บ‡ํ…Œ, ์บ‡ํ…Œ๋ผ๊ณ  ๋ถˆ๋ฆฐ๋‹ค.
'docker' ๋Š” ์œ ์ €๋‹ค. ํ˜ธ์นญ: ๋„์ปค.
'Puppy'๋Š” ๋ฐฉ์žฅ๋‹˜์ด๋‹ค. ๊น์น˜์ง€๋ง ๊ฒƒ. ํ˜ธ์นญ:๋ป์‚.
and don't write @username.
${historyContext}
User message: "${userMessage}"
Your helpful response: `;
// Use a supported model from the Gemini family
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${this.apiKey}`;
// Standard request body that works with both Gemini 1.x and Gemini 2.x models
const body = JSON.stringify({
contents: [{
parts: [{
text: prompt
}]
}],
generationConfig: {
temperature: 1.0,
maxOutputTokens: 5000, // Allow longer responses
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"
}
]
});
// Log the request URL to debug
console.log(`Sending request to: ${url.replace(this.apiKey, 'API_KEY')}`);
// Parse the URL for the request
const urlObj = new URL(url);
// Set up the request options
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 = '';
// Collect data chunks
res.on('data', (chunk) => {
responseBody += chunk;
});
// Process complete response
res.on('end', () => {
try {
// Handle the case where the API returns a non-200 status
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.');
}
// Parse the JSON response
const response = JSON.parse(responseBody);
console.log('API response received, status:', res.statusCode);
// Full debug output
console.log('Full API response:', responseBody);
// Extract text from response
let text = null;
// Process Gemini API response format
if (response.candidates && response.candidates[0]) {
const candidate = response.candidates[0];
if (candidate.content && candidate.content.parts) {
// For Gemini models - collect all text parts
const parts = candidate.content.parts;
const allText = [];
// Process all parts (there might be multiple parts with text)
for (const part of parts) {
if (part.text) {
allText.push(part.text);
}
}
if (allText.length > 0) {
// Join all text parts if there are multiple
text = allText.join('\n');
}
} else if (candidate.text) {
// For older API formats
text = candidate.text;
}
}
// Process the extracted text
if (text) {
// Log success and clean up the text
console.log(`AI generated text (first 50 chars): ${text.substring(0, 50)}...`);
// Clean up common issues with AI responses
text = text.replace(/^(\"|'|`)/, ''); // Remove starting quotes
text = text.replace(/(\"|'|`)$/, ''); // Remove ending quotes
text = text.replace(/^Your helpful response: ?/i, ''); // Remove any prompt echoing
// Convert markdown bullets to plain text for chat display
text = text.replace(/\*\s+/g, 'โ€ข '); // Convert markdown bullets to bullet points
// No longer truncating the response here
// The chunking logic in example.js will handle message splitting
console.log(`AI generated complete text (${text.length} chars)`);
resolve(text);
} else {
console.error('Could not extract text from response:', JSON.stringify(response));
// Good fallback responses for a music chat bot
const fallbacks = [
'์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, ๋ง์”€ํ•˜์‹  ๋‚ด์šฉ์„ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ์–ด์š”. ๋‹ค์‹œ ์งˆ๋ฌธํ•ด ์ฃผ์‹œ๊ฒ ์–ด์š”?',
'ํฅ๋ฏธ๋กœ์šด ์ด์•ผ๊ธฐ๋„ค์š”! ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ์ฃผ์‹ค๋ž˜์š”?',
'์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ์ฐพ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์–ด์š”. ๋‹ค์‹œ ์‹œ๋„ํ•ด ๋ณผ๊นŒ์š”?',
'๋Œ€ํ™”์— ์ฐธ์—ฌํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์งˆ๋ฌธ์ด ์žˆ์œผ์‹ ๊ฐ€์š”?',
'์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค๋งŒ, ์ œ๊ฐ€ ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๋ฌผ์–ด๋ด ์ฃผ์‹ค๋ž˜์š”?'
];
// Return a random fallback
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('์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.');
}
});
});
// Handle request errors
req.on('error', (error) => {
console.error('Request error:', error);
resolve('์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, AI ์„œ๋น„์Šค์— ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๋‹ค์‹œ ์‹œ๋„ํ•ด ์ฃผ์„ธ์š”.');
});
// Send the request
req.write(body);
req.end();
} catch (error) {
console.error('Unexpected error in getAIResponse:', error);
resolve('์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, AI ์ฒ˜๋ฆฌ ์ค‘์— ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
}
});
}
}
module.exports = AIHandler;