@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
JavaScript
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 };