From e71a142291c3141e465f721a97be93950ff3c610 Mon Sep 17 00:00:00 2001 From: anisha-contlo Date: Thu, 13 Jul 2023 18:09:01 +0530 Subject: [PATCH 1/7] Added Controller Function --- superagi/controllers/agent_template.py | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/superagi/controllers/agent_template.py b/superagi/controllers/agent_template.py index 140622aee..6e59faa87 100644 --- a/superagi/controllers/agent_template.py +++ b/superagi/controllers/agent_template.py @@ -13,6 +13,7 @@ from superagi.models.agent_template_config import AgentTemplateConfig from superagi.models.agent_workflow import AgentWorkflow from superagi.models.tool import Tool +import json # from superagi.types.db import AgentTemplateIn, AgentTemplateOut router = APIRouter() @@ -144,6 +145,61 @@ def update_agent_template(agent_template_id: int, return db_agent_template +@router.put("/edit_agent_template/{agent_template_id}", status_code=200) +def edit_agent_template(agent_template_id: int, + edited_agent_configs: dict, + organisation=Depends(get_user_organisation)): + + """ + Update the details of an agent template. + + Args: + agent_template_id (int): The ID of the agent template to update. + edited_agent_configs (dict): The updated agent configurations. + organisation (Depends): Dependency to get the user organisation. + + Returns: + HTTPException (status_code=200): If the agent gets successfully edited. + + Raises: + HTTPException (status_code=404): If the agent template is not found. + """ + + db_agent_template = db.session.query(AgentTemplate).filter(AgentTemplate.organisation_id == organisation.id, + AgentTemplate.id == agent_template_id).first() + if db_agent_template is None: + raise HTTPException(status_code=404, detail="Agent Template not found") + + if "name" in edited_agent_configs: + db_agent_template.name = edited_agent_configs["name"] + if "description" in edited_agent_configs: + db_agent_template.description = edited_agent_configs["description"] + + db.session.commit() + + agent_config_values = edited_agent_configs.get('agent_configs', {}) + + for key, value in agent_config_values.items(): + if isinstance(value, (list, dict)): + value = json.dumps(value) + config = db.session.query(AgentTemplateConfig).filter( + AgentTemplateConfig.agent_template_id == agent_template_id, + AgentTemplateConfig.key == key + ).first() + + if config is not None: + config.value = value + else: + new_config = AgentTemplateConfig( + agent_template_id=agent_template_id, + key=key, + value= value + ) + db.session.add(new_config) + + db.session.commit() + db.session.flush() + @router.post("/save_agent_as_template/{agent_id}") def save_agent_as_template(agent_id: str, From 9439e12c50d9e5c9160863035a23641f12b8a3fc Mon Sep 17 00:00:00 2001 From: anisha-contlo Date: Tue, 18 Jul 2023 12:46:14 +0530 Subject: [PATCH 2/7] updated frontend --- gui/pages/Content/Agents/AgentCreate.js | 86 +++++++++++++++++++++---- gui/pages/api/DashboardService.js | 4 ++ superagi/controllers/agent_template.py | 6 +- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/gui/pages/Content/Agents/AgentCreate.js b/gui/pages/Content/Agents/AgentCreate.js index eb51abe22..f8179b2cb 100644 --- a/gui/pages/Content/Agents/AgentCreate.js +++ b/gui/pages/Content/Agents/AgentCreate.js @@ -5,6 +5,7 @@ import 'react-toastify/dist/ReactToastify.css'; import styles from './Agents.module.css'; import { createAgent, + editAgentTemplate, fetchAgentTemplateConfigLocal, getOrganisationConfig, updateExecution, @@ -32,6 +33,7 @@ export default function AgentCreate({ }) { const [advancedOptions, setAdvancedOptions] = useState(false); const [agentName, setAgentName] = useState(""); + const [agentTemplateId, setAgentTemplateId] = useState(null); const [agentDescription, setAgentDescription] = useState(""); const [longTermMemory, setLongTermMemory] = useState(true); const [addResources, setAddResources] = useState(true); @@ -42,6 +44,7 @@ export default function AgentCreate({ const [maxIterations, setIterations] = useState(25); const [toolkitList, setToolkitList] = useState(toolkits) const [searchValue, setSearchValue] = useState(''); + const [showButton, setShowButton] = useState(false); const constraintsArray = [ "If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.", @@ -132,6 +135,7 @@ export default function AgentCreate({ setLocalStorageValue("agent_name_" + String(internalId), template.name, setAgentName); setLocalStorageValue("agent_description_" + String(internalId), template.description, setAgentDescription); setLocalStorageValue("advanced_options_" + String(internalId), true, setAdvancedOptions); + setLocalStorageValue("agent_template_id_" + String(internalId), template.id, setAgentTemplateId); fetchAgentTemplateConfigLocal(template.id) .then((response) => { @@ -146,6 +150,8 @@ export default function AgentCreate({ setLocalStorageValue("agent_database_" + String(internalId), data.LTM_DB, setDatabase); setLocalStorageValue("agent_model_" + String(internalId), data.model, setModel); setLocalStorageArray("tool_names_" + String(internalId), data.tools, setToolNames); + setLocalStorageValue("is_agent_template_" + String(internalId), true, setShowButton); + setShowButton(true); }) .catch((error) => { console.error('Error fetching template details:', error); @@ -357,33 +363,35 @@ export default function AgentCreate({ } }, [scheduleData]); - const handleAddAgent = () => { - if (!hasAPIkey) { + const validateAgentData = (isNewAgent) => { + if (isNewAgent && !hasAPIkey) { toast.error("Your OpenAI/Palm API key is empty!", {autoClose: 1800}); openNewTab(-3, "Settings", "Settings", false); - return + return false; } - + if (agentName.replace(/\s/g, '') === '') { toast.error("Agent name can't be blank", {autoClose: 1800}); - return + return false; } - if (agentDescription.replace(/\s/g, '') === '') { toast.error("Agent description can't be blank", {autoClose: 1800}); - return + return false; } - const isEmptyGoal = goals.some((goal) => goal.replace(/\s/g, '') === ''); if (isEmptyGoal) { toast.error("Goal can't be empty", {autoClose: 1800}); - return; + return false; } - if (selectedTools.length <= 0) { toast.error("Add atleast one tool", {autoClose: 1800}); - return + return false; } + return true; + } + + const handleAddAgent = () => { + if (!validateAgentData(true)) return; setCreateClickable(false); @@ -526,6 +534,46 @@ export default function AgentCreate({ event.preventDefault(); }; + function updateTemplate() { + + if (!validateAgentData(false)) return; + + let permission_type = permission; + if (permission.includes("RESTRICTED")) { + permission_type = "RESTRICTED"; + } + + const agentTemplateConfigData = { + "goal": goals, + "instruction": instructions, + "agent_type": agentType, + "constraints": constraints, + "tools": toolNames, + "exit": exitCriterion, + "iteration_interval": stepTime, + "model": model, + "max_iterations": maxIterations, + "permission_type": permission_type, + "LTM_DB": longTermMemory ? database : null, + } + const editTemplateData = { + "name": agentName, + "description": agentDescription, + "agent_configs": agentTemplateConfigData + } + + editAgentTemplate(agentTemplateId, editTemplateData) + .then((response) => { + if (response.status === 200) { + toast.success('Agent template has been updated successfully!', {autoClose: 1800}); + } + }) + .catch((error) => { + toast.error("Error updating agent template") + console.error('Error updating agent template:', error); + }); + }; + function setFileData(files) { if (files.length > 0) { const fileData = { @@ -574,11 +622,21 @@ export default function AgentCreate({ setAdvancedOptions(JSON.parse(advanced_options)); } + const is_agent_template = localStorage.getItem("is_agent_template_" + String(internalId)); + if (is_agent_template) { + setShowButton(true); + } + const agent_name = localStorage.getItem("agent_name_" + String(internalId)); if (agent_name) { setAgentName(agent_name); } + const agent_template_id = localStorage.getItem("agent_template_id_"+ String(internalId)); + if(agent_template_id){ + setAgentTemplateId(agent_template_id) + } + const agent_description = localStorage.getItem("agent_description_" + String(internalId)); if (agent_description) { setAgentDescription(agent_description); @@ -981,6 +1039,12 @@ export default function AgentCreate({ + {showButton && ( + + )}
{createDropdown && (
{ return api.put(`/agentexecutions/update/${executionId}`, executionData); }; +export const editAgentTemplate = (agentTemplateId, agentTemplateData) => { + return api.put(`/agent_templates/edit_agent_template/${agentTemplateId}`, agentTemplateData) +} + export const addExecution = (executionData) => { return api.post(`/agentexecutions/add`, executionData); }; diff --git a/superagi/controllers/agent_template.py b/superagi/controllers/agent_template.py index 6e59faa87..1bcb960a1 100644 --- a/superagi/controllers/agent_template.py +++ b/superagi/controllers/agent_template.py @@ -170,10 +170,8 @@ def edit_agent_template(agent_template_id: int, if db_agent_template is None: raise HTTPException(status_code=404, detail="Agent Template not found") - if "name" in edited_agent_configs: - db_agent_template.name = edited_agent_configs["name"] - if "description" in edited_agent_configs: - db_agent_template.description = edited_agent_configs["description"] + db_agent_template.name = edited_agent_configs["name"] + db_agent_template.description = edited_agent_configs["description"] db.session.commit() From 445e80a797b1e2ea6bf3cb632c5a8ce3a2c8ab99 Mon Sep 17 00:00:00 2001 From: anisha-contlo Date: Wed, 19 Jul 2023 11:58:43 +0530 Subject: [PATCH 3/7] made minor changes in frontend --- gui/pages/Content/Agents/AgentCreate.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/gui/pages/Content/Agents/AgentCreate.js b/gui/pages/Content/Agents/AgentCreate.js index f8179b2cb..1469594f3 100644 --- a/gui/pages/Content/Agents/AgentCreate.js +++ b/gui/pages/Content/Agents/AgentCreate.js @@ -22,15 +22,8 @@ import {EventBus} from "@/utils/eventBus"; import 'moment-timezone'; import AgentSchedule from "@/pages/Content/Agents/AgentSchedule"; -export default function AgentCreate({ - sendAgentData, - selectedProjectId, - fetchAgents, - toolkits, - organisationId, - template, - internalId - }) { +export default function AgentCreate({sendAgentData,selectedProjectId,fetchAgents,toolkits,organisationId,template,internalId}) { + const [advancedOptions, setAdvancedOptions] = useState(false); const [agentName, setAgentName] = useState(""); const [agentTemplateId, setAgentTemplateId] = useState(null); @@ -370,11 +363,11 @@ export default function AgentCreate({ return false; } - if (agentName.replace(/\s/g, '') === '') { + if (agentName && agentName.replace(/\s/g, '') === '') { toast.error("Agent name can't be blank", {autoClose: 1800}); return false; } - if (agentDescription.replace(/\s/g, '') === '') { + if (agentDescription && agentDescription.replace(/\s/g, '') === '') { toast.error("Agent description can't be blank", {autoClose: 1800}); return false; } From 5ba61e758439a98194390154a9b18484fba1e0fb Mon Sep 17 00:00:00 2001 From: anisha1607 Date: Wed, 19 Jul 2023 16:21:05 +0530 Subject: [PATCH 4/7] added unit test --- .../controllers/test_agent_template.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/unit_tests/controllers/test_agent_template.py diff --git a/tests/unit_tests/controllers/test_agent_template.py b/tests/unit_tests/controllers/test_agent_template.py new file mode 100644 index 000000000..c19f69815 --- /dev/null +++ b/tests/unit_tests/controllers/test_agent_template.py @@ -0,0 +1,87 @@ +from unittest.mock import patch, MagicMock +from superagi.models.agent_template import AgentTemplate +from superagi.models.agent_template_config import AgentTemplateConfig +from fastapi.testclient import TestClient +from main import app + +client = TestClient(app) + +@patch('superagi.controllers.agent_template.db') +@patch('superagi.helper.auth.db') +@patch('superagi.helper.auth.get_user_organisation') +def test_edit_agent_template_success(mock_get_user_org, mock_auth_db, mock_db): + # Create a mock agent template + mock_agent_template = AgentTemplate(id=1, name="Test Agent Template", description="Test Description") + # mock_agent_goals = AgentTemplateConfig() + + # Create a mock edited agent configuration + mock_edited_agent_configs = { + "name": "Updated Agent Template", + "description": "Updated Description", + "agent_configs": { + "goal": ["Create a simple pacman game for me.", "Write all files properly."], + "instruction": ["write spec","write code","improve the code","write test"], + "agent_type": "Don't Maintain Task Queue", + "constraints": ["If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.","Ensure the tool and args are as per current plan and reasoning","Exclusively use the tools listed under \"TOOLS\"","REMEMBER to format your response as JSON, using double quotes (\"\") around keys and string values, and commas (,) to separate items in arrays and objects. IMPORTANTLY, to use a JSON object as a string in another JSON object, you need to escape the double quotes."], + "tools": ["Read Email", "Send Email", "Write File"], + "exit": "No exit criterion", + "iteration_interval": 500, + "model": "gpt-4", + "max_iterations": 25, + "permission_type": "God Mode", + "LTM_DB": "Pinecone" + } + } + + # Mocking the user organisation + mock_get_user_org.return_value = MagicMock(id=1) + + # Create a session mock + session_mock = MagicMock() + mock_db.session = session_mock + mock_db.session.query.return_value.filter.return_value.first.return_value = mock_agent_template + mock_db.session.commit.return_value = None + mock_db.session.add.return_value = None + mock_db.session.flush.return_value = None + + mock_agent_template_config = AgentTemplateConfig(agent_template_id = 1, key="goal", value=["Create a simple pacman game for me.", "Write all files properly."]) + + + # Call the endpoint + response = client.put("agent_templates/edit_agent_template/1", json=mock_edited_agent_configs) + + assert response.status_code == 200 + + # Verify changes in the mock agent template + assert mock_agent_template.name == "Updated Agent Template" + assert mock_agent_template.description == "Updated Description" + assert mock_agent_template_config.key == "goal" + assert mock_agent_template_config.value == ["Create a simple pacman game for me.", "Write all files properly."] + + + session_mock.commit.assert_called() + session_mock.flush.assert_called() + + +@patch('superagi.controllers.agent_template.db') +@patch('superagi.helper.auth.db') +@patch('superagi.helper.auth.get_user_organisation') +def test_edit_agent_template_failure(mock_get_user_org, mock_auth_db, mock_db): + # Setup: The user organisation exists, but the agent template does not exist. + mock_get_user_org.return_value = MagicMock(id=1) + + # Create a session mock + session_mock = MagicMock() + mock_db.session = session_mock + mock_db.session.query.return_value.filter.return_value.first.return_value = None + + # Call the endpoint + response = client.put("agent_templates/edit_agent_template/1", json={}) + + # Verify: The response status code should be 404, indicating that the agent template was not found. + assert response.status_code == 404 + assert response.json() == {"detail": "Agent Template not found"} + + # Verify: The database commit method should not have been called because the agent template was not found. + session_mock.commit.assert_not_called() + session_mock.flush.assert_not_called() \ No newline at end of file From 4445cd194fd79644b597d2ad18af685aa1df0749 Mon Sep 17 00:00:00 2001 From: anisha1607 Date: Wed, 19 Jul 2023 16:55:53 +0530 Subject: [PATCH 5/7] made minor changes in frontend and backend --- gui/pages/Content/Agents/AgentCreate.js | 4 ++-- superagi/controllers/agent_template.py | 10 +++++----- tests/unit_tests/controllers/test_agent_template.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gui/pages/Content/Agents/AgentCreate.js b/gui/pages/Content/Agents/AgentCreate.js index 9a7f455f6..90aabea58 100644 --- a/gui/pages/Content/Agents/AgentCreate.js +++ b/gui/pages/Content/Agents/AgentCreate.js @@ -368,11 +368,11 @@ export default function AgentCreate({sendAgentData,selectedProjectId,fetchAgents return false; } - if (agentName && agentName.replace(/\s/g, '') === '') { + if (agentName.replace(/\s/g, '') === '') { toast.error("Agent name can't be blank", {autoClose: 1800}); return false; } - if (agentDescription && agentDescription.replace(/\s/g, '') === '') { + if (agentDescription.replace(/\s/g, '') === '') { toast.error("Agent description can't be blank", {autoClose: 1800}); return false; } diff --git a/superagi/controllers/agent_template.py b/superagi/controllers/agent_template.py index 1bcb960a1..100bee8d0 100644 --- a/superagi/controllers/agent_template.py +++ b/superagi/controllers/agent_template.py @@ -145,9 +145,9 @@ def update_agent_template(agent_template_id: int, return db_agent_template -@router.put("/edit_agent_template/{agent_template_id}", status_code=200) +@router.put("/update_agent_template/{agent_template_id}", status_code=200) def edit_agent_template(agent_template_id: int, - edited_agent_configs: dict, + updated_agent_configs: dict, organisation=Depends(get_user_organisation)): """ @@ -170,12 +170,12 @@ def edit_agent_template(agent_template_id: int, if db_agent_template is None: raise HTTPException(status_code=404, detail="Agent Template not found") - db_agent_template.name = edited_agent_configs["name"] - db_agent_template.description = edited_agent_configs["description"] + db_agent_template.name = updated_agent_configs["name"] + db_agent_template.description = updated_agent_configs["description"] db.session.commit() - agent_config_values = edited_agent_configs.get('agent_configs', {}) + agent_config_values = updated_agent_configs.get('agent_configs', {}) for key, value in agent_config_values.items(): if isinstance(value, (list, dict)): diff --git a/tests/unit_tests/controllers/test_agent_template.py b/tests/unit_tests/controllers/test_agent_template.py index c19f69815..415e5af59 100644 --- a/tests/unit_tests/controllers/test_agent_template.py +++ b/tests/unit_tests/controllers/test_agent_template.py @@ -48,7 +48,7 @@ def test_edit_agent_template_success(mock_get_user_org, mock_auth_db, mock_db): # Call the endpoint - response = client.put("agent_templates/edit_agent_template/1", json=mock_edited_agent_configs) + response = client.put("agent_templates/update_agent_template/1", json=mock_edited_agent_configs) assert response.status_code == 200 @@ -76,7 +76,7 @@ def test_edit_agent_template_failure(mock_get_user_org, mock_auth_db, mock_db): mock_db.session.query.return_value.filter.return_value.first.return_value = None # Call the endpoint - response = client.put("agent_templates/edit_agent_template/1", json={}) + response = client.put("agent_templates/update_agent_template/1", json={}) # Verify: The response status code should be 404, indicating that the agent template was not found. assert response.status_code == 404 From 863b6b738b6f208af2a5b7c5f2e5b158287773c4 Mon Sep 17 00:00:00 2001 From: anisha1607 Date: Wed, 19 Jul 2023 17:01:16 +0530 Subject: [PATCH 6/7] added another unit test --- .../controllers/test_agent_template.py | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/controllers/test_agent_template.py b/tests/unit_tests/controllers/test_agent_template.py index 415e5af59..050e9e216 100644 --- a/tests/unit_tests/controllers/test_agent_template.py +++ b/tests/unit_tests/controllers/test_agent_template.py @@ -15,7 +15,7 @@ def test_edit_agent_template_success(mock_get_user_org, mock_auth_db, mock_db): # mock_agent_goals = AgentTemplateConfig() # Create a mock edited agent configuration - mock_edited_agent_configs = { + mock_updated_agent_configs = { "name": "Updated Agent Template", "description": "Updated Description", "agent_configs": { @@ -48,7 +48,7 @@ def test_edit_agent_template_success(mock_get_user_org, mock_auth_db, mock_db): # Call the endpoint - response = client.put("agent_templates/update_agent_template/1", json=mock_edited_agent_configs) + response = client.put("agent_templates/update_agent_template/1", json=mock_updated_agent_configs) assert response.status_code == 200 @@ -84,4 +84,44 @@ def test_edit_agent_template_failure(mock_get_user_org, mock_auth_db, mock_db): # Verify: The database commit method should not have been called because the agent template was not found. session_mock.commit.assert_not_called() - session_mock.flush.assert_not_called() \ No newline at end of file + session_mock.flush.assert_not_called() + + +@patch('superagi.controllers.agent_template.db') +@patch('superagi.helper.auth.db') +@patch('superagi.helper.auth.get_user_organisation') +def test_edit_agent_template_with_new_config_success(mock_get_user_org, mock_auth_db, mock_db): + # Create a mock agent template + mock_agent_template = AgentTemplate(id=1, name="Test Agent Template", description="Test Description") + + # Create a mock edited agent configuration + mock_updated_agent_configs = { + "name": "Updated Agent Template", + "description": "Updated Description", + "agent_configs": { + "new_config_key": "New config value" # This is a new config + } + } + + # Mocking the user organisation + mock_get_user_org.return_value = MagicMock(id=1) + + # Create a session mock + session_mock = MagicMock() + mock_db.session = session_mock + mock_db.session.query.return_value.filter.return_value.first.return_value = mock_agent_template + mock_db.session.commit.return_value = None + mock_db.session.add.return_value = None + mock_db.session.flush.return_value = None + + # Call the endpoint + response = client.put("agent_templates/update_agent_template/1", json=mock_updated_agent_configs) + + assert response.status_code == 200 + + # Verify changes in the mock agent template + assert mock_agent_template.name == "Updated Agent Template" + assert mock_agent_template.description == "Updated Description" + + session_mock.commit.assert_called() + session_mock.flush.assert_called() \ No newline at end of file From f2917006556178972dd397c8f72a2306352b31d3 Mon Sep 17 00:00:00 2001 From: anisha1607 Date: Wed, 19 Jul 2023 18:55:11 +0530 Subject: [PATCH 7/7] added minor change in frontend --- gui/pages/Content/Agents/AgentCreate.js | 4 ++-- gui/pages/api/DashboardService.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/pages/Content/Agents/AgentCreate.js b/gui/pages/Content/Agents/AgentCreate.js index 90aabea58..0de46038a 100644 --- a/gui/pages/Content/Agents/AgentCreate.js +++ b/gui/pages/Content/Agents/AgentCreate.js @@ -368,11 +368,11 @@ export default function AgentCreate({sendAgentData,selectedProjectId,fetchAgents return false; } - if (agentName.replace(/\s/g, '') === '') { + if (agentName?.replace(/\s/g, '') === '') { toast.error("Agent name can't be blank", {autoClose: 1800}); return false; } - if (agentDescription.replace(/\s/g, '') === '') { + if (agentDescription?.replace(/\s/g, '') === '') { toast.error("Agent description can't be blank", {autoClose: 1800}); return false; } diff --git a/gui/pages/api/DashboardService.js b/gui/pages/api/DashboardService.js index a5ae10c8b..f78c005da 100644 --- a/gui/pages/api/DashboardService.js +++ b/gui/pages/api/DashboardService.js @@ -57,7 +57,7 @@ export const updateExecution = (executionId, executionData) => { }; export const editAgentTemplate = (agentTemplateId, agentTemplateData) => { - return api.put(`/agent_templates/edit_agent_template/${agentTemplateId}`, agentTemplateData) + return api.put(`/agent_templates/update_agent_template/${agentTemplateId}`, agentTemplateData) } export const addExecution = (executionData) => {