style: lint

This commit is contained in:
Neko Ayaka
2026-08-26 19:49:58 +08:00
parent e60a04a4ec
commit 98f40d7d0b
1625 changed files with 75216 additions and 75203 deletions
@@ -18,9 +18,9 @@ export async function dispatchAction(
if (!parseResult.success) {
return {
success: false,
shouldContinue: true,
result: `System Error: Invalid action payload: ${parseResult.issues.map(i => i.message).join(', ')}`,
shouldContinue: true,
success: false,
}
}
@@ -29,9 +29,9 @@ export async function dispatchAction(
if (!handler) {
return {
success: false,
shouldContinue: true,
result: `System Error: Action "${validatedAction.action}" is not implemented.`,
shouldContinue: true,
success: false,
}
}
@@ -50,9 +50,9 @@ export async function dispatchAction(
catch (error) {
log.withError(error as Error).error('Action execution failed')
return {
success: false,
shouldContinue: true,
result: `System Error: Execution failed: ${(error as Error).message}`,
shouldContinue: true,
success: false,
}
}
}
+18 -18
View File
@@ -7,24 +7,6 @@ import type { BotContext } from '../types'
import { pushToEventQueue } from '../../lib/db'
import { onMessageArrival } from './scheduler'
/**
* Set up the ready event handler
* Logs connection information when Satori client is ready
*/
export function setupReadyEventHandler(
satoriClient: SatoriClient,
logger: Logg,
): void {
satoriClient.onReady((ready: SatoriReadyBody) => {
logger.log('Satori client ready:', ready)
logger.log(`Connected to ${ready.logins.length} platform(s)`)
for (const login of ready.logins) {
logger.log(`- ${login.platform} (${login.self_id}): ${login.status}`)
}
})
}
/**
* Set up the message-created event handler
* Processes incoming messages, filters bot's own messages, and triggers bot responses
@@ -68,3 +50,21 @@ export function setupMessageEventHandler(
await onMessageArrival(botContext, satoriClient)
})
}
/**
* Set up the ready event handler
* Logs connection information when Satori client is ready
*/
export function setupReadyEventHandler(
satoriClient: SatoriClient,
logger: Logg,
): void {
satoriClient.onReady((ready: SatoriReadyBody) => {
logger.log('Satori client ready:', ready)
logger.log(`Connected to ${ready.logins.length} platform(s)`)
for (const login of ready.logins) {
logger.log(`- ${login.platform} (${login.self_id}): ${login.status}`)
}
})
}
@@ -70,8 +70,8 @@ export async function handleLoopStep(
// Dynamic history injection: Fetch last 10 messages from DB
const dbMessages = await getRecentMessages(chatCtx.channelId, 10)
const llmMessages: LLMMessage[] = dbMessages.map(m => ({
role: m.userId === chatCtx.selfId ? 'assistant' : 'user',
content: m.content,
role: m.userId === chatCtx.selfId ? 'assistant' : 'user',
}))
const actionPayload = await imagineAnAction(
@@ -79,8 +79,8 @@ export async function handleLoopStep(
llmMessages,
chatCtx?.actions || [],
{
unreadEvents: ctx.unreadEvents,
incomingEvents: currentIncoming ? [currentIncoming] : [],
unreadEvents: ctx.unreadEvents,
},
)
@@ -129,6 +129,14 @@ export async function loopIterationForChannel(
await handleLoopStep(bot, satoriClient, chatContext, incomingEvent)
}
/**
* Start the periodic loop
* Begins the recursive periodic processing of channels with unread messages
*/
export function startPeriodicLoop(botCtx: BotContext, satoriClient: SatoriClient) {
loopPeriodic(botCtx, satoriClient)
}
/**
* Process periodic loop iteration for existing channels with unread messages
* Only processes channels that have unread messages to avoid unnecessary LLM calls
@@ -200,14 +208,6 @@ function loopPeriodic(botCtx: BotContext, satoriClient: SatoriClient) {
}, PERIODIC_LOOP_INTERVAL_MS)
}
/**
* Start the periodic loop
* Begins the recursive periodic processing of channels with unread messages
*/
export function startPeriodicLoop(botCtx: BotContext, satoriClient: SatoriClient) {
loopPeriodic(botCtx, satoriClient)
}
let isQueueConsumerRunning = false
/**
@@ -267,9 +267,9 @@ export async function onMessageArrival(
botContext.logger
.withFields({
channelId: chatCtx.channelId,
sourceUserId: currMsg.event.user?.id || currMsg.event.member?.user?.id,
selfId: chatCtx.selfId,
messageId: currMsg.event.id,
selfId: chatCtx.selfId,
sourceUserId: currMsg.event.user?.id || currMsg.event.member?.user?.id,
})
.debug('[DEBUG] Skipping bot\'s own event in unreadEvents - filtered out')
botContext.eventQueue.shift()
@@ -294,7 +294,7 @@ export async function onMessageArrival(
}
const unreadEventId = await pushToUnreadEvents(chatCtx.channelId, currMsg.event)
unreadEventsForThisChannel.push({ id: unreadEventId, event: currMsg.event })
unreadEventsForThisChannel.push({ event: currMsg.event, id: unreadEventId })
if (unreadEventsForThisChannel.length > MAX_UNREAD_EVENTS) {
unreadEventsForThisChannel = unreadEventsForThisChannel.slice(-MAX_UNREAD_EVENTS)
@@ -20,8 +20,8 @@ export async function imagineAnAction(
messages: LLMMessage[],
actions: { action: Action, result: unknown }[],
globalStates: {
unreadEvents: Record<string, StoredUnreadEvent[]>
incomingEvents?: SatoriEvent[]
unreadEvents: Record<string, StoredUnreadEvent[]>
},
): Promise<Action | undefined> {
const logger = useLogg('imagineAnAction').useGlobalConfig()
@@ -57,11 +57,11 @@ export async function imagineAnAction(
try {
const req = {
abortSignal: currentAbortController?.signal,
apiKey: config.llm.apiKey,
baseURL: config.llm.baseUrl,
model: config.llm.model,
messages: requestMessages,
abortSignal: currentAbortController?.signal,
model: config.llm.model,
} satisfies GenerateTextOptions
if (config.llm.ollamaDisableThink) {
@@ -76,12 +76,12 @@ export async function imagineAnAction(
}
logger.withFields({
response: res.text,
unreadEvents: Object.fromEntries(Object.entries(globalStates.unreadEvents).map(([key, value]) => [key, value.length])),
now: new Date().toLocaleString(),
totalTokens: res.usage.totalTokens,
promptTokens: res.usage.inputTokens,
completion_tokens: res.usage.outputTokens,
now: new Date().toLocaleString(),
promptTokens: res.usage.inputTokens,
response: res.text,
totalTokens: res.usage.totalTokens,
unreadEvents: Object.fromEntries(Object.entries(globalStates.unreadEvents).map(([key, value]) => [key, value.length])),
}).log('Generated action')
responseText = res.text
@@ -15,12 +15,12 @@ export async function createBotContext(logger: Logg): Promise<BotContext> {
])
const botSelf: BotContext = {
eventQueue,
unreadEvents,
processedIds: new Set(),
logger,
lastInteractedChannelIds: [],
chats: new Map<string, ChatContext>(),
eventQueue,
lastInteractedChannelIds: [],
logger,
processedIds: new Set(),
unreadEvents,
}
return botSelf
@@ -48,13 +48,13 @@ export async function ensureChatContext(botCtx: BotContext, channelId: string):
const channelInfo = channels.find(c => c.id === channelId)
const newChatContext: ChatContext = {
actions: [],
channelId,
currentAbortController: undefined,
currentTask: undefined,
isProcessing: false,
platform: channelInfo?.platform || '',
selfId: channelInfo?.selfId || '',
isProcessing: false,
currentTask: undefined,
currentAbortController: undefined,
actions: [],
}
log
+36 -36
View File
@@ -24,8 +24,8 @@ export const ListChannelsActionSchema = v.object({
export const SendMessageActionSchema = v.object({
action: v.literal('send_message'),
content: v.string(),
channelId: v.string(),
content: v.string(),
})
export const ReadUnreadMessagesActionSchema = v.object({
@@ -44,9 +44,42 @@ export const ActionSchema = v.union([
export type Action = v.InferOutput<typeof ActionSchema>
export interface BotContext {
chats: Map<string, ChatContext>
currentProcessingStartTime?: number
eventQueue: PendingEvent[]
lastInteractedChannelIds: string[]
logger: Logg
processedIds: Set<string>
unreadEvents: Record<string, StoredUnreadEvent[]> // channelId -> events
}
export interface CancellablePromise<T> {
promise: Promise<T>
cancel: () => void
promise: Promise<T>
}
export interface ChatContext {
actions: { action: Action, result: unknown }[]
channelId: string
currentAbortController?: AbortController
currentTask?: CancellablePromise<void>
isProcessing: boolean
platform: string
selfId: string
}
export interface PendingEvent {
event: SatoriEvent
id: string
status: 'pending' | 'ready'
}
export interface StoredUnreadEvent {
event: SatoriEvent
id: string
}
export function cancellable<T>(promise: Promise<T>): CancellablePromise<T> {
@@ -58,40 +91,7 @@ export function cancellable<T>(promise: Promise<T>): CancellablePromise<T> {
})
return {
promise: wrappedPromise,
cancel: () => cancel?.(),
promise: wrappedPromise,
}
}
export interface PendingEvent {
id: string
event: SatoriEvent
status: 'pending' | 'ready'
}
export interface StoredUnreadEvent {
id: string
event: SatoriEvent
}
export interface BotContext {
logger: Logg
eventQueue: PendingEvent[]
unreadEvents: Record<string, StoredUnreadEvent[]> // channelId -> events
processedIds: Set<string>
lastInteractedChannelIds: string[]
currentProcessingStartTime?: number
chats: Map<string, ChatContext>
}
export interface ChatContext {
channelId: string
platform: string
selfId: string
isProcessing: boolean
currentTask?: CancellablePromise<void>
currentAbortController?: AbortController
actions: { action: Action, result: unknown }[]
}
+44 -44
View File
@@ -1,33 +1,35 @@
import type { SatoriEvent, SatoriMessage } from '../adapter/satori/types'
import type { Action, BotContext, ChatContext } from './types'
/**
* Intelligently truncate action history while preserving logical chains.
* If the first action in the kept list is a 'continue', it backtracks to include
* the action that triggered it, ensuring the LLM has full context of its sequence.
* Format debug context for logging
* Creates a summary of bot state for debugging
*/
export function trimActions(
actions: { action: Action, result: unknown }[],
max: number,
keep: number,
): { action: Action, result: unknown }[] {
if (actions.length <= max) {
return actions
export function formatDebugContext(
ctx: BotContext,
chatCtx?: ChatContext,
): Record<string, unknown> {
const unreadEventsSummary = Object.fromEntries(
Object.entries(ctx.unreadEvents).map(([key, value]) => [key, value.length]),
)
const context: Record<string, unknown> = {
messageQueueLength: ctx.eventQueue.length,
totalUnreadCount: Object.values(ctx.unreadEvents).reduce((acc, cur) => acc + cur.length, 0),
unreadEvents: unreadEventsSummary,
}
let startIndex = actions.length - keep
if (chatCtx) {
context.channelId = chatCtx.channelId
context.totalActionsInContext = chatCtx.actions.length
// Backtrack to avoid starting with a 'continue' action which lacks its previous context
while (startIndex > 0) {
const currentAction = actions[startIndex].action
if (currentAction.action === 'continue') {
startIndex--
}
else {
break
}
const lastActions = chatCtx.actions.slice(-3).map(action => ({
action: action.action.action,
result: typeof action.result === 'string' ? action.result.substring(0, 100) : String(action.result).substring(0, 100),
}))
context.lastActions = lastActions
}
return actions.slice(startIndex)
return context
}
/**
@@ -67,33 +69,31 @@ export function isBotOwnMessage(
}
/**
* Format debug context for logging
* Creates a summary of bot state for debugging
* Intelligently truncate action history while preserving logical chains.
* If the first action in the kept list is a 'continue', it backtracks to include
* the action that triggered it, ensuring the LLM has full context of its sequence.
*/
export function formatDebugContext(
ctx: BotContext,
chatCtx?: ChatContext,
): Record<string, unknown> {
const unreadEventsSummary = Object.fromEntries(
Object.entries(ctx.unreadEvents).map(([key, value]) => [key, value.length]),
)
const context: Record<string, unknown> = {
messageQueueLength: ctx.eventQueue.length,
unreadEvents: unreadEventsSummary,
totalUnreadCount: Object.values(ctx.unreadEvents).reduce((acc, cur) => acc + cur.length, 0),
export function trimActions(
actions: { action: Action, result: unknown }[],
max: number,
keep: number,
): { action: Action, result: unknown }[] {
if (actions.length <= max) {
return actions
}
if (chatCtx) {
context.channelId = chatCtx.channelId
context.totalActionsInContext = chatCtx.actions.length
let startIndex = actions.length - keep
const lastActions = chatCtx.actions.slice(-3).map(action => ({
action: action.action.action,
result: typeof action.result === 'string' ? action.result.substring(0, 100) : String(action.result).substring(0, 100),
}))
context.lastActions = lastActions
// Backtrack to avoid starting with a 'continue' action which lacks its previous context
while (startIndex > 0) {
const currentAction = actions[startIndex].action
if (currentAction.action === 'continue') {
startIndex--
}
else {
break
}
}
return context
return actions.slice(startIndex)
}