mirror of
https://github.com/open-webui/open-webui
synced 2025-06-11 00:49:44 +00:00
feat: model ordering
This commit is contained in:
parent
9c67a94542
commit
fc2b314c4f
6
package-lock.json
generated
6
package-lock.json
generated
@ -24,6 +24,7 @@
|
|||||||
"katex": "^0.16.9",
|
"katex": "^0.16.9",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"pyodide": "^0.26.0-alpha.4",
|
"pyodide": "^0.26.0-alpha.4",
|
||||||
|
"sortablejs": "^1.15.2",
|
||||||
"svelte-sonner": "^0.3.19",
|
"svelte-sonner": "^0.3.19",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
@ -6913,6 +6914,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sortablejs": {
|
||||||
|
"version": "1.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
|
||||||
|
"integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
"katex": "^0.16.9",
|
"katex": "^0.16.9",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"pyodide": "^0.26.0-alpha.4",
|
"pyodide": "^0.26.0-alpha.4",
|
||||||
|
"sortablejs": "^1.15.2",
|
||||||
"svelte-sonner": "^0.3.19",
|
"svelte-sonner": "^0.3.19",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
|
@ -29,8 +29,24 @@ export const getModels = async (token: string = '') => {
|
|||||||
|
|
||||||
models = models
|
models = models
|
||||||
.filter((models) => models)
|
.filter((models) => models)
|
||||||
|
// Sort the models
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
// Compare case-insensitively
|
// Check if models have position property
|
||||||
|
const aHasPosition = a.info?.meta?.position !== undefined;
|
||||||
|
const bHasPosition = b.info?.meta?.position !== undefined;
|
||||||
|
|
||||||
|
// If both a and b have the position property
|
||||||
|
if (aHasPosition && bHasPosition) {
|
||||||
|
return a.info.meta.position - b.info.meta.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only a has the position property, it should come first
|
||||||
|
if (aHasPosition) return -1;
|
||||||
|
|
||||||
|
// If only b has the position property, it should come first
|
||||||
|
if (bHasPosition) return 1;
|
||||||
|
|
||||||
|
// Compare case-insensitively by name for models without position property
|
||||||
const lowerA = a.name.toLowerCase();
|
const lowerA = a.name.toLowerCase();
|
||||||
const lowerB = b.name.toLowerCase();
|
const lowerB = b.name.toLowerCase();
|
||||||
|
|
||||||
@ -39,8 +55,8 @@ export const getModels = async (token: string = '') => {
|
|||||||
|
|
||||||
// If same case-insensitively, sort by original strings,
|
// If same case-insensitively, sort by original strings,
|
||||||
// lowercase will come before uppercase due to ASCII values
|
// lowercase will come before uppercase due to ASCII values
|
||||||
if (a < b) return -1;
|
if (a.name < b.name) return -1;
|
||||||
if (a > b) return 1;
|
if (a.name > b.name) return 1;
|
||||||
|
|
||||||
return 0; // They are equal
|
return 0; // They are equal
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import Sortable from 'sortablejs';
|
||||||
|
|
||||||
import fileSaver from 'file-saver';
|
import fileSaver from 'file-saver';
|
||||||
const { saveAs } = fileSaver;
|
const { saveAs } = fileSaver;
|
||||||
|
|
||||||
import { onMount, getContext } from 'svelte';
|
import { onMount, getContext, tick } from 'svelte';
|
||||||
|
|
||||||
import { WEBUI_NAME, modelfiles, models, settings, user } from '$lib/stores';
|
import { WEBUI_NAME, modelfiles, models, settings, user } from '$lib/stores';
|
||||||
import { addNewModel, deleteModelById, getModelInfos, updateModelById } from '$lib/apis/models';
|
import { addNewModel, deleteModelById, getModelInfos, updateModelById } from '$lib/apis/models';
|
||||||
@ -12,6 +14,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
import { getModels } from '$lib/apis';
|
import { getModels } from '$lib/apis';
|
||||||
|
|
||||||
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
|
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
|
||||||
import ModelMenu from './Models/ModelMenu.svelte';
|
import ModelMenu from './Models/ModelMenu.svelte';
|
||||||
|
|
||||||
@ -22,6 +25,9 @@
|
|||||||
let importFiles;
|
let importFiles;
|
||||||
let modelsImportInputElement: HTMLInputElement;
|
let modelsImportInputElement: HTMLInputElement;
|
||||||
|
|
||||||
|
let _models = [];
|
||||||
|
|
||||||
|
let sortable = null;
|
||||||
let searchValue = '';
|
let searchValue = '';
|
||||||
|
|
||||||
const deleteModelHandler = async (model) => {
|
const deleteModelHandler = async (model) => {
|
||||||
@ -42,6 +48,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
await models.set(await getModels(localStorage.token));
|
await models.set(await getModels(localStorage.token));
|
||||||
|
_models = $models;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloneModelHandler = async (model) => {
|
const cloneModelHandler = async (model) => {
|
||||||
@ -109,6 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
await models.set(await getModels(localStorage.token));
|
await models.set(await getModels(localStorage.token));
|
||||||
|
_models = $models;
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadModels = async (models) => {
|
const downloadModels = async (models) => {
|
||||||
@ -118,13 +126,58 @@
|
|||||||
saveAs(blob, `models-export-${Date.now()}.json`);
|
saveAs(blob, `models-export-${Date.now()}.json`);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
const positionChangeHanlder = async () => {
|
||||||
|
// Get the new order of the models
|
||||||
|
const modelIds = Array.from(document.getElementById('model-list').children).map((child) =>
|
||||||
|
child.id.replace('model-item-', '')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the position of the models
|
||||||
|
for (const [index, id] of modelIds.entries()) {
|
||||||
|
const model = $models.find((m) => m.id === id);
|
||||||
|
if (model) {
|
||||||
|
let info = model.info;
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
info = {
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
meta: {
|
||||||
|
position: index
|
||||||
|
},
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
info.meta = {
|
||||||
|
...info.meta,
|
||||||
|
position: index
|
||||||
|
};
|
||||||
|
await updateModelById(localStorage.token, info.id, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
await models.set(await getModels(localStorage.token));
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
// Legacy code to sync localModelfiles with models
|
// Legacy code to sync localModelfiles with models
|
||||||
|
_models = $models;
|
||||||
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
|
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
|
||||||
|
|
||||||
if (localModelfiles) {
|
if (localModelfiles) {
|
||||||
console.log(localModelfiles);
|
console.log(localModelfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SortableJS
|
||||||
|
sortable = new Sortable(document.getElementById('model-list'), {
|
||||||
|
animation: 150,
|
||||||
|
onUpdate: async (event) => {
|
||||||
|
console.log(event);
|
||||||
|
positionChangeHanlder();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -202,12 +255,13 @@
|
|||||||
|
|
||||||
<hr class=" dark:border-gray-850" />
|
<hr class=" dark:border-gray-850" />
|
||||||
|
|
||||||
<div class=" my-2 mb-5">
|
<div class=" my-2 mb-5" id="model-list">
|
||||||
{#each $models.filter((m) => searchValue === '' || m.name
|
{#each _models.filter((m) => searchValue === '' || m.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchValue.toLowerCase())) as model}
|
.includes(searchValue.toLowerCase())) as model}
|
||||||
<div
|
<div
|
||||||
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
|
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
|
||||||
|
id="model-item-{model.id}"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
|
class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
|
||||||
|
Loading…
Reference in New Issue
Block a user