fix: error handling, retriable handling

This commit is contained in:
Neko Ayaka
2025-01-17 02:47:51 +08:00
parent 5d70b7ae53
commit 8f74b65873
3 changed files with 37 additions and 16 deletions
+16 -14
View File
@@ -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
}
}
+13 -2
View File
@@ -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