diff --git a/DockerfileCelery b/DockerfileCelery index 0d56818b4..0f8625679 100644 --- a/DockerfileCelery +++ b/DockerfileCelery @@ -12,4 +12,14 @@ WORKDIR /app COPY . . COPY config.yaml . +# Downloads the tools +RUN python superagi/tool_manager.py + +# Set executable permissions for install_tool_dependencies.sh +RUN chmod +x install_tool_dependencies.sh + +# Install dependencies +RUN ./install_tool_dependencies.sh + + CMD ["celery", "-A", "superagi.worker", "worker", "--beat","--loglevel=info"] diff --git a/gui/pages/Content/APM/Apm.module.css b/gui/pages/Content/APM/Apm.module.css index 1827b992e..642f26377 100644 --- a/gui/pages/Content/APM/Apm.module.css +++ b/gui/pages/Content/APM/Apm.module.css @@ -5,6 +5,6 @@ .apm_dashboard { margin-top: 16px; - height: calc(100vh - 14vh); + height: calc(100vh - 16vh); overflow-y: auto; } \ No newline at end of file diff --git a/gui/pages/Content/APM/ApmDashboard.js b/gui/pages/Content/APM/ApmDashboard.js index 098559718..510c2ee43 100644 --- a/gui/pages/Content/APM/ApmDashboard.js +++ b/gui/pages/Content/APM/ApmDashboard.js @@ -1,364 +1,429 @@ -import React, { useState, useEffect, useCallback, useRef } from 'react'; +import React, {useState, useEffect, useCallback, useRef} from 'react'; import Image from "next/image"; import style from "./Apm.module.css"; import 'react-toastify/dist/ReactToastify.css'; import {getActiveRuns, getAgentRuns, getAllAgents, getToolsUsage, getMetrics} from "@/pages/api/DashboardService"; import {formatNumber, formatTime} from "@/utils/utils"; import {BarGraph} from "./BarGraph.js"; -import { WidthProvider, Responsive } from 'react-grid-layout'; +import {WidthProvider, Responsive} from 'react-grid-layout'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; const ResponsiveGridLayout = WidthProvider(Responsive); + export default function ApmDashboard() { - const [agentDetails, setAgentDetails] = useState([]); - const [tokenDetails, setTokenDetails] = useState([]); - const [runDetails, setRunDetails] = useState(0); - const [allAgents, setAllAgents] = useState([]); - const [dropdown1, setDropDown1] = useState(false); - const [dropdown2, setDropDown2] = useState(false); - const [dropdown3, setDropDown3] = useState(false); - const [selectedAgent, setSelectedAgent] = useState('Select an Agent'); - const [selectedAgentIndex, setSelectedAgentIndex] = useState(-1); - const [selectedAgentRun, setSelectedAgentRun] = useState([]); - const [activeRuns, setActiveRuns] = useState([]); - const [selectedAgentDetails, setSelectedAgentDetails] = useState(null); - const [toolsUsed, setToolsUsed] = useState([]); - const initialLayout = [ - {i: 'total_agents', x: 0, y: 0, w: 3, h: 1.5}, - {i: 'total_tokens', x: 3, y: 0, w: 3, h: 1.5}, - {i: 'total_runs', x: 6, y: 0, w: 3, h: 1.5}, - {i: 'active_runs', x: 9, y: 0, w: 3, h: 2}, - {i: 'most_used_tools', x: 9, y: 1, w: 3, h: 2}, - {i: 'models_by_agents', x: 0, y: 1, w: 3, h: 2.5}, - {i: 'runs_by_model', x: 3, y: 1, w: 3, h: 2.5}, - {i: 'tokens_by_model', x: 6, y: 1, w: 3, h: 2.5}, - {i: 'agent_details', x: 0, y: 2, w: 12, h: 2.5}, - {i: 'total_tokens_consumed', x: 0, y: 3, w: 4, h: 2}, - {i: 'total_calls_made', x: 4, y: 3, w: 4, h: 2}, - {i: 'tokens_consumed_per_call', x: 8, y: 3, w: 4, h: 2}, - ]; - const storedLayout = localStorage.getItem('myLayoutKey'); - const [layout, setLayout] = useState(storedLayout !== null ? JSON.parse(storedLayout) : initialLayout); - const firstUpdate = useRef(true); + const [agentDetails, setAgentDetails] = useState([]); + const [tokenDetails, setTokenDetails] = useState([]); + const [runDetails, setRunDetails] = useState(0); + const [allAgents, setAllAgents] = useState([]); + const [dropdown1, setDropDown1] = useState(false); + const [dropdown2, setDropDown2] = useState(false); + const [dropdown3, setDropDown3] = useState(false); + const [selectedAgent, setSelectedAgent] = useState('Select an Agent'); + const [selectedAgentIndex, setSelectedAgentIndex] = useState(-1); + const [selectedAgentRun, setSelectedAgentRun] = useState([]); + const [activeRuns, setActiveRuns] = useState([]); + const [selectedAgentDetails, setSelectedAgentDetails] = useState(null); + const [toolsUsed, setToolsUsed] = useState([]); + const initialLayout = [ + {i: 'total_agents', x: 0, y: 0, w: 3, h: 1.5}, + {i: 'total_tokens', x: 3, y: 0, w: 3, h: 1.5}, + {i: 'total_runs', x: 6, y: 0, w: 3, h: 1.5}, + {i: 'active_runs', x: 9, y: 0, w: 3, h: 2}, + {i: 'most_used_tools', x: 9, y: 1, w: 3, h: 2}, + {i: 'models_by_agents', x: 0, y: 1, w: 3, h: 2.5}, + {i: 'runs_by_model', x: 3, y: 1, w: 3, h: 2.5}, + {i: 'tokens_by_model', x: 6, y: 1, w: 3, h: 2.5}, + {i: 'agent_details', x: 0, y: 2, w: 12, h: 2.5}, + {i: 'total_tokens_consumed', x: 0, y: 3, w: 4, h: 2}, + {i: 'total_calls_made', x: 4, y: 3, w: 4, h: 2}, + {i: 'tokens_consumed_per_call', x: 8, y: 3, w: 4, h: 2}, + ]; + const storedLayout = localStorage.getItem('myLayoutKey'); + const [layout, setLayout] = useState(storedLayout !== null ? JSON.parse(storedLayout) : initialLayout); + const firstUpdate = useRef(true); + + const onLayoutChange = (currentLayout) => { + setLayout(currentLayout); + }; - const onLayoutChange = (currentLayout) => { - setLayout(currentLayout); - }; + const onClickLayoutChange = () => { + localStorage.setItem('myLayoutKey', JSON.stringify(initialLayout)); + setLayout(initialLayout); + } - const onClickLayoutChange = () => { - localStorage.setItem('myLayoutKey',JSON.stringify(initialLayout)) - setLayout(initialLayout) + useEffect(() => { + if (!firstUpdate.current) { + localStorage.setItem('myLayoutKey', JSON.stringify(layout)); + } else { + firstUpdate.current = false; } + }, [layout]); - useEffect(() => { - if (!firstUpdate.current) { - localStorage.setItem('myLayoutKey', JSON.stringify(layout)); - } else { - firstUpdate.current = false; - } - }, [layout]); + const assignDefaultDataPerModel = (data, modelList) => { + const modelsInData = data.map(item => item.name); + modelList.forEach((model) => { + if (!modelsInData.includes(model)) { + data.push({name: model, value: 0}); + } + }); + }; - const assignDefaultDataPerModel = (data, modelList) => { - const modelsInData = data.map(item => item.name); - modelList.forEach((model) => { - if (!modelsInData.includes(model)) { - data.push({name: model, value: 0}); - } - }); - }; + useEffect(() => { + const fetchData = async () => { + try { + const [metricsResponse, agentsResponse, activeRunsResponse, toolsUsageResponse] = await Promise.all([getMetrics(), getAllAgents(), getActiveRuns(), getToolsUsage()]); + const models = ['gpt-4', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4-32k', 'google-palm-bison-001']; - useEffect(() => { - const fetchData = async () => { - try { - const [metricsResponse, agentsResponse, activeRunsResponse, toolsUsageResponse] = await Promise.all([getMetrics(), getAllAgents(), getActiveRuns(), getToolsUsage()]); - const models = ['gpt-4', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4-32k', 'google-palm-bison-001']; + assignDefaultDataPerModel(metricsResponse.data.agent_details.model_metrics, models); + assignDefaultDataPerModel(metricsResponse.data.tokens_details.model_metrics, models); + assignDefaultDataPerModel(metricsResponse.data.run_details.model_metrics, models); - assignDefaultDataPerModel(metricsResponse.data.agent_details.model_metrics, models); - assignDefaultDataPerModel(metricsResponse.data.tokens_details.model_metrics, models); - assignDefaultDataPerModel(metricsResponse.data.run_details.model_metrics, models); + setAgentDetails(metricsResponse.data.agent_details); + setTokenDetails(metricsResponse.data.tokens_details); + setRunDetails(metricsResponse.data.run_details); + setAllAgents(agentsResponse.data.agent_details); + setActiveRuns(activeRunsResponse.data); + setToolsUsed(toolsUsageResponse.data); + } catch (error) { + console.log(`Error in fetching data: ${error}`); + } + } - setAgentDetails(metricsResponse.data.agent_details); - setTokenDetails(metricsResponse.data.tokens_details); - setRunDetails(metricsResponse.data.run_details); - setAllAgents(agentsResponse.data.agent_details); - setActiveRuns(activeRunsResponse.data); - setToolsUsed(toolsUsageResponse.data); - } catch(error) { - console.log(`Error in fetching data: ${error}`); - } - } - fetchData(); - const interval = setInterval(fetchData, 10000); - return () => clearInterval(interval); - }, []); + fetchData(); + const interval = setInterval(fetchData, 10000); + return () => clearInterval(interval); + }, []); - const handleSelectedAgent = useCallback((index, name) => { - setDropDown1(false) - setDropDown2(false) - setDropDown3(false) - setSelectedAgent(name) - setSelectedAgentIndex(index) - const agentDetails = allAgents.find(agent => agent.agent_id === index); - setSelectedAgentDetails(agentDetails); + const handleSelectedAgent = useCallback((index, name) => { + setDropDown1(false) + setDropDown2(false) + setDropDown3(false) + setSelectedAgent(name) + setSelectedAgentIndex(index) + const agentDetails = allAgents.find(agent => agent.agent_id === index); + setSelectedAgentDetails(agentDetails); - getAgentRuns(index).then((response) => { - const data = response.data; - setSelectedAgentRun(data); - }).catch((error) => console.error(`Error in fetching agent runs: ${error}`)); - }, [allAgents]); + getAgentRuns(index).then((response) => { + const data = response.data; + setSelectedAgentRun(data); + }).catch((error) => console.error(`Error in fetching agent runs: ${error}`)); + }, [allAgents]); - useEffect(() => handleSelectedAgent(selectedAgentIndex,selectedAgent),[allAgents]); + useEffect(() => handleSelectedAgent(selectedAgentIndex, selectedAgent), [allAgents]); - useEffect(() => { - if(allAgents.length > 0 && selectedAgent === 'Select an Agent') { - const lastAgent = allAgents[allAgents.length-1]; - handleSelectedAgent(lastAgent.agent_id, lastAgent.name); - } - }, [allAgents, selectedAgent, handleSelectedAgent]); + useEffect(() => { + if (allAgents.length > 0 && selectedAgent === 'Select an Agent') { + const lastAgent = allAgents[allAgents.length - 1]; + handleSelectedAgent(lastAgent.agent_id, lastAgent.name); + } + }, [allAgents, selectedAgent, handleSelectedAgent]); - return ( -
-
-
- Agent Performance Monitoring - -
- -
- Total Agents -
{formatNumber(agentDetails.total_agents)}
-
-
- Total tokens consumed -
{formatNumber(tokenDetails.total_tokens)}
-
-
- Total runs -
{formatNumber(runDetails.total_runs)}
-
+ return ( +
+
+
+ Agent Performance Monitoring + {/**/} +
+ +
+ Total Agents +
{formatNumber(agentDetails.total_agents)}
+
+
+ Total tokens consumed +
{formatNumber(tokenDetails.total_tokens)}
+
+
+ Total runs +
{formatNumber(runDetails.total_runs)}
+
-
- Number of Agents per model - {agentDetails.model_metrics && agentDetails.model_metrics.length > 0 - ? <> -
- - Models -
- :
- No Data - No Agents Found -
} -
+
+ Number of Agents per model + {agentDetails.model_metrics && agentDetails.model_metrics.length > 0 + ? <> +
+ + Models +
+ + :
+ No Data + No Agents Found +
} +
-
- Number of Runs per Model - {runDetails.model_metrics && runDetails.model_metrics.length > 0 - ? <> -
- - Models -
- :
- No Data - No Agents Found -
} -
+
+ Number of Runs per Model + {runDetails.model_metrics && runDetails.model_metrics.length > 0 + ? <> +
+ + Models +
+ + :
+ No Data + No Agents Found +
} +
-
- Total Tokens consumed by models - {tokenDetails.model_metrics && tokenDetails.model_metrics.length > 0 - ? <> -
- - Models -
- :
- No Data - No Agents Found -
} -
+
+ Total Tokens consumed by models + {tokenDetails.model_metrics && tokenDetails.model_metrics.length > 0 + ? <> +
+ + Models +
+ + :
+ No Data + No Agents Found +
} +
-
- Most used tools - {toolsUsed.length === 0 ? -
- No Data - No Used Tools Found -
:
- - - - - - - - -
ToolAgentsCalls
+
+ Most used tools + {toolsUsed.length === 0 ? +
+ No Data + No Used Tools Found +
:
+ + + + + + + + +
ToolAgentsCalls
-
- - - {toolsUsed.map((tool, index) => ( - - - - - - ))} - -
{tool.tool_name}{tool.unique_agents}{tool.total_usage}
-
-
} -
+
+ + + {toolsUsed.map((tool, index) => ( + + + + + + ))} + +
{tool.tool_name}{tool.unique_agents}{tool.total_usage}
+
+
} +
-
- Agent Overview - {allAgents.length === 0 ? -
- No Data - {selectedAgent === 'Select an Agent' ? 'Please Select an Agent' : No Runs found for {selectedAgent}} -
:
- - - - - - - - - - - - - -
Agent NameModel arrow_downTokens Consumed arrow_downRuns arrow_downAvg tokens per run arrow_downTools arrow_downCalls arrow_downAvg Run Time arrow_down
+
+ Agent Overview + {allAgents.length === 0 ? +
+ No Data + {selectedAgent === 'Select an Agent' ? 'Please Select an Agent' : + No Runs found for {selectedAgent}} +
:
+ + + + + + + + + + + + + +
Agent NameModel arrow_down + Tokens Consumed arrow_down + Runs arrow_down + Avg tokens per run arrow_downTools arrow_down + Calls arrow_down + Avg Run Time arrow_down +
-
- - - {allAgents.map((run, i) => ( - - - - - - - - - - ))} - -
{run.name}{run.model_name}{formatNumber(run.total_tokens)}{run.runs_completed}{run.runs_completed?(run.total_tokens/run.runs_completed).toFixed(1) : '-'} - {run.tools_used && run.tools_used.slice(0, 3).map((tool,index) => ( -
{tool}
- ))} - {run.tools_used && run.tools_used.length > 3 && -
- +{run.tools_used.length - 3} -
- } -
{run.total_calls}{run.avg_run_time === 0 ? '-' : `${parseFloat((run.avg_run_time/60).toFixed(1))} mins`}
-
-
} -
-
- Active Runs -
- {activeRuns.length === 0 ? -
- No Data - No active runs found -
: activeRuns.map((run,index) => ( -
- {run.name} -
{run.agent_name} · schedule-icon {formatTime(run.created_at)}
-
- ))} -
-
-
-
- Tokens Consumed by Runs -
- {allAgents.length > 0 &&
-
setDropDown2(!dropdown2)}>{selectedAgent}
- {dropdown2 &&
- {allAgents.map((agent,index) => ( -
handleSelectedAgent(agent.agent_id,agent.name)}>{agent.name}
))} -
} -
} +
+ + + {allAgents.map((run, i) => ( + + + + + + + + + + ))} + +
{run.name}{run.model_name}{formatNumber(run.total_tokens)}{run.runs_completed}{run.runs_completed ? (run.total_tokens / run.runs_completed).toFixed(1) : '-'} + {run.tools_used && run.tools_used.slice(0, 3).map((tool, index) => ( +
{tool}
+ ))} + {run.tools_used && run.tools_used.length > 3 && +
+ +{run.tools_used.length - 3}
- - {selectedAgentRun.length > 0 - ? <> -
- - Runs -
- :
- No Data - No Runs Found -
} + } +
{run.total_calls}{run.avg_run_time === 0 ? '-' : `${parseFloat((run.avg_run_time / 60).toFixed(1))} mins`}
+
+
} +
+
+ Active Runs +
+ {activeRuns.length === 0 ? +
+ No Data + No active runs found +
: activeRuns.map((run, index) => ( +
+ {run.name} +
{run.agent_name} · schedule-icon {formatTime(run.created_at)}
+
+ ))} +
+
+
+
+ Tokens Consumed by Runs +
+ {allAgents.length > 0 &&
+
setDropDown2(!dropdown2)}>{selectedAgent} +
+ {dropdown2 && +
+ {allAgents.map((agent, index) => ( +
handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}
))} +
} +
} +
+
+ {selectedAgentRun.length > 0 + ? <> +
+ + Runs +
+ + :
+ No Data + No Runs Found +
} +
-
-
- Calls Made by Runs -
- {allAgents.length > 0 &&
-
setDropDown1(!dropdown1)}>{selectedAgent}
- {dropdown1 &&
- {allAgents.map((agent,index) => ( -
handleSelectedAgent(agent.agent_id,agent.name)}>{agent.name}
))} -
} -
} -
-
- {selectedAgentRun.length > 0 - ? <> -
- - Runs -
- :
- No Data - No Runs Found -
} -
-
-
- Average Tokens consumed in all calls per run -
- {allAgents.length > 0 &&
-
setDropDown3(!dropdown3)}>{selectedAgent}
- {dropdown3 &&
- {allAgents.map((agent,index) => ( -
handleSelectedAgent(agent.agent_id,agent.name)}>{agent.name}
))} -
} -
} -
-
- {selectedAgentRun.length > 0 - ? <> -
- - Runs -
- :
- No Data - No Runs Found -
} -
- +
+
+ Calls Made by Runs +
+ {allAgents.length > 0 &&
+
setDropDown1(!dropdown1)}>{selectedAgent} +
+ {dropdown1 && +
+ {allAgents.map((agent, index) => ( +
handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}
))} +
} +
} +
-
- ); + {selectedAgentRun.length > 0 + ? <> +
+ + Runs +
+ + :
+ No Data + No Runs Found +
} +
+
+
+ Average Tokens consumed in all calls per run +
+ {allAgents.length > 0 &&
+
setDropDown3(!dropdown3)}>{selectedAgent} +
+ {dropdown3 && +
+ {allAgents.map((agent, index) => ( +
handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}
))} +
} +
} +
+
+ {selectedAgentRun.length > 0 + ? <> +
+ + Runs +
+ + :
+ No Data + No Runs Found +
} +
+ +
+
+ ); } \ No newline at end of file diff --git a/gui/pages/Content/APM/BarGraph.js b/gui/pages/Content/APM/BarGraph.js index 54f039993..220505590 100644 --- a/gui/pages/Content/APM/BarGraph.js +++ b/gui/pages/Content/APM/BarGraph.js @@ -1,74 +1,73 @@ -import React, { useEffect, useRef } from "react"; +import React, {useEffect, useRef} from "react"; import * as echarts from 'echarts'; -import {formatNumber} from "@/utils/utils"; -export const BarGraph = ({ data, type, color }) => { - const chartRef = useRef(null); - const containerRef = useRef(null); +export const BarGraph = ({data, type, color}) => { + const chartRef = useRef(null); + const containerRef = useRef(null); - useEffect(() => { - const chartInstance = echarts.getInstanceByDom(chartRef.current); - const chart = chartInstance ? chartInstance : echarts.init(chartRef.current); + useEffect(() => { + const chartInstance = echarts.getInstanceByDom(chartRef.current); + const chart = chartInstance ? chartInstance : echarts.init(chartRef.current); - const option = { - color: color, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } - }, - xAxis: { - type: 'category', - data: data.map(item => item.name), - axisLabel: { - interval: 0, - rotate: 45, - color: '#888' - } - }, - yAxis: { - type: 'value', - axisLabel: { - formatter: function(value) { - if (value >= 1000) { - return `${value / 1000}k`; - } else { - return value; - } - } - }, - splitLine: { - lineStyle: { - color: 'rgba(255, 255, 255, 0.08)' - } - } - }, - series: [{ - data: data.map(item => type === 'tokens_per_call' ? (item.tokens_consumed/item.calls) : item[type]), - type: 'bar' - }], - responsive: true - }; + const option = { + color: color, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'category', + data: data.map(item => item.name), + axisLabel: { + interval: 0, + rotate: 45, + color: '#888' + } + }, + yAxis: { + type: 'value', + axisLabel: { + formatter: function (value) { + if (value >= 1000) { + return `${value / 1000}k`; + } else { + return value; + } + } + }, + splitLine: { + lineStyle: { + color: 'rgba(255, 255, 255, 0.08)' + } + } + }, + series: [{ + data: data.map(item => type === 'tokens_per_call' ? (item.tokens_consumed / item.calls) : item[type]), + type: 'bar' + }], + responsive: true + }; - chart.setOption(option); + chart.setOption(option); - const resizeObserver = new ResizeObserver(entries => { - entries.forEach(entry => { - chart.resize(); - }); - }); + const resizeObserver = new ResizeObserver(entries => { + entries.forEach(entry => { + chart.resize(); + }); + }); - resizeObserver.observe(containerRef.current); + resizeObserver.observe(containerRef.current); - return () => resizeObserver.disconnect(); - }, [data, type]); + return () => resizeObserver.disconnect(); + }, [data, type]); - return ( -
-
-
- ); + return ( +
+
+
+ ); } export default BarGraph; \ No newline at end of file diff --git a/gui/pages/Content/Agents/ActionConsole.js b/gui/pages/Content/Agents/ActionConsole.js index c28772707..6bb5ace8e 100644 --- a/gui/pages/Content/Agents/ActionConsole.js +++ b/gui/pages/Content/Agents/ActionConsole.js @@ -1,153 +1,160 @@ -import React, { useState, useEffect } from 'react'; +import React, {useState, useEffect} from 'react'; import styles from './Agents.module.css'; import Image from 'next/image'; -import { updatePermissions } from '@/pages/api/DashboardService'; +import {updatePermissions} from '@/pages/api/DashboardService'; import {formatTimeDifference} from '@/utils/utils'; -function ActionBox({ action, index, denied, reasons, handleDeny, handleSelection, setReasons }) { - const isDenied = denied[index]; +function ActionBox({action, index, denied, reasons, handleDeny, handleSelection, setReasons}) { + const isDenied = denied[index]; - return ( -
-
-
Tool {action.tool_name} is seeking for Permissions
- {isDenied && ( -
-
Provide Feedback (Optional)
- { - const newReasons = [...reasons]; - newReasons[index] = e.target.value; - setReasons(newReasons); - }}/> -
- )} - {isDenied ? ( -
- - -
- ) : ( -
- - -
- )} -
-
-
- schedule-icon -
-
{formatTimeDifference(action.time_difference)}
-
+ return ( +
+
+
Tool {action.tool_name} is seeking for Permissions
+ {isDenied && ( +
+
Provide Feedback (Optional)
+ { + const newReasons = [...reasons]; + newReasons[index] = e.target.value; + setReasons(newReasons); + }}/> +
+ )} + {isDenied ? ( +
+ + +
+ ) : ( +
+ + +
+ )} +
+
+
+ schedule-icon
- ); +
{formatTimeDifference(action.time_difference)}
+
+
+ ); } -function HistoryBox({ action }){ - return ( -
-
-
Permissions for {action.tool_name} was::
- {action.status && action.status === 'APPROVED' ? ( - - ):( - - )} - {action.user_feedback != null && -
-
FeedBack
-
{action.user_feedback}
-
- } -
-
- schedule-icon -
-
{formatTimeDifference(action.time_difference)}
-
-
+function HistoryBox({action}) { + return ( +
+
+
Permissions for {action.tool_name} was::
+ {action.status && action.status === 'APPROVED' ? ( + + ) : ( + + )} + {action.user_feedback != null && +
+
FeedBack
+
{action.user_feedback}
+
+ } +
+
+ schedule-icon +
+
{formatTimeDifference(action.time_difference)}
- ) +
+
+ ) } -export default function ActionConsole({ actions, pendingPermission, setPendingPermissions }) { - const [hiddenActions, setHiddenActions] = useState([]); - const [denied, setDenied] = useState([]); - const [reasons, setReasons] = useState([]); - const [localActionIds, setLocalActionIds] = useState([]); - - useEffect(() => { - const updatedActions = actions?.filter((action) => !localActionIds.includes(action.id)); +export default function ActionConsole({actions, pendingPermission, setPendingPermissions}) { + const [hiddenActions, setHiddenActions] = useState([]); + const [denied, setDenied] = useState([]); + const [reasons, setReasons] = useState([]); + const [localActionIds, setLocalActionIds] = useState([]); - if (updatedActions && updatedActions.length > 0) { - setLocalActionIds((prevIds) => [...prevIds, ...updatedActions.map(({ id }) => id)]); + useEffect(() => { + const updatedActions = actions?.filter((action) => !localActionIds.includes(action.id)); - setDenied((prevDenied) => prevDenied.map((value, index) => updatedActions[index] ? false : value)); - setReasons((prevReasons) => prevReasons.map((value, index) => updatedActions[index] ? '' : value)); - } - }, [actions]); + if (updatedActions && updatedActions.length > 0) { + setLocalActionIds((prevIds) => [...prevIds, ...updatedActions.map(({id}) => id)]); - const handleDeny = (index) => { - setDenied((prevDenied) => { - const newDeniedState = [...prevDenied]; - newDeniedState[index] = !newDeniedState[index]; - return newDeniedState; - }); - }; + setDenied((prevDenied) => prevDenied.map((value, index) => updatedActions[index] ? false : value)); + setReasons((prevReasons) => prevReasons.map((value, index) => updatedActions[index] ? '' : value)); + } + }, [actions]); - const handleSelection = (index, status, permissionId) => { - setHiddenActions((prevHiddenActions) => [...prevHiddenActions, index]); + const handleDeny = (index) => { + setDenied((prevDenied) => { + const newDeniedState = [...prevDenied]; + newDeniedState[index] = !newDeniedState[index]; + return newDeniedState; + }); + }; - const data = { - status: status, - user_feedback: reasons[index], - }; + const handleSelection = (index, status, permissionId) => { + setHiddenActions((prevHiddenActions) => [...prevHiddenActions, index]); - updatePermissions(permissionId, data).then((response) => { - if(response.status === 200) - setPendingPermissions(pendingPermission-1) - }); + const data = { + status: status, + user_feedback: reasons[index], }; - return ( - <> - {actions && actions.length > 0 ? ( -
- {actions.map((action, index) => { - if (action.status === 'PENDING' && !hiddenActions.includes(index)) { - return (); - } - else if(action.status === 'APPROVED' || action.status === 'REJECTED') { - return (); - } - return null; - })} -
- ) : ( -
- no-permissions - No Actions to Display! -
- )} - - ); + updatePermissions(permissionId, data).then((response) => { + if (response.status === 200) + setPendingPermissions(pendingPermission - 1) + }); + }; + + return ( + <> + {actions && actions.length > 0 ? ( +
+ {actions.map((action, index) => { + if (action.status === 'PENDING' && !hiddenActions.includes(index)) { + return (); + } else if (action.status === 'APPROVED' || action.status === 'REJECTED') { + return (); + } + return null; + })} +
+ ) : ( +
+ no-permissions + No Actions to Display! +
+ )} + + ); } \ No newline at end of file diff --git a/gui/pages/Content/Agents/ActivityFeed.js b/gui/pages/Content/Agents/ActivityFeed.js index affca1642..45a326459 100644 --- a/gui/pages/Content/Agents/ActivityFeed.js +++ b/gui/pages/Content/Agents/ActivityFeed.js @@ -37,7 +37,7 @@ export default function ActivityFeed({selectedRunId, selectedView, setFetchedDat useEffect(() => { loadingTextEffect('Thinking', setLoadingText, 250); - if (agent?.is_scheduled && !agent.is_running) { + if (agent?.is_scheduled && !agent?.is_running) { fetchDateTime(); } }, []); @@ -107,7 +107,7 @@ export default function ActivityFeed({selectedRunId, selectedView, setFetchedDat return (<>
- {agent?.is_scheduled && !agent.is_running && !selectedRunId ? + {agent?.is_scheduled && !agent?.is_running && !selectedRunId ?
github
@@ -158,7 +158,7 @@ export default function ActivityFeed({selectedRunId, selectedView, setFetchedDat
}
} - {!agent?.is_scheduled && !agent.is_running && feeds.length < 1 && + {!agent?.is_scheduled && !agent?.is_running && feeds.length < 1 &&
{ + setCol6ScrollTop(event.target.scrollTop); + }; useEffect(() => { getOrganisationConfig(organisationId, "model_api_key") @@ -652,9 +657,9 @@ export default function AgentCreate({ }, [internalId]) return (<> -
-
-
+
+
+
Create new agent
@@ -962,9 +967,9 @@ export default function AgentCreate({ src={!permissionDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'} alt="expand-icon"/>
-
+
{permissionDropdown && -
+
{permissions.map((permit, index) => (
handlePermissionSelect(index)} style={{padding: '12px 14px', maxWidth: '100%'}}> diff --git a/gui/pages/Content/Agents/AgentSchedule.js b/gui/pages/Content/Agents/AgentSchedule.js index 2185e2ffe..0272c2b85 100644 --- a/gui/pages/Content/Agents/AgentSchedule.js +++ b/gui/pages/Content/Agents/AgentSchedule.js @@ -103,14 +103,14 @@ export default function AgentSchedule({ } }, [internalId]) - const handleDateTimeChange = (momentObj) => { - const expiryDate = convertToGMT(momentObj); - setLocalStorageValue("agent_expiry_date_" + String(internalId), expiryDate, setExpiryDate); - }; + const handleDateTimeChange = (momentObj) => { + const expiryDate = convertToGMT(momentObj); + setLocalStorageValue("agent_expiry_date_" + String(internalId), expiryDate, setExpiryDate); + }; const handleTimeChange = (momentObj) => { const startTime = convertToGMT(momentObj); - setLocalStartTime( typeof momentObj === 'string' ? '' : momentObj.toDate()) + setLocalStartTime(typeof momentObj === 'string' ? '' : momentObj.toDate()) setLocalStorageValue("agent_start_time_" + String(internalId), startTime, setStartTime); }; @@ -159,9 +159,9 @@ export default function AgentSchedule({ const requestData = { "agent_id": agentId, "start_time": startTime, - "recurrence_interval": timeValue && isRecurring ? `${timeValue} ${timeUnit}` : null, - "expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1, - "expiry_date": expiryType === 'Specific Date' && isRecurring ? expiryDate : null, + "recurrence_interval": timeValue && isRecurring ? `${timeValue} ${timeUnit}` : null, + "expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1, + "expiry_date": expiryType === 'Specific Date' && isRecurring ? expiryDate : null, }; createAndScheduleRun(requestData) @@ -184,18 +184,17 @@ export default function AgentSchedule({ }; function checkTime() { - if(expiryDate === null){ + if (expiryDate === null) { return true; } let date1 = expiryDate; if (typeof expiryDate === 'string' && expiryDate.includes('/')) { date1 = moment(expiryDate, 'DD/MM/YYYY').toDate(); } else if (typeof expiryDate === 'string') { - date1 = moment.utc(expiryDate,'YYYY-MM-DD HH:mm:ss').local().toDate(); - } - else + date1 = moment.utc(expiryDate, 'YYYY-MM-DD HH:mm:ss').local().toDate(); + } else return - let date2 = moment.utc(startTime,'YYYY-MM-DD HH:mm:ss').local().toDate(); + let date2 = moment.utc(startTime, 'YYYY-MM-DD HH:mm:ss').local().toDate(); date1.setHours(0, 0, 0, 0); date2.setHours(0, 0, 0, 0); @@ -207,7 +206,7 @@ export default function AgentSchedule({ } function fetchUpdateSchedule() { - if(expiryType === 'Specific Date' && checkTime()){ + if (expiryType === 'Specific Date' && checkTime()) { toast.error('Expiry Date of agent is before Start Date') return; } @@ -215,8 +214,8 @@ export default function AgentSchedule({ "agent_id": agentId, "start_time": startTime, "recurrence_interval": timeValue && isRecurring ? `${timeValue} ${timeUnit}` : null, - "expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1, - "expiry_date": expiryType === 'Specific Date' && isRecurring ? (expiryDate && expiryDate.includes('/') ? convertToGMT(moment(expiryDate, 'DD/MM/YYYY').toDate()): expiryDate ) : null, + "expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1, + "expiry_date": expiryType === 'Specific Date' && isRecurring ? (expiryDate && expiryDate.includes('/') ? convertToGMT(moment(expiryDate, 'DD/MM/YYYY').toDate()) : expiryDate) : null, }; updateSchedule(requestData) @@ -339,7 +338,7 @@ export default function AgentSchedule({ justifyContent: 'space-between' }}>
The expiry date of the run - is {(new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) == "Invalid Da" ? expiryDate : (new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) }
+ is {(new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) == "Invalid Da" ? expiryDate : (new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10)}
setExpiryDate(null)}>Edit
diff --git a/gui/pages/Content/Agents/AgentTemplatesList.js b/gui/pages/Content/Agents/AgentTemplatesList.js index ce3845ef5..01cda96c7 100644 --- a/gui/pages/Content/Agents/AgentTemplatesList.js +++ b/gui/pages/Content/Agents/AgentTemplatesList.js @@ -5,92 +5,114 @@ import {fetchAgentTemplateListLocal} from "@/pages/api/DashboardService"; import AgentCreate from "@/pages/Content/Agents/AgentCreate"; import {setLocalStorageValue, openNewTab} from "@/utils/utils"; -export default function AgentTemplatesList({sendAgentData, selectedProjectId, fetchAgents, toolkits, organisationId, internalId}){ - const [agentTemplates, setAgentTemplates] = useState([]) - const [createAgentClicked, setCreateAgentClicked] = useState(false) - const [sendTemplate, setSendTemplate] = useState(null) +export default function AgentTemplatesList({ + sendAgentData, + selectedProjectId, + fetchAgents, + toolkits, + organisationId, + internalId + }) { + const [agentTemplates, setAgentTemplates] = useState([]) + const [createAgentClicked, setCreateAgentClicked] = useState(false) + const [sendTemplate, setSendTemplate] = useState(null) - useEffect(() => { - fetchAgentTemplateListLocal() - .then((response) => { - const data = response.data || []; - setAgentTemplates(data); - }) - .catch((error) => { - console.error('Error fetching agent templates:', error); - }); - }, []) + useEffect(() => { + fetchAgentTemplateListLocal() + .then((response) => { + const data = response.data || []; + setAgentTemplates(data); + }) + .catch((error) => { + console.error('Error fetching agent templates:', error); + }); + }, []) - useEffect(() => { - if(internalId !== null) { - const agent_create_click = localStorage.getItem("agent_create_click_" + String(internalId)) || 'false'; - if(agent_create_click) { - setCreateAgentClicked(JSON.parse(agent_create_click)); - } - } - }, [internalId]) - - function redirectToCreateAgent() { - setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked); + useEffect(() => { + if (internalId !== null) { + const agent_create_click = localStorage.getItem("agent_create_click_" + String(internalId)) || 'false'; + if (agent_create_click) { + setCreateAgentClicked(JSON.parse(agent_create_click)); + } } + }, [internalId]) - function openMarketplace() { - openNewTab(-4, "Marketplace", "Marketplace", false); - localStorage.setItem('marketplace_tab', 'market_agents'); - } + function redirectToCreateAgent() { + setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked); + } - function handleTemplateClick(item) { - setSendTemplate(item); - setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked); - } + function openMarketplace() { + openNewTab(-4, "Marketplace", "Marketplace", false); + localStorage.setItem('marketplace_tab', 'market_agents'); + } - return ( + function handleTemplateClick(item) { + setSendTemplate(item); + setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked); + } + + return ( +
+ {!createAgentClicked ?
- {!createAgentClicked ? -
-
-
+
+
Choose a template - + style={{ + fontWeight: '400', + verticalAlign: 'text-top', + marginLeft: '10px', + fontSize: '16px' + }}>Choose a template + +
+
+
+ {agentTemplates.length > 0 ?
+ {agentTemplates.map((item) => ( +
handleTemplateClick(item)}> +
+
{item.name}
+
{item.description}
+
+
+ ))} +
+
+
+
arrow-outward  Browse templates from marketplace
+
arrow-outward
+
+
+ SuperAGI marketplace offers a large selection of templates to choose from, so you are sure to find + one that is right for you! +
+
+
+
:
+
+ empty-templates +
Browse templates + from marketplace
-
- {agentTemplates.length > 0 ?
- {agentTemplates.map((item) => ( -
handleTemplateClick(item)}> -
-
{item.name}
-
{item.description}
-
-
- ))} -
-
-
-
arrow-outward  Browse templates from marketplace
-
arrow-outward
-
-
- SuperAGI marketplace offers a large selection of templates to choose from, so you are sure to find one that is right for you! -
-
-
-
:
-
- empty-templates -
Browse templates from marketplace
-
- -
-
-
} +
+
-
: } -
- ) +
+
} +
+
: } +
+ ) }; diff --git a/gui/pages/Content/Agents/AgentWorkspace.js b/gui/pages/Content/Agents/AgentWorkspace.js index 2b4e53ba6..fcd68cd19 100644 --- a/gui/pages/Content/Agents/AgentWorkspace.js +++ b/gui/pages/Content/Agents/AgentWorkspace.js @@ -17,18 +17,20 @@ import { getExecutionDetails, saveAgentAsTemplate, stopSchedule, - getDateTime + getDateTime, + deleteAgent } from "@/pages/api/DashboardService"; import {EventBus} from "@/utils/eventBus"; import 'moment-timezone'; import AgentSchedule from "@/pages/Content/Agents/AgentSchedule"; -export default function AgentWorkspace({agentId, selectedView, agents, internalId}) { +export default function AgentWorkspace({agentId, agentName, selectedView, agents, internalId}) { const [leftPanel, setLeftPanel] = useState('activity_feed') const [rightPanel, setRightPanel] = useState('') const [history, setHistory] = useState(true) const [selectedRun, setSelectedRun] = useState(null) const [runModal, setRunModal] = useState(false) + const [deleteModal, setDeleteModal] = useState(false) const [goals, setGoals] = useState(null) const [currentGoals, setCurrentGoals] = useState(null) const [runName, setRunName] = useState("New Run") @@ -149,11 +151,34 @@ export default function AgentWorkspace({agentId, selectedView, agents, internalI }); }; + const handleDeleteAgent = () => { + deleteAgent(agentId) + .then((response) => { + setDeleteModal(false); + if (response.status === 200) { + EventBus.emit('reFetchAgents', {}); + EventBus.emit('removeTab',{element: {id: agentId, name: agentName, contentType: "Agents", internalId: internalId}}) + toast.success("Agent Deleted Successfully", {autoClose: 1800}); + } + else{ + toast.error("Agent Could not be Deleted", { autoClose: 1800 }); + } + }) + .catch((error) => { + setDeleteModal(false); + toast.error("Agent Could not be Deleted", { autoClose: 1800 }); + console.error("Agent could not be deleted: ", error); + }); + } const closeRunModal = () => { setRunName("New Run"); setRunModal(false); }; + const closeDeleteModal = () => { + setDeleteModal(false); + } + const updateRunStatus = (status) => { const executionData = {"status": status}; @@ -341,10 +366,10 @@ export default function AgentWorkspace({agentId, selectedView, agents, internalI
  • Edit Schedule
  • Stop Schedule
  • ) : (
    - {agent && !agent.is_running && !agent.is_scheduled && + {agent && !agent?.is_running && !agent?.is_scheduled &&
  • setCreateModal(true)}>Schedule Run
  • }
    )} - +
  • setDeleteModal(true)}>Delete Agent
  • } @@ -442,6 +467,7 @@ export default function AgentWorkspace({agentId, selectedView, agents, internalI
    + {runModal && (
    Run agent name
    @@ -504,6 +530,27 @@ export default function AgentWorkspace({agentId, selectedView, agents, internalI
    )} + + {deleteModal && (
    +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    )} + +
    ); diff --git a/gui/pages/Content/Agents/Agents.js b/gui/pages/Content/Agents/Agents.js index 77c18c01d..3eb519868 100644 --- a/gui/pages/Content/Agents/Agents.js +++ b/gui/pages/Content/Agents/Agents.js @@ -25,7 +25,7 @@ export default function Agents({sendAgentData, agents}) { {agents.map((agent, index) => (
    sendAgentData(agent)}> - {agent.is_running && + {agent?.is_running &&
    active-icon
    }
    diff --git a/gui/pages/Content/Agents/Agents.module.css b/gui/pages/Content/Agents/Agents.module.css index f3f6b5c95..9ee9ba09c 100644 --- a/gui/pages/Content/Agents/Agents.module.css +++ b/gui/pages/Content/Agents/Agents.module.css @@ -1,7 +1,7 @@ .container { height: 100%; width: 100%; - padding: 0 8px; + padding: 0 0 0 8px; } .title_box { @@ -382,6 +382,43 @@ line-height: 12px; color: #FFFFFF; } +.delete_agent_modal_label +{ + color: #FFF; + font-size: 16px; + font-family: "Public Sans", sans-serif; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.delete_button +{ + display: flex; + padding: 0px 12px; + align-items: flex-start; + gap: 4px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.08); + background: #FFF; + color: #000; + font-size: 12px; + font-family: "Public Sans", sans-serif; + font-style: normal; + font-weight: 500; + line-height: normal; + +} + +.delete_modal_text +{ + color: #888; + font-size: 12px; + font-family: "Roboto Flex", sans-serif; + font-style: normal; + font-weight: 400; + line-height: normal; +} .rdtPicker input { display: flex !important; diff --git a/gui/pages/Content/Agents/ResourceList.js b/gui/pages/Content/Agents/ResourceList.js index d696289e6..288628eac 100644 --- a/gui/pages/Content/Agents/ResourceList.js +++ b/gui/pages/Content/Agents/ResourceList.js @@ -1,70 +1,78 @@ -import React, { useState, useMemo } from 'react'; +import React, {useState, useMemo} from 'react'; import styles from './Agents.module.css'; import Image from "next/image"; -import { downloadFile, downloadAllFiles, formatBytes, returnResourceIcon } from "@/utils/utils"; +import {downloadFile, downloadAllFiles, formatBytes, returnResourceIcon} from "@/utils/utils"; -export default function ResourceList({ files, channel, runs }) { - const [selectedRun, setSelectedRun] = useState(null); +export default function ResourceList({files, channel, runs}) { + const [selectedRunId, setSelectedRunId] = useState(null); const filesByRun = useMemo(() => runs.map(run => { const relatedFiles = files.filter(file => file.agent_execution_id === run.id); - return relatedFiles.length !== 0 && { "run": run, "files": relatedFiles }; + return relatedFiles.length !== 0 && {"run": run, "files": relatedFiles}; }).filter(Boolean), [files, runs]); - const downloadRunFiles = (run_id,name) => { + const downloadRunFiles = (run_id, name) => { const runFiles = files.filter(file => file.agent_execution_id === run_id); - runFiles.length !== 0 && downloadAllFiles(runFiles,name); + runFiles.length !== 0 && downloadAllFiles(runFiles, name); } - const isAnyFileWithAgentId = files.some(file => file.agent_execution_id !== null) + const isAnyFileWithAgentId = files.some(file => file.agent_execution_id !== null); - const File = ({ file, index }) => ( -
    downloadFile(file.id, file.name)} className={styles.history_box} style={{ background: '#272335', padding: '0px 10px', width: '49.5%' }}> -
    -
    file-icon
    -
    -
    {file.name}
    -
    {file.type.split("/")[1]}{file.size !== '' ? ` • ${formatBytes(file.size)}` : ''}
    -
    + const File = ({file, index}) => ( +
    downloadFile(file.id, file.name)} className={styles.history_box} + style={{background: '#272335', padding: '0px 10px', width: '49.5%'}}> +
    +
    file-icon
    +
    +
    {file.name}
    +
    {file.type.split("/")[1]}{file.size !== '' ? ` • ${formatBytes(file.size)}` : ''}
    +
    ) return ( -
    - {channel === 'output' && (!isAnyFileWithAgentId || files.length <= 0 ? -
    - no-permissions - No Output files! +
    + {channel === 'output' && (!isAnyFileWithAgentId || files.length <= 0 ? +
    + no-permissions + No Output files! +
    + : +
    + {filesByRun.map((filesRun, index) => ( +
    +
    setSelectedRunId(filesRun.run.id === selectedRunId ? null : filesRun.run.id)}> +
    + arrow + {filesRun.run.name} +
    bolt Run {index + 1}
    +
    + download_icon downloadRunFiles(filesRun.run.id, filesRun.run.name)}/>
    - : -
    - {filesByRun.map((filesRun, index) => ( -
    -
    setSelectedRun(filesRun.run === selectedRun ? null : filesRun.run)}> -
    - arrow - {filesRun.run.name} -
    bolt Run {index + 1}
    -
    - download_icon downloadRunFiles(filesRun.run.id,filesRun.run.name)} /> -
    - {selectedRun === filesRun.run && ( -
    - {/* eslint-disable-next-line react/jsx-key */} - {filesRun.files.map((file, index) => )} -
    - )} -
    - ))} -
    - )} + {selectedRunId === filesRun.run.id && ( +
    + {filesRun.files.map((file, index) => )} +
    + )} +
    + ))} +
    + )} - {channel === 'input' && -
    - {/* eslint-disable-next-line react/jsx-key */} - {files.map((file, index) => )} -
    } -
    + {channel === 'input' && +
    + {files.map((file, index) => )} +
    } +
    ) } \ No newline at end of file diff --git a/gui/pages/Content/Agents/ResourceManager.js b/gui/pages/Content/Agents/ResourceManager.js index 7fea25210..f15e4e4c2 100644 --- a/gui/pages/Content/Agents/ResourceManager.js +++ b/gui/pages/Content/Agents/ResourceManager.js @@ -73,10 +73,10 @@ export default function ResourceManager({agentId, runs}) { uploadFile(agentId, formData) .then((response) => { fetchResources(); - toast.success('Resource added successfully', { autoClose: 1800 }); + toast.success('Resource added successfully', {autoClose: 1800}); }) .catch((error) => { - toast.error(error, { autoClose: 1800 }); + toast.error(error, {autoClose: 1800}); console.error('Error uploading resource:', error); }); } @@ -96,28 +96,37 @@ export default function ResourceManager({agentId, runs}) { } return (<> -
    -
    -
    +
    +
    +
    -
    -
    -
    - {channel === 'input' &&
    -
    -

    + Choose or drop a file here

    -

    Supported file formats are txt, pdf, docx, epub, csv, pptx only

    -
    +
    + {channel === 'input' &&
    +
    +

    + Choose or drop a file here

    +

    Supported file formats are txt, pdf, + docx, epub, csv, pptx only

    +
    } diff --git a/gui/pages/Content/Agents/RunHistory.js b/gui/pages/Content/Agents/RunHistory.js index 57775a769..fbc924952 100644 --- a/gui/pages/Content/Agents/RunHistory.js +++ b/gui/pages/Content/Agents/RunHistory.js @@ -9,7 +9,7 @@ export default function RunHistory({runs, setHistory, selectedRunId, setSelected const resetRunStatus = (eventData) => { const updatedExecutions = runs.map((run) => { if (run.id === eventData.executionId) { - return { ...run, status: eventData.status }; + return {...run, status: eventData.status}; } return run; }); @@ -25,33 +25,37 @@ export default function RunHistory({runs, setHistory, selectedRunId, setSelected }); return (<> -
    +
    -
    -
    +
    +
    update-icon
    -
    Run history
    +
    Run history
    -
    -
    setHistory(false)}> +
    +
    setHistory(false)}> close-history-icon
    -
    - {runs && runs.map((run) => (
    setSelectedRun(run)} className={styles.history_box} style={selectedRunId === run.id ? {background:'#474255'} : {background:'#272335'}}> -
    -
    - {run.status === 'RUNNING' &&
    loading-icon
    } -
    {run.name}
    +
    + {runs && runs.map((run) => (
    setSelectedRun(run)} className={styles.history_box} + style={selectedRunId === run.id ? {background: '#474255'} : {background: '#272335'}}> +
    +
    + {run.status === 'RUNNING' && +
    loading-icon
    } +
    {run.name}
    {/*{run.notification_count > 0 &&
    {run.notification_count}
    }*/}
    -
    -
    +
    +
    call-icon
    @@ -59,7 +63,7 @@ export default function RunHistory({runs, setHistory, selectedRunId, setSelected {formatNumber(run?.num_of_calls || 0)} Calls
    -
    +
    schedule-icon
    diff --git a/gui/pages/Content/Agents/TaskQueue.js b/gui/pages/Content/Agents/TaskQueue.js index d56ab8213..691cbc60d 100644 --- a/gui/pages/Content/Agents/TaskQueue.js +++ b/gui/pages/Content/Agents/TaskQueue.js @@ -1,11 +1,11 @@ -import React, { useEffect, useState } from 'react'; +import React, {useEffect, useState} from 'react'; import styles from './Agents.module.css'; import 'react-toastify/dist/ReactToastify.css'; -import { getExecutionTasks } from '@/pages/api/DashboardService'; +import {getExecutionTasks} from '@/pages/api/DashboardService'; import Image from "next/image"; -export default function TaskQueue({ selectedRunId }) { - const [tasks, setTasks] = useState({ pending: [], completed: [] }); +export default function TaskQueue({selectedRunId}) { + const [tasks, setTasks] = useState({pending: [], completed: []}); useEffect(() => { fetchTasks(); @@ -26,34 +26,44 @@ export default function TaskQueue({ selectedRunId }) { return ( <> - {tasks.pending.length <= 0 && tasks.completed.length <= 0 ?
    - no-permissions - No Tasks found! -
    :
    - {tasks.pending.length > 0 &&
    Pending Tasks
    } - {tasks.pending.map((task, index) => ( -
    -
    -
    - loading-icon -
    -
    - {task.name} + {tasks.pending.length <= 0 && tasks.completed.length <= 0 ?
    + no-permissions + No Tasks found! +
    :
    + {tasks.pending.length > 0 &&
    Pending Tasks
    } + {tasks.pending.map((task, index) => ( +
    +
    +
    + loading-icon +
    +
    + {task.name} +
    -
    - ))} - {tasks.completed.length > 0 &&
    Completed Tasks
    } - {tasks.completed.map((task, index) => ( -
    -
    -
    - {task.name} + ))} + {tasks.completed.length > 0 &&
    Completed Tasks
    } + {tasks.completed.map((task, index) => ( +
    +
    +
    + {task.name} +
    -
    - ))} -
    } + ))} +
    } ); } diff --git a/gui/pages/Content/Marketplace/AgentTemplate.js b/gui/pages/Content/Marketplace/AgentTemplate.js index 61290d174..99393a110 100644 --- a/gui/pages/Content/Marketplace/AgentTemplate.js +++ b/gui/pages/Content/Marketplace/AgentTemplate.js @@ -11,172 +11,190 @@ import axios from 'axios'; import {loadingTextEffect} from "@/utils/utils"; export default function AgentTemplate({template, env}) { - const [tools, setTools] = useState([]) - const [agentType, setAgentType] = useState('') - const [templateModel, setTemplateModel] = useState('') - const [rightPanel, setRightPanel] = useState('overview') - const [goals, setGoals] = useState([]) - const [instructions, setInstructions] = useState([]) - const [installed, setInstalled] = useState('') - const [constraints, setConstraints] = useState([]) - const [isLoading, setIsLoading] = useState(true) - const [loadingText, setLoadingText] = useState("Loading Template Details"); + const [tools, setTools] = useState([]) + const [agentType, setAgentType] = useState('') + const [templateModel, setTemplateModel] = useState('') + const [rightPanel, setRightPanel] = useState('overview') + const [goals, setGoals] = useState([]) + const [instructions, setInstructions] = useState([]) + const [installed, setInstalled] = useState('') + const [constraints, setConstraints] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [loadingText, setLoadingText] = useState("Loading Template Details"); - useEffect(() => { - loadingTextEffect('Loading Template Details', setLoadingText, 500); - if(window.location.href.toLowerCase().includes('marketplace')) { - setInstalled('Sign in to install') - axios.get(`https://app.superagi.com/api/agent_templates/marketplace/template_details/${template.id}`) - .then((response) => { - const data = response.data || []; - setValues(data) - }) - .catch((error) => { - console.error('Error fetching agent templates:', error); - }); - } - else { - setInstalled(template && template.is_installed? 'Installed' : 'Install'); - fetchAgentTemplateConfig(template.id) - .then((response) => { - const data = response.data || []; - setValues(data) - }) - .catch((error) => { - console.error('Error fetching template details:', error); - }); - } - }, []); - - function setValues(data){ - setAgentType(data.configs.agent_type.value) - setTemplateModel(data.configs.model.value) - setGoals(data.configs.goal.value) - setConstraints(data.configs.constraints.value) - setTools(data.configs.tools.value) - setInstructions(data.configs.instructions ? data.configs.instructions.value : null); - setIsLoading(false) + useEffect(() => { + loadingTextEffect('Loading Template Details', setLoadingText, 500); + if (window.location.href.toLowerCase().includes('marketplace')) { + setInstalled('Sign in to install') + axios.get(`https://app.superagi.com/api/agent_templates/marketplace/template_details/${template.id}`) + .then((response) => { + const data = response.data || []; + setValues(data) + }) + .catch((error) => { + console.error('Error fetching agent templates:', error); + }); + } else { + setInstalled(template && template.is_installed ? 'Installed' : 'Install'); + fetchAgentTemplateConfig(template.id) + .then((response) => { + const data = response.data || []; + setValues(data) + }) + .catch((error) => { + console.error('Error fetching template details:', error); + }); } + }, []); - function handleInstallClick(){ - if (window.location.href.toLowerCase().includes('marketplace')) { - localStorage.setItem('agent_to_install', template.id); - if (env === 'PROD') { - window.open(`https://app.superagi.com/`, '_self'); - } else { - window.location.href = '/'; - } - return; - } - - if(template && template.is_installed) { - toast.error("Template is already installed", {autoClose: 1800}); - return; - } + function setValues(data) { + setAgentType(data.configs.agent_type.value) + setTemplateModel(data.configs.model.value) + setGoals(data.configs.goal.value) + setConstraints(data.configs.constraints.value) + setTools(data.configs.tools.value) + setInstructions(data.configs.instruction ? data.configs.instruction.value : null); + setIsLoading(false) + } - installAgentTemplate(template.id) - .then((response) => { - toast.success("Template installed", {autoClose: 1800}); - setInstalled('Installed'); - }) - .catch((error) => { - console.error('Error installing template:', error); - }); + function handleInstallClick() { + if (window.location.href.toLowerCase().includes('marketplace')) { + localStorage.setItem('agent_to_install', template.id); + if (env === 'PROD') { + window.open(`https://app.superagi.com/`, '_self'); + } else { + window.location.href = '/'; + } + return; } - function handleBackClick(){ - EventBus.emit('goToMarketplace', {}); + if (template && template.is_installed) { + toast.error("Template is already installed", {autoClose: 1800}); + return; } - return ( - <> -
    - {!isLoading ?
    -
    handleBackClick()}> - back_button - Back -
    -
    -
    - {template.name} - By SuperAGI is_verified {'\u00B7'} upload-icon - + installAgentTemplate(template.id) + .then((response) => { + toast.success("Template installed", {autoClose: 1800}); + setInstalled('Installed'); + }) + .catch((error) => { + console.error('Error installing template:', error); + }); + } + + function handleBackClick() { + EventBus.emit('goToMarketplace', {}); + } -
    + return ( + <> +
    + {!isLoading ?
    +
    handleBackClick()}> + back_button + Back +
    +
    +
    + {template.name} + By SuperAGI is_verified {'\u00B7'} upload-icon + - {template.description} +
    -
    + {template.description} - Tools -
    - {tools.map((tool, index) => (
    -
    {tool || ''}
    -
    ))} -

    - Agent Type -
    -
    {agentType}
    -

    - Model(s) -
    -
    {templateModel}
    -
    +
    -
    + Tools +
    + {tools.map((tool, index) => ( +
    +
    {tool || ''}
    +
    ))} +
    +
    + Agent Type +
    +
    {agentType}
    +
    +
    + Model(s) +
    +
    {templateModel}
    +
    - Last updated - {template.updated_at} -
    -
    -
    - {/*
    */} - {/*
    */} - {/*
    */} - {/* */} - {/* */} - {/*
    */} - {/*
    */} - {/*
    */} -
    -
    -
    - {goals.length} Goals

    - {goals.map((goal, index) => (
    -
    {index + 1}. {goal || ''}
    {index !== goals.length - 1} -
    ))} -
    -
    - {instructions && instructions.length>0 &&
    -
    - {instructions.length} Instructions

    - {instructions.map((instruction, index) => ( -
    -
    {index + 1}. {instruction || ''}
    - {index !== instructions.length - 1 &&
    } -
    ))} -
    -
    } -
    -
    - {constraints.length} Constraints

    - {constraints.map((goal, index) => (
    -
    {index + 1}. {goal || ''}
    {index !== constraints.length - 1} -
    ))} -
    -
    -
    +
    + + Last updated + {template.updated_at} +
    +
    +
    + {/*
    */} + {/*
    */} + {/*
    */} + {/* */} + {/* */} + {/*
    */} + {/*
    */} + {/*
    */} +
    +
    +
    + {goals.length} Goals

    + {goals.map((goal, index) => (
    +
    {index + 1}. {goal || ''}
    + {index !== goals.length - 1} +
    ))} +
    +
    + {instructions && instructions.length > 0 && +
    +
    + {instructions.length} Instructions

    + {instructions.map((instruction, index) => ( +
    +
    {index + 1}. {instruction || ''}
    +
    ))}
    -
    :
    -
    {loadingText}
    -
    } +
    } +
    +
    + {constraints.length} Constraints

    + {constraints.map((goal, index) => (
    +
    {index + 1}. {goal || ''}
    + {index !== constraints.length - 1} +
    ))} +
    +
    +
    - - - ); +
    :
    +
    {loadingText}
    +
    } +
    + + + ); } \ No newline at end of file diff --git a/gui/pages/Content/Marketplace/Embeddings.js b/gui/pages/Content/Marketplace/Embeddings.js deleted file mode 100644 index b0fa9b036..000000000 --- a/gui/pages/Content/Marketplace/Embeddings.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import Image from "next/image"; -import styles from './Market.module.css'; - -export default function Embeddings(){ - - const dummyData = []; - const numIterations = 6; - const itemsPerRow = 3; - - for (let i = 0; i < numIterations; i++) { - const dummyItem = { - id: i, - toolName: `Tool ${i + 1}`, - author: 'Google', - imageSrc: '/images/gmail.png', - altText: 'empty-state', - }; - - dummyData.push(dummyItem); - } - - return( -
    -
    Embeddings
    -
    Top featured
    - -
    - {dummyData.map((item, index) => ( -
    -
    - {item.altText} -
    -
    -
    {item.toolName}
    -
    by {item.author}
    -
    - {/* Add a line break after each row */} - {(index + 1) % itemsPerRow === 0 &&
    } -
    - ))} -
    - -
    New Tools
    - -
    - {dummyData.map((item, index) => ( -
    -
    - {item.altText} -
    -
    -
    {item.toolName}
    -
    by {item.author}
    -
    - {/* Add a line break after each row */} - {(index + 1) % itemsPerRow === 0 &&
    } -
    - ))} -
    -
    - ) -}; diff --git a/gui/pages/Content/Marketplace/Market.js b/gui/pages/Content/Marketplace/Market.js index b227529a9..9fae90193 100644 --- a/gui/pages/Content/Marketplace/Market.js +++ b/gui/pages/Content/Marketplace/Market.js @@ -1,7 +1,6 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, {useState, useEffect} from 'react'; import Image from "next/image"; import styles from './Market.module.css'; -import Embeddings from './Embeddings'; import MarketAgent from './MarketAgent'; import MarketTools from './MarketTools'; import ToolkitTemplate from './ToolkitTemplate'; @@ -10,87 +9,91 @@ import AgentTemplate from "@/pages/Content/Marketplace/AgentTemplate"; import {setLocalStorageValue, setLocalStorageArray} from "@/utils/utils"; export default function Market({env}) { - const [activeTab, setActiveTab] = useState('market_tools'); - const [itemClicked, setItemClicked] = useState(false); - const [templateData, setTemplateData] = useState([]); - const [detailType, setDetailType] = useState(''); + const [activeTab, setActiveTab] = useState('market_tools'); + const [itemClicked, setItemClicked] = useState(false); + const [templateData, setTemplateData] = useState([]); + const [detailType, setDetailType] = useState(''); - useEffect(() => { - const marketplace_tab = localStorage.getItem('marketplace_tab'); - if(marketplace_tab) { - setActiveTab(marketplace_tab); - } + useEffect(() => { + const marketplace_tab = localStorage.getItem('marketplace_tab'); + if (marketplace_tab) { + setActiveTab(marketplace_tab); + } - const item_clicked = localStorage.getItem('market_item_clicked'); - const detail_type = localStorage.getItem('market_detail_type'); - const market_item = localStorage.getItem('market_item'); + const item_clicked = localStorage.getItem('market_item_clicked'); + const detail_type = localStorage.getItem('market_detail_type'); + const market_item = localStorage.getItem('market_item'); - if(item_clicked) { - setItemClicked(JSON.parse(item_clicked)); - if(detail_type) { - setDetailType(item_clicked === 'true' ? detail_type : ''); - setTemplateData(item_clicked === 'true' ? JSON.parse(market_item) : []); - } - } + if (item_clicked) { + setItemClicked(JSON.parse(item_clicked)); + if (detail_type) { + setDetailType(item_clicked === 'true' ? detail_type : ''); + setTemplateData(item_clicked === 'true' ? JSON.parse(market_item) : []); + } + } - const handleOpenTemplateDetails = ({ item, contentType }) => { - setLocalStorageValue('market_detail_type', contentType, setDetailType); - setLocalStorageArray('market_item', item, setTemplateData); - setLocalStorageValue('market_item_clicked', true, setItemClicked); - }; - - const handleBackClick = ()=>{ - setLocalStorageValue('market_item_clicked', false, setItemClicked); - } + const handleOpenTemplateDetails = ({item, contentType}) => { + setLocalStorageValue('market_detail_type', contentType, setDetailType); + setLocalStorageArray('market_item', item, setTemplateData); + setLocalStorageValue('market_item_clicked', true, setItemClicked); + }; - EventBus.on('openTemplateDetails', handleOpenTemplateDetails); - EventBus.on('goToMarketplace', handleBackClick); + const handleBackClick = () => { + setLocalStorageValue('market_item_clicked', false, setItemClicked); + } - return () => { - EventBus.off('openTemplateDetails', handleOpenTemplateDetails); - EventBus.off('goToMarketplace', handleBackClick); - }; - }, []); + EventBus.on('openTemplateDetails', handleOpenTemplateDetails); + EventBus.on('goToMarketplace', handleBackClick); - const switchTab = (tab) => { - setActiveTab(tab); - localStorage.setItem('marketplace_tab', tab); + return () => { + EventBus.off('openTemplateDetails', handleOpenTemplateDetails); + EventBus.off('goToMarketplace', handleBackClick); }; + }, []); + + const switchTab = (tab) => { + setActiveTab(tab); + localStorage.setItem('marketplace_tab', tab); + }; return (
    - {!itemClicked ?
    -
    -
    + {!itemClicked ?
    +
    +
    -
    -
    - -
    - {/*
    */} - {/* */} - {/*
    */} -
    - -
    +
    +
    + +
    +
    +
    -
    - {activeTab === 'market_tools' && } - {activeTab === 'market_embeddings' && } - {activeTab === 'market_agents' && } -
    +
    +
    + {activeTab === 'market_tools' && } + {activeTab === 'market_agents' && } +
    -
    :
    - {detailType === 'agent_template' && } - {detailType === 'tool_template' && } -
    } +
    :
    + {detailType === 'agent_template' && } + {detailType === 'tool_template' && } +
    }
    ); }; diff --git a/gui/pages/Content/Marketplace/MarketAgent.js b/gui/pages/Content/Marketplace/MarketAgent.js index 425ac0008..477ca4b9f 100644 --- a/gui/pages/Content/Marketplace/MarketAgent.js +++ b/gui/pages/Content/Marketplace/MarketAgent.js @@ -6,64 +6,74 @@ import {EventBus} from "@/utils/eventBus"; import {loadingTextEffect} from "@/utils/utils"; import axios from 'axios'; -export default function MarketAgent(){ - const [agentTemplates, setAgentTemplates] = useState([]) - const [showMarketplace, setShowMarketplace] = useState(false); - const [isLoading, setIsLoading] = useState(true) - const [loadingText, setLoadingText] = useState("Loading Agent Templates"); +export default function MarketAgent() { + const [agentTemplates, setAgentTemplates] = useState([]) + const [showMarketplace, setShowMarketplace] = useState(false); + const [isLoading, setIsLoading] = useState(true) + const [loadingText, setLoadingText] = useState("Loading Agent Templates"); - useEffect(() => { - loadingTextEffect('Loading Agent Templates', setLoadingText, 500); + useEffect(() => { + loadingTextEffect('Loading Agent Templates', setLoadingText, 500); - if(window.location.href.toLowerCase().includes('marketplace')) { - setShowMarketplace(true); - axios.get('https://app.superagi.com/api/agent_templates/marketplace/list') - .then((response) => { - const data = response.data || []; - setAgentTemplates(data); - setIsLoading(false); - }) - .catch((error) => { - console.error('Error fetching agent templates:', error); - }); - } else { - fetchAgentTemplateList() - .then((response) => { - const data = response.data || []; - setAgentTemplates(data); - setIsLoading(false); - }) - .catch((error) => { - console.error('Error fetching agent templates:', error); - }); - } - }, []); - - function handleTemplateClick(item) { - const contentType = 'agent_template'; - EventBus.emit('openTemplateDetails', { item, contentType }); + if (window.location.href.toLowerCase().includes('marketplace')) { + setShowMarketplace(true); + axios.get('https://app.superagi.com/api/agent_templates/marketplace/list') + .then((response) => { + const data = response.data || []; + setAgentTemplates(data); + setIsLoading(false); + }) + .catch((error) => { + console.error('Error fetching agent templates:', error); + }); + } else { + fetchAgentTemplateList() + .then((response) => { + const data = response.data || []; + setAgentTemplates(data); + setIsLoading(false); + }) + .catch((error) => { + console.error('Error fetching agent templates:', error); + }); } + }, []); + + function handleTemplateClick(item) { + const contentType = 'agent_template'; + EventBus.emit('openTemplateDetails', {item, contentType}); + } - return ( -
    -
    - {!isLoading ?
    - {agentTemplates.length > 0 ?
    {agentTemplates.map((item, index) => ( -
    handleTemplateClick(item)}> -
    -
    {item.name}
    -
    by SuperAgi is_verified
    -
    {item.description}
    -
    -
    - ))}
    :
    - no-permissions - No Agent Templates found! -
    } -
    :
    -
    {loadingText}
    -
    } -
    + return ( +
    +
    + {!isLoading ?
    + {agentTemplates.length > 0 ?
    {agentTemplates.map((item, index) => ( +
    handleTemplateClick(item)}> +
    +
    {item.name}
    +
    by SuperAgi is_verified
    +
    {item.description}
    +
    +
    + ))}
    :
    + no-permissions + No Agent Templates found! +
    } +
    :
    +
    {loadingText}
    +
    } +
    - ) + ) }; diff --git a/gui/pages/Content/Marketplace/MarketTools.js b/gui/pages/Content/Marketplace/MarketTools.js index 7ed3583b5..6b7c800f1 100644 --- a/gui/pages/Content/Marketplace/MarketTools.js +++ b/gui/pages/Content/Marketplace/MarketTools.js @@ -6,7 +6,7 @@ import {EventBus} from "@/utils/eventBus"; import {loadingTextEffect} from "@/utils/utils"; import axios from 'axios'; -export default function MarketTools(){ +export default function MarketTools() { const [toolTemplates, setToolTemplates] = useState([]) const [showMarketplace, setShowMarketplace] = useState(false); const [isLoading, setIsLoading] = useState(true) @@ -15,7 +15,7 @@ export default function MarketTools(){ useEffect(() => { loadingTextEffect('Loading Toolkits', setLoadingText, 500); - if(window.location.href.toLowerCase().includes('marketplace')) { + if (window.location.href.toLowerCase().includes('marketplace')) { setShowMarketplace(true); axios.get('https://app.superagi.com/api/toolkits/marketplace/list/0') .then((response) => { @@ -41,30 +41,40 @@ export default function MarketTools(){ function handleTemplateClick(item) { const contentType = 'tool_template'; - EventBus.emit('openTemplateDetails', { item, contentType }); + EventBus.emit('openTemplateDetails', {item, contentType}); } return ( -
    -
    - {!isLoading ?
    - {toolTemplates.length > 0 ?
    {toolTemplates.map((item, index) => ( -
    handleTemplateClick(item)}> -
    - {/*tool-icon*/} -
    {item.name}
    -
    by SuperAgi is_verified
    -
    {item.description}
    -
    -
    - ))}
    :
    - no-permissions - No Tools found! -
    } -
    :
    -
    {loadingText}
    +
    +
    + {!isLoading ?
    + {toolTemplates.length > 0 ?
    {toolTemplates.map((item, index) => ( +
    handleTemplateClick(item)}> +
    + {/*tool-icon*/} +
    {item.name}
    +
    by SuperAgi is_verified
    +
    {item.description}
    +
    +
    + ))}
    :
    + no-permissions + No Tools found!
    } -
    +
    :
    +
    {loadingText}
    +
    }
    +
    ) }; diff --git a/gui/pages/Content/Marketplace/MarketplacePublic.js b/gui/pages/Content/Marketplace/MarketplacePublic.js index a83ea7ee9..4edcf74be 100644 --- a/gui/pages/Content/Marketplace/MarketplacePublic.js +++ b/gui/pages/Content/Marketplace/MarketplacePublic.js @@ -13,16 +13,20 @@ export default function MarketplacePublic({env}) { }; return ( -
    -
    -
    super-agi-logo -
    -
     Marketplace
    -
    -
    - -
    -
    +
    +
    +
    super-agi-logo +
    +
     Marketplace
    +
    +
    + +
    +
    diff --git a/gui/pages/Content/Marketplace/ToolkitTemplate.js b/gui/pages/Content/Marketplace/ToolkitTemplate.js index 123ccf969..242606570 100644 --- a/gui/pages/Content/Marketplace/ToolkitTemplate.js +++ b/gui/pages/Content/Marketplace/ToolkitTemplate.js @@ -12,154 +12,162 @@ import axios from 'axios'; import {returnToolkitIcon} from "@/utils/utils"; export default function ToolkitTemplate({template, env}) { - const [rightPanel, setRightPanel] = useState('tool_view') - const [installed, setInstalled] = useState('') - const [markdownContent, setMarkdownContent] = useState(''); + const [rightPanel, setRightPanel] = useState('tool_view') + const [installed, setInstalled] = useState('') + const [markdownContent, setMarkdownContent] = useState(''); - useEffect(() => { - setInstalled(template && template.is_installed ? 'Installed' : 'Install'); - if (window.location.href.toLowerCase().includes('marketplace')) { - setInstalled('Sign in to install'); - axios.get(`https://app.superagi.com/api/toolkits/marketplace/readme/${template.name}`) - .then((response) => { - setMarkdownContent(response.data || ''); - setRightPanel(response.data ? 'overview' : 'tool_view'); - }) - .catch((error) => { - setRightPanel('tool_view'); - console.error('Error fetching template details:', error); - }); - } else { - fetchToolTemplateOverview(template.name) - .then((response) => { - setMarkdownContent(response.data || ''); - setRightPanel(response.data ? 'overview' : 'tool_view'); - }) - .catch((error) => { - setRightPanel('tool_view'); - console.error('Error fetching template details:', error); - }); - } - }, []); - - function handleInstallClick() { - if (window.location.href.toLowerCase().includes('marketplace')) { - localStorage.setItem('toolkit_to_install', template.name); - if (env === 'PROD') { - window.open(`https://app.superagi.com/`, '_self'); - } else { - window.location.href = '/'; - } - return; - } - - if (template && template.is_installed) { - toast.error("Template is already installed", {autoClose: 1800}); - return; - } + useEffect(() => { + setInstalled(template && template.is_installed ? 'Installed' : 'Install'); + if (window.location.href.toLowerCase().includes('marketplace')) { + setInstalled('Sign in to install'); + axios.get(`https://app.superagi.com/api/toolkits/marketplace/readme/${template.name}`) + .then((response) => { + setMarkdownContent(response.data || ''); + setRightPanel(response.data ? 'overview' : 'tool_view'); + }) + .catch((error) => { + setRightPanel('tool_view'); + console.error('Error fetching template details:', error); + }); + } else { + fetchToolTemplateOverview(template.name) + .then((response) => { + setMarkdownContent(response.data || ''); + setRightPanel(response.data ? 'overview' : 'tool_view'); + }) + .catch((error) => { + setRightPanel('tool_view'); + console.error('Error fetching template details:', error); + }); + } + }, []); - installToolkitTemplate(template.name) - .then((response) => { - toast.success("Template installed", {autoClose: 1800}); - setInstalled('Installed'); - }) - .catch((error) => { - console.error('Error installing template:', error); - }); + function handleInstallClick() { + if (window.location.href.toLowerCase().includes('marketplace')) { + localStorage.setItem('toolkit_to_install', template.name); + if (env === 'PROD') { + window.open(`https://app.superagi.com/`, '_self'); + } else { + window.location.href = '/'; + } + return; } - function handleBackClick() { - EventBus.emit('goToMarketplace', {}); + if (template && template.is_installed) { + toast.error("Template is already installed", {autoClose: 1800}); + return; } - return ( - <> + installToolkitTemplate(template.name) + .then((response) => { + toast.success("Template installed", {autoClose: 1800}); + setInstalled('Installed'); + }) + .catch((error) => { + console.error('Error installing template:', error); + }); + } + + function handleBackClick() { + EventBus.emit('goToMarketplace', {}); + } + + return ( + <> +
    +
    +
    handleBackClick()}> + back_button + Back +
    +
    +
    +
    + tool-icon +
    + {template.name} + By SuperAGI is_verified {'\u00B7'} upload-icon + +
    + {template.description} +
    + Last updated + {template.updated_at} +
    +
    +
    -
    -
    handleBackClick()}> - back_button - Back -
    -
    -
    -
    - tool-icon -
    - {template.name} - By SuperAGI is_verified {'\u00B7'} upload-icon - -
    - {template.description} -
    - Last updated - {template.updated_at} -
    -
    -
    -
    -
    -
    -
    - {markdownContent && markdownContent !== '' && } - -
    -
    -
    - {rightPanel === 'overview' && -
    -
    - {markdownContent && markdownContent !== '' ? {markdownContent} : -
    - no-permissions - No Overview to display! -
    - } -
    -
    } - {rightPanel === 'tool_view' &&
    -
    - {template.tools.map((value, index) => ( -
    - {value.name}
    - {value.description} -
    - ))} -
    -
    } -
    +
    +
    +
    + {markdownContent && markdownContent !== '' && + } + +
    +
    +
    + {rightPanel === 'overview' && +
    +
    + {markdownContent && markdownContent !== '' ? {markdownContent} : +
    + no-permissions + No Overview to display! +
    + } +
    +
    } + {rightPanel === 'tool_view' &&
    +
    + {template.tools.map((value, index) => ( +
    + {value.name}
    + {value.description}
    + ))}
    +
    }
    - - - ); +
    +
    +
    + + + ); } \ No newline at end of file diff --git a/gui/pages/Content/Toolkits/AddTool.js b/gui/pages/Content/Toolkits/AddTool.js index 6c5a8607c..38c2b2336 100644 --- a/gui/pages/Content/Toolkits/AddTool.js +++ b/gui/pages/Content/Toolkits/AddTool.js @@ -27,7 +27,7 @@ export default function AddTool({internalId}) { addTool(toolData) .then((response) => { if (response.status === 200) { - toast.success('Tool will be installed in a while', { autoClose: 1800 }); + toast.success('Tool will be installed in a while', {autoClose: 1800}); setAddClickable(true); setLocalStorageValue("tool_github_" + String(internalId), '', setGithubURl); } @@ -35,10 +35,10 @@ export default function AddTool({internalId}) { .catch((error) => { if (error.response && error.response.status === 400) { console.error('Error adding tool:', error); - toast.error('Invalid Github URL', { autoClose: 1800 }); + toast.error('Invalid Github URL', {autoClose: 1800}); } else { console.error('Error adding tool:', error); - toast.error(error.message, { autoClose: 1800 }); + toast.error(error.message, {autoClose: 1800}); } setAddClickable(true); setLocalStorageValue("tool_github_" + String(internalId), '', setGithubURl); @@ -47,9 +47,9 @@ export default function AddTool({internalId}) { }; useEffect(() => { - if(internalId !== null) { + if (internalId !== null) { const github_url = localStorage.getItem("tool_github_" + String(internalId)) - if(github_url) { + if (github_url) { setGithubURl(github_url); } } @@ -58,18 +58,22 @@ export default function AddTool({internalId}) { return (<>
    -
    +
    Add a new tool
    -
    +

    - - + +
    - +
    diff --git a/gui/pages/Content/Toolkits/ToolkitWorkspace.js b/gui/pages/Content/Toolkits/ToolkitWorkspace.js index e0aa12dc4..99fc711e7 100644 --- a/gui/pages/Content/Toolkits/ToolkitWorkspace.js +++ b/gui/pages/Content/Toolkits/ToolkitWorkspace.js @@ -1,154 +1,181 @@ import React, {useEffect, useState} from 'react'; import Image from 'next/image'; import {ToastContainer, toast} from 'react-toastify'; -import {updateToolConfig, getToolConfig, authenticateGoogleCred, authenticateTwitterCred} from "@/pages/api/DashboardService"; +import { + updateToolConfig, + getToolConfig, + authenticateGoogleCred, + authenticateTwitterCred +} from "@/pages/api/DashboardService"; import styles from './Tool.module.css'; import {setLocalStorageValue, setLocalStorageArray, returnToolkitIcon, convertToTitleCase} from "@/utils/utils"; -export default function ToolkitWorkspace({toolkitDetails, internalId}){ - const [activeTab,setActiveTab] = useState('configuration') - const [showDescription,setShowDescription] = useState(false) - const [apiConfigs, setApiConfigs] = useState([]); - const [toolsIncluded, setToolsIncluded] = useState([]); - const [loading, setLoading] = useState(true); - const authenticateToolkits = ['Google Calendar Toolkit', 'Twitter Toolkit']; - - let handleKeyChange = (event, index) => { - const updatedData = [...apiConfigs]; - updatedData[index].value = event.target.value; - setLocalStorageArray('api_configs_' + String(internalId), updatedData, setApiConfigs); - }; - - function getGoogleToken(client_data){ - const client_id = client_data.client_id - const scope = 'https://www.googleapis.com/auth/calendar'; - const redirect_uri = 'http://localhost:3000/api/google/oauth-tokens'; - window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${client_id}&redirect_uri=${redirect_uri}&access_type=offline&response_type=code&scope=${scope}`; - } - - function getTwitterToken(oauth_data){ - window.location.href = `https://api.twitter.com/oauth/authenticate?oauth_token=${oauth_data.oauth_token}` - } +export default function ToolkitWorkspace({toolkitDetails, internalId}) { + const [activeTab, setActiveTab] = useState('configuration') + const [showDescription, setShowDescription] = useState(false) + const [apiConfigs, setApiConfigs] = useState([]); + const [toolsIncluded, setToolsIncluded] = useState([]); + const [loading, setLoading] = useState(true); + const authenticateToolkits = ['Google Calendar Toolkit', 'Twitter Toolkit']; + + let handleKeyChange = (event, index) => { + const updatedData = [...apiConfigs]; + updatedData[index].value = event.target.value; + setLocalStorageArray('api_configs_' + String(internalId), updatedData, setApiConfigs); + }; + + function getGoogleToken(client_data) { + const client_id = client_data.client_id + const scope = 'https://www.googleapis.com/auth/calendar'; + const redirect_uri = 'http://localhost:3000/api/google/oauth-tokens'; + window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${client_id}&redirect_uri=${redirect_uri}&access_type=offline&response_type=code&scope=${scope}`; + } + + function getTwitterToken(oauth_data) { + window.location.href = `https://api.twitter.com/oauth/authenticate?oauth_token=${oauth_data.oauth_token}` + } - useEffect(() => { - if(toolkitDetails !== null) { - if (toolkitDetails.tools) { - setToolsIncluded(toolkitDetails.tools); - } - - getToolConfig(toolkitDetails.name) - .then((response) => { - const localStoredConfigs = localStorage.getItem('api_configs_' + String(internalId)); - const apiConfigs = response.data || []; - setApiConfigs(localStoredConfigs ? JSON.parse(localStoredConfigs) : apiConfigs); - }) - .catch((errPor) => { - console.log('Error fetching API data:', error); - }) - .finally(() => { - setLoading(false); - }); + useEffect(() => { + if (toolkitDetails !== null) { + if (toolkitDetails.tools) { + setToolsIncluded(toolkitDetails.tools); } - }, [toolkitDetails]); - - const handleUpdateChanges = async () => { - const updatedConfigData = apiConfigs.map((config) => ({ - key: config.key, - value: config.value, - })); - - updateToolConfig(toolkitDetails.name, updatedConfigData) + + getToolConfig(toolkitDetails.name) .then((response) => { - toast.success('Toolkit configuration updated', {autoClose: 1800}); + const localStoredConfigs = localStorage.getItem('api_configs_' + String(internalId)); + const apiConfigs = response.data || []; + setApiConfigs(localStoredConfigs ? JSON.parse(localStoredConfigs) : apiConfigs); }) - .catch((error) => { - toast.error('Unable to update Toolkit configuration', {autoClose: 1800}); - console.error('Error updating tool config:', error); + .catch((errPor) => { + console.log('Error fetching API data:', error); + }) + .finally(() => { + setLoading(false); }); - }; + } + }, [toolkitDetails]); + + const handleUpdateChanges = async () => { + const updatedConfigData = apiConfigs.map((config) => ({ + key: config.key, + value: config.value, + })); - const handleAuthenticateClick = async (toolkitName) => { - if (toolkitName === "Google Calendar Toolkit"){ + updateToolConfig(toolkitDetails.name, updatedConfigData) + .then((response) => { + toast.success('Toolkit configuration updated', {autoClose: 1800}); + }) + .catch((error) => { + toast.error('Unable to update Toolkit configuration', {autoClose: 1800}); + console.error('Error updating tool config:', error); + }); + }; + + const handleAuthenticateClick = async (toolkitName) => { + if (toolkitName === "Google Calendar Toolkit") { authenticateGoogleCred(toolkitDetails.id) .then((response) => { localStorage.setItem("google_calendar_toolkit_id", toolkitDetails.id) - getGoogleToken(response.data); + getGoogleToken(response.data); }) .catch((error) => { console.error('Error fetching data:', error); }); - }else if(toolkitName === "Twitter Toolkit"){ - authenticateTwitterCred(toolkitDetails.id) - .then((response) => { - localStorage.setItem("twitter_toolkit_id", toolkitDetails.id) - getTwitterToken(response.data); - }) - .catch((error) => { - console.error('Error fetching data: ', error); - }); + } else if (toolkitName === "Twitter Toolkit") { + authenticateTwitterCred(toolkitDetails.id) + .then((response) => { + localStorage.setItem("twitter_toolkit_id", toolkitDetails.id) + getTwitterToken(response.data); + }) + .catch((error) => { + console.error('Error fetching data: ', error); + }); } - }; - - useEffect(() => { - if(internalId !== null) { - const active_tab = localStorage.getItem('toolkit_tab_' + String(internalId)); - if(active_tab) { - setActiveTab(active_tab); - } + }; + + useEffect(() => { + if (internalId !== null) { + const active_tab = localStorage.getItem('toolkit_tab_' + String(internalId)); + if (active_tab) { + setActiveTab(active_tab); } - }, [internalId]); - - return (<> -
    -
    -
    -
    -
    + } + }, [internalId]); + + return (<> +
    +
    +
    +
    +
    - toolkit-icon + toolkit-icon
    -
    -
    -
    {toolkitDetails.name}
    -
    +
    +
    +
    {toolkitDetails.name}
    +
    {`${showDescription ? toolkitDetails.description : toolkitDetails.description.slice(0, 70)}`} - {toolkitDetails.description.length > 70 && setShowDescription(!showDescription)}> + {toolkitDetails.description.length > 70 && + setShowDescription(!showDescription)}> {showDescription ? '...less' : '...more'} }
    -
    -
    setLocalStorageValue('toolkit_tab_' + String(internalId), 'configuration', setActiveTab)} style={activeTab === 'configuration' ? { background: '#454254'} : { background: 'transparent'}}> +
    +
    setLocalStorageValue('toolkit_tab_' + String(internalId), 'configuration', setActiveTab)} + style={activeTab === 'configuration' ? {background: '#454254'} : {background: 'transparent'}}>
    Configuration
    -
    setLocalStorageValue('toolkit_tab_' + String(internalId), 'tools_included', setActiveTab)} style={activeTab === 'tools_included' ? { background: '#454254' } : { background: 'transparent' }}> +
    setLocalStorageValue('toolkit_tab_' + String(internalId), 'tools_included', setActiveTab)} + style={activeTab === 'tools_included' ? {background: '#454254'} : {background: 'transparent'}}>
    Tools Included
    {!loading && activeTab === 'configuration' &&
    {apiConfigs.length > 0 ? (apiConfigs.map((config, index) => (
    -
    - +
    +
    - handleKeyChange(event, index)}/> + handleKeyChange(event, index)}/>
    - ))) : (
    - no-permissions + ))) : (
    + no-permissions No Keys found!
    )} {apiConfigs.length > 0 && ( -
    +
    {authenticateToolkits.includes(toolkitDetails.name) && - + }
    -
    - +
    +
    )}
    } @@ -156,18 +183,18 @@ export default function ToolkitWorkspace({toolkitDetails, internalId}){ {toolsIncluded.map((tool, index) => (
    -
    {tool.name}
    -
    {tool.description}
    +
    {tool.name}
    +
    {tool.description}
    ))}
    }
    -
    -
    - - ); +
    +
    + + ); } diff --git a/gui/pages/Content/Toolkits/Toolkits.js b/gui/pages/Content/Toolkits/Toolkits.js index 1c4e26855..6a9d31646 100644 --- a/gui/pages/Content/Toolkits/Toolkits.js +++ b/gui/pages/Content/Toolkits/Toolkits.js @@ -5,7 +5,7 @@ import styles1 from '../Agents/Agents.module.css' import 'react-toastify/dist/ReactToastify.css'; import {createInternalId, returnToolkitIcon} from "@/utils/utils"; -export default function Toolkits({ sendToolkitData, toolkits, env }) { +export default function Toolkits({sendToolkitData, toolkits, env}) { const excludedToolkits = ["Thinking Toolkit", "Human Input Toolkit", "Resource Toolkit"]; return ( @@ -14,32 +14,39 @@ export default function Toolkits({ sendToolkitData, toolkits, env }) {

    Toolkits

    - {env !== 'PROD' &&
    -
    } {toolkits && toolkits.length > 0 ? ( -
    +
    {toolkits.map((tool, index) => tool.name !== null && !excludedToolkits.includes(tool.name) && ( -
    sendToolkitData(tool)}> -
    -
    -
    -
    - tool-icon -
    -
    -
    {tool.name}
    -
    by SuperAGI
    +
    sendToolkitData(tool)}> +
    +
    +
    +
    + tool-icon +
    +
    +
    {tool.name}
    +
    by SuperAGI
    +
    -
    ) )}
    diff --git a/gui/pages/Dashboard/Content.js b/gui/pages/Dashboard/Content.js index 2fd7f6c80..fd02188f5 100644 --- a/gui/pages/Dashboard/Content.js +++ b/gui/pages/Dashboard/Content.js @@ -7,8 +7,14 @@ import Settings from "./Settings/Settings"; import styles from './Dashboard.module.css'; import ApmDashboard from "../Content/APM/ApmDashboard"; import Image from "next/image"; -import { EventBus } from "@/utils/eventBus"; -import {getAgents, getToolKit, getLastActiveAgent, sendTwitterCreds, sendGoogleCreds} from "@/pages/api/DashboardService"; +import {EventBus} from "@/utils/eventBus"; +import { + getAgents, + getToolKit, + getLastActiveAgent, + sendTwitterCreds, + sendGoogleCreds +} from "@/pages/api/DashboardService"; import Market from "../Content/Marketplace/Market"; import AgentTemplatesList from '../Content/Agents/AgentTemplatesList'; import {useRouter} from 'next/router'; @@ -114,7 +120,7 @@ export default function Content({env, selectedView, selectedProjectId, organisat const selectTab = (element, index) => { setSelectedTab(index); - if(element.contentType === "Toolkits") { + if (element.contentType === "Toolkits") { setToolkitDetails(element); } }; @@ -136,30 +142,32 @@ export default function Content({env, selectedView, selectedProjectId, organisat const queryParams = router.asPath.split('?')[1]; const parsedParams = querystring.parse(queryParams); parsedParams["toolkit_id"] = toolkitDetails.toolkit_id; - if (window.location.href.indexOf("twitter_creds") > -1){ + if (window.location.href.indexOf("twitter_creds") > -1) { const toolkit_id = localStorage.getItem("twitter_toolkit_id") || null; parsedParams["toolkit_id"] = toolkit_id; const params = JSON.stringify(parsedParams) sendTwitterCreds(params) - .then((response) => { - console.log("Authentication completed successfully"); - }) - .catch((error) => { - console.error("Error fetching data: ",error); - }) - }; - if (window.location.href.indexOf("google_calendar_creds") > -1){ + .then((response) => { + console.log("Authentication completed successfully"); + }) + .catch((error) => { + console.error("Error fetching data: ", error); + }) + } + ; + if (window.location.href.indexOf("google_calendar_creds") > -1) { const toolkit_id = localStorage.getItem("google_calendar_toolkit_id") || null; var data = Object.keys(parsedParams)[0]; var params = JSON.parse(data) sendGoogleCreds(params, toolkit_id) - .then((response) => { - console.log("Authentication completed successfully"); - }) - .catch((error) => { - console.error("Error fetching data: ", error); - }) - }; + .then((response) => { + console.log("Authentication completed successfully"); + }) + .catch((error) => { + console.error("Error fetching data: ", error); + }) + } + ; }, [selectedTab]); useEffect(() => { @@ -231,8 +239,9 @@ export default function Content({env, selectedView, selectedProjectId, organisat }, []); return (<> -
    - {(selectedView === 'agents' || selectedView === 'toolkits') &&
    +
    + {(selectedView === 'agents' || selectedView === 'toolkits') && +
    {selectedView === 'agents' &&
    } {selectedView === 'toolkits' &&
    }
    } @@ -283,8 +292,9 @@ export default function Content({env, selectedView, selectedProjectId, organisat
    -
    :
    -
    +
    :
    +
    {tabs.map((tab, index) => (
    marketplace-icon
    } - {tab.contentType === 'APM' &&
    apm-icon
    } + {tab.contentType === 'APM' && +
    apm-icon
    }
    {tab.name}
    { @@ -318,13 +330,14 @@ export default function Content({env, selectedView, selectedProjectId, organisat ))}
    -
    0 ? {backgroundColor:'#2F2C40',overflowX:'hidden'} : {}}> -
    +
    0 ? {backgroundColor: '#2F2C40', overflowX: 'hidden'} : {}}> +
    {tabs.map((tab, index) => ( -
    +
    {selectedTab === index &&
    {tab.contentType === 'Agents' && - } {tab.contentType === 'Toolkits' && } @@ -335,7 +348,7 @@ export default function Content({env, selectedView, selectedProjectId, organisat } - {tab.contentType === 'APM' && } + {tab.contentType === 'APM' && }
    }
    ))} diff --git a/gui/pages/Dashboard/Settings/Settings.js b/gui/pages/Dashboard/Settings/Settings.js index 45a495edb..291feea85 100644 --- a/gui/pages/Dashboard/Settings/Settings.js +++ b/gui/pages/Dashboard/Settings/Settings.js @@ -17,22 +17,22 @@ export default function Settings({organisationId}) { function getKey(key) { getOrganisationConfig(organisationId, key) - .then((response) => { - setKey(response.data.value); - }) - .catch((error) => { - console.error('Error fetching project:', error); - }); + .then((response) => { + setKey(response.data.value); + }) + .catch((error) => { + console.error('Error fetching project:', error); + }); } function getSource(key) { getOrganisationConfig(organisationId, key) - .then((response) => { - setSource(response.data.value); - }) - .catch((error) => { - console.error('Error fetching project:', error); - }); + .then((response) => { + setSource(response.data.value); + }) + .catch((error) => { + console.error('Error fetching project:', error); + }); } useEffect(() => { @@ -54,14 +54,14 @@ export default function Settings({organisationId}) { function updateKey(key, value) { const configData = {"key": key, "value": value}; updateOrganisationConfig(organisationId, configData) - .then((response) => { - getKey("model_api_key"); - EventBus.emit("keySet", {}); - toast.success("Settings updated", {autoClose: 1800}); - }) - .catch((error) => { - console.error('Error fetching project:', error); - }); + .then((response) => { + getKey("model_api_key"); + EventBus.emit("keySet", {}); + toast.success("Settings updated", {autoClose: 1800}); + }) + .catch((error) => { + console.error('Error fetching project:', error); + }); } const handleModelApiKey = (event) => { @@ -73,10 +73,6 @@ export default function Settings({organisationId}) { setSourceDropdown(false); }; - const preventDefault = (e) => { - e.stopPropagation(); - }; - const saveSettings = () => { if (modelApiKey === null || modelApiKey.replace(/\s/g, '') === '') { toast.error("API key is empty", {autoClose: 1800}); @@ -101,28 +97,35 @@ export default function Settings({organisationId}) { return (<>
    -
    +
    Settings
    - -
    -
    setSourceDropdown(!sourceDropdown)} style={{width:'100%'}}> - {source}expand-icon -
    -
    - {sourceDropdown &&
    - {sources.map((source, index) => (
    handleSourceSelect(index)} style={{padding:'12px 14px',maxWidth:'100%'}}> + +
    +
    setSourceDropdown(!sourceDropdown)} + style={{width: '100%'}}> + {source}expand-icon +
    +
    + {sourceDropdown &&
    + {sources.map((source, index) => ( +
    handleSourceSelect(index)} + style={{padding: '12px 14px', maxWidth: '100%'}}> {source}
    ))} -
    } -
    +
    }
    -

    +
    +
    +
    - +
    {/*
    */} {/* */} @@ -131,8 +134,9 @@ export default function Settings({organisationId}) { {/* */} {/*
    */} {/*
    */} -
    -
    - By continuing, you agree to Super AGI’s Terms of Service and Privacy Policy, and to receive important updates. + By continuing, you agree to Super AGI’s Terms of Service and Privacy Policy, and to receive important + updates.
    -
    :
    -
    {loadingText}
    +
    :
    +
    {loadingText}
    }
    -
    ) : true } +
    ) : true}
    ); } diff --git a/gui/pages/api/DashboardService.js b/gui/pages/api/DashboardService.js index cf976ab64..8adb26bc7 100644 --- a/gui/pages/api/DashboardService.js +++ b/gui/pages/api/DashboardService.js @@ -131,6 +131,10 @@ export const updatePermissions = (permissionId, data) => { return api.put(`/agentexecutionpermissions/update/status/${permissionId}`, data) } +export const deleteAgent = (agentId) => { + return api.put(`/agents/delete/${agentId}`) +} + export const authenticateGoogleCred = (toolKitId) => { return api.get(`/google/get_google_creds/toolkit_id/${toolKitId}`); } @@ -186,15 +190,19 @@ export const getDateTime = (agentId) => { export const getMetrics = () => { return api.get(`/analytics/metrics`) } + export const getAllAgents = () => { return api.get(`/analytics/agents/all`) } + export const getAgentRuns = (agent_id) => { return api.get(`analytics/agents/${agent_id}`); } + export const getActiveRuns = () => { return api.get(`analytics/runs/active`); } + export const getToolsUsage = () => { return api.get(`analytics/tools/used`); } \ No newline at end of file diff --git a/gui/public/images/duckduckgo_icon.png b/gui/public/images/duckduckgo_icon.png new file mode 100644 index 000000000..a98719646 Binary files /dev/null and b/gui/public/images/duckduckgo_icon.png differ diff --git a/gui/utils/utils.js b/gui/utils/utils.js index e3781c9e7..2375d56cc 100644 --- a/gui/utils/utils.js +++ b/gui/utils/utils.js @@ -1,5 +1,5 @@ -import { formatDistanceToNow, parseISO } from 'date-fns'; -import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz'; +import {formatDistanceToNow} from 'date-fns'; +import {utcToZonedTime} from 'date-fns-tz'; import {baseUrl} from "@/pages/api/apiConfig"; import {EventBus} from "@/utils/eventBus"; import JSZip from "jszip"; @@ -48,6 +48,7 @@ export const formatNumber = (number) => { return scaledNumber.toFixed(1) + suffix; }; + export const formatTime = (lastExecutionTime) => { try { const parsedTime = new Date(lastExecutionTime + 'Z'); // append 'Z' to indicate UTC @@ -62,15 +63,16 @@ export const formatTime = (lastExecutionTime) => { addSuffix: true, includeSeconds: true }).replace(/about\s/, '') - .replace(/minutes?/, 'min') - .replace(/hours?/, 'hrs') - .replace(/days?/, 'day') - .replace(/weeks?/, 'week'); + .replace(/minutes?/, 'min') + .replace(/hours?/, 'hrs') + .replace(/days?/, 'day') + .replace(/weeks?/, 'week'); } catch (error) { console.error('Error formatting time:', error); return 'Invalid Time'; } }; + export const formatBytes = (bytes, decimals = 2) => { if (bytes === 0) { return '0 Bytes'; @@ -94,7 +96,7 @@ export const downloadFile = (fileId, fileName = null) => { Authorization: `Bearer ${authToken}`, }; - return fetch(url, { headers }) + return fetch(url, {headers}) .then((response) => response.blob()) .then((blob) => { if (fileName) { @@ -124,7 +126,7 @@ export const downloadFile = (fileId, fileName = null) => { } }; -export const downloadAllFiles = (files,run_name) => { +export const downloadAllFiles = (files, run_name) => { const zip = new JSZip(); const promises = []; const fileNamesCount = {}; @@ -155,21 +157,21 @@ export const downloadAllFiles = (files,run_name) => { }); Promise.all(promises) - .then(() => { - zip.generateAsync({ type: "blob" }) - .then((content) => { - const now = new Date(); - const timestamp = `${now.getFullYear()}-${("0" + (now.getMonth() + 1)).slice(-2)}-${("0" + now.getDate()).slice(-2)}_${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`.replace(/:/g, '-'); - const zipFilename = `${run_name}_${timestamp}.zip`; - const downloadLink = document.createElement("a"); - downloadLink.href = URL.createObjectURL(content); - downloadLink.download = zipFilename; - downloadLink.click(); - }) - .catch((error) => { - console.error("Error generating zip:", error); - }); - }); + .then(() => { + zip.generateAsync({type: "blob"}) + .then((content) => { + const now = new Date(); + const timestamp = `${now.getFullYear()}-${("0" + (now.getMonth() + 1)).slice(-2)}-${("0" + now.getDate()).slice(-2)}_${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`.replace(/:/g, '-'); + const zipFilename = `${run_name}_${timestamp}.zip`; + const downloadLink = document.createElement("a"); + downloadLink.href = URL.createObjectURL(content); + downloadLink.download = zipFilename; + downloadLink.click(); + }) + .catch((error) => { + console.error("Error generating zip:", error); + }); + }); }; export const refreshUrl = () => { @@ -339,6 +341,7 @@ export const returnToolkitIcon = (toolkitName) => { {name: 'File Toolkit', imageSrc: '/images/filemanager_icon.svg'}, {name: 'CodingToolkit', imageSrc: '/images/app-logo-light.png'}, {name: 'Image Generation Toolkit', imageSrc: '/images/app-logo-light.png'}, + {name: 'DuckDuckGo Search Toolkit', imageSrc: '/images/duckduckgo_icon.png'}, ]; const toolkit = toolkitData.find((tool) => tool.name === toolkitName); @@ -346,18 +349,24 @@ export const returnToolkitIcon = (toolkitName) => { } export const returnResourceIcon = (file) => { + let fileIcon; const fileTypeIcons = { 'application/pdf': '/images/pdf_file.svg', 'application/txt': '/images/txt_file.svg', 'text/plain': '/images/txt_file.svg', - 'image': '/images/img_file.svg', }; - return fileTypeIcons[file.type] || '/images/default_file.svg'; + if (file.type.includes('image')) { + fileIcon = '/images/img_file.svg'; + } else { + fileIcon = fileTypeIcons[file.type] || '/images/default_file.svg'; + } + + return fileIcon; }; export const convertToTitleCase = (str) => { - if(str === null || str === '') { + if (str === null || str === '') { return ''; } diff --git a/migrations/versions/cac478732572_delete_agent_feature.py b/migrations/versions/cac478732572_delete_agent_feature.py new file mode 100644 index 000000000..e0c483975 --- /dev/null +++ b/migrations/versions/cac478732572_delete_agent_feature.py @@ -0,0 +1,23 @@ +"""delete_agent_feature + +Revision ID: cac478732572 +Revises: e39295ec089c +Create Date: 2023-07-13 17:18:42.003412 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cac478732572' +down_revision = 'e39295ec089c' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column('agents', sa.Column('is_deleted', sa.Boolean(), nullable=True)) + +def downgrade() -> None: + op.drop_column('agents', 'is_deleted') diff --git a/superagi/agent/output_parser.py b/superagi/agent/output_parser.py index a2a7fcefd..bd3747580 100644 --- a/superagi/agent/output_parser.py +++ b/superagi/agent/output_parser.py @@ -23,11 +23,14 @@ class BaseOutputParser(ABC): def parse(self, text: str) -> AgentGPTAction: """Return AgentGPTAction""" + class AgentSchemaOutputParser(BaseOutputParser): def parse(self, response: str) -> AgentGPTAction: if response.startswith("```") and response.endswith("```"): response = "```".join(response.split("```")[1:-1]) - response = JsonCleaner.extract_json_section(response) + response = JsonCleaner.extract_json_section(response) + # ast throws error if true/false params passed in json + response = JsonCleaner.clean_boolean(response) # OpenAI returns `str(content_dict)`, literal_eval reverses this try: @@ -40,76 +43,3 @@ def parse(self, response: str) -> AgentGPTAction: except BaseException as e: logger.info(f"AgentSchemaOutputParser: Error parsing JSON respons {e}") return {} - -class AgentOutputParser(BaseOutputParser): - def parse(self, text: str) -> AgentGPTAction: - try: - logger.info(text) - text = JsonCleaner.check_and_clean_json(text) - parsed = json5.loads(text) - except json.JSONDecodeError: - return AgentGPTAction( - name="ERROR", - args={"error": f"Could not parse invalid json: {text}"}, - ) - try: - format_prefix_yellow = "\033[93m\033[1m" - format_suffix_yellow = "\033[0m\033[0m" - format_prefix_green = "\033[92m\033[1m" - format_suffix_green = "\033[0m\033[0m" - logger.info(format_prefix_green + "Intelligence : " + format_suffix_green) - if "text" in parsed["thoughts"]: - logger.info(format_prefix_yellow + "Thoughts: " + format_suffix_yellow + parsed["thoughts"]["text"] + "\n") - - if "reasoning" in parsed["thoughts"]: - logger.info(format_prefix_yellow + "Reasoning: " + format_suffix_yellow + parsed["thoughts"]["reasoning"] + "\n") - - if "plan" in parsed["thoughts"]: - logger.info(format_prefix_yellow + "Plan: " + format_suffix_yellow + str(parsed["thoughts"]["plan"]) + "\n") - - if "criticism" in parsed["thoughts"]: - logger.info(format_prefix_yellow + "Criticism: " + format_suffix_yellow + parsed["thoughts"]["criticism"] + "\n") - - logger.info(format_prefix_green + "Action : " + format_suffix_green) - # print(format_prefix_yellow + "Args: "+ format_suffix_yellow + parsed["tool"]["args"] + "\n") - if "tool" not in parsed: - raise Exception("No tool found in the response..") - if parsed["tool"] is None or not parsed["tool"]: - return AgentGPTAction(name="", args="") - if "name" in parsed["tool"]: - logger.info(format_prefix_yellow + "Tool: " + format_suffix_yellow + parsed["tool"]["name"] + "\n") - args = {} - if "args" in parsed["tool"]: - args = parsed["tool"]["args"] - return AgentGPTAction( - name=parsed["tool"]["name"], - args=args, - ) - except (KeyError, TypeError) as e: - logger.error(f"Error parsing output:", e) - # If the tool is null or incomplete, return an erroneous tool - return AgentGPTAction( - name="ERROR", args={"error": f"Unable to parse the output: {parsed}"} - ) - - def parse_tasks(self, text: str) -> AgentTasks: - try: - parsed = json.loads(text, strict=False) - except json.JSONDecodeError: - preprocessed_text = JsonCleaner.preprocess_json_input(text) - try: - parsed = json.loads(preprocessed_text, strict=False) - except Exception: - return AgentTasks( - error=f"Could not parse invalid json: {text}", - ) - try: - logger.info("Tasks: ", parsed["tasks"]) - return AgentTasks( - tasks=parsed["tasks"] - ) - except (KeyError, TypeError): - # If the command is null or incomplete, return an erroneous tool - return AgentTasks( - error=f"Incomplete tool args: {parsed}", - ) \ No newline at end of file diff --git a/superagi/agent/super_agi.py b/superagi/agent/super_agi.py index 0eda8afdd..cbcda55ca 100644 --- a/superagi/agent/super_agi.py +++ b/superagi/agent/super_agi.py @@ -6,7 +6,7 @@ import time from typing import Any from typing import Tuple -import json + import numpy as np from pydantic import ValidationError from pydantic.types import List @@ -14,9 +14,10 @@ from sqlalchemy.orm import sessionmaker from superagi.agent.agent_prompt_builder import AgentPromptBuilder -from superagi.agent.output_parser import BaseOutputParser, AgentOutputParser, AgentSchemaOutputParser +from superagi.agent.output_parser import BaseOutputParser, AgentSchemaOutputParser from superagi.agent.task_queue import TaskQueue from superagi.apm.event_handler import EventHandler +from superagi.config.config import get_config from superagi.helper.token_counter import TokenCounter from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm @@ -65,23 +66,6 @@ def __init__(self, self.agent_config = agent_config self.agent_execution_config = agent_execution_config - @classmethod - def from_llm_and_tools( - cls, - ai_name: str, - ai_role: str, - memory: VectorStore, - tools: List[BaseTool], - llm: BaseLlm - ) -> SuperAgi: - return cls( - ai_name=ai_name, - ai_role=ai_role, - llm=llm, - memory=memory, - output_parser=AgentOutputParser(), - tools=tools - ) def fetch_agent_feeds(self, session, agent_execution_id): agent_feeds = session.query(AgentExecutionFeed.role, AgentExecutionFeed.feed) \ @@ -114,23 +98,23 @@ def execute(self, workflow_step: AgentWorkflowStep): if len(agent_feeds) <= 0: task_queue.clear_tasks() messages = [] - max_token_limit = 600 + max_output_token_limit = int(get_config("MAX_TOOL_TOKEN_LIMIT", 600)) # adding history to the messages if workflow_step.history_enabled: prompt = self.build_agent_prompt(workflow_step.prompt, task_queue=task_queue, - max_token_limit=max_token_limit) + max_token_limit=max_output_token_limit) messages.append({"role": "system", "content": prompt}) messages.append({"role": "system", "content": f"The current time and date is {time.strftime('%c')}"}) base_token_limit = TokenCounter.count_message_tokens(messages, self.llm.get_model()) full_message_history = [{'role': role, 'content': feed} for role, feed in agent_feeds] past_messages, current_messages = self.split_history(full_message_history, - token_limit - base_token_limit - max_token_limit) + token_limit - base_token_limit - max_output_token_limit) for history in current_messages: messages.append({"role": history["role"], "content": history["content"]}) messages.append({"role": "user", "content": workflow_step.completion_prompt}) else: prompt = self.build_agent_prompt(workflow_step.prompt, task_queue=task_queue, - max_token_limit=max_token_limit) + max_token_limit=max_output_token_limit) messages.append({"role": "system", "content": prompt}) # agent_execution_feed = AgentExecutionFeed(agent_execution_id=self.agent_config["agent_execution_id"], # agent_id=self.agent_config["agent_id"], feed=template_step.prompt, diff --git a/superagi/agent/task_queue.py b/superagi/agent/task_queue.py index b44bfb532..5885f3b5f 100644 --- a/superagi/agent/task_queue.py +++ b/superagi/agent/task_queue.py @@ -4,7 +4,7 @@ from superagi.config.config import get_config -redis_url = get_config('REDIS_URL') +redis_url = get_config('REDIS_URL') or "localhost:6379" """TaskQueue manages current tasks and past tasks in Redis """ class TaskQueue: def __init__(self, queue_name: str): diff --git a/superagi/apm/__init__.py b/superagi/apm/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/superagi/apm/analytics_helper.py b/superagi/apm/analytics_helper.py index 3fadff23e..154fa5778 100644 --- a/superagi/apm/analytics_helper.py +++ b/superagi/apm/analytics_helper.py @@ -1,11 +1,10 @@ -from typing import List, Dict, Tuple, Optional, Iterator, Union, Any +from typing import List, Dict, Union, Any + +from sqlalchemy import text, func, and_ from sqlalchemy.orm import Session -from sqlalchemy.orm.query import Query + from superagi.models.events import Event -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy import text, func, Integer, and_ -from collections import defaultdict -import logging + class AnalyticsHelper: diff --git a/superagi/apm/tools_handler.py b/superagi/apm/tools_handler.py index 71e3702af..71b0937ce 100644 --- a/superagi/apm/tools_handler.py +++ b/superagi/apm/tools_handler.py @@ -1,8 +1,11 @@ from typing import List, Dict -from sqlalchemy.orm import Session -from sqlalchemy.orm.query import Query + from sqlalchemy import func +from sqlalchemy.orm import Session + from superagi.models.events import Event + + class ToolsHandler: def __init__(self, session: Session, organisation_id: int): diff --git a/superagi/controllers/agent.py b/superagi/controllers/agent.py index 11b98643e..0b7169330 100644 --- a/superagi/controllers/agent.py +++ b/superagi/controllers/agent.py @@ -10,6 +10,7 @@ from jsonmerge import merge from pytz import timezone from sqlalchemy import func +from superagi.models.agent_execution_permission import AgentExecutionPermission from superagi.worker import execute_agent from superagi.helper.auth import check_auth from superagi.models.agent import Agent @@ -105,13 +106,17 @@ def get_agent(agent_id: int, Agent: An object of Agent representing the retrieved Agent. Raises: - HTTPException (Status Code=404): If the Agent is not found. + HTTPException (Status Code=404): If the Agent is not found or deleted. """ - db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first() - if not db_agent: + if ( + db_agent := db.session.query(Agent) + .filter(Agent.id == agent_id, Agent.is_deleted == False) + .first() + ): + return db_agent + else: raise HTTPException(status_code=404, detail="agent not found") - return db_agent @router.put("/update/{agent_id}", response_model=AgentOut) @@ -136,15 +141,15 @@ def update_agent(agent_id: int, agent: AgentIn, HTTPException (Status Code=404): If the Agent or associated Project is not found. """ - db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first() + db_agent = db.session.query(Agent).filter(Agent.id == agent_id, Agent.is_deleted == False).first() if not db_agent: raise HTTPException(status_code=404, detail="agent not found") if agent.project_id: - project = db.session.query(Project).get(agent.project_id) - if not project: + if project := db.session.query(Project).get(agent.project_id): + db_agent.project_id = project.id + else: raise HTTPException(status_code=404, detail="Project not found") - db_agent.project_id = project.id db_agent.name = agent.name db_agent.description = agent.description @@ -369,7 +374,7 @@ def get_schedule_data(agent_id: int, Authorize: AuthJWT = Depends(check_auth)): current_datetime = datetime.now(tzone).strftime("%d/%m/%Y %I:%M %p") - response_data = { + return { "current_datetime": current_datetime, "start_date": agent.start_time.astimezone(tzone).strftime("%d %b %Y"), "start_time": agent.start_time.astimezone(tzone).strftime("%I:%M %p"), @@ -378,8 +383,6 @@ def get_schedule_data(agent_id: int, Authorize: AuthJWT = Depends(check_auth)): "expiry_runs": agent.expiry_runs if agent.expiry_runs != -1 else None } - return response_data - @router.get("/get/project/{project_id}") def get_agents_by_project_id(project_id: int, @@ -403,11 +406,12 @@ def get_agents_by_project_id(project_id: int, if not project: raise HTTPException(status_code=404, detail="Project not found") - agents = db.session.query(Agent).filter(Agent.project_id == project_id).all() + agents = db.session.query(Agent).filter(Agent.project_id == project_id, Agent.is_deleted == False).all() new_agents, new_agents_sorted = [], [] for agent in agents: agent_dict = vars(agent) + agent_id = agent.id # Query the AgentExecution table using the agent ID @@ -445,12 +449,12 @@ def get_agent_configuration(agent_id: int, dict: Agent configuration including its details. Raises: - HTTPException (status_code=404): If the agent is not found. + HTTPException (status_code=404): If the agent is not found or deleted. """ # Define the agent_config keys to fetch keys_to_fetch = AgentTemplate.main_keys() - agent = db.session.query(Agent).filter(agent_id == Agent.id).first() + agent = db.session.query(Agent).filter(agent_id == Agent.id, Agent.is_deleted == False).first() if not agent: raise HTTPException(status_code=404, detail="Agent not found") @@ -480,3 +484,36 @@ def get_agent_configuration(agent_id: int, db.session.close() return response + +@router.put("/delete/{agent_id}", status_code = 200) +def delete_agent(agent_id: int, Authorize: AuthJWT = Depends(check_auth)): + """ + Delete an existing Agent + - Updates the is_deleted flag: Executes a soft delete + - AgentExecutions are updated to: "TERMINATED" if agentexecution is created, All the agent executions are updated + - AgentExecutionPermission is set to: "REJECTED" if agentexecutionpersmision is created + + Args: + agent_id (int): Identifier of the Agent to delete + + Returns: + A dictionary containing a "success" key with the value True to indicate a successful delete. + + Raises: + HTTPException (Status Code=404): If the Agent or associated Project is not found or deleted already. + """ + + db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first() + db_agent_executions = db.session.query(AgentExecution).filter(AgentExecution.agent_id == agent_id).all() + + if not db_agent or db_agent.is_deleted: + raise HTTPException(status_code=404, detail="agent not found") + + # Deletion Procedure + db_agent.is_deleted = True + if db_agent_executions: + # Updating all the RUNNING executions to TERMINATED + for db_agent_execution in db_agent_executions: + db_agent_execution.status = "TERMINATED" + + db.session.commit() diff --git a/superagi/controllers/agent_config.py b/superagi/controllers/agent_config.py index d57d4943e..f3ba396c0 100644 --- a/superagi/controllers/agent_config.py +++ b/superagi/controllers/agent_config.py @@ -52,10 +52,10 @@ def create_agent_config(agent_config: AgentConfigurationIn, AgentConfiguration: The created agent configuration. Raises: - HTTPException (Status Code=404): If the associated agent is not found. + HTTPException (Status Code=404): If the associated agent is not found or deleted. """ - agent = db.session.query(Agent).get(agent_config.agent_id) + agent = db.session.query(Agent).filter(Agent.id == agent_config.agent_id, Agent.is_deleted == False).first() if not agent: raise HTTPException(status_code=404, detail="Agent not found") @@ -82,10 +82,14 @@ def get_agent(agent_config_id: int, HTTPException (Status Code=404): If the agent configuration is not found. """ - db_agent_config = db.session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_config_id).first() - if not db_agent_config: + if ( + db_agent_config := db.session.query(AgentConfiguration) + .filter(AgentConfiguration.id == agent_config_id) + .first() + ): + return db_agent_config + else: raise HTTPException(status_code=404, detail="Agent Configuration not found") - return db_agent_config @router.put("/update", response_model=AgentConfigurationOut) @@ -133,10 +137,10 @@ def get_agent_configurations(agent_id: int, dict: The parsed response containing agent configurations. Raises: - HTTPException (Status Code=404): If the agent or agent configurations are not found. + HTTPException (Status Code=404): If the agent or agent configurations are not found or deleted. """ - agent = db.session.query(Agent).filter(Agent.id == agent_id).first() + agent = db.session.query(Agent).filter(Agent.id == agent_id, Agent.is_deleted == False).first() if not agent: raise HTTPException(status_code=404, detail="agent not found") @@ -191,5 +195,5 @@ def get_agent_configurations(agent_id: int, parsed_response["permission_type"] = value elif key == "LTM_DB": parsed_response["LTM_DB"] = value - + return parsed_response diff --git a/superagi/controllers/agent_execution.py b/superagi/controllers/agent_execution.py index 54d574caf..646f426bf 100644 --- a/superagi/controllers/agent_execution.py +++ b/superagi/controllers/agent_execution.py @@ -73,8 +73,7 @@ def create_agent_execution(agent_execution: AgentExecutionIn, HTTPException (Status Code=404): If the agent is not found. """ - agent = db.session.query(Agent).get(agent_execution.agent_id) - + agent = db.session.query(Agent).filter(Agent.id == agent_execution.agent_id, Agent.is_deleted == False).first() if not agent: raise HTTPException(status_code=404, detail="Agent not found") @@ -182,10 +181,14 @@ def get_agent_execution(agent_execution_id: int, HTTPException (Status Code=404): If the agent execution is not found. """ - db_agent_execution = db.session.query(AgentExecution).filter(AgentExecution.id == agent_execution_id).first() - if not db_agent_execution: + if ( + db_agent_execution := db.session.query(AgentExecution) + .filter(AgentExecution.id == agent_execution_id) + .first() + ): + return db_agent_execution + else: raise HTTPException(status_code=404, detail="Agent execution not found") - return db_agent_execution @router.put("/update/{agent_execution_id}", response_model=AgentExecutionOut) @@ -202,11 +205,17 @@ def update_agent_execution(agent_execution_id: int, raise HTTPException(status_code=404, detail="Agent Execution not found") if agent_execution.agent_id: - agent = db.session.query(Agent).get(agent_execution.agent_id) - if not agent: + if agent := db.session.query(Agent).get(agent_execution.agent_id): + db_agent_execution.agent_id = agent.id + else: raise HTTPException(status_code=404, detail="Agent not found") - db_agent_execution.agent_id = agent.id - if agent_execution.status != "CREATED" and agent_execution.status != "RUNNING" and agent_execution.status != "PAUSED" and agent_execution.status != "COMPLETED" and agent_execution.status != "TERMINATED": + if agent_execution.status not in [ + "CREATED", + "RUNNING", + "PAUSED", + "COMPLETED", + "TERMINATED", + ]: raise HTTPException(status_code=400, detail="Invalid Request") db_agent_execution.status = agent_execution.status diff --git a/superagi/helper/feed_parser.py b/superagi/helper/feed_parser.py index c1e868b38..c05e217b5 100644 --- a/superagi/helper/feed_parser.py +++ b/superagi/helper/feed_parser.py @@ -45,7 +45,7 @@ def parse_feed(feed): if feed.role == "system": final_output = feed.feed if "json-schema.org" in feed.feed: - final_output = feed.feed.split("TOOLS")[0] + final_output = feed.feed.split("TOOLS:")[0] return {"role": "system", "feed": final_output, "updated_at": feed.updated_at, "time_difference": feed.time_difference} diff --git a/superagi/helper/json_cleaner.py b/superagi/helper/json_cleaner.py index b4085a3d3..d97b1ecf4 100644 --- a/superagi/helper/json_cleaner.py +++ b/superagi/helper/json_cleaner.py @@ -8,60 +8,20 @@ class JsonCleaner: @classmethod - def check_and_clean_json(cls, json_string: str): + def clean_boolean(cls, input_str: str = ""): """ - Checks if the given string is a valid json string. - If not, tries to clean it up and return a valid json string. + Clean the boolean values in the given string. Args: - json_string (str): The json string to be checked and cleaned. + input_str (str): The string from which the json section is to be extracted. Returns: - str: The cleaned json string. - """ - try: - json_string = cls.remove_escape_sequences(json_string) - json_string = cls.remove_trailing_newline_spaces(json_string) - json_string = cls.clean_newline_characters(json_string) - - json5.loads(json_string) - return json_string - except (ValueError, json.JSONDecodeError) as e: - # If the json is invalid, try to clean it up - json_string = cls.extract_json_section(json_string) - json_string = cls.preprocess_json_input(json_string) - json_string = cls.add_quotes_to_property_names(json_string) - json_string = cls.remove_escape_sequences(json_string) - # call this post remove_escape_sequences - json_string = cls.clean_newline_characters(json_string) - json_string = cls.balance_braces(json_string) - try: - json5.loads(json_string) - return json_string - except (ValueError, json.JSONDecodeError) as e: - logger.info(json_string) - # If the json is still invalid, try to extract the json section - json_string = cls.extract_json_section(json_string) - return json_string - return json_string - - @classmethod - def preprocess_json_input(cls, input_str: str) -> str: + str: The extracted json section. """ - Preprocess the given json string. - Replace single backslashes with double backslashes, - while leaving already escaped ones intact. - - Args: - input_str (str): The json string to be preprocessed. + input_str = re.sub(r':\s*false', ': False', input_str) + input_str = re.sub(r':\s*true', ': True', input_str) + return input_str - Returns: - str: The preprocessed json string. - """ - corrected_str = re.sub( - r'(? str: - """ - Add quotes to property names in the given json string. - - Args: - json_string (str): The json string to be processed. - - Returns: - str: The json string with quotes added to property names. - """ - - def replace(match: re.Match) -> str: - return f'{match.group(1)}"{match.group(2)}":' - - json_string = re.sub(r'([,{])\n*\s*(\b\w+\b):', replace, json_string) - - return json_string - @classmethod def balance_braces(cls, json_string: str) -> str: """ diff --git a/superagi/jobs/agent_executor.py b/superagi/jobs/agent_executor.py index 68c893cc0..b321361bf 100644 --- a/superagi/jobs/agent_executor.py +++ b/superagi/jobs/agent_executor.py @@ -257,7 +257,7 @@ def execute_next_action(self, agent_execution_id): return if "retry" in response and response["retry"]: - superagi.worker.execute_agent.apply_async((agent_execution_id, datetime.now()), countdown=10) + superagi.worker.execute_agent.apply_async((agent_execution_id, datetime.now()), countdown=15) session.close() return @@ -343,7 +343,7 @@ def handle_wait_for_permission(self, agent_execution, spawned_agent, session): if agent_execution_permission.status == "PENDING": raise ValueError("Permission is still pending") if agent_execution_permission.status == "APPROVED": - result = spawned_agent.handle_tool_response(agent_execution_permission.assistant_reply).get("result") + result = spawned_agent.handle_tool_response(session, agent_execution_permission.assistant_reply).get("result") else: result = f"User denied the permission to run the tool {agent_execution_permission.tool_name}" \ f"{' and has given the following feedback : ' + agent_execution_permission.user_feedback if agent_execution_permission.user_feedback else ''}" diff --git a/superagi/lib/logger.py b/superagi/lib/logger.py index 048c0c8a4..f600ff11c 100644 --- a/superagi/lib/logger.py +++ b/superagi/lib/logger.py @@ -52,27 +52,27 @@ def _make_custom_log_record(self, name, level, fn, lno, msg, args, exc_info, fun def debug(self, message, *args): self.logger.debug(message) - if len(args) > 0: + if args: self.logger.debug(*args) def info(self, message, *args): self.logger.info(message) - if len(args) > 0: + if args: self.logger.info(*args) def warning(self, message, *args): self.logger.warning(message) - if len(args) > 0: + if args: self.logger.warning(*args) def error(self, message, *args): self.logger.error(message) - if len(args) > 0: + if args: self.logger.error(*args) def critical(self, message, *args): self.logger.critical(message) - if len(args) > 0: + if args: self.logger.critical(*args) diff --git a/superagi/llms/google_palm.py b/superagi/llms/google_palm.py index b19b595b3..707a1579e 100644 --- a/superagi/llms/google_palm.py +++ b/superagi/llms/google_palm.py @@ -75,7 +75,7 @@ def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT return {"response": completion, "content": completion.result} except Exception as exception: logger.info("Google palm Exception:", exception) - return {"error": exception} + return {"error": "ERROR_GOOGLE_PALM", "message": "Google palm exception"} def verify_access_key(self): """ diff --git a/superagi/llms/llm_model_factory.py b/superagi/llms/llm_model_factory.py index 65d8bd41a..b13f46067 100644 --- a/superagi/llms/llm_model_factory.py +++ b/superagi/llms/llm_model_factory.py @@ -18,6 +18,8 @@ def get_model(self, model, **kwargs): factory = ModelFactory() factory.register_format("gpt-4", lambda **kwargs: OpenAi(model="gpt-4", **kwargs)) +factory.register_format("gpt-4-32k", lambda **kwargs: OpenAi(model="gpt-4-32k", **kwargs)) +factory.register_format("gpt-3.5-turbo-16k", lambda **kwargs: OpenAi(model="gpt-3.5-turbo-16k", **kwargs)) factory.register_format("gpt-3.5-turbo", lambda **kwargs: OpenAi(model="gpt-3.5-turbo", **kwargs)) factory.register_format("google-palm-bison-001", lambda **kwargs: GooglePalm(model='models/chat-bison-001', **kwargs)) diff --git a/superagi/llms/openai.py b/superagi/llms/openai.py index 1d44cc6dc..a25c0c847 100644 --- a/superagi/llms/openai.py +++ b/superagi/llms/openai.py @@ -1,4 +1,6 @@ import openai +from openai import APIError, InvalidRequestError +from openai.error import RateLimitError, AuthenticationError from superagi.config.config import get_config from superagi.lib.logger import logger @@ -73,9 +75,18 @@ def chat_completion(self, messages, max_tokens=get_config("MAX_MODEL_TOKEN_LIMIT ) content = response.choices[0].message["content"] return {"response": response, "content": content} + except AuthenticationError as auth_error: + logger.info("OpenAi AuthenticationError:", auth_error) + return {"error": "ERROR_AUTHENTICATION", "message": "Authentication error please check the api keys.."} + except RateLimitError as api_error: + logger.info("OpenAi RateLimitError:", api_error) + return {"error": "ERROR_RATE_LIMIT", "message": "Openai rate limit exceeded.."} + except InvalidRequestError as invalid_request_error: + logger.info("OpenAi InvalidRequestError:", invalid_request_error) + return {"error": "ERROR_INVALID_REQUEST", "message": "Openai invalid request error.."} except Exception as exception: logger.info("OpenAi Exception:", exception) - return {"error": exception} + return {"error": "ERROR_OPENAI", "message": "Open ai exception"} def verify_access_key(self): """ diff --git a/superagi/models/agent.py b/superagi/models/agent.py index 9ef14fdab..cca1d6e30 100644 --- a/superagi/models/agent.py +++ b/superagi/models/agent.py @@ -2,7 +2,7 @@ import json -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, Boolean import superagi.models from superagi.models.agent_config import AgentConfiguration @@ -25,6 +25,7 @@ class Agent(DBBaseModel): project_id (int): The identifier of the associated project. description (str): The description of the agent. agent_workflow_id (int): The identifier of the associated agent workflow. + is_deleted (bool): The flag associated for agent deletion """ __tablename__ = 'agents' @@ -34,7 +35,8 @@ class Agent(DBBaseModel): project_id = Column(Integer) description = Column(String) agent_workflow_id = Column(Integer) - + is_deleted = Column(Boolean, default = False) + def __repr__(self): """ Returns a string representation of the Agent object. @@ -44,8 +46,9 @@ def __repr__(self): """ return f"Agent(id={self.id}, name='{self.name}', project_id={self.project_id}, " \ - f"description='{self.description}', agent_workflow_id={self.agent_workflow_id})" - + f"description='{self.description}', agent_workflow_id={self.agent_workflow_id}," \ + f"is_deleted='{self.is_deleted}')" + @classmethod def fetch_configuration(cls, session, agent_id: int): """ @@ -79,7 +82,8 @@ def fetch_configuration(cls, session, agent_id: int): "permission_type": None, "LTM_DB": None, "memory_window": None, - "max_iterations": None + "max_iterations": None, + "is_deleted": agent.is_deleted, } if not agent_configurations: return parsed_config @@ -105,7 +109,7 @@ def eval_agent_config(cls, key, value): return value elif key in ["project_id", "memory_window", "max_iterations", "iteration_interval"]: return int(value) - elif key == "goal" or key == "constraints" or key == "instruction": + elif key in ["goal", "constraints", "instruction", "is_deleted"]: return eval(value) elif key == "tools": return [int(x) for x in json.loads(value)] @@ -197,9 +201,12 @@ def create_agent_with_template_id(cls, db, project_id, agent_template): configs = db.session.query(AgentTemplateConfig).filter( AgentTemplateConfig.agent_template_id == agent_template.id).all() - agent_configurations = [] - for config in configs: - agent_configurations.append(AgentConfiguration(agent_id=db_agent.id, key=config.key, value=config.value)) + agent_configurations = [ + AgentConfiguration( + agent_id=db_agent.id, key=config.key, value=config.value + ) + for config in configs + ] db.session.add_all(agent_configurations) db.session.commit() db.session.flush() @@ -229,9 +236,10 @@ def create_agent_with_marketplace_template_id(cls, db, project_id, agent_templat db.session.flush() # Flush pending changes to generate the agent's ID db.session.commit() - agent_configurations = [] - for key, value in agent_template["configs"].items(): - agent_configurations.append(AgentConfiguration(agent_id=db_agent.id, key=key, value=value["value"])) + agent_configurations = [ + AgentConfiguration(agent_id=db_agent.id, key=key, value=value["value"]) + for key, value in agent_template["configs"].items() + ] db.session.add_all(agent_configurations) db.session.commit() db.session.flush() diff --git a/superagi/tools/base_tool.py b/superagi/tools/base_tool.py index e066c8046..5c072832f 100644 --- a/superagi/tools/base_tool.py +++ b/superagi/tools/base_tool.py @@ -94,7 +94,7 @@ def _execute(self, *args: Any, **kwargs: Any): @property def max_token_limit(self): - return get_config("MAX_TOOL_TOKEN_LIMIT", 600) + return int(get_config("MAX_TOOL_TOKEN_LIMIT", 600)) def _parse_input( self, diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 107589d56..9c1a001a9 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -99,7 +99,7 @@ def _execute(self, code_description: str) -> str: # Get README contents and save split_result = result["content"].split("```") - if len(split_result) > 0: + if split_result: readme = split_result[0] save_readme_result = self.resource_manager.write_file("README.md", readme) if save_readme_result.startswith("Error"): diff --git a/superagi/tools/email/README.md b/superagi/tools/email/README.md new file mode 100644 index 000000000..bb360e296 --- /dev/null +++ b/superagi/tools/email/README.md @@ -0,0 +1,91 @@ +

    + +SuperAGI logo + + +SuperAGI logo + +

    + +# SuperAGI Email Tool + +The robust SuperAGI Email Tool lets users send and read emails while providing a foundation for other fascinating use cases. + +## 💡 Features + +1.**Read Emails:** With SuperAGI's Email Tool, you can effortlessly manage your inbox and ensure that you never overlook a critical detail. + +2. **Send Emails:** SuperAGI's Email Tool uses its comprehensive language model capabilities to create personalised, context-aware emails, sparing you effort and time. + +3. **Save Emails to Drafts Folder:** By allowing SuperAGI to develop email draughts that you can examine and modify before sending, you'll gain greater control and make sure your messages are tailored to your tastes. + +4. **Send Emails with Attachments:** Send attachments in emails with ease to enrich and expand the scope of your message. + +5. **Custom Email Signature:** Create a unique signature for each email you send to add a touch of customization and automation. + +6. **Auto-Reply and Answer Questions:** Allow SuperAGI to read, analyse, and respond to incoming emails with precise answers to streamline your email responses. + +## ⚙️ Installation + +### 🛠 **Setting Up of SuperAGI** +Set up the SuperAGI by following the instructions given (https://github.com/TransformerOptimus/SuperAGI/blob/main/README.MD) + +### 🔧 **Add Email configuration settings SuperAGI's Dashboard** + +![Config_Page](https://github.com/Phoenix2809/SuperAGI/assets/133874957/6abe8f84-370e-4512-8374-e7eebe5f836d) + +Add the following configuration in the Email Toolkit Page: + +1. _Email address and password:_ + - Set 'EMAIL_ADDRESS' to sender's email address + - Set 'EMAIL_PASSWORD' to your Password. If using Gmail, use App Password (Follow the steps given below to obtain your app password.) + +2. _Provider-specific settings:_ + - If not using Gmail, modify 'EMAIL_SMTP_HOST', 'EMAIL_SMTP_PORT', AND 'EMAIL_IMAP_HOST' according to your email service provider. + +3. _Sending and Drafts:_ + - You can set the EMAIL_DRAFT_MODE to "FALSE" if you'd like your email to be directly sent and "TRUE" if you'd like to save your emails in Draft. + - If you're setting Draft Mode to True, Make sure to add the draft folder for your email service provider to prevent it from being sent. + +4. _Optional Settings:_ + - Change the 'EMAIL_SIGNATURE' to your personalize signature. + + +## Obtain your App Password + +To obtain App password for your Gmail Account follow the steps: + +- Navigate to the link (https://myaccount.google.com/apppasswords) + +![app_password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/ec1e6222-e5d4-4b88-a69c-1fd5774ae0ea) + +- To get the App Password ensure that you have set up 2-Step Verification for your email address. + +- Generate the password by creating a custom app + +![password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/32219756-8715-4f5a-bb1c-0b2cae4e73a3) + +- Copy the password generated and use it for 'EMAIL_PASSWORD' + +- Also make sure IMAP Access is enabled for your Gmail Address (Settings > See all settings > Forwarding and POP/IMAP > Enable IMAP) + +![imap_enable](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/50ef3e0c-c2ff-4848-aba7-8a6bd4a800ab) + +## Running SuperAGI Email Tool + +1. **Read an email** + +By default SuperAGI's email tool reads last 10 emails from your inbox, to change the limit you can modify the default limit in read_email.py + +2. **Send an email** + +To send an email to a particular receiver, mention the receiver's ID in your goal. Email will be stored in drafts if in case receiver's email address is not mentioned. + +![send_email](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/c4dc52b9-ab68-4db3-b1f9-3431c00710c4) + +3. **Send an email with attachment** + +SuperAGI can send Emails with Attachments if you have uploaded the file in the Resource Manager, or if your file is in the Input or the Output of your SuperAGI Workspace. + +![attachment](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/de112910-a623-469d-a0db-99063fb8572e) +``` diff --git a/superagi/tools/google_serp_search/google_serp_search.py b/superagi/tools/google_serp_search/google_serp_search.py index f780d2d78..e3b0cb438 100644 --- a/superagi/tools/google_serp_search/google_serp_search.py +++ b/superagi/tools/google_serp_search/google_serp_search.py @@ -51,7 +51,7 @@ def _execute(self, query: str) -> tuple: serp_api = GoogleSerpApiWrap(api_key) response = serp_api.search_run(query) summary = self.summarise_result(query, response["snippets"]) - if len(response["links"]) > 0: + if response["links"]: return summary + "\n\nLinks:\n" + "\n".join("- " + link for link in response["links"][:3]) return summary diff --git a/superagi/tools/jira/edit_issue.py b/superagi/tools/jira/edit_issue.py index d5119dc1f..b8c2e4162 100644 --- a/superagi/tools/jira/edit_issue.py +++ b/superagi/tools/jira/edit_issue.py @@ -44,7 +44,7 @@ def _execute(self, key: str, fields: dict): """ jira = self.build_jira_instance() issues = jira.search_issues('key=') - if len(issues) > 0: + if issues: issues[0].update(fields=fields) return f"Issue '{issues[0].key}' created successfully!" return f"Issue not found!" diff --git a/superagi/tools/twitter/README.md b/superagi/tools/twitter/README.md new file mode 100644 index 000000000..23d065c39 --- /dev/null +++ b/superagi/tools/twitter/README.md @@ -0,0 +1,62 @@ +

    + +SuperAGI logo + + +SuperAGI logo + +

    + +# SuperAGI Twitter Toolkit + +Introducing Twitter Toolkit for SuperAGI. With Twitter Integrated into SuperAGI, you can now deploy agents to + +1. Send Tweets +2. Send Tweets with Images + +## Installation + +### 🛠️ Setting up SuperAGI: + +Set up SuperAGI by following the instructions given [here](https://github.com/TransformerOptimus/SuperAGI/blob/main/README.MD) + +### 🔐 Obtaining API Key and Secret from Twitter Developer Portal + +1. Log in to your Twitter Developer Portal Account and select your project under the “Projects & Apps” section. + +![TW1](https://github.com/Phoenix2809/SuperAGI/assets/133874957/90064a9e-321b-499e-bad1-2d6758e07252) + +2. Proceed with creating a new app. Once you have created the app by adding a name, you will get an API Key and an API Secret, copy that and keep it in a separate text file. + +![TW2](https://github.com/Phoenix2809/SuperAGI/assets/133874957/b33341af-bfe7-473f-8735-3ff19dd5370d) +![TW3](https://github.com/Phoenix2809/SuperAGI/assets/133874957/7bc79f38-32fb-4ccc-b482-cd505b6838bf) + + +### 🚪 Configuring OAuth + +3. Once you have saved the key and the secret, click on “App Settings” +4. Once you are on the App Settings Page, start setting up the User Authentication Settings. + +![TW4](https://github.com/Phoenix2809/SuperAGI/assets/133874957/5b46f42b-9631-4138-92e6-3cc5e8e58863) + +5. Fill in the details as shown in the below image. Give “Read and Write Permissions” and make it a “Web Application" + +![TW5](https://github.com/Phoenix2809/SuperAGI/assets/133874957/ff8bb022-ea86-4f91-b484-f7cda04226f9) + +6. Add the Callback URI and the Website URL as shown in the image below + +![TW6](https://github.com/Phoenix2809/SuperAGI/assets/133874957/e6471e3b-dc4d-4ac4-8454-4e85d086271a) + +7. Save the settings. you have now configured OAuth Authentication for Twitter. + + ### ✅ Configuring Keys and Authenticating in SuperAGI. + +1. In the SuperAGI’s Dashboard, navigate to the Twitter Toolkit Page, add the API Key and API Secret you’ve saved, and click on ‘Update Changes’ + +![TW7](https://github.com/Phoenix2809/SuperAGI/assets/133874957/0b5e7fd7-c1f0-4738-bacf-9aa3dbd1ef06) + +2. After you’ve updated the changes, click on Authenticate. This will take you to the OAuth Flow. Authorize the app through the flow. + +![TW8](https://github.com/Phoenix2809/SuperAGI/assets/133874957/99fe96b1-350c-43b8-a269-cc68996bc080) + +Once you have followe the above steps, you have successfully integrated Twitter with SuperAGI. diff --git a/superagi/worker.py b/superagi/worker.py index 5961ed62c..95e5430a0 100644 --- a/superagi/worker.py +++ b/superagi/worker.py @@ -60,7 +60,7 @@ def summarize_resource(agent_id: int, resource_id: int): engine = connect_db() Session = sessionmaker(bind=engine) session = Session() - model_source = Configuration.fetch_value_by_agent_id(session, agent_id, "model_source") + model_source = Configuration.fetch_value_by_agent_id(session, agent_id, "model_source") or "OpenAi" if ModelSourceType.GooglePalm.value in model_source: return diff --git a/tests/unit_tests/agent/test_output_parser.py b/tests/unit_tests/agent/test_output_parser.py index 453144d3f..b0ad88d2a 100644 --- a/tests/unit_tests/agent/test_output_parser.py +++ b/tests/unit_tests/agent/test_output_parser.py @@ -1,9 +1,10 @@ import pytest -from superagi.agent.output_parser import AgentOutputParser, AgentGPTAction, AgentTasks + +from superagi.agent.output_parser import AgentGPTAction, AgentSchemaOutputParser def test_parse(): - parser = AgentOutputParser() + parser = AgentSchemaOutputParser() # test with valid input valid_text = '{"thoughts": {"text": "some thought", "reasoning": "some reasoning", "plan": "some plan", "criticism": "some criticism"}, "tool": {"name": "some tool", "args": {"arg1": "value1"}}}' @@ -12,31 +13,3 @@ def test_parse(): assert output.name == "some tool" assert output.args == {"arg1": "value1"} - # test with invalid json - invalid_json = '{"this is not valid json' - with pytest.raises(ValueError): - parser.parse(invalid_json) - - -def test_parse_tasks(): - parser = AgentOutputParser() - - # test with valid input - valid_text = '{"tasks": [{"task1": "value1"}, {"task2": "value2"}]}' - output = parser.parse_tasks(valid_text) - assert isinstance(output, AgentTasks) - assert output.tasks == [{"task1": "value1"}, {"task2": "value2"}] - assert output.error == "" - - # test with incomplete tool args - invalid_tasks = '{"tasks": []}' - output = parser.parse_tasks(invalid_tasks) - assert isinstance(output, AgentTasks) - assert output.tasks == [] - assert output.error == "" - - # test with invalid json - invalid_json = '{"this is not valid json' - output = parser.parse_tasks(invalid_json) - assert isinstance(output, AgentTasks) - assert "Could not parse invalid json" in output.error diff --git a/tests/unit_tests/agent_permissions/test_check_permission_in_restricted_mode.py b/tests/unit_tests/agent_permissions/test_check_permission_in_restricted_mode.py index cf17fb2ef..196887556 100644 --- a/tests/unit_tests/agent_permissions/test_check_permission_in_restricted_mode.py +++ b/tests/unit_tests/agent_permissions/test_check_permission_in_restricted_mode.py @@ -1,6 +1,8 @@ -import pytest from unittest.mock import MagicMock, Mock -from superagi.agent.output_parser import AgentOutputParser + +import pytest + +from superagi.agent.output_parser import AgentSchemaOutputParser from superagi.agent.super_agi import SuperAgi from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool @@ -31,7 +33,7 @@ def super_agi(): tools = [MockTool(name="NotRestrictedTool", permission_required=False), MockTool(name="RestrictedTool", permission_required=True)] agent_config = {"permission_type": "RESTRICTED", "agent_execution_id": 1, "agent_id": 2} - output_parser = AgentOutputParser() + output_parser = AgentSchemaOutputParser() super_agi = SuperAgi(ai_name, ai_role, llm, memory, tools, agent_config, output_parser) return super_agi diff --git a/tests/unit_tests/apm/__init__.py b/tests/unit_tests/apm/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit_tests/apm/test_analytics_helper.py b/tests/unit_tests/apm/test_analytics_helper.py index d1087e32b..87d26af55 100644 --- a/tests/unit_tests/apm/test_analytics_helper.py +++ b/tests/unit_tests/apm/test_analytics_helper.py @@ -1,41 +1,36 @@ import pytest -from unittest.mock import MagicMock +from superagi.models.events import Event from superagi.apm.analytics_helper import AnalyticsHelper -from sqlalchemy.orm import Session - -@pytest.fixture -def mock_session(): - return MagicMock(spec=Session) +from unittest.mock import MagicMock @pytest.fixture def organisation_id(): return 1 +@pytest.fixture +def mock_session(): + return MagicMock() + @pytest.fixture def analytics_helper(mock_session, organisation_id): return AnalyticsHelper(mock_session, organisation_id) def test_calculate_run_completed_metrics(analytics_helper, mock_session): - analytics_helper.calculate_run_completed_metrics = MagicMock(return_value = {}) + mock_session.query().all.return_value = [MagicMock()] result = analytics_helper.calculate_run_completed_metrics() assert isinstance(result, dict) - analytics_helper.calculate_run_completed_metrics.assert_called() def test_fetch_agent_data(analytics_helper, mock_session): - analytics_helper.fetch_agent_data = MagicMock(return_value = {}) + mock_session.query().all.return_value = [MagicMock()] result = analytics_helper.fetch_agent_data() assert isinstance(result, dict) - analytics_helper.fetch_agent_data.assert_called() def test_fetch_agent_runs(analytics_helper, mock_session): - agent_id = 1 - analytics_helper.fetch_agent_runs = MagicMock(return_value = []) - result = analytics_helper.fetch_agent_runs(agent_id) + mock_session.query().all.return_value = [MagicMock()] + result = analytics_helper.fetch_agent_runs(1) assert isinstance(result, list) - analytics_helper.fetch_agent_runs.assert_called_with(agent_id) def test_get_active_runs(analytics_helper, mock_session): - analytics_helper.get_active_runs = MagicMock(return_value = []) + mock_session.query().all.return_value = [MagicMock()] result = analytics_helper.get_active_runs() - assert isinstance(result, list) - analytics_helper.get_active_runs.assert_called() \ No newline at end of file + assert isinstance(result, list) \ No newline at end of file diff --git a/tests/unit_tests/apm/test_tools_handler.py b/tests/unit_tests/apm/test_tools_handler.py index 207fef4bd..f58650cc7 100644 --- a/tests/unit_tests/apm/test_tools_handler.py +++ b/tests/unit_tests/apm/test_tools_handler.py @@ -1,22 +1,22 @@ import pytest from unittest.mock import MagicMock -from sqlalchemy.orm import Session -from superagi.apm.tools_handler import ToolsHandler -@pytest.fixture -def mock_session(): - return MagicMock(spec=Session) +from superagi.apm.tools_handler import ToolsHandler +from sqlalchemy.orm import Session @pytest.fixture def organisation_id(): return 1 +@pytest.fixture +def mock_session(): + return MagicMock() + @pytest.fixture def tools_handler(mock_session, organisation_id): return ToolsHandler(mock_session, organisation_id) -def test_calculate_tool_usage(tools_handler): - tools_handler.calculate_tool_usage = MagicMock(return_value=[]) +def test_calculate_tool_usage(tools_handler, mock_session): + mock_session.query().all.return_value = [MagicMock()] result = tools_handler.calculate_tool_usage() - assert isinstance(result, list) - tools_handler.calculate_tool_usage.assert_called() \ No newline at end of file + assert isinstance(result, list) \ No newline at end of file diff --git a/tests/unit_tests/controllers/test_analytics.py b/tests/unit_tests/controllers/test_analytics.py index d0172106e..c131f321b 100644 --- a/tests/unit_tests/controllers/test_analytics.py +++ b/tests/unit_tests/controllers/test_analytics.py @@ -1,41 +1,61 @@ -from unittest.mock import patch +from unittest.mock import patch, MagicMock import pytest from fastapi.testclient import TestClient from main import app client = TestClient(app) -def test_get_metrics_success(): - with patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper: +@patch('superagi.controllers.analytics.db') +def test_get_metrics_success(mock_get_db): + with patch('superagi.helper.auth.get_user_organisation') as mock_get_user_org, \ + patch('superagi.controllers.analytics.db') as mock_db, \ + patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper, \ + patch('superagi.helper.auth.db') as mock_auth_db: mock_helper().calculate_run_completed_metrics.return_value = {'total_tokens': 10, 'total_calls': 5, 'runs_completed': 2} - response = client.get("analytics/metrics") + response = client.get("/analytics/metrics") assert response.status_code == 200 assert response.json() == {'total_tokens': 10, 'total_calls': 5, 'runs_completed': 2} -def test_get_agents_success(): - with patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper: +@patch('superagi.controllers.analytics.db') +def test_get_agents_success(mock_get_db): + with patch('superagi.helper.auth.get_user_organisation') as mock_get_user_org, \ + patch('superagi.controllers.analytics.db') as mock_db, \ + patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper, \ + patch('superagi.helper.auth.db') as mock_auth_db: mock_helper().fetch_agent_data.return_value = {"agent_details": "mock_details", "model_info": "mock_info"} - response = client.get("analytics/agents/all") + response = client.get("/analytics/agents/all") assert response.status_code == 200 assert response.json() == {"agent_details": "mock_details", "model_info": "mock_info"} -def test_get_agent_runs_success(): - with patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper: +@patch('superagi.controllers.analytics.db') +def test_get_agent_runs_success(mock_get_db): + with patch('superagi.helper.auth.get_user_organisation') as mock_get_user_org, \ + patch('superagi.controllers.analytics.db') as mock_db, \ + patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper, \ + patch('superagi.helper.auth.db') as mock_auth_db: mock_helper().fetch_agent_runs.return_value = "mock_agent_runs" - response = client.get("analytics/agents/1") + response = client.get("/analytics/agents/1") assert response.status_code == 200 assert response.json() == "mock_agent_runs" -def test_get_active_runs_success(): - with patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper: +@patch('superagi.controllers.analytics.db') +def test_get_active_runs_success(mock_get_db): + with patch('superagi.helper.auth.get_user_organisation') as mock_get_user_org, \ + patch('superagi.controllers.analytics.db') as mock_db, \ + patch('superagi.controllers.analytics.AnalyticsHelper') as mock_helper, \ + patch('superagi.helper.auth.db') as mock_auth_db: mock_helper().get_active_runs.return_value = ["mock_run_1", "mock_run_2"] - response = client.get("analytics/runs/active") + response = client.get("/analytics/runs/active") assert response.status_code == 200 assert response.json() == ["mock_run_1", "mock_run_2"] -def test_get_tools_user_success(): - with patch('superagi.controllers.analytics.ToolsHandler') as mock_handler: +@patch('superagi.controllers.analytics.db') +def test_get_tools_user_success(mock_get_db): + with patch('superagi.helper.auth.get_user_organisation') as mock_get_user_org, \ + patch('superagi.controllers.analytics.db') as mock_db, \ + patch('superagi.controllers.analytics.ToolsHandler') as mock_handler, \ + patch('superagi.helper.auth.db') as mock_auth_db: mock_handler().calculate_tool_usage.return_value = ["tool1", "tool2"] - response = client.get("analytics/tools/used") + response = client.get("/analytics/tools/used") assert response.status_code == 200 assert response.json() == ["tool1", "tool2"] \ No newline at end of file diff --git a/tests/unit_tests/helper/test_json_cleaner.py b/tests/unit_tests/helper/test_json_cleaner.py index 8579a9900..fbf941d91 100644 --- a/tests/unit_tests/helper/test_json_cleaner.py +++ b/tests/unit_tests/helper/test_json_cleaner.py @@ -1,11 +1,6 @@ from superagi.helper.json_cleaner import JsonCleaner import pytest -def test_preprocess_json_input(): - test_str = r'This is a test\ string' - result = JsonCleaner.preprocess_json_input(test_str) - assert result == r'This is a test\\ string' - def test_extract_json_section(): test_str = 'Before json {"key":"value"} after json' result = JsonCleaner.extract_json_section(test_str) @@ -16,28 +11,14 @@ def test_remove_escape_sequences(): result = JsonCleaner.remove_escape_sequences(test_str) assert result == 'This is a test\nstring' -def test_add_quotes_to_property_names(): - test_str = '{key: "value"}' - result = JsonCleaner.add_quotes_to_property_names(test_str) - assert result == '{"key": "value"}' - def test_balance_braces(): test_str = '{{{{"key":"value"}}' result = JsonCleaner.balance_braces(test_str) assert result == '{{{{"key":"value"}}}}' -def test_check_and_clean_json(): - test_str = r'{key: "value"\n}' - result = JsonCleaner.check_and_clean_json(test_str) - assert result == '{key: "value"}' +def test_balance_braces(): + test_str = '{"key": false}' + result = JsonCleaner.clean_boolean(test_str) + assert result == '{"key": False}' -def test_clean_newline_spaces_json(): - test_str = r'{key: "value"\n \n}' - result = JsonCleaner.check_and_clean_json(test_str) - assert result == '{key: "value"}' - -def test_has_newline_in_string(): - test_str = r'{key: "value\n"\n \n}' - result = JsonCleaner.check_and_clean_json(test_str) - assert result == '{key: "value"}' diff --git a/tools.json b/tools.json index 883ce4d92..94de16ccc 100644 --- a/tools.json +++ b/tools.json @@ -1,5 +1,4 @@ { "tools": { - } } \ No newline at end of file