Skip to content

Commit

Permalink
Share model (#153)
Browse files Browse the repository at this point in the history
* share model

* fix

* Add SharedLoraLoader node (#154)

* Add SharedLoraLoader node

* poc of use js front end to get loras list (#156)

* poc

* refine

* refine

---------

Co-authored-by: FengWen <[email protected]>

* fix SharedLoraLoader

* Remove unused comments

---------

Co-authored-by: Yao Chi <[email protected]>

* refine message and add docs

---------

Co-authored-by: dantegarden <[email protected]>
Co-authored-by: FengWen <[email protected]>
Co-authored-by: Yao Chi <[email protected]>
  • Loading branch information
4 people authored Sep 29, 2024
1 parent ff1cff6 commit 48870fe
Show file tree
Hide file tree
Showing 18 changed files with 552 additions and 64 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# BizyAir

- [2024/09/29] 🌩️ BizyAir has support to share your custom LoRAs. [Share Your LoRAs](http://bizyair.siliconflow.cn/model-host/sharemodel.html)
- [2024/09/19] 🌩️ BizyAir has updated the model upload feature, allowing the upload process to display a progress bar, and the size of the models uploaded is no longer restricted by ComfyUI. (https://siliconflow.github.io/BizyAir/model-host/introduce.html)
- [2024/09/06] 🌩️ BizyAir supports InstantID for SDXL now. [SDXL InstantID workflow](./examples/bizyair_sdxl_InstantID_basic.json)
- [2024/09/05] 🌩️ BizyAir supports users in running custom LoRA models, including SDXL and Flux LoRA. [How to upload and run custom model](https://siliconflow.github.io/BizyAir/model-host/introduce.html)
Expand Down
Binary file added docs/docs/model-host/imgs/share_model01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/docs/model-host/imgs/share_model02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/docs/model-host/imgs/share_model03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/docs/model-host/imgs/usesharemodel01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/docs/model-host/imgs/usesharemodel03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions docs/docs/model-host/sharemodel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 分享自定义的 LoRA 模型

用户可以将已经上传的 BizyAir 模型设置为公开模式。这样可以直接让其他用户使用。

![](./imgs/usesharemodel03.png)

## 将模型设置为公开

点击 “Model/Remote Folders” 菜单。

![](./imgs/share_model01.png)

默认情况下,上传的模型都是非公开形式的,需要点击如图红圈所示的按钮,将模型设置为公开。

![](./imgs/share_model02.png)

切换到 Public "Yes" 的标签栏,可以查看已经设置为公开的模型,点击如图所示的标志,可以取消公开该模型。

![](./imgs/share_model03.png)

!!! note
已经公开的模型是不能删除的,如果想删除公开的模型,需要先将其设置为非公开,再在非公开的标签页中删除该模型。


## 使用公开的自定义 LoRA 模型

使用 "☁️BizyAir Shared Lora Loader" 节点,即可显示指定 shareid 下的公开 LoRA。


![](./imgs/usesharemodel03.png)

## 如何获取自己的 share id

登陆 [cloud.siliconflow.cn](https://cloud.siliconflow.cn),点击右上角,可以查看并复制自己的 share id。

![](./imgs/usesharemodel01.png)
4 changes: 3 additions & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,7 @@ nav:
- Controlnet Union: controlnet-union/introduce.md
- Controlnet Preprocessing: controlnet-preprocessor/introduce.md
- Kolors: kolors/introduce.md
- Model Host: model-host/introduce.md
- Model Host:
- Upload Custom Models: model-host/introduce.md
- Share Your Models: model-host/sharemodel.md
- Others: others/index.md
6 changes: 5 additions & 1 deletion js/apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ export function set_api_key ( data ) {
}

export function models_files ( data ) {
return customFetch(`/bizyair/modelhost/models/files?type=${data}`, {method: 'GET'})
return customFetch(`/bizyair/modelhost/models/files?type=${data.type}&public=${data.public}`, {method: 'GET'})
}

export function change_public ( data ) {
return customFetch(`/bizyair/modelhost/models/change_public`, {method: 'PUT', body: JSON.stringify(data)})
}

export function model_types () {
Expand Down
161 changes: 119 additions & 42 deletions js/dialog/modelList.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
import { dialog } from '../subassembly/dialog.js';
import { $el } from "../../../scripts/ui.js";
import { delModels, models_files, model_types } from "../apis.js"
import { delModels, models_files, model_types, change_public } from "../apis.js"
import { subscribe, unsubscribe } from '../subassembly/subscribers.js'

export const modelList = async () => {

const resList = await models_files('bizyair/lora');
let isPublic = 'false';
let type = 'bizyair/lora';
const resType = await model_types();
const listData = resList.data;
const typeList = resType.data;

const getData = async () => {
const elItemBody = document.querySelector('#bizyair-model-list-item-body')
return models_files({type, public: isPublic}).then(res => {
if (res.code == 20000) {
elItemBody.innerHTML = ''
const elData = elDataItem(res.data)
elData.length && elData.forEach(ele => {
elItemBody.appendChild(ele)
});
}
})
}
const elDataItemChild = (list) => {
return list.map(item => $el('div.bizyair-model-list-item-child.bizyair-model-list-item', {}, [
$el('div.bizyair-flex-item', { title: item.label_path}, [item.label_path]),
$el('div.bizyair-flex-item-avaulable', {}, [
item.available ? 'Available' : $el('span.spinner-container', {}, [$el('span.spinner')])
])
$el('div.bizyair-flex-item', {}, [
item.available
?
$el('span.available-word', {}, ['Available'])
:
$el('span.spinner-container.spinner-container-in-list', {}, [$el('span.spinner')])
]),
$el('div.bizyair-flex-item-avaulable', {}, [' '])
]))
}
const del = (name, ele) => {
Expand All @@ -25,7 +42,7 @@ export const modelList = async () => {
noText: "No",
onYes: () => {
delModels({
type: document.querySelector('#bizyair-model-filter').value,
type,
name,
}).then(res => {
if (res.code == 20000) {
Expand All @@ -36,6 +53,41 @@ export const modelList = async () => {
}
})
}
const share = (data, ele) => {
const changePublic = (publicStatus) => {
change_public({
type,
name: data.name,
public: publicStatus
}).then(res => {
if (res.code == 20000) {
ele.closest('.bizyair-model-list-item').remove()
}
})
}
if (!data.list[0].available) {
dialog({
content: "The model is not available, please try again later.",
noText: 'Close',
type: 'warning',
});
return
}
if (data.list[0] && data.list[0].public) {
dialog({
content: "Are you sure you want to cancel this?",
yesText: "Yes",
noText: "No",
onYes: () => {
changePublic(false)
return true
}
})
} else {
changePublic(true)
}
}

const handleItemLis = (ele) => {
ele.className = ele.className == 'bizyair-icon-fold' ? 'bizyair-icon-fold unfold' : 'bizyair-icon-fold';
ele.closest('.bizyair-model-list-item').querySelector('.bizyair-model-list-item-lis').style.display = ele.closest('.bizyair-model-list-item').querySelector('.bizyair-model-list-item-lis').style.display == 'none' ? 'block' : 'none'
Expand All @@ -50,12 +102,23 @@ export const modelList = async () => {
handleItemLis(this)
}
}, ['']),
$el('span', {}, [e.name]),
$el('span.bizyair-icon-delete', {
onclick: function() {
del(e.name, this)
}
}),
$el('span.bizyair-model-list-content', {}, [
$el('span', {}, [e.name]),
$el('span.bizyair-model-handle', {}, [
(isPublic !== 'true' ?
$el('span.bizyair-icon-delete', {
onclick: function() {
del(e.name, this)
}
}) : ''
),
$el(`span.bizyair-icon-share${isPublic === 'true' ? '.bizyair-icon-unshared' : ''}`, {
onclick: function() {
share(e, this)
}
}),
]),
]),
]),
$el('div.bizyair-model-list-item-lis',
{ style: { display: 'none' } },
Expand All @@ -65,51 +128,64 @@ export const modelList = async () => {
}

const changeType = (e) => {
const elItemBody = document.querySelector('#bizyair-model-list-item-body')
models_files(e.target.value).then(res => {
if (res.code == 20000) {
elItemBody.innerHTML = ''
const elData = elDataItem(res.data)
elData.length && elData.forEach(ele => {
elItemBody.appendChild(ele)
});
}
})
type = e.target.value;
getData();
}
const changePublic = async (e) => {
isPublic = e.target.value;
await getData();

}

const content = $el('div.bizyair-model-list', {}, [
$el('div.bizyair-model-filter-item', {}, [
$el("span.bizyair-filter-label", {}, ['Filter']),
$el("select.cm-input-item", {
id: 'bizyair-model-filter',
onchange: (e) => changeType(e)
}, [
...elOptions
$el("div.bizyair-model-filter-lis", {}, [
$el("span.bizyair-filter-label", {}, ['Filter']),
$el("select.cm-input-item", {
id: 'bizyair-model-filter',
onchange: (e) => changeType(e)
}, [
...elOptions
]),
]),
$el("div.bizyair-model-filter-lis", {}, [
$el("span.bizyair-filter-label", {}, ['Public']),
$el("label.radio-container", {}, [
$el("input", {
type: 'radio',
name: 'isPublic',
value: 'false',
checked: isPublic == 'false',
onchange: (e) => changePublic(e)
}),
'No'
]),
$el("label.radio-container", {}, [
$el("input", {
type: 'radio',
name: 'isPublic',
value: 'true',
onchange: (e) => changePublic(e)
}),
'Yes'
]),
])
]),

$el('div.bizyair-model-list-item.bizyair-model-list-item-header', {}, [
$el('div.bizyair-flex-item', {}, ['File Name']),
$el('div.bizyair-flex-item-avaulable', {}, ['Status']),
$el('div.bizyair-flex-item', {}, ['Status']),
$el('div.bizyair-flex-item-avaulable', {}, ['Operate']),
]),
$el('div.bizyair-model-list-item-body',
{ id: 'bizyair-model-list-item-body' },
elDataItem(listData)
[]
)
]);
const fnMessage = (data) => {
const res = JSON.parse(data.data);
if (res && res.type == "synced") {
const elItemBody = document.querySelector('#bizyair-model-list-item-body')
models_files(document.getElementById('bizyair-model-filter').value).then(res => {
if (res.code == 20000) {
elItemBody.innerHTML = ''
const elData = elDataItem(res.data)
elData.length && elData.forEach(ele => {
elItemBody.appendChild(ele)
});
}
})
getData();
}
}
subscribe('socketMessage', fnMessage);
Expand All @@ -120,4 +196,5 @@ export const modelList = async () => {
unsubscribe('socketMessage', fnMessage)
}
})
getData()
}
43 changes: 43 additions & 0 deletions js/share_lora_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { api } from "../../../scripts/api.js";
import { app } from "../../scripts/app.js";
app.registerExtension({
name: "bizyair.siliconcloud.share.lora.loader",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "BizyAir_SharedLoraLoader") {
async function onTextChange(share_id, canvas, comfynode) {
console.log("share_id:", share_id);
const response = await api.fetchApi(`/bizyair/modelhost/${share_id}/models/files?type=bizyair/lora`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

const { data: loras_list } = await response.json();
const lora_name_widget = comfynode.widgets.find(widget => widget.name === "lora_name");
if (loras_list.length > 0) {
lora_name_widget.value = loras_list[0];
lora_name_widget.options.values = loras_list;
} else {
console.log("No loras found in the response");
lora_name_widget.value = "";
lora_name_widget.options.values = [];
}
}

function setWigetCallback(){
const shareid_widget = this.widgets.find(widget => widget.name === "share_id");
if (shareid_widget) {
shareid_widget.callback = onTextChange;
} else {
console.log("share_id widget not found");
}
}
const onNodeCreated = nodeType.prototype.onNodeCreated
nodeType.prototype.onNodeCreated = function () {
onNodeCreated?.apply(this, arguments);
setWigetCallback.call(this, arguments);
};
}
},
})
Loading

0 comments on commit 48870fe

Please sign in to comment.