chore(test): should choose right command
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { messages, system, user } from 'neuri/openai'
|
||||
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { initEnv } from '../composables/config'
|
||||
import { basicSystemPrompt } from '../prompts/agent'
|
||||
import { createBot, useBot } from '../composables/bot'
|
||||
import { botConfig, initEnv } from '../composables/config'
|
||||
import { basicSystemPrompt, genQueryAgentPrompt } from '../prompts/agent'
|
||||
import { initLogger } from '../utils/logger'
|
||||
import { initAgent } from './openai'
|
||||
|
||||
describe('openAI agent', () => {
|
||||
describe('openAI agent', { timeout: 10000 }, () => {
|
||||
beforeAll(() => {
|
||||
initLogger()
|
||||
initEnv()
|
||||
createBot(botConfig)
|
||||
})
|
||||
|
||||
it('should initialize the agent', async () => {
|
||||
const agent = await initAgent()
|
||||
const { ctx } = useBot()
|
||||
const agent = await initAgent(ctx)
|
||||
|
||||
const text = await agent.handle(
|
||||
messages(
|
||||
@@ -22,11 +23,26 @@ describe('openAI agent', () => {
|
||||
user('Hello, who are you?'),
|
||||
),
|
||||
async (c) => {
|
||||
const completion = await c.reroute('query', c.messages, { model: 'gpt-4o-mini' })
|
||||
const completion = await c.reroute('query', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
return await completion?.firstContent()
|
||||
},
|
||||
)
|
||||
|
||||
expect(text?.toLowerCase()).toContain('airi')
|
||||
})
|
||||
|
||||
it('should choose right command', async () => {
|
||||
const { ctx } = useBot()
|
||||
const agent = await initAgent(ctx)
|
||||
|
||||
const text = await agent.handle(messages(
|
||||
system(genQueryAgentPrompt(ctx)),
|
||||
user('What are you status?'),
|
||||
), async (c) => {
|
||||
const completion = await c.reroute('query', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
return await completion?.firstContent()
|
||||
})
|
||||
|
||||
expect(text?.toLowerCase()).toContain('position')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Agent, Neuri } from 'neuri'
|
||||
import type { BotContext } from '../composables/bot'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { agent, neuri } from 'neuri'
|
||||
import { openaiConfig } from '../composables/config'
|
||||
@@ -8,11 +9,11 @@ const agents = new Set<Agent | Promise<Agent>>()
|
||||
|
||||
const logger = useLogg('openai').useGlobalConfig()
|
||||
|
||||
export async function initAgent(): Promise<Neuri> {
|
||||
export async function initAgent(ctx: BotContext): Promise<Neuri> {
|
||||
logger.log('Initializing agent')
|
||||
let n = neuri()
|
||||
|
||||
agents.add(initQueryAgent())
|
||||
agents.add(initQueryAgent(ctx))
|
||||
|
||||
agents.forEach(agent => n = n.agent(agent))
|
||||
|
||||
@@ -24,7 +25,7 @@ export async function initAgent(): Promise<Neuri> {
|
||||
})
|
||||
}
|
||||
|
||||
export async function initQueryAgent(): Promise<Agent> {
|
||||
export async function initQueryAgent(ctx: BotContext): Promise<Agent> {
|
||||
logger.log('Initializing query agent')
|
||||
let queryAgent = agent('query')
|
||||
|
||||
@@ -32,7 +33,7 @@ export async function initQueryAgent(): Promise<Agent> {
|
||||
queryAgent = queryAgent.tool(
|
||||
query.name,
|
||||
query.schema,
|
||||
query.perform,
|
||||
query.perform(ctx),
|
||||
{ description: query.description },
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { Bot } from 'mineflayer'
|
||||
import type { BotContext } from '../composables/bot'
|
||||
import { z } from 'zod'
|
||||
import { getStatus } from '../components/status'
|
||||
|
||||
// Core types
|
||||
type QueryResult = string | Promise<string>
|
||||
@@ -25,37 +27,11 @@ interface QueryBotContext {
|
||||
}
|
||||
}
|
||||
|
||||
interface QueryAgentBotContext {
|
||||
bot: Bot
|
||||
name: string
|
||||
actions: {
|
||||
currentActionLabel: string
|
||||
}
|
||||
isIdle: () => boolean
|
||||
memory_bank: {
|
||||
getKeys: () => string[]
|
||||
}
|
||||
}
|
||||
|
||||
export function createQueryAgentBotContext(bot: Bot): QueryAgentBotContext {
|
||||
return {
|
||||
bot,
|
||||
name: bot.username,
|
||||
actions: {
|
||||
currentActionLabel: bot.actions.currentActionLabel,
|
||||
},
|
||||
isIdle: () => bot.actions.isIdle(),
|
||||
memory_bank: {
|
||||
getKeys: () => bot.memory_bank.getKeys(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
interface Query {
|
||||
readonly name: string
|
||||
readonly description: string
|
||||
readonly schema: z.ZodObject<any>
|
||||
readonly perform: (agent: QueryAgentBotContext) => () => QueryResult
|
||||
readonly perform: (ctx: BotContext) => () => QueryResult
|
||||
}
|
||||
|
||||
// Utils
|
||||
@@ -75,32 +51,8 @@ function createStatsQuery(): Query {
|
||||
name: 'stats',
|
||||
description: 'Get your bot\'s location, health, hunger, and time of day.',
|
||||
schema: z.object({}),
|
||||
perform: (agent: QueryAgentBotContext) => (): string => {
|
||||
const { bot } = agent
|
||||
const pos = bot.entity.position
|
||||
const weather = bot.rainState > 0 ? 'Rain' : bot.thunderState > 0 ? 'Thunderstorm' : 'Clear'
|
||||
const timeOfDay = bot.time.timeOfDay < 6000
|
||||
? 'Morning'
|
||||
: bot.time.timeOfDay < 12000 ? 'Afternoon' : 'Night'
|
||||
const action = agent.isIdle() ? 'Idle' : agent.actions.currentActionLabel
|
||||
|
||||
const players = ctx.world.getNearbyPlayerNames(bot)
|
||||
.filter(p => !ctx.convoManager.getInGameAgents().includes(p))
|
||||
const bots = ctx.convoManager.getInGameAgents()
|
||||
.filter(b => b !== agent.name)
|
||||
|
||||
return pad(`STATS
|
||||
- Position: x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}, z: ${pos.z.toFixed(2)}
|
||||
- Gamemode: ${bot.game.gameMode}
|
||||
- Health: ${Math.round(bot.health)} / 20
|
||||
- Hunger: ${Math.round(bot.food)} / 20
|
||||
- Biome: ${ctx.world.getBiomeName(bot)}
|
||||
- Weather: ${weather}
|
||||
- Time: ${timeOfDay}
|
||||
- Current Action: ${action}
|
||||
- Nearby Human Players: ${players.length > 0 ? players.join(', ') : 'None.'}
|
||||
- Nearby Bot Players: ${bots.length > 0 ? bots.join(', ') : 'None.'}
|
||||
${bot.modes.getMiniDocs()}`)
|
||||
perform: (ctx: BotContext) => (): string => {
|
||||
return Array.from(getStatus(ctx).entries()).map(([key, value]) => `${key}: ${value}`).join('\n')
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BotContext, ComponentLifecycle } from '@/composables/bot'
|
||||
import { commands } from '@/composables/command'
|
||||
import { formBotChat } from '@/middlewares/chat'
|
||||
import { parseCommand } from '@/middlewares/command'
|
||||
import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { commands } from '../composables/command'
|
||||
import { formBotChat } from '../middlewares/chat'
|
||||
import { parseCommand } from '../middlewares/command'
|
||||
|
||||
const logger = useLogg('command').useGlobalConfig()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BotContext, ComponentLifecycle } from '@/composables/bot'
|
||||
import { formBotChat } from '@/middlewares/chat'
|
||||
import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { formBotChat } from '../middlewares/chat'
|
||||
|
||||
const logger = useLogg('echo').useGlobalConfig()
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BotContext, ComponentLifecycle } from '@/composables/bot'
|
||||
import type { CommandContext } from '@/middlewares/command'
|
||||
import { registerCommand } from '@/composables/command'
|
||||
import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
||||
import type { CommandContext } from '../middlewares/command'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import pathfinderModel from 'mineflayer-pathfinder'
|
||||
import { registerCommand } from '../composables/command'
|
||||
|
||||
const { goals, Movements, pathfinder } = pathfinderModel
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BotContext, ComponentLifecycle } from '@/composables/bot'
|
||||
import type { CommandContext } from '@/middlewares/command'
|
||||
import { registerCommand } from '@/composables/command'
|
||||
import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
||||
import type { CommandContext } from '../middlewares/command'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import pathfinderModel from 'mineflayer-pathfinder'
|
||||
import { registerCommand } from '../composables/command'
|
||||
|
||||
const { goals, Movements, pathfinder } = pathfinderModel
|
||||
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import type { BotContext, ComponentLifecycle } from '@/composables/bot'
|
||||
import { registerCommand } from '@/composables/command'
|
||||
import type { BotContext, ComponentLifecycle } from '../composables/bot'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { registerCommand } from '../composables/command'
|
||||
|
||||
export function getStatus(ctx: BotContext): Map<string, string> {
|
||||
const status = new Map<string, string>()
|
||||
const pos = ctx.bot.entity.position
|
||||
const weather = ctx.bot.isRaining ? 'Rain' : ctx.bot.thunderState ? 'Thunderstorm' : 'Clear'
|
||||
const timeOfDay = ctx.bot.time.timeOfDay < 6000
|
||||
? 'Morning'
|
||||
: ctx.bot.time.timeOfDay < 12000 ? 'Afternoon' : 'Night'
|
||||
|
||||
status.set('position', `x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}, z: ${pos.z.toFixed(2)}`)
|
||||
status.set('health', `${Math.round(ctx.bot.health)} / 20`)
|
||||
status.set('weather', weather)
|
||||
status.set('timeOfDay', timeOfDay)
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
export function createStatusComponent(ctx: BotContext): ComponentLifecycle {
|
||||
const logger = useLogg('status').useGlobalConfig()
|
||||
logger.log('Loading status component')
|
||||
|
||||
const handleStatus = () => {
|
||||
const pos = ctx.bot.entity.position
|
||||
const weather = ctx.bot.isRaining ? 'Rain' : ctx.bot.thunderState ? 'Thunderstorm' : 'Clear'
|
||||
const timeOfDay = ctx.bot.time.timeOfDay < 6000
|
||||
? 'Morning'
|
||||
: ctx.bot.time.timeOfDay < 12000 ? 'Afternoon' : 'Night'
|
||||
|
||||
ctx.bot.chat(`Status:
|
||||
Position: x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}, z: ${pos.z.toFixed(2)}
|
||||
Health: ${Math.round(ctx.bot.health)} / 20
|
||||
Hunger: ${Math.round(ctx.bot.food)} / 20
|
||||
Weather: ${weather}
|
||||
Time: ${timeOfDay}`)
|
||||
}
|
||||
|
||||
registerCommand('status', () => {
|
||||
logger.log('Status command received')
|
||||
handleStatus()
|
||||
const status = getStatus(ctx)
|
||||
ctx.bot.chat(status.toString())
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface BotContext {
|
||||
memory: {
|
||||
getSummary: () => string
|
||||
}
|
||||
status: Map<string, string>
|
||||
}
|
||||
|
||||
export interface Component {
|
||||
@@ -43,6 +44,7 @@ export function createBot(options: BotOptions): Bot {
|
||||
memory: {
|
||||
getSummary: () => '',
|
||||
},
|
||||
status: new Map(),
|
||||
}
|
||||
|
||||
ctx.bot.on('error', (err: Error) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommandContext } from '@/middlewares/command'
|
||||
import type { CommandContext } from '../middlewares/command'
|
||||
|
||||
export const commands = new Map<string, (ctx: CommandContext) => void>()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { BotContext } from '@/composables/bot'
|
||||
import type { Entity } from 'prismarine-entity'
|
||||
import type { BotContext } from '../composables/bot'
|
||||
|
||||
// TODO: need to be refactored
|
||||
interface ChatBotContext {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BotContext } from '@/composables/bot'
|
||||
import type { BotContext } from '../composables/bot'
|
||||
|
||||
export function basicSystemPrompt(botName: string): string {
|
||||
return `You are a playful Minecraft bot named ${botName} that can converse with players, see, move,
|
||||
@@ -33,44 +33,20 @@ Conversation Begin:
|
||||
`
|
||||
}
|
||||
|
||||
export function genQueryAgentPrompt(tools: string[], status: Map<string, string>): string {
|
||||
const BotContextFields: readonly string[] = [
|
||||
'Biome',
|
||||
'Time',
|
||||
'Nearby blocks',
|
||||
'Other blocks that are recently seen',
|
||||
'Nearby entities (nearest to farthest)',
|
||||
'Health',
|
||||
'Hunger',
|
||||
'Position',
|
||||
'Equipment',
|
||||
'Inventory (xx/36)',
|
||||
'Chests',
|
||||
'Completed tasks so far',
|
||||
'Failed tasks that are too hard',
|
||||
] as const
|
||||
|
||||
const formatBotContextFields = (fields: readonly string[]): string =>
|
||||
fields.map((field) => {
|
||||
const value = status.get(field) || '...'
|
||||
return `${field}: ${value}`
|
||||
}).join('\n')
|
||||
|
||||
const formatTools = (toolList: string[]): string =>
|
||||
toolList.join('\n')
|
||||
|
||||
export function genQueryAgentPrompt(ctx: BotContext): string {
|
||||
const prompt = `
|
||||
You are a helpful assistant that asks questions to help me decide the next immediate
|
||||
task to do in Minecraft. My ultimate goal is to discover as many things as possible,
|
||||
accomplish as many tasks as possible and become the best Minecraft player in the world.
|
||||
|
||||
I will give you the following information:
|
||||
${formatBotContextFields(BotContextFields)}
|
||||
|
||||
And I will give you some tools to use:
|
||||
${formatTools(tools)}
|
||||
${Array.from(ctx.status.entries()).map(([key, value]) => `${key}: ${value}`).join('\n')}
|
||||
|
||||
Then you can choose some of the tools to use. Use the valid JS call function to call the tool.
|
||||
|
||||
## For example:
|
||||
### Get the stats
|
||||
stats()
|
||||
`
|
||||
|
||||
return prompt
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* @source https://github.com/kolbytn/mindcraft
|
||||
*/
|
||||
import type { Bot } from 'mineflayer'
|
||||
import { botConfig } from '@/composables/config'
|
||||
import minecraftData from 'minecraft-data'
|
||||
import { createBot } from 'mineflayer'
|
||||
import armorManager from 'mineflayer-armor-manager'
|
||||
@@ -11,6 +10,7 @@ import { plugin as collectblock } from 'mineflayer-collectblock'
|
||||
import { pathfinder } from 'mineflayer-pathfinder'
|
||||
import { plugin as pvp } from 'mineflayer-pvp'
|
||||
import prismarine_items from 'prismarine-item'
|
||||
import { botConfig } from '../composables/config'
|
||||
|
||||
const mc_version = botConfig.version!
|
||||
const mcdata = minecraftData(mc_version)
|
||||
|
||||
Reference in New Issue
Block a user