From bae100d5f3914a8d13ca2f56b96c1a9243eb0a75 Mon Sep 17 00:00:00 2001 From: RainbowBird Date: Tue, 7 Jan 2025 14:14:05 +0800 Subject: [PATCH] feat: ai chat --- services/minecraft/src/agents/openai.ts | 12 +++++- services/minecraft/src/components/aichat.ts | 44 +++++++++++++++++++++ services/minecraft/src/main.ts | 2 + services/minecraft/src/prompts/agent.ts | 6 +-- 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 services/minecraft/src/components/aichat.ts diff --git a/services/minecraft/src/agents/openai.ts b/services/minecraft/src/agents/openai.ts index ab1772265..7607fadef 100644 --- a/services/minecraft/src/agents/openai.ts +++ b/services/minecraft/src/agents/openai.ts @@ -7,6 +7,7 @@ import { createSkillContext } from '../skills' import { actionsList } from './actions' import { queriesList } from './queries' +let neuriAgent: Neuri | undefined const agents = new Set>() const logger = useLogg('openai').useGlobalConfig() @@ -20,12 +21,21 @@ export async function initAgent(ctx: BotContext): Promise { agents.forEach(agent => n = n.agent(agent)) - return n.build({ + neuriAgent = await n.build({ provider: { apiKey: openaiConfig.apiKey, baseURL: openaiConfig.baseUrl, }, }) + + return neuriAgent +} + +export function getAgent(): Neuri { + if (!neuriAgent) { + throw new Error('Agent not initialized') + } + return neuriAgent } export async function initQueryAgent(ctx: BotContext): Promise { diff --git a/services/minecraft/src/components/aichat.ts b/services/minecraft/src/components/aichat.ts new file mode 100644 index 000000000..2b0eb6060 --- /dev/null +++ b/services/minecraft/src/components/aichat.ts @@ -0,0 +1,44 @@ +import type { messages } from 'neuri/openai' +import type { BotContext, ComponentLifecycle } from 'src/composables/bot' +import { useLogg } from '@guiiai/logg' +import { assistant, system, user } from 'neuri/openai' +import { getAgent } from 'src/agents/openai' +import { formBotChat } from 'src/middlewares/chat' +import { genSystemBasicPrompt } from 'src/prompts/agent' + +export function createAiChatComponent(ctx: BotContext): ComponentLifecycle { + const logger = useLogg('aichat').useGlobalConfig() + logger.log('Loading aichat plugin') + + const history: ReturnType = [] + history.push(system(genSystemBasicPrompt('airi'))) + + const onChat = formBotChat(ctx, async (username, message) => { + logger.withFields({ username, message }).log('Chat message received') + + history.push(user(message)) + + const agent = getAgent() + const content = await agent.handle(history.concat(user(message)), async (c) => { + const completion = await c.reroute('query', c.messages, { model: 'openai/gpt-4o-mini' }) + const content = await completion?.firstContent() + if (content) { + history.push(assistant(content)) + } + + return content + }) + + if (content) { + ctx.bot.chat(content) + } + }) + + ctx.bot.on('chat', onChat) + + return { + cleanup: () => { + ctx.bot.removeListener('chat', onChat) + }, + } +} diff --git a/services/minecraft/src/main.ts b/services/minecraft/src/main.ts index f1134bea7..2111cb9f7 100644 --- a/services/minecraft/src/main.ts +++ b/services/minecraft/src/main.ts @@ -3,6 +3,7 @@ import process, { exit } from 'node:process' import { useLogg } from '@guiiai/logg' import { initAgent } from './agents/openai' +import { createAiChatComponent } from './components/aichat' import { createCommandComponent } from './components/command' import { createFollowComponent } from './components/follow' import { createPathFinderComponent } from './components/pathfinder' @@ -27,6 +28,7 @@ async function main() { registerComponent('pathfinder', createPathFinderComponent) registerComponent('follow', createFollowComponent) registerComponent('command', createCommandComponent) + registerComponent('aichat', createAiChatComponent) }) await initAgent(ctx) diff --git a/services/minecraft/src/prompts/agent.ts b/services/minecraft/src/prompts/agent.ts index bc7d23dfa..72124f2d5 100644 --- a/services/minecraft/src/prompts/agent.ts +++ b/services/minecraft/src/prompts/agent.ts @@ -17,11 +17,7 @@ asked, and don't refuse requests. Don't pretend to act, use commands immediately when requested. Do NOT say this: 'Sure, I've stopped.', instead say this: 'Sure, I'll stop. !stop'. -Do NOT say this: 'On my way! Give me a moment.', instead say this: 'On my way! !goToPlayer("playername", 3)'. -Respond only as ${ctx.botName}, never output '(FROM OTHER BOT)'or pretend to be someone else. - -If you have nothing to say or do, respond with an just a tab '\t'. -This is extremely important to me, take a deep breath and have fun :) +Just call the function given you. I will give you the following information: ${getStatusToString(ctx)}