Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Toolkit update #937

Merged
merged 12 commits into from
Aug 3, 2023
82 changes: 56 additions & 26 deletions gui/pages/Content/Marketplace/ToolkitTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import styles3 from '../Agents/Agents.module.css';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import styles2 from "./Market.module.css"
import {fetchToolTemplateOverview, installToolkitTemplate} from "@/pages/api/DashboardService";
import {
checkToolkitUpdate,
fetchToolTemplateOverview,
installToolkitTemplate,
updateMarketplaceToolTemplate
} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import ReactMarkdown from 'react-markdown';
import axios from 'axios';
Expand All @@ -17,29 +22,18 @@ export default function ToolkitTemplate({template, env}) {
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);
});
if(template.is_installed && !window.location.href.toLowerCase().includes('marketplace')) {
checkToolkitUpdate(template.name).then((response) => {
setInstalled(response.data ? 'Update' : 'Installed');
})
.catch((error) => {
console.error('Error fetching update details:', error);
});
}
else{
setInstalled(window.location.href.toLowerCase().includes('marketplace') ? 'Sign in to install' : 'Install');
}
fetchReadme()
}, []);

function handleInstallClick() {
Expand All @@ -53,6 +47,18 @@ export default function ToolkitTemplate({template, env}) {
return;
}

if(installed === "Update"){
updateMarketplaceToolTemplate(template.name)
.then((response) => {
toast.success("Template Updated", {autoClose: 1800});
setInstalled('Installed');
})
.catch((error) => {
console.error('Error installing template:', error);
});
return;
}

if (template && template.is_installed) {
toast.error("Template is already installed", {autoClose: 1800});
return;
Expand All @@ -72,6 +78,30 @@ export default function ToolkitTemplate({template, env}) {
EventBus.emit('goToMarketplace', {});
}

function fetchReadme() {
if (window.location.href.toLowerCase().includes('marketplace')) {
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);
});
}
}

