UNPKG

@llamaindex/ui

Version:

A comprehensive UI component library built with React, TypeScript, and Tailwind CSS for LlamaIndex applications

657 lines (651 loc) 24.3 kB
import { ScrollArea } from './chunk-AKNMVAML.mjs'; import { StopEvent, WorkflowEvent, isStopEvent } from './chunk-D3KRTFL2.mjs'; import { workflowStreamingManager } from './chunk-D267IRNU.mjs'; import { Badge } from './chunk-TIB3D576.mjs'; import { Card, CardContent, CardHeader, CardTitle } from './chunk-BYCYB6AC.mjs'; import { useWorkflowsClient } from './chunk-3YCQYQNL.mjs'; import { FileUploader } from './chunk-4AP7BPTD.mjs'; import { logger } from './chunk-QZKN7AZG.mjs'; import { Button } from './chunk-JLPGK5XZ.mjs'; import { __spreadValues, __objRest } from './chunk-4AMAFZLZ.mjs'; import { useMemo, useEffect, useState, useCallback } from 'react'; import { useSnapshot, proxy } from 'valtio'; import { getHandlers, getWorkflows, postWorkflowsByNameRunNowait, postWorkflowsByNameRun, getWorkflowsByNameRepresentation, getHandlersByHandlerId, postEventsByHandlerId, postHandlersByHandlerIdCancel } from '@llamaindex/workflows-client'; import { toast } from 'sonner'; import { jsx, jsxs } from 'react/jsx-runtime'; // src/shared/store.ts var globalStoreCache = /* @__PURE__ */ new Map(); function getOrCreate(key, factory) { if (!globalStoreCache.has(key)) { globalStoreCache.set(key, factory()); } return globalStoreCache.get(key); } // src/workflows/store/handler.ts var emptyState = { handler_id: "", workflow_name: "", status: "not_started", started_at: "", updated_at: void 0, completed_at: void 0, result: void 0, loading: true}; var createState = (rawHandler = {}) => { var _a, _b, _c, _d; const state = { handler_id: (_a = rawHandler.handler_id) != null ? _a : emptyState.handler_id, workflow_name: (_b = rawHandler.workflow_name) != null ? _b : emptyState.workflow_name, status: (_c = rawHandler.status) != null ? _c : emptyState.status, started_at: (_d = rawHandler.started_at) != null ? _d : emptyState.started_at, updated_at: rawHandler.updated_at ? new Date(rawHandler.updated_at) : emptyState.updated_at, completed_at: rawHandler.completed_at ? new Date(rawHandler.completed_at) : emptyState.completed_at, error: rawHandler.error, result: rawHandler.result ? StopEvent.fromRawEvent( rawHandler.result ) : emptyState.result, // use "status" field as a canary to indicate that this is a real response loading: rawHandler.status ? false : emptyState.loading, loadingError: void 0 }; return proxy(state); }; function createActions(state, client) { const actions = { async sendEvent(event, step) { if (!state.handler_id) { throw new Error("Handler ID is not yet initialized"); } const rawEvent = event instanceof WorkflowEvent ? event.toRawEvent() : event; const data = await postEventsByHandlerId({ client, path: { handler_id: state.handler_id }, body: { event: rawEvent, step } }); return data.data; }, async sync() { var _a; state.loading = true; state.loadingError = void 0; const resolvedHandlerId = state.handler_id; if (!resolvedHandlerId) return; try { const data = await getHandlersByHandlerId({ client, path: { handler_id: resolvedHandlerId } }); const updated = createState((_a = data.data) != null ? _a : {}); Object.assign(state, updated); } catch (error) { state.loadingError = error instanceof Error ? error.message : String(error); } finally { state.loading = false; } }, subscribeToEvents(callbacks, includeInternal = false) { if (!state.handler_id) { throw new Error("Handler ID is not yet initialized"); } const streamKey = `handler:${state.handler_id}`; const subscriber = { onStart: () => { var _a; state.status = "running"; (_a = callbacks == null ? void 0 : callbacks.onStart) == null ? void 0 : _a.call(callbacks); }, onData: (event) => { var _a; state.updated_at = /* @__PURE__ */ new Date(); (_a = callbacks == null ? void 0 : callbacks.onData) == null ? void 0 : _a.call(callbacks, event); }, onError: (error) => { var _a; state.status = "failed"; state.completed_at = /* @__PURE__ */ new Date(); state.updated_at = /* @__PURE__ */ new Date(); state.error = error.message; (_a = callbacks == null ? void 0 : callbacks.onError) == null ? void 0 : _a.call(callbacks, error); }, onSuccess: (events) => { var _a; state.status = "completed"; state.completed_at = /* @__PURE__ */ new Date(); state.updated_at = /* @__PURE__ */ new Date(); state.result = events[events.length - 1]; (_a = callbacks == null ? void 0 : callbacks.onSuccess) == null ? void 0 : _a.call(callbacks, events); }, onComplete: () => { var _a; state.completed_at = /* @__PURE__ */ new Date(); state.updated_at = /* @__PURE__ */ new Date(); (_a = callbacks == null ? void 0 : callbacks.onComplete) == null ? void 0 : _a.call(callbacks); } }; const canceler = async () => { await postHandlersByHandlerIdCancel({ client, path: { handler_id: state.handler_id } }); }; const { promise, unsubscribe, disconnect, cancel } = workflowStreamingManager.subscribe( streamKey, subscriber, async (subscriber2, signal) => { return streamByEventSource( { client, handlerId: state.handler_id, includeInternal, abortSignal: signal }, subscriber2, actions, state ); }, canceler ); return { promise, unsubscribe, disconnect, cancel }; } }; return actions; } function streamByEventSource(params, callbacks, actions, state) { return new Promise((resolve) => { var _a; const baseUrl = ((_a = params.client.getConfig().baseUrl) != null ? _a : "").replace( /\/$/, "" ); const urlParams = new URLSearchParams(); urlParams.set("sse", "true"); if (params.includeInternal) { urlParams.set("include_internal", "true"); } const accumulatedEvents = []; const eventSource = new EventSource( `${baseUrl}/events/${encodeURIComponent(params.handlerId)}?${urlParams.toString()}`, { withCredentials: true } ); if (params.abortSignal) { params.abortSignal.addEventListener("abort", () => { eventSource.close(); }); } eventSource.addEventListener("message", (event) => { var _a2; logger.debug("[streamByEventSource] message", JSON.parse(event.data)); const workflowEvent = WorkflowEvent.fromRawEvent( JSON.parse(event.data) ); (_a2 = callbacks.onData) == null ? void 0 : _a2.call(callbacks, workflowEvent); accumulatedEvents.push(workflowEvent); if (isStopEvent(workflowEvent)) { eventSource.close(); actions.sync().then(() => { var _a3, _b, _c; if (state.status === "completed") { (_a3 = callbacks.onSuccess) == null ? void 0 : _a3.call(callbacks, accumulatedEvents); } else if (state.status === "failed") { (_b = callbacks.onError) == null ? void 0 : _b.call(callbacks, new Error(state.error || "Server Error")); } else if (state.status === "cancelled") { (_c = callbacks.onCancel) == null ? void 0 : _c.call(callbacks); } else { throw new Error( `[This should never happen] Unexpected running status: ${state.status}` ); } resolve(accumulatedEvents); }); } }); eventSource.addEventListener("error", (event) => { logger.warn("[streamByEventSource] error", event); return; }); eventSource.addEventListener("open", () => { var _a2; logger.debug("[streamByEventSource] open"); (_a2 = callbacks.onStart) == null ? void 0 : _a2.call(callbacks); }); eventSource.addEventListener("close", () => { var _a2; logger.debug("[streamByEventSource] close"); (_a2 = callbacks.onSuccess) == null ? void 0 : _a2.call(callbacks, accumulatedEvents); resolve(accumulatedEvents); }); }); } function applyUpdateToHandler(state, update) { const updated = createState(update); Object.assign(state, updated); return state; } // src/workflows/store/handlers.ts var createState2 = ({ query } = {}) => { return proxy({ query: query != null ? query : {}, loading: true, error: void 0, handlers: {} }); }; function createActions2(state, client) { return { async sync() { var _a, _b; state.loading = true; state.loadingError = void 0; try { const resp = await getHandlers({ client, query: state.query }); const allHandlers = (_b = (_a = resp.data) == null ? void 0 : _a.handlers) != null ? _b : []; const newIds = new Set(allHandlers.map((h) => h.handler_id)); const oldIds = new Set(Object.keys(state.handlers)); for (const id of oldIds) { if (!newIds.has(id)) { delete state.handlers[id]; } } for (const h of allHandlers) { state.handlers[h.handler_id] = state.handlers[h.handler_id] ? applyUpdateToHandler(state.handlers[h.handler_id], h) : createState(h); } } catch (error) { state.loadingError = error instanceof Error ? error.message : String(error); } finally { state.loading = false; } }, setHandler(handler) { state.handlers[handler.handler_id] = handler; }, actions(handlerId) { return createActions(state.handlers[handlerId], client); } }; } function createState3() { return proxy({ workflows: {}, loading: true, error: void 0 }); } function createActions3(state, client) { return { async sync() { var _a, _b; state.loading = true; state.loadingError = void 0; try { const resp = await getWorkflows({ client }); const allWorkflows = (_b = (_a = resp.data) == null ? void 0 : _a.workflows) != null ? _b : []; allWorkflows.forEach((name) => { state.workflows[name] = { name }; }); } catch (error) { state.loadingError = error instanceof Error ? error.message : String(error); } finally { state.loading = false; } }, setWorkflow(workflow) { state.workflows[workflow.name] = workflow; } }; } function createState4(name) { return proxy({ name, graph: null, loading: true, loadingError: void 0 }); } function createActions4(state, client) { return { async sync() { var _a, _b; state.loading = true; state.loadingError = void 0; try { const data = await getWorkflowsByNameRepresentation({ client, path: { name: state.name } }); state.graph = (_b = (_a = data.data) == null ? void 0 : _a.graph) != null ? _b : null; } catch (error) { state.loadingError = error instanceof Error ? error.message : String(error); } finally { state.loading = false; } }, async runToCompletion(input) { const data = await postWorkflowsByNameRun({ client, path: { name: state.name }, body: { start_event: input } }); if (!data.data) { throw new Error(`Workflow run empty, response ${JSON.stringify(data)}`); } return createState(data.data); }, async createHandler(input, handlerId) { const data = await postWorkflowsByNameRunNowait({ client, path: { name: state.name }, body: { start_event: input, handler_id: handlerId } }); if (!data.data) { throw new Error("Handler creation failed"); } return createState(data.data); } }; } // src/workflows/hooks/index.ts function useHandlers({ query, sync = true } = {}) { const client = useWorkflowsClient(); const state = getOrCreate( `handlers:${JSON.stringify(query)}`, () => createState2({ query: query != null ? query : {} }) ); const actions = useMemo(() => createActions2(state, client), [state, client]); useEffect(() => { if (sync) { actions.sync(); } }, [actions, sync]); return __spreadValues({ state: useSnapshot(state) }, actions); } function useWorkflows({ sync = true } = {}) { const client = useWorkflowsClient(); const state = getOrCreate( "workflows", () => createState3() ); const actions = useMemo( () => createActions3(state, client), [state, client] ); useEffect(() => { if (sync) { actions.sync(); } }, [actions, sync]); return __spreadValues({ state: useSnapshot(state) }, actions); } function useWorkflow(name) { const client = useWorkflowsClient(); const state = getOrCreate( `workflow:${name}`, () => createState4(name) ); const actions = useMemo( () => createActions4(state, client), [state, client] ); return __spreadValues({ state: useSnapshot(state) }, actions); } function useHandler(handlerId, { sync = true } = {}) { const client = useWorkflowsClient(); const state = getOrCreate( `handler:${handlerId}`, () => createState({ handler_id: handlerId != null ? handlerId : void 0 }) ); const actions = useMemo( () => createActions(state, client), [state, client] ); useEffect(() => { if (sync) { actions.sync(); } }, [actions, sync, handlerId]); return __spreadValues({ state: useSnapshot(state) }, actions); } function WorkflowTrigger(_a) { var _b = _a, { workflowName, customWorkflowInput, onSuccess, onError, title, description = "Upload files to start workflow processing" } = _b, fileUploaderProps = __objRest(_b, [ "workflowName", "customWorkflowInput", "onSuccess", "onError", "title", "description" ]); const { createHandler } = useWorkflow(workflowName); const [isCreating, setIsCreating] = useState(false); const handleFileUpload = useCallback( async (data, fieldValues) => { try { const workflowInput = customWorkflowInput ? customWorkflowInput(data, fieldValues) : __spreadValues({ files: data.map((file) => ({ fileId: file.fileId, url: file.url, name: file.file.name, type: file.file.type })) }, fieldValues); setIsCreating(true); const handler = await createHandler(workflowInput); onSuccess == null ? void 0 : onSuccess(handler); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); toast.error(`Failed to create workflow task: ${error.message}`); onError == null ? void 0 : onError(error); throw error; } finally { setIsCreating(false); } }, [createHandler, onSuccess, onError, customWorkflowInput] ); return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx( FileUploader, __spreadValues({ title, description, onSuccess: handleFileUpload, isProcessing: isCreating }, fileUploaderProps) ) }); } function HandlerList({ onSelectHandler }) { const { state, sync } = useHandlers(); const handlerList = Object.values(state.handlers); const getStatusColor = (status) => { switch (status) { case "running": return "bg-blue-500"; case "completed": return "bg-green-500"; case "failed": return "bg-red-500"; case "cancelled": return "bg-gray-500"; default: return "bg-gray-400"; } }; return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Workflow Handlers" }), /* @__PURE__ */ jsx(Button, { onClick: () => sync(), variant: "outline", children: "Refresh" }) ] }), handlerList.length === 0 ? /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "pt-6", children: /* @__PURE__ */ jsx("p", { className: "text-center text-muted-foreground", children: "No handlers found. Create a workflow to get started." }) }) }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: handlerList.map((handler) => { var _a; return /* @__PURE__ */ jsxs( Card, { className: "cursor-pointer hover:bg-accent transition-colors", onClick: () => onSelectHandler == null ? void 0 : onSelectHandler(handler.handler_id), children: [ /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-medium", children: handler.workflow_name }), /* @__PURE__ */ jsx(Badge, { className: getStatusColor(handler.status), children: handler.status }) ] }) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "ID:" }), " ", handler.handler_id.slice(0, 8), "..." ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Started:" }), " ", handler.started_at.toLocaleString() ] }), handler.completed_at && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Completed:" }), " ", (_a = handler.completed_at) == null ? void 0 : _a.toLocaleString() ] }) ] }) }) ] }, handler.handler_id ); }) }) ] }); } function HandlerDetails({ handlerId, onBack }) { const { state, subscribeToEvents } = useHandler(handlerId); const [events, setEvents] = useState([]); const [isStreaming, setIsStreaming] = useState(false); useEffect(() => { if (state.status === "running") { setIsStreaming(true); const { disconnect } = subscribeToEvents( { onData: (event) => { setEvents((prev) => [...prev, event]); }, onSuccess: (allEvents) => { setEvents(allEvents); setIsStreaming(false); }, onError: () => { setIsStreaming(false); }, onComplete: () => { setIsStreaming(false); } }, true ); return () => disconnect(); } }, [state.status, subscribeToEvents]); const getStatusColor = (status) => { switch (status) { case "running": return "bg-blue-500"; case "completed": return "bg-green-500"; case "failed": return "bg-red-500"; case "cancelled": return "bg-gray-500"; default: return "bg-gray-400"; } }; return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Handler Details" }), onBack && /* @__PURE__ */ jsx(Button, { onClick: onBack, variant: "outline", children: "Back to List" }) ] }), /* @__PURE__ */ jsxs(Card, { children: [ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx(CardTitle, { children: state.workflow_name }), /* @__PURE__ */ jsx(Badge, { className: getStatusColor(state.status), children: state.status }) ] }) }), /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 text-sm", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: "Handler ID:" }), /* @__PURE__ */ jsx("p", { className: "mt-1 font-mono text-xs break-all", children: state.handler_id }) ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: "Started At:" }), /* @__PURE__ */ jsx("p", { className: "mt-1", children: state.started_at.toLocaleString() }) ] }), state.updated_at && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: "Updated At:" }), /* @__PURE__ */ jsx("p", { className: "mt-1", children: state.updated_at.toLocaleString() }) ] }), state.completed_at && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: "Completed At:" }), /* @__PURE__ */ jsx("p", { className: "mt-1", children: state.completed_at.toLocaleString() }) ] }) ] }), state.error && /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-destructive/10 p-3", children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-destructive", children: "Error:" }), /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-destructive", children: state.error }) ] }), state.result && /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-green-50 dark:bg-green-900/10 p-3", children: [ /* @__PURE__ */ jsx("span", { className: "font-medium text-green-700 dark:text-green-400", children: "Result:" }), /* @__PURE__ */ jsx("pre", { className: "mt-1 text-xs overflow-auto", children: JSON.stringify(state.result.data, null, 2) }) ] }) ] }) ] }), /* @__PURE__ */ jsxs(Card, { children: [ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx(CardTitle, { children: "Events" }), isStreaming && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "animate-pulse", children: "Streaming..." }) ] }) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(ScrollArea, { className: "h-[400px]", children: events.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-center text-muted-foreground py-8", children: "No events yet. Waiting for workflow events..." }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: events.map((event, index) => /* @__PURE__ */ jsxs( "div", { className: "rounded-lg border p-3 text-sm space-y-2", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: event.type }), /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: event.timestamp.toLocaleTimeString() }) ] }), event.data && /* @__PURE__ */ jsx("pre", { className: "text-xs overflow-auto bg-muted p-2 rounded", children: JSON.stringify(event.data, null, 2) }) ] }, index )) }) }) }) ] }) ] }); } export { HandlerDetails, HandlerList, WorkflowTrigger, useHandler, useHandlers, useWorkflow, useWorkflows };