UNPKG

@llamaindex/ui

Version:

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

623 lines (616 loc) 19.9 kB
import { StopEvent, WorkflowEvent, isStopEvent } from './chunk-D3KRTFL2.mjs'; import { useWorkflowsClient } from './chunk-3YCQYQNL.mjs'; import { logger, FileUploader } from './chunk-XPDHL32K.mjs'; import { __publicField, __spreadValues, __objRest } from './chunk-4AMAFZLZ.mjs'; import { useMemo, useState, useCallback } from 'react'; import { useSnapshot, proxy } from 'valtio'; import { getHandlers, getWorkflows, postWorkflowsByNameRunNowait, getWorkflowsByNameRepresentation, getHandlersByHandlerId, postEventsByHandlerId, postHandlersByHandlerIdCancel } from '@llamaindex/workflows-client'; import { toast } from 'sonner'; import { jsx } from 'react/jsx-runtime'; var createState = () => { return proxy({ handlers: {} }); }; function createActions(state, client) { return { async sync() { var _a, _b; const resp = await getHandlers({ client }); const allHandlers = (_b = (_a = resp.data) == null ? void 0 : _a.handlers) != null ? _b : []; allHandlers.forEach((h) => { state.handlers[h.handler_id] = { handler_id: h.handler_id, workflow_name: h.workflow_name, status: h.status, started_at: h.started_at, updated_at: h.updated_at ? new Date(h.updated_at) : void 0, completed_at: h.completed_at ? new Date(h.completed_at) : void 0, error: h.error, result: h.result ? StopEvent.fromRawEvent( h.result ) : void 0 }; }); }, setHandler(handler) { state.handlers[handler.handler_id] = handler; } }; } // 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); } function createState2() { return proxy({ workflows: {} }); } function createActions2(state, client) { return { async sync() { var _a, _b; 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 }; }); }, setWorkflow(workflow) { state.workflows[workflow.name] = workflow; } }; } // src/lib/shared-streaming.ts var SharedStreamingManager = class { constructor() { __publicField(this, "activeStreams", /* @__PURE__ */ new Map()); } /** * Subscribe to a shared stream identified by key. * If stream already exists, reuses it and sends historical events. * If stream doesn't exist, creates a new one using the executor. * * @param streamKey - Unique identifier for the stream * @param subscriber - Event handlers for the stream * @param executor - Function that performs the actual streaming * @param externalSignal - Optional abort signal from caller * @returns Promise that resolves with all events and unsubscribe function */ subscribe(streamKey, subscriber, executor, canceler) { const existingStream = this.activeStreams.get(streamKey); if (existingStream) { return this.subscribeToExistingStream( streamKey, existingStream, subscriber ); } return this.createNewStream(streamKey, subscriber, executor, canceler); } /** * Get current events for a stream without subscribing */ getStreamEvents(streamKey) { const stream = this.activeStreams.get(streamKey); return stream ? [...stream.events] : []; } /** * Check if a stream is currently active */ isStreamActive(streamKey) { return this.activeStreams.has(streamKey); } /** * Get number of subscribers for a stream */ getSubscriberCount(streamKey) { const stream = this.activeStreams.get(streamKey); return stream ? stream.subscribers.size : 0; } /** * Force close a stream and all its subscribers */ closeStream(streamKey) { const stream = this.activeStreams.get(streamKey); if (stream) { stream.controller.abort(); this.cleanupStream(streamKey); } } /** * Close all active streams */ closeAllStreams() { for (const streamKey of this.activeStreams.keys()) { this.closeStream(streamKey); } } subscribeToExistingStream(streamKey, stream, subscriber) { var _a, _b, _c, _d, _e; stream.subscribers.add(subscriber); try { (_a = subscriber.onStart) == null ? void 0 : _a.call(subscriber); for (const event of stream.events) { (_b = subscriber.onData) == null ? void 0 : _b.call(subscriber, event); } if (stream.isCompleted) { if (stream.error) { (_c = subscriber.onError) == null ? void 0 : _c.call(subscriber, stream.error); } else { (_d = subscriber.onSuccess) == null ? void 0 : _d.call(subscriber, stream.events); } (_e = subscriber.onComplete) == null ? void 0 : _e.call(subscriber); } } catch (error) { console.error("Error sending historical events to subscriber:", error); } return { promise: stream.promise, unsubscribe: () => this.unsubscribe(streamKey, subscriber), disconnect: () => this.disconnect(streamKey), cancel: () => this.cancel(streamKey) }; } createNewStream(streamKey, subscriber, executor, canceler) { const controller = new AbortController(); const subscribers = /* @__PURE__ */ new Set([subscriber]); const events = []; const streamState = { controller, promise: Promise.resolve([]), // Will be replaced below subscribers, events, isCompleted: false, error: null, canceler }; this.activeStreams.set(streamKey, streamState); const compositeSubscriber = { onStart: () => { streamState.subscribers.forEach((sub) => { var _a; try { (_a = sub.onStart) == null ? void 0 : _a.call(sub); } catch (error) { console.error("Error in subscriber onStart:", error); } }); }, onData: (event) => { events.push(event); streamState.subscribers.forEach((sub) => { var _a; try { (_a = sub.onData) == null ? void 0 : _a.call(sub, event); } catch (error) { console.error("Error in subscriber onData:", error); } }); }, onError: (error) => { streamState.error = error; streamState.isCompleted = true; streamState.subscribers.forEach((sub) => { var _a, _b; try { (_a = sub.onError) == null ? void 0 : _a.call(sub, error); (_b = sub.onComplete) == null ? void 0 : _b.call(sub); } catch (err) { console.error("Error in subscriber onError:", err); } }); this.cleanupStream(streamKey); }, onSuccess: (allEvents) => { streamState.isCompleted = true; streamState.subscribers.forEach((sub) => { var _a, _b; try { (_a = sub.onSuccess) == null ? void 0 : _a.call(sub, allEvents); (_b = sub.onComplete) == null ? void 0 : _b.call(sub); } catch (error) { console.error("Error in subscriber onFinish:", error); } }); this.cleanupStream(streamKey); } }; const streamPromise = this.executeStream( executor, compositeSubscriber, controller.signal ); streamState.promise = streamPromise; return { promise: streamPromise, unsubscribe: () => this.unsubscribe(streamKey, subscriber), cancel: () => this.cancel(streamKey), disconnect: () => this.disconnect(streamKey) }; } async executeStream(executor, subscriber, signal) { var _a; try { return await executor(subscriber, signal); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); (_a = subscriber.onError) == null ? void 0 : _a.call(subscriber, err); throw err; } } async cancel(streamKey) { const stream = this.activeStreams.get(streamKey); this.disconnect(streamKey); await (stream == null ? void 0 : stream.canceler()); } disconnect(streamKey) { const stream = this.activeStreams.get(streamKey); if (stream) { for (const subscriber of stream.subscribers) { this.unsubscribe(streamKey, subscriber); } this.cleanupStream(streamKey); } } unsubscribe(streamKey, subscriber) { const stream = this.activeStreams.get(streamKey); if (!stream) return; stream.subscribers.delete(subscriber); if (stream.subscribers.size === 0) { try { stream.controller.abort(); } catch (error) { logger.debug("Error aborting stream in unsubscribe", error); } this.cleanupStream(streamKey); } } cleanupStream(streamKey) { this.activeStreams.delete(streamKey); } }; var workflowStreamingManager = new SharedStreamingManager(); var emptyState = { handler_id: "", workflow_name: "", status: "not_started", started_at: "", updated_at: void 0, completed_at: void 0, error: "", result: void 0 }; var createState3 = (rawHandler) => { const state = rawHandler ? { handler_id: rawHandler.handler_id, workflow_name: rawHandler.workflow_name, status: rawHandler.status, started_at: rawHandler.started_at, updated_at: rawHandler.updated_at ? new Date(rawHandler.updated_at) : void 0, completed_at: rawHandler.completed_at ? new Date(rawHandler.completed_at) : void 0, error: rawHandler.error, result: rawHandler.result ? StopEvent.fromRawEvent( rawHandler.result ) : void 0 } : emptyState; return proxy(state); }; function createActions3(state, client) { const actions = { async sendEvent(event, step) { const rawEvent = event.toRawEvent(); const data = await postEventsByHandlerId({ client, path: { handler_id: state.handler_id }, body: { event: rawEvent, step } }); return data.data; }, async sync(handlerId) { var _a, _b, _c; const data = await getHandlersByHandlerId({ client, path: { handler_id: handlerId != null ? handlerId : state.handler_id } }); Object.assign(state, data.data, { updated_at: ((_a = data.data) == null ? void 0 : _a.updated_at) ? new Date(data.data.updated_at) : void 0, completed_at: ((_b = data.data) == null ? void 0 : _b.completed_at) ? new Date(data.data.completed_at) : void 0, result: ((_c = data.data) == null ? void 0 : _c.result) ? StopEvent.fromRawEvent( data.data.result ) : void 0 }); }, subscribeToEvents(callbacks, includeInternal = false) { 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); }); }); } // src/workflows/store/workflow.ts function createState4(name) { return proxy({ name, graph: null }); } function createActions4(state, client) { return { async sync() { var _a, _b; const data = await getWorkflowsByNameRepresentation({ client, path: { name: state.name } }); state.graph = (_b = (_a = data.data) == null ? void 0 : _a.graph) != null ? _b : null; }, 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 createState3(data.data); } }; } // src/workflows/hooks/index.ts function useHandlers() { const client = useWorkflowsClient(); const state = getOrCreate( "handlers", () => createState() ); const actions = useMemo(() => createActions(state, client), [state, client]); return __spreadValues({ state: useSnapshot(state) }, actions); } function useWorkflows() { const client = useWorkflowsClient(); const state = getOrCreate( "workflows", () => createState2() ); const actions = useMemo( () => createActions2(state, client), [state, client] ); 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) { const client = useWorkflowsClient(); const state = getOrCreate( `handler:${handlerId}`, () => createState3() ); const actions = useMemo( () => createActions3(state, client), [state, client] ); return __spreadValues({ state: useSnapshot(state) }, actions); } function WorkflowTrigger(_a) { var _b = _a, { workflowName, customWorkflowInput, onSuccess, onError, title = "Trigger Workflow", 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 { setIsCreating(true); if (customWorkflowInput) { const workflowInput2 = customWorkflowInput(data, fieldValues); const handler2 = await createHandler(workflowInput2); toast.success("Workflow task created successfully!"); onSuccess == null ? void 0 : onSuccess(handler2); return; } const workflowInput = __spreadValues({ files: data.map((file) => ({ fileId: file.fileId, url: file.url, name: file.file.name, type: file.file.type })) }, fieldValues); const handler = await createHandler(workflowInput); toast.success("Workflow task created successfully!"); onSuccess == null ? void 0 : onSuccess(handler); setIsCreating(false); } 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); setIsCreating(false); throw error; } }, [createHandler, onSuccess, onError, customWorkflowInput] ); return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx( FileUploader, __spreadValues({ title, description, onSuccess: handleFileUpload, isProcessing: isCreating }, fileUploaderProps) ) }); } export { WorkflowTrigger, useHandler, useHandlers, useWorkflow, useWorkflows };