feat(web): model add is_vision/is_omni config

This commit is contained in:
zhaoying
2026-03-04 11:52:54 +08:00
parent c6b76438f4
commit 31bee889d7
7 changed files with 77 additions and 25 deletions

View File

@@ -603,7 +603,13 @@ export const en = {
ollama: "Ollama",
xinference: "Xinference",
gpustack: "Gpustack",
bedrock: "Bedrock"
bedrock: "Bedrock",
is_vision: 'Vision Support',
is_omni: 'Omni Support',
vision: 'Vision',
audio: 'Audio',
video: 'Video',
},
knowledgeBase: {
home: 'Home',

View File

@@ -1184,6 +1184,12 @@ export const zh = {
xinference: "Xinference",
gpustack: "Gpustack",
bedrock: "Bedrock",
is_vision: '支持视觉',
is_omni: '支持全模态',
vision: '视觉',
audio: '音频',
video: '视频',
},
timezones: {
'Asia/Shanghai': '中国标准时间 (UTC+8)',

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:28
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-28 17:24:05
* @Last Modified time: 2026-03-04 11:31:43
*/
/**
* Custom Model Modal
@@ -10,8 +10,8 @@
* Supports logo upload, type/provider selection, and tagging
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Form, Input, App } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { Form, Input, App, Checkbox } from 'antd';
import { useTranslation } from 'react-i18next';
import type { CustomModelForm, ModelListItem, CustomModelModalRef, CustomModelModalProps } from '../types';
@@ -35,6 +35,14 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
const [isEdit, setIsEdit] = useState(false);
const [form] = Form.useForm<CustomModelForm>();
const [loading, setLoading] = useState(false)
const modelType = Form.useWatch(['type'], form);
const isOmni = Form.useWatch(['is_omni'], form);
useEffect(() => {
if (isOmni) {
form.setFieldsValue({ is_vision: true })
}
}, [isOmni])
/** Close modal and reset state */
const handleClose = () => {
@@ -49,9 +57,12 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
if (model) {
setIsEdit(true);
setModel(model);
const { capability, is_omni, ...rest} = model
form.setFieldsValue({
...model,
logo: model.logo && model.logo.startsWith('http') ? { url: model.logo, uid: model.logo, status: 'done', name: 'logo' } : undefined
...rest,
logo: model.logo && model.logo.startsWith('http') ? { url: model.logo, uid: model.logo, status: 'done', name: 'logo' } : undefined,
is_omni,
is_vision: capability?.includes('vision') || false,
});
} else {
setIsEdit(false);
@@ -79,9 +90,14 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
form
.validateFields()
.then((values) => {
const { logo, ...rest } = values;
const { logo, type, is_vision, is_omni, ...rest } = values;
const formData: CustomModelForm = {
...rest
...rest,
type,
}
if (!['embedding', 'rerank'].includes(type as string)) {
formData.capability = is_omni ? ["vision", "audio"] : is_vision ? ['vision'] : []
formData.is_omni = is_omni
}
if (typeof logo === 'object' && logo?.response?.data.file_id) {
@@ -108,7 +124,7 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
useImperativeHandle(ref, () => ({
handleOpen,
}));
console.log('modelType', modelType)
return (
<RbModal
title={isEdit ? `${model.name} - ${t('modelNew.modelConfiguration')}` : t('modelNew.createCustomModel')}
@@ -180,7 +196,6 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
<Input.TextArea placeholder={t('common.pleaseEnter')} />
</Form.Item>
<Form.Item
name={["api_keys", 0, "api_key"]}
label={t('modelNew.api_key')}
@@ -196,6 +211,17 @@ const CustomModelModal = forwardRef<CustomModelModalRef, CustomModelModalProps>(
>
<Input placeholder="https://api.example.com/v1" />
</Form.Item>
{!['embedding', 'rerank'].includes(modelType as string) &&
<>
<Form.Item name="is_omni" valuePropName="checked" className="rb:mb-2!">
<Checkbox>{t('modelNew.is_omni')}</Checkbox>
</Form.Item>
<Form.Item name="is_vision" valuePropName="checked" className="rb:mb-0!">
<Checkbox disabled={isOmni}>{t('modelNew.is_vision')}</Checkbox>
</Form.Item>
</>
}
</Form>
</RbModal>
);

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:20
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:54:54
* @Last Modified time: 2026-03-04 11:51:01
*/
/**
* Sub-Model Modal
@@ -10,8 +10,8 @@
* Uses cascader for hierarchical selection
*/
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Form, Cascader, App, type CascaderProps } from 'antd';
import { type ReactNode, forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Form, Cascader, App, type CascaderProps, Space } from 'antd';
import { useTranslation } from 'react-i18next';
import type { SubModelModalForm, SubModelModalRef, SubModelModalProps } from './types';
@@ -19,6 +19,7 @@ import RbModal from '@/components/RbModal'
import CustomSelect from '@/components/CustomSelect'
import { modelProviderUrl, getModelNewList } from '@/api/models'
import type { ProviderModelItem } from '../../types'
import Tag from '@/components/Tag';
const { SHOW_CHILD } = Cascader;
@@ -27,7 +28,7 @@ const { SHOW_CHILD } = Cascader;
*/
interface Option {
value: string | number;
label: string;
label: string | ReactNode;
children?: Option[];
[key: string]: any;
}
@@ -116,7 +117,11 @@ const SubModelModal = forwardRef<SubModelModalRef, SubModelModalProps>(({
}))
return {
...vo,
label: vo.name,
label: <Space>
{vo.name}
<Tag>{t(`modelNew.${vo.type}`)}</Tag>
{vo.capability?.filter(item => item !== 'video').map(vo => <Tag key={vo}>{t(`modelNew.${vo}`)}</Tag>)}
</Space>,
value: vo.id,
children: children
}

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:45
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:49:45
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-04 11:50:47
*/
/**
* Model List Detail Drawer
@@ -133,9 +133,10 @@ const ModelListDetail = forwardRef<ModelListDetailRef, ModelListDetailProps>(({
<RbCard
key={item.id}
title={item.name}
subTitle={<Space className="rb:mt-1!">
subTitle={<Space size={8} className="rb:mt-1!">
<Tag>{t(`modelNew.${item.type}`)}</Tag>
<Tag color="warning">{item.api_keys.length}{t('modelNew.apiKeyNum')}</Tag>
{item.capability?.filter(item => item !=='video').map(vo => <Tag key={vo}>{t(`modelNew.${vo}`)}</Tag>)}
</Space>}
avatarUrl={getLogoUrl(item.logo)}
avatar={

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:49
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:54:26
* @Last Modified time: 2026-03-04 11:50:31
*/
/**
* Model Square Detail Drawer
@@ -89,9 +89,10 @@ const ModelSquareDetail = forwardRef<ModelSquareDetailRef, ModelSquareDetailProp
<RbCard
key={item.id}
title={item.name}
subTitle={<Space size={8}>
<Tag className="rb:mt-1">{t(`modelNew.${item.type}`)}</Tag>
{item.is_official && <Tag color="success" className="rb:mt-1">{t(`modelNew.official`)}</Tag>}
subTitle={<Space size={8} className="rb:mt-1!">
<Tag>{t(`modelNew.${item.type}`)}</Tag>
{item.is_official && <Tag color="success">{t(`modelNew.official`)}</Tag>}
{item.capability?.filter(item => item !== 'video').map(vo => <Tag key={vo}>{t(`modelNew.${vo}`)}</Tag>)}
</Space>}
avatarUrl={getLogoUrl(item.logo)}
avatar={

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 16:50:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 16:50:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-04 11:39:20
*/
/**
* Type definitions for Model Management
@@ -148,7 +148,9 @@ export interface ModelListItem {
/** Update timestamp */
updated_at: number;
/** Associated API keys */
api_keys: ModelApiKey[]
api_keys: ModelApiKey[];
capability?: string[];
is_omni?: boolean;
}
/**
@@ -261,6 +263,8 @@ export interface ModelPlazaItem {
add_count: number;
/** Whether user has added this model */
is_added: boolean;
capability?: string[];
is_omni?: boolean;
}
/**
@@ -291,6 +295,9 @@ export interface CustomModelForm {
/** API base URL */
api_base: string;
}>
is_vision?: boolean;
is_omni?: boolean;
capability?: string[];
}
/**