Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
lmp_web
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lmp
lmp_web
Commits
4fcaf270
Commit
4fcaf270
authored
Mar 08, 2024
by
mhw
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
版本更新
parent
84bea040
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
1758 additions
and
1000 deletions
+1758
-1000
.eslintrc.js
.eslintrc.js
+2
-1
ModelDeployment.js
src/api/GptController/ModelDeployment.js
+26
-0
gptController.js
src/api/gptController.js
+3
-1
development.js
src/core/config/development.js
+2
-1
systemRouters.js
src/router/systemRouters.js
+5
-5
gptStaticDict.js
src/staticDict/gptStaticDict.js
+32
-2
editOrAdd.vue
...iews/gptTraining/modelFineTuning/sft/dialog/editOrAdd.vue
+114
-88
index.vue
...delFineTuning/sft/dialog/parameterConfiguration/index.vue
+263
-255
index.vue
...ws/gptTraining/modelManagement/modelCompression/index.vue
+208
-99
editOrAdd.vue
...ning/modelManagement/modelDeployment/dialog/editOrAdd.vue
+148
-0
index.vue
...ement/modelDeployment/dialog/modelConfiguration/index.vue
+70
-0
index.vue
...ews/gptTraining/modelManagement/modelDeployment/index.vue
+294
-0
index.vue
...ing/modelManagement/modelDeployment/particulars/index.vue
+127
-0
index.vue
...nagement/modelEvaluation/parameterConfiguration/index.vue
+175
-168
index.vue
...ning/modelService/testOnline/components/leftBox/index.vue
+154
-106
index.vue
...ing/modelService/testOnline/components/rightBox/index.vue
+67
-83
index-2.vue
src/views/welcome/index-2.vue
+0
-124
index.vue
src/views/welcome/index.vue
+68
-67
No files found.
.eslintrc.js
View file @
4fcaf270
...
...
@@ -23,7 +23,8 @@ module.exports = {
'lines-between-class-members'
:
[
'off'
],
// 'no-undef': ['off', 'always'],
// 'no-unused-vars': ['off', 'always'],
'no-new-func'
:
[
'off'
,
'always'
]
'no-new-func'
:
[
'off'
,
'always'
],
'space-before-function-paren'
:
0
},
overrides
:
[
{
...
...
src/api/GptController/ModelDeployment.js
0 → 100644
View file @
4fcaf270
export
default
class
ModelDeployment
{
static
list
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/list'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
listForTree
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelManage/listForTree'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
add
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/add'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
delete
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/delete'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
deploy
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/deploy'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
getGpuInfo
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/getGpuInfo'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
stop
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/stop'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
static
canUseList
(
sender
,
params
,
axiosOption
,
httpOption
)
{
return
sender
.
doUrl
(
'/admin/app/modelDeploy/canUseList'
,
'post'
,
params
,
axiosOption
,
httpOption
);
}
}
src/api/gptController.js
View file @
4fcaf270
...
...
@@ -16,6 +16,7 @@ import TuningRun from './GptController/TuningRun.js';
import
PyApi
from
'./GptController/PyApi.js'
;
import
KnowledgeManage
from
'./GptController/KnowledgeManage.js'
;
import
DatasetData
from
'./GptController/DatasetData.js'
;
import
ModelDeployment
from
'./GptController/ModelDeployment.js'
;
export
{
TemplateController
,
...
...
@@ -34,5 +35,6 @@ export {
TuningRun
,
PyApi
,
KnowledgeManage
,
DatasetData
DatasetData
,
ModelDeployment
}
src/core/config/development.js
View file @
4fcaf270
module
.
exports
=
{
baseUrl
:
'http://218.76.0.69:8082/'
,
// baseUrl: 'http://218.76.0.69:8082/',
baseUrl
:
'http://192.168.0.34:8082/'
,
wsUrl
:
'ws://218.76.0.69:7860/'
,
projectName
:
'灵境大模型平台'
}
src/router/systemRouters.js
View file @
4fcaf270
...
...
@@ -14,20 +14,21 @@ const routers = [
component
:
_import
(
'login/index'
),
name
:
'root'
},
{
path
:
'/aggregate'
,
component
:
_import
(
'welcome/index-2
'
),
name
:
'aggregate'
,
meta
:
{
title
:
'集群'
}},
// {path: '/aggregate', component: _import('welcome/index
'), name: 'aggregate', meta: { title: '集群' }},
{
path
:
'/main'
,
component
:
_import
(
'layout/index'
),
name
:
'main'
,
props
:
getProps
,
redirect
:
{
name
:
'
aggregat
e'
name
:
'
welcom
e'
},
meta
:
{
title
:
'主页'
,
showOnly
:
true
},
children
:
[
{
path
:
'welcome'
,
component
:
_import
(
'welcome/index'
),
name
:
'welcome'
,
meta
:
{
title
:
'欢迎'
}
},
{
path
:
'formSysUser'
,
component
:
_import
(
'upms/formSysUser/index'
),
name
:
'formSysUser'
,
meta
:
{
title
:
'用户管理'
}
},
{
path
:
'formSysDept'
,
component
:
_import
(
'upms/formSysDept/index'
),
name
:
'formSysDept'
,
meta
:
{
title
:
'部门管理'
}
},
{
path
:
'formSysRole'
,
component
:
_import
(
'upms/formSysRole/index'
),
name
:
'formSysRole'
,
meta
:
{
title
:
'角色管理'
}
},
...
...
@@ -52,6 +53,7 @@ const routers = [
{
path
:
'modelSquare'
,
component
:
_import
(
'gptTraining/modelManagement/modelSquare/index'
),
name
:
'modelSquare'
,
props
:
getProps
,
meta
:
{
title
:
'模型广场'
}
},
{
path
:
'modelEvaluation'
,
component
:
_import
(
'gptTraining/modelManagement/modelEvaluation/index'
),
name
:
'modelEvaluation'
,
props
:
getProps
,
meta
:
{
title
:
'模型评估'
}
},
{
path
:
'modelCompression'
,
component
:
_import
(
'gptTraining/modelManagement/modelCompression/index'
),
name
:
'modelCompression'
,
props
:
getProps
,
meta
:
{
title
:
'模型压缩'
}
},
{
path
:
'modelDeployment'
,
component
:
_import
(
'gptTraining/modelManagement/modelDeployment/index'
),
name
:
'modelDeployment'
,
props
:
getProps
,
meta
:
{
title
:
'模型部署'
}
},
// 模型服务
{
path
:
'serviceManagement'
,
component
:
_import
(
'gptTraining/modelService/serviceManagement/index'
),
name
:
'serviceManagement'
,
props
:
getProps
,
meta
:
{
title
:
'在线服务'
}
},
{
path
:
'applicationAccess'
,
component
:
_import
(
'gptTraining/modelService/applicationAccess/index'
),
name
:
'applicationAccess'
,
props
:
getProps
,
meta
:
{
title
:
'应用接入'
}
},
...
...
@@ -89,9 +91,7 @@ const routers = [
// { path: 'onLineOptimization', component: _import('gptTraining/promptProject/onLineOptimization/index'), name: 'onLineOptimization', props: getProps, meta: { title: '在线优化' } },
// { path: 'batchOptimization', component: _import('gptTraining/promptProject/batchOptimization/index'), name: 'batchOptimization', props: getProps, meta: { title: '批量优化' } },
// 知识库
{
path
:
'knowledgeBase'
,
component
:
_import
(
'gptTraining/knowledgeBase/index'
),
name
:
'knowledgeBase'
,
props
:
getProps
,
meta
:
{
title
:
'知识库'
}
},
{
path
:
'welcome'
,
component
:
_import
(
'welcome/index'
),
name
:
'welcome'
,
meta
:
{
title
:
'欢迎'
}
}
{
path
:
'knowledgeBase'
,
component
:
_import
(
'gptTraining/knowledgeBase/index'
),
name
:
'knowledgeBase'
,
props
:
getProps
,
meta
:
{
title
:
'知识库'
}
}
]
}
];
...
...
src/staticDict/gptStaticDict.js
View file @
4fcaf270
...
...
@@ -455,7 +455,36 @@ const RunningStatus = new DictionaryBase('运行状态', [
symbol
:
'TrainingUnderway'
}
]);
Vue
.
prototype
.
RunningStatus
=
RunningStatus
;
const
DeploymentStatus
=
new
DictionaryBase
(
'部署状态'
,
[
{
id
:
-
1
,
name
:
'部署失败'
,
symbol
:
'DeploymentFailure'
},
{
id
:
0
,
name
:
'部署中'
,
symbol
:
'Deployed'
},
{
id
:
1
,
name
:
'部署成功'
,
symbol
:
'DeploymentComplete'
},
{
id
:
2
,
name
:
'部署中断'
,
symbol
:
'DeploymentInterrupt'
},
{
id
:
3
,
name
:
'待部署'
,
symbol
:
'ToBeDeployed'
}
]);
Vue
.
prototype
.
DeploymentStatus
=
DeploymentStatus
;
export
{
TemplateLabelDict
,
ScenarioTypeDict
,
...
...
@@ -475,5 +504,6 @@ export {
ModelCreationMode
,
TrainingMethod
,
ModeOfSpeaking
,
RunningStatus
RunningStatus
,
DeploymentStatus
}
src/views/gptTraining/modelFineTuning/sft/dialog/editOrAdd.vue
View file @
4fcaf270
<!-- 创建sft模板 -->
<
template
>
<el-form
label-position=
"left"
ref=
"form"
label-width=
"120px"
:model=
"form"
:size=
"defaultFormItemSize"
:rules=
"rules"
>
<el-row
class=
"title"
>
基本信息
</el-row>
<el-form-item
label=
"任务名称:"
prop=
"tuningTaskDto.taskName"
>
<el-input
v-model=
"form.tuningTaskDto.taskName"
:readonly=
"isEdit||existingTask"
class=
"inputWidth"
:size=
"defaultFormItemSize"
v-if=
"!(isEdit||existingTask)"
></el-input>
<span
v-else
>
{{
form
.
tuningTaskDto
.
taskName
}}
</span>
<el-input
v-model=
"form.tuningTaskDto.taskName"
:readonly=
"isEdit || existingTask"
class=
"inputWidth"
:size=
"defaultFormItemSize"
v-if=
"!(isEdit || existingTask)"
></el-input>
<span
v-else
>
{{
form
.
tuningTaskDto
.
taskName
}}
</span>
</el-form-item>
<!--
<el-form-item
label=
"任务类型:"
>
<el-select
v-model=
"form.tuningTaskDto.taskType"
placeholder=
"请选择"
>
...
...
@@ -13,39 +13,41 @@
</el-select>
</el-form-item>
-->
<el-form-item
label=
"任务描述:"
>
<el-input
v-model=
"form.tuningTaskDto.taskDescribe"
:readonly=
"isEdit||existingTask"
type=
"textarea"
:rows=
"4"
class=
"inputWidth"
:size=
"defaultFormItemSize"
v-if=
"!(isEdit||existingTask)"
></el-input>
<el-input
v-model=
"form.tuningTaskDto.taskDescribe"
:readonly=
"isEdit || existingTask"
type=
"textarea"
:rows=
"4"
class=
"inputWidth"
:size=
"defaultFormItemSize"
v-if=
"!(isEdit || existingTask)"
></el-input>
<span
v-else
>
{{
form
.
tuningTaskDto
.
taskDescribe
}}
</span>
</el-form-item>
<el-row
class=
"title"
>
训练配置
</el-row>
<el-row>
<span
class=
"introduce"
>
训练任务的算法选择、参数及相关配置,训练配置参数影响训练速度及模型效果。
</span></el-row>
<el-row>
<span
class=
"introduce"
>
训练任务的算法选择、参数及相关配置,训练配置参数影响训练速度及模型效果。
</span></el-row>
<el-form-item
label=
"选择基础模型版本:"
prop=
"tuningRunDto.modelVersionId"
>
<el-cascader
v-model=
"form.tuningRunDto.modelVersionId"
:options=
"modelList"
:props=
'
{ label: "name", value: "id", emitPath: false }'
>
</el-cascader>
:props=
"
{ label: 'name', value: 'id', emitPath: false }"
>
</el-cascader>
</el-form-item>
<el-form-item
label=
"训练方法:"
>
<el-radio
v-for=
"item in TrainingMethod.getList()"
:key=
"item.id"
v-model=
"form.tuningRunDto.trainMethod"
:label=
"item.name"
>
{{
item
.
name
}}
</el-radio>
<el-radio
v-for=
"item in TrainingMethod.getList()"
:key=
"item.id"
v-model=
"form.tuningRunDto.trainMethod"
:label=
"item.name"
>
{{
item
.
name
}}
</el-radio>
</el-form-item>
<el-form-item
label=
"参数配置:"
>
<parameterConfiguration
v-model=
"form.tuningRunDto.configuration"
:existingTask=
"existingTask"
/>
<parameterConfiguration
v-model=
"form.tuningRunDto.configuration"
:existingTask=
"existingTask"
/>
</el-form-item>
<el-row
class=
"title"
>
数据配置
</el-row>
<el-row>
<span
class=
"introduce"
>
训练任务的选择数据及相关配置,支持选择该模型可使用的数据。
</span></el-row>
<el-row>
<span
class=
"introduce"
>
训练任务的选择数据及相关配置,支持选择该模型可使用的数据。
</span></el-row>
<el-form-item
label=
"选择数据集版本"
prop=
"tuningRunDto.datasetVersionId"
>
<el-cascader
v-model=
"form.tuningRunDto.datasetVersionId"
:options=
"dataList"
:props=
'
{ label: "name", value: "id", emitPath: false }'
>
</el-cascader>
:props=
"
{ label: 'name', value: 'id', emitPath: false }"
>
</el-cascader>
</el-form-item>
<el-form-item
label=
"数据拆分比例:"
>
<el-input-number
v-model=
"form.tuningRunDto.splitRatio"
class=
"inputWidth"
:size=
"defaultFormItemSize"
:min=
"0"
:max=
"100"
></el-input-number>
<el-input-number
v-model=
"form.tuningRunDto.splitRatio"
class=
"inputWidth"
:size=
"defaultFormItemSize"
:min=
"0"
:max=
"100"
></el-input-number>
</el-form-item>
<el-row
type=
"flex"
justify=
"end"
class=
"dialog-btn-layer mt20"
>
<el-button
:size=
"defaultFormItemSize"
:plain=
"true"
@
click=
"onCancel(false)"
>
取消
</el-button>
<el-button
type=
"primary"
:size=
"defaultFormItemSize"
@
click=
"onSubmit"
>
确定
</el-button>
</el-row>
...
...
@@ -53,146 +55,170 @@
</
template
>
<
script
>
import
{
TuningTask
,
TuningRun
,
MyModel
,
MyDataSet
}
from
'@/api/gptController.js'
;
import
parameterConfiguration
from
'./parameterConfiguration/index.vue'
;
import
{
TuningTask
,
TuningRun
,
MyModel
,
MyDataSet
}
from
'@/api/gptController.js'
import
parameterConfiguration
from
'./parameterConfiguration/index.vue'
export
default
{
data
()
{
data
()
{
return
{
modelList
:
[],
dataList
:
[],
form
:
{
'tuningRunDto'
:
{
'configuration'
:
undefined
,
'modelId'
:
undefined
,
'publishStatus'
:
0
,
'runName'
:
''
,
'runStatus'
:
0
,
'runTime'
:
0
,
'runVersion'
:
0
,
'splitRatio'
:
0
,
'taskId'
:
undefined
,
'trainMethod'
:
'full'
,
'trainMode'
:
''
,
tuningRunDto
:
{
configuration
:
undefined
,
modelId
:
undefined
,
publishStatus
:
0
,
runName
:
''
,
runStatus
:
0
,
runTime
:
0
,
runVersion
:
0
,
splitRatio
:
0
,
taskId
:
undefined
,
trainMethod
:
'full'
,
trainMode
:
''
,
datasetVersionId
:
undefined
,
modelVersionId
:
undefined
},
'tuningTaskDto'
:
{
'taskDescribe'
:
''
,
'taskName'
:
''
,
'taskType'
:
0
tuningTaskDto
:
{
taskDescribe
:
''
,
taskName
:
''
,
taskType
:
0
}
},
rules
:
{
'tuningTaskDto.taskName'
:
[
{
required
:
true
,
message
:
'请输入任务名称'
,
trigger
:
'blur'
}
],
'tuningTaskDto.taskName'
:
[{
required
:
true
,
message
:
'请输入任务名称'
,
trigger
:
'blur'
}],
'tuningRunDto.modelVersionId'
:
[{
required
:
true
,
message
:
'请选择基础模型版'
,
trigger
:
'blur'
}],
'tuningRunDto.datasetVersionId'
:
[{
required
:
true
,
message
:
'请选择数据集版本'
,
trigger
:
'blur'
}]
}
}
;
}
},
props
:
[
'isEdit'
,
'item'
,
'existingTask'
],
components
:
{
parameterConfiguration
},
components
:
{
parameterConfiguration
},
computed
:
{
},
computed
:
{},
mounted
()
{
mounted
()
{
this
.
getModelList
()
this
.
getDataList
()
this
.
intFrom
()
},
methods
:
{
intFrom
()
{
intFrom
()
{
if
(
this
.
item
)
{
this
.
form
.
tuningTaskDto
=
this
.
item
.
tuningTaskDto
if
(
this
.
isEdit
)
{
this
.
form
.
tuningRunDto
=
{...
this
.
form
.
tuningRunDto
,
...
this
.
item
.
tuningRunDto
}
this
.
form
.
tuningRunDto
=
{
...
this
.
form
.
tuningRunDto
,
...
this
.
item
.
tuningRunDto
}
}
if
(
this
.
existingTask
)
{
this
.
form
.
tuningRunDto
.
taskId
=
this
.
item
.
tuningTaskDto
.
taskId
}
}
},
onCancel
(
isSuccess
)
{
onCancel
(
isSuccess
)
{
if
(
this
.
observer
!=
null
)
{
this
.
observer
.
cancel
(
isSuccess
)
;
this
.
observer
.
cancel
(
isSuccess
)
}
},
onSubmit
()
{
onSubmit
()
{
let
apiFunction
=
this
.
existingTask
||
this
.
isEdit
?
TuningRun
:
TuningTask
return
new
Promise
((
resolve
,
reject
)
=>
{
this
.
$refs
[
'form'
].
validate
((
valid
)
=>
{
if
(
valid
)
{
let
params
=
{}
;
params
=
this
.
existingTask
?
{
tuningRunDto
:
this
.
form
.
tuningRunDto
}
:
{
...
this
.
form
};
console
.
log
(
params
)
;
let
params
=
{}
params
=
this
.
existingTask
?
{
tuningRunDto
:
this
.
form
.
tuningRunDto
}
:
{
...
this
.
form
}
console
.
log
(
params
)
if
(
this
.
isEdit
)
{
apiFunction
.
update
(
this
,
params
).
then
(
res
=>
{
resolve
(
res
);
this
.
$message
.
success
(
'编辑成功'
);
this
.
onCancel
(
true
);
}).
catch
(
e
=>
{
reject
(
e
);
});
apiFunction
.
update
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
(
res
)
this
.
$message
.
success
(
'编辑成功'
)
this
.
onCancel
(
true
)
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
}
else
{
apiFunction
.
add
(
this
,
params
).
then
(
res
=>
{
resolve
(
res
);
this
.
$message
.
success
(
'添加成功'
);
this
.
onCancel
(
true
);
}).
catch
(
e
=>
{
reject
(
e
);
});
apiFunction
.
add
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
(
res
)
this
.
$message
.
success
(
'添加成功'
)
this
.
onCancel
(
true
)
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
}
}
else
{
// reject();
}
})
;
})
;
})
})
},
getModelList
()
{
MyModel
.
listForTree
(
this
,
{}).
then
(
res
=>
{
this
.
modelList
=
res
.
data
.
filter
((
item
)
=>
item
.
isBaseModel
===
1
).
map
((
item
)
=>
{
return
{
id
:
item
.
modelId
,
name
:
item
.
modelName
,
children
:
item
.
modelVersionList
===
[]
?
[]
:
item
.
modelVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
'V'
+
item2
.
modelVersion
}
})
}
getModelList
()
{
MyModel
.
listForTree
(
this
,
{})
.
then
((
res
)
=>
{
this
.
modelList
=
res
.
data
.
filter
((
item
)
=>
item
.
isBaseModel
===
1
)
.
map
((
item
)
=>
{
return
{
id
:
item
.
modelId
,
name
:
item
.
modelName
,
children
:
item
.
modelVersionList
===
[]
?
[]
:
item
.
modelVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
'V'
+
item2
.
modelVersion
}
})
}
})
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
},
getDataList
()
{
MyDataSet
.
listForTree
(
this
,
{}).
then
(
res
=>
{
this
.
dataList
=
res
.
data
.
map
((
item
)
=>
{
return
{
id
:
item
.
datasetId
,
name
:
item
.
datasetName
,
children
:
item
.
datasetVersionList
===
[]
?
[]
:
item
.
datasetVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
'V'
+
item2
.
datasetVersion
}
})
}
getDataList
()
{
MyDataSet
.
listForTree
(
this
,
{})
.
then
((
res
)
=>
{
this
.
dataList
=
res
.
data
.
map
((
item
)
=>
{
return
{
id
:
item
.
datasetId
,
name
:
item
.
datasetName
,
// prettier-ignore
children
:
item
.
datasetVersionList
===
[]
?
[]
:
item
.
datasetVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
'V'
+
item2
.
datasetVersion
}
})
}
})
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
}
}
}
;
}
</
script
>
<
style
scoped
>
.inputWidth
{
width
:
600px
;
}
.title
{
font-size
:
20px
;
margin-bottom
:
16px
;
}
.introduce
{
font-size
:
12px
;
color
:
#909399
;
margin-bottom
:
10px
;
display
:
block
;
display
:
block
;
}
.isReadonly
input
{
.isReadonly
input
{
border
:
none
;
}
</
style
>
src/views/gptTraining/modelFineTuning/sft/dialog/parameterConfiguration/index.vue
View file @
4fcaf270
<!-- 参数配置-->
<
template
>
<el-table
:data=
"existingTasklist"
style=
"width: 100%"
>
<el-table-column
prop=
"hyperParameter"
label=
"超参数"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"numericalValue"
label=
"数值"
>
<template
slot-scope=
"scope"
>
<component
v-if=
"configData"
:ref=
"scope.row.numericalValue"
:is=
"scope.row.componentOptions.component"
style=
"width: 100%"
v-bind=
"scope.row.componentOptions.property"
:value=
"configData[scope.row.numericalValue]||scope.row.defaultValue"
:size=
"defaultFormItemSize"
@
input=
" e =>
{changeValue(e, scope.row.numericalValue);}">
</component>
</
template
>
</el-table-column>
<el-table-column
prop=
"explain"
label=
"说明"
>
</el-table-column>
</el-table>
<el-table
:data=
"existingTasklist"
style=
"width: 100%"
>
<el-table-column
prop=
"hyperParameter"
label=
"超参数"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"numericalValue"
label=
"数值"
>
<template
slot-scope=
"scope"
>
<component
v-if=
"configData"
:ref=
"scope.row.numericalValue"
:is=
"scope.row.componentOptions.component"
style=
"width: 100%"
v-bind=
"scope.row.componentOptions.property"
:value=
"configData[scope.row.numericalValue]||scope.row.defaultValue"
:size=
"defaultFormItemSize"
@
input=
" e =>
{changeValue(e, scope.row.numericalValue);}">
</component>
</
template
>
</el-table-column>
<el-table-column
prop=
"explain"
label=
"说明"
>
</el-table-column>
</el-table>
</template>
<
script
>
export
default
{
data
()
{
data
()
{
return
{
configData
:
undefined
,
parameterConfiguration
:
[
{
...
...
@@ -35,20 +26,23 @@ export default {
explain
:
'启用 4/8 比特模型量化(QLoRA)。'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[{
value
:
'none'
,
label
:
'none'
},
{
value
:
'8'
,
label
:
'8'
},
{
value
:
'4'
,
label
:
'4'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'none'
,
label
:
'none'
},
{
value
:
'8'
,
label
:
'8'
},
{
value
:
'4'
,
label
:
'4'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
...
...
@@ -58,87 +52,111 @@ export default {
explain
:
'构建提示词时使用的模板'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[
{
value
:
'alpaca'
,
label
:
'alpaca'
},
{
value
:
'aquila'
,
label
:
'aquila'
},
{
value
:
'baichuan'
,
label
:
'baichuan'
},
{
value
:
'baichuan2'
,
label
:
'baichuan2'
},
{
value
:
'belle'
,
label
:
'belle'
},
{
value
:
'bluelm'
,
label
:
'bluelm'
},
{
value
:
'chatglm2'
,
label
:
'chatglm2'
},
{
value
:
'chatglm3'
,
label
:
'chatglm3'
},
{
value
:
'chatglm3_raw'
,
label
:
'chatglm3_raw'
},
{
value
:
'deepseek'
,
label
:
'deepseek'
},
{
value
:
'default'
,
label
:
'default'
},
{
value
:
'falcon'
,
label
:
'falcon'
},
{
value
:
'intern'
,
label
:
'intern'
},
{
value
:
'llama2'
,
label
:
'llama2'
},
{
value
:
'llama2_zh'
,
label
:
'llama2_zh'
},
{
value
:
'mistral'
,
label
:
'mistral'
},
{
value
:
'openchat'
,
label
:
'openchat'
},
{
value
:
'qwen'
,
label
:
'qwen'
},
{
value
:
'starchat'
,
label
:
'starchat'
},
{
value
:
'vanilla'
,
label
:
'vanilla'
},
{
value
:
'vicuna'
,
label
:
'vicuna'
},
{
value
:
'xverse'
,
label
:
'xverse'
},
{
value
:
'yayi'
,
label
:
'yayi'
},
{
value
:
'zephyr'
,
label
:
'zephyr'
},
{
value
:
'ziya'
,
label
:
'ziya'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'alpaca'
,
label
:
'alpaca'
},
{
value
:
'aquila'
,
label
:
'aquila'
},
{
value
:
'baichuan'
,
label
:
'baichuan'
},
{
value
:
'baichuan2'
,
label
:
'baichuan2'
},
{
value
:
'belle'
,
label
:
'belle'
},
{
value
:
'bluelm'
,
label
:
'bluelm'
},
{
value
:
'chatglm2'
,
label
:
'chatglm2'
},
{
value
:
'chatglm3'
,
label
:
'chatglm3'
},
{
value
:
'chatglm3_raw'
,
label
:
'chatglm3_raw'
},
{
value
:
'deepseek'
,
label
:
'deepseek'
},
{
value
:
'default'
,
label
:
'default'
},
{
value
:
'falcon'
,
label
:
'falcon'
},
{
value
:
'intern'
,
label
:
'intern'
},
{
value
:
'llama2'
,
label
:
'llama2'
},
{
value
:
'llama2_zh'
,
label
:
'llama2_zh'
},
{
value
:
'mistral'
,
label
:
'mistral'
},
{
value
:
'openchat'
,
label
:
'openchat'
},
{
value
:
'qwen'
,
label
:
'qwen'
},
{
value
:
'starchat'
,
label
:
'starchat'
},
{
value
:
'vanilla'
,
label
:
'vanilla'
},
{
value
:
'vicuna'
,
label
:
'vicuna'
},
{
value
:
'xverse'
,
label
:
'xverse'
},
{
value
:
'yayi'
,
label
:
'yayi'
},
{
value
:
'zephyr'
,
label
:
'zephyr'
},
{
value
:
'ziya'
,
label
:
'ziya'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
...
...
@@ -148,12 +166,10 @@ export default {
explain
:
'输入序列分词后的最大长度。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
min
:
4
,
max
:
8192
}
property
:
{
min
:
4
,
max
:
8192
}
}
},
{
...
...
@@ -163,11 +179,10 @@ export default {
explain
:
'学习率(LearningRate)是在梯度下降的过程中更新权重时的超参数,过高会导致模型难以收敛,过低则会导致模型收敛速度过慢,平台已给出默认推荐值,可根据经验调整。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
0.0002
,
min
:
2
e
-
7
}
property
:
{
max
:
0.0002
,
min
:
2
e
-
7
}
}
},
{
...
...
@@ -177,11 +192,10 @@ export default {
explain
:
'迭代轮次(Epoch),控制训练过程中的迭代轮数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
500
,
min
:
0
}
property
:
{
max
:
500
,
min
:
0
}
}
},
{
...
...
@@ -191,11 +205,10 @@ export default {
explain
:
'每个数据集最多使用的样本数'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
100000
,
min
:
0
}
property
:
{
max
:
100000
,
min
:
0
}
}
},
{
...
...
@@ -205,17 +218,19 @@ export default {
explain
:
'是否启用 FP16 或 BF16 混合精度训练。'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[{
value
:
'fp16'
,
label
:
'fp16'
},
{
value
:
'bf16'
,
label
:
'bf16'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'fp16'
,
label
:
'fp16'
},
{
value
:
'bf16'
,
label
:
'bf16'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
...
...
@@ -225,14 +240,13 @@ export default {
explain
:
'批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
512
,
min
:
1
}
property
:
{
max
:
512
,
min
:
1
}
}
},
{
hyperParameter
:
'梯度累积'
,
defaultValue
:
4
,
...
...
@@ -240,11 +254,10 @@ export default {
explain
:
'梯度累积的步数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
512
,
min
:
1
}
property
:
{
max
:
512
,
min
:
1
}
}
},
{
...
...
@@ -254,38 +267,46 @@ export default {
explain
:
'采用的学习率调节器名称。'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[{
value
:
'linear'
,
label
:
'linear'
},
{
value
:
'cosine'
,
label
:
'cosine'
},
{
value
:
'cosine_with_restarts'
,
label
:
'cosine_with_restarts'
},
{
value
:
'polynomial'
,
label
:
'polynomial'
},
{
value
:
'constant'
,
label
:
'constant'
},
{
value
:
'constant_with_warmup'
,
label
:
'constant_with_warmup'
},
{
value
:
'inverse_sqrt'
,
label
:
'inverse_sqrt'
},
{
value
:
'reduce_lr_on_plateau'
,
label
:
'reduce_lr_on_plateau'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'linear'
,
label
:
'linear'
},
{
value
:
'cosine'
,
label
:
'cosine'
},
{
value
:
'cosine_with_restarts'
,
label
:
'cosine_with_restarts'
},
{
value
:
'polynomial'
,
label
:
'polynomial'
},
{
value
:
'constant'
,
label
:
'constant'
},
{
value
:
'constant_with_warmup'
,
label
:
'constant_with_warmup'
},
{
value
:
'inverse_sqrt'
,
label
:
'inverse_sqrt'
},
{
value
:
'reduce_lr_on_plateau'
,
label
:
'reduce_lr_on_plateau'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
hyperParameter
:
'最大梯度范数'
,
defaultValue
:
1
,
...
...
@@ -293,14 +314,13 @@ export default {
explain
:
'用于梯度裁剪的范数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
100000
,
min
:
0
}
property
:
{
max
:
100000
,
min
:
0
}
}
},
{
hyperParameter
:
'验证集比例'
,
defaultValue
:
0
,
...
...
@@ -308,14 +328,13 @@ export default {
explain
:
'验证集占全部样本的百分比。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1
,
min
:
0
}
property
:
{
max
:
1
,
min
:
0
}
}
},
{
hyperParameter
:
'日志间隔'
,
defaultValue
:
5
,
...
...
@@ -323,14 +342,13 @@ export default {
explain
:
'每两次日志输出间的更新步数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1000
,
min
:
5
}
property
:
{
max
:
1000
,
min
:
5
}
}
},
{
hyperParameter
:
'保存间隔'
,
defaultValue
:
100
,
...
...
@@ -338,11 +356,10 @@ export default {
explain
:
'每两次断点保存间的更新步数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
5000
,
min
:
10
}
property
:
{
max
:
5000
,
min
:
10
}
}
},
{
...
...
@@ -352,11 +369,10 @@ export default {
explain
:
'学习率预热采用的步数。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
5000
,
min
:
0
}
property
:
{
max
:
5000
,
min
:
0
}
}
},
{
...
...
@@ -366,11 +382,10 @@ export default {
explain
:
'嵌入向量所添加的噪声大小。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
10
,
min
:
0
}
property
:
{
max
:
10
,
min
:
0
}
}
},
{
...
...
@@ -380,11 +395,10 @@ export default {
explain
:
'LoRA 矩阵的秩。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1024
,
min
:
0
}
property
:
{
max
:
1024
,
min
:
0
}
}
},
{
...
...
@@ -394,11 +408,10 @@ export default {
explain
:
'LoRA 权重随机丢弃的概率。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1
,
min
:
0
}
property
:
{
max
:
1
,
min
:
0
}
}
},
{
...
...
@@ -408,37 +421,33 @@ export default {
explain
:
'DPO 损失函数中 beta 超参数大小。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1
,
min
:
0
}
property
:
{
max
:
1
,
min
:
0
}
}
}
]
}
;
}
},
props
:
[
'existingTask'
,
'value'
],
components
:
{},
computed
:
{
existingTasklist
()
{
existingTasklist
()
{
return
this
.
parameterConfiguration
}
},
mounted
()
{
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
init
()
;
this
.
init
()
})
},
methods
:
{
getProperty
()
{
},
init
()
{
getProperty
()
{},
init
()
{
let
initConfig
=
{}
this
.
parameterConfiguration
.
forEach
((
item
)
=>
{
initConfig
[
item
.
numericalValue
]
=
item
.
defaultValue
...
...
@@ -448,27 +457,26 @@ export default {
}
else
{
this
.
configData
=
initConfig
}
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
;
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
},
isJSON
(
str
)
{
isJSON
(
str
)
{
try
{
JSON
.
parse
(
str
)
;
JSON
.
parse
(
str
)
}
catch
(
e
)
{
// 转换出错,抛出异常
return
false
;
return
false
}
return
true
;
return
true
},
changeValue
(
value
,
numericalValue
)
{
changeValue
(
value
,
numericalValue
)
{
this
.
configData
[
numericalValue
]
=
value
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
;
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
}
}
}
</
script
>
<
style
>
div
/
deep
/
.el-cascader-menu__wrap
{
div
/
deep
/
.el-cascader-menu__wrap
{
height
:
auto
;
}
</
style
>
...
...
src/views/gptTraining/modelManagement/modelCompression/index.vue
View file @
4fcaf270
...
...
@@ -5,43 +5,122 @@
<div
class=
"title"
>
模型压缩
</div>
<div
class=
"instructions"
>
通过量化、稀疏化等方法在尽量减少精度损失的前提下,降低AI加速卡资源占用,提高推理速度。
</div>
</div>
<div
class=
"tableBox"
:style=
"
{ height: tableHeight }">
<div
class=
"tableBox"
:style=
"
{ height: tableHeight }"
>
<el-form
ref=
"myDataSetPage"
:model=
"myDataSetPage"
label-width=
"75px"
:size=
"defaultFormItemSize"
label-position=
"right"
@
submit
.
native
.
prevent
>
<filter-box
:item-width=
"350"
@
search=
"refresh()"
@
reset=
"onReset"
>
<el-form
ref=
"myDataSetPage"
:model=
"myDataSetPage"
label-width=
"75px"
:size=
"defaultFormItemSize"
label-position=
"right"
@
submit
.
native
.
prevent
>
<filter-box
:item-width=
"350"
@
search=
"refresh()"
@
reset=
"onReset"
>
<el-form-item
label-width=
"0px"
>
<el-button
class=
"add"
type=
"primary"
icon=
"el-icon-plus"
:size=
"defaultFormItemSize"
@
click=
"add()"
>
创建压缩任务
</el-button>
<el-button
class=
"add"
type=
"primary"
icon=
"el-icon-plus"
:size=
"defaultFormItemSize"
@
click=
"add()"
>
创建压缩任务
</el-button>
</el-form-item>
<el-form-item
label=
"任务名称"
prop=
"formFilter.taskName"
label-width=
"70px"
>
<el-input
class=
"filter-item"
v-model=
"myDataSetPage.formFilter.taskName"
:clearable=
"true"
placeholder=
"任务名称"
/>
<el-form-item
label=
"任务名称"
prop=
"formFilter.taskName"
label-width=
"70px"
>
<el-input
class=
"filter-item"
v-model=
"myDataSetPage.formFilter.taskName"
:clearable=
"true"
placeholder=
"任务名称"
/>
</el-form-item>
</filter-box>
</el-form>
<vxe-table
border
show-header-overflow
show-overflow
:row-config=
"
{ isHover: true }" :data="myDataSetPage.tableData.impl.dataList" min-height="96">
<vxe-column
field=
"taskName"
title=
"任务名称"
></vxe-column>
<vxe-column
field=
"taskStatus"
title=
"任务状态"
>
<vxe-table
border
show-header-overflow
show-overflow
:row-config=
"
{ isHover: true }"
:data="myDataSetPage.tableData.impl.dataList"
min-height="96"
>
<vxe-column
field=
"taskName"
title=
"任务名称"
></vxe-column>
<vxe-column
field=
"taskStatus"
title=
"任务状态"
>
<template
slot-scope=
"scope"
>
{{
TaskStatus
.
getValue
(
scope
.
row
.
taskStatus
)
}}
</
template
>
{{
TaskStatus
.
getValue
(
scope
.
row
.
taskStatus
)
}}
</
template
>
</vxe-column>
<vxe-column
field=
"sourceVersionId"
title=
"源模型"
>
<vxe-column
field=
"sourceVersionId"
title=
"源模型"
>
<
template
slot-scope=
"scope"
>
{{
scope
.
row
.
modelTask
?.
versionName
}}
</
template
>
{{
scope
.
row
.
modelTask
?.
versionName
}}
</
template
>
</vxe-column>
<vxe-column
field=
"targetVersionId"
title=
"压缩后模型"
></vxe-column>
<vxe-column
field=
"createTime"
title=
"创建时间"
></vxe-column>
<vxe-column
field=
"operation"
title=
"操作"
>
<vxe-column
field=
"targetVersionId"
title=
"压缩后模型"
></vxe-column>
<vxe-column
field=
"createTime"
title=
"创建时间"
></vxe-column>
<vxe-column
field=
"operation"
title=
"操作"
>
<
template
slot-scope=
"scope"
>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"particulars(scope.row)"
>
详情
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"copy(scope.row)"
>
复制
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"del(scope.row)"
>
删除
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"particulars(scope.row)"
>
详情
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"copy(scope.row)"
>
复制
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"del(scope.row)"
>
删除
</el-button>
</
template
>
</vxe-column>
</vxe-table>
<el-row
slot=
"pagination"
type=
"flex"
justify=
"end"
style=
"margin-top: 16px;"
>
<el-pagination
:total=
"myDataSetPage.tableData.impl.totalCount"
:current-page=
"myDataSetPage.tableData.impl.currentPage"
:page-size=
"myDataSetPage.tableData.impl.pageSize"
:page-sizes=
"[10, 20, 50, 100]"
layout=
"total, prev, pager, next, sizes"
@
current-change=
"myDataSetPage.tableData.impl.onCurrentPageChange"
@
size-change=
"myDataSetPage.tableData.impl.onPageSizeChange"
>
<el-row
slot=
"pagination"
type=
"flex"
justify=
"end"
style=
"margin-top: 16px;"
>
<el-pagination
:total=
"myDataSetPage.tableData.impl.totalCount"
:current-page=
"myDataSetPage.tableData.impl.currentPage"
:page-size=
"myDataSetPage.tableData.impl.pageSize"
:page-sizes=
"[10, 20, 50, 100]"
layout=
"total, prev, pager, next, sizes"
@
current-change=
"myDataSetPage.tableData.impl.onCurrentPageChange"
@
size-change=
"myDataSetPage.tableData.impl.onPageSizeChange"
>
</el-pagination>
</el-row>
</div>
...
...
@@ -49,40 +128,39 @@
</template>
<
script
>
import
{
mapGetters
}
from
'vuex'
;
import
{
mapGetters
}
from
'vuex'
/* eslint-disable-next-line */
import
{
DropdownWidget
,
TableWidget
,
UploadWidget
,
ChartWidget
}
from
'@/utils/widget.js'
;
import
{
ModelCompress
}
from
'@/api/gptController.js'
;
import
editOrAdd
from
'./dialog/editOrAdd'
;
import
particulars
from
'./dialog/particulars'
;
import
{
TableWidget
}
from
'@/utils/widget.js'
import
{
ModelCompress
}
from
'@/api/gptController.js'
import
editOrAdd
from
'./dialog/editOrAdd'
import
particulars
from
'./dialog/particulars'
export
default
{
data
()
{
data
()
{
return
{
myDataSetPage
:
{
formFilter
:
{
'taskName'
:
''
taskName
:
''
},
tableData
:
{
impl
:
new
TableWidget
(
this
.
getwidgetData
,
true
,
true
,
false
,
undefined
,
false
)
}
}
}
;
}
},
components
:
{},
computed
:
{
...
mapGetters
([
'getMainContextHeight'
]),
tableHeight
()
{
return
this
.
getMainContextHeight
-
145
+
'px'
;
tableHeight
()
{
return
this
.
getMainContextHeight
-
145
+
'px'
}
},
methods
:
{
getwidgetData
(
params
)
{
if
(
params
==
null
)
params
=
{}
;
getwidgetData
(
params
)
{
if
(
params
==
null
)
params
=
{}
params
=
{
...
params
,
// orderParam: [
...
...
@@ -100,94 +178,125 @@ export default {
}
return
new
Promise
((
resolve
,
reject
)
=>
{
ModelCompress
.
list
(
this
,
params
).
then
(
res
=>
{
resolve
({
dataList
:
res
.
data
.
dataList
,
totalCount
:
res
.
data
.
totalCount
});
}).
catch
(
e
=>
{
reject
(
e
);
});
});
ModelCompress
.
list
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
({
dataList
:
res
.
data
.
dataList
,
totalCount
:
res
.
data
.
totalCount
})
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
})
},
particulars
(
item
)
{
this
.
$dialog
.
show
(
'详情'
,
particulars
,
{
area
:
[
'100%'
,
'100%'
]
},
{
item
:
item
}).
then
(
res
=>
{
}).
catch
(
e
=>
{
});
particulars
(
item
)
{
this
.
$dialog
.
show
(
'详情'
,
particulars
,
{
area
:
[
'100%'
,
'100%'
]
},
{
item
:
item
}
)
.
then
((
res
)
=>
{})
.
catch
((
e
)
=>
{})
},
add
()
{
this
.
$dialog
.
show
(
'创建模版'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
false
}).
then
(
res
=>
{
this
.
refresh
();
}).
catch
(
e
=>
{
});
add
()
{
this
.
$dialog
.
show
(
'创建模版'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
false
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
copy
(
item
)
{
this
.
$dialog
.
show
(
'复制任务'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isCopy
:
true
,
item
:
{
'createMethod'
:
item
.
createMethod
,
'sourceVersionId'
:
item
.
sourceVersionId
,
'targetModelId'
:
item
.
targetModelId
,
'taskDescribe'
:
item
.
taskDescribe
,
'taskName'
:
item
.
taskName
}
}).
then
(
res
=>
{
this
.
refresh
();
}).
catch
(
e
=>
{
});
copy
(
item
)
{
this
.
$dialog
.
show
(
'复制任务'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isCopy
:
true
,
item
:
{
createMethod
:
item
.
createMethod
,
sourceVersionId
:
item
.
sourceVersionId
,
targetModelId
:
item
.
targetModelId
,
taskDescribe
:
item
.
taskDescribe
,
taskName
:
item
.
taskName
}
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
edit
(
item
)
{
this
.
$dialog
.
show
(
'修改模版'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
true
,
item
:
item
}).
then
(
res
=>
{
this
.
refresh
();
}).
catch
(
e
=>
{
});
edit
(
item
)
{
this
.
$dialog
.
show
(
'修改模版'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
true
,
item
:
item
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
del
(
item
)
{
del
(
item
)
{
this
.
$confirm
(
'是否确认删除'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
let
params
=
{
taskId
:
item
.
taskId
}
ModelCompress
.
delete
(
this
,
params
).
then
(
res
=>
{
this
.
$message
.
success
(
'删除成功'
);
this
.
refresh
()
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
}
);
ModelCompress
.
delete
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
'删除成功'
)
this
.
refresh
()
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
})
},
refresh
(
reloadData
=
false
)
{
refresh
(
reloadData
=
false
)
{
if
(
reloadData
)
{
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
(
true
,
1
)
;
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
(
true
,
1
)
}
else
{
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
()
;
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
()
}
},
onReset
()
{
this
.
$refs
.
myDataSetPage
.
resetFields
()
;
this
.
refresh
(
true
)
;
onReset
()
{
this
.
$refs
.
myDataSetPage
.
resetFields
()
this
.
refresh
(
true
)
},
formInit
()
{
this
.
refresh
()
;
formInit
()
{
this
.
refresh
()
}
},
mounted
()
{
mounted
()
{
this
.
formInit
()
}
};
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
"@/assets/style/element-variables.scss"
;
@import
'@/assets/style/element-variables.scss'
;
.topBox
{
background-color
:
white
;
width
:
100%
;
...
...
src/views/gptTraining/modelManagement/modelDeployment/dialog/editOrAdd.vue
0 → 100644
View file @
4fcaf270
<!-- 基本信息-->
<
template
>
<el-form
label-position=
"left"
ref=
"form"
label-width=
"100px"
:model=
"form"
:size=
"defaultFormItemSize"
:rules=
"rules"
>
<el-row
class=
"title"
>
基本信息
</el-row>
<el-form-item
label=
"服务名称:"
prop=
"modelDeployDto.serviceName"
>
<el-input
v-model=
"form.modelDeployDto.serviceName"
class=
"inputWidth"
placeholder=
"服务名称"
></el-input>
<el-row>
<span
class=
"introduce"
>
支持中英文、数字、下划线(_),2-20个字符,不能以下划线为开头
</span></el-row>
</el-form-item>
<el-form-item
label=
"服务描述:"
>
<el-input
type=
"textarea"
:rows=
"2"
v-model=
"form.modelDeployDto.serviceDescription"
placeholder=
"服务描述"
></el-input>
</el-form-item>
<el-form-item
label=
"选择模型:"
prop=
"modelDeployDto.versionId"
>
<el-cascader
v-model=
"form.modelDeployDto.versionId"
:options=
"modelList"
:props=
'
{ label: "name", value: "id", emitPath: false }'>
</el-cascader>
</el-form-item>
<el-form-item
label=
"API地址:"
>
<el-input
v-model=
"form.modelDeployDto.apiUrl"
class=
"inputWidth"
placeholder=
"API地址"
></el-input>
</el-form-item>
<el-row
class=
"title"
>
模型配置
</el-row>
<el-form-item
label=
"资源配置:"
prop=
"modelDeployDto.resourceInfo"
>
<modelConfiguration
v-model=
"form.modelDeployDto.resourceInfo"
/>
</el-form-item>
<el-row
type=
"flex"
justify=
"end"
class=
"dialog-btn-layer mt20"
>
<el-button
:plain=
"true"
@
click=
"onCancel(false)"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"onSubmit"
>
确定
</el-button>
</el-row>
</el-form>
</
template
>
<
script
>
import
{
ModelDeployment
}
from
'@/api/gptController.js'
import
modelConfiguration
from
'./modelConfiguration'
export
default
{
data
()
{
return
{
modelList
:
[],
form
:
{
modelDeployDto
:
{
apiUrl
:
undefined
,
resourceInfo
:
''
,
serviceName
:
''
,
serviceVersion
:
undefined
,
versionId
:
undefined
}
},
rules
:
{
'modelDeployDto.serviceName'
:
[{
required
:
true
,
message
:
'请输入服务名称'
,
trigger
:
'blur'
}],
'modelDeployDto.resourceInfo'
:
[{
required
:
true
,
message
:
'请选择资源配置'
,
trigger
:
'blur'
}],
'modelDeployDto.versionId'
:
[{
required
:
true
,
message
:
'请选择模型'
,
trigger
:
'blur'
}]
}
}
},
props
:
[
'isEdit'
,
'item'
],
components
:
{
modelConfiguration
},
computed
:
{},
mounted
()
{
this
.
intFrom
()
this
.
getModelList
()
},
methods
:
{
intFrom
()
{
this
.
form
=
{
...
this
.
form
,
...
this
.
item
}
try
{
this
.
form
.
templateLabel
=
JSON
.
parse
(
this
.
item
.
templateLabel
)
}
catch
(
error
)
{
// console.log(error);
}
},
onCancel
(
isSuccess
)
{
if
(
this
.
observer
!=
null
)
{
this
.
observer
.
cancel
(
isSuccess
)
}
},
onSubmit
()
{
return
new
Promise
((
resolve
,
reject
)
=>
{
this
.
$refs
[
'form'
].
validate
((
valid
)
=>
{
if
(
valid
)
{
let
params
=
{}
params
=
{
...
this
.
form
}
if
(
this
.
isEdit
)
{
ModelDeployment
.
update
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
(
res
)
this
.
$message
.
success
(
'编辑成功'
)
this
.
onCancel
(
true
)
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
}
else
{
ModelDeployment
.
add
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
(
res
)
this
.
$message
.
success
(
'添加成功'
)
this
.
onCancel
(
true
)
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
}
}
else
{
// reject();
}
})
})
},
getModelList
()
{
ModelDeployment
.
listForTree
(
this
,
{})
.
then
((
res
)
=>
{
this
.
modelList
=
res
.
data
.
filter
((
item
)
=>
item
.
isBaseModel
===
1
)
.
map
((
item
)
=>
{
return
{
id
:
item
.
modelId
,
name
:
item
.
modelName
,
children
:
// prettier-ignore
item
.
modelVersionList
===
[]
?
[]
:
item
.
modelVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
'V'
+
item2
.
modelVersion
}
})
}
})
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
}
}
}
</
script
>
<
style
scoped
>
.inputWidth
{
width
:
600px
;
}
.title
{
font-size
:
20px
;
margin-bottom
:
16px
;
}
.introduce
{
font-size
:
12px
;
color
:
#909399
;
}
</
style
>
src/views/gptTraining/modelManagement/modelDeployment/dialog/modelConfiguration/index.vue
0 → 100644
View file @
4fcaf270
<!-- 参数配置-->
<
template
>
<el-table
:data=
"list"
style=
"width: 100%"
@
selection-change=
"change"
>
<el-table-column
type=
"selection"
width=
"55"
></el-table-column>
<el-table-column
prop=
"gpu_id"
label=
"gpuID"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"gpu_name"
label=
"名称"
>
</el-table-column>
<el-table-column
prop=
"total_memory"
label=
"显存总大小(M)"
>
</el-table-column>
<el-table-column
prop=
"used_memory"
label=
"使用大小(M)"
>
</el-table-column>
<el-table-column
prop=
"free_memory"
label=
"剩余大小(M)"
>
</el-table-column>
<el-table-column
prop=
"gpu_util"
label=
"显卡利用率"
>
<template
slot-scope=
"scope"
>
{{
scope
.
row
.
gpu_util
}}
%
</
template
>
</el-table-column>
</el-table>
</template>
<
script
>
import
{
ModelDeployment
}
from
'@/api/gptController.js'
export
default
{
data
()
{
return
{
list
:
[],
configData
:
undefined
}
},
props
:
[
'existingTask'
,
'value'
],
components
:
{},
computed
:
{},
mounted
()
{
this
.
getGpuInfo
()
},
methods
:
{
change
(
data
)
{
console
.
log
(
data
)
this
.
$emit
(
'input'
,
JSON
.
stringify
(
data
))
},
isJSON
(
str
)
{
try
{
return
JSON
.
parse
(
str
)
}
catch
(
e
)
{
// 转换出错,抛出异常
return
false
}
},
getGpuInfo
()
{
ModelDeployment
.
getGpuInfo
(
this
,
{})
.
then
((
res
)
=>
{
this
.
list
=
this
.
isJSON
(
res
.
data
).
gpu_list
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
}
}
}
</
script
>
<
style
>
div
/
deep
/
.el-cascader-menu__wrap
{
height
:
auto
;
}
</
style
>
src/views/gptTraining/modelManagement/modelDeployment/index.vue
0 → 100644
View file @
4fcaf270
<!-- 模型部署 -->
<
template
>
<div
style=
"position: relative"
>
<div
class=
"topBox"
>
<div
class=
"title"
>
模型部署
</div>
<div
class=
"instructions"
></div>
</div>
<div
class=
"tableBox"
:style=
"
{ height: tableHeight }">
<el-form
ref=
"myDataSetPage"
:model=
"myDataSetPage"
label-width=
"75px"
:size=
"defaultFormItemSize"
label-position=
"right"
@
submit
.
native
.
prevent
>
<filter-box
:item-width=
"350"
@
search=
"refresh()"
@
reset=
"onReset"
>
<el-form-item
label-width=
"0px"
>
<el-button
class=
"add"
type=
"primary"
icon=
"el-icon-plus"
:size=
"defaultFormItemSize"
@
click=
"add()"
>
部署模型
</el-button>
</el-form-item>
<el-form-item
label-width=
"0px"
>
</el-form-item>
<el-form-item
label=
"服务名称"
prop=
"formFilter.serviceName"
label-width=
"120px"
>
<el-input
class=
"filter-item"
v-model=
"myDataSetPage.formFilter.serviceName"
:clearable=
"true"
placeholder=
"服务名称"
/>
</el-form-item>
</filter-box>
</el-form>
<vxe-table
border
show-header-overflow
show-overflow
:row-config=
"
{ isHover: true }" :data="myDataSetPage.tableData.impl.dataList" min-height="96">
<vxe-column
field=
"serviceName"
title=
"服务名称"
></vxe-column>
<vxe-column
field=
"versionName"
title=
"模型版本"
></vxe-column>
<vxe-column
field=
"deployStatus"
title=
"部署状态"
>
<template
slot-scope=
"scope"
>
{{
DeploymentStatus
.
getValue
(
scope
.
row
.
deployStatus
)
}}
</
template
>
</vxe-column>
<vxe-column
field=
"createTime"
title=
"创建时间"
></vxe-column>
<vxe-column
field=
"updateTime"
title=
"更新时间"
></vxe-column>
<vxe-column
field=
"operation"
title=
"操作"
>
<
template
slot-scope=
"scope"
>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"particulars(scope.row)"
>
详情
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"stop(scope.row)"
>
卸载
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"deploy(scope.row,'start')"
v-if=
"scope.row.deployStatus==-1||scope.row.deployStatus==2||scope.row.deployStatus==3"
>
启动
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"deploy(scope.row,'reload')"
>
重载
</el-button>
<el-button
type=
"text"
:size=
"defaultFormItemSize"
@
click=
"del(scope.row)"
>
删除
</el-button>
</
template
>
</vxe-column>
</vxe-table>
<el-row
slot=
"pagination"
type=
"flex"
justify=
"end"
style=
"margin-top: 16px"
>
<el-pagination
:total=
"myDataSetPage.tableData.impl.totalCount"
:current-page=
"myDataSetPage.tableData.impl.currentPage"
:page-size=
"myDataSetPage.tableData.impl.pageSize"
:page-sizes=
"[10, 20, 50, 100]"
layout=
"total, prev, pager, next, sizes"
@
current-change=
"myDataSetPage.tableData.impl.onCurrentPageChange"
@
size-change=
"myDataSetPage.tableData.impl.onPageSizeChange"
>
</el-pagination>
</el-row>
</div>
</div>
</template>
<
script
>
import
{
mapGetters
}
from
'vuex'
/* eslint-disable-next-line */
import
{
TableWidget
}
from
'@/utils/widget.js'
import
{
ModelDeployment
}
from
'@/api/gptController.js'
import
editOrAdd
from
'./dialog/editOrAdd'
import
particulars
from
'./particulars/index'
export
default
{
data
()
{
return
{
myDataSetPage
:
{
formFilter
:
{
serviceName
:
''
,
isBaseModel
:
0
},
tableData
:
{
impl
:
new
TableWidget
(
this
.
getwidgetData
,
true
,
true
,
false
,
undefined
,
false
)
}
}
}
},
components
:
{},
computed
:
{
...
mapGetters
([
'getMainContextHeight'
]),
tableHeight
()
{
return
this
.
getMainContextHeight
-
145
+
'px'
}
},
methods
:
{
getwidgetData
(
params
)
{
if
(
params
==
null
)
params
=
{}
params
=
{
modelDeployDtoFilter
:
{
...
this
.
myDataSetPage
.
formFilter
},
...
params
,
// orderParam: [
// {
// asc: true,
// dateAggregateBy: '',
// fieldName: ''
// }
// ],
// pageParam: {
// pageNum: 0,
// pageSize: 0
// },
promptTemplateDtoFilter
:
{
...
this
.
myDataSetPage
.
formFilter
}
}
return
new
Promise
((
resolve
,
reject
)
=>
{
ModelDeployment
.
list
(
this
,
params
)
.
then
((
res
)
=>
{
resolve
({
dataList
:
res
.
data
.
dataList
,
totalCount
:
res
.
data
.
totalCount
})
})
.
catch
((
e
)
=>
{
reject
(
e
)
})
})
},
add
()
{
this
.
$dialog
.
show
(
'创建服务'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
false
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
edit
(
item
)
{
this
.
$dialog
.
show
(
'修改服务'
,
editOrAdd
,
{
area
:
[
'100%'
,
'100%'
]
},
{
isEdit
:
true
,
item
:
item
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
del
(
item
)
{
this
.
$confirm
(
'是否确认删除'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
let
params
=
{
deployId
:
item
.
deployId
}
ModelDeployment
.
delete
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
'删除成功'
)
this
.
refresh
()
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
})
},
particulars
(
item
)
{
this
.
$dialog
.
show
(
'详情'
,
particulars
,
{
area
:
[
'100%'
,
'100%'
]
},
{
tableItem
:
item
}
)
.
then
((
res
)
=>
{
this
.
refresh
()
})
.
catch
((
e
)
=>
{})
},
deploy
(
item
,
type
)
{
// 部署
this
.
$confirm
(
'是否确认部署'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
let
params
=
{
modelDeployDto
:
{
deployId
:
item
.
deployId
},
type
}
ModelDeployment
.
deploy
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
'部署成功'
)
this
.
refresh
()
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
})
},
stop
(
item
)
{
// 卸载
this
.
$confirm
(
'是否确认卸载'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
let
params
=
{
modelDeployDto
:
{
deployId
:
item
.
deployId
}
}
ModelDeployment
.
stop
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
'卸载成功'
)
this
.
refresh
()
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
})
},
refresh
(
reloadData
=
false
)
{
if
(
reloadData
)
{
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
(
true
,
1
)
}
else
{
this
.
myDataSetPage
.
tableData
.
impl
.
refreshTable
()
}
},
onReset
()
{
this
.
$refs
.
myDataSetPage
.
resetFields
()
this
.
refresh
(
true
)
},
formInit
()
{
this
.
refresh
()
}
},
mounted
()
{
this
.
formInit
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
'@/assets/style/element-variables.scss'
;
.topBox
{
background-color
:
white
;
width
:
100%
;
height
:
95px
;
margin-bottom
:
16px
;
.title
{
color
:
$--color-text-primary
;
font-size
:
16px
;
font-weight
:
500
;
margin-bottom
:
8px
;
padding
:
20px
20px
10px
20px
;
}
}
.instructions
{
padding
:
0px
20px
20px
20px
;
.describe
{
color
:
$--color-text-secondary
;
margin-bottom
:
16px
;
}
.itemTitle
{
color
:
$--color-text-primary
;
font-size
:
16px
;
font-weight
:
500
;
margin-bottom
:
8px
;
}
.head-index
{
font-size
:
24px
;
color
:
#e8e9eb
;
line-height
:
32px
;
}
.itemDescribe
{
width
:
170px
;
color
:
$--color-text-secondary
;
}
.img
{
height
:
72px
;
width
:
140px
;
}
}
.tableBox
{
background-color
:
white
;
width
:
100%
;
padding
:
20px
;
overflow
:
auto
;
.add
{
margin-bottom
:
20px
;
}
}
</
style
>
src/views/gptTraining/modelManagement/modelDeployment/particulars/index.vue
0 → 100644
View file @
4fcaf270
<!-- 详情 -->
<
template
>
<div
style=
"position: relative"
>
<el-form
label-position=
"left"
ref=
"form"
label-width=
"100px"
:size=
"defaultFormItemSize"
>
<el-row
class=
"title"
>
基本信息
</el-row>
<el-form-item
label=
"服务名称:"
prop=
"modelManageDto.serviceName"
>
{{
tableItem
.
serviceName
}}
</el-form-item>
<el-form-item
label=
"服务描述:"
>
{{
tableItem
.
serviceDescription
}}
</el-form-item>
<el-form-item
label=
"选择模型:"
>
{{
tableItem
.
modelName
}}
</el-form-item>
<el-form-item
label=
"API地址:"
>
{{
tableItem
.
apiUrl
}}
</el-form-item>
<el-row
class=
"title"
>
模型配置
</el-row>
<el-form-item
label=
"资源配置:"
>
<el-table
:data=
"getResourceInfoList"
style=
"width: 100%"
>
<el-table-column
prop=
"gpu_id"
label=
"gpuID"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"gpu_name"
label=
"名称"
>
</el-table-column>
<el-table-column
prop=
"total_memory"
label=
"显存总大小(M)"
>
</el-table-column>
<el-table-column
prop=
"used_memory"
label=
"使用大小(M)"
>
</el-table-column>
<el-table-column
prop=
"free_memory"
label=
"剩余大小(M)"
>
</el-table-column>
<el-table-column
prop=
"gpu_util"
label=
"显卡利用率"
>
<template
slot-scope=
"scope"
>
{{
scope
.
row
.
gpu_util
}}
%
</
template
>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
</div>
</template>
<
script
>
import
{
mapGetters
}
from
'vuex'
export
default
{
data
()
{
return
{}
},
components
:
{},
props
:
[
'tableItem'
],
computed
:
{
...
mapGetters
([
'getMainContextHeight'
]),
tableHeight
()
{
return
this
.
getMainContextHeight
-
95
+
'px'
},
getResourceInfoList
()
{
return
JSON
.
parse
(
this
.
tableItem
.
resourceInfo
)
}
},
methods
:
{},
mounted
()
{}
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
'@/assets/style/element-variables.scss'
;
.topBox
{
background-color
:
white
;
width
:
100%
;
height
:
95px
;
margin-bottom
:
16px
;
.title
{
color
:
$--color-text-primary
;
font-size
:
16px
;
font-weight
:
500
;
margin-bottom
:
8px
;
padding
:
20px
20px
10px
20px
;
}
}
.instructions
{
padding
:
0px
20px
20px
20px
;
.describe
{
color
:
$--color-text-secondary
;
margin-bottom
:
16px
;
}
.itemTitle
{
color
:
$--color-text-primary
;
font-size
:
16px
;
font-weight
:
500
;
margin-bottom
:
8px
;
}
.head-index
{
font-size
:
24px
;
color
:
#e8e9eb
;
line-height
:
32px
;
}
.itemDescribe
{
width
:
170px
;
color
:
$--color-text-secondary
;
}
.img
{
height
:
72px
;
width
:
140px
;
}
}
.tableBox
{
background-color
:
white
;
width
:
100%
;
padding
:
20px
;
overflow
:
auto
;
.add
{
margin-bottom
:
20px
;
}
}
/
deep
/
.el-descriptions__table
{
width
:
100%
;
}
</
style
>
src/views/gptTraining/modelManagement/modelEvaluation/parameterConfiguration/index.vue
View file @
4fcaf270
<!-- 参数配置-->
<
template
>
<el-table
:data=
"existingTasklist"
style=
"width: 100%"
>
<el-table-column
prop=
"hyperParameter"
label=
"超参数"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"numericalValue"
label=
"数值"
>
<template
slot-scope=
"scope"
>
<component
v-if=
"configData"
:ref=
"scope.row.numericalValue"
:is=
"scope.row.componentOptions.component"
style=
"width: 100%"
v-bind=
"scope.row.componentOptions.property"
:value=
"configData[scope.row.numericalValue]||scope.row.defaultValue"
:size=
"defaultFormItemSize"
@
input=
" e =>
{changeValue(e, scope.row.numericalValue);}">
</component>
</
template
>
</el-table-column>
<el-table-column
prop=
"explain"
label=
"说明"
>
</el-table-column>
</el-table>
<el-table
:data=
"existingTasklist"
style=
"width: 100%"
>
<el-table-column
prop=
"hyperParameter"
label=
"超参数"
width=
"150"
>
</el-table-column>
<el-table-column
prop=
"numericalValue"
label=
"数值"
>
<template
slot-scope=
"scope"
>
<component
v-if=
"configData"
:ref=
"scope.row.numericalValue"
:is=
"scope.row.componentOptions.component"
style=
"width: 100%"
v-bind=
"scope.row.componentOptions.property"
:value=
"configData[scope.row.numericalValue]||scope.row.defaultValue"
:size=
"defaultFormItemSize"
@
input=
" e =>
{changeValue(e, scope.row.numericalValue);}">
</component>
</
template
>
</el-table-column>
<el-table-column
prop=
"explain"
label=
"说明"
>
</el-table-column>
</el-table>
</template>
<
script
>
export
default
{
data
()
{
data
()
{
return
{
configData
:
undefined
,
parameterConfiguration
:
[
...
...
@@ -34,20 +26,23 @@ export default {
explain
:
'启用 4/8 比特模型量化(QLoRA)。'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[{
value
:
'none'
,
label
:
'none'
},
{
value
:
'8'
,
label
:
'8'
},
{
value
:
'4'
,
label
:
'4'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'none'
,
label
:
'none'
},
{
value
:
'8'
,
label
:
'8'
},
{
value
:
'4'
,
label
:
'4'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
...
...
@@ -57,87 +52,111 @@ export default {
explain
:
'构建提示词时使用的模板'
,
componentOptions
:
{
component
:
'el-cascader'
,
property
:
{
options
:
[
{
value
:
'alpaca'
,
label
:
'alpaca'
},
{
value
:
'aquila'
,
label
:
'aquila'
},
{
value
:
'baichuan'
,
label
:
'baichuan'
},
{
value
:
'baichuan2'
,
label
:
'baichuan2'
},
{
value
:
'belle'
,
label
:
'belle'
},
{
value
:
'bluelm'
,
label
:
'bluelm'
},
{
value
:
'chatglm2'
,
label
:
'chatglm2'
},
{
value
:
'chatglm3'
,
label
:
'chatglm3'
},
{
value
:
'chatglm3_raw'
,
label
:
'chatglm3_raw'
},
{
value
:
'deepseek'
,
label
:
'deepseek'
},
{
value
:
'default'
,
label
:
'default'
},
{
value
:
'falcon'
,
label
:
'falcon'
},
{
value
:
'intern'
,
label
:
'intern'
},
{
value
:
'llama2'
,
label
:
'llama2'
},
{
value
:
'llama2_zh'
,
label
:
'llama2_zh'
},
{
value
:
'mistral'
,
label
:
'mistral'
},
{
value
:
'openchat'
,
label
:
'openchat'
},
{
value
:
'qwen'
,
label
:
'qwen'
},
{
value
:
'starchat'
,
label
:
'starchat'
},
{
value
:
'vanilla'
,
label
:
'vanilla'
},
{
value
:
'vicuna'
,
label
:
'vicuna'
},
{
value
:
'xverse'
,
label
:
'xverse'
},
{
value
:
'yayi'
,
label
:
'yayi'
},
{
value
:
'zephyr'
,
label
:
'zephyr'
},
{
value
:
'ziya'
,
label
:
'ziya'
}],
props
:
{
emitPath
:
false
}
}
property
:
{
options
:
[
{
value
:
'alpaca'
,
label
:
'alpaca'
},
{
value
:
'aquila'
,
label
:
'aquila'
},
{
value
:
'baichuan'
,
label
:
'baichuan'
},
{
value
:
'baichuan2'
,
label
:
'baichuan2'
},
{
value
:
'belle'
,
label
:
'belle'
},
{
value
:
'bluelm'
,
label
:
'bluelm'
},
{
value
:
'chatglm2'
,
label
:
'chatglm2'
},
{
value
:
'chatglm3'
,
label
:
'chatglm3'
},
{
value
:
'chatglm3_raw'
,
label
:
'chatglm3_raw'
},
{
value
:
'deepseek'
,
label
:
'deepseek'
},
{
value
:
'default'
,
label
:
'default'
},
{
value
:
'falcon'
,
label
:
'falcon'
},
{
value
:
'intern'
,
label
:
'intern'
},
{
value
:
'llama2'
,
label
:
'llama2'
},
{
value
:
'llama2_zh'
,
label
:
'llama2_zh'
},
{
value
:
'mistral'
,
label
:
'mistral'
},
{
value
:
'openchat'
,
label
:
'openchat'
},
{
value
:
'qwen'
,
label
:
'qwen'
},
{
value
:
'starchat'
,
label
:
'starchat'
},
{
value
:
'vanilla'
,
label
:
'vanilla'
},
{
value
:
'vicuna'
,
label
:
'vicuna'
},
{
value
:
'xverse'
,
label
:
'xverse'
},
{
value
:
'yayi'
,
label
:
'yayi'
},
{
value
:
'zephyr'
,
label
:
'zephyr'
},
{
value
:
'ziya'
,
label
:
'ziya'
}
],
props
:
{
emitPath
:
false
}
}
}
},
{
...
...
@@ -147,12 +166,10 @@ export default {
explain
:
'输入序列分词后的最大长度。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
min
:
4
,
max
:
8192
}
property
:
{
min
:
4
,
max
:
8192
}
}
},
{
...
...
@@ -162,11 +179,10 @@ export default {
explain
:
'每个数据集最多使用的样本数'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
100000
,
min
:
0
}
property
:
{
max
:
100000
,
min
:
0
}
}
},
{
...
...
@@ -176,11 +192,10 @@ export default {
explain
:
'批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
512
,
min
:
1
}
property
:
{
max
:
512
,
min
:
1
}
}
},
{
...
...
@@ -190,11 +205,10 @@ export default {
explain
:
'最大生成长度'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
2048
,
min
:
10
}
property
:
{
max
:
2048
,
min
:
10
}
}
},
{
...
...
@@ -204,12 +218,11 @@ export default {
explain
:
'Top-p 采样值'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1
,
min
:
0.01
,
step
:
0.01
}
property
:
{
max
:
1
,
min
:
0.01
,
step
:
0.01
}
}
},
{
...
...
@@ -219,39 +232,34 @@ export default {
explain
:
'温度系数'
,
componentOptions
:
{
component
:
'el-input-number'
,
property
:
{
max
:
1.5
,
min
:
0.01
,
step
:
0.01
}
property
:
{
max
:
1.5
,
min
:
0.01
,
step
:
0.01
}
}
}
]
}
;
}
},
props
:
[
'existingTask'
,
'value'
],
components
:
{},
computed
:
{
existingTasklist
()
{
existingTasklist
()
{
return
this
.
parameterConfiguration
}
},
mounted
()
{
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
init
()
;
this
.
init
()
})
},
methods
:
{
getProperty
()
{
},
init
()
{
getProperty
()
{},
init
()
{
let
initConfig
=
{}
this
.
parameterConfiguration
.
forEach
((
item
)
=>
{
initConfig
[
item
.
numericalValue
]
=
item
.
defaultValue
...
...
@@ -261,28 +269,27 @@ export default {
}
else
{
this
.
configData
=
initConfig
}
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
;
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
},
isJSON
(
str
)
{
isJSON
(
str
)
{
try
{
JSON
.
parse
(
str
)
;
JSON
.
parse
(
str
)
}
catch
(
e
)
{
// 转换出错,抛出异常
return
false
;
return
false
}
return
true
;
return
true
},
changeValue
(
value
,
numericalValue
)
{
changeValue
(
value
,
numericalValue
)
{
this
.
configData
[
numericalValue
]
=
value
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
;
this
.
$emit
(
'input'
,
JSON
.
stringify
(
this
.
configData
))
}
}
}
</
script
>
<
style
>
div
/
deep
/
.el-cascader-menu__wrap
{
div
/
deep
/
.el-cascader-menu__wrap
{
height
:
auto
;
}
</
style
>
src/views/gptTraining/modelService/testOnline/components/leftBox/index.vue
View file @
4fcaf270
...
...
@@ -2,13 +2,19 @@
<
template
>
<el-form
label-position=
"left"
ref=
"form"
label-width=
"120px"
:model=
"form"
:size=
"defaultFormItemSize"
style=
"padding:20px;width:400px"
v-loading
.
fullscreen
.
lock=
"fullscreenLoading"
>
<el-form-item
label=
"问答模式:"
>
<el-select
v-model=
"form.pattern"
placeholder=
"请选择"
@
change=
"patternChange"
>
<el-select
v-model=
"form.pattern"
placeholder=
"请选择"
@
change=
"patternChange"
>
<el-option
v-for=
"item in ModeOfSpeaking.getList()"
:key=
"item.id"
:label=
"item.name"
:value=
"item.name"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label=
"模型选择:"
>
<
!--
<
el-form-item
label=
"模型选择:"
>
<el-cascader
v-model=
"form.model_name"
ref=
"modelCascader"
:options=
"modelList"
:props=
'
{ label: "name", value: "name", emitPath: false }' @change="changeModel">
</el-cascader>
</el-form-item>
-->
<el-form-item
label=
"服务选择:"
>
<el-select
ref=
"serveCascader"
v-model=
"form.model_name"
placeholder=
"请选择"
>
<el-option
v-for=
"item in serveList"
:key=
"item.createUserId"
:label=
"item.serviceName"
:value=
"item.versionName"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label=
"Temperature:"
>
<el-slider
v-model=
"temperature"
:format-tooltip=
"formatTooltip"
@
change=
"form.temperature = temperature / 100"
></el-slider>
...
...
@@ -41,7 +47,7 @@
</el-option>
</el-select>
<div
class=
"el-upload__tip"
>
{{knowledgeDescribe}}
</div>
<div
class=
"el-upload__tip"
>
{{knowledgeDescribe}}
</div>
</el-form-item>
<el-form-item
label=
"匹配知识条数:"
style=
"margin-bottom:30px"
>
...
...
@@ -69,19 +75,19 @@
</el-collapse-item>
</el-collapse>
</el-form-item>
<el-form-item
label-width=
"0px"
v-if=
"form.pattern == '
文件对话
'"
>
<el-form-item
label-width=
"0px"
v-if=
"form.pattern == '
基于文件问答
'"
>
<el-collapse
value=
"1"
>
<el-collapse-item
name=
"1"
>
<
template
slot=
"title"
>
文件配置
</
template
>
<el-form-item
label=
"上传文件:"
style=
"margin-bottom:30px"
>
<el-button
@
click=
"clickUp"
:size=
"defaultFormItemSize"
type=
"primary"
>
选择文件
</el-button>
<input
style=
"display:none"
ref=
"upFile"
type=
"file"
@
change=
"fileinfo($event.target.files)"
multiple
accept=
".html, .md, .json, .jsonl, .csv, .pdf, .png, .jpg, .jpeg, .bmp, .eml, .msg, .epub, .xlsx, .xls, .ipynb, .odt, .py, .rst, .rtf, .srt, .toml, .tsv, .docx, .doc, .xml, .ppt, .pptx, .txt, .htm"
>
<div
class=
"el-upload__tip"
>
可上传HTML, MD, JSON, JSONL, CSV, PDF, PNG, JPG, JPEG, BMP, EML, MSG, EPUB, XLSX, XLSD, IPYNB, ODT, PY, RST, RTF, SRT, TOML, TSV, DOCX, DOC, XML, PPT, PPTX, TXT, HTM文件
</div>
<div
class=
"itemFile"
v-for=
"(item,index) in files"
:key=
"item.name"
><i
class=
"el-icon-document"
style=
"color:#909399;margin-right:5px"
></i>
{{ item.name }}
<input
style=
"display:none"
ref=
"upFile"
type=
"file"
@
change=
"fileinfo($event.target.files)"
multiple
accept=
".html, .md, .json, .jsonl, .csv, .pdf, .png, .jpg, .jpeg, .bmp, .eml, .msg, .epub, .xlsx, .xls, .ipynb, .odt, .py, .rst, .rtf, .srt, .toml, .tsv, .docx, .doc, .xml, .ppt, .pptx, .txt, .htm"
>
<div
class=
"el-upload__tip"
>
可上传HTML, MD, JSON, JSONL, CSV, PDF, PNG, JPG, JPEG, BMP, EML, MSG, EPUB, XLSX, XLSD, IPYNB, ODT, PY, RST, RTF, SRT, TOML, TSV, DOCX, DOC, XML, PPT, PPTX, TXT, HTM文件
</div>
<div
class=
"itemFile"
v-for=
"(item,index) in files"
:key=
"item.name"
><i
class=
"el-icon-document"
style=
"color:#909399;margin-right:5px"
></i>
{{ item.name }}
<i
class=
"el-icon-circle-close"
style=
"color:#0092FF;cursor: pointer;"
@
click=
"clearFile(index)"
></i>
</div>
<el-button
style=
"display:block;margin-top:10px"
:size=
"defaultFormItemSize"
type=
"primary"
@
click=
"uploadFiles"
:disabled=
"files.length===0"
>
上传
</el-button>
<el-button
style=
"display:block;margin-top:10px"
:size=
"defaultFormItemSize"
type=
"primary"
@
click=
"uploadFiles"
:disabled=
"files.length===0"
>
上传
</el-button>
</el-form-item>
<el-form-item
label=
"匹配知识条数:"
style=
"margin-bottom:30px"
>
<el-input-number
v-model=
"form.fileConfige.top_k"
:min=
"1"
:max=
"20"
></el-input-number>
...
...
@@ -97,7 +103,7 @@
<el-collapse-item name="1">
<template slot="title">知识图谱配置</template>
<el-form-item label="请选择知识图谱:" style="margin-bottom:30px">
<el-select ref="knowledgeSelect" v-model="form.knowledgeGraphConfige. knowledge_graph_name" placeholder="请选择"
>
<el-select ref="knowledgeSelect" v-model="form.knowledgeGraphConfige. knowledge_graph_name" placeholder="请选择">
<el-option v-for="item in []" :key="item.knowledgeId" :label="item.knowledgeGraphName" :value="item.knowledgeGraphName">
</el-option>
</el-select>
...
...
@@ -116,10 +122,10 @@
</template>
<
script
>
import
{
KnowledgeManage
,
MyModel
,
ModelVersion
,
TemplateController
}
from
'@/api/gptController.js'
;
import
promptWordTemplate
from
'../promptWordTemplate'
;
import
{
KnowledgeManage
,
MyModel
,
ModelVersion
,
TemplateController
,
ModelDeployment
}
from
'@/api/gptController.js'
import
promptWordTemplate
from
'../promptWordTemplate'
export
default
{
data
()
{
data
()
{
return
{
knowledgeDescribe
:
''
,
files
:
[],
...
...
@@ -128,6 +134,7 @@ export default {
promptTemplate
:
''
,
templateControllerList
:
[],
modelList
:
[],
serveList
:
[],
activeModelList
:
[],
loading
:
undefined
,
temperature
:
70
,
...
...
@@ -141,28 +148,30 @@ export default {
temperature
:
0.7
,
prompt_template
:
''
,
heistoryRotate
:
10
,
// 历史对话轮数
knowledgeConfige
:
{
// 知识库配置
knowledgeConfige
:
{
// 知识库配置
knowledge_base_name
:
''
,
top_k
:
1
,
score_threshold
:
0.5
},
searchConfige
:
{
// 搜索引擎配置
searchConfige
:
{
// 搜索引擎配置
search_engine_name
:
'bing'
,
top_k
:
1
},
fileConfige
:
{
// 文件对哈配置
fileConfige
:
{
// 文件对哈配置
top_k
:
1
,
score_threshold
:
0.5
,
knowledge_id
:
undefined
,
// max_tokens: 0,
prompt_name
:
'default'
},
knowledgeGraphConfige
:
{
// 知识图谱对话
knowledgeGraphConfige
:
{
// 知识图谱对话
knowledge_graph_name
:
''
,
top_k
:
1
,
score_threshold
:
0.5
}
},
filesForm
:
{
...
...
@@ -171,177 +180,216 @@ export default {
chunk_overlap
:
50
,
zh_title_enhance
:
false
}
}
;
}
},
components
:
{},
computed
:
{
},
computed
:
{},
watch
:
{
form
:
{
handler
(
newVal
,
oldVal
)
{
handler
(
newVal
,
oldVal
)
{
this
.
$emit
(
'chatForm'
,
this
.
form
)
},
deep
:
true
,
// 是否深度监听
immediate
:
true
// 是否在组件创建时立即执行回调函数
}
},
mounted
()
{
mounted
()
{
this
.
getKnowledgeList
()
this
.
getModelList
()
//
this.getModelList()
this
.
getTemplateControllerList
()
this
.
getServeList
()
},
methods
:
{
getTemplateControllerList
()
{
TemplateController
.
listForTree
(
this
,
{}).
then
(
res
=>
{
this
.
templateControllerList
=
res
.
data
.
map
((
item
)
=>
{
return
{
templateId
:
item
.
templateId
,
label
:
item
.
templateName
,
value
:
item
.
templateContent
,
parameterFormat
:
item
.
parameterFormat
}
getTemplateControllerList
()
{
TemplateController
.
listForTree
(
this
,
{})
.
then
((
res
)
=>
{
this
.
templateControllerList
=
res
.
data
.
map
((
item
)
=>
{
return
{
templateId
:
item
.
templateId
,
label
:
item
.
templateName
,
value
:
item
.
templateContent
,
parameterFormat
:
item
.
parameterFormat
}
})
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
},
getModelList
()
{
MyModel
.
listForTree
(
this
,
{}).
then
(
res
=>
{
this
.
modelList
=
res
.
data
.
map
((
item
)
=>
{
return
{
id
:
item
.
modelId
,
name
:
item
.
modelName
,
children
:
item
.
modelVersionList
===
[]
?
[]
:
item
.
modelVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
item2
.
versionName
}
})
}
getServeList
()
{
ModelDeployment
.
canUseList
(
this
,
{})
.
then
((
res
)
=>
{
console
.
log
(
res
)
this
.
serveList
=
res
?.
data
?.
dataList
this
.
form
.
model_name
=
this
.
serveList
[
0
].
versionName
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
},
getModelList
()
{
MyModel
.
listForTree
(
this
,
{})
.
then
((
res
)
=>
{
this
.
modelList
=
res
.
data
.
map
((
item
)
=>
{
return
{
id
:
item
.
modelId
,
name
:
item
.
modelName
,
// prettier-ignore
children
:
item
.
modelVersionList
===
[]
?
[]
:
item
.
modelVersionList
.
map
((
item2
)
=>
{
return
{
id
:
item2
.
versionId
,
name
:
item2
.
versionName
}
})
}
})
this
.
getActiveModelList
()
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
this
.
getActiveModelList
()
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
},
getKnowledgeList
()
{
getKnowledgeList
()
{
let
params
=
{}
KnowledgeManage
.
load
(
this
,
params
).
then
(
res
=>
{
this
.
knowledgeList
=
res
.
data
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
KnowledgeManage
.
load
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
knowledgeList
=
res
.
data
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
},
getActiveModelList
()
{
// 获取当前挂载上的模型列表
getActiveModelList
()
{
// 获取当前挂载上的模型列表
let
params
=
{}
ModelVersion
.
listModels
(
this
,
params
).
then
(
res
=>
{
this
.
activeModelList
=
res
.
data
if
(
this
.
activeModelList
.
length
===
0
)
{
this
.
form
.
model_name
=
this
.
modelList
[
0
].
children
[
0
].
name
this
.
switchModel
(
this
.
modelList
[
0
].
children
[
0
].
id
)
}
else
{
this
.
form
.
model_name
=
this
.
activeModelList
[
0
]
}
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
ModelVersion
.
listModels
(
this
,
params
)
.
then
((
res
)
=>
{
this
.
activeModelList
=
res
.
data
if
(
this
.
activeModelList
.
length
===
0
)
{
this
.
form
.
model_name
=
this
.
modelList
[
0
].
children
[
0
].
name
this
.
switchModel
(
this
.
modelList
[
0
].
children
[
0
].
id
)
}
else
{
this
.
form
.
model_name
=
this
.
activeModelList
[
0
]
}
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
},
openLoading
(
text
)
{
openLoading
(
text
)
{
this
.
loading
=
this
.
$loading
({
lock
:
true
,
text
:
text
,
customClass
:
'myLoading'
,
spinner
:
'el-icon-loading'
,
background
:
'rgba(255, 255, 255, 0.9)'
})
;
})
// loading.close();
},
formatTooltip
(
val
)
{
return
val
/
100
;
formatTooltip
(
val
)
{
return
val
/
100
},
switchModel
(
versionId
)
{
// 切换模型
switchModel
(
versionId
)
{
// 切换模型
this
.
openLoading
(
'LLM模型加载中'
)
ModelVersion
.
change
(
this
,
{
versionId
:
versionId
}).
then
(
res
=>
{
this
.
loading
.
close
();
}).
catch
(
e
=>
{
this
.
loading
.
close
();
console
.
log
(
e
);
});
ModelVersion
.
change
(
this
,
{
versionId
:
versionId
})
.
then
((
res
)
=>
{
this
.
loading
.
close
()
})
.
catch
((
e
)
=>
{
this
.
loading
.
close
()
console
.
log
(
e
)
})
},
changeModel
(
data
)
{
// 修改模型
changeModel
(
data
)
{
// 修改模型
// this.openLoading('LLM模型加载中')
let
id
=
this
.
$refs
.
modelCascader
.
getCheckedNodes
()[
0
].
data
.
id
this
.
switchModel
(
id
)
},
changeKnowledge
(
data
)
{
// 修改知识库
changeKnowledge
(
data
)
{
// 修改知识库
// this.openLoading('知识库加载中')
this
.
knowledgeDescribe
=
this
.
knowledgeList
.
filter
((
item
)
=>
{
return
item
.
knowledgeCode
===
data
})[
0
].
knowledgeDescribe
},
changeSe
(
data
)
{
// 修改模型引擎
changeSe
(
data
)
{
// 修改模型引擎
// this.openLoading('模型引擎加载中')
// console.log(this.$refs.searchSelect);
},
templateControllerChange
(
data
)
{
templateControllerChange
(
data
)
{
if
(
!
data
)
{
this
.
form
.
prompt_template
=
''
return
}
this
.
$dialog
.
show
(
data
.
label
,
promptWordTemplate
,
{
area
:
[
'50%'
,
'40%'
]
},
{
data
:
data
}).
then
(
res
=>
{
this
.
form
.
prompt_template
=
res
}).
catch
(
e
=>
{
this
.
promptTemplate
=
undefined
});
this
.
$dialog
.
show
(
data
.
label
,
promptWordTemplate
,
{
area
:
[
'50%'
,
'40%'
]
},
{
data
:
data
}
)
.
then
((
res
)
=>
{
this
.
form
.
prompt_template
=
res
})
.
catch
((
e
)
=>
{
this
.
promptTemplate
=
undefined
})
},
changePromptTemplate
()
{
this
.
$bus
.
$emit
(
'isPromptTemplate'
,
this
.
isPromptTemplate
)
;
changePromptTemplate
()
{
this
.
$bus
.
$emit
(
'isPromptTemplate'
,
this
.
isPromptTemplate
)
},
fileinfo
(
files
)
{
fileinfo
(
files
)
{
for
(
const
key
in
files
)
{
if
(
Object
.
hasOwnProperty
.
call
(
files
,
key
))
{
const
element
=
files
[
key
]
;
const
element
=
files
[
key
]
if
(
this
.
files
.
map
((
item
)
=>
item
.
name
).
indexOf
(
element
.
name
)
===
-
1
)
{
this
.
files
.
push
(
element
)
this
.
filesForm
.
filesArr
=
this
.
files
}
else
{
console
.
log
(
'重复上传'
)
;
console
.
log
(
'重复上传'
)
}
}
}
},
clickUp
()
{
clickUp
()
{
this
.
$refs
.
upFile
.
click
()
},
clearFile
(
index
)
{
clearFile
(
index
)
{
this
.
files
.
splice
(
index
,
1
)
},
uploadFiles
()
{
uploadFiles
()
{
this
.
fullscreenLoading
=
true
let
params
=
this
.
filesForm
this
.
upload
(
'/2api/knowledge_base/upload_temp_docs'
,
params
,
false
).
then
(
res
=>
{
this
.
$message
.
success
(
'上传成功'
);
this
.
fullscreenLoading
=
false
;
this
.
form
.
fileConfige
.
knowledge_id
=
res
.
data
.
id
}).
catch
(
e
=>
{
console
.
log
(
e
);
});
this
.
upload
(
'/2api/knowledge_base/upload_temp_docs'
,
params
,
false
)
.
then
((
res
)
=>
{
this
.
$message
.
success
(
'上传成功'
)
this
.
fullscreenLoading
=
false
this
.
form
.
fileConfige
.
knowledge_id
=
res
.
data
.
id
})
.
catch
((
e
)
=>
{
console
.
log
(
e
)
})
},
patternChange
()
{
patternChange
()
{
// console.log(111);
// Object.assign(this.$data.form, this.$options.data().form);
}
}
}
</
script
>
<
style
scoped
>
.myLoading
.el-loading-spinner
i
{
font-size
:
24px
!important
;
}
.el-upload__tip
{
.el-upload__tip
{
line-height
:
normal
;
font-size
:
12px
!important
;
}
...
...
src/views/gptTraining/modelService/testOnline/components/rightBox/index.vue
View file @
4fcaf270
<!-- 右侧聊天界面 -->
<
template
>
<div
class=
"box"
ref=
"box"
>
<div
class=
"contentBox"
ref=
"contentBox"
v-if=
"myHistory.length > 0"
>
<div
v-for=
"item in myHistory"
:key=
"item.id"
>
<div
class=
"box"
ref=
"box"
>
<div
class=
"contentBox"
ref=
"contentBox"
v-if=
"myHistory.length > 0"
>
<div
v-for=
"item in myHistory"
:key=
"item.id"
>
<div
class=
"userBox"
>
<div
class=
"content"
>
<contentView
:content=
"item.content"
/>
...
...
@@ -15,19 +11,15 @@
{{
item
.
role
}}
</div>
</div>
<div
v-if=
"item.answer"
class=
"gptBox"
>
<div
v-if=
"item.answer"
class=
"gptBox"
>
<div
class=
"icon"
>
GPT
</div>
<div
class=
"content"
>
<contentView
:content=
"item.answer"
:ref=
"'contentView' + item.id"
/>
<contentView
:content=
"item.answer"
:ref=
"'contentView' + item.id"
/>
</div>
</div>
</div>
</div>
<div
class=
"initBox"
ref=
"initBox"
v-else
>
<div
class=
"initBox"
ref=
"initBox"
v-else
>
<div
class=
"text"
>
你好,
<br
/>
我是人工智能语言模型
<br
/>
...
...
@@ -35,32 +27,22 @@
</div>
</div>
<div
class=
"inputBox"
>
<el-input
placeholder=
"请输入内容"
v-model=
"inputContent"
class=
"input-with-select"
@
keyup
.
enter
.
native=
"submit"
>
<el-button
slot=
"append"
icon=
"el-icon-position"
@
click=
"submit"
></el-button>
<el-input
placeholder=
"请输入内容"
v-model=
"inputContent"
class=
"input-with-select"
@
keyup
.
enter
.
native=
"submit"
>
<el-button
slot=
"append"
icon=
"el-icon-position"
@
click=
"submit"
></el-button>
</el-input>
<el-button
icon=
"el-icon-delete"
style=
"margin-left: 10px"
@
click=
"clear"
type=
"danger"
plain
></el-button>
<el-button
icon=
"el-icon-delete"
style=
"margin-left: 10px"
@
click=
"clear"
type=
"danger"
plain
></el-button>
</div>
<div
id=
"network"
></div>
</div>
</
template
>
<
script
>
import
{
GetStreaming
}
from
'@/utils/getStreaming.js'
;
import
{
GetStreaming
}
from
'@/utils/getStreaming.js'
// import codePreview from '../codePreview';
import
contentView
from
'../contentView'
;
import
contentView
from
'../contentView'
export
default
{
data
()
{
data
()
{
return
{
templateController
:
undefined
,
isPromptTemplate
:
true
,
...
...
@@ -71,38 +53,35 @@ export default {
query
:
null
,
history
:
[],
prompt_template
:
''
,
'stream'
:
true
,
'model_name'
:
''
,
'temperature'
:
0.7
stream
:
true
,
model_name
:
''
,
temperature
:
0.7
}
}
;
}
},
props
:
[
'chatForm'
],
components
:
{
contentView
},
computed
:
{
},
computed
:
{},
mounted
()
{
mounted
()
{
this
.
$bus
.
$on
(
'isPromptTemplate'
,
(
data
)
=>
{
this
.
isPromptTemplate
=
data
})
},
beforeDestroy
()
{
beforeDestroy
()
{
// 移除监听事件 "share"
this
.
$bus
.
$off
(
'isPromptTemplate'
)
},
methods
:
{
clear
()
{
clear
()
{
this
.
param
.
query
=
null
this
.
param
.
history
=
[]
this
.
inputContent
=
null
this
.
myHistory
=
[]
},
submit
()
{
submit
()
{
this
.
param
.
model_name
=
this
.
chatForm
.
model_name
this
.
param
.
temperature
=
this
.
chatForm
.
temperature
this
.
heistoryRotate
=
this
.
chatForm
.
heistoryRotate
...
...
@@ -120,7 +99,7 @@ export default {
apiUrl
=
'/2api/chat/file_chat'
this
.
param
=
{
...
this
.
param
,
...
this
.
chatForm
.
fileConfige
}
if
(
!
this
.
chatForm
.
fileConfige
.
knowledge_id
)
{
this
.
$message
.
error
(
'请先上传文件'
)
;
this
.
$message
.
error
(
'请先上传文件'
)
return
}
}
else
if
(
this
.
chatForm
.
pattern
===
'知识图谱问答'
)
{
...
...
@@ -128,54 +107,60 @@ export default {
this
.
param
=
{
...
this
.
param
,
...
this
.
chatForm
.
knowledgeGraphConfige
}
}
if
(
!
this
.
inputContent
)
return
this
.
param
.
query
=
this
.
inputContent
;
this
.
param
.
query
=
this
.
inputContent
this
.
myHistory
.
push
({
'role'
:
'user'
,
'content'
:
this
.
inputContent
,
'answer'
:
''
,
'excludeReferenceAnswer'
:
''
,
'id'
:
this
.
generateRandomString
(
8
)
role
:
'user'
,
content
:
this
.
inputContent
,
answer
:
''
,
excludeReferenceAnswer
:
''
,
id
:
this
.
generateRandomString
(
8
)
})
this
.
inputContent
=
null
;
this
.
inputContent
=
null
this
.
param
.
history
=
[]
this
.
myHistory
.
slice
(
0
,
-
1
).
slice
(
-
this
.
heistoryRotate
).
forEach
((
item
)
=>
{
this
.
param
.
history
.
push
(...[{
role
:
item
.
role
,
content
:
item
.
content
},
{
role
:
'assistant'
,
content
:
item
.
excludeReferenceAnswer
}])
})
this
.
myHistory
.
slice
(
0
,
-
1
)
.
slice
(
-
this
.
heistoryRotate
)
.
forEach
((
item
)
=>
{
this
.
param
.
history
.
push
(
...[
{
role
:
item
.
role
,
content
:
item
.
content
},
{
role
:
'assistant'
,
content
:
item
.
excludeReferenceAnswer
}
]
)
})
this
.
$emit
(
'submit'
)
let
getData
=
new
GetStreaming
(
apiUrl
,
this
.
param
,
this
.
onmessage
,
this
.
setSuccess
)
getData
.
initeventSource
()
},
setSuccess
()
{
setSuccess
()
{
this
.
$nextTick
(()
=>
{
let
nowChat
=
this
.
myHistory
[
this
.
myHistory
.
length
-
1
]
this
.
$refs
[
'contentView'
+
nowChat
.
id
][
0
].
isSuccess
()
})
},
onmessage
(
data
)
{
onmessage
(
data
)
{
let
nowChat
=
this
.
myHistory
[
this
.
myHistory
.
length
-
1
]
if
(
this
.
chatForm
.
pattern
===
'通用智能问答'
)
{
// data = data.replace(/^data:\s+|\s+$/g, '')
nowChat
.
excludeReferenceAnswer
+=
data
nowChat
.
answer
+=
data
;
nowChat
.
answer
+=
data
}
else
if
(
this
.
chatForm
.
pattern
===
'专业知识库问答'
)
{
console
.
log
(
data
);
// console.log(data)
let
temporary
=
JSON
.
parse
(
`[
${
data
}
]`
.
replace
(
/}{/g
,
'},{'
))
temporary
.
forEach
((
item
)
=>
{
this
.
modifyContent
(
item
)
// 修改返回内容
this
.
modifyContent
(
item
)
// 修改返回内容
nowChat
.
excludeReferenceAnswer
+=
item
.
answer
||
''
// 排除回答中的引用内容
nowChat
.
answer
+=
item
.
answer
||
'
\
n'
+
item
.
docs
})
}
else
if
(
this
.
chatForm
.
pattern
===
'搜索引擎问答'
)
{
}
else
if
(
this
.
chatForm
.
pattern
===
'基于文件问答'
)
{
let
temporary
=
JSON
.
parse
(
`[
${
data
}
]`
.
replace
(
/}{/g
,
'},{'
))
temporary
.
forEach
((
item
)
=>
{
this
.
modifyContent
(
item
)
// 修改返回内容
this
.
modifyContent
(
item
)
// 修改返回内容
nowChat
.
excludeReferenceAnswer
+=
item
.
answer
||
''
// 排除回答中的引用内容
nowChat
.
answer
+=
item
.
answer
||
'
\
n'
+
item
.
docs
})
...
...
@@ -183,8 +168,8 @@ export default {
let
temporary
=
JSON
.
parse
(
`[
${
data
}
]`
.
replace
(
/}{/g
,
'},{'
))
temporary
.
forEach
((
item
)
=>
{
console
.
log
(
item
)
;
this
.
modifyContent
(
item
)
// 修改返回内容
console
.
log
(
item
)
this
.
modifyContent
(
item
)
// 修改返回内容
nowChat
.
excludeReferenceAnswer
+=
item
.
answer
||
''
// 排除回答中的引用内容
nowChat
.
answer
+=
item
.
answer
||
'
\
n'
+
item
.
docs
.
join
()
// nowChat.answer += item.answer += '\n' + item.docs.join()
...
...
@@ -195,36 +180,37 @@ export default {
this
.
$refs
.
contentBox
.
scrollTo
(
0
,
this
.
$refs
.
contentBox
.
scrollHeight
)
})
},
modifyContent
(
item
)
{
// 修改返回内容
modifyContent
(
item
)
{
// 修改返回内容
if
(
item
.
docs
)
{
item
.
docs
=
item
.
docs
.
map
((
item2
)
=>
{
let
tagUrl
=
((
item2
?.
match
(
/
\]\((
.*
?)\)
/g
)
||
[])[
0
]
||
''
).
slice
(
2
,
-
1
)
let
url
=
''
// item2 = item2.replace(/\n/g, '
<
br
>
')
if (tagUrl.indexOf('
https
') !== -1) {
url = tagUrl.replace(/^https:
\
/
\
/[^/]+
\
//, `https://${window.location.hostname}:${window.location.port}/`)
;
url = tagUrl.replace(/^https:
\
/
\
/[^/]+
\
//, `https://${window.location.hostname}:${window.location.port}/`)
item2 = item2.replace(/
\
]
\
((.*?)
\
)/g, `](${url})`)
} else {
url = tagUrl.replace(/^http:
\
/
\
/[^/]+
\
//, `http://${window.location.hostname}:${window.location.port}/`)
;
url = tagUrl.replace(/^http:
\
/
\
/[^/]+
\
//, `http://${window.location.hostname}:${window.location.port}/`)
item2 = item2.replace(/
\
]
\
((.*?)
\
)/g, `](${url})`)
}
/// ////////出处内容包装div
var match = (item2.match(/出处(.*)
\
)/) || [])[0]
// 查找指定字符串的位置
var indexOfSpecifiedString = item2.indexOf(match)
;
var indexOfSpecifiedString = item2.indexOf(match)
if (indexOfSpecifiedString !== -1) {
// 使用 slice 获取指定字符串后面的所有内容
var contentAfterSpecifiedString = item2.slice(indexOfSpecifiedString + match.length)
;
var contentAfterSpecifiedString = item2.slice(indexOfSpecifiedString + match.length)
item2 = item2.replace(contentAfterSpecifiedString, `<div class="describe-view">${contentAfterSpecifiedString}</div>`)
} else {
console.log('
未找到匹配的指定字符串
')
;
console.log('
未找到匹配的指定字符串
')
}
/// ////////特殊标签后包装div
// var specifiedString = '
<
a
class
=
"show_detail"
><
/a>';
// var match2 = item2.match(new RegExp(specifiedString + '(.*)')) || [];
const
match2
=
item2
.
match
(
/<a
\s
+class="show_detail"
\s
+data="
([^
"
]
*
)
"><
\/
a>
([\s\S]
*
)
/
)
;
const
match2
=
item2
.
match
(
/<a
\s
+class="show_detail"
\s
+data="
([^
"
]
*
)
"><
\/
a>
([\s\S]
*
)
/
)
if
(
match2
&&
match2
.
length
===
3
)
{
var
textAfterSpecifiedString
=
match2
[
2
]
;
var
textAfterSpecifiedString
=
match2
[
2
]
var
data
=
match2
[
1
]
// 修改文本内容
textAfterSpecifiedString
=
`<div class="describe-view" data="
${
data
}
" >
${
textAfterSpecifiedString
}
</div>`
...
...
@@ -234,24 +220,22 @@ export default {
})
}
},
generateRandomString
(
length
)
{
const
characters
=
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
;
const
charactersLength
=
characters
.
length
;
const
randomValues
=
new
Uint32Array
(
length
)
;
generateRandomString
(
length
)
{
const
characters
=
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const
charactersLength
=
characters
.
length
const
randomValues
=
new
Uint32Array
(
length
)
crypto
.
getRandomValues
(
randomValues
)
;
crypto
.
getRandomValues
(
randomValues
)
let
result
=
''
;
let
result
=
''
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
result
+=
characters
.
charAt
(
randomValues
[
i
]
%
charactersLength
)
;
result
+=
characters
.
charAt
(
randomValues
[
i
]
%
charactersLength
)
}
return
result
;
return
result
}
}
}
</
script
>
<
style
scoped
>
.contentBox
{
...
...
@@ -283,7 +267,7 @@ export default {
position
:
relative
;
display
:
flex
;
flex-direction
:
column
;
background-image
:
url(
"~@/assets/img/ai.png"
)
;
background-image
:
url(
'~@/assets/img/ai.png'
)
;
background-repeat
:
no-repeat
;
background-position
:
center
center
;
background-size
:
auto
50%
;
...
...
src/views/welcome/index-2.vue
deleted
100644 → 0
View file @
84bea040
<
template
>
<div
class=
"aggregate"
>
<div
class=
"logo"
>
<img
src=
"@/assets/img/logo2.png"
alt=
""
>
<span
class=
"title"
>
陆军装备知识图谱系统
</span>
</div>
<div
class=
"aggregateBox"
>
<div
class=
"itemBox"
v-for=
"(item,index) in itemList"
:key=
"index"
@
click=
"goPage(item.url)"
>
<span
class=
"text"
>
{{
item
.
name
}}
</span>
</div>
</div>
</div>
</
template
>
<
script
>
export
default
{
data
()
{
return
{
itemList
:
[
{
name
:
'基于NEO4J的知识图谱构建'
,
url
:
`http://
${
window
.
location
.
hostname
}
:
${
window
.
location
.
port
}
/#/main/welcome`
},
{
name
:
'基于大语言模型与知识图谱双轮驱动的机械维修保障智能问答系统'
,
url
:
`http://
${
window
.
location
.
hostname
}
:
${
window
.
location
.
port
}
/#/main/welcome`
},
{
name
:
'基于gstore的知识图谱系统'
,
url
:
`http://
${
window
.
location
.
hostname
}
:
${
window
.
location
.
port
}
/#/main/welcome`
}
]
}
},
components
:
{},
mounted
()
{
},
methods
:
{
goPage
(
url
)
{
location
.
href
=
url
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.logo
{
position
:
absolute
;
top
:
20px
;
left
:
20px
;
display
:
flex
;
img
{
height
:
40px
;
}
.title
{
color
:
#fff
;
font-size
:
18px
;
height
:
40px
;
line-height
:
40px
;
display
:
inline-block
;
margin-left
:
20px
;
}
}
.aggregate
{
height
:
100%
;
width
:
100%
;
background
:
url(~@/assets/img/login_bg_2.jpg)
center
center
;
background-size
:
cover
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
position
:
relative
;
overflow
:
hidden
;
}
.aggregateBox
{
width
:
80%
;
height
:
50%
;
min-width
:
1000px
;
min-height
:
400px
;
border-radius
:
20px
;
background-color
:
rgba
(
255
,
255
,
255
,
0
.1
);
/* 背景色透明度,根据需要调整 */
backdrop-filter
:
blur
(
10px
);
/* 模糊程度,根据需要调整 */
display
:
flex
;
padding
:
20px
;
/* 内边距,根据需要调整 */
justify-content
:
space-around
;
flex-direction
:
row
;
align-items
:
center
;
.itemBox
{
width
:
400px
;
cursor
:
pointer
;
height
:
200px
;
background
:
url(~@/assets/img/cardBg.png)
center
center
;
background-size
:
cover
;
border-radius
:
10px
;
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
justify-content
:
center
;
padding
:
20px
;
border
:
1px
solid
#b1b1b1
;
font-size
:
20px
;
line-height
:
30px
;
// color: #5b6fab;
color
:
#273379
;
font-weight
:
900
;
.text
{
background-color
:
#fff
;
padding
:
10px
;
border-radius
:
5px
;
}
}
}
.aggregateBox
:hover
{
box-shadow
:
1px
1px
8px
1px
#fff
;
transition
:
all
0
.5s
;
}
</
style
>
src/views/welcome/index.vue
View file @
4fcaf270
<
template
>
<div
style=
"background-color: white;padding: 16px;"
class=
"allCard"
>
<Card
v-for=
"(item,index) in cardList"
@
click=
"cardClick(item)"
:key=
"index"
>
<Card
v-for=
"(item,index) in cardList"
@
click=
"cardClick(item)"
:key=
"index"
>
<template
#
title
>
<div
class=
"title"
>
{{
item
.
name
}}
</div>
<div
class=
"title"
>
{{
item
.
name
}}
</div>
</
template
>
<
template
#
content
>
<div
class=
"content"
>
...
...
@@ -18,15 +18,14 @@
</div>
</
template
>
</Card>
</div>
</template>
<
script
>
import
Card
from
'./card'
;
import
*
as
echarts
from
'echarts'
;
import
Card
from
'./card'
import
*
as
echarts
from
'echarts'
export
default
{
data
()
{
data
()
{
return
{
echarts
:
undefined
,
cardList
:
[
...
...
@@ -52,10 +51,15 @@ export default {
},
{
name
:
'调用统计'
}
],
option
:
{
tooltip
:
{
trigger
:
'axis'
,
position
:
function
(
pt
)
{
return
[
pt
[
0
],
'10%'
]
}
},
color
:
[
'#37A2FF'
,
'#FFBF00'
],
legend
:
{
type
:
'scroll'
,
...
...
@@ -65,15 +69,15 @@ export default {
},
xAxis
:
{
type
:
'category'
,
boundaryGap
:
false
,
data
:
[
'Mon'
,
'Tue'
,
'Wed'
,
'Thu'
,
'Fri'
,
'Sat'
,
'Sun'
]
},
yAxis
:
{
type
:
'value'
},
series
:
[
{
smooth
:
tru
e
,
smooth
:
fals
e
,
lineStyle
:
{
width
:
0
},
...
...
@@ -99,20 +103,20 @@ export default {
}
}
},
components
:
{
Card
},
mounted
()
{
components
:
{
Card
},
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
echarts
=
echarts
.
init
(
document
.
getElementById
(
'homeMain'
))
;
this
.
echarts
.
setOption
(
this
.
option
,
true
)
;
this
.
echarts
=
echarts
.
init
(
document
.
getElementById
(
'homeMain'
))
this
.
echarts
.
setOption
(
this
.
option
,
true
)
})
},
methods
:
{
cardClick
()
{
console
.
log
(
1
)
;
cardClick
()
{
console
.
log
(
1
)
},
resize
()
{
resize
()
{
if
(
this
.
echarts
!=
null
)
{
this
.
echarts
.
resize
()
;
this
.
echarts
.
resize
()
}
}
}
...
...
@@ -120,50 +124,50 @@ export default {
</
script
>
<
style
lang=
"scss"
scoped
>
@import
'@/assets/style/element-variables.scss'
;
.title
{
border-left
:
1px
solid
;
border-left-width
:
5px
;
border-left-color
:
$--color-primary
;
margin-bottom
:
20px
;
font-size
:
17px
;
padding-left
:
10px
;
}
#homeMain
{
height
:
400px
;
width
:
100%
;
@import
'@/assets/style/element-variables.scss'
;
.title
{
border-left
:
1px
solid
;
border-left-width
:
5px
;
border-left-color
:
$--color-primary
;
margin-bottom
:
20px
;
font-size
:
17px
;
padding-left
:
10px
;
}
#homeMain
{
height
:
400px
;
width
:
100%
;
}
.title
p
{
height
:
40px
;
line-height
:
40px
;
font-size
:
16px
;
margin
:
0px
;
padding-left
:
20px
;
}
.title
p
{
height
:
40px
;
line-height
:
40px
;
font-size
:
16px
;
margin
:
0px
;
padding-left
:
20px
;
}
.title
p
span
{
font-size
:
20px
;
color
:
$--color-primary
;
}
.title
p
span
{
font-size
:
20px
;
color
:
$--color-primary
;
}
.item-list
{
margin
:
0px
;
}
.item-list
li
{
margin
:
10px
0px
;
}
.item-list
{
margin
:
0px
;
}
.item-list
li
{
margin
:
10px
0px
;
}
.item
{
height
:
48px
;
display
:
flex
;
align-items
:
center
;
}
.item
{
height
:
48px
;
display
:
flex
;
align-items
:
center
;
}
strong
.warning
{
color
:
red
;
font-size
:
16px
;
}
.allCard
{
strong
.warning
{
color
:
red
;
font-size
:
16px
;
}
.allCard
{
display
:
grid
!
important
;
/* grid-column-gap: 50px;
grid-row-gap: 50px; */
...
...
@@ -175,23 +179,20 @@ height:400px;
.allCard
:nth-child
(
5
)
{
grid-column-start
:
1
;
grid-column-end
:
5
;
}
.content
{
.content
{
height
:
calc
(
100%
-
45px
);
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-around
;
align-items
:
center
;
flex-direction
:
column
;
justify-content
:
space-around
;
align-items
:
center
;
}
.num
{
.num
{
font-size
:
40px
;
font-weight
:
bold
;
text-align
:
center
;
}
.describe
{
.describe
{
text-align
:
center
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment