Skip to content

Commit

Permalink
Merge pull request #454 from kreneskyp/run_log_v2
Browse files Browse the repository at this point in the history
Run Log for nested chains
  • Loading branch information
kreneskyp authored Feb 19, 2024
2 parents 835b966 + 643df1f commit ece1bf7
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 107 deletions.
60 changes: 21 additions & 39 deletions frontend/chains/editor/run_log/ExecutionDetail.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
import React from "react";
import { Box, Heading, VStack } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/color-mode";
import SyntaxHighlighter from "react-syntax-highlighter";
import {
stackoverflowDark,
stackoverflowLight,
} from "react-syntax-highlighter/dist/cjs/styles/hljs";

import { SCROLLBAR_CSS } from "site/css";
import CodeEditor from "components/code_editor/CodeEditor";

export const ExecutionDetail = ({ execution }) => {
const { colorMode } = useColorMode();
const isLight = colorMode === "light";
const syntaxTheme = isLight ? stackoverflowLight : stackoverflowDark;
const messageStyle = isLight
? { color: "#222", bg: "#f6f6f6" }
: { color: "white", bg: "#1c1b1b" };

const formattedInputs =
typeof execution?.inputs === "string"
? execution?.inputs
Expand All @@ -26,48 +12,44 @@ export const ExecutionDetail = ({ execution }) => {
? execution?.outputs
: JSON.stringify(execution?.outputs, null, 4);

const syntaxStyle = {
fontSize: "12px",
maxWidth: "900px",
margniRight: "5px",
};

return (
<Box pl={4} pt={1} height={"100%"}>
<VStack alignItems={"start"} spacing={3}>
<Box width={"100%"}>
<Heading size={"sm"} mb={1}>
Input
</Heading>
<SyntaxHighlighter
customStyle={syntaxStyle}
language="json"
style={syntaxTheme}
wrapLines={true}
>
{formattedInputs}
</SyntaxHighlighter>
<Box mr={10}>
<CodeEditor
key={execution?.id}
language="json"
value={formattedInputs}
/>
</Box>
</Box>
<Box width={"100%"}>
<Heading size={"sm"} mb={1}>
Output
</Heading>
<SyntaxHighlighter
customStyle={syntaxStyle}
language="json"
style={syntaxTheme}
wrapLines={true}
>
{formattedOutputs}
</SyntaxHighlighter>
<Box mr={10}>
<CodeEditor
key={execution?.id}
language="json"
value={formattedOutputs}
/>
</Box>
</Box>
{execution?.message && (
<Box width={"100%"}>
<Heading size={"sm"} mb={1}>
Message
</Heading>
<Box {...messageStyle} minW={300} minH={100} p={2}>
<code>{execution?.message || "<no message>"}</code>
<Box mr={10}>
<CodeEditor
key={execution?.id}
language="json"
value={execution?.message || "<no message>"}
/>
</Box>
</Box>
)}
Expand Down
191 changes: 143 additions & 48 deletions frontend/chains/editor/run_log/ExecutionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,95 @@ import {
Badge,
Box,
HStack,
List,
ListItem,
IconButton,
Text,
VStack,
useDisclosure,
} from "@chakra-ui/react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faChevronDown,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
import { ExecutionStatusIcon } from "chains/editor/run_log/icons";
import { ChainTypes, NodeStateContext } from "chains/editor/contexts";
import { useEditorColorMode } from "chains/editor/useColorMode";
import TreeItem, { BranchLine } from "chains/editor/run_log/Tree";
import { StyledIcon } from "components/StyledIcon";
import { DEFAULT_NODE_STYLE, NODE_STYLES } from "chains/editor/styles";
import { useRunLog } from "chains/editor/run_log/useRunLog";

const useExecutionTree = (executions, nodes, types) => {
const buildTree = (items, parentId = null) => {
return (
items
?.filter((item) => item.parent_id === parentId)
?.map((item) => {
const node = nodes?.[item.node_id];
return {
execution: item,
children: buildTree(items, item.id),
node: node,
type: types?.find((t) => t.id === node?.node_type_id),
};
}) || []
);
};

return React.useMemo(() => {
return buildTree(executions);
}, [executions, nodes, types]);
};

const ExecutionIcon = ({ type, isLight }) => {
const styles = NODE_STYLES[type?.type] || DEFAULT_NODE_STYLE;
const iconStyle = isLight
? {
color: "gray.600",
}
: {
color: "gray.200",
};
return (
<Text {...iconStyle}>
<StyledIcon style={styles.icon} {...iconStyle} />
</Text>
);
};

const ExecutionBrief = ({ nodes, types, execution, onClick }) => {
const node = nodes?.[execution.node_id];
const type = types?.find((t) => t.id === node?.node_type_id);
const ExecutionBrief = ({ execution, node, type }) => {
const { badge, highlight } = useEditorColorMode();
return (
<HStack cursor={"pointer"} onClick={onClick}>
<Box px={2}>
<ExecutionStatusIcon execution={execution} />
</Box>
<Box fontSize={"xs"}>
<Box pl={2}>
<HStack>
<Badge
bg={highlight[type?.type] || highlight.default}
size={"xs"}
mx={1}
my={2}
fontSize={10}
mx={0}
my={1}
{...badge}
>
{type?.type || "unknown"}
</Badge>
<Text>
{type?.name || node?.class_path.split(".").pop() || "unknown"}
<Text fontSize={10}>
<ExecutionStatusIcon execution={execution.execution} />{" "}
</Text>
</Box>
</HStack>
</HStack>
<Text fontSize={12} width={120} overflowX={"hidden"}>
{type?.name || node?.class_path.split(".").pop() || "unknown"}
</Text>
</Box>
);
};

export const ExecutionList = ({ log, selectedExecution, setExecution }) => {
const { nodes } = React.useContext(NodeStateContext);
const executions =
log?.executions.filter((e) => nodes?.[e.node_id] !== undefined) || [];
const [types, setTypes] = React.useContext(ChainTypes);
const ExecutionTreeNode = ({ execution, isFirst, isLast }) => {
const { execution: selectedExecution, setExecution } = useRunLog();

const onClick = () => {
setExecution(execution);
};
const isSelected = selectedExecution?.execution.id === execution.execution.id;
const { isOpen, onToggle } = useDisclosure();

const { isLight } = useEditorColorMode();
const itemStyle = isLight
Expand All @@ -53,7 +101,7 @@ export const ExecutionList = ({ log, selectedExecution, setExecution }) => {
}
: {
borderColor: "gray.600",
_hover: { bg: "blackAlpha.400" },
_hover: { bg: "blackAlpha.400", borderRadius: 3 },
};
const selectedItemStyle = isLight
? {
Expand All @@ -63,33 +111,80 @@ export const ExecutionList = ({ log, selectedExecution, setExecution }) => {
bg: "blackAlpha.400",
};

const children = execution.children.map((child, i) => (
<ExecutionTreeNode
key={i}
execution={child}
isFirst={i === 0}
isLast={i === execution.children.length - 1}
/>
));

return (
<Box>
<VStack alignItems={"start"}>
<List alignItems={"start"} px={2}>
{executions.map((execution, i) => {
return (
<ListItem
key={i}
p={2}
borderBottom={
i !== executions.length - 1 ? "1px solid" : "none"
}
{...itemStyle}
{...(selectedExecution?.id === execution.id
? selectedItemStyle
: {})}
>
<ExecutionBrief
nodes={nodes}
types={types}
execution={execution}
onClick={() => setExecution(execution)}
<Box p={0} height={"100%"}>
<HStack
cursor={"pointer"}
onClick={onClick}
height={"60px"}
py={0}
px={2}
whiteSpace={"nowrap"}
{...(isSelected ? selectedItemStyle : itemStyle)}
width={"100%"}
>
<TreeItem isFirst={isFirst} isLast={isLast}>
<ExecutionIcon type={execution.type} isLight={isLight} />
</TreeItem>
<ExecutionBrief
execution={execution}
node={execution.node}
type={execution.type}
/>
<Box height={"100%"} alignItems={"end"} display={"flex"} width={"35px"}>
{execution.children.length > 0 && (
<IconButton
fontSize={10}
h={5}
px={0}
variant="ghost"
onClick={onToggle}
icon={
<FontAwesomeIcon
icon={isOpen ? faChevronDown : faChevronRight}
/>
</ListItem>
);
})}
</List>
}
/>
)}
</Box>
</HStack>

{isOpen && (
<HStack bg={"transparent"} height={"100%"} spacing={0}>
<BranchLine height={execution.children.length * 60} />
{children.length > 1 && <VStack spacing={0}>{children}</VStack>}
</HStack>
)}
</Box>
);
};

export const ExecutionList = ({ log }) => {
const { nodes } = React.useContext(NodeStateContext);
const [types, setTypes] = React.useContext(ChainTypes);

const executions = useExecutionTree(log?.executions, nodes, types);

return (
<Box width={"200px"}>
<VStack alignItems={"start"} spacing={0}>
{executions.map((execution, i) => (
<ExecutionTreeNode
key={execution.execution.id}
execution={execution}
isFirst={i === 0}
isLast={i === executions.length - 1}
/>
))}
</VStack>
</Box>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/chains/editor/run_log/NodeExecutionIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const NodeExecutionIcon = ({ node }) => {
const execution = run_log.log_by_node[node?.id];

const onClick = React.useCallback(() => {
run_log.setExecution(execution);
run_log.setExecution({ execution });
run_log.disclosure.onOpen();
}, [execution]);

Expand Down
1 change: 1 addition & 0 deletions frontend/chains/editor/run_log/RunEventSubscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const RunEventSubscription = graphql`
__typename
... on ExecutionType {
id
parentId
nodeId
startedAt
finishedAt
Expand Down
10 changes: 8 additions & 2 deletions frontend/chains/editor/run_log/RunLog.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ export const RunLog = ({}) => {

return (
<HStack alignItems="start">
<Box overflowY={"auto"} height={"calc(100vh - 250px)"} css={scrollbar}>
<Box
overflowY={"auto"}
height={"calc(100vh - 250px)"}
css={scrollbar}
pl={2}
minWidth={"260px"}
>
<ExecutionList
log={log}
selectedExecution={execution}
Expand All @@ -25,7 +31,7 @@ export const RunLog = ({}) => {
height={"calc(100vh - 250px)"}
css={scrollbar}
>
<ExecutionDetail execution={execution} />
<ExecutionDetail execution={execution?.execution} />
</Box>
</HStack>
);
Expand Down
Loading

0 comments on commit ece1bf7

Please sign in to comment.