Merge pull request #494 from SuanmoSuanyangTechnology/release/v0.2.6
Release/v0.2.6
This commit is contained in:
@@ -371,6 +371,11 @@ def update_model(
|
||||
|
||||
if model_data.type is not None or model_data.provider is not None:
|
||||
raise BusinessException("不允许更改模型类型和供应商", BizCode.INVALID_PARAMETER)
|
||||
|
||||
if model_data.is_active:
|
||||
active_keys = ModelApiKeyService.get_api_keys_by_model(db=db, model_config_id=model_id, is_active=model_data.is_active)
|
||||
if not active_keys:
|
||||
raise BusinessException("请先为该模型配置可用的 API Key", BizCode.INVALID_PARAMETER)
|
||||
|
||||
try:
|
||||
api_logger.debug(f"开始更新模型配置: model_id={model_id}")
|
||||
|
||||
@@ -192,8 +192,10 @@ class Settings:
|
||||
# Celery configuration (internal)
|
||||
# NOTE: 变量名不以 CELERY_ 开头,避免被 Celery CLI 的前缀匹配机制劫持
|
||||
# 详见 docs/celery-env-bug-report.md
|
||||
REDIS_DB_CELERY_BROKER: int = int(os.getenv("REDIS_DB_CELERY_BROKER", "1"))
|
||||
REDIS_DB_CELERY_BACKEND: int = int(os.getenv("REDIS_DB_CELERY_BACKEND", "2"))
|
||||
# 默认使用 Redis DB 3 (broker) 和 DB 4 (backend),与业务缓存 (DB 1/2) 隔离
|
||||
# 多人共用同一 Redis 时,每位开发者应在 .env 中配置不同的 DB 编号避免任务互相干扰
|
||||
REDIS_DB_CELERY_BROKER: int = int(os.getenv("REDIS_DB_CELERY_BROKER", "3"))
|
||||
REDIS_DB_CELERY_BACKEND: int = int(os.getenv("REDIS_DB_CELERY_BACKEND", "4"))
|
||||
|
||||
# SMTP Email Configuration
|
||||
SMTP_SERVER: str = os.getenv("SMTP_SERVER", "smtp.gmail.com")
|
||||
|
||||
@@ -23,6 +23,7 @@ class ModelConfigBase(BaseModel):
|
||||
load_balance_strategy: Optional[str] = Field(LoadBalanceStrategy.NONE.value, description="负载均衡策略")
|
||||
capability: List[str] = Field(default_factory=list, description="模型能力列表")
|
||||
is_omni: bool = Field(False, description="是否为Omni模型")
|
||||
model_id: Optional[uuid.UUID] = Field(None, description="基础模型ID")
|
||||
|
||||
|
||||
class ApiKeyCreateNested(BaseModel):
|
||||
|
||||
@@ -703,7 +703,7 @@ class AppService:
|
||||
self.db.flush()
|
||||
|
||||
# 如果是 agent 类型,复制 AgentConfig
|
||||
if source_app.type == "agent":
|
||||
if source_app.type == AppType.AGENT:
|
||||
source_config = self.db.query(AgentConfig).filter(
|
||||
AgentConfig.app_id == source_app.id
|
||||
).first()
|
||||
@@ -725,6 +725,50 @@ class AppService:
|
||||
)
|
||||
self.db.add(new_config)
|
||||
|
||||
elif source_app.type == AppType.WORKFLOW:
|
||||
source_config = self.db.query(WorkflowConfig).filter(
|
||||
WorkflowConfig.app_id == source_app.id
|
||||
).first()
|
||||
|
||||
if source_config:
|
||||
new_config = WorkflowConfig(
|
||||
id=uuid.uuid4(),
|
||||
app_id=new_app.id,
|
||||
nodes=source_config.nodes.copy() if source_config.nodes else [],
|
||||
edges=source_config.edges.copy() if source_config.edges else [],
|
||||
variables=source_config.variables.copy() if source_config.variables else [],
|
||||
execution_config=source_config.execution_config.copy() if source_config.execution_config else {},
|
||||
triggers=source_config.triggers.copy() if source_config.triggers else [],
|
||||
is_active=True,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
self.db.add(new_config)
|
||||
|
||||
elif source_app.type == AppType.MULTI_AGENT:
|
||||
source_config = self.db.query(MultiAgentConfig).filter(
|
||||
MultiAgentConfig.app_id == source_app.id
|
||||
).first()
|
||||
|
||||
if source_config:
|
||||
new_config = MultiAgentConfig(
|
||||
id=uuid.uuid4(),
|
||||
app_id=new_app.id,
|
||||
master_agent_id=source_config.master_agent_id,
|
||||
master_agent_name=source_config.master_agent_name,
|
||||
default_model_config_id=source_config.default_model_config_id,
|
||||
model_parameters=source_config.model_parameters,
|
||||
orchestration_mode=source_config.orchestration_mode,
|
||||
sub_agents=source_config.sub_agents.copy() if source_config.sub_agents else [],
|
||||
routing_rules=source_config.routing_rules.copy() if source_config.routing_rules else None,
|
||||
execution_config=source_config.execution_config.copy() if source_config.execution_config else {},
|
||||
aggregation_strategy=source_config.aggregation_strategy,
|
||||
is_active=True,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
self.db.add(new_config)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(new_app)
|
||||
|
||||
|
||||
@@ -780,6 +780,7 @@ class ModelBaseService:
|
||||
"description": model_base.description,
|
||||
"capability": model_base.capability,
|
||||
"is_omni": model_base.is_omni,
|
||||
"is_active": False,
|
||||
"is_composite": False
|
||||
}
|
||||
model_config = ModelConfigRepository.create(db, model_config_data)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2025-12-10 16:46:14
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-04 18:42:49
|
||||
* @Last Modified time: 2026-03-06 13:36:20
|
||||
*/
|
||||
import { type FC, useEffect, useMemo } from 'react'
|
||||
import { Flex, Input, Form } from 'antd'
|
||||
@@ -50,13 +50,13 @@ const ChatInput: FC<ChatInputProps> = ({
|
||||
|
||||
|
||||
const handleDelete = (file: any) => {
|
||||
fileChange?.(fileList?.filter(item => item.uid !== file.uid) || [])
|
||||
fileChange?.(fileList?.filter(item => file.url ? item.url !== file.url : item.uid !== file.uid) || [])
|
||||
}
|
||||
// Convert file object to preview URL
|
||||
const previewFileList = useMemo(() => {
|
||||
return fileList?.map(file => ({
|
||||
...file,
|
||||
url: file.url || (file.originFileObj ? URL.createObjectURL(file.originFileObj) : file.thumbUrl)
|
||||
url: file.thumbUrl || file.url || (file.originFileObj ? URL.createObjectURL(file.originFileObj) : undefined)
|
||||
})) || []
|
||||
}, [fileList])
|
||||
|
||||
@@ -72,7 +72,7 @@ const ChatInput: FC<ChatInputProps> = ({
|
||||
{previewFileList.map((file) => {
|
||||
if (file.type.includes('image')) {
|
||||
return (
|
||||
<div key={file.uid} className="rb:inline-block rb:group rb:relative rb:rounded-lg">
|
||||
<div key={file.url || file.uid} className="rb:inline-block rb:group rb:relative rb:rounded-lg">
|
||||
<img src={file.url} alt={file.name} className="rb:size-12! rb:rounded-lg rb:object-cover rb:cursor-pointer" />
|
||||
<div
|
||||
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
|
||||
@@ -83,7 +83,7 @@ const ChatInput: FC<ChatInputProps> = ({
|
||||
}
|
||||
if (file.type.includes('video')) {
|
||||
return (
|
||||
<div key={file.uid} className="rb:w-45 rb:h-16 rb:inline-block rb:group rb:relative rb:rounded-lg">
|
||||
<div key={file.url || file.uid} className="rb:w-45 rb:h-16 rb:inline-block rb:group rb:relative rb:rounded-lg">
|
||||
<video src={file.url} controls className="rb:w-45 rb:h-16 rb:rounded-lg rb:object-cover rb:cursor-pointer" />
|
||||
<div
|
||||
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
|
||||
@@ -94,7 +94,7 @@ const ChatInput: FC<ChatInputProps> = ({
|
||||
}
|
||||
if (file.type.includes('audio')) {
|
||||
return (
|
||||
<div key={file.uid} className="rb:w-45 rb:h-16 rb:inline-flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5 rb:gap-2">
|
||||
<div key={file.url || file.uid} className="rb:w-45 rb:h-16 rb:inline-flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5 rb:gap-2">
|
||||
<audio src={file.url} controls className="rb:w-45 rb:h-16" />
|
||||
<div
|
||||
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
|
||||
@@ -104,7 +104,7 @@ const ChatInput: FC<ChatInputProps> = ({
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div key={file.uid} className="rb:w-45 rb:text-[12px] rb:gap-2.5 rb:flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5">
|
||||
<div key={file.url || file.uid} className="rb:w-45 rb:text-[12px] rb:gap-2.5 rb:flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5">
|
||||
{(file.type.includes('doc') || file.type.includes('docx') || file.type.includes('word') || file.type.includes('wordprocessingml.document')) && <div
|
||||
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/word_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/word.svg')]"
|
||||
></div>}
|
||||
|
||||
@@ -1572,7 +1572,7 @@ export const en = {
|
||||
intelligentSemanticPruningFunction: 'Intelligent Semantic Pruning Function',
|
||||
intelligentSemanticPruningFunctionDesc: 'Whether to activate intelligent semantic pruning (true/false).',
|
||||
intelligentSemanticPruningScene: 'Intelligent Semantic Pruning Scene',
|
||||
intelligentSemanticPruningSceneDesc: 'Select intelligent semantic pruning scene (education, online_service, outbound).',
|
||||
intelligentSemanticPruningSceneDesc: 'Semantic pruning scenarios are consistent with ontology engineering scenarios',
|
||||
intelligentSemanticPruningThreshold: 'Intelligent Semantic Pruning Threshold',
|
||||
intelligentSemanticPruningThresholdDesc: 'Set intelligent semantic pruning threshold (0-0.9).',
|
||||
reflectionEngine: 'Self-Reflexion Engine',
|
||||
|
||||
@@ -96,7 +96,7 @@ export const zh = {
|
||||
createMemorySummary: '创建记忆摘要',
|
||||
memoryManagement: '记忆管理',
|
||||
spaceManagement: '空间管理',
|
||||
memoryExtractionEngine: '记忆提取引擎',
|
||||
memoryExtractionEngine: '记忆萃取引擎',
|
||||
forgettingEngine: '遗忘引擎',
|
||||
apiKeyManagement: 'API KEY管理',
|
||||
knowledgePrivate: '详情',
|
||||
@@ -1283,7 +1283,7 @@ export const zh = {
|
||||
createConfiguration: '创建配置',
|
||||
editConfiguration: '编辑配置',
|
||||
desc: '描述',
|
||||
memoryExtractionEngine: '记忆提取引擎',
|
||||
memoryExtractionEngine: '记忆萃取引擎',
|
||||
forgottenEngine: '遗忘引擎',
|
||||
active: '活跃',
|
||||
inactive: '不活跃',
|
||||
@@ -1571,7 +1571,7 @@ export const zh = {
|
||||
intelligentSemanticPruningFunction: '智能语义修剪功能',
|
||||
intelligentSemanticPruningFunctionDesc: '是否激活智能语义修剪(true/false)。',
|
||||
intelligentSemanticPruningScene: '智能语义修剪场景',
|
||||
intelligentSemanticPruningSceneDesc: '选择智能语义修剪场景(education、online_service、outbound)。',
|
||||
intelligentSemanticPruningSceneDesc: '语义剪枝场景与本体工程场景一致',
|
||||
intelligentSemanticPruningThreshold: '智能语义修剪阈值',
|
||||
intelligentSemanticPruningThresholdDesc: '设置智能语义修剪阈值(0-0.9)。',
|
||||
reflectionEngine: '自我反思引擎',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-28 14:08:14
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-02 17:39:49
|
||||
* @Last Modified time: 2026-03-06 12:05:46
|
||||
*/
|
||||
/**
|
||||
* UploadWorkflowModal Component
|
||||
@@ -101,6 +101,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
|
||||
formData.append('platform', values.platform);
|
||||
formData.append('file', values.file[0]);
|
||||
|
||||
setLoading(true)
|
||||
// Call import workflow API
|
||||
importWorkflow(formData)
|
||||
.then(res => {
|
||||
@@ -114,21 +115,24 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
|
||||
} else {
|
||||
setCurrent(2);
|
||||
// Pre-fill form with file information
|
||||
const fileNameSplit = values.file[0].name.split('.')
|
||||
form.setFieldsValue({
|
||||
name: values.file[0].name.split('.')[0],
|
||||
name: fileNameSplit.slice(0, fileNameSplit.length - 1).join('.'),
|
||||
platform: values.platform,
|
||||
fileName: values.file[0].name,
|
||||
fileSize: values.file[0].size,
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
break;
|
||||
case 1: // Step 2: Error/warning display
|
||||
if (firstFormData) {
|
||||
const { file, platform } = firstFormData;
|
||||
const fileNameSplit = firstFormData.file[0].name.split('.')
|
||||
// Pre-fill form with file information
|
||||
form.setFieldsValue({
|
||||
name: file[0].name.split('.')[0],
|
||||
name: fileNameSplit.slice(0, fileNameSplit.length - 1).join('.'),
|
||||
platform: platform,
|
||||
fileName: file[0].name,
|
||||
fileSize: file[0].size,
|
||||
@@ -175,7 +179,9 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
|
||||
}
|
||||
|
||||
// Reset form if not going back to error/warning step
|
||||
if (newStep !== 1) {
|
||||
if (newStep === 0) {
|
||||
form.setFieldsValue(firstFormData || {})
|
||||
} else if (newStep !== 1) {
|
||||
form.resetFields();
|
||||
}
|
||||
setCurrent(newStep);
|
||||
@@ -186,14 +192,16 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
|
||||
* @param {string} type - Navigation type ('detail' or 'list')
|
||||
*/
|
||||
const handleJump = (type: string) => {
|
||||
switch(type) {
|
||||
case 'detail':
|
||||
// Open application detail page in new tab
|
||||
window.open(`/#/application/config/${appId}`, '_blank');
|
||||
break;
|
||||
}
|
||||
refresh();
|
||||
handleClose();
|
||||
refresh();
|
||||
setTimeout(() => {
|
||||
switch (type) {
|
||||
case 'detail':
|
||||
// Open application detail page in new tab
|
||||
window.open(`/#/application/config/${appId}`, '_blank');
|
||||
break;
|
||||
}
|
||||
}, 100)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -350,7 +358,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
|
||||
title={t('application.importSuccess')}
|
||||
subTitle={t('application.importSuccessDesc')}
|
||||
extra={[
|
||||
<Button key="back" onClick={() => handleJump('list')}>
|
||||
<Button key="back" onClick={() => handleJump('list')}>
|
||||
{t('application.gotoList')}
|
||||
</Button>,
|
||||
<Button
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-06 21:09:42
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-05 15:09:22
|
||||
* @Last Modified time: 2026-03-06 12:20:43
|
||||
*/
|
||||
/**
|
||||
* File Upload Component
|
||||
@@ -208,6 +208,7 @@ const UploadFiles = forwardRef<UploadFilesRef, UploadFilesProps>(({
|
||||
newFileList.map(file => {
|
||||
const type = (file.type && transform_file_type[file.type as keyof typeof transform_file_type]) || file.type || 'document'
|
||||
file.type = type
|
||||
file.thumbUrl = file.thumbUrl || URL.createObjectURL(file.originFileObj as Blob)
|
||||
})
|
||||
setFileList(newFileList);
|
||||
if (onChange) {
|
||||
|
||||
@@ -672,9 +672,17 @@ const CreateModal = forwardRef<CreateModalRef, CreateModalRefProps>(({
|
||||
{currentType !== 'Folder' && dynamicTypeList.map((tp) => {
|
||||
const fieldKey = typeToFieldKey(tp);
|
||||
// When tp is 'llm', merge llm and chat options
|
||||
const options = tp.toLowerCase() === 'llm' || tp.toLowerCase() === 'image2text'
|
||||
let options = tp.toLowerCase() === 'llm' || tp.toLowerCase() === 'image2text'
|
||||
? [...(modelOptionsByType['llm'] || []), ...(modelOptionsByType['chat'] || [])]
|
||||
: modelOptionsByType[tp] || [];
|
||||
|
||||
// When tp is 'image2text', filter to only include models with 'vision' capability
|
||||
if (tp.toLowerCase() === 'image2text') {
|
||||
options = options.filter((opt: any) => {
|
||||
const model = models?.items?.find((m: any) => m.id === opt.value);
|
||||
return model?.capability?.includes('vision');
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Form.Item
|
||||
key={tp}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:06
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-04 10:09:45
|
||||
* @Last Modified time: 2026-03-06 13:49:00
|
||||
*/
|
||||
/**
|
||||
* Memory Extraction Engine Configuration Constants
|
||||
@@ -140,13 +140,8 @@ export const configList: ConfigVo[] = [
|
||||
{
|
||||
label: 'intelligentSemanticPruningScene',
|
||||
variableName: 'pruning_scene',
|
||||
control: 'select',
|
||||
control: 'text',
|
||||
type: 'enum',
|
||||
options: [
|
||||
{ label: 'education', value: 'education' },
|
||||
{ label: 'online_service', value: 'online_service' },
|
||||
{ label: 'outbound', value: 'outbound' },
|
||||
],
|
||||
meaning: 'intelligentSemanticPruningSceneDesc',
|
||||
},
|
||||
// Intelligent semantic pruning阈值
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:02
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:30:02
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-06 13:50:05
|
||||
*/
|
||||
/**
|
||||
* Memory Extraction Engine Configuration Page
|
||||
@@ -13,7 +13,7 @@
|
||||
import { type FC, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Row, Col, Space, Select, InputNumber, Slider, App, Form } from 'antd'
|
||||
import { Row, Col, Space, Select, InputNumber, Slider, App, Form, Input } from 'antd'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import Card from './components/Card'
|
||||
@@ -35,15 +35,15 @@ const keys = [
|
||||
/**
|
||||
* Configuration description component
|
||||
*/
|
||||
const ConfigDesc: FC<{ config: Variable, className?: string }> = ({config, className}) => {
|
||||
const ConfigDesc: FC<{ config: Variable, className?: string; onlyMeaning?: boolean; }> = ({ config, className, onlyMeaning = false}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={className}>
|
||||
<Space size={8} className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
|
||||
{!onlyMeaning && <Space size={8} className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
|
||||
{config.variableName && <span className="rb:font-regular">{t('memoryExtractionEngine.variableName')}: {config.variableName}</span>}
|
||||
{config.control && <span className="rb:font-regular">{t('memoryExtractionEngine.control')}: {t(`memoryExtractionEngine.${config.control}`)}</span>}
|
||||
{config.type && <span className="rb:font-regular">{t('memoryExtractionEngine.type')}: {config.type}</span>}
|
||||
</Space>
|
||||
</Space>}
|
||||
{config.meaning && <div className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>{t('memoryExtractionEngine.Meaning')}: {t(`memoryExtractionEngine.${config.meaning}`)}</div>}
|
||||
</div>
|
||||
)
|
||||
@@ -253,6 +253,21 @@ const MemoryExtractionEngine: FC = () => {
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{config.control === 'text' &&
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
-{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
<div className="rb:pl-2">
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} disabled />
|
||||
</Form.Item>
|
||||
<ConfigDesc config={config} onlyMeaning={true} className="rb:-mt-4!" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:33:15
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-05 16:28:58
|
||||
* @Last Modified time: 2026-03-06 13:53:53
|
||||
*/
|
||||
/**
|
||||
* Memory Management Page
|
||||
@@ -154,10 +154,10 @@ const MemoryManagement: React.FC = () => {
|
||||
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/edit.svg')] rb:hover:bg-[url('@/assets/images/edit_hover.svg')]"
|
||||
onClick={() => handleEdit(item)}
|
||||
></div>
|
||||
<div
|
||||
{!item.is_system_default && <div
|
||||
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"
|
||||
onClick={() => handleDelete(item)}
|
||||
></div>
|
||||
></div>}
|
||||
</Space>
|
||||
</div>
|
||||
</RbCard>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:49:45
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-04 11:50:47
|
||||
* @Last Modified time: 2026-03-06 12:26:12
|
||||
*/
|
||||
/**
|
||||
* Model List Detail Drawer
|
||||
@@ -153,7 +153,7 @@ const ModelListDetail = forwardRef<ModelListDetailRef, ModelListDetailProps>(({
|
||||
<div className="rb:absolute rb:bottom-4 rb:left-6 rb:right-6">
|
||||
<Row gutter={12}>
|
||||
<Col span={12}>
|
||||
<Button block onClick={() => handleEdit(item)}>{t('modelNew.modelConfiguration')}</Button>
|
||||
{!item.model_id && <Button block onClick={() => handleEdit(item)}>{t('modelNew.modelConfiguration')}</Button>}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Button type="primary" ghost block onClick={() => handleKeyConfig(item)}>{t('modelNew.keyConfig')}</Button>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:50:18
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-04 11:39:20
|
||||
* @Last Modified time: 2026-03-06 12:26:11
|
||||
*/
|
||||
/**
|
||||
* Type definitions for Model Management
|
||||
@@ -121,6 +121,7 @@ export interface ModelApiKey {
|
||||
* Model list item data structure
|
||||
*/
|
||||
export interface ModelListItem {
|
||||
model_id?: string;
|
||||
/** Model name */
|
||||
model_name?: string;
|
||||
/** Associated model config IDs */
|
||||
|
||||
@@ -105,7 +105,7 @@ const Detail: FC = () => {
|
||||
<Tag color="warning">{t('common.default')}</Tag>
|
||||
</Space>}
|
||||
subTitle={<Tooltip title={data.scene_description}><div className="rb:h-4 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{data.scene_description}</div></Tooltip>}
|
||||
extra={data.is_system_default ? undefined : (<Space>
|
||||
extra={!data.is_system_default ? undefined : (<Space>
|
||||
<Button type="primary" ghost className="rb:h-6! rb:px-2! rb:leading-5.5!" onClick={handleAdd}>+ {t('ontology.addClass')}</Button>
|
||||
<Button className="rb:h-6! rb:px-2! rb:leading-5.5!" type="primary" onClick={handleExtract}>+ {t('ontology.extract')}</Button>
|
||||
</Space>)}
|
||||
|
||||
Reference in New Issue
Block a user