feat(server-sdk,stage-ui,server-shared): more server channel hooks
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import type { WebSocketBaseEvent, WebSocketEvent, WebSocketEventOptionalSource, WebSocketEvents, WebSocketEventSource } from '@proj-airi/server-shared/types'
|
||||
import type {
|
||||
WebSocketBaseEvent,
|
||||
WebSocketEvent,
|
||||
WebSocketEventOptionalSource,
|
||||
WebSocketEvents,
|
||||
WebSocketEventSource,
|
||||
} from '@proj-airi/server-shared/types'
|
||||
|
||||
import WebSocket from 'crossws/websocket'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AssistantMessage, ToolMessage } from '@xsai/shared-chat'
|
||||
import type { AssistantMessage, Message, ToolMessage, UserMessage } from '@xsai/shared-chat'
|
||||
|
||||
export interface DiscordGuildMember {
|
||||
nickname: string
|
||||
@@ -19,13 +19,13 @@ export enum WebSocketEventSource {
|
||||
}
|
||||
|
||||
interface InputSource {
|
||||
'stage-web': string
|
||||
'stage-tamagotchi': string
|
||||
'stage-web': boolean
|
||||
'stage-tamagotchi': boolean
|
||||
'discord': Discord
|
||||
}
|
||||
|
||||
interface OutputSource {
|
||||
'gen-ai-model-chat': string
|
||||
'gen-ai:chat': string
|
||||
}
|
||||
|
||||
export enum ContextUpdateStrategy {
|
||||
@@ -80,6 +80,7 @@ export interface WebSocketEvents<C = undefined> {
|
||||
'error': {
|
||||
message: string
|
||||
}
|
||||
|
||||
'module:authenticate': {
|
||||
token: string
|
||||
}
|
||||
@@ -93,11 +94,13 @@ export interface WebSocketEvents<C = undefined> {
|
||||
'module:configure': {
|
||||
config: C
|
||||
}
|
||||
|
||||
'ui:configure': {
|
||||
moduleName: string
|
||||
moduleIndex?: number
|
||||
config: C | Record<string, unknown>
|
||||
}
|
||||
|
||||
'input:text': {
|
||||
text: string
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>>
|
||||
@@ -107,9 +110,21 @@ export interface WebSocketEvents<C = undefined> {
|
||||
'input:voice': {
|
||||
audio: ArrayBuffer
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>>
|
||||
|
||||
'output:gen-ai:chat:tool-call': {
|
||||
toolCalls: ToolMessage[]
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>> & Partial<WithOutputSource<'gen-ai:chat'>>
|
||||
'output:gen-ai:chat:message': {
|
||||
messages: Array<AssistantMessage | ToolMessage>
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>> & Partial<WithOutputSource<'gen-ai-model-chat'>>
|
||||
message: AssistantMessage
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>> & Partial<WithOutputSource<'gen-ai:chat'>>
|
||||
'output:gen-ai:chat:complete': {
|
||||
input: UserMessage
|
||||
contexts: Record<string, ContextUpdate[]>
|
||||
composedMessage: Array<Message>
|
||||
message: AssistantMessage
|
||||
toolCalls: ToolMessage[]
|
||||
} & Partial<WithInputSource<'stage-web' | 'stage-tamagotchi' | 'discord'>> & Partial<WithOutputSource<'gen-ai:chat'>>
|
||||
|
||||
'context:update': ContextUpdate
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ChatProvider } from '@xsai-ext/shared-providers'
|
||||
import type { CommonContentPart, Message, SystemMessage } from '@xsai/shared-chat'
|
||||
import type { CommonContentPart, Message, SystemMessage, ToolMessage } from '@xsai/shared-chat'
|
||||
|
||||
import type { StreamEvent, StreamOptions } from '../stores/llm'
|
||||
import type { ChatAssistantMessage, ChatHistoryItem, ChatSlices, ContextMessage, StreamingAssistantMessage } from '../types/chat'
|
||||
@@ -98,8 +98,15 @@ export const useChatStore = defineStore('chat', () => {
|
||||
const onTokenSpecialHooks = ref<Array<(special: string) => Promise<void>>>([])
|
||||
const onStreamEndHooks = ref<Array<() => Promise<void>>>([])
|
||||
const onAssistantResponseEndHooks = ref<Array<(message: string) => Promise<void>>>([])
|
||||
const onAssistantMessageHooks = ref<Array<(message: StreamingAssistantMessage) => Promise<void>>>([])
|
||||
const onChatTurnCompleteHooks = ref<Array<(chat: { input: ChatHistoryItem, contexts: Record<string, ContextMessage[]>, composedMessage: Message[], output: StreamingAssistantMessage }) => Promise<void>>>([])
|
||||
const onAssistantMessageHooks = ref<Array<(message: StreamingAssistantMessage, messageText?: string) => Promise<void>>>([])
|
||||
const onChatTurnCompleteHooks = ref<Array<(chat: {
|
||||
input: ChatHistoryItem
|
||||
contexts: Record<string, ContextMessage[]>
|
||||
composedMessage: Message[]
|
||||
output: StreamingAssistantMessage
|
||||
outputText: string
|
||||
toolCalls: ToolMessage[]
|
||||
}) => Promise<void>>>([])
|
||||
|
||||
function onBeforeMessageComposed(cb: (message: string) => Promise<void>) {
|
||||
onBeforeMessageComposedHooks.value.push(cb)
|
||||
@@ -141,12 +148,19 @@ export const useChatStore = defineStore('chat', () => {
|
||||
return () => onAssistantResponseEndHooks.value = onAssistantResponseEndHooks.value.filter(hook => hook !== cb) // return remove listener callback
|
||||
}
|
||||
|
||||
function onAssistantMessage(cb: (message: StreamingAssistantMessage) => Promise<void>) {
|
||||
function onAssistantMessage(cb: (message: StreamingAssistantMessage, messageText?: string) => Promise<void>) {
|
||||
onAssistantMessageHooks.value.push(cb)
|
||||
return () => onAssistantMessageHooks.value = onAssistantMessageHooks.value.filter(hook => hook !== cb) // return remove listener callback
|
||||
}
|
||||
|
||||
function onChatTurnComplete(cb: (chat: { input: ChatHistoryItem, contexts: Record<string, ContextMessage[]>, composedMessage: Message[], output: StreamingAssistantMessage }) => Promise<void>) {
|
||||
function onChatTurnComplete(cb: (chat: {
|
||||
input: ChatHistoryItem
|
||||
contexts: Record<string, ContextMessage[]>
|
||||
composedMessage: Message[]
|
||||
output: StreamingAssistantMessage
|
||||
outputText: string
|
||||
toolCalls: ToolMessage[]
|
||||
}) => Promise<void>) {
|
||||
onChatTurnCompleteHooks.value.push(cb)
|
||||
return () => onChatTurnCompleteHooks.value = onChatTurnCompleteHooks.value.filter(hook => hook !== cb) // return remove listener callback
|
||||
}
|
||||
@@ -204,12 +218,19 @@ export const useChatStore = defineStore('chat', () => {
|
||||
await hook(message)
|
||||
}
|
||||
|
||||
async function emitAssistantMessageHooks(message: StreamingAssistantMessage) {
|
||||
async function emitAssistantMessageHooks(message: StreamingAssistantMessage, messageText?: string) {
|
||||
for (const hook of onAssistantMessageHooks.value)
|
||||
await hook(message)
|
||||
await hook(message, messageText)
|
||||
}
|
||||
|
||||
async function emitChatTurnCompleteHooks(chat: { input: ChatHistoryItem, contexts: Record<string, ContextMessage[]>, composedMessage: Message[], output: StreamingAssistantMessage }) {
|
||||
async function emitChatTurnCompleteHooks(chat: {
|
||||
input: ChatHistoryItem
|
||||
contexts: Record<string, ContextMessage[]>
|
||||
composedMessage: Message[]
|
||||
output: StreamingAssistantMessage
|
||||
outputText: string
|
||||
toolCalls: ToolMessage[]
|
||||
}) {
|
||||
for (const hook of onChatTurnCompleteHooks.value)
|
||||
await hook(chat)
|
||||
}
|
||||
@@ -528,12 +549,14 @@ export const useChatStore = defineStore('chat', () => {
|
||||
await emitAssistantResponseEndHooks(fullText)
|
||||
|
||||
await emitAfterSendHooks(sendingMessage)
|
||||
await emitAssistantMessageHooks({ ...streamingMessage.value })
|
||||
await emitAssistantMessageHooks({ ...streamingMessage.value }, fullText)
|
||||
await emitChatTurnCompleteHooks({
|
||||
input: { role: 'user', content: sendingMessage, createdAt: sendingCreatedAt },
|
||||
contexts: { ...activeContexts.value },
|
||||
composedMessage: newMessages as Message[],
|
||||
output: { ...streamingMessage.value },
|
||||
outputText: fullText,
|
||||
toolCalls: sessionMessagesForSend.filter(msg => msg.role === 'tool') as ToolMessage[],
|
||||
})
|
||||
|
||||
// Reset the streaming message for the next turn
|
||||
@@ -576,12 +599,13 @@ export const useChatStore = defineStore('chat', () => {
|
||||
|
||||
send,
|
||||
setActiveSession,
|
||||
ingestContextMessage,
|
||||
cleanupMessages,
|
||||
getAllSessions,
|
||||
replaceSessions,
|
||||
resetAllSessions,
|
||||
|
||||
ingestContextMessage,
|
||||
|
||||
clearHooks,
|
||||
|
||||
emitBeforeMessageComposedHooks,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { UserMessage } from '@xsai/shared-chat'
|
||||
|
||||
import type { ChatStreamEvent, ContextMessage } from '../../../types/chat'
|
||||
|
||||
import { isStageTamagotchi, isStageWeb } from '@proj-airi/stage-shared'
|
||||
import { useBroadcastChannel } from '@vueuse/core'
|
||||
import { Mutex } from 'es-toolkit'
|
||||
import { defineStore } from 'pinia'
|
||||
@@ -87,8 +90,32 @@ export const useContextBridgeStore = defineStore('mods:api:context-bridge', () =
|
||||
}),
|
||||
)
|
||||
|
||||
disposeHookFns.value.push(chatStore.onAssistantMessage(async (message, messageText) => {
|
||||
serverChannelStore.send({
|
||||
type: 'output:gen-ai:chat:message',
|
||||
data: {
|
||||
message,
|
||||
'stage-web': isStageWeb(),
|
||||
'stage-tamagotchi': isStageTamagotchi(),
|
||||
'gen-ai:chat': messageText || '',
|
||||
},
|
||||
})
|
||||
}))
|
||||
|
||||
disposeHookFns.value.push(chatStore.onChatTurnComplete(async (chat) => {
|
||||
serverChannelStore.send({ type: 'output:gen-ai:chat:message', data: { messages: [chat.output] } })
|
||||
serverChannelStore.send({
|
||||
type: 'output:gen-ai:chat:complete',
|
||||
data: {
|
||||
'input': chat.input as UserMessage,
|
||||
'composedMessage': chat.composedMessage,
|
||||
'contexts': chat.contexts,
|
||||
'message': chat.output,
|
||||
'toolCalls': [],
|
||||
'stage-web': isStageWeb(),
|
||||
'stage-tamagotchi': isStageTamagotchi(),
|
||||
'gen-ai:chat': chat.outputText,
|
||||
},
|
||||
})
|
||||
}))
|
||||
|
||||
const { stop: stopIncomingStreamWatch } = watch(incomingStreamEvent, async (event) => {
|
||||
|
||||
Reference in New Issue
Block a user