feat: integrated as Airi client
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import process, { exit } from 'node:process'
|
||||
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { Client } from '@proj-airi/server-sdk'
|
||||
import MineflayerArmorManager from 'mineflayer-armor-manager'
|
||||
import { loader as MineflayerAutoEat } from 'mineflayer-auto-eat'
|
||||
import { plugin as MineflayerCollectBlock } from 'mineflayer-collectblock'
|
||||
@@ -33,9 +34,11 @@ async function main() {
|
||||
],
|
||||
})
|
||||
|
||||
const airiClient = new Client({ name: 'minecraft-bot', url: 'ws://localhost:6121/ws' })
|
||||
|
||||
// Dynamically load LLMAgent after bot is initialized
|
||||
const agent = await initAgent(bot)
|
||||
await bot.loadPlugin(LLMAgent({ agent }))
|
||||
await bot.loadPlugin(LLMAgent({ agent, airiClient }))
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
bot.stop()
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { Client } from '@proj-airi/server-sdk'
|
||||
import type { Neuri } from 'neuri'
|
||||
import type { MineflayerPlugin } from '../libs/mineflayer/plugin'
|
||||
|
||||
import type { MineflayerPlugin } from '../libs/mineflayer/plugin'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { assistant, system, user } from 'neuri/openai'
|
||||
import { toRetriable } from 'src/utils/reliability'
|
||||
import { formBotChat } from '../libs/mineflayer/message'
|
||||
import { genActionAgentPrompt, genStatusPrompt } from '../prompts/agent'
|
||||
|
||||
export function LLMAgent(options: { agent: Neuri }): MineflayerPlugin {
|
||||
export function LLMAgent(options: { agent: Neuri, airiClient: Client }): MineflayerPlugin {
|
||||
return {
|
||||
async created(bot) {
|
||||
const agent = options.agent
|
||||
@@ -59,6 +60,48 @@ export function LLMAgent(options: { agent: Neuri }): MineflayerPlugin {
|
||||
}
|
||||
})
|
||||
|
||||
options.airiClient.onEvent('input:text:voice', async (event) => {
|
||||
logger.withFields({ user: event.data.discord?.guildMember, message: event.data.transcription }).log('Chat message received')
|
||||
|
||||
const statusPrompt = await genStatusPrompt(bot)
|
||||
bot.memory.chatHistory.push(system(statusPrompt))
|
||||
bot.memory.chatHistory.push(user(`${'NekoMeowww'}: ${event.data.transcription}`))
|
||||
|
||||
// logger.withFields({ chatHistory: bot.memory.chatHistory }).log('chatHistory')
|
||||
logger.withFields({ statusPrompt }).log('statusPrompt')
|
||||
|
||||
const content = await agent.handleStateless([...bot.memory.chatHistory], async (c) => {
|
||||
logger.log('thinking...')
|
||||
|
||||
const handleCompletion = async (c: any): Promise<string> => {
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' }) || { error: { message: 'Unknown error' } }
|
||||
if (!completion || 'error' in completion) {
|
||||
logger.withFields(c).error('Completion')
|
||||
throw new Error(completion?.error?.message ?? 'Unknown error')
|
||||
}
|
||||
|
||||
const content = await completion?.firstContent()
|
||||
logger.withFields({ usage: completion.usage, content }).log('output')
|
||||
bot.memory.chatHistory.push(assistant(content))
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
const retirableHandler = toRetriable<any, string>(
|
||||
3, // retryLimit
|
||||
1000, // delayInterval in ms
|
||||
handleCompletion,
|
||||
)
|
||||
|
||||
return await retirableHandler(c)
|
||||
})
|
||||
|
||||
if (content) {
|
||||
logger.withFields({ content }).log('responded')
|
||||
bot.bot.chat(content)
|
||||
}
|
||||
})
|
||||
|
||||
bot.bot.on('chat', onChat)
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user