mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
enh: connection tags
This commit is contained in:
parent
b427f506f6
commit
c309412980
@ -965,14 +965,24 @@ async def get_models(request: Request, user=Depends(get_verified_user)):
|
|||||||
|
|
||||||
return filtered_models
|
return filtered_models
|
||||||
|
|
||||||
models = await get_all_models(request, user=user)
|
all_models = await get_all_models(request, user=user)
|
||||||
|
|
||||||
# Filter out filter pipelines
|
models = []
|
||||||
models = [
|
for model in all_models:
|
||||||
model
|
# Filter out filter pipelines
|
||||||
for model in models
|
if "pipeline" in model and model["pipeline"].get("type", None) == "filter":
|
||||||
if "pipeline" not in model or model["pipeline"].get("type", None) != "filter"
|
continue
|
||||||
]
|
|
||||||
|
model_tags = [
|
||||||
|
tag.get("name")
|
||||||
|
for tag in model.get("info", {}).get("meta", {}).get("tags", [])
|
||||||
|
]
|
||||||
|
tags = [tag.get("name") for tag in model.get("tags", [])]
|
||||||
|
|
||||||
|
tags = list(set(model_tags + tags))
|
||||||
|
model["tags"] = [{"name": tag} for tag in tags]
|
||||||
|
|
||||||
|
models.append(model)
|
||||||
|
|
||||||
model_order_list = request.app.state.config.MODEL_ORDER_LIST
|
model_order_list = request.app.state.config.MODEL_ORDER_LIST
|
||||||
if model_order_list:
|
if model_order_list:
|
||||||
|
@ -295,7 +295,7 @@ async def update_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@cached(ttl=3)
|
@cached(ttl=1)
|
||||||
async def get_all_models(request: Request, user: UserModel = None):
|
async def get_all_models(request: Request, user: UserModel = None):
|
||||||
log.info("get_all_models()")
|
log.info("get_all_models()")
|
||||||
if request.app.state.config.ENABLE_OLLAMA_API:
|
if request.app.state.config.ENABLE_OLLAMA_API:
|
||||||
@ -336,6 +336,7 @@ async def get_all_models(request: Request, user: UserModel = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
prefix_id = api_config.get("prefix_id", None)
|
prefix_id = api_config.get("prefix_id", None)
|
||||||
|
tags = api_config.get("tags", [])
|
||||||
model_ids = api_config.get("model_ids", [])
|
model_ids = api_config.get("model_ids", [])
|
||||||
|
|
||||||
if len(model_ids) != 0 and "models" in response:
|
if len(model_ids) != 0 and "models" in response:
|
||||||
@ -350,6 +351,10 @@ async def get_all_models(request: Request, user: UserModel = None):
|
|||||||
for model in response.get("models", []):
|
for model in response.get("models", []):
|
||||||
model["model"] = f"{prefix_id}.{model['model']}"
|
model["model"] = f"{prefix_id}.{model['model']}"
|
||||||
|
|
||||||
|
if tags:
|
||||||
|
for model in response.get("models", []):
|
||||||
|
model["tags"] = tags
|
||||||
|
|
||||||
def merge_models_lists(model_lists):
|
def merge_models_lists(model_lists):
|
||||||
merged_models = {}
|
merged_models = {}
|
||||||
|
|
||||||
|
@ -353,6 +353,7 @@ async def get_all_models_responses(request: Request, user: UserModel) -> list:
|
|||||||
)
|
)
|
||||||
|
|
||||||
prefix_id = api_config.get("prefix_id", None)
|
prefix_id = api_config.get("prefix_id", None)
|
||||||
|
tags = api_config.get("tags", [])
|
||||||
|
|
||||||
if prefix_id:
|
if prefix_id:
|
||||||
for model in (
|
for model in (
|
||||||
@ -360,6 +361,12 @@ async def get_all_models_responses(request: Request, user: UserModel) -> list:
|
|||||||
):
|
):
|
||||||
model["id"] = f"{prefix_id}.{model['id']}"
|
model["id"] = f"{prefix_id}.{model['id']}"
|
||||||
|
|
||||||
|
if tags:
|
||||||
|
for model in (
|
||||||
|
response if isinstance(response, list) else response.get("data", [])
|
||||||
|
):
|
||||||
|
model["tags"] = tags
|
||||||
|
|
||||||
log.debug(f"get_all_models:responses() {responses}")
|
log.debug(f"get_all_models:responses() {responses}")
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
@ -377,7 +384,7 @@ async def get_filtered_models(models, user):
|
|||||||
return filtered_models
|
return filtered_models
|
||||||
|
|
||||||
|
|
||||||
@cached(ttl=3)
|
@cached(ttl=1)
|
||||||
async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
|
async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
|
||||||
log.info("get_all_models()")
|
log.info("get_all_models()")
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ async def get_all_base_models(request: Request, user: UserModel = None):
|
|||||||
"created": int(time.time()),
|
"created": int(time.time()),
|
||||||
"owned_by": "ollama",
|
"owned_by": "ollama",
|
||||||
"ollama": model,
|
"ollama": model,
|
||||||
|
"tags": model.get("tags", []),
|
||||||
}
|
}
|
||||||
for model in ollama_models["models"]
|
for model in ollama_models["models"]
|
||||||
]
|
]
|
||||||
|
@ -114,6 +114,13 @@ export const getModels = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tags = apiConfig.tags;
|
||||||
|
if (tags) {
|
||||||
|
for (const model of models) {
|
||||||
|
model.tags = tags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
localModels = localModels.concat(models);
|
localModels = localModels.concat(models);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
||||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
import Switch from '$lib/components/common/Switch.svelte';
|
import Switch from '$lib/components/common/Switch.svelte';
|
||||||
|
import Tags from './common/Tags.svelte';
|
||||||
|
|
||||||
export let onSubmit: Function = () => {};
|
export let onSubmit: Function = () => {};
|
||||||
export let onDelete: Function = () => {};
|
export let onDelete: Function = () => {};
|
||||||
@ -31,6 +32,7 @@
|
|||||||
|
|
||||||
let prefixId = '';
|
let prefixId = '';
|
||||||
let enable = true;
|
let enable = true;
|
||||||
|
let tags = [];
|
||||||
|
|
||||||
let modelId = '';
|
let modelId = '';
|
||||||
let modelIds = [];
|
let modelIds = [];
|
||||||
@ -88,6 +90,7 @@
|
|||||||
key,
|
key,
|
||||||
config: {
|
config: {
|
||||||
enable: enable,
|
enable: enable,
|
||||||
|
tags: tags,
|
||||||
prefix_id: prefixId,
|
prefix_id: prefixId,
|
||||||
model_ids: modelIds
|
model_ids: modelIds
|
||||||
}
|
}
|
||||||
@ -101,6 +104,7 @@
|
|||||||
url = '';
|
url = '';
|
||||||
key = '';
|
key = '';
|
||||||
prefixId = '';
|
prefixId = '';
|
||||||
|
tags = [];
|
||||||
modelIds = [];
|
modelIds = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,6 +114,7 @@
|
|||||||
key = connection.key;
|
key = connection.key;
|
||||||
|
|
||||||
enable = connection.config?.enable ?? true;
|
enable = connection.config?.enable ?? true;
|
||||||
|
tags = connection.config?.tags ?? [];
|
||||||
prefixId = connection.config?.prefix_id ?? '';
|
prefixId = connection.config?.prefix_id ?? '';
|
||||||
modelIds = connection.config?.model_ids ?? [];
|
modelIds = connection.config?.model_ids ?? [];
|
||||||
}
|
}
|
||||||
@ -244,6 +249,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2 mt-2">
|
||||||
|
<div class="flex flex-col w-full">
|
||||||
|
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<Tags
|
||||||
|
bind:tags
|
||||||
|
on:add={(e) => {
|
||||||
|
tags = [
|
||||||
|
...tags,
|
||||||
|
{
|
||||||
|
name: e.detail
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}}
|
||||||
|
on:delete={(e) => {
|
||||||
|
tags = tags.filter((tag) => tag.name !== e.detail);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
||||||
|
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
const _item = {
|
const _item = {
|
||||||
...item,
|
...item,
|
||||||
modelName: item.model?.name,
|
modelName: item.model?.name,
|
||||||
tags: item.model?.info?.meta?.tags?.map((tag) => tag.name).join(' '),
|
tags: (item.model?.tags ?? []).map((tag) => tag.name).join(' '),
|
||||||
desc: item.model?.info?.meta?.description
|
desc: item.model?.info?.meta?.description
|
||||||
};
|
};
|
||||||
return _item;
|
return _item;
|
||||||
@ -98,7 +98,7 @@
|
|||||||
if (selectedTag === '') {
|
if (selectedTag === '') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return item.model?.info?.meta?.tags?.map((tag) => tag.name).includes(selectedTag);
|
return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
|
||||||
})
|
})
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
if (selectedConnectionType === '') {
|
if (selectedConnectionType === '') {
|
||||||
@ -116,7 +116,7 @@
|
|||||||
if (selectedTag === '') {
|
if (selectedTag === '') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return item.model?.info?.meta?.tags?.map((tag) => tag.name).includes(selectedTag);
|
return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
|
||||||
})
|
})
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
if (selectedConnectionType === '') {
|
if (selectedConnectionType === '') {
|
||||||
@ -262,7 +262,7 @@
|
|||||||
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
|
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
|
||||||
|
|
||||||
if (items) {
|
if (items) {
|
||||||
tags = items.flatMap((item) => item.model?.info?.meta?.tags ?? []).map((tag) => tag.name);
|
tags = items.flatMap((item) => item.model?.tags ?? []).map((tag) => tag.name);
|
||||||
|
|
||||||
// Remove duplicates and sort
|
// Remove duplicates and sort
|
||||||
tags = Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b));
|
tags = Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b));
|
||||||
@ -291,12 +291,12 @@
|
|||||||
onOpenChange={async () => {
|
onOpenChange={async () => {
|
||||||
searchValue = '';
|
searchValue = '';
|
||||||
// Do NOT reset filters - keep the previously selected tag/connection type
|
// Do NOT reset filters - keep the previously selected tag/connection type
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
// First check if the currently selected model is visible in the filtered list
|
// First check if the currently selected model is visible in the filtered list
|
||||||
const selectedInFiltered = filteredItems.findIndex(item => item.value === value);
|
const selectedInFiltered = filteredItems.findIndex((item) => item.value === value);
|
||||||
|
|
||||||
if (selectedInFiltered >= 0) {
|
if (selectedInFiltered >= 0) {
|
||||||
// The selected model is visible in the current filter
|
// The selected model is visible in the current filter
|
||||||
selectedModelIdx = selectedInFiltered;
|
selectedModelIdx = selectedInFiltered;
|
||||||
@ -304,22 +304,23 @@
|
|||||||
// The selected model is not visible, default to first item in filtered list
|
// The selected model is not visible, default to first item in filtered list
|
||||||
selectedModelIdx = 0;
|
selectedModelIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
// Scroll to the selected item if it exists in the current filtered view
|
// Scroll to the selected item if it exists in the current filtered view
|
||||||
const itemToScrollTo = selectedInFiltered >= 0
|
const itemToScrollTo =
|
||||||
? document.querySelector(`[data-value="${value}"]`)
|
selectedInFiltered >= 0
|
||||||
: document.querySelector('[data-arrow-selected="true"]');
|
? document.querySelector(`[data-value="${value}"]`)
|
||||||
|
: document.querySelector('[data-arrow-selected="true"]');
|
||||||
|
|
||||||
if (itemToScrollTo) {
|
if (itemToScrollTo) {
|
||||||
const container = itemToScrollTo.closest('.overflow-y-auto');
|
const container = itemToScrollTo.closest('.overflow-y-auto');
|
||||||
if (container) {
|
if (container) {
|
||||||
const itemTop = itemToScrollTo.offsetTop;
|
const itemTop = itemToScrollTo.offsetTop;
|
||||||
const containerHeight = container.clientHeight;
|
const containerHeight = container.clientHeight;
|
||||||
const itemHeight = itemToScrollTo.clientHeight;
|
const itemHeight = itemToScrollTo.clientHeight;
|
||||||
|
|
||||||
container.scrollTop = itemTop - (containerHeight / 2) + (itemHeight / 2);
|
container.scrollTop = itemTop - containerHeight / 2 + itemHeight / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -483,9 +484,9 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
{#if $mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
|
{#if $mobile && (item?.model?.tags ?? []).length > 0}
|
||||||
<div class="flex gap-0.5 self-start h-full mb-1.5 -translate-x-1">
|
<div class="flex gap-0.5 self-start h-full mb-1.5 -translate-x-1">
|
||||||
{#each item.model?.info?.meta.tags as tag}
|
{#each item.model?.tags as tag}
|
||||||
<div
|
<div
|
||||||
class=" text-xs font-bold px-1 rounded-sm uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
|
class=" text-xs font-bold px-1 rounded-sm uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
|
||||||
>
|
>
|
||||||
@ -605,11 +606,11 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !$mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
|
{#if !$mobile && (item?.model?.tags ?? []).length > 0}
|
||||||
<div
|
<div
|
||||||
class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px] overflow-x-auto scrollbar-none"
|
class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px] overflow-x-auto scrollbar-none"
|
||||||
>
|
>
|
||||||
{#each item.model?.info?.meta.tags as tag}
|
{#each item.model?.tags as tag}
|
||||||
<Tooltip content={tag.name} className="flex-shrink-0">
|
<Tooltip content={tag.name} className="flex-shrink-0">
|
||||||
<div
|
<div
|
||||||
class=" text-xs font-bold px-1 rounded-sm uppercase bg-gray-500/20 text-gray-700 dark:text-gray-200"
|
class=" text-xs font-bold px-1 rounded-sm uppercase bg-gray-500/20 text-gray-700 dark:text-gray-200"
|
||||||
|
Loading…
Reference in New Issue
Block a user