Tons of minor fixes from cloud sync
This commit is contained in:
+209
-43
@@ -1,11 +1,14 @@
|
||||
import React, { useState, useEffect, useContext, memo } from "react";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
import { useNavigate, Link, useLocation } from "react-router-dom";
|
||||
import { getTheme } from "../theme.jsx";
|
||||
import { toast } from "react-toastify"
|
||||
import ReactJson from "react-json-view-ssr";
|
||||
import { v4 as uuidv4} from "uuid";
|
||||
import { validateJson, collapseField, handleReactJsonClipboard, HandleJsonCopy } from "../views/Workflows.jsx";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Typography,
|
||||
@@ -13,6 +16,7 @@ import {
|
||||
CircularProgress,
|
||||
Tooltip,
|
||||
IconButton,
|
||||
TextField,
|
||||
} from '@mui/material'
|
||||
|
||||
import {
|
||||
@@ -21,6 +25,8 @@ import {
|
||||
RestartAlt as RestartAltIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
ExpandLess as ExpandLessIcon,
|
||||
Send as SendIcon,
|
||||
Error as ErrorIcon,
|
||||
} from '@mui/icons-material'
|
||||
|
||||
import {
|
||||
@@ -39,9 +45,12 @@ const AgentUI = (props) => {
|
||||
|
||||
const [originalStartTime, setOriginalStartTime] = useState(0)
|
||||
const [latestEndTime, setLatestEndTime] = useState(0)
|
||||
const [showAgentStarter, setShowAgentStarter] = useState(false)
|
||||
const [actionInput, setActionInput] = useState("")
|
||||
|
||||
const {themeMode} = useContext(Context)
|
||||
const theme = getTheme(themeMode)
|
||||
const navigate = useNavigate();
|
||||
|
||||
const agentWrapperStyle = {
|
||||
width: 1000,
|
||||
@@ -64,6 +73,10 @@ const AgentUI = (props) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (node_id === undefined || node_id === null || node_id === "") {
|
||||
return
|
||||
}
|
||||
|
||||
var found = false
|
||||
for (var key in execution_data.results) {
|
||||
const item = execution_data.results[key]
|
||||
@@ -86,6 +99,16 @@ const AgentUI = (props) => {
|
||||
|
||||
if (found === false) {
|
||||
toast.warn("Failed to find the relevant AI Agent result")
|
||||
|
||||
if (execution_data?.results?.length === 1) {
|
||||
setAgentActionResult(execution_data.results[0])
|
||||
const validatedData = validateJson(execution_data.results[0].result)
|
||||
if (validatedData.valid) {
|
||||
setData(validatedData.result)
|
||||
} else {
|
||||
toast.warn("Action output result is not valid JSON!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +118,10 @@ const AgentUI = (props) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (node_id === undefined || node_id === null) {
|
||||
toast.error("No node ID provided. Please provide node_id in the URL.")
|
||||
return
|
||||
}
|
||||
//if (node_id === undefined || node_id === null || node_id === "") {
|
||||
// toast.error("No node ID provided. Please provide node_id in the URL.")
|
||||
// return
|
||||
//}
|
||||
|
||||
if (authorization === undefined || authorization === null) {
|
||||
toast.error("No authorization provided. Please provide authorization in the URL.")
|
||||
@@ -166,6 +189,7 @@ const AgentUI = (props) => {
|
||||
|
||||
const url = `${globalUrl}/api/v1/apps/agent/run?rerun=true&decision_id=${decision?.run_details?.id}`
|
||||
var body = agentActionResult.action
|
||||
console.log("BODY: ", body)
|
||||
body.source_execution = execution.execution_id
|
||||
body.source_workflow = execution.workflow.id
|
||||
|
||||
@@ -202,10 +226,11 @@ const AgentUI = (props) => {
|
||||
const executionId = params.get("execution_id")
|
||||
const nodeId = params.get("node_id")
|
||||
const authorization = params.get("authorization")
|
||||
if (executionId !== undefined && executionId !== null && nodeId !== undefined && nodeId !== null && authorization !== undefined && authorization !== null) {
|
||||
if (executionId !== undefined && executionId !== null && authorization !== undefined && authorization !== null) {
|
||||
GetExecution(executionId, nodeId, authorization)
|
||||
} else {
|
||||
toast.warn("No execution ID or node ID provided. Please provide execution_id and node_id in the URL.")
|
||||
setShowAgentStarter(true)
|
||||
//toast.warn("No execution ID or node ID provided. Please provide execution_id and node_id in the URL.")
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -221,6 +246,11 @@ const AgentUI = (props) => {
|
||||
<Tooltip title="Finished" placement="top">
|
||||
<CheckCircleIcon style={{color: green, marginRight: 10, }} />
|
||||
</Tooltip>
|
||||
:
|
||||
item.status === "ABORTED" || item.status === "FAILURE" ?
|
||||
<Tooltip title={`${item.status}: Check the raw data`} placement="top">
|
||||
<ErrorIcon style={{color: red, marginRight: 10, }} />
|
||||
</Tooltip>
|
||||
:
|
||||
<Tooltip title={`Not started yet: ${item.status}`} placement="top">
|
||||
<HourglassDisabledIcon style={{marginRight: 10, }} />
|
||||
@@ -246,11 +276,13 @@ const AgentUI = (props) => {
|
||||
const validate = validateJson(item.details)
|
||||
const itemStartTime = item.start_time
|
||||
var itemEndTime = item.end_time
|
||||
if (itemStartTime !== undefined && (itemStartTime < originalStartTime || originalStartTime === 0)) {
|
||||
setOriginalStartTime(itemStartTime)
|
||||
if (itemStartTime !== undefined && itemStartTime !== originalStartTime && (itemStartTime < originalStartTime || originalStartTime === 0)) {
|
||||
console.log("Rerender 1")
|
||||
//setOriginalStartTime(itemStartTime)
|
||||
}
|
||||
|
||||
if (itemEndTime !== undefined && itemEndTime > latestEndTime) {
|
||||
console.log("Rerender 2")
|
||||
setLatestEndTime(itemEndTime)
|
||||
}
|
||||
|
||||
@@ -280,23 +312,47 @@ const AgentUI = (props) => {
|
||||
borderTop: "1px solid " + theme.palette.surfaceColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
if (!hovered) {
|
||||
console.log("HOVER")
|
||||
setHovered(true)
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (hovered) {
|
||||
setHovered(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
backgroundColor: hovered ? theme.palette.surfaceColor : "inherit",
|
||||
}}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onClick={(e) => {
|
||||
if (item.details === undefined || item.details === null || item.details === "") {
|
||||
toast("No details to open")
|
||||
|
||||
if (item?.category === "agent" && item?.type === "agent") {
|
||||
// Show all the data
|
||||
if (openIndexes.includes(index)) {
|
||||
console.log("Rerender 3")
|
||||
setOpenIndexes(openIndexes.filter((i) => i !== index))
|
||||
} else {
|
||||
console.log("Rerender 4")
|
||||
setOpenIndexes([...openIndexes, index])
|
||||
}
|
||||
} else {
|
||||
toast.warn("No details to open")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (openIndexes.includes(index)) {
|
||||
console.log("Rerender 5")
|
||||
setOpenIndexes(openIndexes.filter((i) => i !== index))
|
||||
} else {
|
||||
console.log("Rerender 6")
|
||||
setOpenIndexes([...openIndexes, index])
|
||||
}
|
||||
}}
|
||||
@@ -421,18 +477,31 @@ const AgentUI = (props) => {
|
||||
|
||||
const TimelineRender = (props) => {
|
||||
const { agent_data } = props;
|
||||
|
||||
const actionResult = execution?.results?.length > 0 ? execution.results[0] : execution
|
||||
var timelineItems = [
|
||||
{
|
||||
"label": "AI Agent 2",
|
||||
"type": "agent",
|
||||
"category": "agent",
|
||||
"details": actionResult?.result,
|
||||
|
||||
"status": agent_data.status,
|
||||
"start_time": agent_data.started_at,
|
||||
"end_time": agent_data.completed_at,
|
||||
"status": agent_data?.status,
|
||||
"start_time": agent_data?.started_at,
|
||||
"end_time": agent_data?.completed_at,
|
||||
},
|
||||
]
|
||||
|
||||
// Autofixer for result lol
|
||||
if ((agent_data?.decisions === undefined || agent_data?.decisions === null)) {
|
||||
const verifiedInput = validateJson(actionResult?.result)
|
||||
if (verifiedInput.valid === true && verifiedInput.result?.decisions !== undefined && verifiedInput.result?.decisions !== null) {
|
||||
agent_data.decisions = verifiedInput.result?.decisions
|
||||
|
||||
setAgentActionResult(actionResult)
|
||||
}
|
||||
}
|
||||
|
||||
var sortedTimelineItems = []
|
||||
for (var key in agent_data?.decisions) {
|
||||
const item = agent_data.decisions[key]
|
||||
@@ -501,39 +570,136 @@ const AgentUI = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
const submitInput = (inputText) => {
|
||||
toast.info("Submitting AI Agent input: " + inputText);
|
||||
|
||||
//setShowAgentStarter(false);
|
||||
//GetExecution(execution?.execution_id, execution?.node_id, execution?.authorization);
|
||||
|
||||
if (inputText === undefined || inputText === null || inputText === "") {
|
||||
toast.error("Please provide a valid input for the AI Agent.")
|
||||
return
|
||||
}
|
||||
|
||||
// 1. Run the execution. Can this be a single-action run?
|
||||
// 2. Get the execution ID and node ID from the response.
|
||||
const uuid = uuidv4()
|
||||
const data = {
|
||||
"id": uuid,
|
||||
"name":"agent",
|
||||
//"app_name":"Shuffle AI",
|
||||
"app_name":"AI Agent", // Failover for rerun
|
||||
"app_id":"shuffle_agent",
|
||||
"app_version":"1.0.0",
|
||||
|
||||
"environment":"cloud",
|
||||
"parameters":[
|
||||
{
|
||||
"name":"app_name",
|
||||
"value":"openai"
|
||||
},
|
||||
{
|
||||
"name":"input",
|
||||
"value": inputText
|
||||
},
|
||||
{
|
||||
"name":"action",
|
||||
"value":"list_tickets"
|
||||
}
|
||||
]}
|
||||
|
||||
const url = `${globalUrl}/api/v1/apps/agent_starter/run`
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json()
|
||||
})
|
||||
.then((responseJson) => {
|
||||
toast.success("Got response!")
|
||||
console.log("Agent run response: ", responseJson)
|
||||
|
||||
if (responseJson.success === true && responseJson.authorization !== undefined && responseJson.execution_id !== undefined) {
|
||||
navigate("?execution_id=" + responseJson.execution_id + "&authorization=" + responseJson.authorization)
|
||||
setShowAgentStarter(false)
|
||||
GetExecution(responseJson.execution_id, "", responseJson.authorization)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error("Error: " + error)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={agentWrapperStyle}>
|
||||
{/*
|
||||
<Typography variant="h4">
|
||||
Agent Input: {data.input}
|
||||
</Typography>
|
||||
*/}
|
||||
|
||||
<ButtonGroup style={{marginTop: 50, }}>
|
||||
<Button
|
||||
variant={buttonState === "default" ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setButtonState("default");
|
||||
}}
|
||||
>
|
||||
Default
|
||||
</Button>
|
||||
<Button
|
||||
variant={buttonState === "timeline" ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setButtonState("timeline");
|
||||
}}
|
||||
>
|
||||
Timeline
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
{showAgentStarter ?
|
||||
<Box component="form" style={{textAlign: "center", }} onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
submitInput(actionInput);
|
||||
}}>
|
||||
<img src="/images/logos/agent.svg" style={{
|
||||
width: 200,
|
||||
height: 200,
|
||||
}} />
|
||||
<div />
|
||||
|
||||
{buttonState === "timeline" ?
|
||||
<TimelineRender agent_data={data} />
|
||||
:
|
||||
null
|
||||
<Typography variant="h5" style={{marginTop: 30, }}>
|
||||
Shuffle AI Agents
|
||||
</Typography>
|
||||
<TextField
|
||||
label="Agent Input"
|
||||
variant="outlined"
|
||||
style={{width: 300, marginRight: 20, marginTop: 30, }}
|
||||
defaultValue={execution?.execution_id || ""}
|
||||
onChange={(e) => {
|
||||
setActionInput(e.target.value)
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<Tooltip title="This is the input for the AI Agent. It can be any valid JSON.">
|
||||
<IconButton type="submit">
|
||||
<SendIcon
|
||||
color="primary"
|
||||
/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
:
|
||||
<div>
|
||||
<ButtonGroup style={{marginTop: 50, }}>
|
||||
<Button
|
||||
variant={buttonState === "default" ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setButtonState("default");
|
||||
}}
|
||||
>
|
||||
Default
|
||||
</Button>
|
||||
<Button
|
||||
variant={buttonState === "timeline" ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setButtonState("timeline");
|
||||
}}
|
||||
>
|
||||
Timeline
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
{buttonState === "timeline" ?
|
||||
<TimelineRender agent_data={data} />
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user