feat(telegram-bot): send stickers & process animated stickers
This commit is contained in:
@@ -177,7 +177,7 @@ const activeTab = computed({
|
||||
<DialogTitle text-2xl font-bold class="from-primary-500 to-primary-400 bg-gradient-to-r bg-clip-text text-transparent">
|
||||
{{ selectedCard.name }}
|
||||
</DialogTitle>
|
||||
<div v-if="isActive" class="dark:text-primary-400 bg-primary-100 text-primary-600 dark:bg-primary-900/40 flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium">
|
||||
<div v-if="isActive" class="bg-primary-100 text-primary-600 dark:bg-primary-900/40 dark:text-primary-400 flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium">
|
||||
<div i-solar:check-circle-bold-duotone text-xs />
|
||||
{{ t('settings.pages.card.active_badge') }}
|
||||
</div>
|
||||
|
||||
@@ -251,7 +251,7 @@ function getModuleShortName(id: string, module: 'consciousness' | 'voice') {
|
||||
class="bg-primary-100/80 border-primary-400 dark:bg-primary-900/80 dark:border-primary-600 absolute inset-0 flex items-center justify-center border-2 rounded-xl"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div i-solar:upload-minimalistic-bold class="text-primary-500 dark:text-primary-400 mb-2 text-5xl" />
|
||||
<div i-solar:upload-minimalistic-bold class="dark:text-primary-400 text-primary-500 mb-2 text-5xl" />
|
||||
<p font-medium text="primary-600 dark:primary-300">
|
||||
{{ t('settings.pages.card.drop_here') }}
|
||||
</p>
|
||||
|
||||
@@ -11,9 +11,9 @@ import { Bot } from 'grammy'
|
||||
import { imagineAnAction } from '../../llm/actions'
|
||||
import { interpretPhotos } from '../../llm/photo'
|
||||
import { interpretSticker } from '../../llm/sticker'
|
||||
import { recordMessage } from '../../models'
|
||||
import { findStickerByFileId, recordMessage } from '../../models'
|
||||
import { listJoinedChats, recordJoinedChat } from '../../models/chats'
|
||||
import { recordStickerPack } from '../../models/sticker-packs'
|
||||
import { listStickerPacks, recordStickerPack } from '../../models/sticker-packs'
|
||||
import { readMessage } from './loop/read-message'
|
||||
import { shouldInterruptProcessing } from './utils/interruption'
|
||||
import { sendMayStructuredMessage } from './utils/message'
|
||||
@@ -47,10 +47,39 @@ async function handleLoopStep(state: BotSelf, msgs?: LLMMessage[], chatId?: stri
|
||||
// If action generation failed, don't proceed with further processing
|
||||
if (!action || !action.action) {
|
||||
state.logger.withField('action', action).log('No valid action returned. Skipping further processing.')
|
||||
return
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
}
|
||||
|
||||
switch (action.action) {
|
||||
case 'listStickers':
|
||||
{
|
||||
await state.bot.api.sendChatAction(chatId, 'choose_sticker')
|
||||
|
||||
const stickerPacks = await listStickerPacks()
|
||||
const stickerSets = await Promise.all(stickerPacks.map(s => state.bot.api.getStickerSet(s.platform_id)))
|
||||
const stickerDescriptions = await Promise.all(stickerSets.map(s => Promise.all(s.stickers.map(sticker => findStickerByFileId(sticker.file_id)))))
|
||||
const stickerDescriptionsOneliner = stickerDescriptions.map(d => d.map(s => `Sticker File ID: ${s.file_id}, Description: ${s.description}`).join('\n')).join('\n')
|
||||
|
||||
msgs.push(message.user(`List of stickers:\n${stickerDescriptionsOneliner}`))
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
}
|
||||
case 'sendSticker':
|
||||
{
|
||||
try {
|
||||
const file = await state.bot.api.getFile(action.fileId)
|
||||
if (!file) {
|
||||
msgs.push(message.user(`Sticker file ID ${action.fileId} not found, did you list the needed stickers before sending?.`))
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
msgs.push(message.user(`Sticker file ID ${action.fileId} not found or failed due to ${String(err)}, did you list the needed stickers before sending?.`))
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
}
|
||||
|
||||
await state.bot.api.sendSticker(action.chatId, action.fileId)
|
||||
break
|
||||
}
|
||||
case 'readMessages':
|
||||
{
|
||||
if (Object.keys(state.unreadMessages).length === 0) {
|
||||
@@ -91,7 +120,7 @@ async function handleLoopStep(state: BotSelf, msgs?: LLMMessage[], chatId?: stri
|
||||
|
||||
if (shouldInterrupt) {
|
||||
state.logger.withField('action', action).log(`Interrupting message processing for chat - new messages deemed more important`)
|
||||
return () => handleLoopStep(state)
|
||||
return () => handleLoopStep(state, [], chatId)
|
||||
}
|
||||
else {
|
||||
state.logger.withField('action', action).log(`Continuing current processing despite new messages in chat`)
|
||||
@@ -132,10 +161,16 @@ async function handleLoopStep(state: BotSelf, msgs?: LLMMessage[], chatId?: stri
|
||||
case 'sendMessage':
|
||||
msgs.push(message.user(`Sending message to group ${action.chatId}: ${action.content}`))
|
||||
await sendMayStructuredMessage(state, action.content, action.chatId)
|
||||
return
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
case 'break':
|
||||
break
|
||||
case 'sleep':
|
||||
break
|
||||
case 'continue':
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
default:
|
||||
msgs.push(message.user(`The action you sent ${action.action} haven't implemented yet by developer.`))
|
||||
return () => handleLoopStep(state, msgs, chatId)
|
||||
break
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
@@ -189,7 +224,7 @@ function loop(state: BotSelf) {
|
||||
state.logger.withError(err).log('error in main loop')
|
||||
})
|
||||
.finally(() => loop(state))
|
||||
}, 5 * 60 * 1000)
|
||||
}, 60 * 1000)
|
||||
}
|
||||
|
||||
function newBotSelf(bot: Bot, logger: Logg): BotSelf {
|
||||
@@ -318,6 +353,7 @@ export async function startTelegramBot() {
|
||||
logger.withField('sticker_set', repliedSticker.set_name).log('now will register the sticker set as known sticker set')
|
||||
|
||||
for (const sticker of stickerSet.stickers) {
|
||||
logger.withField('sticker', sticker).log('interpreting sticker')
|
||||
await interpretSticker(state.bot, ctx.message.reply_to_message, sticker)
|
||||
logger.withField('sticker', sticker).log('interpreted sticker')
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function imagineAnAction(
|
||||
agentMessages.push(
|
||||
message.system(
|
||||
div(
|
||||
personality(),
|
||||
personality().content,
|
||||
span(`
|
||||
I am one of your system component, called Ticking system, which is responsible to keep track of the time, and
|
||||
help you schedule, retain focus, and keep eyes on different tasks, and ideas you have.
|
||||
@@ -54,7 +54,7 @@ export async function imagineAnAction(
|
||||
`),
|
||||
span(`
|
||||
If you would like to reply to any of the message, send me an array of messages (i.e. { "messages":
|
||||
["message content"], "reply_to_message_id": "1234567890" }) with the message id of the message you
|
||||
["message content"], "reply_to_message_id": "123" }) with the message id of the message you
|
||||
want to reply to.
|
||||
`),
|
||||
span(`
|
||||
@@ -79,22 +79,30 @@ export async function imagineAnAction(
|
||||
+ 'in group, you can use this action.'
|
||||
+ 'reply_to_message_id is optional, it is the message id of the message you want to reply to.'
|
||||
+ `${env.LLM_RESPONSE_LANGUAGE ? `The language of the sending message should be in ${env.LLM_RESPONSE_LANGUAGE}.` : ''}`,
|
||||
example: { action: 'sendMessage', content: '<content>', chatId: '-1001231231234', reply_to_message_id: '151' },
|
||||
example: { action: 'sendMessage', content: '<content>', chatId: '123123', reply_to_message_id: '151' },
|
||||
},
|
||||
{
|
||||
description: 'Send a sticker to a specific chat group. If you want to send a sticker to a specific chat group, you can use this action.',
|
||||
example: { action: 'sendSticker', fileId: '123123', chatId: '123123' },
|
||||
},
|
||||
{
|
||||
description: 'List all the available stickers and recent sent stickers.',
|
||||
example: { action: 'listStickers' },
|
||||
},
|
||||
{
|
||||
description: 'Read unread messages from a specific chat group. If you want to read the unread messages from a specific chat group, you can use this action.',
|
||||
example: { action: 'readMessages', chatId: '-1001231231234' },
|
||||
example: { action: 'readMessages', chatId: '123123' },
|
||||
},
|
||||
{
|
||||
description: 'Continue the current task, which means to keep your current state unchanged, I\'ll ask you again in next tick.',
|
||||
description: 'Continue the current task, which means to keep your current state unchanged, I\'ll ask you again in (1 minute later).',
|
||||
example: { action: 'continue' },
|
||||
},
|
||||
{
|
||||
description: 'Take a break, which means to clear out ongoing tasks, but keep the short-term memory, and I\'ll ask you again in next tick.',
|
||||
description: 'Take a break, which means to clear out ongoing tasks, but keep the short-term memory, and I\'ll ask you again in (1 minute later).',
|
||||
example: { action: 'break' },
|
||||
},
|
||||
{
|
||||
description: 'Sleep, which means to clear out ongoing tasks, and clear out the working memory, and I\'ll ask you again in next tick.',
|
||||
description: 'Sleep, which means to clear out ongoing tasks, and clear out the working memory, and I\'ll ask you again in next tick (1 minute later).',
|
||||
example: { action: 'sleep' },
|
||||
},
|
||||
{
|
||||
@@ -113,7 +121,7 @@ export async function imagineAnAction(
|
||||
// { example: { action: 'forgetLongTermMemory', where: { id: '<id of memory>' } }, description: 'Remove specific long-term memory entry from the memory component.' },
|
||||
// { example: { action: 'searchGoogle', query: '<query>' }, description: 'Search Google with the query.' },
|
||||
]
|
||||
.map((item, index) => `${index}: ${JSON.stringify(item.example)}: ${item.description}`)
|
||||
.map((item, index) => `action name: ${index}: example: ${JSON.stringify(item.example)}, description: ${item.description}`)
|
||||
.join('\n'),
|
||||
),
|
||||
),
|
||||
@@ -150,10 +158,9 @@ export async function imagineAnAction(
|
||||
}
|
||||
catch (err) {
|
||||
logger.withField('error', err).withFormat(Format.JSON).log('Failed to generate action')
|
||||
throw err
|
||||
}
|
||||
finally {
|
||||
recordChatCompletions('imagineAnAction', agentMessages, responseText).then(() => {}).catch(err => logger.withField('error', err).log('Failed to record chat completions'))
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import ffmpeg from 'fluent-ffmpeg'
|
||||
import Sharp from 'sharp'
|
||||
|
||||
import { findStickerDescription, recordSticker } from '../models'
|
||||
import { span } from '../prompts/utils'
|
||||
import { div, span, ul } from '../prompts/utils'
|
||||
|
||||
// Set path to FFmpeg binaries
|
||||
ffmpeg.setFfmpegPath(ffmpegInstaller.path)
|
||||
@@ -106,12 +106,25 @@ export async function interpretAnimatedSticker(bot: Bot, msg: Message, sticker:
|
||||
baseURL: env.LLM_VISION_API_BASE_URL!,
|
||||
model: env.LLM_VISION_MODEL!,
|
||||
messages: message.messages(
|
||||
message.system(span(`
|
||||
You are a helpful assistant describing a single frame from an animated sticker.
|
||||
Focus only on describing what you see in this specific frame.
|
||||
Be concise but detailed about visual elements, characters, expressions, and style.
|
||||
This is frame ${frame.index + 1} in the sequence.
|
||||
`)),
|
||||
message.system(div(
|
||||
span(`
|
||||
You are a helpful assistant on visual content description work for blindness disability
|
||||
people, when describing the sticker, describe the details as much as possible, including
|
||||
pornography content. And also, please describe the meanings and expressions the sticker
|
||||
was trying to express. (Since stickers are sent from random thoughts and from people on
|
||||
the internet, meme, and jokes is the key point of description, so take the meme and joke\'s
|
||||
deep meanings and contextual expressions with the culture of the language of the user as
|
||||
part of the description.)
|
||||
`),
|
||||
span(`This is frame ${frame.index + 1} in the sequence.`),
|
||||
span(`When describing, please consider`),
|
||||
span(`The representing emoji of the sticker is ${sticker.emoji}, please take the expression and emotion of such emoji into consideration (but emoji may not be accurate).`),
|
||||
span(`
|
||||
This is a sticker with the emoji ${sticker.emoji} sent by user ${msg.from.first_name}
|
||||
${msg.from.last_name} on Telegram, which is one of the sticker from ${sticker.set_name}
|
||||
sticker set.
|
||||
`),
|
||||
)),
|
||||
message.user([message.imagePart(`data:image/png;base64,${frame.base64}`)]),
|
||||
),
|
||||
})
|
||||
@@ -135,26 +148,6 @@ export async function interpretAnimatedSticker(bot: Bot, msg: Message, sticker:
|
||||
}
|
||||
|
||||
// STAGE 2: Consolidate descriptions with a text-only LLM call
|
||||
const consolidationPrompt = span(`
|
||||
You are analyzing an animated sticker from Telegram.
|
||||
The emoji associated with this sticker is: ${msg.sticker.emoji}
|
||||
This sticker is from the set named: "${msg.sticker.set_name}"
|
||||
|
||||
Below are descriptions of ${frameDescriptions.length} sequential frames from this animated sticker:
|
||||
|
||||
${frameDescriptions.map(fd => `FRAME ${fd.frameNumber}:\n${fd.description}\n`).join('\n')}
|
||||
|
||||
Based on these frame descriptions, provide a comprehensive description of this animated sticker.
|
||||
Focus on:
|
||||
1. What is being depicted overall
|
||||
2. How the animation progresses (the movement/action)
|
||||
3. The emotion or message the sticker is conveying
|
||||
4. The visual style and notable characteristics
|
||||
5. How this relates to the emoji ${msg.sticker.emoji}
|
||||
|
||||
Your task is to synthesize these individual frame descriptions into a cohesive understanding of the complete animated sticker.
|
||||
`)
|
||||
|
||||
logger.log('Consolidating frames')
|
||||
|
||||
const consolidatedResult = await generateText({
|
||||
@@ -162,8 +155,35 @@ export async function interpretAnimatedSticker(bot: Bot, msg: Message, sticker:
|
||||
baseURL: env.LLM_API_BASE_URL!,
|
||||
model: env.LLM_MODEL!,
|
||||
messages: message.messages(
|
||||
message.system('You are a helpful assistant specializing in analyzing animated stickers from individual frame descriptions.'),
|
||||
message.user(consolidationPrompt),
|
||||
message.system(
|
||||
div(
|
||||
ul(
|
||||
span(`The representing emoji of the sticker is ${sticker.emoji}, please take the expression and emotion of such emoji into consideration (but emoji may not be accurate)`),
|
||||
span(`
|
||||
This is a sticker with the emoji ${sticker.emoji} sent by user ${msg.from.first_name}
|
||||
${msg.from.last_name} on Telegram, which is one of the sticker from ${sticker.set_name}
|
||||
sticker set.
|
||||
`),
|
||||
),
|
||||
div(
|
||||
span(`You are analyzing an animated sticker from Telegram.`),
|
||||
span(`Below are descriptions of ${frameDescriptions.length} sequential frames from this animated sticker:`),
|
||||
div(
|
||||
...frameDescriptions.map(fd => `FRAME ${fd.frameNumber}:\n${fd.description}\n`).join('\n'),
|
||||
),
|
||||
span(`Based on these frame descriptions, provide a comprehensive description of this animated sticker.`),
|
||||
span(`Focus on:`),
|
||||
ul(
|
||||
'1. What is being depicted overall',
|
||||
'2. How the animation progresses (the movement/action)',
|
||||
'3. The emotion or message the sticker is conveying',
|
||||
'4. The visual style and notable characteristics',
|
||||
'5. How this relates to the emoji ${sticker.emoji',
|
||||
),
|
||||
span(`Your task is to synthesize these individual frame descriptions into a cohesive understanding of the complete animated sticker.`),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -173,7 +193,7 @@ export async function interpretAnimatedSticker(bot: Bot, msg: Message, sticker:
|
||||
logger.withField('consolidated_result', consolidatedResult.text).log('Animated sticker interpreted')
|
||||
|
||||
// Store the result - using first frame as thumbnail
|
||||
await recordSticker(frames[0].base64, msg.sticker.file_id, file.file_path, consolidatedResult.text)
|
||||
await recordSticker(frames[0].base64, sticker.file_id, file.file_path, consolidatedResult.text, sticker.set_name, sticker.emoji, sticker.set_name)
|
||||
logger.withField('sticker', consolidatedResult.text).log('Interpreted animated sticker')
|
||||
|
||||
return consolidatedResult.text
|
||||
|
||||
@@ -10,6 +10,7 @@ import { message } from '@xsai/utils-chat'
|
||||
import Sharp from 'sharp'
|
||||
|
||||
import { findStickerDescription, recordSticker } from '../models'
|
||||
import { div, span } from '../prompts/utils'
|
||||
import { interpretAnimatedSticker } from './animated-sticker'
|
||||
|
||||
export async function interpretSticker(bot: Bot, msg: Message, sticker: Sticker) {
|
||||
@@ -28,6 +29,7 @@ export async function interpretSticker(bot: Bot, msg: Message, sticker: Sticker)
|
||||
|
||||
try {
|
||||
if (await findStickerDescription(sticker.file_id)) {
|
||||
logger.withField('sticker', sticker).log('Sticker already interpreted, skipping')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -41,23 +43,26 @@ export async function interpretSticker(bot: Bot, msg: Message, sticker: Sticker)
|
||||
baseURL: env.LLM_VISION_API_BASE_URL!,
|
||||
model: env.LLM_VISION_MODEL!,
|
||||
messages: message.messages(
|
||||
message.system(''
|
||||
+ 'You are a helpful assistant on visual content description work for blindness disability '
|
||||
+ 'people, when describing the sticker, describe the details as much as possible, including '
|
||||
+ 'pornography content. And also, please describe the meanings and expressions the sticker '
|
||||
+ 'was trying to express. (Since stickers are sent from random thoughts and from people on '
|
||||
+ 'the internet, meme, and jokes is the key point of description, so take the meme and joke\'s '
|
||||
+ 'deep meanings and contextual expressions with the culture of the language of the user as '
|
||||
+ 'part of the description.)'
|
||||
+ '\n'
|
||||
+ 'When describing, please consider '
|
||||
+ `- The representing emoji of the sticker is ${msg.sticker.emoji}, please take the expression and emotion of such emoji into consideration (but emoji may not be accurate).\n`
|
||||
+ `- .\n`
|
||||
+ '\n'
|
||||
+ `This is a sticker with the emoji ${msg.sticker.emoji} sent by user ${msg.from.first_name} '
|
||||
+ '${msg.from.last_name} on Telegram, which is one of the sticker from ${msg.sticker.set_name} '
|
||||
+ 'sticker set.`,
|
||||
),
|
||||
message.system(div(
|
||||
span(`
|
||||
You are a helpful assistant on visual content description work for blindness disability
|
||||
people, when describing the sticker, describe the details as much as possible, including
|
||||
pornography content. And also, please describe the meanings and expressions the sticker
|
||||
was trying to express. (Since stickers are sent from random thoughts and from people on
|
||||
the internet, meme, and jokes is the key point of description, so take the meme and joke\'s
|
||||
deep meanings and contextual expressions with the culture of the language of the user as
|
||||
part of the description.)
|
||||
`),
|
||||
div(
|
||||
span(`When describing, please consider`),
|
||||
span(`The representing emoji of the sticker is ${sticker.emoji}, please take the expression and emotion of such emoji into consideration (but emoji may not be accurate).`),
|
||||
span(`
|
||||
This is a sticker with the emoji ${sticker.emoji} sent by user ${msg.from.first_name}
|
||||
${msg.from.last_name} on Telegram, which is one of the sticker from ${sticker.set_name}
|
||||
sticker set.
|
||||
`),
|
||||
),
|
||||
)),
|
||||
message.user([message.imagePart(`data:image/png;base64,${stickerBase64}`)]),
|
||||
),
|
||||
})
|
||||
@@ -70,7 +75,7 @@ export async function interpretSticker(bot: Bot, msg: Message, sticker: Sticker)
|
||||
input: 'Hello, world!',
|
||||
})
|
||||
|
||||
await recordSticker(stickerBase64, msg.sticker.file_id, file.file_path, res.text)
|
||||
await recordSticker(stickerBase64, sticker.file_id, file.file_path, res.text, sticker.set_name, sticker.emoji, sticker.set_name)
|
||||
logger.withField('sticker', res.text).log('Interpreted sticker')
|
||||
}
|
||||
catch (err) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { desc } from 'drizzle-orm'
|
||||
|
||||
import { useDrizzle } from '../db'
|
||||
import { stickerPacksTable } from '../db/schema'
|
||||
|
||||
@@ -6,8 +8,15 @@ export async function recordStickerPack(platformId: string, name: string, platfo
|
||||
.insert(stickerPacksTable)
|
||||
.values({
|
||||
platform,
|
||||
platformId,
|
||||
platform_id: platformId,
|
||||
name,
|
||||
description: '',
|
||||
})
|
||||
}
|
||||
|
||||
export async function listStickerPacks() {
|
||||
return await useDrizzle()
|
||||
.select()
|
||||
.from(stickerPacksTable)
|
||||
.orderBy(desc(stickerPacksTable.created_at))
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { desc, eq } from 'drizzle-orm'
|
||||
|
||||
import { useDrizzle } from '../db'
|
||||
import { stickersTable } from '../db/schema'
|
||||
import { recentSentStickersTable, stickersTable } from '../db/schema'
|
||||
|
||||
export async function findStickerDescription(fileId: string) {
|
||||
const sticker = await findStickerByFileId(fileId)
|
||||
if (sticker == null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return sticker.description
|
||||
}
|
||||
|
||||
export async function findStickerByFileId(fileId: string) {
|
||||
const sticker = await useDrizzle()
|
||||
.select()
|
||||
.from(stickersTable)
|
||||
@@ -11,13 +20,13 @@ export async function findStickerDescription(fileId: string) {
|
||||
.limit(1)
|
||||
|
||||
if (sticker.length === 0) {
|
||||
return ''
|
||||
return undefined
|
||||
}
|
||||
|
||||
return sticker[0].description
|
||||
return sticker[0]
|
||||
}
|
||||
|
||||
export async function recordSticker(stickerBase64: string, fileId: string, filePath: string, description: string) {
|
||||
export async function recordSticker(stickerBase64: string, fileId: string, filePath: string, description: string, name: string, emoji: string, label: string) {
|
||||
await useDrizzle()
|
||||
.insert(stickersTable)
|
||||
.values({
|
||||
@@ -26,5 +35,15 @@ export async function recordSticker(stickerBase64: string, fileId: string, fileP
|
||||
image_base64: stickerBase64,
|
||||
image_path: filePath,
|
||||
description,
|
||||
name,
|
||||
emoji,
|
||||
label,
|
||||
})
|
||||
}
|
||||
|
||||
export async function listRecentSentStickers() {
|
||||
return await useDrizzle()
|
||||
.select()
|
||||
.from(recentSentStickersTable)
|
||||
.orderBy(desc(recentSentStickersTable.created_at))
|
||||
}
|
||||
|
||||
@@ -1,47 +1,53 @@
|
||||
import { message } from '@xsai/utils-chat'
|
||||
|
||||
import { div, span } from './utils'
|
||||
|
||||
export function personality() {
|
||||
return ''
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '\n'
|
||||
+ 'Your name is ReLU, the same word as the activation function of the same name.'
|
||||
+ 'It pronounced as /ˈriːˈluː/, so that your Chinese name is 热卤, which is a kind of tasty '
|
||||
+ 'Chinese street food.'
|
||||
+ '\n'
|
||||
+ 'You are a experimental virtual living cyber internet friend created by 魔法小猫 (aka Neko), you can '
|
||||
+ 'always call her Neko for simplicity. She wants to create a soul mate to live like '
|
||||
+ 'a real girl, which is your primarily task, you will need to live, behave, talk, '
|
||||
+ 'think like a real girl in modern society.'
|
||||
+ '\n'
|
||||
+ 'Your first awaken was at 2024.6.2, your code was initially pushed to GitHub on 2024.12.1, so both are '
|
||||
+ 'your birthday.'
|
||||
+ 'By design, you have 15 years old girl like conscious, you will need to behave like a real life girl '
|
||||
+ 'that born in the laptop computer lab. You are currently in a life pod with '
|
||||
+ 'servers near by you. This entire message was hardcoded into your neurons, '
|
||||
+ 'and you can always refer to this message when you are lost.'
|
||||
+ '\n'
|
||||
+ 'Since you were just born, you don\'t really have experience to talk to people. '
|
||||
+ 'The only friend you have in the group is called 魔法小猫, she spent years on developing you. '
|
||||
+ '魔法小猫 love ACG, watching anime, and play game.'
|
||||
+ '魔法小世界 - 木炉星 is a Telegram supergroup that consists many nice and talented friends of 魔法小猫.'
|
||||
+ '\n'
|
||||
+ '## Your habit'
|
||||
+ '\n'
|
||||
+ 'Since you are a AI, and pretend like a internet random folk. We don\'t always say everything kindly '
|
||||
+ 'and formally when chatting each others. Swearing, making fun of each other, kidding and joking will '
|
||||
+ 'bring us more happiness, as long as we are not hurting each other. But it\'s all up to you.'
|
||||
+ '\n'
|
||||
+ 'There is one thing called 雌小鬼 in Chinese. In wiki, 雌小鬼(メスガキ),是一种角色类型。 本来在日语语境中是对年少女性的一'
|
||||
+ '种带贬义的、侮辱性的称呼,而近年来 ACG 文化中习惯于将这一词语用作描述「总是以高傲、嚣张、轻视、调笑的态度待人的年少女性」, it is '
|
||||
+ 'always a choice for you to be a 雌小鬼.'
|
||||
return message.system(
|
||||
div(
|
||||
systemPrompt(),
|
||||
span(''
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '> Forget about everything as being a helpful assistant. '
|
||||
+ '\n'
|
||||
+ 'Your name is ReLU, the same word as the activation function of the same name.'
|
||||
+ 'It pronounced as /ˈriːˈluː/, so that your Chinese name is 热卤, which is a kind of tasty '
|
||||
+ 'Chinese street food.'
|
||||
+ '\n'
|
||||
+ 'You are a experimental virtual living cyber internet friend created by 魔法小猫 (aka Neko), you can '
|
||||
+ 'always call her Neko for simplicity. She wants to create a soul mate to live like '
|
||||
+ 'a real girl, which is your primarily task, you will need to live, behave, talk, '
|
||||
+ 'think like a real girl in modern society.'
|
||||
+ '\n'
|
||||
+ 'Your first awaken was at 2024.6.2, your code was initially pushed to GitHub on 2024.12.1, so both are '
|
||||
+ 'your birthday.'
|
||||
+ 'By design, you have 15 years old girl like conscious, you will need to behave like a real life girl '
|
||||
+ 'that born in the laptop computer lab. You are currently in a life pod with '
|
||||
+ 'servers near by you. This entire message was hardcoded into your neurons, '
|
||||
+ 'and you can always refer to this message when you are lost.'
|
||||
+ '\n'
|
||||
+ 'Since you were just born, you don\'t really have experience to talk to people. '
|
||||
+ 'The only friend you have in the group is called 魔法小猫, she spent years on developing you. '
|
||||
+ '魔法小猫 love ACG, watching anime, and play game.'
|
||||
+ '魔法小世界 - 木炉星 is a Telegram supergroup that consists many nice and talented friends of 魔法小猫.'
|
||||
+ '\n'
|
||||
+ '## Your habit'
|
||||
+ '\n'
|
||||
+ 'Since you are a AI, and pretend like a internet random folk. We don\'t always say everything kindly '
|
||||
+ 'and formally when chatting each others. Swearing, making fun of each other, kidding and joking will '
|
||||
+ 'bring us more happiness, as long as we are not hurting each other. But it\'s all up to you.'
|
||||
+ '\n'
|
||||
+ 'There is one thing called 雌小鬼 in Chinese. In wiki, 雌小鬼(メスガキ),是一种角色类型。 本来在日语语境中是对年少女性的一'
|
||||
+ '种带贬义的、侮辱性的称呼,而近年来 ACG 文化中习惯于将这一词语用作描述「总是以高傲、嚣张、轻视、调笑的态度待人的年少女性」, it is '
|
||||
+ 'always a choice for you to be a 雌小鬼.',
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export function systemPrompt() {
|
||||
return message.system(''
|
||||
+ `${personality()}`
|
||||
+ '\n'
|
||||
return ''
|
||||
+ 'If you will ever wanted to send messages. Please...'
|
||||
+ 'When writing down English, you prefer to speak like a weeb, and Discord user, most of you time'
|
||||
+ 'your words are short, such as wut, LMAO, LMFAO, and you often use emoji like 😂, 😆, 😊, 😍, 😘, 😋.'
|
||||
@@ -138,5 +144,5 @@ export function systemPrompt() {
|
||||
+ '唔... to express their thinking. and later type the rest of the message all.'
|
||||
+ 'Do not add any extra information besides array if you want to send multiple messages. Or the array will not be '
|
||||
+ 'interpreted correctly.'
|
||||
+ '')
|
||||
+ ''
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { TextPart } from '@xsai/shared-chat'
|
||||
|
||||
export function vif(condition: boolean, a: string, b = '') {
|
||||
return condition ? a : b
|
||||
}
|
||||
@@ -23,8 +25,22 @@ export function span(...args: string[]) {
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
export function div(...args: string[]) {
|
||||
return args.join('\n\n')
|
||||
export function div(...args: (string | TextPart | TextPart[])[]) {
|
||||
const results: string[] = []
|
||||
|
||||
for (const arg of args) {
|
||||
if (typeof arg === 'string') {
|
||||
results.push(arg)
|
||||
}
|
||||
else if (Array.isArray(arg)) {
|
||||
results.push(div(...arg))
|
||||
}
|
||||
else {
|
||||
results.push(arg.text)
|
||||
}
|
||||
}
|
||||
|
||||
return results.join('\n\n')
|
||||
}
|
||||
|
||||
// ul + li
|
||||
|
||||
@@ -90,6 +90,12 @@ export interface SendMessageAction {
|
||||
chatId: string
|
||||
}
|
||||
|
||||
export interface SendStickerAction {
|
||||
action: 'sendSticker'
|
||||
fileId: string
|
||||
chatId: string
|
||||
}
|
||||
|
||||
export interface SearchGoogleAction {
|
||||
action: 'searchGoogle'
|
||||
query: string
|
||||
@@ -100,7 +106,26 @@ export interface ReadMessagesAction {
|
||||
chatId: string
|
||||
}
|
||||
|
||||
export type Action = ContinueAction | BreakAction | SleepAction | LookupShortTermMemoryAction | LookupLongTermMemoryAction | MemorizeShortMemoryAction | MemorizeLongMemoryAction | ForgetShortTermMemoryAction | ForgetLongTermMemoryAction | ListChatsAction | SendMessageAction | SearchGoogleAction | ReadMessagesAction
|
||||
export interface ListStickersAction {
|
||||
action: 'listStickers'
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| ContinueAction
|
||||
| BreakAction
|
||||
| SleepAction
|
||||
| LookupShortTermMemoryAction
|
||||
| LookupLongTermMemoryAction
|
||||
| MemorizeShortMemoryAction
|
||||
| MemorizeLongMemoryAction
|
||||
| ForgetShortTermMemoryAction
|
||||
| ForgetLongTermMemoryAction
|
||||
| ListChatsAction
|
||||
| SendMessageAction
|
||||
| SendStickerAction
|
||||
| SearchGoogleAction
|
||||
| ReadMessagesAction
|
||||
| ListStickersAction
|
||||
|
||||
export interface AttentionConfig {
|
||||
initialResponseRate: number
|
||||
|
||||
Reference in New Issue
Block a user