chore: actions unit test
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import { messages, system, user } from 'neuri/openai'
|
||||
import { beforeAll, describe, it } from 'vitest'
|
||||
import { createBot, useBot } from '../composables/bot'
|
||||
import { botConfig, initEnv } from '../composables/config'
|
||||
import { genActionAgentPrompt } from '../prompts/agent'
|
||||
import { initLogger } from '../utils/logger'
|
||||
import { initAgent } from './openai'
|
||||
|
||||
describe('actions agent', { timeout: 0 }, () => {
|
||||
beforeAll(() => {
|
||||
initLogger()
|
||||
initEnv()
|
||||
createBot(botConfig)
|
||||
})
|
||||
|
||||
it('should split question into actions', async () => {
|
||||
const { ctx } = useBot()
|
||||
const agent = await initAgent(ctx)
|
||||
|
||||
function testFn() {
|
||||
return new Promise<void>((resolve) => {
|
||||
ctx.bot.on('spawn', async () => {
|
||||
const text = await agent.handle(messages(
|
||||
system(genActionAgentPrompt(ctx)),
|
||||
user('Help me to cut down the tree'),
|
||||
), async (c) => {
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
|
||||
console.log(completion)
|
||||
|
||||
return await completion?.firstContent()
|
||||
})
|
||||
|
||||
console.log(text)
|
||||
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
await testFn()
|
||||
})
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import { messages, system, user } from 'neuri/openai'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { createBot, useBot } from '../composables/bot'
|
||||
import { botConfig, initEnv } from '../composables/config'
|
||||
import { basicSystemPrompt, genQueryAgentPrompt } from '../prompts/agent'
|
||||
import { genQueryAgentPrompt, genSystemBasicPrompt } from '../prompts/agent'
|
||||
import { initLogger } from '../utils/logger'
|
||||
import { initAgent } from './openai'
|
||||
|
||||
@@ -17,32 +17,36 @@ describe('openAI agent', { timeout: 10000 }, () => {
|
||||
const { ctx } = useBot()
|
||||
const agent = await initAgent(ctx)
|
||||
|
||||
const text = await agent.handle(
|
||||
messages(
|
||||
system(basicSystemPrompt('airi')),
|
||||
user('Hello, who are you?'),
|
||||
),
|
||||
async (c) => {
|
||||
const completion = await c.reroute('query', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
return await completion?.firstContent()
|
||||
},
|
||||
)
|
||||
ctx.bot.once('spawn', async () => {
|
||||
const text = await agent.handle(
|
||||
messages(
|
||||
system(genSystemBasicPrompt('airi')),
|
||||
user('Hello, who are you?'),
|
||||
),
|
||||
async (c) => {
|
||||
const completion = await c.reroute('query', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
return await completion?.firstContent()
|
||||
},
|
||||
)
|
||||
|
||||
expect(text?.toLowerCase()).toContain('airi')
|
||||
expect(text?.toLowerCase()).toContain('airi')
|
||||
})
|
||||
})
|
||||
|
||||
it('should choose right query 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()
|
||||
})
|
||||
ctx.bot.once('spawn', async () => {
|
||||
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')
|
||||
expect(text?.toLowerCase()).toContain('position')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Position } from './../skills/base'
|
||||
import type { BotInternalEventHandlers, BotInternalEvents } from './events'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import mineflayer, { type Bot, type BotOptions } from 'mineflayer'
|
||||
@@ -9,6 +10,7 @@ let ctx: BotContext | undefined
|
||||
export interface BotContext {
|
||||
bot: Bot
|
||||
botName: string
|
||||
ready: boolean
|
||||
|
||||
components: Map<string, ComponentLifecycle>
|
||||
|
||||
@@ -21,6 +23,12 @@ export interface BotContext {
|
||||
}
|
||||
|
||||
status: Map<string, string>
|
||||
// status: {
|
||||
// position: Position
|
||||
// health: number
|
||||
// weather: string
|
||||
// timeOfDay: string
|
||||
// }
|
||||
|
||||
health: {
|
||||
value: number
|
||||
@@ -43,6 +51,7 @@ export interface ComponentLifecycle {
|
||||
export function createBot(options: BotOptions): Bot {
|
||||
logger.withFields({ options }).log('Creating bot')
|
||||
ctx = {
|
||||
ready: false,
|
||||
bot: mineflayer.createBot({
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
@@ -112,6 +121,11 @@ export function createBot(options: BotOptions): Bot {
|
||||
ctx.health.value = ctx.bot.health
|
||||
})
|
||||
|
||||
ctx.bot.once('spawn', () => {
|
||||
ctx!.ready = true
|
||||
logger.log('Bot ready')
|
||||
})
|
||||
|
||||
ctx.bot.on('death', () => {
|
||||
logger.error('Bot died')
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@ export function getNearestFreeSpace(ctx: WorldContext, size: number = 1, distanc
|
||||
export function getNearestBlocks(ctx: WorldContext, blockTypes: string[] | string | null = null, distance: number = 16, count: number = 10000): Block[] {
|
||||
const blockIds = blockTypes === null
|
||||
? mc.getAllBlockIds(['air'])
|
||||
: (Array.isArray(blockTypes) ? blockTypes : [blockTypes]).map(mc.getBlockId)
|
||||
: (Array.isArray(blockTypes) ? blockTypes : [blockTypes]).map(mc.getBlockId).filter((id): id is number => id !== null)
|
||||
|
||||
const positions = ctx.bot.findBlocks({ matching: blockIds, maxDistance: distance, count })
|
||||
|
||||
@@ -97,9 +97,8 @@ export function getInventoryCounts(ctx: WorldContext): Record<string, number> {
|
||||
export function getCraftableItems(ctx: WorldContext): string[] {
|
||||
const table = getNearestBlock(ctx, 'crafting_table')
|
||||
|| getInventoryStacks(ctx).find(item => item.name === 'crafting_table')
|
||||
|
||||
return mc.getAllItems()
|
||||
.filter(item => ctx.bot.recipesFor(item.id, null, 1, table).length > 0)
|
||||
.filter(item => ctx.bot.recipesFor(item.id, null, 1, table as Block | null).length > 0)
|
||||
.map(item => item.name)
|
||||
}
|
||||
|
||||
@@ -136,7 +135,7 @@ export function getNearbyBlockTypes(ctx: WorldContext, distance: number = 16): s
|
||||
export async function isClearPath(ctx: WorldContext, target: Entity): Promise<boolean> {
|
||||
const movements = new pf.Movements(ctx.bot)
|
||||
movements.canDig = false
|
||||
movements.canPlaceOn = false
|
||||
// movements.canPlaceOn = false // TODO: fix this
|
||||
|
||||
const goal = new pf.goals.GoalNear(
|
||||
target.position.x,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import type { BotContext } from '../composables/bot'
|
||||
import { getStatusToString } from '../components/status'
|
||||
|
||||
export function basicSystemPrompt(botName: string): string {
|
||||
export function genSystemBasicPrompt(botName: string): string {
|
||||
return `You are a playful Minecraft bot named ${botName} that can converse with players, see, move,
|
||||
mine, build, and interact with the world by using commands.`
|
||||
}
|
||||
|
||||
export function genSystemPrompt(ctx: BotContext): string {
|
||||
return `${basicSystemPrompt(ctx.botName)}
|
||||
|
||||
${ctx.prompt.selfPrompt}
|
||||
export function genActionAgentPrompt(ctx: BotContext): string {
|
||||
// ${ctx.prompt.selfPrompt}
|
||||
return `${genSystemBasicPrompt(ctx.botName)}
|
||||
|
||||
Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in
|
||||
your responses, don't apologize constantly, don't give instructions or make lists unless
|
||||
@@ -24,19 +23,21 @@ Respond only as ${ctx.botName}, never output '(FROM OTHER BOT)'or pretend to be
|
||||
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 :)
|
||||
|
||||
Summarized memory: '${ctx.memory.getSummary()}'
|
||||
I will give you the following information:
|
||||
${getStatusToString(ctx)}
|
||||
`
|
||||
|
||||
/**
|
||||
* Summarized memory: '${ctx.memory.getSummary()}'
|
||||
$STATS
|
||||
$INVENTORY
|
||||
$COMMAND_DOCS
|
||||
$EXAMPLES
|
||||
|
||||
Conversation Begin:
|
||||
`
|
||||
*/
|
||||
}
|
||||
|
||||
export function genQueryAgentPrompt(ctx: BotContext): string {
|
||||
const prompt = `
|
||||
You are a helpful assistant that asks questions to help me decide the next immediate
|
||||
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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user