mirror of
https://github.com/infinilabs/coco-app.git
synced 2026-09-02 04:01:53 +02:00
* style: add dark drop shadow to images * docs: add release note * style: add dark * fix: duplicate chat content * docs: add release note * chore: update history list
142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
import { useAsyncEffect, useMount, useReactive, useUnmount } from "ahooks";
|
|
import { noop } from "lodash-es";
|
|
import { useRef, useState } from "react";
|
|
|
|
import { useAppStore } from "@/stores/appStore";
|
|
import { EventPayloads } from "@/types/platform";
|
|
import platformAdapter from "@/utils/platformAdapter";
|
|
import useMessageChunkData from "./useMessageChunkData";
|
|
import { nanoid } from "nanoid";
|
|
|
|
interface Options {
|
|
message: string;
|
|
clientId: keyof EventPayloads;
|
|
server?: any;
|
|
assistant?: any;
|
|
setVisible: (visible: boolean) => void;
|
|
}
|
|
|
|
interface State {
|
|
sessionId?: string;
|
|
isTyping?: boolean;
|
|
messageId: string;
|
|
}
|
|
|
|
export const useStreamChat = (options: Options) => {
|
|
const { message, clientId, server, assistant, setVisible } = options;
|
|
|
|
const unlistenRef = useRef<() => void>(noop);
|
|
const { addError } = useAppStore();
|
|
const state = useReactive<State>({
|
|
isTyping: true,
|
|
messageId: nanoid(),
|
|
});
|
|
const [loadingStep, setLoadingStep] = useState<Record<string, boolean>>({
|
|
query_intent: false,
|
|
tools: false,
|
|
fetch_source: false,
|
|
pick_source: false,
|
|
deep_read: false,
|
|
think: false,
|
|
response: false,
|
|
});
|
|
|
|
const {
|
|
data: chunkData,
|
|
handlers,
|
|
clearAllChunkData,
|
|
} = useMessageChunkData();
|
|
|
|
useMount(async () => {
|
|
try {
|
|
unlistenRef.current = await platformAdapter.listenEvent(
|
|
clientId,
|
|
({ payload }) => {
|
|
//console.log(clientId, JSON.parse(payload));
|
|
|
|
const chunkData = JSON.parse(payload);
|
|
|
|
if (chunkData?._id) {
|
|
state.sessionId = chunkData._id;
|
|
|
|
return;
|
|
}
|
|
|
|
if (state.sessionId !== chunkData.session_id) {
|
|
return;
|
|
}
|
|
|
|
// If the chunk data does not contain a message_chunk, we ignore it
|
|
if (chunkData.message_chunk.trim()) {
|
|
setVisible(true);
|
|
}
|
|
|
|
state.isTyping = true;
|
|
|
|
setLoadingStep(() => ({
|
|
query_intent: false,
|
|
tools: false,
|
|
fetch_source: false,
|
|
pick_source: false,
|
|
deep_read: false,
|
|
think: false,
|
|
response: false,
|
|
[chunkData.chunk_type]: true,
|
|
}));
|
|
|
|
if (chunkData.chunk_type === "query_intent") {
|
|
handlers.deal_query_intent(chunkData);
|
|
} else if (chunkData.chunk_type === "tools") {
|
|
handlers.deal_tools(chunkData);
|
|
} else if (chunkData.chunk_type === "fetch_source") {
|
|
handlers.deal_fetch_source(chunkData);
|
|
} else if (chunkData.chunk_type === "pick_source") {
|
|
handlers.deal_pick_source(chunkData);
|
|
} else if (chunkData.chunk_type === "deep_read") {
|
|
handlers.deal_deep_read(chunkData);
|
|
} else if (chunkData.chunk_type === "think") {
|
|
handlers.deal_think(chunkData);
|
|
} else if (chunkData.chunk_type === "response") {
|
|
handlers.deal_response(chunkData);
|
|
} else if (chunkData.chunk_type === "reply_end") {
|
|
console.log("AI finished output");
|
|
state.isTyping = false;
|
|
return;
|
|
}
|
|
}
|
|
);
|
|
} catch (error) {
|
|
addError(String(error));
|
|
}
|
|
});
|
|
|
|
useAsyncEffect(async () => {
|
|
if (!message || !server || !assistant) return;
|
|
|
|
await clearAllChunkData();
|
|
|
|
state.messageId = nanoid();
|
|
|
|
try {
|
|
await platformAdapter.invokeBackend("ask_ai", {
|
|
message,
|
|
clientId,
|
|
serverId: server.id,
|
|
assistantId: assistant.id,
|
|
});
|
|
} catch (error) {
|
|
addError(String(error));
|
|
}
|
|
}, [message, server, assistant]);
|
|
|
|
useUnmount(() => {
|
|
unlistenRef.current();
|
|
});
|
|
|
|
return {
|
|
...state,
|
|
chunkData,
|
|
loadingStep,
|
|
};
|
|
};
|