fix: useSkillContext

This commit is contained in:
RainbowBird
2025-01-08 22:35:14 +08:00
parent a811ce41ee
commit dfd888b4b4
8 changed files with 89 additions and 46 deletions
@@ -1,9 +1,9 @@
import { messages, system, user } from 'neuri/openai' import { messages, system, user } from 'neuri/openai'
import { sleep } from 'src/utils/helper'
import { beforeAll, describe, expect, it } from 'vitest' import { beforeAll, describe, expect, it } from 'vitest'
import { createBot, useBot } from '../composables/bot' import { createBot, useBot } from '../composables/bot'
import { botConfig, initEnv } from '../composables/config' import { botConfig, initEnv } from '../composables/config'
import { genActionAgentPrompt, genQueryAgentPrompt } from '../prompts/agent' import { genActionAgentPrompt, genQueryAgentPrompt } from '../prompts/agent'
import { sleep } from '../utils/helper'
import { initLogger } from '../utils/logger' import { initLogger } from '../utils/logger'
import { initAgent } from './openai' import { initAgent } from './openai'
+17 -18
View File
@@ -6,7 +6,7 @@ import * as skills from '../skills'
type ActionResult = string | Promise<string> type ActionResult = string | Promise<string>
interface Action { export interface Action {
readonly name: string readonly name: string
readonly description: string readonly description: string
readonly schema: z.ZodObject<any> readonly schema: z.ZodObject<any>
@@ -105,23 +105,22 @@ export const actionsList: Action[] = [
// } // }
// }, // },
// getStopAction(): Action { {
// return { name: 'stop',
// name: 'stop', description: 'Force stop all actions and commands that are currently executing.',
// description: 'Force stop all actions and commands that are currently executing.', schema: z.object({}),
// schema: z.object({}), perform: (ctx: SkillContext) => async () => {
// perform: (ctx: BotContext) => async () => { // await ctx.actions.stop()
// await ctx.actions.stop() // ctx.clearBotLogs()
// ctx.clearBotLogs() // ctx.actions.cancelResume()
// ctx.actions.cancelResume() // ctx.bot.emit('idle')
// ctx.bot.emit('idle') ctx.shouldInterrupt = true
// let msg = 'Agent stopped.' const msg = 'Agent stopped.'
// if (ctx.self_prompter.on) // if (ctx.self_prompter.on)
// msg += ' Self-prompting still active.' // msg += ' Self-prompting still active.'
// return msg return msg
// }, },
// } },
// },
// getStfuAction(): Action { // getStfuAction(): Action {
// return { // return {
+2 -2
View File
@@ -3,7 +3,7 @@ import type { BotContext } from '../composables/bot'
import { useLogg } from '@guiiai/logg' import { useLogg } from '@guiiai/logg'
import { agent, neuri } from 'neuri' import { agent, neuri } from 'neuri'
import { openaiConfig } from '../composables/config' import { openaiConfig } from '../composables/config'
import { createSkillContext } from '../skills' import { useSkillContext } from '../skills'
import { actionsList } from './actions' import { actionsList } from './actions'
let neuriAgent: Neuri | undefined let neuriAgent: Neuri | undefined
@@ -47,7 +47,7 @@ export async function initActionAgent(ctx: BotContext): Promise<Agent> {
async ({ parameters }) => { async ({ parameters }) => {
logger.withFields({ name: action.name, parameters }).log('Calling action') logger.withFields({ name: action.name, parameters }).log('Calling action')
ctx.memory.actions.push(action) ctx.memory.actions.push(action)
return action.perform(createSkillContext(ctx))(...Object.values(parameters)) return action.perform(useSkillContext(ctx))(...Object.values(parameters))
}, },
{ description: action.description }, { description: action.description },
) )
@@ -11,6 +11,7 @@ export function createAiChatComponent(ctx: BotContext): ComponentLifecycle {
ctx.memory.chatHistory.push(system(genActionAgentPrompt(ctx))) ctx.memory.chatHistory.push(system(genActionAgentPrompt(ctx)))
// todo: get system message
const onChat = formBotChat(ctx, async (username, message) => { const onChat = formBotChat(ctx, async (username, message) => {
logger.withFields({ username, message }).log('Chat message received') logger.withFields({ username, message }).log('Chat message received')
+3 -1
View File
@@ -1,5 +1,5 @@
import type { Message } from 'neuri/openai' import type { Message } from 'neuri/openai'
import type { Action } from 'src/agents/actions' import type { Action } from '../agents/actions'
import type { BotInternalEventHandlers, BotInternalEvents } from './events' import type { BotInternalEventHandlers, BotInternalEvents } from './events'
import { useLogg } from '@guiiai/logg' import { useLogg } from '@guiiai/logg'
import mineflayer, { type Bot, type BotOptions } from 'mineflayer' import mineflayer, { type Bot, type BotOptions } from 'mineflayer'
@@ -51,6 +51,7 @@ export interface ComponentLifecycle {
cleanup: () => void cleanup: () => void
} }
// todo: reconnect
export function createBot(options: BotOptions): Bot { export function createBot(options: BotOptions): Bot {
logger.withFields({ options }).log('Creating bot') logger.withFields({ options }).log('Creating bot')
ctx = { ctx = {
@@ -68,6 +69,7 @@ export function createBot(options: BotOptions): Bot {
}, },
memory: { memory: {
chatHistory: [], chatHistory: [],
actions: [],
}, },
status: new Map(), status: new Map(),
health: { health: {
+1 -1
View File
@@ -16,7 +16,7 @@ import { createTicker } from './utils/ticker'
const logger = useLogg('main').useGlobalConfig() const logger = useLogg('main').useGlobalConfig()
async function main() { async function main() {
initLogger() initLogger() // todo: save logs to file
initEnv() initEnv()
createBot(botConfig) createBot(botConfig)
+15 -1
View File
@@ -1,5 +1,19 @@
import type { Bot } from 'mineflayer' import type { Bot } from 'mineflayer'
import type { BotContext } from 'src/composables/bot' import type { BotContext } from '../composables/bot'
import { useLogg } from '@guiiai/logg'
let ctx: SkillContext | undefined
const logger = useLogg('skills').useGlobalConfig()
export function useSkillContext(botCtx: BotContext): SkillContext {
if (!ctx) {
logger.log('Creating skill context')
ctx = createSkillContext(botCtx)
}
return ctx
}
/** /**
* Context for skill execution * Context for skill execution
*/ */
+49 -22
View File
@@ -110,41 +110,69 @@ export async function followPlayer(
username: string, username: string,
distance = 4, distance = 4,
): Promise<boolean> { ): Promise<boolean> {
// const player = ctx.bot.players[username]?.entity
// if (!player) {
// log(ctx, `Could not find player ${username}`)
// return false
// }
// const movements = new Movements(ctx.bot)
// ctx.bot.pathfinder.setMovements(movements)
// ctx.bot.pathfinder.setGoal(new goals.GoalNear(player.position.x, player.position.y, player.position.z, distance))
// log(ctx, `Started following ${username}`)
// const followInterval = setInterval(() => {
// const target = ctx.bot.players[username]?.entity
// if (!target) {
// log(ctx, 'Lost sight of player')
// clearInterval(followInterval)
// return
// }
// const { x, y, z } = target.position
// ctx.bot.pathfinder.setGoal(new goals.GoalNear(x, y, z, distance))
// }, 1000)
// while (!ctx.shouldInterrupt) {
// await new Promise(resolve => setTimeout(resolve, 500))
// if (ctx.allowCheats && ctx.bot.entity.position.distanceTo(player.position) > 100) {
// await goToPlayer(ctx, username)
// }
// }
// // TODO: need global status management
// clearInterval(followInterval)
// ctx.bot.pathfinder.stop()
// return true
const player = ctx.bot.players[username]?.entity const player = ctx.bot.players[username]?.entity
if (!player) { if (!player) {
log(ctx, `Could not find player ${username}`)
return false return false
} }
const movements = new Movements(ctx.bot) const movements = new Movements(ctx.bot)
ctx.bot.pathfinder.setMovements(movements) ctx.bot.pathfinder.setMovements(movements)
ctx.bot.pathfinder.setGoal(new goals.GoalNear(player.position.x, player.position.y, player.position.z, distance)) ctx.bot.pathfinder.setGoal(new goals.GoalFollow(player, distance), true)
log(ctx, `You are now actively following player ${username}.`)
log(ctx, `Started following ${username}`)
const followInterval = setInterval(() => {
const target = ctx.bot.players[username]?.entity
if (!target) {
log(ctx, 'Lost sight of player')
clearInterval(followInterval)
return
}
const { x, y, z } = target.position
ctx.bot.pathfinder.setGoal(new goals.GoalNear(x, y, z, distance))
}, 1000)
while (!ctx.shouldInterrupt) { while (!ctx.shouldInterrupt) {
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, 500))
if (ctx.allowCheats && ctx.bot.entity.position.distanceTo(player.position) > 100) { if (ctx.allowCheats && ctx.bot.entity.position.distanceTo(player.position) > 100 && player.onGround) {
await goToPlayer(ctx, username) await goToPlayer(ctx, username)
} }
}
// TODO: need global status management // if (ctx.bot.modes?.isOn('unstuck')) {
// clearInterval(followInterval) // const isNearby = ctx.bot.entity.position.distanceTo(player.position) <= distance + 1
// ctx.bot.pathfinder.stop() // if (isNearby) {
// ctx.bot.modes.pause('unstuck')
// } else {
// ctx.bot.modes.unpause('unstuck')
// }
// }
}
return true return true
} }
@@ -184,7 +212,6 @@ export async function moveAwayFromEntity(
return true return true
} }
export async function stay(ctx: SkillContext, seconds = 30): Promise<boolean> { export async function stay(ctx: SkillContext, seconds = 30): Promise<boolean> {
const start = Date.now() const start = Date.now()
const targetTime = seconds === -1 ? Infinity : start + seconds * 1000 const targetTime = seconds === -1 ? Infinity : start + seconds * 1000