Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agent workflow frontend #1347

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
680 changes: 679 additions & 1 deletion gui/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"export": "next export"
},
"dependencies": {
"ace-builds": "^1.27.0",
"axios": "^1.4.0",
"bootstrap": "^5.2.3",
"date-fns": "^2.30.0",
Expand All @@ -18,14 +19,17 @@
"echarts-for-react": "^3.0.2",
"eslint": "8.40.0",
"eslint-config-next": "13.4.2",
"js-yaml": "^4.1.0",
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"mermaid": "^10.4.0",
"mitt": "^3.0.0",
"mixpanel-browser": "^2.47.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"next": "13.4.2",
"react": "18.2.0",
"react-ace": "^10.1.0",
"react-datetime": "^3.2.0",
"react-dom": "18.2.0",
"react-draggable": "^4.4.5",
Expand Down
36 changes: 36 additions & 0 deletions gui/pages/Content/AgentWorkflow/AgentWorkflow.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.dropdown_placement{
z-index: 9999;
position :relative;
top: 4vh
}

.code_block_topbar{
background: #2E294B;
color: white;
font-size: 14px;
padding:8px;
height:4.5vh;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}

.diagram_content{
padding-left:0;
padding-right:0;
height:76vh;
width:50%
}

.code_section{
background-image: url('/images/workflow_background.svg');
height:71.5vh
}

.daigram_section{
background-image: url('/images/workflow_background.svg');
height: 71.5vh;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
overflow-x: scroll;
padding: 16px;
}
89 changes: 89 additions & 0 deletions gui/pages/Content/AgentWorkflow/AgentWorkflowWorkspace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, {useState, useEffect, useRef} from "react";
import Image from "next/image";
import {excludedToolkits, loadingTextEffect, warningContainer} from "@/utils/utils";
import WorkflowDiagram from "@/pages/Content/AgentWorkflow/WorkflowDiagram";
import CodeEditor from "@/pages/Content/AgentWorkflow/CodeEditor";
import {EventBus} from "@/utils/eventBus";
import styles from "@/pages/Content/AgentWorkflow/AgentWorkflow.module.css";
import {fetchAgentWorkflowDetails, updateAgentWorkflow} from "@/pages/api/DashboardService";


export default function AgentWorkflowWorkspace({tools, workflowId, internalId}){
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Workflows");
const [yamlContent, setYamlContent] = useState(``);
const [workflowDetails, setWorkflowDetails] = useState(null);
const [buttonDisabled, setButtonDisabled] = useState(true);


const [toolkitList, setToolkitList] = useState(tools)

const parseYamlContent = () => {
EventBus.emit('sendCodeContent',{})
}

const getCode = (code) =>{
setYamlContent(code)
}

useEffect(() => {
loadingTextEffect('Loading Workflows', setLoadingText, 500);
getWorkflowDetails()
},[]);

useEffect(() => {
EventBus.on('setCode', getWorkflowDetails);
return () => {
EventBus.off('setCode', getWorkflowDetails);
};
}, []);

useEffect(() => {
console.log(workflowDetails)
},[workflowDetails]);

function getWorkflowDetails() {
fetchAgentWorkflowDetails(workflowId)
.then((response) => {
setWorkflowDetails(response.data)
setIsLoading(false)
setYamlContent(response.data.agent_workflow_code)
})
.catch((error) => {
console.error('Error fetching workflow details:', error);
});
}

return(
<div className="col-12 padding_5 overflowY_auto h_calc92">
{!isLoading && workflowDetails ? <div>
<div className="vertical_containers padding_16_8 display_flex_container justify_space_between flex_dir_row">
<div>
<span className="text_16">{workflowDetails?.agent_workflow_name}</span><br />
<span className="text_12 color_gray mt_8 lh_18">{workflowDetails?.agent_workflow_description}</span>
</div>
<button className="primary_button" onClick={parseYamlContent} disabled={buttonDisabled}>&nbsp;Update Changes
</button>
</div>
<div className="display_flex_container gap_8">
<div className="w_50">
<div className={`${styles.code_block_topbar} ${"display_flex_container justify_space_between"}`}><span>Code</span>
</div>
<div className={styles.code_section}>
<CodeEditor internalId={internalId} getCode={getCode} code={workflowDetails?.agent_workflow_code} workflowId={workflowId} getWorkflowDetails={getWorkflowDetails} setButtonDisabled={setButtonDisabled} />
</div>
</div>
<div className={styles.diagram_content}>
<div className={styles.code_block_topbar}>Preview</div>
<div className={styles.daigram_section}>
{yamlContent ? <WorkflowDiagram yamlContent={yamlContent} /> : <div>{warningContainer("No preview available to display. Please type the .yaml to view the respective workflow preview here")}</div>}
</div>
</div>
</div>
</div> :
<div className="horizontal_container_center h_75vh">
<div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
</div>}
</div>
)
}
90 changes: 90 additions & 0 deletions gui/pages/Content/AgentWorkflow/AgentWorkflows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, {useState} from 'react';
import Image from "next/image";
import 'react-toastify/dist/ReactToastify.css';
import {createInternalId, getUserClick, preventDefault} from "@/utils/utils";
import styles from "@/pages/Content/Agents/Agents.module.css";
import {createAgentWorkflow, fetchAgentWorkflowDetails} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import {toast, ToastContainer} from "react-toastify";

export default function AgentWorkflows({sendWorkflowData, workflows}) {
const [createWorkflow, setCreateWorflow] = useState(false);
const [workflowDescription, setWorkflowDescription] = useState('');
const [workflowName, setWorkflowName] = useState('');
const handleAddAgentWorkflow = () => {
createAgentWorkflow({name: workflowName, description: workflowDescription, code_yaml: ""})
.then((response) => {
EventBus.emit('reFetchAgentWorkflows', {});
setCreateWorflow(false)
toast.success('Agent Workflow successfully', {autoClose: 1800});
// sendWorkflowData({
// id: response.data.id,
// name: workflowName,
// contentType: "Agent_Workflow",
// internalId: createInternalId()
// });
})
.catch((error) => {
console.error('Error fetching workflow details:', error);
});
}
const handleWorkflowNameChange = (event) => {
setWorkflowName(event.target.value);
}

const handleWorkflowDescriptionChange = (event) => {
setWorkflowDescription(event.target.value);
}

return (<>
<div className="container">
<p className="text_14 mt_8 mb_12 ml_8">Agent Workflows</p>
<div className="w_100 mb_10">
<button className="secondary_button w_100" onClick={() => {
setCreateWorflow(true); getUserClick('Agent Workflow Create Clicked', {})
}}>
+ Create Workflow
</button>
</div>

{workflows && workflows.length > 0 ? <div className="vertical_selection_scroll w_100">
{workflows.map((workflow, index) => (
<div key={index}>
<div className="agent_box w_100" onClick={() => sendWorkflowData({
id: workflow.id,
name: workflow.name,
contentType: "Agent_Workflow",
internalId: createInternalId()
})}>
<div className="text_ellipsis"><span className="agent_text text_ellipsis">{workflow.name}</span></div>
</div>
</div>
))}
</div> : <div className="form_label mt_20 horizontal_container justify_center">No Agents found</div>}
</div>

{createWorkflow && (<div className="modal" onClick={() => setCreateWorflow(false)}>
<div className="modal-content" style={{width: '502px', padding: '16px', gap: '24px'}} onClick={preventDefault}>
<div className={styles.detail_name}>Create Agent Workflow</div>
<div>
<label className={styles.form_label}>Name</label>
<input className="input_medium" type="text" value={workflowName} onChange={handleWorkflowNameChange}/>
</div>
<div>
<label className={styles.form_label}>Description</label>
<textarea className="textarea_medium" rows={3} value={workflowDescription} onChange={handleWorkflowDescriptionChange}/>
</div>
<div className="display_flex_container justify_end">
<button className="secondary_button mr_10" onClick={() => setCreateWorflow(false)}>
Cancel
</button>
<button className="primary_button" onClick={() => handleAddAgentWorkflow()}>
Create Agent Workflow
</button>
</div>
</div>
</div>)}
<ToastContainer/>
</>
);
}
74 changes: 74 additions & 0 deletions gui/pages/Content/AgentWorkflow/CodeEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, {useEffect, useState} from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-yaml';
import 'ace-builds/src-noconflict/theme-twilight';
import {EventBus} from "@/utils/eventBus";
import {updateAgentWorkflow} from "@/pages/api/DashboardService";
import {setLocalStorageValue} from "@/utils/utils";
import {toast, ToastContainer} from "react-toastify";

const YamlEditor = ({internalId, getCode, code, workflowId, getWorkflowDetails, setButtonDisabled }) => {
const [yamlContent, setYamlContent] = useState('');


const handleYamlChange = (newContent) => {
setYamlContent(newContent);
setLocalStorageValue("agent_workflow_code_" + String(internalId), newContent, setYamlContent);
setButtonDisabled(false)
};
useEffect(() => {
const sendData = () => {
// getCode(yamlContent)
updateAgentWorkflow(workflowId, {name:"", description: "", code_yaml: yamlContent})
.then((response) => {
getWorkflowDetails();
})
.catch((error) => {
toast.error('Enter Valid .yaml code', {autoClose: 1800});
});
}
EventBus.on('sendCodeContent', sendData);
return () => {
EventBus.off('sendCodeContent', sendData);
};
});
useEffect(() => {
if(code){
setLocalStorageValue("agent_workflow_code_" + String(internalId), code, setYamlContent);
}
},[code]);

useEffect(() => {
if(localStorage.getItem("agent_workflow_code_" + String(internalId))){
setYamlContent(localStorage.getItem("agent_workflow_code_" + String(internalId)))
}
},[]);

return (
<div>
<AceEditor
mode="yaml"
theme="twilight"
value={yamlContent}
onChange={handleYamlChange}
name="yaml-editor"
editorProps={{ $blockScrolling: true, }}
width="100%"
height="71.5vh"
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
wrapEnabled: true,
}}
style={{
borderBottomLeftRadius: '8px',
borderBottomRightRadius: '8px',
}}

/>
<ToastContainer/>
</div>
);
};

export default YamlEditor;
Loading
Loading