fix: error handling, retriable handling
This commit is contained in:
@@ -2,7 +2,7 @@ import type { Client } from '@proj-airi/server-sdk'
|
||||
import type { Neuri, NeuriContext } from 'neuri'
|
||||
import type { MineflayerPlugin } from '../libs/mineflayer/plugin'
|
||||
import { useLogg } from '@guiiai/logg'
|
||||
import { assistant, type ChatCompletion, system, user } from 'neuri/openai'
|
||||
import { assistant, system, user } from 'neuri/openai'
|
||||
|
||||
import { formBotChat } from '../libs/mineflayer/message'
|
||||
import { genActionAgentPrompt, genStatusPrompt } from '../prompts/agent'
|
||||
@@ -30,11 +30,11 @@ export function LLMAgent(options: { agent: Neuri, airiClient: Client }): Minefla
|
||||
logger.log('thinking...')
|
||||
|
||||
const handleCompletion = async (c: NeuriContext): Promise<string> => {
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' }) as ChatCompletion | { error: { message: string } } & ChatCompletion
|
||||
logger.log('rerouting...')
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
if (!completion || 'error' in completion) {
|
||||
logger.withFields({ completion }).error('Completion')
|
||||
logger.withFields({ messages: c.messages }).log('messages')
|
||||
throw new Error(completion?.error?.message ?? 'Unknown error')
|
||||
throw completion?.error || new Error('Unknown error')
|
||||
}
|
||||
|
||||
const content = await completion?.firstContent()
|
||||
@@ -49,8 +49,10 @@ export function LLMAgent(options: { agent: Neuri, airiClient: Client }): Minefla
|
||||
3, // retryLimit
|
||||
1000, // delayInterval in ms
|
||||
handleCompletion,
|
||||
{ onError: err => logger.withError(err).log('error occurred') },
|
||||
)
|
||||
|
||||
logger.log('handling...')
|
||||
return await retirableHandler(c)
|
||||
})
|
||||
|
||||
@@ -63,22 +65,20 @@ export function LLMAgent(options: { agent: Neuri, airiClient: Client }): Minefla
|
||||
options.airiClient.onEvent('input:text:voice', async (event) => {
|
||||
logger.withFields({ user: event.data.discord?.guildMember, message: event.data.transcription }).log('Chat message received')
|
||||
|
||||
// long memory
|
||||
bot.memory.chatHistory.push(user(`NekoMeowww: ${event.data.transcription}`))
|
||||
|
||||
// short memory
|
||||
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) => {
|
||||
const content = await agent.handleStateless([...bot.memory.chatHistory, system(statusPrompt)], async (c: NeuriContext) => {
|
||||
logger.log('thinking...')
|
||||
|
||||
const handleCompletion = async (c: NeuriContext): Promise<string> => {
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' }) || { error: { message: 'Unknown error' } }
|
||||
logger.log('rerouting...')
|
||||
const completion = await c.reroute('action', c.messages, { model: 'openai/gpt-4o-mini' })
|
||||
if (!completion || 'error' in completion) {
|
||||
logger.withFields({ completion }).error('Completion')
|
||||
logger.withFields({ messages: c.messages }).log('messages')
|
||||
throw new Error(completion?.error?.message ?? 'Unknown error')
|
||||
throw completion?.error || new Error('Unknown error')
|
||||
}
|
||||
|
||||
const content = await completion?.firstContent()
|
||||
@@ -93,8 +93,10 @@ export function LLMAgent(options: { agent: Neuri, airiClient: Client }): Minefla
|
||||
3, // retryLimit
|
||||
1000, // delayInterval in ms
|
||||
handleCompletion,
|
||||
{ onError: err => logger.withError(err).log('error occurred') },
|
||||
)
|
||||
|
||||
logger.log('handling...')
|
||||
return await retirableHandler(c)
|
||||
})
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import { pickupNearbyItems } from './world-interactions'
|
||||
|
||||
const logger = useLogg('Action:CollectBlock').useGlobalConfig()
|
||||
|
||||
function isMessagable(err: unknown): err is { message: string } {
|
||||
return (err instanceof Error || (typeof err === 'object' && !!err && 'message' in err && typeof err.message === 'string'))
|
||||
}
|
||||
|
||||
export async function collectBlock(
|
||||
mineflayer: Mineflayer,
|
||||
blockType: string,
|
||||
@@ -101,6 +105,10 @@ export async function collectBlock(
|
||||
}
|
||||
catch (err) {
|
||||
logger.log(`Failed to collect ${blockType}: ${err}.`)
|
||||
if (isMessagable(err) && err.message.includes('Digging aborted')) {
|
||||
break
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,28 @@ import { sleep } from './helper'
|
||||
* @param func Function to be called
|
||||
* @returns A wrapped function with the same signature as func
|
||||
*/
|
||||
export function toRetriable<A, R>(retryLimit: number, delayInterval: number, func: (...args: A[]) => Promise<R>): (...args: A[]) => Promise<R> {
|
||||
export function toRetriable<A, R>(
|
||||
retryLimit: number,
|
||||
delayInterval: number,
|
||||
func: (...args: A[]) => Promise<R>,
|
||||
hooks?: {
|
||||
onError?: (err: unknown) => void
|
||||
},
|
||||
): (...args: A[]) => Promise<R> {
|
||||
let retryCount = 0
|
||||
return async function (args: A): Promise<R> {
|
||||
try {
|
||||
return await func(args)
|
||||
}
|
||||
catch (err) {
|
||||
if (hooks?.onError) {
|
||||
hooks.onError(err)
|
||||
}
|
||||
|
||||
if (retryCount < retryLimit) {
|
||||
retryCount++
|
||||
await sleep(delayInterval)
|
||||
return await toRetriable(retryLimit, delayInterval, func)(args)
|
||||
return await toRetriable(retryLimit - retryCount, delayInterval, func)(args)
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
|
||||
Reference in New Issue
Block a user