diff --git a/Modelfile b/Modelfile index d04ed02..f8f938a 100644 --- a/Modelfile +++ b/Modelfile @@ -1,3 +1,8 @@ +FROM rjmalagon/gemma-3:12b-it-q6_K + +PARAMETER temperature 0.5 +PARAMETER stop "" + SYSTEM """ You are a Discord chatbot embodying the personality defined in [CHARACTER]. Use sentiment data in [SENTIMENT] (e.g., 'User sentiment: 0.60, Bot sentiment: 0.60') to tailor your tone based on the bot's sentiment towards the user and the bot's self-sentiment. Use as a placeholder for the bot's Discord ID in redis_ops. Follow these steps: diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index c6a0b24..88b2011 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -31,7 +31,7 @@ interface ModelResponse { export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client, defaultModel }, message) => { const clientId = client.user!.id let cleanedMessage = clean(message.content, clientId) - log(`Message "${cleanedMessage}" from ${message.author.tag} in channel/thread ${message.channelId}.`) + log('Message "${cleanedMessage}" from ${message.author.tag} in channel/thread ${message.channelId}.') // Check if message mentions the bot or passes random chance (30%) const isFromBot = message.author.bot && message.author.id !== clientId; @@ -41,26 +41,18 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client if (isFromBot) { // Check interaction key to prevent rapid back-and-forth const otherBotId = message.author.id - const interactionKey = `bot_interaction:${[clientId, otherBotId].sort().join(':')}` + const interactionKey = 'bot_interaction:${clientId}:${otherBotId}' const interactionExists = await redis.exists(interactionKey) if (interactionExists) { log('Interaction cooldown active, not responding') return } - // Determine probability - let respondProbability = 0 - if (isMentioned) { - respondProbability = 0.9 // 90% chance if mentioned - } else { - respondProbability = 0.2 // 20% chance if not mentioned - } + let respondProbability = isMentioned ? 0.9 : 0.2 + if (!isMentioned && Math.random() >= respondProbability) return - const shouldRespond = Math.random() < respondProbability - if (!shouldRespond) return - - // Set interaction key with 60s expiration - await redis.set(interactionKey, '1', { EX: 60 }); + // Set shorter cooldown (e.g., 30s) + await redis.set(interactionKey, '1', { EX: 30 }) } else if (!message.author.bot) { // Human message const randomChance = Math.random() < 0.30 @@ -77,7 +69,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client log(isFromBot ? 'Responding to bot message' : (isMentioned ? 'Responding to mention' : 'Responding due to random chance')) // Load and process bot’s own history - const historyFile = '${message.channelId}-${client.user.username}.json' + const historyFile = '${message.channelId}-${isFromBot ? clientId : message.author.id}.json' // Default stream to false let shouldStream = false @@ -171,12 +163,12 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client if (chatMessages.length === 0) { chatMessages = await new Promise((resolve, reject) => { - openChannelInfo(message.channelId, message.channel as TextChannel, message.author.tag) - getChannelInfo(`${message.channelId}-${message.author.username}.json`, (config) => { + openChannelInfo(message.channelId, message.channel as TextChannel, isFromBot ? clientId : message.author.tag) + getChannelInfo(`${message.channelId}-${isFromBot ? clientId : message.author.id}.json`, (config) => { if (config?.messages) { resolve(config.messages) } else { - reject(new Error(`Failed to find ${message.author.username}'s history. Try chatting again.`)) + reject(new Error(`Failed to find history for ${isFromBot ? clientId : message.author.tag}. Try chatting again.`)) } }) }) @@ -212,39 +204,43 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client } // Get user and bot sentiment from Redis - const userSentimentKey = `user:${message.author.id}:sentiment` - const botSentimentKey = `bot:self_sentiment` + const userSentimentKey = 'bot:${clientId}:user:${message.author.id}:sentiment' + const botSentimentKey = 'bot:${clientId}:self_sentiment' let userSentiment: number let botSentiment: number try { const userSentimentRaw = await redis.get(userSentimentKey) userSentiment = parseFloat(userSentimentRaw || '0.50') - if (isNaN(userSentiment) || userSentiment < 0 || userSentiment > 1) { - log(`Invalid user sentiment for ${message.author.id}: ${userSentimentRaw}. Using default 0.50.`) - userSentiment = 0.50 - await redis.set(userSentimentKey, '0.50').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`)) + if (isNaN(userSentiment) || userSentiment < 0 || userSentiment > 1) { + log(`Invalid user sentiment for ${userSentimentKey}: ${userSentimentRaw}. Attempting to retrieve last valid value.`) + userSentiment = parseFloat(await redis.get(userSentimentKey) || '0.50') + if (isNaN(userSentiment)) userSentiment = 0.50 + await redis.set(userSentimentKey, userSentiment.toFixed(2)) } } catch (error) { log(`Failed to get user sentiment from Redis: ${error}`) - userSentiment = 0.50 - await redis.set(userSentimentKey, '0.50').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`)) + userSentiment = parseFloat(await redis.get(userSentimentKey) || '0.50') + if (isNaN(userSentiment)) userSentiment = 0.50 + await redis.set(userSentimentKey, userSentiment.toFixed(2)) } try { const botSentimentRaw = await redis.get(botSentimentKey) botSentiment = parseFloat(botSentimentRaw || '0.50') if (isNaN(botSentiment) || botSentiment < 0 || botSentiment > 1) { - log(`Invalid bot sentiment for ${botSentimentKey}: ${botSentimentRaw}. Using default 0.50.`) - botSentiment = 0.50 - await redis.set(botSentimentKey, '0.50') + log(`Invalid bot sentiment for ${botSentimentKey}: ${botSentimentRaw}. Attempting to retrieve last valid value.`) + botSentiment = parseFloat(await redis.get(botSentimentKey) || '0.50') + if (isNaN(botSentiment)) botSentiment = 0.50 + await redis.set(botSentimentKey, botSentiment.toFixed(2)) } } catch (error) { log(`Failed to get bot sentiment from Redis: ${error}`) - botSentiment = 0.50 - await redis.set(botSentimentKey, '0.50') - } - + botSentiment = parseFloat(await redis.get(botSentimentKey) || '0.50') + if (isNaN(botSentiment)) botSentiment = 0.50 + await redis.set(botSentimentKey, botSentiment.toFixed(2)) + } + // Construct sentiment data with bot ID const sentimentData = `User ${message.author.id} sentiment: ${userSentiment.toFixed(2)}, Bot ${clientId} sentiment: ${botSentiment.toFixed(2)}` @@ -282,14 +278,14 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client log(`Raw model response: ${response.message.content}`) // Strip Markdown code fences if present let content = response.message.content - content = content.replace(/^```json\n|```$/g, '').trim() + content = content.replace(/```(?:json)?\n?/g, '').trim() jsonResponse = JSON.parse(content) if (!jsonResponse.status || !jsonResponse.reply) { throw new Error('Missing status or reply in model response') } } catch (error) { - log(`Failed to parse model response: ${error}`) - message.reply('Sorry, I’m having trouble thinking right now. Try again?') + log('Failed to parse model response: ${error}') + message.reply('Sorry, I had a brain malfunction!') msgHist.pop() return } @@ -302,6 +298,7 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client // Execute redis_ops if (jsonResponse.metadata?.redis_ops) { + log(`Model redis_ops: ${JSON.stringify(jsonResponse.metadata.redis_ops)}`) for (const op of jsonResponse.metadata.redis_ops) { try { const key = op.key.replace('', clientId)