updated src/events/messageCreate.ts, src/index.ts; npx tsc no errors
This commit is contained in:
@@ -4,10 +4,24 @@ import {
|
|||||||
getChannelInfo, getServerConfig, getUserConfig, openChannelInfo,
|
getChannelInfo, getServerConfig, getUserConfig, openChannelInfo,
|
||||||
openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData
|
openConfig, UserConfig, getAttachmentData, getTextFileAttachmentData
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
import { redis } from '../../client.js'
|
import { redis } from '../client.js' // Fixed import: added .ts extension
|
||||||
|
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
|
// Define interface for model response to improve type safety
|
||||||
|
interface ModelResponse {
|
||||||
|
status: 'success' | 'error'
|
||||||
|
reply: string
|
||||||
|
metadata?: {
|
||||||
|
timestamp: string
|
||||||
|
self_sentiment: number
|
||||||
|
user_sentiment: { [userId: string]: number }
|
||||||
|
redis_ops: Array<{ action: 'set' | 'get'; key: string; value?: number }>
|
||||||
|
need_help: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Max Message length for free users is 2000 characters (bot or not).
|
* Max Message length for free users is 2000 characters (bot or not).
|
||||||
* Bot supports infinite lengths for normal messages.
|
* Bot supports infinite lengths for normal messages.
|
||||||
@@ -154,14 +168,45 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
|||||||
personality = 'You are a friendly and helpful AI assistant.'
|
personality = 'You are a friendly and helpful AI assistant.'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user sentiment from Redis
|
// Get user and bot sentiment from Redis
|
||||||
const userSentimentKey = `user:${message.author.id}:sentiment`
|
const userSentimentKey = `user:${message.author.id}:sentiment`
|
||||||
let userSentiment = await redis.get(userSentimentKey) || '0.5'
|
const botSentimentKey = `bot:self_sentiment`
|
||||||
|
let userSentiment: number
|
||||||
|
let botSentiment: number
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userSentimentRaw = await redis.get(userSentimentKey)
|
||||||
|
userSentiment = parseFloat(userSentimentRaw || '0.5')
|
||||||
|
if (isNaN(userSentiment) || userSentiment < 0 || userSentiment > 1) {
|
||||||
|
log(`Invalid user sentiment for ${message.author.id}: ${userSentimentRaw}. Using default 0.5.`)
|
||||||
|
userSentiment = 0.5
|
||||||
|
await redis.set(userSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log(`Failed to get user sentiment from Redis: ${error}`)
|
||||||
|
userSentiment = 0.5
|
||||||
|
await redis.set(userSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default user sentiment: ${err.message}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const botSentimentRaw = await redis.get(botSentimentKey)
|
||||||
|
botSentiment = parseFloat(botSentimentRaw || '0.5')
|
||||||
|
if (isNaN(botSentiment) || botSentiment < 0 || botSentiment > 1) {
|
||||||
|
log(`Invalid bot sentiment: ${botSentimentRaw}. Using default 0.5.`)
|
||||||
|
botSentiment = 0.5
|
||||||
|
await redis.set(botSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default bot sentiment: ${err.message}`))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log(`Failed to get bot sentiment from Redis: ${error}`)
|
||||||
|
botSentiment = 0.5
|
||||||
|
await redis.set(botSentimentKey, '0.5').catch((err: Error) => log(`Failed to set default bot sentiment: ${err.message}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct sentiment data for prompt
|
||||||
|
const sentimentData = `User ${message.author.id} sentiment: ${userSentiment}, Bot sentiment: ${botSentiment}`
|
||||||
|
|
||||||
// Construct prompt with [CHARACTER] and [SENTIMENT]
|
// Construct prompt with [CHARACTER] and [SENTIMENT]
|
||||||
//const sentimentData = `User ${message.author.id} sentiment: ${userSentiment}`
|
|
||||||
const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n${sentimentData}\n[USER_INPUT]\n${cleanedMessage}`
|
const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n${sentimentData}\n[USER_INPUT]\n${cleanedMessage}`
|
||||||
//const prompt = `[CHARACTER]\n${personality}\n[SENTIMENT]\n[USER_INPUT]\n${cleanedMessage}`
|
|
||||||
|
|
||||||
// Set up message history queue
|
// Set up message history queue
|
||||||
msgHist.setQueue(chatMessages)
|
msgHist.setQueue(chatMessages)
|
||||||
@@ -182,11 +227,14 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Parse JSON response
|
// Parse JSON response
|
||||||
let jsonResponse: any
|
let jsonResponse: ModelResponse
|
||||||
try {
|
try {
|
||||||
jsonResponse = JSON.parse(response.message.content)
|
jsonResponse = JSON.parse(response.message.content)
|
||||||
|
if (!jsonResponse.status || !jsonResponse.reply) {
|
||||||
|
throw new Error('Missing status or reply in model response')
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Invalid JSON response from model')
|
throw new Error(`Invalid JSON response from model: ${error}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jsonResponse.status === 'error') {
|
if (jsonResponse.status === 'error') {
|
||||||
@@ -198,10 +246,24 @@ export default event(Events.MessageCreate, async ({ log, msgHist, ollama, client
|
|||||||
// Execute redis_ops
|
// Execute redis_ops
|
||||||
if (jsonResponse.metadata?.redis_ops) {
|
if (jsonResponse.metadata?.redis_ops) {
|
||||||
for (const op of jsonResponse.metadata.redis_ops) {
|
for (const op of jsonResponse.metadata.redis_ops) {
|
||||||
if (op.action === 'set' && op.key && op.value !== undefined) {
|
try {
|
||||||
await redis.set(op.key, op.value)
|
if (op.action === 'set' && op.key && op.value !== undefined) {
|
||||||
} else if (op.action === 'get' && op.key) {
|
// Validate sentiment value
|
||||||
await redis.get(op.key)
|
const value = parseFloat(op.value.toString())
|
||||||
|
if (isNaN(value) || value < 0 || value > 1) {
|
||||||
|
log(`Invalid sentiment value for ${op.key}: ${op.value}. Skipping.`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
await redis.set(op.key, value)
|
||||||
|
log(`Set ${op.key} to ${value}`)
|
||||||
|
} else if (op.action === 'get' && op.key) {
|
||||||
|
const value = await redis.get(op.key)
|
||||||
|
log(`Got ${op.key}: ${value}`)
|
||||||
|
} else {
|
||||||
|
log(`Invalid redis_op: ${JSON.stringify(op)}. Skipping.`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log(`Redis operation failed for ${op.key}: ${error}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import events from '../src/events/index.js'
|
import events from '../src/events/index.js'
|
||||||
|
|
||||||
|
import { redis } from '../client.js';
|
||||||
|
jest.mock('../client.js', () => ({
|
||||||
|
redis: {
|
||||||
|
get: jest.fn().mockResolvedValue('0.5'),
|
||||||
|
set: jest.fn().mockResolvedValue('OK'),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mocking ollama found in client.ts because pullModel.ts
|
* Mocking ollama found in client.ts because pullModel.ts
|
||||||
* relies on the existence on ollama. To prevent the mock,
|
* relies on the existence on ollama. To prevent the mock,
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// Dependent on node version
|
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
// We must set the type
|
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
// Will not go through node_modules
|
|
||||||
"skipDefaultLibCheck": true,
|
"skipDefaultLibCheck": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
// We can import json files like JavaScript
|
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
// Decompile .ts to .js into a folder named build
|
|
||||||
"outDir": "build",
|
"outDir": "build",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
@@ -23,7 +18,6 @@
|
|||||||
"*": ["node_modules/"]
|
"*": ["node_modules/"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// environment for env vars
|
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user