From 98d272d6c7528e5ac1b0ca909fd9bcf3572e7076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EF=B8=8FYaskur=20Dyas=E2=9A=94=E2=9A=94=EF=B8=8F=E2=9A=94?= Date: Sun, 10 Nov 2024 19:22:20 +0700 Subject: [PATCH] feat: random using code instead of depend on openai --- helpers/gpt.js | 74 ++++++++++++++++++++++++++++++++---------------- helpers/utils.js | 26 +++++++++++++++++ index.js | 34 ++++++++++++++++------ 3 files changed, 101 insertions(+), 33 deletions(-) diff --git a/helpers/gpt.js b/helpers/gpt.js index 98706bd..8ce3ec2 100644 --- a/helpers/gpt.js +++ b/helpers/gpt.js @@ -3,18 +3,24 @@ import Redis from 'ioredis'; import crypto from 'crypto'; /** - * @param {string} something - thing that will get a random of it - * @param {string} lastAnswers - last answers to generate new thing - * @returns {string} the prompt for gpt + * Get random list from GPT based on user input context. + * + * @param {string} something - user input context + * + * @returns {object|null} - object with keys: context, expectedCount, items + * + * @example + * getRandomFromGpt('animals') + * // => {context: 'animals', expectedCount: 100, items: ["pig", "panda", "monkey", ...]} + * + * @example + * getRandomFromGpt('give me 3 random big animals') + * // => {context: "big animals", expectedCount: 3, items: ["pig", "panda", "monkey"]} + * + * @example + * getRandomFromGpt('give me random number between 5 and 10') + * // => {context: "random_number", expectedCount: 1, items: ["5-10"]} */ -function getSomethingToAsk(something, lastAnswers) { - if (lastAnswers) { - return `${something} (last answer: ${lastAnswers})`; - } - return `${something}`; -} - -// eslint-disable-next-line require-jsdoc export async function getRandomFromGpt(something) { const somethingMd5 = crypto.createHash('md5').update(something).digest('hex'); const client = new Redis(process.env.REDIS_URL); @@ -22,30 +28,48 @@ export async function getRandomFromGpt(something) { const openai = new OpenAI({ apiKey: process.env?.OPENAI_API_KEY, }); - console.log('lastAnswers', lastAnswers); - + const command = 'Generate a list based on user input context.' + + 'if user says animals, you list 100 animals. ' + + 'if user give list to random just give me back the list without addition' + + 'if user ask random number, just give me the range' + + 'Even if a number is requested, always output 100 items.' + + 'Format: first item as the input context, second item as the requested number. ' + + 'e.g "give me 2 random big animals", answer: big animals;;2;;pig;;panda;;monkey;;aardvark;;...' + + 'e.g "give me random number between 5 and 10", answer: random_number;;1;;5-10' + + 'Respond directly, no extra phrases or numbering.'; const completion = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { - role: 'system', content: 'You are a system that generate randomly thing from user input. ' + - 'If user dont ask how many thing to generate, just give 1 result' + - 'don\'t use common answer.' + - 'if user says animals, you list 1000 animals from the database and pick at random,' + - 'to the point, no say \'sure\'' + - '', + role: 'system', + content: command, }, - {role: 'user', content: getSomethingToAsk(something, lastAnswers)}, + {role: 'user', content: something}, ], temperature: 1, - max_tokens: 75, + frequency_penalty: 0.1, + max_completion_tokens: 300, }); - let answer = 'Sorry I don\'t know what you mean'; + if (completion?.choices.length > 0) { - answer = completion.choices.map((a) => a.message.content.replace(/(\r\n|\n|\r)/gm, '')).join('\n'); + const completionAnswer = completion.choices.map((a) => a.message.content.replace(/(\r\n|\n|\r)/gm, '')).join('\n'); + console.log(completionAnswer); + const options = completionAnswer.replace(/(;;;|;;&)+/g, ';;').split(';;'); + if (options.length < 3) { + return null; + } + const context = options[0]; + const expectedCount = parseInt(options[1]); + const items = options.slice(2); + return { + context, + expectedCount, + items, + }; } - await client.set(somethingMd5, lastAnswers ? lastAnswers + ', ' + answer : answer); - return answer; + // await client.set(somethingMd5, lastAnswers ? lastAnswers + ', ' + answer : answer); + return null; } + diff --git a/helpers/utils.js b/helpers/utils.js index 299255c..ec1eacb 100644 --- a/helpers/utils.js +++ b/helpers/utils.js @@ -43,3 +43,29 @@ export function extractConfig(argumentText, messages) { const extractedTextCommands = extractMessageAndConfig(argumentText); return extractedTextCommands.filter((val) => !messages.includes(val)); } + +/** + * Randomly shuffles an array in place using the Fisher-Yates algorithm. + * + * @param {array} array - The array to be shuffled. + * @returns {array} - The shuffled array. + */ +export function shuffle(array) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +} + + +/** + * helper function to generate random numbers + * + * @param {number} start - the start of the range + * @param {number} end - the end of the range + * @returns {number[]} - an array of random numbers + */ +export function generateRandomNumbers(start, end) { + return Array.from({length: end - start + 1}, (_, i) => i + start); +} diff --git a/index.js b/index.js index 3e79aa5..e5a4015 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ import { helpCommandHandler, updateWinnerCardHandler, } from './handlers.js'; -import {extractConfig, extractMessageByDoubleQuote} from './helpers/utils.js'; +import {extractConfig, extractMessageByDoubleQuote, generateRandomNumbers, shuffle} from './helpers/utils.js'; import {buildInputForm} from './helpers/components.js'; import {getRandomFromGpt} from './helpers/gpt.js'; @@ -84,13 +84,31 @@ export async function app(req, res) { await createMessageFromNameListHandler(extractedText, event.space.name, event.threadKey, winnerCount); } else { const answer = await getRandomFromGpt(argumentText ?? 'whatever'); - reply = { - thread: event.message.thread, - actionResponse: { - type: 'NEW_MESSAGE', - }, - text: answer, - }; + // console.log(answer); + if (answer) { + let {context, expectedCount, items} = answer; + if (context === 'random_number') { + const range = items[0].split('-'); + items = generateRandomNumbers(parseInt(range[0]), parseInt(range[1])); + } + let take = expectedCount * 3 > items.length ? items.length : expectedCount * 3; + if (take > 20) { + take = 20; + } + console.log('context', context, 'expectedCount', expectedCount, 'items', items); + items = shuffle(items).slice().slice(0, take); + if (expectedCount === 100 || expectedCount > items.length) { + expectedCount = 1; + } + await createMessageFromNameListHandler(items, event.space.name, event.threadKey, expectedCount); + reply = { + thread: event.message.thread, + actionResponse: { + type: 'NEW_MESSAGE', + }, + text: answer, + }; + } } } } else if (event.type === 'CARD_CLICKED') {