@llamaindex/ui
Version:
A comprehensive UI component library built with React, TypeScript, and Tailwind CSS for LlamaIndex applications
841 lines (832 loc) • 29 kB
JavaScript
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './chunk-WQSELGI3.mjs';
export { Checkbox } from './chunk-7RKMIYR6.mjs';
export { Calendar, CalendarDayButton } from './chunk-S7JTBJ5O.mjs';
export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from './chunk-UJ3O5PS5.mjs';
export { Avatar, AvatarFallback, AvatarImage } from './chunk-3TCRI4BE.mjs';
export { HoverCard, HoverCardContent, HoverCardTrigger } from './chunk-BR7JIMR3.mjs';
export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useFormField } from './chunk-EFV6VMVK.mjs';
export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from './chunk-LMJBLECE.mjs';
export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from './chunk-GS22VIAB.mjs';
export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from './chunk-R2BYKSW4.mjs';
export { RadioGroup, RadioGroupItem } from './chunk-DKNYE4BS.mjs';
export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, navigationMenuTriggerStyle } from './chunk-VJHFD65M.mjs';
export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from './chunk-R6343CNL.mjs';
export { Toaster } from './chunk-FN23HJDE.mjs';
export { Skeleton } from './chunk-QFASJAVY.mjs';
export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from './chunk-PAVNEVCL.mjs';
export { Separator } from './chunk-D2VCHEW3.mjs';
export { ScrollArea, ScrollBar } from './chunk-BQ4VTUAU.mjs';
export { ResizableHandle, ResizablePanel, ResizablePanelGroup } from './chunk-NS4OBKGT.mjs';
export { FilePreview, PdfPreview, useFileData } from './chunk-EH4MGBB4.mjs';
export { PdfNavigator } from './chunk-XIANQWOZ.mjs';
export { AcceptReject, ConfidenceThresholdSettings, EditableField, ExtractedDataDisplay, ListRenderer, PropertyRenderer, TableRenderer, getConfidenceBackgroundClass, useItemData } from './chunk-CU226N6Z.mjs';
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from './chunk-YGYJH2KV.mjs';
export { Label } from './chunk-GJTDG4HZ.mjs';
export { Slider } from './chunk-XHJVNRXI.mjs';
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from './chunk-ETHWXTFI.mjs';
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './chunk-ACWZ42IC.mjs';
export { Toggle, toggleVariants } from './chunk-4QZ4SDAN.mjs';
export { Textarea } from './chunk-BSMI5PLR.mjs';
export { Tabs, TabsContent, TabsList, TabsTrigger } from './chunk-R2YZKBS7.mjs';
export { Switch } from './chunk-PYUECJC6.mjs';
import './chunk-3IBS2UPL.mjs';
import { workflowStreamingManager, fetchHandlerEvents } from './chunk-K5V72JEW.mjs';
export { AgentStreamDisplay, WorkflowProgressBar, WorkflowTrigger, createHandlerStore, useHandlerStore, useWorkflowHandler, useWorkflowHandlerList, useWorkflowProgress, useWorkflowRun } from './chunk-K5V72JEW.mjs';
import './chunk-KI3KVHLZ.mjs';
export { ColumnHeader, EXTRACTED_DATA_COLUMN_NAMES, ExtractedDataItemGrid, FormattedDate, ItemGrid, PaginationControls, ReviewStatusBadge, STATUS_OPTIONS, SyncedIcon, createExtractedDataColumn, getExtractedDataItemsToReviewCount as getItemsToReviewCount, useItemGridData } from './chunk-EREFUPHC.mjs';
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } from './chunk-DJSXQ6E4.mjs';
export { Badge, badgeVariants } from './chunk-4SLZJPDY.mjs';
import './chunk-NH3TF2QK.mjs';
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from './chunk-77RHHXY2.mjs';
import './chunk-QQG6BRXU.mjs';
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from './chunk-54HXPXVZ.mjs';
export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from './chunk-7UVHBMZO.mjs';
export { ItemCount } from './chunk-3FRLYLMY.mjs';
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-2QBCG3BE.mjs';
export { useIndex, useIndexList, useIndexStore } from './chunk-JKRSZAN2.mjs';
import { useWorkflowsClient } from './chunk-FBUZYPY4.mjs';
export { ApiProvider, cloudApiClient, createAgentDataClient as createCloudAgentClient, createMockClients, createClient as createWorkflowsClient, createConfig as createWorkflowsConfig, useAgentDataClient, useApiClients, useCloudApiClient, useWorkflowsClient, workflowsClient } from './chunk-FBUZYPY4.mjs';
export { FILE_TYPE_GROUPS, FileType, FileUploader, UploadProgress, createFileTypeValidator, formatFileSize, getFileExtensions, getFileMimeTypes, getFileTypeDefinition, getFileTypesByCategory, isCryptoSupported, isFileApiSupported, isFileTypeMatch, useFileUpload, useUploadProgress, validateFile } from './chunk-FUT5SO57.mjs';
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './chunk-ICIG4S2N.mjs';
export { Progress } from './chunk-KRVDHZW4.mjs';
export { Input } from './chunk-A734CEDD.mjs';
export { Button, buttonVariants } from './chunk-KPGB4IWL.mjs';
export { cn } from './chunk-MG2ARK3A.mjs';
import { __spreadProps, __spreadValues, __objRest, __restKey } from './chunk-JD6AELXS.mjs';
import { useMemo, useState, useCallback, useEffect } from 'react';
import { create } from 'zustand';
import { postEventsByHandlerId, postWorkflowsByNameRunNowait } from '@llamaindex/workflows-client';
// src/chat/components/chat.interface.ts
var MessageRole = {
Assistant: "assistant"
};
async function createChatHandler(client, workflowName) {
var _a;
const response = await postWorkflowsByNameRunNowait({
client,
path: { name: workflowName },
body: { start_event: {} }
});
if (!((_a = response.data) == null ? void 0 : _a.handler_id)) {
throw new Error("Failed to create chat handler: no handler_id returned");
}
return response.data.handler_id;
}
async function sendEventToHandler(client, handlerId, event) {
await postEventsByHandlerId({
client,
path: { handler_id: handlerId },
body: {
event: JSON.stringify(event)
}
});
}
function subscribeToHandlerEvents(client, handlerId, callbacks) {
const { onEvent, onError, onComplete } = callbacks;
const { unsubscribe } = fetchHandlerEvents(
{ client, handlerId },
{
onData: (event) => {
onEvent == null ? void 0 : onEvent(event);
},
onError: (error) => {
onError == null ? void 0 : onError(error);
},
onFinish: () => {
onComplete == null ? void 0 : onComplete();
}
}
);
return unsubscribe;
}
function unsubscribeFromHandler(handlerId) {
const streamKey = `handler:${handlerId}`;
workflowStreamingManager.closeStream(streamKey);
}
// src/chat/components/message-parts/types.ts
var TextPartType = "text";
var FilePartType = "data-file";
var ArtifactPartType = "data-artifact";
var EventPartType = "data-event";
var SourcesPartType = "data-sources";
var SuggestionPartType = "data-suggested_questions";
// src/chat/store/adapters.ts
function extractTextFromParts(parts) {
return parts.filter((part) => part.type === TextPartType).map((part) => part.text).join(" ");
}
function isDeltaEvent(event) {
return event.type.includes("ChatDeltaEvent");
}
function isStopEvent(event) {
return event.type === "workflow.events.StopEvent" /* StopEvent */;
}
function isInputRequiredEvent(event) {
return event.type === "workflow.events.InputRequiredEvent" /* InputRequiredEvent */;
}
function isMessageTerminator(event) {
return isStopEvent(event) || isInputRequiredEvent(event);
}
function messageToEvent(message) {
const text = extractTextFromParts(message.parts);
if (!text || text.trim() === "") {
throw new Error("Cannot send empty message");
}
return {
type: "workflow.events.HumanResponseEvent" /* HumanResponseEvent */,
data: {
response: text
}
};
}
function autoCompleteMarkdown(text) {
const stack = [];
let i = 0;
while (i < text.length) {
const inFencedCode = stack[stack.length - 1] === "```";
if (text.slice(i, i + 3) === "```") {
if (stack[stack.length - 1] === "```") {
stack.pop();
} else {
stack.push("```");
}
i += 3;
continue;
}
if (text.slice(i, i + 2) === "**") {
if (stack[stack.length - 1] === "**") {
stack.pop();
} else {
stack.push("**");
}
i += 2;
continue;
}
if (text[i] === "*") {
if (inFencedCode) {
i++;
continue;
}
if (text[i - 1] === "*") {
i++;
continue;
}
if (text[i + 1] === "*") {
i++;
continue;
}
if (i === text.length - 1) {
if (stack[stack.length - 1] === "*") {
stack.pop();
}
i++;
continue;
}
if (stack[stack.length - 1] === "*") {
stack.pop();
} else {
stack.push("*");
}
i++;
continue;
}
if (text[i] === "`") {
if (inFencedCode) {
i++;
continue;
}
if (text[i - 1] === "`" && text[i - 2] === "`") {
i++;
continue;
}
if (text[i + 1] === "`" && text[i + 2] === "`") {
i++;
continue;
}
if (text[i + 1] === "*" && text[i - 1] === "*") {
i++;
continue;
}
if (i === text.length - 1) {
if (stack[stack.length - 1] === "`") {
stack.pop();
}
i++;
continue;
}
if (stack[stack.length - 1] === "`") {
stack.pop();
} else {
stack.push("`");
}
i++;
continue;
}
i++;
}
let completedText = text;
while (stack.length > 0) {
const marker = stack.pop();
if (marker === "```") {
completedText += "\n";
}
completedText += marker;
}
return completedText;
}
function parseTextWithXMLMarkers(text) {
const parts = [];
const completedText = autoCompleteMarkdown(text);
const codeBlockRanges = findCodeBlockRanges(completedText);
const xmlPattern = /<(\w+)>([\s\S]*?)<\/\1>/g;
let lastIndex = 0;
let match;
while ((match = xmlPattern.exec(completedText)) !== null) {
const [fullMatch, tagName, content] = match;
const matchStart = match.index;
const matchEnd = matchStart + fullMatch.length;
if (isInsideCodeBlock(matchStart, matchEnd, codeBlockRanges)) {
continue;
}
if (matchStart > lastIndex) {
const plainText = completedText.slice(lastIndex, matchStart);
const trimmed = plainText.trim();
if (trimmed) {
parts.push({ type: TextPartType, text: trimmed });
}
}
const part = parseXMLMarker(tagName, content.trim());
if (part) {
parts.push(part);
}
lastIndex = matchEnd;
}
if (lastIndex < completedText.length) {
const remaining = completedText.slice(lastIndex);
const startTagMatch = remaining.match(/<(\w+)>/);
if (startTagMatch) {
const tagStartIndex = lastIndex + (startTagMatch.index || 0);
if (!isInsideCodeBlock(tagStartIndex, completedText.length, codeBlockRanges)) {
const before = remaining.slice(0, startTagMatch.index);
const trimmed = before.trim();
if (trimmed) {
parts.push({ type: TextPartType, text: trimmed });
}
} else {
const trimmed = remaining.trim();
if (trimmed) parts.push({ type: TextPartType, text: trimmed });
}
} else {
const trimmed = remaining.trim();
if (trimmed) parts.push({ type: TextPartType, text: trimmed });
}
}
return parts;
}
function findCodeBlockRanges(text) {
const ranges = [];
const lines = text.split("\n");
let currentIndex = 0;
let inFencedBlock = false;
let fenceStart = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineStart = currentIndex;
if (line.trim().startsWith("```")) {
if (!inFencedBlock) {
inFencedBlock = true;
fenceStart = lineStart;
} else {
inFencedBlock = false;
const fenceEnd = lineStart + line.length;
ranges.push([fenceStart, fenceEnd]);
}
}
currentIndex += line.length + 1;
}
const inlinePattern = /`[^`\n]+`/g;
let match;
while ((match = inlinePattern.exec(text)) !== null) {
const matchStart = match.index;
const matchEnd = matchStart + match[0].length;
let isInFenced = false;
for (const [blockStart, blockEnd] of ranges) {
if (matchStart >= blockStart && matchEnd <= blockEnd) {
isInFenced = true;
break;
}
}
if (!isInFenced) {
ranges.push([matchStart, matchEnd]);
}
}
return ranges;
}
function isInsideCodeBlock(start, end, codeBlockRanges) {
for (const [blockStart, blockEnd] of codeBlockRanges) {
if (start >= blockStart && start < blockEnd) {
return true;
}
if (end > blockStart && end <= blockEnd) {
return true;
}
if (start <= blockStart && end >= blockEnd) {
return true;
}
}
return false;
}
function parseXMLMarker(tagName, content) {
try {
const data = JSON.parse(content);
switch (tagName) {
case "sources":
return {
type: SourcesPartType,
data
// Expected: { nodes: SourceNode[] }
};
case "suggested_questions":
return {
type: SuggestionPartType,
data
// Expected: string[]
};
case "file":
return {
type: FilePartType,
data
// Expected: { filename, mediaType, url }
};
case "artifact":
return {
type: ArtifactPartType,
data
// Expected: { type, created_at, data }
};
case "event":
return {
type: EventPartType,
data
// Expected: { title, status, description?, data? }
};
default:
return {
type: tagName,
data
};
}
} catch (error) {
console.warn(
`[XML Protocol] Failed to parse <${tagName}> content:`,
content,
error
);
return null;
}
}
// src/chat/store/streaming-message.ts
var StreamingMessage = class {
constructor(messageId) {
this.events = [];
this.currentTextBuffer = "";
// Buffer for adjacent delta events
this.currentTextParts = [];
// Parsed parts from current buffer
this.finalizedParts = [];
// Finalized parts (flushed)
this._status = "streaming";
this.messageId = messageId;
}
/**
* Get current status
*/
get status() {
return this._status;
}
/**
* Add a new event and incrementally update the parsed result
* Only adjacent delta events are merged together
*/
addEvent(event) {
if (!event || !event.type) {
return;
}
this.events.push(event);
if (isStopEvent(event)) {
return;
}
if (isDeltaEvent(event)) {
const delta = event.data.delta;
if (delta) {
this.currentTextBuffer += delta;
this.currentTextParts = this.currentTextBuffer ? parseTextWithXMLMarkers(this.currentTextBuffer) : [];
}
return;
}
this.flushTextBuffer();
this.finalizedParts.push({
type: event.type,
data: event.data
});
}
/**
* Mark the streaming as completed and flush any remaining text
*/
complete() {
this.flushTextBuffer();
this._status = "completed";
}
/**
* Get current MessagePart[] (returns finalized + current, O(1))
*/
getParts() {
return [...this.finalizedParts, ...this.currentTextParts];
}
/**
* Get accumulated events (mainly for debugging)
*/
getEvents() {
return [...this.events];
}
/**
* Get current text buffer (mainly for debugging)
*/
getTextBuffer() {
return this.currentTextBuffer;
}
/**
* Clear all accumulated state
*/
clear() {
this.events = [];
this.currentTextBuffer = "";
this.currentTextParts = [];
this.finalizedParts = [];
this._status = "streaming";
}
/**
* Flush the current text buffer (finalize adjacent delta events)
* This is called when a non-delta event arrives or streaming completes
*/
flushTextBuffer() {
if (!this.currentTextBuffer.trim()) {
return;
}
this.finalizedParts.push(...this.currentTextParts);
this.currentTextBuffer = "";
this.currentTextParts = [];
}
};
// src/chat/store/chat-store.ts
var createChatStore = (client) => create()((set, get) => ({
// Initial state
sessions: {},
// Session management
createSession: async (options) => {
const {
workflowName,
handlerId: providedHandlerId,
initialMessages = []
} = options;
const handlerId = providedHandlerId || await createChatHandler(client, workflowName);
const session = {
handlerId,
workflowName,
messages: initialMessages,
status: "ready",
error: null,
streamingMessage: null
};
set((state) => ({
sessions: __spreadProps(__spreadValues({}, state.sessions), { [handlerId]: session })
}));
subscribeToHandlerEvents(client, handlerId, {
onEvent: (event) => {
const session2 = get().sessions[handlerId];
if (!session2) return;
let streamingMsg = session2.streamingMessage;
if (!streamingMsg) {
const assistantMessageId = `assistant-${Date.now()}`;
const assistantMessage = {
id: assistantMessageId,
role: MessageRole.Assistant,
parts: []
};
get()._appendMessage(handlerId, assistantMessage);
streamingMsg = new StreamingMessage(assistantMessageId);
get()._setStreamingMessage(handlerId, streamingMsg);
get().setStatus(handlerId, "streaming");
}
streamingMsg.addEvent(event);
const updatedParts = streamingMsg.getParts();
get()._updateAssistantMessage(
handlerId,
streamingMsg.messageId,
updatedParts
);
if (isMessageTerminator(event)) {
streamingMsg.complete();
get().setStatus(handlerId, "ready");
get()._setStreamingMessage(handlerId, null);
}
},
onError: (error) => {
if (error.name === "AbortError" || error.name === "TypeError" && error.message.includes("network error")) {
return;
}
const session2 = get().sessions[handlerId];
if (session2 == null ? void 0 : session2.streamingMessage) {
session2.streamingMessage.complete();
get()._setStreamingMessage(handlerId, null);
}
get().setError(handlerId, error);
get().setStatus(handlerId, "error");
},
onComplete: () => {
const session2 = get().sessions[handlerId];
if (session2 == null ? void 0 : session2.streamingMessage) {
session2.streamingMessage.complete();
get()._setStreamingMessage(handlerId, null);
}
get().setStatus(handlerId, "ready");
}
});
return handlerId;
},
deleteSession: (handlerId) => {
unsubscribeFromHandler(handlerId);
set((state) => {
const _a = state.sessions, { [handlerId]: _ } = _a, remainingSessions = __objRest(_a, [__restKey(handlerId)]);
return { sessions: remainingSessions };
});
},
getSession: (handlerId) => {
return get().sessions[handlerId];
},
// Message operations
sendMessage: async (handlerId, message, _opts) => {
const session = get().sessions[handlerId];
if (!session) {
throw new Error(`Session ${handlerId} not found`);
}
get()._appendMessage(handlerId, message);
const assistantMessageId = `assistant-${Date.now()}`;
const assistantMessage = {
id: assistantMessageId,
role: MessageRole.Assistant,
parts: []
};
get()._appendMessage(handlerId, assistantMessage);
get().setStatus(handlerId, "submitted");
get()._setStreamingMessage(
handlerId,
new StreamingMessage(assistantMessageId)
);
try {
const event = messageToEvent(message);
await sendEventToHandler(client, handlerId, event);
get().setStatus(handlerId, "streaming");
} catch (error) {
get().setError(handlerId, error);
get().setStatus(handlerId, "error");
throw error;
}
},
setMessages: (handlerId, messages) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), { messages })
})
};
});
},
_appendMessage: (handlerId, message) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), {
messages: [...session.messages, message]
})
})
};
});
},
_updateAssistantMessage: (handlerId, messageId, parts) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
const updatedMessages = session.messages.map(
(msg) => msg.id === messageId ? __spreadProps(__spreadValues({}, msg), { parts }) : msg
);
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), { messages: updatedMessages })
})
};
});
},
// Status management
setStatus: (handlerId, status) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), { status })
})
};
});
},
setError: (handlerId, error) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), { error })
})
};
});
},
// Internal helper for setting streamingMessage
_setStreamingMessage: (handlerId, streamingMessage) => {
set((state) => {
const session = state.sessions[handlerId];
if (!session) return state;
return {
sessions: __spreadProps(__spreadValues({}, state.sessions), {
[handlerId]: __spreadProps(__spreadValues({}, session), { streamingMessage })
})
};
});
},
// Streaming control
stop: async (handlerId) => {
const session = get().sessions[handlerId];
if (!session) {
throw new Error(`Session ${handlerId} not found`);
}
if (session.streamingMessage) {
session.streamingMessage.complete();
}
unsubscribeFromHandler(handlerId);
get().setStatus(handlerId, "ready");
get()._setStreamingMessage(handlerId, null);
},
regenerate: async (handlerId, messageId) => {
const session = get().sessions[handlerId];
if (!session) {
throw new Error(`Session ${handlerId} not found`);
}
let targetIndex = -1;
if (messageId) {
targetIndex = session.messages.findIndex((m) => m.id === messageId);
} else {
for (let i = session.messages.length - 1; i >= 0; i--) {
if (session.messages[i].role === "user") {
targetIndex = i;
break;
}
}
}
if (targetIndex === -1) {
throw new Error("No message found to regenerate");
}
const messagesToKeep = session.messages.slice(0, targetIndex + 1);
get().setMessages(handlerId, messagesToKeep);
const assistantMessageId = `assistant-${Date.now()}`;
const assistantMessage = {
id: assistantMessageId,
role: MessageRole.Assistant,
parts: []
};
get()._appendMessage(handlerId, assistantMessage);
get().setStatus(handlerId, "submitted");
get()._setStreamingMessage(
handlerId,
new StreamingMessage(assistantMessageId)
);
try {
const messageToResend = session.messages[targetIndex];
const event = messageToEvent(messageToResend);
await sendEventToHandler(client, handlerId, event);
get().setStatus(handlerId, "streaming");
} catch (error) {
get().setError(handlerId, error);
get().setStatus(handlerId, "error");
throw error;
}
}
}));
// src/chat/hooks/use-chat-store.ts
var globalStore = null;
function useChatStore(selector) {
const client = useWorkflowsClient();
const store = useMemo(() => {
if (!globalStore) {
globalStore = createChatStore(client);
}
return globalStore;
}, [client]);
return selector ? store(selector) : store();
}
// src/chat/hooks/use-chat.ts
function useChat(options) {
const {
workflowName,
handlerId: providedHandlerId,
initialMessages,
onReady,
onError
} = options;
const store = useChatStore();
const [handlerId, setHandlerId] = useState(
providedHandlerId || null
);
const session = useChatStore(
(state) => handlerId ? state.sessions[handlerId] : void 0
);
const initSession = useCallback(async () => {
const id = await store.createSession({
workflowName,
handlerId: providedHandlerId,
initialMessages
});
setHandlerId(id);
onReady == null ? void 0 : onReady(id);
return id;
}, [workflowName, providedHandlerId, initialMessages, store, onReady]);
useEffect(() => {
if (providedHandlerId && !session) {
initSession().catch((error) => {
onError == null ? void 0 : onError(error);
});
}
}, [providedHandlerId, session, initSession, onError]);
useEffect(() => {
if (session == null ? void 0 : session.error) {
onError == null ? void 0 : onError(session.error);
}
}, [session == null ? void 0 : session.error, onError]);
const sendMessage = useCallback(
async (message, opts) => {
let currentHandlerId = handlerId;
if (!currentHandlerId) {
try {
currentHandlerId = await initSession();
} catch (error) {
onError == null ? void 0 : onError(error);
throw error;
}
}
await store.sendMessage(currentHandlerId, message, opts);
},
[handlerId, initSession, store, onError]
);
const stop = useCallback(async () => {
if (!handlerId) return;
await store.stop(handlerId);
}, [handlerId, store]);
const regenerate = useCallback(
async (opts) => {
if (!handlerId) {
throw new Error("Cannot regenerate: no messages sent yet");
}
await store.regenerate(handlerId, opts == null ? void 0 : opts.messageId);
},
[handlerId, store]
);
const setMessages = useCallback(
(messages) => {
if (!handlerId) return;
store.setMessages(handlerId, messages);
},
[handlerId, store]
);
return useMemo(
() => ({
handlerId,
messages: (session == null ? void 0 : session.messages) || [],
status: (session == null ? void 0 : session.status) || (handlerId ? "ready" : "idle"),
sendMessage,
stop,
regenerate,
setMessages
}),
[
handlerId,
session == null ? void 0 : session.messages,
session == null ? void 0 : session.status,
sendMessage,
stop,
regenerate,
setMessages
]
);
}
export { useChat, useChatStore };