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,
Chip,
CircularProgress,
Tooltip,
IconButton,
TextField,
} from '@mui/material'
import {
CheckCircle as CheckCircleIcon,
HourglassDisabled as HourglassDisabledIcon,
RestartAlt as RestartAltIcon,
ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon,
Send as SendIcon,
Error as ErrorIcon,
} from '@mui/icons-material'
import {
green,
red,
} from '../views/AngularWorkflow.jsx'
const AgentUI = (props) => {
const { globalUrl, isLoggedIn, isLoaded, userdata, } = props
const [buttonState, setButtonState] = useState("timeline")
const [execution, setExecution] = useState(null)
const [agentActionResult, setAgentActionResult] = useState(null)
const [agentRequestLoading, setAgentRequestLoading] = useState(false)
const [data, setData] = useState({})
const [openIndexes, setOpenIndexes] = useState([])
const [disableButtons, setDisableButtons] = useState(false)
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,
height: 1000,
margin: "auto",
paddingTop: 100,
}
if (data.input === undefined || data.input === null) {
data.input = ""
} else {
const verifiedInput = validateJson(data.input)
if (verifiedInput.valid === true) {
data.input = JSON.stringify(verifiedInput.result, null, 2)
}
}
const findNodeData = (execution_data, node_id) => {
if (execution_data === undefined || execution_data === null) {
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]
if (item?.action?.id !== node_id) {
continue
}
setAgentActionResult(item)
found = true
const validate = validateJson(item.result)
if (validate.valid) {
setData(validate.result)
} else {
toast.warn("Action output result is not valid JSON!")
}
break
}
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!")
}
}
}
}
const GetExecution = (execution_id, node_id, authorization) => {
if (execution_id === undefined || execution_id === null) {
toast.error("No execution ID provided. Please provide execution_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.")
return
}
const headers = {}
const executionRequest = {
"execution_id": execution_id,
"authorization": authorization,
}
const url = `${globalUrl}/api/v1/streams/results`
fetch(url, {
method: "POST",
headers: headers,
body: JSON.stringify(executionRequest),
credentials: "include",
cors: "no-cors",
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
if (responseJson.success !== false) {
if (responseJson.status === "EXECUTING") {
// Recursively looking for updates until it's not executing anymore
setTimeout(() => {
GetExecution(execution_id, node_id, authorization)
}, 3000)
} else {
setDisableButtons(false)
setDisableButtons(false)
}
setExecution(responseJson)
findNodeData(responseJson, node_id)
} else {
setDisableButtons(false)
if (responseJson.reason === undefined || responseJson.reason === null) {
toast.error("Failed to load the agent data. Please try again and contact support@shuffler.io if this persists")
} else {
toast.error("Error: " + responseJson.reason)
}
}
})
.catch((error) => {
setDisableButtons(false)
toast.error("Error: " + error)
})
}
const RerunDecision = (decision) => {
if (execution.execution_id === undefined || execution.execution_id === null) {
toast.error("No workflow run loaded. Please try again and contact support@shuffler.io if this persists.")
return
}
if (agentActionResult === undefined || agentActionResult === null) {
toast.error("Failed to find the relevant agent action. Please try again, and contact support@shuffler.io if it persists.")
return
}
console.log("DECISION: ", decision)
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
fetch(url, {
method: "POST",
body: JSON.stringify(body),
credentials: "include",
cors: "no-cors",
})
.then((response) => {
return response.json()
})
.then((responseJson) => {
console.log("RESP: ", responseJson)
if (responseJson.success !== false) {
} else {
if (responseJson.reason === undefined || responseJson.reason === null) {
toast.warn("Failed to restart the agent decision. Please try again and contact support@shuffler.io if this persists")
} else {
toast.warn(responseJson.reason)
}
}
GetExecution(execution.execution_id, agentActionResult.action.id, execution.authorization)
})
.catch((error) => {
toast.error("Error: " + error)
})
}
useEffect(() => {
const params = new URLSearchParams(window.location.search)
const executionId = params.get("execution_id")
const nodeId = params.get("node_id")
const authorization = params.get("authorization")
if (executionId !== undefined && executionId !== null && authorization !== undefined && authorization !== null) {
GetExecution(executionId, nodeId, authorization)
} else {
setShowAgentStarter(true)
//toast.warn("No execution ID or node ID provided. Please provide execution_id and node_id in the URL.")
}
}, [])
const maxTimelineWidth = 150
const TimelineItem = (props) => {
const { item, index } = props;
const [hovered, setHovered] = useState(false);
const parsedStatus = item.status === "RUNNING" || item.status === "WAITING" ?
{item.details}
}