handleChange(option)}>
+ {option.recommend &&
{t('common.recommend')}
}
{/* Use custom render or default card layout */}
{itemRender ? itemRender(option) : (
<>
diff --git a/web/src/components/SearchInput/index.tsx b/web/src/components/SearchInput/index.tsx
index 32a64310..476c2cbb 100644
--- a/web/src/components/SearchInput/index.tsx
+++ b/web/src/components/SearchInput/index.tsx
@@ -41,6 +41,8 @@ interface SearchInputProps {
className?: string;
/** Input size */
size?: InputProps['size']
+ /** Maximum length of the input value */
+ maxLength?: number;
}
/** Search input component with debounce and throttle support */
diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts
index fd8003dc..5f041a7c 100644
--- a/web/src/i18n/en.ts
+++ b/web/src/i18n/en.ts
@@ -452,6 +452,9 @@ export const en = {
nextStep: 'Next Step',
prevStep: 'Previous Step',
exportSuccess: 'Export successful',
+ recommend: 'Recommend',
+ logoTip: `Supported image formats: JPG, PNG \n Suggested size: square ratio \n Maximum size: ≤ 2MB`,
+ imageSquareRequired: 'Please upload a square image',
},
model: {
searchPlaceholder: 'search model…',
@@ -541,7 +544,8 @@ export const en = {
ollama: "Ollama",
xinference: "Xinference",
gpustack: "Gpustack",
- bedrock: "Bedrock"
+ bedrock: "Bedrock",
+ nameInvalid: 'Model name can only contain letters, numbers, underscores and spaces, cannot be empty or pure whitespace',
},
modelNew: {
group: 'Model Group',
@@ -1642,6 +1646,10 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
scene_type_distribution: 'Scene Type Distribution',
general_type_distribution: 'General Type Distribution',
unmatched: 'Unmatched',
+ disagreementCase: 'Disagreement Case',
+ Pruned: 'Pruned',
+ pruning: 'Pruning',
+ pruning_desc: 'Text pruning {{count}} fragments'
},
memoryConversation: {
searchPlaceholder: 'Enter user ID...',
diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts
index 72641422..0c0f7562 100644
--- a/web/src/i18n/zh.ts
+++ b/web/src/i18n/zh.ts
@@ -1031,6 +1031,9 @@ export const zh = {
nextStep: '下一步',
prevStep: '上一步',
exportSuccess: '导出成功',
+ recommend: '推荐',
+ logoTip: `支持图片格式(JPG、PNG)\n 尺寸:正方形比例 \n 文件大小限制:≤ 2MB`,
+ imageSquareRequired: '请上传正方形比例图片',
},
model: {
searchPlaceholder: '搜索模型…',
@@ -1178,7 +1181,8 @@ export const zh = {
ollama: "Ollama",
xinference: "Xinference",
gpustack: "Gpustack",
- bedrock: "Bedrock"
+ bedrock: "Bedrock",
+ nameInvalid: '模型名称只能包含字母、数字、下划线和空格, 不能为空或纯空格',
},
timezones: {
'Asia/Shanghai': '中国标准时间 (UTC+8)',
@@ -1639,6 +1643,10 @@ export const zh = {
scene_type_distribution: '场景类型',
general_type_distribution: '通用类型',
unmatched: '未匹配',
+ disagreementCase: '不一致案例',
+ Pruned: '已剪枝',
+ pruning: '剪枝',
+ pruning_desc: '文本剪枝{{count}}个片段'
},
memoryConversation: {
chatEmpty:'有什么我可以帮您的吗?',
diff --git a/web/src/store/user.ts b/web/src/store/user.ts
index c9231d9c..f5e0cb28 100644
--- a/web/src/store/user.ts
+++ b/web/src/store/user.ts
@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 16:33:54
* @Last Modified by: ZhaoYing
- * @Last Modified time: 2026-02-04 18:30:10
+ * @Last Modified time: 2026-02-28 17:21:20
*/
/**
* User Store
@@ -44,7 +44,7 @@ export interface UserState {
/** Update login information */
updateLoginInfo: (values: LoginInfo) => void;
/** Get user information */
- getUserInfo: (flag?: boolean) => void;
+ getUserInfo: (flag?: boolean, notNeedJump?: boolean) => void;
/** Clear user information */
clearUserInfo: () => void;
/** Logout user */
@@ -73,13 +73,13 @@ export const useUser = create
((set, get) => ({
cookieUtils.set('refreshToken', values.refresh_token);
set({ loginInfo: values });
},
- getUserInfo: async (flag?: boolean) => {
+ getUserInfo: async (flag?: boolean, notNeedJump?: boolean) => {
if (!cookieUtils.get('authToken')) {
return
}
const { checkJump } = get()
const localUser = JSON.parse(localStorage.getItem('user') || '{}') as User;
- if (localUser.id) {
+ if (localUser.id && !notNeedJump) {
checkJump()
return
}
diff --git a/web/src/utils/validator.ts b/web/src/utils/validator.ts
new file mode 100644
index 00000000..c55c52ca
--- /dev/null
+++ b/web/src/utils/validator.ts
@@ -0,0 +1,50 @@
+/*
+ * @Author: ZhaoYing
+ * @Date: 2026-03-02 13:46:53
+ * @Last Modified by: ZhaoYing
+ * @Last Modified time: 2026-03-02 14:38:33
+ */
+/**
+ * Form validation utilities
+ */
+
+interface UploadFile {
+ originFileObj: Blob;
+ [key: string]: unknown;
+}
+
+/**
+ * Validate if uploaded image is square (width === height)
+ * @param errorMessage - Error message to display when validation fails
+ * @returns Ant Design form validator
+ */
+export const validateSquareImage = (errorMessage: string = 'Image must be square') => {
+ return (_: unknown, value: UploadFile | UploadFile[] | undefined) => {
+ if (!value || (Array.isArray(value) && value.length === 0)) {
+ return Promise.resolve();
+ }
+
+ const file = Array.isArray(value) ? value[0] : value;
+
+ if (file?.originFileObj) {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => {
+ if (img.width === img.height) {
+ resolve();
+ } else {
+ reject(new Error(errorMessage));
+ }
+ };
+ img.onerror = () => reject(new Error('Failed to load image'));
+ img.src = URL.createObjectURL(file.originFileObj);
+ });
+ }
+
+ return Promise.resolve();
+ };
+};
+
+// - Cannot be empty or pure whitespace
+// - Cannot start with a space
+export const stringRegExp = /^[a-zA-Z0-9\u4e00-\u9fa5][a-zA-Z0-9\u4e00-\u9fa5\s]*$/
\ No newline at end of file
diff --git a/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx b/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx
index 9395df43..05e73992 100644
--- a/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx
+++ b/web/src/views/ApiKeyManagement/components/ApiKeyModal.tsx
@@ -12,6 +12,7 @@ import dayjs from 'dayjs'
import type { ApiKey, ApiKeyModalRef } from '../types';
import RbModal from '@/components/RbModal'
import { createApiKey, updateApiKey } from '@/api/apiKey';
+import { stringRegExp } from '@/utils/validator';
const FormItem = Form.Item;
@@ -78,7 +79,7 @@ const ApiKeyModal = forwardRef(({
form.validateFields()
.then((values) => {
const { memory, rag, expires_at, ...rest } = values
- let scopes = []
+ const scopes = []
if (memory) {
scopes.push('memory')
@@ -130,7 +131,11 @@ const ApiKeyModal = forwardRef(({
@@ -138,6 +143,7 @@ const ApiKeyModal = forwardRef(({
diff --git a/web/src/views/ApplicationConfig/Agent.tsx b/web/src/views/ApplicationConfig/Agent.tsx
index 2ece4b6e..4bee291b 100644
--- a/web/src/views/ApplicationConfig/Agent.tsx
+++ b/web/src/views/ApplicationConfig/Agent.tsx
@@ -169,8 +169,8 @@ const Agent = forwardRef((_props, ref) => {
getApplicationConfig(id as string).then(res => {
const response = res as Config
const { skills, variables } = response
- let allSkills = Array.isArray(skills?.skill_ids) ? skills?.skill_ids.map(vo => ({ id: vo })) : []
- let allTools = Array.isArray(response.tools) ? response.tools : []
+ const allSkills = Array.isArray(skills?.skill_ids) ? skills?.skill_ids.map(vo => ({ id: vo })) : []
+ const allTools = Array.isArray(response.tools) ? response.tools : []
const memoryContent = response.memory?.memory_config_id
const parsedMemoryContent = memoryContent === null || memoryContent === ''
? undefined
@@ -431,7 +431,11 @@ const Agent = forwardRef((_props, ref) => {
-