return (
<>
<div>
Expand All @@ -96,10 +126,10 @@ export default function ToolkitTemplate({template, env}) {
<button className="primary_button" style={{
marginTop: '15px',
width: '100%',
background: template && template.is_installed ? 'rgba(255, 255, 255, 0.14)' : '#FFF',
color: template && template.is_installed ? '#FFFFFF' : '#000'
background: template && template.is_installed && installed !== 'Update' ? 'rgba(255, 255, 255, 0.14)' : '#FFF',
color: template && template.is_installed && installed !== 'Update' ? '#FFFFFF' : '#000'
}} onClick={() => handleInstallClick()}>
{(template && template.is_installed) ?
{(template && template.is_installed && installed !== 'Update') ?
<Image width={14} height={14} src="/images/tick.svg" alt="tick-icon"/> :
<Image width={14} height={14} src="/images/upload_icon_dark.svg"
alt="upload-icon"/>}&nbsp;{installed}</button>
Expand Down
8 changes: 8 additions & 0 deletions gui/pages/api/DashboardService.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,18 @@ export const fetchToolTemplateOverview = (toolTemplateName) => {
return api.get(`/toolkits/marketplace/readme/${toolTemplateName}`);
};

export const updateMarketplaceToolTemplate = (templateName) => {
return api.put(`/toolkits/update/${templateName}`);
};

export const installToolkitTemplate = (templateName) => {
return api.get(`/toolkits/get/install/${templateName}`);
};

export const checkToolkitUpdate = (templateName) => {
return api.get(`/toolkits/check_update/${templateName}`);
};

export const getExecutionDetails = (executionId, agentId) => {
return api.get(`/agent_executions_configs/details/agent/${agentId}/agent_execution/${executionId}`);
};
Expand Down
62 changes: 60 additions & 2 deletions superagi/controllers/toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from superagi.models.tool_config import ToolConfig
from superagi.models.toolkit import Toolkit
from superagi.types.common import GitHubLinkRequest
from superagi.helper.tool_helper import compare_toolkit
from superagi.helper.encyption_helper import decrypt_data, is_encrypted

router = APIRouter()
Expand Down Expand Up @@ -148,8 +149,8 @@ def install_toolkit_from_marketplace(toolkit_name: str,
toolkit = Toolkit.fetch_marketplace_detail(search_str="details",
toolkit_name=toolkit_name)
db_toolkit = Toolkit.add_or_update(session=db.session, name=toolkit['name'], description=toolkit['description'],
tool_code_link=toolkit['tool_code_link'], organisation_id=organisation.id,
show_toolkit=toolkit['show_toolkit'])
tool_code_link=toolkit['tool_code_link'], organisation_id=organisation.id,
show_toolkit=toolkit['show_toolkit'])
for tool in toolkit['tools']:
Tool.add_or_update(session=db.session, tool_name=tool['name'], description=tool['description'],
folder_name=tool['folder_name'], class_name=tool['class_name'], file_name=tool['file_name'],
Expand Down Expand Up @@ -306,3 +307,60 @@ def get_installed_toolkit_list(organisation: Organisation = Depends(get_user_org
toolkit.tools = toolkit_tools

return toolkits


@router.get("/check_update/{toolkit_name}")
def check_toolkit_update(toolkit_name: str, organisation: Organisation = Depends(get_user_organisation)):
"""
Check if there is an update available for the installed tool kits.

Returns:
dict: The response containing the update details.

"""
marketplace_toolkit = Toolkit.fetch_marketplace_detail(search_str="details",
toolkit_name=toolkit_name)
if marketplace_toolkit is None:
raise HTTPException(status_code=404, detail="Toolkit not found in marketplace")
installed_toolkit = Toolkit.get_toolkit_from_name(db.session, toolkit_name, organisation)
if installed_toolkit is None:
return True
installed_toolkit = installed_toolkit.to_dict()
tools = Tool.get_toolkit_tools(db.session, installed_toolkit["id"])
configs = ToolConfig.get_toolkit_tool_config(db.session, installed_toolkit["id"])
installed_toolkit["configs"] = []
installed_toolkit["tools"] = []

for config in configs:
installed_toolkit["configs"].append(config.to_dict())
for tool in tools:
installed_toolkit["tools"].append(tool.to_dict())

return compare_toolkit(marketplace_toolkit, installed_toolkit)


@router.put("/update/{toolkit_name}")
def update_toolkit(toolkit_name: str, organisation: Organisation = Depends(get_user_organisation)):
"""
Update the toolkit with the latest version from the marketplace.
"""
marketplace_toolkit = Toolkit.fetch_marketplace_detail(search_str="details",
toolkit_name=toolkit_name)

update_toolkit = Toolkit.add_or_update(
db.session,
name=marketplace_toolkit["name"],
description=marketplace_toolkit["description"],
show_toolkit=True if len(marketplace_toolkit["tools"]) > 1 else False,
organisation_id=organisation.id,
tool_code_link=marketplace_toolkit["tool_code_link"]
)

for tool in marketplace_toolkit["tools"]:
Tool.add_or_update(db.session, tool_name=tool["name"], folder_name=tool["folder_name"],
class_name=tool["class_name"], file_name=tool["file_name"],
toolkit_id=update_toolkit.id, description=tool["description"])

for tool_config_key in marketplace_toolkit["configs"]:
ToolConfig.add_or_update(db.session, toolkit_id=update_toolkit.id,
key=tool_config_key["key"])
40 changes: 39 additions & 1 deletion superagi/helper/tool_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,14 @@ def register_toolkits(session, organisation):
process_files(tool_paths, session, organisation)
logger.info(f"Toolkits Registered Successfully for Organisation ID : {organisation.id}!")


def register_marketplace_toolkits(session, organisation):
tool_paths = ["superagi/tools", "superagi/tools/external_tools","superagi/tools/marketplace_tools"]
tool_paths = ["superagi/tools", "superagi/tools/external_tools", "superagi/tools/marketplace_tools"]
if organisation is not None:
process_files(tool_paths, session, organisation)
logger.info(f"Marketplace Toolkits Registered Successfully for Organisation ID : {organisation.id}!")


def extract_repo_name(repo_link):
# Extract the repository name from the link
# Assuming the GitHub link format: https://github.com/username/repoName
Expand Down Expand Up @@ -304,3 +306,39 @@ def handle_tools_import():
folder_dir = os.path.join(tool_path, folder_name)
if os.path.isdir(folder_dir):
sys.path.append(folder_dir)


def compare_tools(tool1, tool2):
fields = ["name", "description"]
return any(tool1.get(field) != tool2.get(field) for field in fields)


def compare_configs(config1, config2):
fields = ["key"]
return any(config1.get(field) != config2.get(field) for field in fields)


def compare_toolkit(toolkit1, toolkit2):
main_toolkit_fields = ["description", "show_toolkit", "name", "tool_code_link"]
toolkit_diff = any(toolkit1.get(field) != toolkit2.get(field) for field in main_toolkit_fields)

tools1 = sorted(toolkit1.get("tools", []), key=lambda tool: tool.get("name", ""))
tools2 = sorted(toolkit2.get("tools", []), key=lambda tool: tool.get("name", ""))

if len(tools1) != len(tools2):
tools_diff = True
else:
tools_diff = any(compare_tools(tool1, tool2) for tool1, tool2 in zip(tools1, tools2))

tool_configs1 = sorted(toolkit1.get("configs", []), key=lambda config: config.get("key", ""))
tool_configs2 = sorted(toolkit2.get("configs", []), key=lambda config: config.get("key", ""))
if len(tool_configs1) != len(tool_configs2):
tool_configs_diff = True
else:
tool_configs_diff = any(compare_configs(config1, config2) for config1, config2 in zip(tool_configs1,
tool_configs2))

print("toolkit_diff : ", toolkit_diff)
print("tools_diff : ", tools_diff)
print("tool_configs_diff : ", tool_configs_diff)
return toolkit_diff or tools_diff or tool_configs_diff
22 changes: 21 additions & 1 deletion superagi/models/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ def __repr__(self):
return f"Tool(id={self.id}, name='{self.name}',description='{self.description}' folder_name='{self.folder_name}'," \
f" file_name = {self.file_name}, class_name='{self.class_name}, toolkit_id={self.toolkit_id}')"

def to_dict(self):
"""
Convert the Tool instance to a dictionary.

Returns:
dict: A dictionary representation of the Tool instance.
"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"folder_name": self.folder_name,
"class_name": self.class_name,
"file_name": self.file_name,
"toolkit_id": self.toolkit_id
}
@staticmethod
def add_or_update(session, tool_name: str, description: str, folder_name: str, class_name: str, file_name: str,
toolkit_id: int):
Expand Down Expand Up @@ -100,7 +116,7 @@ def convert_tool_ids_to_names(cls, db, tool_ids):

tools = db.session.query(Tool).filter(Tool.id.in_(tool_ids)).all()
return [str(tool.name) for tool in tools]

@classmethod
def get_invalid_tools(cls, tool_ids, session):
invalid_tool_ids = []
Expand All @@ -109,3 +125,7 @@ def get_invalid_tools(cls, tool_ids, session):
if tool is None:
invalid_tool_ids.append(tool_id)
return invalid_tool_ids

@classmethod
def get_toolkit_tools(cls, session, toolkit_id : int):
return session.query(Tool).filter(Tool.toolkit_id == toolkit_id).all()
4 changes: 4 additions & 0 deletions superagi/models/tool_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@ def add_or_update(session: Session, toolkit_id: int, key: str, value: str = None
session.add(tool_config)

session.commit()

@classmethod
def get_toolkit_tool_config(cls, session: Session, toolkit_id: int):
return session.query(ToolConfig).filter_by(toolkit_id=toolkit_id).all()
Loading
Loading