feat: 对话流采用策略设计模式重构
parent
97d2ccc154
commit
86918a667e
|
|
@ -14,6 +14,7 @@ import {
|
|||
useState,
|
||||
} from "react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { MessageHandlerStrategy } from "@/hooks/useRealtimeClient";
|
||||
|
||||
type RoomInfo = {
|
||||
appId: string;
|
||||
|
|
@ -94,6 +95,8 @@ export const RealtimeClientProvider = ({
|
|||
|
||||
const { toast } = useToast();
|
||||
|
||||
const messageHandlerStrategy = useRef(new MessageHandlerStrategy());
|
||||
|
||||
/** 初始化客户端并设置监听 */
|
||||
const initClient = async ({
|
||||
initMessage,
|
||||
|
|
@ -244,101 +247,17 @@ export const RealtimeClientProvider = ({
|
|||
client.on(EventNames.ALL, (_eventName, event: any) => {
|
||||
// 交给状态机处理解析流程
|
||||
|
||||
// 普通消息流处理
|
||||
if (
|
||||
event.event_type !== ChatEventType.CONVERSATION_MESSAGE_DELTA &&
|
||||
event.event_type !== ChatEventType.CONVERSATION_MESSAGE_COMPLETED
|
||||
) {
|
||||
// 处理conversation.created事件
|
||||
if (
|
||||
event.event_type === "conversation.created" &&
|
||||
!opts.initMessage &&
|
||||
!opts.fileInfo
|
||||
) {
|
||||
setMessageList((prev) => [
|
||||
...prev,
|
||||
{
|
||||
content: event.data.prologue,
|
||||
role: RoleType.Assistant,
|
||||
event,
|
||||
},
|
||||
]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是assistant的completed消息或verbose类型,直接返回
|
||||
if (
|
||||
(event.data.role === "assistant" &&
|
||||
event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED &&
|
||||
event.data.type === "verbose") ||
|
||||
(event.data.type === "answer" &&
|
||||
event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED)
|
||||
event.event_type !== ChatEventType.CONVERSATION_MESSAGE_COMPLETED &&
|
||||
event.event_type !== "conversation.created"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = event.data.content;
|
||||
setMessageList((prev) => {
|
||||
// 如果是工具调用相关的消息,不添加到消息列表
|
||||
if (
|
||||
event.data.type === "function_call" ||
|
||||
event.data.type === "tool_response"
|
||||
) {
|
||||
const jsonContent = JSON.parse(event.data.content);
|
||||
const lastMessage = prev[prev.length - 1];
|
||||
|
||||
if (jsonContent.name === "doc_reader-PDF_reader") {
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
content: "",
|
||||
role: RoleType.Assistant,
|
||||
fileInfo: opts.fileInfo,
|
||||
fileParseStatus: 1,
|
||||
event,
|
||||
},
|
||||
];
|
||||
}
|
||||
else if (
|
||||
lastMessage.event.type === "function_call" &&
|
||||
event.data.type === "tool_response"
|
||||
) {
|
||||
return [
|
||||
...prev.slice(0, prev.length - 2),
|
||||
{
|
||||
content: "",
|
||||
role: RoleType.Assistant,
|
||||
fileInfo: opts.fileInfo,
|
||||
fileParseStatus: 2,
|
||||
event,
|
||||
},
|
||||
];
|
||||
}else{
|
||||
return [...prev]
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
prev.length > 0 &&
|
||||
prev[prev.length - 1].event?.event_type ===
|
||||
ChatEventType.CONVERSATION_MESSAGE_DELTA &&
|
||||
event.event_type === ChatEventType.CONVERSATION_MESSAGE_DELTA &&
|
||||
prev[prev.length - 1].event.data.answer_id === event.data.answer_id
|
||||
) {
|
||||
return [
|
||||
...prev.slice(0, -1),
|
||||
{
|
||||
content: prev[prev.length - 1].content + content,
|
||||
role: prev[prev.length - 1].role,
|
||||
event,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
// 新消息追加
|
||||
return [...prev, { content, role: event.data.role, event }];
|
||||
}
|
||||
});
|
||||
setMessageList(prev =>
|
||||
messageHandlerStrategy.current.process(event, prev, opts)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,175 @@
|
|||
class MessageHandler {
|
||||
canHandle(_eventType: string, _dataType: string) {
|
||||
throw new Error("Subclass must implement canHandle method");
|
||||
import { ChatEventType, RoleType } from "@coze/api";
|
||||
|
||||
interface MessageHandler {
|
||||
canHandle(event: any, opts?: any, prevMessageList?: any[]): boolean;
|
||||
handle(event: any, prevMessageList: any[], opts?: any): any[];
|
||||
}
|
||||
|
||||
handle(_event: string) {
|
||||
throw new Error("Subclass must implement handle method");
|
||||
abstract class BaseMessageHandler implements MessageHandler {
|
||||
abstract canHandle(event: any, opts: any, prevMessageList?: any[]): boolean;
|
||||
abstract handle(event: any, prevMessageList: any[], opts: any): any[];
|
||||
|
||||
protected createMessage(content: string, role: RoleType, event: any, opts?: any) {
|
||||
const fileParseStatus = opts && opts.fileInfo ? event.data.type === "function_call" ? 1 :
|
||||
event.data.type === "tool_response" ? 2 : undefined:undefined
|
||||
return {
|
||||
content,
|
||||
role,
|
||||
fileInfo: (opts && opts.fileInfo) ? opts.fileInfo : undefined,
|
||||
fileParseStatus: fileParseStatus,
|
||||
event
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setNextHandle(){}
|
||||
|
||||
// 普通消息处理 添加欢迎语
|
||||
class ConversationHelloHandler extends BaseMessageHandler {
|
||||
canHandle(event: any, opts: any, _prevMessageList?: any[]): boolean {
|
||||
return event.event_type === "conversation.created" &&
|
||||
!opts.initMessage &&
|
||||
!opts.fileInfo;
|
||||
}
|
||||
|
||||
class ToolResponseHandler extends MessageHandler{
|
||||
canHandle(eventType: string, dataType: string): void {
|
||||
handle(event: any, prevMessageList: any[], _opts: any): any[] {
|
||||
return [
|
||||
...prevMessageList,
|
||||
this.createMessage(event.data.prologue, RoleType.Assistant, event,)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 处理忽略的事件类型
|
||||
class IgnoredEventHandler extends BaseMessageHandler {
|
||||
canHandle(event: any, _opts: any, _prevMessageList?: any[]): boolean {
|
||||
return (
|
||||
(event.data.role === "assistant" &&
|
||||
event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED &&
|
||||
event.data.type === "verbose") ||
|
||||
(event.data.type === "answer" &&
|
||||
event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED)
|
||||
);
|
||||
}
|
||||
handle(event: string): void {
|
||||
|
||||
handle(_event: any, prevMessageList: any[], _opts: any): any[] {
|
||||
// 直接返回原消息列表,不做任何更改
|
||||
return prevMessageList;
|
||||
}
|
||||
}
|
||||
|
||||
class UserMessageEventHandle extends BaseMessageHandler{
|
||||
canHandle(event: any, _opts: any, _prevMessageList?: any[]): boolean {
|
||||
return event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED && event.data.role === RoleType.User
|
||||
}
|
||||
|
||||
handle(event: any, prevMessageList: any[], _opts: any): any[] {
|
||||
return [...prevMessageList,this.createMessage(event.data.content,event.data.role,event)]
|
||||
}
|
||||
}
|
||||
|
||||
class NormalMessageEventHandle extends BaseMessageHandler{
|
||||
canHandle(event: any, _opts: any, prevMessageList?: any[]): boolean {
|
||||
if(typeof prevMessageList === 'undefined'){
|
||||
return false
|
||||
}
|
||||
let prevMessage = prevMessageList[prevMessageList.length - 1];
|
||||
// 当前信息为增量,并且上一条信息为完成
|
||||
if(prevMessage?.event.event_type === ChatEventType.CONVERSATION_MESSAGE_COMPLETED && event.event_type === ChatEventType.CONVERSATION_MESSAGE_DELTA){
|
||||
return true
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
handle(event: any, prevMessageList: any[], _opts: any): any[] {
|
||||
return [...prevMessageList,this.createMessage(event.data.content,event.data.role,event)]
|
||||
}
|
||||
}
|
||||
|
||||
class AppendMessageEventHandle extends BaseMessageHandler{
|
||||
canHandle(event: any, _opts: any, prevMessageList?: any[]): boolean {
|
||||
if(typeof prevMessageList === 'undefined' || prevMessageList.length === 0){
|
||||
return false;
|
||||
}
|
||||
let prevMessage = prevMessageList[prevMessageList.length - 1];
|
||||
if(prevMessage.event?.event_type === ChatEventType.CONVERSATION_MESSAGE_DELTA && event.event_type === ChatEventType.CONVERSATION_MESSAGE_DELTA){
|
||||
return true
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
handle(event: any, prevMessageList: any[], _opts: any): any[] {
|
||||
let prevMessage = prevMessageList[prevMessageList.length - 1];
|
||||
return [...prevMessageList.slice(0,prevMessageList.length-1),{...prevMessage,content:prevMessage.content + event.data.content}]
|
||||
}
|
||||
}
|
||||
|
||||
class BottomMessageEventHandle extends BaseMessageHandler{
|
||||
canHandle(_event: any, _opts: any, _prevMessageList?: any[]): boolean {
|
||||
return true
|
||||
}
|
||||
handle(_event: any, _prevMessageList: any[], _opts: any): any[] {
|
||||
return _prevMessageList
|
||||
}
|
||||
}
|
||||
|
||||
class FileFunctionCallEventHandle extends BaseMessageHandler{
|
||||
canHandle(event: any, _opts: any, _prevMessageList?: any[]): boolean {
|
||||
if(event.data.type === "function_call"){
|
||||
let jsonData = JSON.parse(event.data.content)
|
||||
return jsonData.name==='doc_reader-PDF_reader' && event.event_type === "conversation.message.completed"
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
handle(event: any, prevMessageList: any[], opts: any): any[] {
|
||||
return [...prevMessageList,this.createMessage('',event.data.role,event,opts)]
|
||||
}
|
||||
}
|
||||
|
||||
class FileFunctionResponseEventHandle extends BaseMessageHandler{
|
||||
canHandle(event: any, _opts: any, prevMessageList?: any[]): boolean {
|
||||
if(typeof prevMessageList === 'undefined'){
|
||||
return false
|
||||
}
|
||||
let prevMessage = prevMessageList[prevMessageList.length - 1];
|
||||
|
||||
if( prevMessage?.event.data.type === "function_call" && event.data.type ==="tool_response"){
|
||||
return true
|
||||
}else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
handle(event: any, prevMessageList: any[], opts: any): any[] {
|
||||
return [...prevMessageList.slice(0,prevMessageList.length-1),this.createMessage('',event.data.role,event,opts)]
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageHandlerStrategy {
|
||||
private handlers: MessageHandler[];
|
||||
|
||||
constructor() {
|
||||
this.handlers = [
|
||||
new ConversationHelloHandler(),
|
||||
new IgnoredEventHandler(),
|
||||
new UserMessageEventHandle(),
|
||||
new NormalMessageEventHandle(),
|
||||
new AppendMessageEventHandle(),
|
||||
new FileFunctionCallEventHandle(),
|
||||
new FileFunctionResponseEventHandle(),
|
||||
new BottomMessageEventHandle()
|
||||
];
|
||||
}
|
||||
|
||||
process(event: any, prevMessageList: any[], opts: any): any[] {
|
||||
for (const handler of this.handlers) {
|
||||
if (handler.canHandle(event, opts, prevMessageList)) {
|
||||
return handler.handle(event, prevMessageList, opts);
|
||||
}
|
||||
}
|
||||
return prevMessageList; // 理论上不会执行到这里
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue