Implement streaming
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
||||
getSearch,
|
||||
listChats,
|
||||
listSearches,
|
||||
runCompletion,
|
||||
runCompletionStream,
|
||||
runSearchStream,
|
||||
type ModelCatalogResponse,
|
||||
type Provider,
|
||||
@@ -268,6 +268,7 @@ export default function App() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const transcriptEndRef = useRef<HTMLDivElement>(null);
|
||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
||||
const selectedItemRef = useRef<SidebarSelection | null>(null);
|
||||
const searchRunAbortRef = useRef<AbortController | null>(null);
|
||||
const searchRunCounterRef = useRef(0);
|
||||
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
||||
@@ -404,6 +405,10 @@ export default function App() {
|
||||
|
||||
const selectedKey = selectedItem ? `${selectedItem.kind}:${selectedItem.id}` : null;
|
||||
|
||||
useEffect(() => {
|
||||
selectedItemRef.current = selectedItem;
|
||||
}, [selectedItem]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
setSelectedChat(null);
|
||||
@@ -438,6 +443,13 @@ export default function App() {
|
||||
const messages = selectedChat?.messages ?? [];
|
||||
const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
|
||||
const isSearchRunning = isSending && isSearchMode;
|
||||
const isSendingActiveChat =
|
||||
isSending &&
|
||||
!isSearchMode &&
|
||||
!!pendingChatState &&
|
||||
!!pendingChatState.chatId &&
|
||||
selectedItem?.kind === "chat" &&
|
||||
selectedItem.id === pendingChatState.chatId;
|
||||
const displayMessages = useMemo(() => {
|
||||
if (!pendingChatState) return messages;
|
||||
if (pendingChatState.chatId) {
|
||||
@@ -606,14 +618,62 @@ export default function App() {
|
||||
throw new Error("No model available for selected provider");
|
||||
}
|
||||
|
||||
await runCompletion({
|
||||
chatId,
|
||||
provider,
|
||||
model: selectedModel,
|
||||
messages: requestMessages,
|
||||
});
|
||||
let streamErrorMessage: string | null = null;
|
||||
|
||||
await Promise.all([refreshCollections({ kind: "chat", id: chatId }), refreshChat(chatId)]);
|
||||
await runCompletionStream(
|
||||
{
|
||||
chatId,
|
||||
provider,
|
||||
model: selectedModel,
|
||||
messages: requestMessages,
|
||||
},
|
||||
{
|
||||
onMeta: (payload) => {
|
||||
if (payload.chatId !== chatId) return;
|
||||
setPendingChatState((current) => (current ? { ...current, chatId: payload.chatId } : current));
|
||||
},
|
||||
onDelta: (payload) => {
|
||||
if (!payload.text) return;
|
||||
setPendingChatState((current) => {
|
||||
if (!current) return current;
|
||||
let updated = false;
|
||||
const nextMessages = current.messages.map((message, index, all) => {
|
||||
const isTarget = index === all.length - 1 && message.id.startsWith("temp-assistant-");
|
||||
if (!isTarget) return message;
|
||||
updated = true;
|
||||
return { ...message, content: message.content + payload.text };
|
||||
});
|
||||
return updated ? { ...current, messages: nextMessages } : current;
|
||||
});
|
||||
},
|
||||
onDone: (payload) => {
|
||||
setPendingChatState((current) => {
|
||||
if (!current) return current;
|
||||
let updated = false;
|
||||
const nextMessages = current.messages.map((message, index, all) => {
|
||||
const isTarget = index === all.length - 1 && message.id.startsWith("temp-assistant-");
|
||||
if (!isTarget) return message;
|
||||
updated = true;
|
||||
return { ...message, content: payload.text };
|
||||
});
|
||||
return updated ? { ...current, messages: nextMessages } : current;
|
||||
});
|
||||
},
|
||||
onError: (payload) => {
|
||||
streamErrorMessage = payload.message;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (streamErrorMessage) {
|
||||
throw new Error(streamErrorMessage);
|
||||
}
|
||||
|
||||
await refreshCollections();
|
||||
const currentSelection = selectedItemRef.current;
|
||||
if (currentSelection?.kind === "chat" && currentSelection.id === chatId) {
|
||||
await refreshChat(chatId);
|
||||
}
|
||||
setPendingChatState(null);
|
||||
};
|
||||
|
||||
@@ -914,7 +974,7 @@ export default function App() {
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-3 py-6 md:px-10">
|
||||
{!isSearchMode ? (
|
||||
<ChatMessagesPanel messages={displayMessages} isLoading={isLoadingSelection} isSending={isSending} />
|
||||
<ChatMessagesPanel messages={displayMessages} isLoading={isLoadingSelection} isSending={isSendingActiveChat} />
|
||||
) : (
|
||||
<SearchResultsPanel search={selectedSearch} isLoading={isLoadingSelection} isRunning={isSearchRunning} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user