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

Models Hosting #1239

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
2 changes: 1 addition & 1 deletion gui/pages/Content/Models/AddModelMarketPlace.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default function AddModelMarketPlace({ template, getModels, sendModelData
</div>}

{templateData.provider === 'Hugging Face' && <div className="horizontal_container align_start info_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_info.svg" alt="error-icon" />
<Image width={16} height={16} src="/images/icon_info.svg" alt="info-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">In order to get the endpoint for this model, you will need to deploy it on your Replicate dashboard. Once you have deployed your model on Hugging Face, you will be able to access the endpoint through the Hugging Face dashboard. The endpoint is a URL that you can use to send requests to your model.</span>
<button className="secondary_button_small w_fit_content mt_16"
Expand Down
15 changes: 11 additions & 4 deletions gui/pages/Content/Models/ModelDetails.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, {useState, useEffect} from "react";
import Image from "next/image";
import ModelMetrics from "./ModelMetrics";
import ModelInfo from "./ModelInfo";
import ModelReadMe from "./ModelReadMe";
import {fetchModel} from "@/pages/api/DashboardService";
import {loadingTextEffect} from "@/utils/utils";

export default function ModelDetails({modelId, modelName}){
const [modelDetails, setModelDetails] = useState([])
const [selectedOption, setSelectedOption] = useState('metrics')
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Models");
const [loadingText, setLoadingText] = useState("Loading Models")

useEffect(() => {
loadingTextEffect('Loading Models', setLoadingText, 500);
Expand All @@ -26,20 +26,27 @@ export default function ModelDetails({modelId, modelName}){
fetchModelDetails().then().catch();
},[])

const goToTab = (tab) => {
setSelectedOption(tab)
}

return(
<div id="model_details" className="col-12 padding_5 overflowY_auto h_calc92">
{!isLoading && <div className="vertical_containers padding_16_8">
<span className="text_16">{ modelDetails.name ? (modelDetails.name.split('/')[1] || modelDetails.name) : ""}</span>
<span className="text_12 color_gray mt_8 lh_18">{modelDetails.description}</span>
<div className="horizontal_container gap_4 mt_16 mb_2">
<div className="horizontal_container gap_4 mt_16 pb_8 bb_white">
<button className={selectedOption === 'metrics' ? 'tab_button_selected' : 'tab_button'}
onClick={() => setSelectedOption('metrics')}>Metrics</button>
<button className={selectedOption === 'details' ? 'tab_button_selected' : 'tab_button'}
onClick={() => setSelectedOption('details')}>Details</button>
<button className={selectedOption === 'readme' ? 'tab_button_selected' : 'tab_button'}
onClick={() => setSelectedOption('readme')}>ReadMe</button>
</div>
</div>}
{selectedOption === 'metrics' && !isLoading && <ModelMetrics modelDetails={modelDetails} />}
{selectedOption === 'details' && !isLoading && <ModelInfo modelDetails={modelDetails} />}
{selectedOption === 'details' && !isLoading && <ModelInfo modelDetails={modelDetails} goToTab={goToTab} />}
{selectedOption === 'readme' && !isLoading && <ModelReadMe modelDetails={modelDetails} /> }
{isLoading && <div className="loading_container h_75vh"><div className="signInInfo loading_text">{loadingText}</div></div>}
</div>
)
Expand Down
25 changes: 16 additions & 9 deletions gui/pages/Content/Models/ModelInfo.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import React, {useState, useEffect} from "react";
import Image from "next/image";

export default function ModelInfo(modelDetails){
const [modelData, setModelData] = useState(modelDetails?.modelDetails)
export default function ModelInfo({modelDetails, goToTab}){

return(
<div id="model_info" className="text_12">
<hr className="horizontal_line padding_0" />
<div className="row">
<div className="col-3" />
<div className="col-6 col-6-scrollable vertical_containers">
<div className="horizontal_container align_start info_box mb_24 gap_6">
<Image width={16} height={16} src="/images/icon_info.svg" alt="info-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">Please follow the ReadMe provided to setup this model and start using it.</span>
<button className="secondary_button_small w_fit_content mt_16" onClick={() => goToTab("readme")}>Go to Readme<Image src="/images/open_in_new.svg" alt="deploy_icon" width={12} height={12} className="ml_4" /></button>
</div>
</div>

<span>Installation Type</span>
<div className="horizontal_container mt_8 color_white gap_4">
{modelData === 'Marketplace' && <Image width={16} height={16} src="/images/marketplace_logo.png" alt="marketplace_logo" />}
<span>{modelData.type}</span>
{modelDetails === 'Marketplace' && <Image width={16} height={16} src="/images/marketplace_logo.png" alt="marketplace_logo" />}
<span>{modelDetails.type}</span>
</div>

<span className="mt_24">Model Provider</span>
<span className="mt_8 color_white">{modelData.model_provider}</span>
<span className="mt_8 color_white">{modelDetails.model_provider}</span>

{modelData.end_point && <div className="vertical_containers">
{modelDetails.end_point && <div className="vertical_containers">
<span className="mt_24">Model Endpoint</span>
<span className="mt_8 color_white">{modelData.end_point}</span>
<span className="mt_8 color_white">{modelDetails.end_point}</span>
</div>}

<span className="mt_24">Token Limit</span>
<input className="input_medium mt_8" type="number" placeholder="Enter Model Token Limit" value={modelData.token_limit} disabled/>
<input className="input_medium mt_8" type="number" placeholder="Enter Model Token Limit" value={modelDetails.token_limit} disabled/>
</div>
<div className="col-3" />
</div>
Expand Down
46 changes: 46 additions & 0 deletions gui/pages/Content/Models/ModelReadMe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, {useEffect, useState} from "react";
import {fetchModelReadme} from "@/pages/api/DashboardService";
import Image from "next/image";

export default function ModelReadMe({modelDetails}) {
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading ReadMe")
const [readmeContent, setReadmeContent] = useState(null)
const [contentType, setContentType] = useState("python")
const [allReadmeContent, setAllReadmeContent] = useState(null)

useEffect(() => {
fetchAllReadmeContent().then().catch()
}, [])

async function fetchAllReadmeContent() {
const response = await fetchModelReadme(modelDetails.id);
if(response) {
setAllReadmeContent(response.data);
filterReadmeContent(response.data);
}
}

function filterReadmeContent(data) {
const filteredData = data.filter(item => item.language === contentType);
setReadmeContent(filteredData);
}

async function handleSelection(code_language) {
setContentType(code_language)
filterReadmeContent(allReadmeContent)
}

return (
<div id="model_readme" className="display_column_container overflowY_scroll color_white">
<div className="horizontal_container gap_4 pb_8 bb_white">
<button className={contentType === 'python' ? 'tab_button_selected' : 'tab_button'} onClick={() => handleSelection("python")}>
<Image width={18} height={18} src="/images/python_logo.svg" alt="python_logo" />Python</button>
<button className={contentType === 'javascript' ? 'tab_button_selected' : 'tab_button'} onClick={() => handleSelection("javascript")}>
<Image width={18} height={18} src="/images/javascript_logo.svg" alt="javascript_logo" />Javascript</button>
</div>
{readmeContent && <div dangerouslySetInnerHTML={{ __html: readmeContent[0].content }} />}
{/*{isLoading && <div className="loading_container h_75vh"><div className="signInInfo loading_text">{loadingText}</div></div>}*/}
</div>
)
}
23 changes: 19 additions & 4 deletions gui/pages/Content/Models/ModelTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import Image from "next/image";
import {EventBus} from "@/utils/eventBus";
import {getFormattedDate, modelIcon} from "@/utils/utils";
import AddModelMarketPlace from "./AddModelMarketPlace";
import {deleteModel} from "@/pages/api/DashboardService";

export default function ModelTemplate({env, template, getModels, sendModelData}){
const [isInstalled, setIsInstalled] = useState(false);

Expand All @@ -23,6 +25,16 @@ export default function ModelTemplate({env, template, getModels, sendModelData})
}
}

async function uninstallModel(model_name) {
console.log(model_name)
const response = await deleteModel(model_name);
if(response) {
console.log(response)
}
}

console.log(template)

return (
<div id="model_template">
<div className="back_button mt_16 mb_16" onClick={() => isInstalled ? setIsInstalled(false) : handleBackClick()}>
Expand All @@ -33,10 +45,13 @@ export default function ModelTemplate({env, template, getModels, sendModelData})
<div className="col_3 display_column_container padding_16">
<span className="text_20 color_white">{template.model_name}</span>
<span className="text_12 color_gray mt_4">by {template.model_name.includes('/') ? template.model_name.split('/')[0] : template.provider}</span>
<button className="primary_button w_100 mt_16" disabled={template.is_installed} onClick={() => handleInstallClick()}>
<Image width={16} height={16} src={template.is_installed ? '/images/tick.svg' : '/images/marketplace_download.svg'} alt="download-icon" />
<span className="ml_8">{template.is_installed ? 'Installed' : 'Install'}</span>
</button>
<div className="w_100 mt_16">
<button className="primary_button w_100" disabled={template.is_installed} onClick={() => handleInstallClick()}>
<Image width={16} height={16} src={template.is_installed ? '/images/tick.svg' : '/images/marketplace_download.svg'} alt="download-icon" />
<span className="ml_8">{template.is_installed ? 'Installed' : 'Install'}</span>
</button>
<button onClick={() => uninstallModel(template.model_name)}>UNINSTALL</button>
</div>

<hr className="horizontal_line" />
<span className="text_12 color_white lh_18">{template.description}</span>
Expand Down
28 changes: 28 additions & 0 deletions gui/pages/_app.css
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ p {
justify-content: space-between;
}

.mt_0{margin-top: 0;}
.mt_4{margin-top: 4px;}
.mt_5{margin-top: 5px;}
.mt_6{margin-top: 6px;}
Expand All @@ -772,6 +773,7 @@ p {
.mt_80{margin-top: 80px;}
.mt_90{margin-top: 90px;}

.mb_0{margin-bottom: 0;}
.mb_1{margin-bottom: 1px;}
.mb_2{margin-bottom: 2px;}
.mb_3{margin-bottom: 3px;}
Expand Down Expand Up @@ -1144,6 +1146,7 @@ p {
.border_bottom_grey{border-bottom: 1px solid rgba(255, 255, 255, 0.08)}

.bt_white{border-top: 1px solid rgba(255, 255, 255, 0.08);}
.bb_white{border-bottom: 1px solid rgba(255, 255, 255, 0.08);}

.color_white{color:#FFFFFF}
.color_gray{color:#888888}
Expand All @@ -1153,6 +1156,8 @@ p {
.lh_18{line-height: 18px;}
.lh_24{line-height: 24px;}

.pb_8{padding-bottom: 8px;}

.padding_0{padding: 0;}
.padding_5{padding: 5px;}
.padding_6{padding: 6px;}
Expand Down Expand Up @@ -1889,3 +1894,26 @@ tr{
}




/*----------------------------------------------------------------------------------*/

.public_sans {
font-family: Public Sans, sans-serif;
font-style: normal;
line-height: 16px;
}
.text_18 {font-size: 18px;}
.code_box {
display: flex;
padding: 16px 8px;
align-items: flex-start;
gap: 10px;
align-self: stretch;
border: 1px solid rgba(255, 255, 255, 0.10);
background: rgba(255, 255, 255, 0.04);
font-family: Space Mono, monospace;
line-height: 16px;
font-style: normal;
}

8 changes: 8 additions & 0 deletions gui/pages/api/DashboardService.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,14 @@ export const fetchMarketPlaceModel = () => {
return api.get(`/models_controller/get/list`)
}

export const deleteModel = (model_name) => {
return api.post(`/models_controller/delete_model`, { model: model_name })
}

export const fetchModelReadme = (model_id) => {
return api.get(`/models_controller/fetch_model_readme/${model_id}`)
}

export const getToolMetrics = (toolName) => {
return api.get(`analytics/tools/${toolName}/usage`)
}
Expand Down
9 changes: 9 additions & 0 deletions gui/public/images/javascript_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions gui/public/images/python_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""create models_readme_content table

Revision ID: 7d4a89ef9c23
Revises: f35c9b59b5a6
Create Date: 2023-09-18 08:53:41.247571

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '7d4a89ef9c23'
down_revision = '3867bb00a495'
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('models_readme_content',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('content', sa.String(), nullable=False),
sa.Column('model_id', sa.Integer(), nullable=False),
sa.Column('code_language', sa.String(), nullable=False),
sa.Column('org_id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.add_column('models', sa.Column('state', sa.String(), nullable=False, server_default='INSTALLED'))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('models', 'state')
op.drop_table('models_readme_content')
# ### end Alembic commands ###
27 changes: 24 additions & 3 deletions superagi/controllers/models_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from superagi.helper.auth import check_auth, get_user_organisation
from superagi.helper.models_helper import ModelsHelper
from superagi.apm.call_log_helper import CallLogHelper
from superagi.models.readme_content import ReadmeContent
from superagi.models.models import Models
from superagi.models.models_config import ModelsConfig
from superagi.config.config import get_config
Expand All @@ -27,9 +28,11 @@ class StoreModelRequest(BaseModel):
type: str
version: str


class ModelName (BaseModel):
model: str


@router.post("/store_api_keys", status_code=200)
async def store_api_keys(request: ValidateAPIKeyRequest, organisation=Depends(get_user_organisation)):
try:
Expand Down Expand Up @@ -102,6 +105,24 @@ async def fetch_data(request: ModelName, organisation=Depends(get_user_organisat
raise HTTPException(status_code=500, detail="Internal Server Error")


@router.post("/delete_model", status_code=200)
async def delete_model(request: ModelName, organisation=Depends(get_user_organisation)):
try:
return Models.delete_model(db.session, organisation.id, request.model)
except Exception as e:
logging.error(f"Error Deleting Model: {str(e)}")
raise HTTPException(status_code=500, detail="Internal Server Error")


@router.get("/fetch_model_readme/{model_id}", status_code=200)
async def model_readme(model_id: int, organisation=Depends(get_user_organisation)):
try:
return ReadmeContent.fetch_model_readme(db.session, organisation.id, model_id)
except Exception as e:
logging.error(f"Error Fetching the Model Readme: {str(e)}")
raise HTTPException(status_code=500, detail="Internal Server Error")


@router.get("/get/list", status_code=200)
def get_models_list(page: int = 0, organisation=Depends(get_user_organisation)):
"""
Expand All @@ -123,9 +144,9 @@ def get_models_list(page: int = 0, organisation=Depends(get_user_organisation)):

@router.get("/marketplace/list/{page}", status_code=200)
def get_marketplace_models_list(page: int = 0):
organisation_id = get_config("MARKETPLACE_ORGANISATION_ID")
if organisation_id is not None:
organisation_id = int(organisation_id)
organisation_id = 2
# if organisation_id is not None:
# organisation_id = int(organisation_id)
page_size = 16

query = db.session.query(Models).filter(Models.org_id == organisation_id)
Expand Down
15 changes: 15 additions & 0 deletions superagi/controllers/types/is_installed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from enum import Enum

class IsInstalled(Enum):
INSTALLED = 'INSTALLED'
UNINSTALLED = 'UNINSTALLED'

@classmethod
def get_install_state(cls, is_installed):
if is_installed is None:
raise ValueError("Queue status type cannot be None.")
is_installed = is_installed.upper()

if is_installed in cls.__members__:
return cls[is_installed]
raise ValueError(f"{is_installed} is not a valid storage name.")
Loading
Loading