Commit 4fcaf270 authored by mhw's avatar mhw

版本更新

parent 84bea040
...@@ -23,7 +23,8 @@ module.exports = { ...@@ -23,7 +23,8 @@ module.exports = {
'lines-between-class-members': ['off'], 'lines-between-class-members': ['off'],
// 'no-undef': ['off', 'always'], // 'no-undef': ['off', 'always'],
// 'no-unused-vars': ['off', 'always'], // 'no-unused-vars': ['off', 'always'],
'no-new-func': ['off', 'always'] 'no-new-func': ['off', 'always'],
'space-before-function-paren':0
}, },
overrides: [ overrides: [
{ {
......
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);
}
}
...@@ -16,6 +16,7 @@ import TuningRun from './GptController/TuningRun.js'; ...@@ -16,6 +16,7 @@ import TuningRun from './GptController/TuningRun.js';
import PyApi from './GptController/PyApi.js'; import PyApi from './GptController/PyApi.js';
import KnowledgeManage from './GptController/KnowledgeManage.js'; import KnowledgeManage from './GptController/KnowledgeManage.js';
import DatasetData from './GptController/DatasetData.js'; import DatasetData from './GptController/DatasetData.js';
import ModelDeployment from './GptController/ModelDeployment.js';
export { export {
TemplateController, TemplateController,
...@@ -34,5 +35,6 @@ export { ...@@ -34,5 +35,6 @@ export {
TuningRun, TuningRun,
PyApi, PyApi,
KnowledgeManage, KnowledgeManage,
DatasetData DatasetData,
ModelDeployment
} }
module.exports = { 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/', wsUrl: 'ws://218.76.0.69:7860/',
projectName: '灵境大模型平台' projectName: '灵境大模型平台'
} }
...@@ -14,20 +14,21 @@ const routers = [ ...@@ -14,20 +14,21 @@ const routers = [
component: _import('login/index'), component: _import('login/index'),
name: 'root' 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', path: '/main',
component: _import('layout/index'), component: _import('layout/index'),
name: 'main', name: 'main',
props: getProps, props: getProps,
redirect: { redirect: {
name: 'aggregate' name: 'welcome'
}, },
meta: { meta: {
title: '主页', title: '主页',
showOnly: true showOnly: true
}, },
children: [ children: [
{ path: 'welcome', component: _import('welcome/index'), name: 'welcome', meta: { title: '欢迎' } },
{ path: 'formSysUser', component: _import('upms/formSysUser/index'), name: 'formSysUser', 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: 'formSysDept', component: _import('upms/formSysDept/index'), name: 'formSysDept', meta: { title: '部门管理' } },
{ path: 'formSysRole', component: _import('upms/formSysRole/index'), name: 'formSysRole', meta: { title: '角色管理' } }, { path: 'formSysRole', component: _import('upms/formSysRole/index'), name: 'formSysRole', meta: { title: '角色管理' } },
...@@ -52,6 +53,7 @@ const routers = [ ...@@ -52,6 +53,7 @@ const routers = [
{ path: 'modelSquare', component: _import('gptTraining/modelManagement/modelSquare/index'), name: 'modelSquare', props: getProps, meta: { title: '模型广场' } }, { 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: '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: '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: '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: '应用接入' } }, { path: 'applicationAccess', component: _import('gptTraining/modelService/applicationAccess/index'), name: 'applicationAccess', props: getProps, meta: { title: '应用接入' } },
...@@ -89,9 +91,7 @@ const routers = [ ...@@ -89,9 +91,7 @@ const routers = [
// { path: 'onLineOptimization', component: _import('gptTraining/promptProject/onLineOptimization/index'), name: 'onLineOptimization', props: getProps, meta: { title: '在线优化' } }, // { 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: '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: 'knowledgeBase', component: _import('gptTraining/knowledgeBase/index'), name: 'knowledgeBase', props: getProps, meta: { title: '知识库' } }
{ path: 'welcome', component: _import('welcome/index'), name: 'welcome', meta: { title: '欢迎' } }
] ]
} }
]; ];
......
...@@ -455,7 +455,36 @@ const RunningStatus = new DictionaryBase('运行状态', [ ...@@ -455,7 +455,36 @@ const RunningStatus = new DictionaryBase('运行状态', [
symbol: 'TrainingUnderway' 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 { export {
TemplateLabelDict, TemplateLabelDict,
ScenarioTypeDict, ScenarioTypeDict,
...@@ -475,5 +504,6 @@ export { ...@@ -475,5 +504,6 @@ export {
ModelCreationMode, ModelCreationMode,
TrainingMethod, TrainingMethod,
ModeOfSpeaking, ModeOfSpeaking,
RunningStatus RunningStatus,
DeploymentStatus
} }
<!-- 创建sft模板 --> <!-- 创建sft模板 -->
<template> <template>
<el-form label-position="left" ref="form" label-width="120px" :model="form" :size="defaultFormItemSize" :rules="rules"> <el-form label-position="left" ref="form" label-width="120px" :model="form" :size="defaultFormItemSize" :rules="rules">
<el-row class="title">基本信息</el-row> <el-row class="title">基本信息</el-row>
<el-form-item label="任务名称:" prop="tuningTaskDto.taskName"> <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> <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> <span v-else>{{ form.tuningTaskDto.taskName }}</span>
</el-form-item> </el-form-item>
<!-- <el-form-item label="任务类型:"> <!-- <el-form-item label="任务类型:">
...@@ -13,39 +13,41 @@ ...@@ -13,39 +13,41 @@
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
<el-form-item label="任务描述:"> <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> <span v-else>{{ form.tuningTaskDto.taskDescribe }}</span>
</el-form-item> </el-form-item>
<el-row class="title">训练配置</el-row> <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-form-item label="选择基础模型版本:" prop="tuningRunDto.modelVersionId">
<el-cascader v-model="form.tuningRunDto.modelVersionId" :options="modelList" <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>
<el-form-item label="训练方法:"> <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>
<el-form-item label="参数配置:"> <el-form-item label="参数配置:">
<parameterConfiguration v-model="form.tuningRunDto.configuration" :existingTask="existingTask"/> <parameterConfiguration v-model="form.tuningRunDto.configuration" :existingTask="existingTask" />
</el-form-item> </el-form-item>
<el-row class="title">数据配置</el-row> <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-form-item label="选择数据集版本" prop="tuningRunDto.datasetVersionId">
<el-cascader v-model="form.tuningRunDto.datasetVersionId" :options="dataList" <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>
<el-form-item label="数据拆分比例:"> <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-form-item>
<el-row type="flex" justify="end" class="dialog-btn-layer mt20"> <el-row type="flex" justify="end" class="dialog-btn-layer mt20">
<el-button :size="defaultFormItemSize" :plain="true" @click="onCancel(false)">取消</el-button> <el-button :size="defaultFormItemSize" :plain="true" @click="onCancel(false)">取消</el-button>
<el-button type="primary" :size="defaultFormItemSize" @click="onSubmit">确定</el-button> <el-button type="primary" :size="defaultFormItemSize" @click="onSubmit">确定</el-button>
</el-row> </el-row>
...@@ -53,146 +55,170 @@ ...@@ -53,146 +55,170 @@
</template> </template>
<script> <script>
import { TuningTask, TuningRun, MyModel, MyDataSet } from '@/api/gptController.js'
import { TuningTask, TuningRun, MyModel, MyDataSet } from '@/api/gptController.js'; import parameterConfiguration from './parameterConfiguration/index.vue'
import parameterConfiguration from './parameterConfiguration/index.vue';
export default { export default {
data () { data() {
return { return {
modelList: [], modelList: [],
dataList: [], dataList: [],
form: { form: {
'tuningRunDto': { tuningRunDto: {
'configuration': undefined, configuration: undefined,
'modelId': undefined, modelId: undefined,
'publishStatus': 0, publishStatus: 0,
'runName': '', runName: '',
'runStatus': 0, runStatus: 0,
'runTime': 0, runTime: 0,
'runVersion': 0, runVersion: 0,
'splitRatio': 0, splitRatio: 0,
'taskId': undefined, taskId: undefined,
'trainMethod': 'full', trainMethod: 'full',
'trainMode': '', trainMode: '',
datasetVersionId: undefined, datasetVersionId: undefined,
modelVersionId: undefined modelVersionId: undefined
}, },
'tuningTaskDto': { tuningTaskDto: {
'taskDescribe': '', taskDescribe: '',
'taskName': '', taskName: '',
'taskType': 0 taskType: 0
} }
}, },
rules: { rules: {
'tuningTaskDto.taskName': [ 'tuningTaskDto.taskName': [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
{ required: true, message: '请输入任务名称', trigger: 'blur' }
],
'tuningRunDto.modelVersionId': [{ required: true, message: '请选择基础模型版', trigger: 'blur' }], 'tuningRunDto.modelVersionId': [{ required: true, message: '请选择基础模型版', trigger: 'blur' }],
'tuningRunDto.datasetVersionId': [{ required: true, message: '请选择数据集版本', trigger: 'blur' }] 'tuningRunDto.datasetVersionId': [{ required: true, message: '请选择数据集版本', trigger: 'blur' }]
} }
}; }
}, },
props: ['isEdit', 'item', 'existingTask'], props: ['isEdit', 'item', 'existingTask'],
components: {parameterConfiguration}, components: { parameterConfiguration },
computed: {
}, computed: {},
mounted () { mounted() {
this.getModelList() this.getModelList()
this.getDataList() this.getDataList()
this.intFrom() this.intFrom()
}, },
methods: { methods: {
intFrom () { intFrom() {
if (this.item) { if (this.item) {
this.form.tuningTaskDto = this.item.tuningTaskDto this.form.tuningTaskDto = this.item.tuningTaskDto
if (this.isEdit) { if (this.isEdit) {
this.form.tuningRunDto = {...this.form.tuningRunDto, ...this.item.tuningRunDto} this.form.tuningRunDto = {
...this.form.tuningRunDto,
...this.item.tuningRunDto
}
} }
if (this.existingTask) { if (this.existingTask) {
this.form.tuningRunDto.taskId = this.item.tuningTaskDto.taskId this.form.tuningRunDto.taskId = this.item.tuningTaskDto.taskId
} }
} }
}, },
onCancel (isSuccess) { onCancel(isSuccess) {
if (this.observer != null) { if (this.observer != null) {
this.observer.cancel(isSuccess); this.observer.cancel(isSuccess)
} }
}, },
onSubmit () { onSubmit() {
let apiFunction = this.existingTask || this.isEdit ? TuningRun : TuningTask let apiFunction = this.existingTask || this.isEdit ? TuningRun : TuningTask
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.$refs['form'].validate((valid) => { this.$refs['form'].validate((valid) => {
if (valid) { if (valid) {
let params = {}; let params = {}
params = this.existingTask ? {tuningRunDto: this.form.tuningRunDto} : { ...this.form }; params = this.existingTask ? { tuningRunDto: this.form.tuningRunDto } : { ...this.form }
console.log(params); console.log(params)
if (this.isEdit) { if (this.isEdit) {
apiFunction.update(this, params).then(res => { apiFunction
resolve(res); .update(this, params)
this.$message.success('编辑成功'); .then((res) => {
this.onCancel(true); resolve(res)
}).catch(e => { this.$message.success('编辑成功')
reject(e); this.onCancel(true)
}); })
.catch((e) => {
reject(e)
})
} else { } else {
apiFunction.add(this, params).then(res => { apiFunction
resolve(res); .add(this, params)
this.$message.success('添加成功'); .then((res) => {
this.onCancel(true); resolve(res)
}).catch(e => { this.$message.success('添加成功')
reject(e); this.onCancel(true)
}); })
.catch((e) => {
reject(e)
})
} }
} else { } else {
// reject(); // reject();
} }
}); })
}); })
}, },
getModelList () { getModelList() {
MyModel.listForTree(this, {}).then(res => { MyModel.listForTree(this, {})
this.modelList = res.data.filter((item) => item.isBaseModel === 1).map((item) => { .then((res) => {
return { id: item.modelId, name: item.modelName, children: item.modelVersionList === [] ? [] : item.modelVersionList.map((item2) => { return { id: item2.versionId, name: 'V' + item2.modelVersion } }) } 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 () { getDataList() {
MyDataSet.listForTree(this, {}).then(res => { MyDataSet.listForTree(this, {})
.then((res) => {
this.dataList = res.data.map((item) => { 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 } }) } 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> </script>
<style scoped> <style scoped>
.inputWidth { .inputWidth {
width: 600px; width: 600px;
} }
.title { .title {
font-size: 20px; font-size: 20px;
margin-bottom: 16px; margin-bottom: 16px;
} }
.introduce { .introduce {
font-size: 12px; font-size: 12px;
color: #909399; color: #909399;
margin-bottom: 10px; margin-bottom: 10px;
display: block; display: block;
} }
.isReadonly input{
.isReadonly input {
border: none; border: none;
} }
</style> </style>
...@@ -3,17 +3,9 @@ ...@@ -3,17 +3,9 @@
<el-table :data="existingTasklist" style="width: 100%"> <el-table :data="existingTasklist" style="width: 100%">
<el-table-column prop="hyperParameter" label="超参数" width="150"> <el-table-column prop="hyperParameter" label="超参数" width="150">
</el-table-column> </el-table-column>
<el-table-column prop="numericalValue" label="数值" > <el-table-column prop="numericalValue" label="数值">
<template slot-scope="scope"> <template slot-scope="scope">
<component <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>
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> </template>
</el-table-column> </el-table-column>
<el-table-column prop="explain" label="说明"> <el-table-column prop="explain" label="说明">
...@@ -23,9 +15,8 @@ ...@@ -23,9 +15,8 @@
<script> <script>
export default { export default {
data () { data() {
return { return {
configData: undefined, configData: undefined,
parameterConfiguration: [ parameterConfiguration: [
{ {
...@@ -35,18 +26,21 @@ export default { ...@@ -35,18 +26,21 @@ export default {
explain: '启用 4/8 比特模型量化(QLoRA)。', explain: '启用 4/8 比特模型量化(QLoRA)。',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
options: [
{ {
options: [{
value: 'none', value: 'none',
label: 'none' label: 'none'
}, { },
{
value: '8', value: '8',
label: '8' label: '8'
}, { },
{
value: '4', value: '4',
label: '4' label: '4'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -58,85 +52,109 @@ export default { ...@@ -58,85 +52,109 @@ export default {
explain: '构建提示词时使用的模板', explain: '构建提示词时使用的模板',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
{
options: [ options: [
{ {
value: 'alpaca', value: 'alpaca',
label: 'alpaca' label: 'alpaca'
}, { },
{
value: 'aquila', value: 'aquila',
label: 'aquila' label: 'aquila'
}, { },
{
value: 'baichuan', value: 'baichuan',
label: 'baichuan' label: 'baichuan'
}, { },
{
value: 'baichuan2', value: 'baichuan2',
label: 'baichuan2' label: 'baichuan2'
}, { },
{
value: 'belle', value: 'belle',
label: 'belle' label: 'belle'
}, { },
{
value: 'bluelm', value: 'bluelm',
label: 'bluelm' label: 'bluelm'
}, { },
{
value: 'chatglm2', value: 'chatglm2',
label: 'chatglm2' label: 'chatglm2'
}, { },
{
value: 'chatglm3', value: 'chatglm3',
label: 'chatglm3' label: 'chatglm3'
}, { },
{
value: 'chatglm3_raw', value: 'chatglm3_raw',
label: 'chatglm3_raw' label: 'chatglm3_raw'
}, { },
{
value: 'deepseek', value: 'deepseek',
label: 'deepseek' label: 'deepseek'
}, { },
{
value: 'default', value: 'default',
label: 'default' label: 'default'
}, { },
{
value: 'falcon', value: 'falcon',
label: 'falcon' label: 'falcon'
}, { },
{
value: 'intern', value: 'intern',
label: 'intern' label: 'intern'
}, { },
{
value: 'llama2', value: 'llama2',
label: 'llama2' label: 'llama2'
}, { },
{
value: 'llama2_zh', value: 'llama2_zh',
label: 'llama2_zh' label: 'llama2_zh'
}, { },
{
value: 'mistral', value: 'mistral',
label: 'mistral' label: 'mistral'
}, { },
{
value: 'openchat', value: 'openchat',
label: 'openchat' label: 'openchat'
}, { },
{
value: 'qwen', value: 'qwen',
label: 'qwen' label: 'qwen'
}, { },
{
value: 'starchat', value: 'starchat',
label: 'starchat' label: 'starchat'
}, { },
{
value: 'vanilla', value: 'vanilla',
label: 'vanilla' label: 'vanilla'
}, { },
{
value: 'vicuna', value: 'vicuna',
label: 'vicuna' label: 'vicuna'
}, { },
{
value: 'xverse', value: 'xverse',
label: 'xverse' label: 'xverse'
}, { },
{
value: 'yayi', value: 'yayi',
label: 'yayi' label: 'yayi'
}, { },
{
value: 'zephyr', value: 'zephyr',
label: 'zephyr' label: 'zephyr'
}, { },
{
value: 'ziya', value: 'ziya',
label: 'ziya' label: 'ziya'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -148,11 +166,9 @@ export default { ...@@ -148,11 +166,9 @@ export default {
explain: '输入序列分词后的最大长度。', explain: '输入序列分词后的最大长度。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
min: 4, min: 4,
max: 8192 max: 8192
} }
} }
}, },
...@@ -163,8 +179,7 @@ export default { ...@@ -163,8 +179,7 @@ export default {
explain: '学习率(LearningRate)是在梯度下降的过程中更新权重时的超参数,过高会导致模型难以收敛,过低则会导致模型收敛速度过慢,平台已给出默认推荐值,可根据经验调整。', explain: '学习率(LearningRate)是在梯度下降的过程中更新权重时的超参数,过高会导致模型难以收敛,过低则会导致模型收敛速度过慢,平台已给出默认推荐值,可根据经验调整。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 0.0002, max: 0.0002,
min: 2e-7 min: 2e-7
} }
...@@ -177,8 +192,7 @@ export default { ...@@ -177,8 +192,7 @@ export default {
explain: '迭代轮次(Epoch),控制训练过程中的迭代轮数。', explain: '迭代轮次(Epoch),控制训练过程中的迭代轮数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 500, max: 500,
min: 0 min: 0
} }
...@@ -191,8 +205,7 @@ export default { ...@@ -191,8 +205,7 @@ export default {
explain: '每个数据集最多使用的样本数', explain: '每个数据集最多使用的样本数',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 100000, max: 100000,
min: 0 min: 0
} }
...@@ -205,15 +218,17 @@ export default { ...@@ -205,15 +218,17 @@ export default {
explain: '是否启用 FP16 或 BF16 混合精度训练。', explain: '是否启用 FP16 或 BF16 混合精度训练。',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
options: [
{ {
options: [{
value: 'fp16', value: 'fp16',
label: 'fp16' label: 'fp16'
}, { },
{
value: 'bf16', value: 'bf16',
label: 'bf16' label: 'bf16'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -225,8 +240,7 @@ export default { ...@@ -225,8 +240,7 @@ export default {
explain: '批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。', explain: '批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 512, max: 512,
min: 1 min: 1
} }
...@@ -240,8 +254,7 @@ export default { ...@@ -240,8 +254,7 @@ export default {
explain: '梯度累积的步数。', explain: '梯度累积的步数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 512, max: 512,
min: 1 min: 1
} }
...@@ -254,33 +267,41 @@ export default { ...@@ -254,33 +267,41 @@ export default {
explain: '采用的学习率调节器名称。', explain: '采用的学习率调节器名称。',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
options: [
{ {
options: [{
value: 'linear', value: 'linear',
label: 'linear' label: 'linear'
}, { },
{
value: 'cosine', value: 'cosine',
label: 'cosine' label: 'cosine'
}, { },
{
value: 'cosine_with_restarts', value: 'cosine_with_restarts',
label: 'cosine_with_restarts' label: 'cosine_with_restarts'
}, { },
{
value: 'polynomial', value: 'polynomial',
label: 'polynomial' label: 'polynomial'
}, { },
{
value: 'constant', value: 'constant',
label: 'constant' label: 'constant'
}, { },
{
value: 'constant_with_warmup', value: 'constant_with_warmup',
label: 'constant_with_warmup' label: 'constant_with_warmup'
}, { },
{
value: 'inverse_sqrt', value: 'inverse_sqrt',
label: 'inverse_sqrt' label: 'inverse_sqrt'
}, { },
{
value: 'reduce_lr_on_plateau', value: 'reduce_lr_on_plateau',
label: 'reduce_lr_on_plateau' label: 'reduce_lr_on_plateau'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -293,8 +314,7 @@ export default { ...@@ -293,8 +314,7 @@ export default {
explain: '用于梯度裁剪的范数。', explain: '用于梯度裁剪的范数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 100000, max: 100000,
min: 0 min: 0
} }
...@@ -308,8 +328,7 @@ export default { ...@@ -308,8 +328,7 @@ export default {
explain: '验证集占全部样本的百分比。', explain: '验证集占全部样本的百分比。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1, max: 1,
min: 0 min: 0
} }
...@@ -323,8 +342,7 @@ export default { ...@@ -323,8 +342,7 @@ export default {
explain: '每两次日志输出间的更新步数。', explain: '每两次日志输出间的更新步数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1000, max: 1000,
min: 5 min: 5
} }
...@@ -338,8 +356,7 @@ export default { ...@@ -338,8 +356,7 @@ export default {
explain: '每两次断点保存间的更新步数。', explain: '每两次断点保存间的更新步数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 5000, max: 5000,
min: 10 min: 10
} }
...@@ -352,8 +369,7 @@ export default { ...@@ -352,8 +369,7 @@ export default {
explain: '学习率预热采用的步数。', explain: '学习率预热采用的步数。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 5000, max: 5000,
min: 0 min: 0
} }
...@@ -366,8 +382,7 @@ export default { ...@@ -366,8 +382,7 @@ export default {
explain: '嵌入向量所添加的噪声大小。', explain: '嵌入向量所添加的噪声大小。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 10, max: 10,
min: 0 min: 0
} }
...@@ -380,8 +395,7 @@ export default { ...@@ -380,8 +395,7 @@ export default {
explain: 'LoRA 矩阵的秩。', explain: 'LoRA 矩阵的秩。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1024, max: 1024,
min: 0 min: 0
} }
...@@ -394,8 +408,7 @@ export default { ...@@ -394,8 +408,7 @@ export default {
explain: 'LoRA 权重随机丢弃的概率。', explain: 'LoRA 权重随机丢弃的概率。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1, max: 1,
min: 0 min: 0
} }
...@@ -408,37 +421,33 @@ export default { ...@@ -408,37 +421,33 @@ export default {
explain: 'DPO 损失函数中 beta 超参数大小。', explain: 'DPO 损失函数中 beta 超参数大小。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1, max: 1,
min: 0 min: 0
} }
} }
} }
] ]
}; }
}, },
props: ['existingTask', 'value'], props: ['existingTask', 'value'],
components: {}, components: {},
computed: { computed: {
existingTasklist () { existingTasklist() {
return this.parameterConfiguration return this.parameterConfiguration
} }
}, },
mounted () { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.init(); this.init()
}) })
}, },
methods: { methods: {
getProperty () { getProperty() {},
init() {
},
init () {
let initConfig = {} let initConfig = {}
this.parameterConfiguration.forEach((item) => { this.parameterConfiguration.forEach((item) => {
initConfig[item.numericalValue] = item.defaultValue initConfig[item.numericalValue] = item.defaultValue
...@@ -448,27 +457,26 @@ export default { ...@@ -448,27 +457,26 @@ export default {
} else { } else {
this.configData = initConfig this.configData = initConfig
} }
this.$emit('input', JSON.stringify(this.configData)); this.$emit('input', JSON.stringify(this.configData))
}, },
isJSON (str) { isJSON(str) {
try { try {
JSON.parse(str); JSON.parse(str)
} catch (e) { } catch (e) {
// 转换出错,抛出异常 // 转换出错,抛出异常
return false; return false
} }
return true; return true
}, },
changeValue (value, numericalValue) { changeValue(value, numericalValue) {
this.configData[numericalValue] = value this.configData[numericalValue] = value
this.$emit('input', JSON.stringify(this.configData)); this.$emit('input', JSON.stringify(this.configData))
} }
} }
} }
</script> </script>
<style > <style >
div /deep/ .el-cascader-menu__wrap{ div /deep/ .el-cascader-menu__wrap {
height: auto; height: auto;
} }
</style> </style>
......
...@@ -5,43 +5,122 @@ ...@@ -5,43 +5,122 @@
<div class="title">模型压缩</div> <div class="title">模型压缩</div>
<div class="instructions">通过量化、稀疏化等方法在尽量减少精度损失的前提下,降低AI加速卡资源占用,提高推理速度。</div> <div class="instructions">通过量化、稀疏化等方法在尽量减少精度损失的前提下,降低AI加速卡资源占用,提高推理速度。</div>
</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> <el-form
<filter-box :item-width="350" @search="refresh()" @reset="onReset"> 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-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>
<el-form-item label="任务名称" prop="formFilter.taskName" label-width="70px"> <el-form-item
<el-input class="filter-item" v-model="myDataSetPage.formFilter.taskName" :clearable="true" placeholder="任务名称" /> label="任务名称"
prop="formFilter.taskName"
label-width="70px"
>
<el-input
class="filter-item"
v-model="myDataSetPage.formFilter.taskName"
:clearable="true"
placeholder="任务名称"
/>
</el-form-item> </el-form-item>
</filter-box> </filter-box>
</el-form> </el-form>
<vxe-table border show-header-overflow show-overflow :row-config="{ isHover: true }" :data="myDataSetPage.tableData.impl.dataList" min-height="96"> <vxe-table
<vxe-column field="taskName" title="任务名称"></vxe-column> border
<vxe-column field="taskStatus" title="任务状态"> 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"> <template slot-scope="scope">
{{ TaskStatus.getValue(scope.row.taskStatus) }} {{ TaskStatus.getValue(scope.row.taskStatus) }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="sourceVersionId" title="源模型"> <vxe-column
field="sourceVersionId"
title="源模型"
>
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.modelTask?.versionName }} {{ scope.row.modelTask?.versionName }}
</template> </template>
</vxe-column> </vxe-column>
<vxe-column field="targetVersionId" title="压缩后模型"></vxe-column> <vxe-column
<vxe-column field="createTime" title="创建时间"></vxe-column> field="targetVersionId"
<vxe-column field="operation" title="操作"> title="压缩后模型"
></vxe-column>
<vxe-column
field="createTime"
title="创建时间"
></vxe-column>
<vxe-column
field="operation"
title="操作"
>
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" :size="defaultFormItemSize" @click="particulars(scope.row)">详情</el-button> <el-button
<el-button type="text" :size="defaultFormItemSize" @click="copy(scope.row)">复制</el-button> type="text"
<el-button type="text" :size="defaultFormItemSize" @click="del(scope.row)">删除</el-button> :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> </template>
</vxe-column> </vxe-column>
</vxe-table> </vxe-table>
<el-row slot="pagination" type="flex" justify="end" style="margin-top: 16px;"> <el-row
<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"> 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-pagination>
</el-row> </el-row>
</div> </div>
...@@ -49,40 +128,39 @@ ...@@ -49,40 +128,39 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex'
/* eslint-disable-next-line */ /* eslint-disable-next-line */
import { DropdownWidget, TableWidget, UploadWidget, ChartWidget } from '@/utils/widget.js'; import { TableWidget } from '@/utils/widget.js'
import { ModelCompress } from '@/api/gptController.js'; import { ModelCompress } from '@/api/gptController.js'
import editOrAdd from './dialog/editOrAdd'; import editOrAdd from './dialog/editOrAdd'
import particulars from './dialog/particulars'; import particulars from './dialog/particulars'
export default { export default {
data () { data() {
return { return {
myDataSetPage: { myDataSetPage: {
formFilter: { formFilter: {
'taskName': '' taskName: ''
}, },
tableData: { tableData: {
impl: new TableWidget(this.getwidgetData, true, true, false, undefined, false) impl: new TableWidget(this.getwidgetData, true, true, false, undefined, false)
} }
} }
}; }
}, },
components: {}, components: {},
computed: { computed: {
...mapGetters(['getMainContextHeight']), ...mapGetters(['getMainContextHeight']),
tableHeight () { tableHeight() {
return this.getMainContextHeight - 145 + 'px'; return this.getMainContextHeight - 145 + 'px'
} }
}, },
methods: { methods: {
getwidgetData (params) { getwidgetData(params) {
if (params == null) params = {}; if (params == null) params = {}
params = { params = {
...params, ...params,
// orderParam: [ // orderParam: [
...@@ -100,94 +178,125 @@ export default { ...@@ -100,94 +178,125 @@ export default {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ModelCompress.list(this, params).then(res => { ModelCompress.list(this, params)
.then((res) => {
resolve({ resolve({
dataList: res.data.dataList, dataList: res.data.dataList,
totalCount: res.data.totalCount totalCount: res.data.totalCount
}); })
}).catch(e => { })
reject(e); .catch((e) => {
}); reject(e)
}); })
}, })
particulars (item) { },
this.$dialog.show('详情', particulars, { particulars(item) {
this.$dialog
.show(
'详情',
particulars,
{
area: ['100%', '100%'] area: ['100%', '100%']
}, {item: item}).then(res => {
}).catch(e => { });
}, },
add () { { item: item }
this.$dialog.show('创建模版', editOrAdd, { )
.then((res) => {})
.catch((e) => {})
},
add() {
this.$dialog
.show(
'创建模版',
editOrAdd,
{
area: ['100%', '100%'] area: ['100%', '100%']
}, { isEdit: false }).then(res => {
this.refresh();
}).catch(e => { });
}, },
copy (item) { { isEdit: false }
this.$dialog.show('复制任务', editOrAdd, { )
.then((res) => {
this.refresh()
})
.catch((e) => {})
},
copy(item) {
this.$dialog
.show(
'复制任务',
editOrAdd,
{
area: ['100%', '100%'] area: ['100%', '100%']
}, { },
{
isCopy: true, isCopy: true,
item: { item: {
'createMethod': item.createMethod, createMethod: item.createMethod,
'sourceVersionId': item.sourceVersionId, sourceVersionId: item.sourceVersionId,
'targetModelId': item.targetModelId, targetModelId: item.targetModelId,
'taskDescribe': item.taskDescribe, taskDescribe: item.taskDescribe,
'taskName': item.taskName taskName: item.taskName
} }
}).then(res => { }
this.refresh(); )
}).catch(e => { }); .then((res) => {
}, this.refresh()
edit (item) { })
this.$dialog.show('修改模版', editOrAdd, { .catch((e) => {})
},
edit(item) {
this.$dialog
.show(
'修改模版',
editOrAdd,
{
area: ['100%', '100%'] area: ['100%', '100%']
}, { isEdit: true, item: item }).then(res => {
this.refresh();
}).catch(e => { });
}, },
del (item) { { isEdit: true, item: item }
)
.then((res) => {
this.refresh()
})
.catch((e) => {})
},
del(item) {
this.$confirm('是否确认删除', '提示', { this.$confirm('是否确认删除', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
let params = { taskId: item.taskId } let params = { taskId: item.taskId }
ModelCompress.delete(this, params).then(res => { ModelCompress.delete(this, params)
this.$message.success('删除成功'); .then((res) => {
this.$message.success('删除成功')
this.refresh() this.refresh()
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
} })
); })
}, },
refresh (reloadData = false) { refresh(reloadData = false) {
if (reloadData) { if (reloadData) {
this.myDataSetPage.tableData.impl.refreshTable(true, 1); this.myDataSetPage.tableData.impl.refreshTable(true, 1)
} else { } else {
this.myDataSetPage.tableData.impl.refreshTable(); this.myDataSetPage.tableData.impl.refreshTable()
} }
}, },
onReset () { onReset() {
this.$refs.myDataSetPage.resetFields(); this.$refs.myDataSetPage.resetFields()
this.refresh(true); this.refresh(true)
}, },
formInit () { formInit() {
this.refresh(); this.refresh()
} }
}, },
mounted () { mounted() {
this.formInit() this.formInit()
} }
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/assets/style/element-variables.scss"; @import '@/assets/style/element-variables.scss';
.topBox { .topBox {
background-color: white; background-color: white;
width: 100%; width: 100%;
......
<!-- 基本信息-->
<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>
<!-- 参数配置-->
<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>
<!-- 模型部署 -->
<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>
<!-- 详情 -->
<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>
...@@ -3,17 +3,9 @@ ...@@ -3,17 +3,9 @@
<el-table :data="existingTasklist" style="width: 100%"> <el-table :data="existingTasklist" style="width: 100%">
<el-table-column prop="hyperParameter" label="超参数" width="150"> <el-table-column prop="hyperParameter" label="超参数" width="150">
</el-table-column> </el-table-column>
<el-table-column prop="numericalValue" label="数值" > <el-table-column prop="numericalValue" label="数值">
<template slot-scope="scope"> <template slot-scope="scope">
<component <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>
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> </template>
</el-table-column> </el-table-column>
<el-table-column prop="explain" label="说明"> <el-table-column prop="explain" label="说明">
...@@ -23,7 +15,7 @@ ...@@ -23,7 +15,7 @@
<script> <script>
export default { export default {
data () { data() {
return { return {
configData: undefined, configData: undefined,
parameterConfiguration: [ parameterConfiguration: [
...@@ -34,18 +26,21 @@ export default { ...@@ -34,18 +26,21 @@ export default {
explain: '启用 4/8 比特模型量化(QLoRA)。', explain: '启用 4/8 比特模型量化(QLoRA)。',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
options: [
{ {
options: [{
value: 'none', value: 'none',
label: 'none' label: 'none'
}, { },
{
value: '8', value: '8',
label: '8' label: '8'
}, { },
{
value: '4', value: '4',
label: '4' label: '4'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -57,85 +52,109 @@ export default { ...@@ -57,85 +52,109 @@ export default {
explain: '构建提示词时使用的模板', explain: '构建提示词时使用的模板',
componentOptions: { componentOptions: {
component: 'el-cascader', component: 'el-cascader',
property: property: {
{
options: [ options: [
{ {
value: 'alpaca', value: 'alpaca',
label: 'alpaca' label: 'alpaca'
}, { },
{
value: 'aquila', value: 'aquila',
label: 'aquila' label: 'aquila'
}, { },
{
value: 'baichuan', value: 'baichuan',
label: 'baichuan' label: 'baichuan'
}, { },
{
value: 'baichuan2', value: 'baichuan2',
label: 'baichuan2' label: 'baichuan2'
}, { },
{
value: 'belle', value: 'belle',
label: 'belle' label: 'belle'
}, { },
{
value: 'bluelm', value: 'bluelm',
label: 'bluelm' label: 'bluelm'
}, { },
{
value: 'chatglm2', value: 'chatglm2',
label: 'chatglm2' label: 'chatglm2'
}, { },
{
value: 'chatglm3', value: 'chatglm3',
label: 'chatglm3' label: 'chatglm3'
}, { },
{
value: 'chatglm3_raw', value: 'chatglm3_raw',
label: 'chatglm3_raw' label: 'chatglm3_raw'
}, { },
{
value: 'deepseek', value: 'deepseek',
label: 'deepseek' label: 'deepseek'
}, { },
{
value: 'default', value: 'default',
label: 'default' label: 'default'
}, { },
{
value: 'falcon', value: 'falcon',
label: 'falcon' label: 'falcon'
}, { },
{
value: 'intern', value: 'intern',
label: 'intern' label: 'intern'
}, { },
{
value: 'llama2', value: 'llama2',
label: 'llama2' label: 'llama2'
}, { },
{
value: 'llama2_zh', value: 'llama2_zh',
label: 'llama2_zh' label: 'llama2_zh'
}, { },
{
value: 'mistral', value: 'mistral',
label: 'mistral' label: 'mistral'
}, { },
{
value: 'openchat', value: 'openchat',
label: 'openchat' label: 'openchat'
}, { },
{
value: 'qwen', value: 'qwen',
label: 'qwen' label: 'qwen'
}, { },
{
value: 'starchat', value: 'starchat',
label: 'starchat' label: 'starchat'
}, { },
{
value: 'vanilla', value: 'vanilla',
label: 'vanilla' label: 'vanilla'
}, { },
{
value: 'vicuna', value: 'vicuna',
label: 'vicuna' label: 'vicuna'
}, { },
{
value: 'xverse', value: 'xverse',
label: 'xverse' label: 'xverse'
}, { },
{
value: 'yayi', value: 'yayi',
label: 'yayi' label: 'yayi'
}, { },
{
value: 'zephyr', value: 'zephyr',
label: 'zephyr' label: 'zephyr'
}, { },
{
value: 'ziya', value: 'ziya',
label: 'ziya' label: 'ziya'
}], }
],
props: { emitPath: false } props: { emitPath: false }
} }
} }
...@@ -147,11 +166,9 @@ export default { ...@@ -147,11 +166,9 @@ export default {
explain: '输入序列分词后的最大长度。', explain: '输入序列分词后的最大长度。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
min: 4, min: 4,
max: 8192 max: 8192
} }
} }
}, },
...@@ -162,8 +179,7 @@ export default { ...@@ -162,8 +179,7 @@ export default {
explain: '每个数据集最多使用的样本数', explain: '每个数据集最多使用的样本数',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 100000, max: 100000,
min: 0 min: 0
} }
...@@ -176,8 +192,7 @@ export default { ...@@ -176,8 +192,7 @@ export default {
explain: '批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。', explain: '批处理大小(BatchSize)表示在每次训练迭代中使用的样本数。较大的批处理大小可以加速训练,但可能会导致内存问题。',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 512, max: 512,
min: 1 min: 1
} }
...@@ -190,8 +205,7 @@ export default { ...@@ -190,8 +205,7 @@ export default {
explain: '最大生成长度', explain: '最大生成长度',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 2048, max: 2048,
min: 10 min: 10
} }
...@@ -204,8 +218,7 @@ export default { ...@@ -204,8 +218,7 @@ export default {
explain: 'Top-p 采样值', explain: 'Top-p 采样值',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1, max: 1,
min: 0.01, min: 0.01,
step: 0.01 step: 0.01
...@@ -219,39 +232,34 @@ export default { ...@@ -219,39 +232,34 @@ export default {
explain: '温度系数', explain: '温度系数',
componentOptions: { componentOptions: {
component: 'el-input-number', component: 'el-input-number',
property: property: {
{
max: 1.5, max: 1.5,
min: 0.01, min: 0.01,
step: 0.01 step: 0.01
} }
} }
} }
] ]
}; }
}, },
props: ['existingTask', 'value'], props: ['existingTask', 'value'],
components: {}, components: {},
computed: { computed: {
existingTasklist () { existingTasklist() {
return this.parameterConfiguration return this.parameterConfiguration
} }
}, },
mounted () { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.init(); this.init()
}) })
}, },
methods: { methods: {
getProperty () { getProperty() {},
init() {
},
init () {
let initConfig = {} let initConfig = {}
this.parameterConfiguration.forEach((item) => { this.parameterConfiguration.forEach((item) => {
initConfig[item.numericalValue] = item.defaultValue initConfig[item.numericalValue] = item.defaultValue
...@@ -261,28 +269,27 @@ export default { ...@@ -261,28 +269,27 @@ export default {
} else { } else {
this.configData = initConfig this.configData = initConfig
} }
this.$emit('input', JSON.stringify(this.configData)); this.$emit('input', JSON.stringify(this.configData))
}, },
isJSON (str) { isJSON(str) {
try { try {
JSON.parse(str); JSON.parse(str)
} catch (e) { } catch (e) {
// 转换出错,抛出异常 // 转换出错,抛出异常
return false; return false
} }
return true; return true
}, },
changeValue (value, numericalValue) { changeValue(value, numericalValue) {
this.configData[numericalValue] = value this.configData[numericalValue] = value
this.$emit('input', JSON.stringify(this.configData)); this.$emit('input', JSON.stringify(this.configData))
} }
} }
} }
</script> </script>
<style > <style >
div /deep/ .el-cascader-menu__wrap{ div /deep/ .el-cascader-menu__wrap {
height: auto; height: auto;
} }
</style> </style>
...@@ -7,8 +7,14 @@ ...@@ -7,8 +7,14 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </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-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>
<el-form-item label="Temperature:"> <el-form-item label="Temperature:">
<el-slider v-model="temperature" :format-tooltip="formatTooltip" @change="form.temperature = temperature / 100"></el-slider> <el-slider v-model="temperature" :format-tooltip="formatTooltip" @change="form.temperature = temperature / 100"></el-slider>
...@@ -41,7 +47,7 @@ ...@@ -41,7 +47,7 @@
</el-option> </el-option>
</el-select> </el-select>
<div class="el-upload__tip" >{{knowledgeDescribe}}</div> <div class="el-upload__tip">{{knowledgeDescribe}}</div>
</el-form-item> </el-form-item>
<el-form-item label="匹配知识条数:" style="margin-bottom:30px"> <el-form-item label="匹配知识条数:" style="margin-bottom:30px">
...@@ -69,18 +75,18 @@ ...@@ -69,18 +75,18 @@
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-form-item> </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 value="1">
<el-collapse-item name="1"> <el-collapse-item name="1">
<template slot="title">文件配置</template> <template slot="title">文件配置</template>
<el-form-item label="上传文件:" style="margin-bottom:30px"> <el-form-item label="上传文件:" style="margin-bottom:30px">
<el-button @click="clickUp" :size="defaultFormItemSize" type="primary">选择文件</el-button> <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"> <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="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 }} <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> <i class="el-icon-circle-close" style="color:#0092FF;cursor: pointer;" @click="clearFile(index)"></i>
</div> </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>
<el-form-item label="匹配知识条数:" style="margin-bottom:30px"> <el-form-item label="匹配知识条数:" style="margin-bottom:30px">
...@@ -97,7 +103,7 @@ ...@@ -97,7 +103,7 @@
<el-collapse-item name="1"> <el-collapse-item name="1">
<template slot="title">知识图谱配置</template> <template slot="title">知识图谱配置</template>
<el-form-item label="请选择知识图谱:" style="margin-bottom:30px"> <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 v-for="item in []" :key="item.knowledgeId" :label="item.knowledgeGraphName" :value="item.knowledgeGraphName">
</el-option> </el-option>
</el-select> </el-select>
...@@ -116,10 +122,10 @@ ...@@ -116,10 +122,10 @@
</template> </template>
<script> <script>
import { KnowledgeManage, MyModel, ModelVersion, TemplateController } from '@/api/gptController.js'; import { KnowledgeManage, MyModel, ModelVersion, TemplateController, ModelDeployment } from '@/api/gptController.js'
import promptWordTemplate from '../promptWordTemplate'; import promptWordTemplate from '../promptWordTemplate'
export default { export default {
data () { data() {
return { return {
knowledgeDescribe: '', knowledgeDescribe: '',
files: [], files: [],
...@@ -128,6 +134,7 @@ export default { ...@@ -128,6 +134,7 @@ export default {
promptTemplate: '', promptTemplate: '',
templateControllerList: [], templateControllerList: [],
modelList: [], modelList: [],
serveList: [],
activeModelList: [], activeModelList: [],
loading: undefined, loading: undefined,
temperature: 70, temperature: 70,
...@@ -141,28 +148,30 @@ export default { ...@@ -141,28 +148,30 @@ export default {
temperature: 0.7, temperature: 0.7,
prompt_template: '', prompt_template: '',
heistoryRotate: 10, // 历史对话轮数 heistoryRotate: 10, // 历史对话轮数
knowledgeConfige: {// 知识库配置 knowledgeConfige: {
// 知识库配置
knowledge_base_name: '', knowledge_base_name: '',
top_k: 1, top_k: 1,
score_threshold: 0.5 score_threshold: 0.5
}, },
searchConfige: {// 搜索引擎配置 searchConfige: {
// 搜索引擎配置
search_engine_name: 'bing', search_engine_name: 'bing',
top_k: 1 top_k: 1
}, },
fileConfige: {// 文件对哈配置 fileConfige: {
// 文件对哈配置
top_k: 1, top_k: 1,
score_threshold: 0.5, score_threshold: 0.5,
knowledge_id: undefined, knowledge_id: undefined,
// max_tokens: 0, // max_tokens: 0,
prompt_name: 'default' prompt_name: 'default'
}, },
knowledgeGraphConfige: {// 知识图谱对话 knowledgeGraphConfige: {
// 知识图谱对话
knowledge_graph_name: '', knowledge_graph_name: '',
top_k: 1, top_k: 1,
score_threshold: 0.5 score_threshold: 0.5
} }
}, },
filesForm: { filesForm: {
...@@ -171,33 +180,32 @@ export default { ...@@ -171,33 +180,32 @@ export default {
chunk_overlap: 50, chunk_overlap: 50,
zh_title_enhance: false zh_title_enhance: false
} }
}; }
}, },
components: {}, components: {},
computed: { computed: {},
},
watch: { watch: {
form: { form: {
handler (newVal, oldVal) { handler(newVal, oldVal) {
this.$emit('chatForm', this.form) this.$emit('chatForm', this.form)
}, },
deep: true, // 是否深度监听 deep: true, // 是否深度监听
immediate: true // 是否在组件创建时立即执行回调函数 immediate: true // 是否在组件创建时立即执行回调函数
} }
}, },
mounted () { mounted() {
this.getKnowledgeList() this.getKnowledgeList()
this.getModelList() // this.getModelList()
this.getTemplateControllerList() this.getTemplateControllerList()
this.getServeList()
}, },
methods: { methods: {
getTemplateControllerList () { getTemplateControllerList() {
TemplateController.listForTree(this, {}).then(res => { TemplateController.listForTree(this, {})
.then((res) => {
this.templateControllerList = res.data.map((item) => { this.templateControllerList = res.data.map((item) => {
return { return {
templateId: item.templateId, templateId: item.templateId,
...@@ -206,32 +214,57 @@ export default { ...@@ -206,32 +214,57 @@ export default {
parameterFormat: item.parameterFormat parameterFormat: item.parameterFormat
} }
}) })
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
})
},
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 () { getModelList() {
MyModel.listForTree(this, {}).then(res => { MyModel.listForTree(this, {})
.then((res) => {
this.modelList = res.data.map((item) => { 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 } }) } 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() this.getActiveModelList()
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
})
}, },
getKnowledgeList () { getKnowledgeList() {
let params = {} let params = {}
KnowledgeManage.load(this, params).then(res => { KnowledgeManage.load(this, params)
.then((res) => {
this.knowledgeList = res.data this.knowledgeList = res.data
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
})
}, },
getActiveModelList () { // 获取当前挂载上的模型列表 getActiveModelList() {
// 获取当前挂载上的模型列表
let params = {} let params = {}
ModelVersion.listModels(this, params).then(res => { ModelVersion.listModels(this, params)
.then((res) => {
this.activeModelList = res.data this.activeModelList = res.data
if (this.activeModelList.length === 0) { if (this.activeModelList.length === 0) {
this.form.model_name = this.modelList[0].children[0].name this.form.model_name = this.modelList[0].children[0].name
...@@ -239,109 +272,124 @@ export default { ...@@ -239,109 +272,124 @@ export default {
} else { } else {
this.form.model_name = this.activeModelList[0] this.form.model_name = this.activeModelList[0]
} }
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
})
}, },
openLoading (text) { openLoading(text) {
this.loading = this.$loading({ this.loading = this.$loading({
lock: true, lock: true,
text: text, text: text,
customClass: 'myLoading', customClass: 'myLoading',
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
background: 'rgba(255, 255, 255, 0.9)' background: 'rgba(255, 255, 255, 0.9)'
}); })
// loading.close(); // loading.close();
}, },
formatTooltip (val) { formatTooltip(val) {
return val / 100; return val / 100
}, },
switchModel (versionId) { // 切换模型 switchModel(versionId) {
// 切换模型
this.openLoading('LLM模型加载中') this.openLoading('LLM模型加载中')
ModelVersion.change(this, { versionId: versionId }).then(res => { ModelVersion.change(this, { versionId: versionId })
this.loading.close(); .then((res) => {
}).catch(e => { this.loading.close()
this.loading.close(); })
console.log(e); .catch((e) => {
}); this.loading.close()
console.log(e)
})
}, },
changeModel (data) { // 修改模型 changeModel(data) {
// 修改模型
// this.openLoading('LLM模型加载中') // this.openLoading('LLM模型加载中')
let id = this.$refs.modelCascader.getCheckedNodes()[0].data.id let id = this.$refs.modelCascader.getCheckedNodes()[0].data.id
this.switchModel(id) this.switchModel(id)
}, },
changeKnowledge (data) { // 修改知识库 changeKnowledge(data) {
// 修改知识库
// this.openLoading('知识库加载中') // this.openLoading('知识库加载中')
this.knowledgeDescribe = this.knowledgeList.filter((item) => { this.knowledgeDescribe = this.knowledgeList.filter((item) => {
return item.knowledgeCode === data return item.knowledgeCode === data
})[0].knowledgeDescribe })[0].knowledgeDescribe
}, },
changeSe (data) { // 修改模型引擎 changeSe(data) {
// 修改模型引擎
// this.openLoading('模型引擎加载中') // this.openLoading('模型引擎加载中')
// console.log(this.$refs.searchSelect); // console.log(this.$refs.searchSelect);
}, },
templateControllerChange (data) { templateControllerChange(data) {
if (!data) { if (!data) {
this.form.prompt_template = '' this.form.prompt_template = ''
return return
} }
this.$dialog.show(data.label, promptWordTemplate, { this.$dialog
.show(
data.label,
promptWordTemplate,
{
area: ['50%', '40%'] area: ['50%', '40%']
}, { data: data }).then(res => { },
{ data: data }
)
.then((res) => {
this.form.prompt_template = res this.form.prompt_template = res
}).catch(e => { })
.catch((e) => {
this.promptTemplate = undefined this.promptTemplate = undefined
}); })
}, },
changePromptTemplate () { changePromptTemplate() {
this.$bus.$emit('isPromptTemplate', this.isPromptTemplate); this.$bus.$emit('isPromptTemplate', this.isPromptTemplate)
}, },
fileinfo (files) { fileinfo(files) {
for (const key in files) { for (const key in files) {
if (Object.hasOwnProperty.call(files, key)) { 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) { if (this.files.map((item) => item.name).indexOf(element.name) === -1) {
this.files.push(element) this.files.push(element)
this.filesForm.filesArr = this.files this.filesForm.filesArr = this.files
} else { } else {
console.log('重复上传'); console.log('重复上传')
} }
} }
} }
}, },
clickUp () { clickUp() {
this.$refs.upFile.click() this.$refs.upFile.click()
}, },
clearFile (index) { clearFile(index) {
this.files.splice(index, 1) this.files.splice(index, 1)
}, },
uploadFiles () { uploadFiles() {
this.fullscreenLoading = true this.fullscreenLoading = true
let params = this.filesForm let params = this.filesForm
this.upload('/2api/knowledge_base/upload_temp_docs', params, false).then(res => { this.upload('/2api/knowledge_base/upload_temp_docs', params, false)
this.$message.success('上传成功'); .then((res) => {
this.fullscreenLoading = false; this.$message.success('上传成功')
this.fullscreenLoading = false
this.form.fileConfige.knowledge_id = res.data.id this.form.fileConfige.knowledge_id = res.data.id
}).catch(e => { })
console.log(e); .catch((e) => {
}); console.log(e)
})
}, },
patternChange () { patternChange() {
// console.log(111); // console.log(111);
// Object.assign(this.$data.form, this.$options.data().form); // Object.assign(this.$data.form, this.$options.data().form);
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.myLoading .el-loading-spinner i { .myLoading .el-loading-spinner i {
font-size: 24px !important; font-size: 24px !important;
} }
.el-upload__tip{ .el-upload__tip {
line-height: normal; line-height: normal;
font-size: 12px !important; font-size: 12px !important;
} }
......
<!-- 右侧聊天界面 --> <!-- 右侧聊天界面 -->
<template> <template>
<div class="box" <div class="box" ref="box">
ref="box"> <div class="contentBox" ref="contentBox" v-if="myHistory.length > 0">
<div class="contentBox" <div v-for="item in myHistory" :key="item.id">
ref="contentBox"
v-if="myHistory.length > 0">
<div v-for="item in myHistory"
:key="item.id">
<div class="userBox"> <div class="userBox">
<div class="content"> <div class="content">
<contentView :content="item.content" /> <contentView :content="item.content" />
...@@ -15,19 +11,15 @@ ...@@ -15,19 +11,15 @@
{{ item.role }} {{ item.role }}
</div> </div>
</div> </div>
<div v-if="item.answer" <div v-if="item.answer" class="gptBox">
class="gptBox">
<div class="icon">GPT</div> <div class="icon">GPT</div>
<div class="content"> <div class="content">
<contentView :content="item.answer" <contentView :content="item.answer" :ref="'contentView' + item.id" />
:ref="'contentView' + item.id" />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="initBox" <div class="initBox" ref="initBox" v-else>
ref="initBox"
v-else>
<div class="text"> <div class="text">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你好,<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你好,<br />
我是人工智能语言模型<br /> 我是人工智能语言模型<br />
...@@ -35,32 +27,22 @@ ...@@ -35,32 +27,22 @@
</div> </div>
</div> </div>
<div class="inputBox"> <div class="inputBox">
<el-input placeholder="请输入内容" <el-input placeholder="请输入内容" v-model="inputContent" class="input-with-select" @keyup.enter.native="submit">
v-model="inputContent" <el-button slot="append" icon="el-icon-position" @click="submit"></el-button>
class="input-with-select"
@keyup.enter.native="submit">
<el-button slot="append"
icon="el-icon-position"
@click="submit"></el-button>
</el-input> </el-input>
<el-button icon="el-icon-delete" <el-button icon="el-icon-delete" style="margin-left: 10px" @click="clear" type="danger" plain></el-button>
style="margin-left: 10px"
@click="clear"
type="danger"
plain></el-button>
</div> </div>
<div id="network"></div> <div id="network"></div>
</div> </div>
</template> </template>
<script> <script>
import { GetStreaming } from '@/utils/getStreaming.js'
import { GetStreaming } from '@/utils/getStreaming.js';
// import codePreview from '../codePreview'; // import codePreview from '../codePreview';
import contentView from '../contentView'; import contentView from '../contentView'
export default { export default {
data () { data() {
return { return {
templateController: undefined, templateController: undefined,
isPromptTemplate: true, isPromptTemplate: true,
...@@ -71,38 +53,35 @@ export default { ...@@ -71,38 +53,35 @@ export default {
query: null, query: null,
history: [], history: [],
prompt_template: '', prompt_template: '',
'stream': true, stream: true,
'model_name': '', model_name: '',
'temperature': 0.7 temperature: 0.7
}
} }
};
}, },
props: ['chatForm'], props: ['chatForm'],
components: { contentView }, components: { contentView },
computed: { computed: {},
},
mounted () { mounted() {
this.$bus.$on('isPromptTemplate', (data) => { this.$bus.$on('isPromptTemplate', (data) => {
this.isPromptTemplate = data this.isPromptTemplate = data
}) })
}, },
beforeDestroy () { beforeDestroy() {
// 移除监听事件 "share" // 移除监听事件 "share"
this.$bus.$off('isPromptTemplate') this.$bus.$off('isPromptTemplate')
}, },
methods: { methods: {
clear() {
clear () {
this.param.query = null this.param.query = null
this.param.history = [] this.param.history = []
this.inputContent = null this.inputContent = null
this.myHistory = [] this.myHistory = []
}, },
submit () { submit() {
this.param.model_name = this.chatForm.model_name this.param.model_name = this.chatForm.model_name
this.param.temperature = this.chatForm.temperature this.param.temperature = this.chatForm.temperature
this.heistoryRotate = this.chatForm.heistoryRotate this.heistoryRotate = this.chatForm.heistoryRotate
...@@ -120,7 +99,7 @@ export default { ...@@ -120,7 +99,7 @@ export default {
apiUrl = '/2api/chat/file_chat' apiUrl = '/2api/chat/file_chat'
this.param = { ...this.param, ...this.chatForm.fileConfige } this.param = { ...this.param, ...this.chatForm.fileConfige }
if (!this.chatForm.fileConfige.knowledge_id) { if (!this.chatForm.fileConfige.knowledge_id) {
this.$message.error('请先上传文件'); this.$message.error('请先上传文件')
return return
} }
} else if (this.chatForm.pattern === '知识图谱问答') { } else if (this.chatForm.pattern === '知识图谱问答') {
...@@ -128,54 +107,60 @@ export default { ...@@ -128,54 +107,60 @@ export default {
this.param = { ...this.param, ...this.chatForm.knowledgeGraphConfige } this.param = { ...this.param, ...this.chatForm.knowledgeGraphConfige }
} }
if (!this.inputContent) return if (!this.inputContent) return
this.param.query = this.inputContent; this.param.query = this.inputContent
this.myHistory.push({ this.myHistory.push({
'role': 'user', role: 'user',
'content': this.inputContent, content: this.inputContent,
'answer': '', answer: '',
'excludeReferenceAnswer': '', excludeReferenceAnswer: '',
'id': this.generateRandomString(8) id: this.generateRandomString(8)
}) })
this.inputContent = null; this.inputContent = null
this.param.history = [] this.param.history = []
this.myHistory.slice(0, -1).slice(-this.heistoryRotate).forEach((item) => { this.myHistory
this.param.history.push(...[{ role: item.role, content: item.content }, { role: 'assistant', content: item.excludeReferenceAnswer }]) .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') this.$emit('submit')
let getData = new GetStreaming(apiUrl, this.param, this.onmessage, this.setSuccess) let getData = new GetStreaming(apiUrl, this.param, this.onmessage, this.setSuccess)
getData.initeventSource() getData.initeventSource()
}, },
setSuccess () { setSuccess() {
this.$nextTick(() => { this.$nextTick(() => {
let nowChat = this.myHistory[this.myHistory.length - 1] let nowChat = this.myHistory[this.myHistory.length - 1]
this.$refs['contentView' + nowChat.id][0].isSuccess() this.$refs['contentView' + nowChat.id][0].isSuccess()
}) })
}, },
onmessage (data) { onmessage(data) {
let nowChat = this.myHistory[this.myHistory.length - 1] let nowChat = this.myHistory[this.myHistory.length - 1]
if (this.chatForm.pattern === '通用智能问答') { if (this.chatForm.pattern === '通用智能问答') {
// data = data.replace(/^data:\s+|\s+$/g, '')
nowChat.excludeReferenceAnswer += data nowChat.excludeReferenceAnswer += data
nowChat.answer += data; nowChat.answer += data
} else if (this.chatForm.pattern === '专业知识库问答') { } else if (this.chatForm.pattern === '专业知识库问答') {
console.log(data); // console.log(data)
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{')) let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => { temporary.forEach((item) => {
this.modifyContent(item)// 修改返回内容 this.modifyContent(item) // 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容 nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs nowChat.answer += item.answer || '\n' + item.docs
}) })
} else if (this.chatForm.pattern === '搜索引擎问答') { } else if (this.chatForm.pattern === '搜索引擎问答') {
} else if (this.chatForm.pattern === '基于文件问答') { } else if (this.chatForm.pattern === '基于文件问答') {
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{')) let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => { temporary.forEach((item) => {
this.modifyContent(item)// 修改返回内容 this.modifyContent(item) // 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容 nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs nowChat.answer += item.answer || '\n' + item.docs
}) })
...@@ -183,8 +168,8 @@ export default { ...@@ -183,8 +168,8 @@ export default {
let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{')) let temporary = JSON.parse(`[${data}]`.replace(/}{/g, '},{'))
temporary.forEach((item) => { temporary.forEach((item) => {
console.log(item); console.log(item)
this.modifyContent(item)// 修改返回内容 this.modifyContent(item) // 修改返回内容
nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容 nowChat.excludeReferenceAnswer += item.answer || '' // 排除回答中的引用内容
nowChat.answer += item.answer || '\n' + item.docs.join() nowChat.answer += item.answer || '\n' + item.docs.join()
// nowChat.answer += item.answer += '\n' + item.docs.join() // nowChat.answer += item.answer += '\n' + item.docs.join()
...@@ -195,36 +180,37 @@ export default { ...@@ -195,36 +180,37 @@ export default {
this.$refs.contentBox.scrollTo(0, this.$refs.contentBox.scrollHeight) this.$refs.contentBox.scrollTo(0, this.$refs.contentBox.scrollHeight)
}) })
}, },
modifyContent (item) { // 修改返回内容 modifyContent(item) {
// 修改返回内容
if (item.docs) { if (item.docs) {
item.docs = item.docs.map((item2) => { item.docs = item.docs.map((item2) => {
let tagUrl = ((item2?.match(/\]\((.*?)\)/g) || [])[0] || '').slice(2, -1) let tagUrl = ((item2?.match(/\]\((.*?)\)/g) || [])[0] || '').slice(2, -1)
let url = '' let url = ''
// item2 = item2.replace(/\n/g, '<br>') // item2 = item2.replace(/\n/g, '<br>')
if (tagUrl.indexOf('https') !== -1) { 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})`) item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
} else { } 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})`) item2 = item2.replace(/\]\((.*?)\)/g, `](${url})`)
} }
/// ////////出处内容包装div /// ////////出处内容包装div
var match = (item2.match(/出处(.*)\)/) || [])[0] var match = (item2.match(/出处(.*)\)/) || [])[0]
// 查找指定字符串的位置 // 查找指定字符串的位置
var indexOfSpecifiedString = item2.indexOf(match); var indexOfSpecifiedString = item2.indexOf(match)
if (indexOfSpecifiedString !== -1) { if (indexOfSpecifiedString !== -1) {
// 使用 slice 获取指定字符串后面的所有内容 // 使用 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>`) item2 = item2.replace(contentAfterSpecifiedString, `<div class="describe-view">${contentAfterSpecifiedString}</div>`)
} else { } else {
console.log('未找到匹配的指定字符串'); console.log('未找到匹配的指定字符串')
} }
/// ////////特殊标签后包装div /// ////////特殊标签后包装div
// var specifiedString = '<a class="show_detail"></a>'; // var specifiedString = '<a class="show_detail"></a>';
// var match2 = item2.match(new RegExp(specifiedString + '(.*)')) || []; // 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) { if (match2 && match2.length === 3) {
var textAfterSpecifiedString = match2[2]; var textAfterSpecifiedString = match2[2]
var data = match2[1] var data = match2[1]
// 修改文本内容 // 修改文本内容
textAfterSpecifiedString = `<div class="describe-view" data="${data}" >${textAfterSpecifiedString}</div>` textAfterSpecifiedString = `<div class="describe-view" data="${data}" >${textAfterSpecifiedString}</div>`
...@@ -234,24 +220,22 @@ export default { ...@@ -234,24 +220,22 @@ export default {
}) })
} }
}, },
generateRandomString (length) { generateRandomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length; const charactersLength = characters.length
const randomValues = new Uint32Array(length); const randomValues = new Uint32Array(length)
crypto.getRandomValues(randomValues); crypto.getRandomValues(randomValues)
let result = ''; let result = ''
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
result += characters.charAt(randomValues[i] % charactersLength); result += characters.charAt(randomValues[i] % charactersLength)
} }
return result; return result
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
.contentBox { .contentBox {
...@@ -283,7 +267,7 @@ export default { ...@@ -283,7 +267,7 @@ export default {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-image: url("~@/assets/img/ai.png"); background-image: url('~@/assets/img/ai.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center center; background-position: center center;
background-size: auto 50%; background-size: auto 50%;
......
<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>
...@@ -18,15 +18,14 @@ ...@@ -18,15 +18,14 @@
</div> </div>
</template> </template>
</Card> </Card>
</div> </div>
</template> </template>
<script> <script>
import Card from './card'; import Card from './card'
import * as echarts from 'echarts'; import * as echarts from 'echarts'
export default { export default {
data () { data() {
return { return {
echarts: undefined, echarts: undefined,
cardList: [ cardList: [
...@@ -52,10 +51,15 @@ export default { ...@@ -52,10 +51,15 @@ export default {
}, },
{ {
name: '调用统计' name: '调用统计'
} }
], ],
option: { option: {
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%']
}
},
color: ['#37A2FF', '#FFBF00'], color: ['#37A2FF', '#FFBF00'],
legend: { legend: {
type: 'scroll', type: 'scroll',
...@@ -65,15 +69,15 @@ export default { ...@@ -65,15 +69,15 @@ export default {
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'
}, },
series: [ series: [
{ {
smooth: true, smooth: false,
lineStyle: { lineStyle: {
width: 0 width: 0
}, },
...@@ -99,20 +103,20 @@ export default { ...@@ -99,20 +103,20 @@ export default {
} }
} }
}, },
components: {Card}, components: { Card },
mounted () { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.echarts = echarts.init(document.getElementById('homeMain')); this.echarts = echarts.init(document.getElementById('homeMain'))
this.echarts.setOption(this.option, true); this.echarts.setOption(this.option, true)
}) })
}, },
methods: { methods: {
cardClick () { cardClick() {
console.log(1); console.log(1)
}, },
resize () { resize() {
if (this.echarts != null) { if (this.echarts != null) {
this.echarts.resize(); this.echarts.resize()
} }
} }
} }
...@@ -120,50 +124,50 @@ export default { ...@@ -120,50 +124,50 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/style/element-variables.scss'; @import '@/assets/style/element-variables.scss';
.title { .title {
border-left: 1px solid; border-left: 1px solid;
border-left-width: 5px; border-left-width: 5px;
border-left-color: $--color-primary; border-left-color: $--color-primary;
margin-bottom: 20px; margin-bottom: 20px;
font-size: 17px; font-size: 17px;
padding-left: 10px; padding-left: 10px;
}
#homeMain{
height:400px;
width:100%;
} }
.title p { #homeMain {
height: 400px;
width: 100%;
}
.title p {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
font-size: 16px; font-size: 16px;
margin: 0px; margin: 0px;
padding-left: 20px; padding-left: 20px;
} }
.title p span { .title p span {
font-size: 20px; font-size: 20px;
color: $--color-primary; color: $--color-primary;
} }
.item-list { .item-list {
margin: 0px; margin: 0px;
} }
.item-list li { .item-list li {
margin: 10px 0px; margin: 10px 0px;
} }
.item { .item {
height: 48px; height: 48px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
strong.warning { strong.warning {
color: red; color: red;
font-size: 16px; font-size: 16px;
} }
.allCard { .allCard {
display: grid !important; display: grid !important;
/* grid-column-gap: 50px; /* grid-column-gap: 50px;
grid-row-gap: 50px; */ grid-row-gap: 50px; */
...@@ -175,23 +179,20 @@ height:400px; ...@@ -175,23 +179,20 @@ height:400px;
.allCard :nth-child(5) { .allCard :nth-child(5) {
grid-column-start: 1; grid-column-start: 1;
grid-column-end: 5; grid-column-end: 5;
} }
.content{ .content {
height: calc(100% - 45px); height: calc(100% - 45px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-around; justify-content: space-around;
align-items: center; align-items: center;
} }
.num{ .num {
font-size: 40px; font-size: 40px;
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
} }
.describe{ .describe {
text-align: center; text-align: center;
} }
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment