|
@@ -0,0 +1,1425 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <div class="header">
|
|
|
+ <h2>
|
|
|
+ <span>{{ id == undefined ? "新增" : "编辑" }}</span
|
|
|
+ >应用
|
|
|
+ </h2>
|
|
|
+ </div>
|
|
|
+ <div class="center" v-if="id == undefined">
|
|
|
+ <el-form
|
|
|
+ :inline="true"
|
|
|
+ :model="AIform"
|
|
|
+ :rules="rules"
|
|
|
+ ref="AIformRef"
|
|
|
+ class="demo-form-inline"
|
|
|
+ style="margin-top: 15px; margin-left: 50px"
|
|
|
+ label-position="top"
|
|
|
+ label-width="90px"
|
|
|
+ >
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="应用名称:" prop="chat_name">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.chat_name"
|
|
|
+ placeholder="请输入应用名称"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="应用类型:" prop="application_type">
|
|
|
+ <el-select
|
|
|
+ v-model="AIform.application_type"
|
|
|
+ placeholder="应用类型"
|
|
|
+ style="width: 55%"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in appTypeList"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="模型名称:" prop="model_name">
|
|
|
+ <el-select
|
|
|
+ v-model="AIform.model_name"
|
|
|
+ placeholder="请输入选择"
|
|
|
+ style="width: 55%"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in modelNameList"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.name"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="知识库:" prop="knowledge_base_names">
|
|
|
+ <el-select
|
|
|
+ v-model="AIform.knowledge_base_names"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择知识库"
|
|
|
+ style="width: 55%"
|
|
|
+ @change="handleKnowledgeBaseChange"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in kneList"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select> </el-form-item
|
|
|
+ ></el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="文档目录:" prop="document_directories">
|
|
|
+ <el-select
|
|
|
+ v-model="AIform.document_directories"
|
|
|
+ placeholder="请选择文档目录"
|
|
|
+ style="width: 55%"
|
|
|
+ :disabled="!AIform.knowledge_base_names.length"
|
|
|
+ @change="handleDirectoryChange"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(dir, index) in directoryList"
|
|
|
+ :label="dir.name"
|
|
|
+ :value="dir.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select> </el-form-item
|
|
|
+ ></el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="文 档:" prop="documents">
|
|
|
+ <el-select
|
|
|
+ v-model="AIform.documents"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择文档"
|
|
|
+ style="width: 55%"
|
|
|
+ :disabled="isDocumentSelectDisabled"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(doc, index) in documentList"
|
|
|
+ :label="doc.name"
|
|
|
+ :value="doc.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="描 述:">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.role_description"
|
|
|
+ placeholder="请输入描述"
|
|
|
+ type="textarea"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="温度:" prop="temperature">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.temperature"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.temperature"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">控制生成文本的随机性,0为最保守,1为最创新</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="最大token数:" prop="max_tokens">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.max_tokens"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.max_tokens"
|
|
|
+ :min="0"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">限制生成文本的最大长度</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="核采样参数:" prop="top_p">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.top_p"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.top_p"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">控制词汇多样性,1为考虑所有可能性</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="重复内容惩罚:" prop="frequency_penalty">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.frequency_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.frequency_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">防止重复,正值减少重复,负值增加重复</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="新话题偏好:" prop="presence_penalty">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.presence_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.presence_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">正值鼓励新话题,负值保持话题一致性</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="响应格式:" prop="response_format">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.response_format"
|
|
|
+ placeholder="text"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">指定AI响应的格式,如text, json等</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="上下文窗口:" prop="context_window">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.context_window"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.presence_penalty"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">AI考虑的上下文token数量</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="会话ID:" prop="session_id">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.session_id"
|
|
|
+ placeholder="可选"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">特定会话的唯一标识符,如果有的话</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="语言:" prop="language">
|
|
|
+ <el-select v-model="AIform.language" style="width: 55%">
|
|
|
+ <el-option label="中文" value="zh"></el-option>
|
|
|
+ <el-option label="English" value="en"></el-option>
|
|
|
+ <el-option label="日本語" value="ja"></el-option>
|
|
|
+ <el-option label="한국어" value="ko"></el-option>
|
|
|
+ <el-option label="Français" value="fr"></el-option>
|
|
|
+ <el-option label="Deutsch" value="de"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <div class="hint">选择AI响应的主要语言</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="请求超时:" prop="timeout">
|
|
|
+ <el-input-number
|
|
|
+ v-model="AIform.timeout"
|
|
|
+ :min="1"
|
|
|
+ :max="60"
|
|
|
+ ></el-input-number>
|
|
|
+ <div class="hint">设置请求的最大等待时间(秒)</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="角色名称:" prop="role_name">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.role_name"
|
|
|
+ placeholder="例如: 客服专员"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">为AI助手设定一个角色名称</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <!-- <el-col :span="12">
|
|
|
+ <el-form-item label="角色描述:" prop="role_description">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.role_description"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="例如: 你是一位专业的客服专员,负责解答客户关于我们产品和服务的问题。"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">详细描述AI助手的角色和职责</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col> -->
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="角色权限:" prop="role_permissions">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.role_permissions"
|
|
|
+ placeholder='["回答产品问题", "处理退换货请求", "升级复杂问题"]'
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">输入JSON数组,列出AI助手的具体权限</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="自定义变量:" prop="custom_variables">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="AIform.custom_variables"
|
|
|
+ placeholder='{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}'
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">
|
|
|
+ 输入JSON对象,定义可在提示模板中使用的自定义变量
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="自定义提示模板:" prop="custom_prompt">
|
|
|
+ <el-input
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="AIform.custom_prompt"
|
|
|
+ type="textarea"
|
|
|
+ :rows="6"
|
|
|
+ placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
|
|
|
+ 上下文: {context}
|
|
|
+ 人类: {question}
|
|
|
+ AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">
|
|
|
+ 自定义AI助手的回答模板,可使用变量如{context}, {question}等
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
|
|
|
+ <el-switch
|
|
|
+ v-model="AIform.generate_new_api_key"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ >
|
|
|
+ </el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="是否设为默认:" prop="is_default">
|
|
|
+ <el-switch
|
|
|
+ v-model="AIform.is_default"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ >
|
|
|
+ </el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <div class="center" v-else>
|
|
|
+ <el-form
|
|
|
+ :model="editForm"
|
|
|
+ :rules="rules"
|
|
|
+ ref="editFormRef"
|
|
|
+ label-width="120px"
|
|
|
+ style="margin: 20px 50px"
|
|
|
+ label-position="top"
|
|
|
+ >
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="应用名称:" prop="chat_name">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.chat_name"
|
|
|
+ placeholder="请输入应用名称"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <!-- <el-form-item label="模型库:" prop="modelLibrary">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.modelLibrary"
|
|
|
+ placeholder="ollama"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option label="ollama" value="ollama"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item> -->
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="应用类型:" prop="application_type">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.application_type"
|
|
|
+ placeholder="应用类型"
|
|
|
+ style="width: 55%"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in appTypeList"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="模型名称:" prop="model_name">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.model_name"
|
|
|
+ placeholder="请输入选择"
|
|
|
+ style="width: 55%"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in modelNameList"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.name"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <!-- <el-form-item label="模型类型:" prop="model_type">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.model_type"
|
|
|
+ placeholder="请输入选择"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option label="chat" value="chat"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item> -->
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="知识库:" prop="knowledge_base_names">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.knowledge_base_names"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择知识库"
|
|
|
+ style="width: 55%"
|
|
|
+ @change="handleEditKnowledgeBaseChange"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(item, index) in kneList"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="文档目录:" prop="document_directories">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.document_directories"
|
|
|
+ placeholder="请选择文档目录"
|
|
|
+ style="width: 55%"
|
|
|
+ :disabled="
|
|
|
+ !(
|
|
|
+ editForm.knowledge_base_names &&
|
|
|
+ editForm.knowledge_base_names.length
|
|
|
+ )
|
|
|
+ "
|
|
|
+ @change="handleEditDirectoryChange"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(dir, index) in editDirectoryList"
|
|
|
+ :label="dir.name"
|
|
|
+ :value="dir.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="文 档:" prop="documents">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.documents"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择文档"
|
|
|
+ style="width: 55%"
|
|
|
+ :disabled="editForm.document_directories == ''"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="(doc, index) in editDocumentList"
|
|
|
+ :label="doc.name"
|
|
|
+ :value="doc.id"
|
|
|
+ :key="index"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="描 述:">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.role_description"
|
|
|
+ placeholder="请输入描述"
|
|
|
+ type="textarea"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="温度:" prop="temperature">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.temperature"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.temperature"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">控制生成文本的随机性,0为最保守,1为最创新</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="最大token数:" prop="max_tokens">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.max_tokens"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.max_tokens"
|
|
|
+ :min="0"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">限制生成文本的最大长度</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="核采样参数:" prop="top_p">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.top_p"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.top_p"
|
|
|
+ :min="0"
|
|
|
+ :max="1"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">控制词汇多样性,1为考虑所有可能性</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="重复内容惩罚:" prop="frequency_penalty">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.frequency_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.frequency_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">防止重复,正值减少重复,负值增加重复</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="新话题偏好:" prop="presence_penalty">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.presence_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.presence_penalty"
|
|
|
+ :min="-2"
|
|
|
+ :max="2"
|
|
|
+ :step="0.1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">正值鼓励新话题,负值保持话题一致性</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="响应格式:" prop="response_format">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.response_format"
|
|
|
+ placeholder="text"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">指定AI响应的格式,如text, json等</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="上下文窗口:" prop="context_window">
|
|
|
+ <div style="display: flex; justify-content: space-around">
|
|
|
+ <el-slider
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.context_window"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ ></el-slider>
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.presence_penalty"
|
|
|
+ :min="1"
|
|
|
+ :max="4096"
|
|
|
+ :step="1"
|
|
|
+ ></el-input-number>
|
|
|
+ </div>
|
|
|
+ <div class="hint">AI考虑的上下文token数量</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="会话ID:" prop="session_id">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.session_id"
|
|
|
+ placeholder="可选"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">特定会话的唯一标识符,如果有的话</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="语言:" prop="language">
|
|
|
+ <el-select v-model="editForm.language" style="width: 55%">
|
|
|
+ <el-option label="中文" value="zh"></el-option>
|
|
|
+ <el-option label="English" value="en"></el-option>
|
|
|
+ <el-option label="日本語" value="ja"></el-option>
|
|
|
+ <el-option label="한국어" value="ko"></el-option>
|
|
|
+ <el-option label="Français" value="fr"></el-option>
|
|
|
+ <el-option label="Deutsch" value="de"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <div class="hint">选择AI响应的主要语言</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="请求超时:" prop="timeout">
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.timeout"
|
|
|
+ :min="1"
|
|
|
+ :max="60"
|
|
|
+ ></el-input-number>
|
|
|
+ <div class="hint">设置请求的最大等待时间(秒)</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="角色名称:" prop="role_name">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.role_name"
|
|
|
+ placeholder="例如: 客服专员"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">为AI助手设定一个角色名称</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="角色权限:" prop="role_permissions">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.role_permissions"
|
|
|
+ placeholder='["回答产品问题", "处理退换货请求", "升级复杂问题"]'
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">输入JSON数组,列出AI助手的具体权限</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <!-- <el-col :span="12">
|
|
|
+ <el-form-item label="角色描述:" prop="role_description">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.role_description"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="例如: 你是一位专业的客服专员,负责解答客户关于我们产品和服务的问题。"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">详细描述AI助手的角色和职责</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col> -->
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="自定义变量:" prop="custom_variables">
|
|
|
+ <el-input
|
|
|
+ style="width: 55%"
|
|
|
+ v-model="editForm.custom_variables"
|
|
|
+ placeholder='{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}'
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">
|
|
|
+ 输入JSON对象,定义可在提示模板中使用的自定义变量
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="自定义提示模板:" prop="custom_prompt">
|
|
|
+ <el-input
|
|
|
+ style="width: 60%"
|
|
|
+ v-model="editForm.custom_prompt"
|
|
|
+ type="textarea"
|
|
|
+ :rows="6"
|
|
|
+ placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
|
|
|
+ 上下文: {context}
|
|
|
+ 人类: {question}
|
|
|
+ AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}"
|
|
|
+ ></el-input>
|
|
|
+ <div class="hint">
|
|
|
+ 自定义AI助手的回答模板,可使用变量如{context}, {question}等
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="24">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
|
|
|
+ <el-switch
|
|
|
+ v-model="editForm.generate_new_api_key"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ >
|
|
|
+ </el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="是否设为默认:" prop="is_default">
|
|
|
+ <el-switch
|
|
|
+ v-model="editForm.is_default"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ >
|
|
|
+ </el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <div class="footer">
|
|
|
+ <el-button @click="cancelApplication">取 消</el-button>
|
|
|
+ <el-button type="primary" v-if="id==undefined" @click="generateApplication"
|
|
|
+ >生成应用</el-button
|
|
|
+ >
|
|
|
+ <el-button type="primary" v-else @click="submitEdit"
|
|
|
+ >确认修改</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import axios from "axios";
|
|
|
+import {
|
|
|
+ modelList,
|
|
|
+ listBuckets,
|
|
|
+ selectTypeList,
|
|
|
+ getBucketContents,
|
|
|
+ configSave,
|
|
|
+ configList,
|
|
|
+ configDelete,
|
|
|
+ application_types,
|
|
|
+ set_default,
|
|
|
+ configurationInfo
|
|
|
+} from "@/api/knowledge";
|
|
|
+export default {
|
|
|
+ /*组件参数 接收来自父组件的数据*/
|
|
|
+ props: {},
|
|
|
+ /*局部注册的组件*/
|
|
|
+ components: {},
|
|
|
+ /*组件状态值*/
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ AIform: {
|
|
|
+ chat_name: "",
|
|
|
+ modelLibrary: "ollama",
|
|
|
+ model_type: "chat",
|
|
|
+ model_name: "",
|
|
|
+ knowledge_base_names: [],
|
|
|
+ document_directories: [],
|
|
|
+ documents: [],
|
|
|
+ temperature: 0.7,
|
|
|
+ max_tokens: 150,
|
|
|
+ top_p: 1.0,
|
|
|
+ frequency_penalty: 0.0,
|
|
|
+ presence_penalty: 0.0,
|
|
|
+ response_format: "text",
|
|
|
+ context_window: 2048,
|
|
|
+ user_id: "user123",
|
|
|
+ session_id: "session456",
|
|
|
+ language: "en",
|
|
|
+ timeout: 30,
|
|
|
+ role_name: "Admin",
|
|
|
+ role_description: "",
|
|
|
+ role_permissions: "",
|
|
|
+ custom_variables: "",
|
|
|
+ custom_prompt: "",
|
|
|
+ application_type: "",
|
|
|
+ is_default: false,
|
|
|
+ generate_new_api_key: true,
|
|
|
+ },
|
|
|
+ /* 模型库 */
|
|
|
+ modelList: [],
|
|
|
+ /* 模型类型 */
|
|
|
+ modelTypeList: [],
|
|
|
+ /* 模型名称 */
|
|
|
+ modelNameList: [],
|
|
|
+ /* 知识库 */
|
|
|
+ kneList: [],
|
|
|
+ /* 文档目录 */
|
|
|
+ directoryList: [],
|
|
|
+ /* 文档 */
|
|
|
+ documentList: [],
|
|
|
+ bucket_id: "",
|
|
|
+ rules: {
|
|
|
+ chat_name: [
|
|
|
+ { required: true, message: "请填写应名称", trigger: "blur" },
|
|
|
+ ],
|
|
|
+ model_name: [
|
|
|
+ { required: true, message: "请选择模型名称", trigger: "change" },
|
|
|
+ ],
|
|
|
+ knowledge_base_names: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: "请选择至少一个知识库",
|
|
|
+ trigger: "change",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ /* document_directories: [
|
|
|
+ { required: true, message: '请选择文档目录', trigger: 'change' }
|
|
|
+ ], */
|
|
|
+ documents: [
|
|
|
+ { required: true, message: "请选择至少一个文档", trigger: "change" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ /* 配置类型列表 */
|
|
|
+ appTypeList: [],
|
|
|
+ id: "",
|
|
|
+ editForm: {
|
|
|
+ // 复制 AIform 的结构,但初始值为空
|
|
|
+ chat_name: "",
|
|
|
+ modelLibrary: "ollama",
|
|
|
+ model_type: "chat",
|
|
|
+ model_name: "",
|
|
|
+ knowledge_base_names: [],
|
|
|
+ document_directories: [],
|
|
|
+ documents: [],
|
|
|
+ temperature: 0.7,
|
|
|
+ max_tokens: 150,
|
|
|
+ top_p: 1.0,
|
|
|
+ frequency_penalty: 0.0,
|
|
|
+ presence_penalty: 0.0,
|
|
|
+ response_format: "text",
|
|
|
+ context_window: 2048,
|
|
|
+ user_id: "user123",
|
|
|
+ session_id: "session456",
|
|
|
+ language: "en",
|
|
|
+ timeout: 30,
|
|
|
+ role_name: "Admin",
|
|
|
+ role_description: "",
|
|
|
+ role_permissions: "",
|
|
|
+ custom_variables: "",
|
|
|
+ custom_prompt: "",
|
|
|
+ application_type: "",
|
|
|
+ is_default: false,
|
|
|
+ generate_new_api_key: true,
|
|
|
+ },
|
|
|
+ editDirectoryList: [],
|
|
|
+ editDocumentList: [],
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ /*计算属性*/
|
|
|
+ computed: {
|
|
|
+ getKnowledgeBaseNames() {
|
|
|
+ const names = this.AIform.knowledge_base_names.map(
|
|
|
+ (id) => this.kneList.find((item) => item.id === id)?.name || id
|
|
|
+ );
|
|
|
+ if (names.length <= 2) {
|
|
|
+ return names.join(", ");
|
|
|
+ } else {
|
|
|
+ return `${names[0]}, ${names[1]} 等${names.length}个`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getDocumentNames() {
|
|
|
+ const names = this.AIform.documents.map(
|
|
|
+ (id) => this.documentList.find((item) => item.id === id)?.name || id
|
|
|
+ );
|
|
|
+ if (names.length <= 2) {
|
|
|
+ return names.join(", ");
|
|
|
+ } else {
|
|
|
+ return `${names[0]}, ${names[1]} 等${names.length}个`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isDocumentSelectDisabled() {
|
|
|
+ return (
|
|
|
+ !this.AIform.knowledge_base_names.length ||
|
|
|
+ this.AIform.document_directories == ""
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ /*侦听器*/
|
|
|
+ watch: {},
|
|
|
+
|
|
|
+ /*
|
|
|
+ * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
|
|
|
+ * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
|
|
|
+ */
|
|
|
+ mounted() {
|
|
|
+ /* 初始化列表 */
|
|
|
+ this.init();
|
|
|
+ this.id = this.$route.query.id;
|
|
|
+ if(this.id!==undefined){
|
|
|
+ this.initView()
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /*组件方法*/
|
|
|
+ methods: {
|
|
|
+ /* 编辑 */
|
|
|
+ /* 编辑确认修改 */
|
|
|
+ submitEdit(){
|
|
|
+ this.$refs.editFormRef.validate(async (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ try {
|
|
|
+ const convertedForm = { ...this.editForm };
|
|
|
+ console.log(this.editForm);
|
|
|
+ // 转换知识库、文档目录和文档的ID为名称
|
|
|
+ /* convertedForm.knowledge_base_names = this.safeGetNamesByIds(this.editForm.knowledge_base_names, this.kneList); */
|
|
|
+ /* convertedForm.documents = this.safeGetNamesByIds(this.editForm.documents, this.editDocumentList); */
|
|
|
+ convertedForm.document_directories = this.safeGetNamesByIds(this.editForm.document_directories, this.editDirectoryList);
|
|
|
+ if (convertedForm.document_directories.length === 0) {
|
|
|
+ convertedForm.document_directories = ['全部'];
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Converted form for edit:', convertedForm);
|
|
|
+
|
|
|
+ const response = await axios.post(`${process.env.VUE_APP_BASE_API}/chatbot/configuration/update/`, convertedForm, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ this.$message.success('应用更新成功');
|
|
|
+ this.$router.push({
|
|
|
+ path: "/knowledge/chatPage/index",
|
|
|
+ }); //跳转到聊天应用页面
|
|
|
+ /* this.editDialogVisible = false; */
|
|
|
+ /* this.fetchApplicationList(); */
|
|
|
+ } else {
|
|
|
+ this.$message.error(response.data.message || '应用更新失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error updating application:', error);
|
|
|
+ this.$message.error('应用更新失败,请稍后重试');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.$message.error('请填写所有必填字段');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ handleEditKnowledgeBaseChange(val) {
|
|
|
+ // 重置目录和文档选择
|
|
|
+ this.editForm.document_directories = [];
|
|
|
+ this.editForm.documents = [];
|
|
|
+ this.editDocumentList = [];
|
|
|
+ // 加载新选择的知识库对应的目录列表
|
|
|
+ this.loadEditDirectoryList(val);
|
|
|
+ },
|
|
|
+ handleEditDirectoryChange(val) {
|
|
|
+ // 重置文档选择
|
|
|
+ this.editForm.documents = [];
|
|
|
+
|
|
|
+ // 加载选中目录对应的文档列表
|
|
|
+ this.loadEditDocumentList(val);
|
|
|
+ },
|
|
|
+ /* 知识库选择监听 */
|
|
|
+ async loadEditDirectoryList(val) {
|
|
|
+ this.editDirectoryList = []; // 清空现有目录列表
|
|
|
+ let totalDocuments = 0;
|
|
|
+ let otherFolderCount = 0;
|
|
|
+
|
|
|
+ for (const kbName of val) {
|
|
|
+ // 根据知识库名称找到对应的 ID
|
|
|
+ const kbId = this.kneList.find(kb => kb.name === kbName)?.id;
|
|
|
+
|
|
|
+ /* if (!kbId) {
|
|
|
+ console.error(`No matching knowledge base found for name: ${kbName}`);
|
|
|
+ continue;
|
|
|
+ } */
|
|
|
+
|
|
|
+ const typeForm = {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ kb_id: kbName,
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await selectTypeList(typeForm);
|
|
|
+ if (res.data) {
|
|
|
+ this.editDirectoryList = [...new Set([...this.editDirectoryList, ...res.data.dataList])];
|
|
|
+
|
|
|
+ res.data.dataList.forEach(folder => {
|
|
|
+ if (folder.id === "other") {
|
|
|
+ otherFolderCount += folder.document_count || 0;
|
|
|
+ } else {
|
|
|
+ totalDocuments += folder.document_count || 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`Error loading directory list for kb_id ${kbId}:`, error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.editDirectoryList.unshift({
|
|
|
+ id: "001",
|
|
|
+ name: "全部",
|
|
|
+ document_count: totalDocuments + otherFolderCount,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 监听目录选择 */
|
|
|
+ loadEditDocumentList(val) {
|
|
|
+ // 找到选中目录的名称
|
|
|
+ const selectedDirectoryName = val;
|
|
|
+ const id= this.directoryList.find(el=>el.id==val)
|
|
|
+ // 从 kneList 中找到对应的知识库
|
|
|
+ const selectedKnowledgeBase = this.kneList.find(kb =>
|
|
|
+ kb.id === this.editForm.knowledge_base_names[0]
|
|
|
+ );
|
|
|
+
|
|
|
+ /* if (!selectedKnowledgeBase) {
|
|
|
+ console.error('No matching knowledge base found');
|
|
|
+ return;
|
|
|
+ } */
|
|
|
+ let queryForm = {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ bucket_id:selectedKnowledgeBase.id,
|
|
|
+ doc_type_id: selectedDirectoryName === '001' ? '' : this.getDirectoryIdByName(selectedDirectoryName),
|
|
|
+ };
|
|
|
+
|
|
|
+ getBucketContents(queryForm).then(res => {
|
|
|
+ this.editDocumentList = res.data.documents;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 添加一个辅助方法来根据目录名称获取目录ID
|
|
|
+ getDirectoryIdByName(directoryName) {
|
|
|
+ const directory = this.editDirectoryList.find(dir => dir.name === directoryName);
|
|
|
+ return directory ? directory.id : null;
|
|
|
+ },
|
|
|
+
|
|
|
+ /* 新增 */
|
|
|
+ /* 重置表单 */
|
|
|
+ resetForm() {
|
|
|
+ if (this.$refs.AIformRef) {
|
|
|
+ this.$refs.AIformRef.resetFields();
|
|
|
+ }
|
|
|
+ this.AIform = {
|
|
|
+ chat_name: "",
|
|
|
+ modelLibrary: "ollama",
|
|
|
+ model_type: "chat",
|
|
|
+ model_name: "",
|
|
|
+ knowledge_base_names: [],
|
|
|
+ document_directories: [],
|
|
|
+ documents: [],
|
|
|
+ temperature: 0.7,
|
|
|
+ max_tokens: 150,
|
|
|
+ top_p: 1.0,
|
|
|
+ frequency_penalty: 0.0,
|
|
|
+ presence_penalty: 0.0,
|
|
|
+ response_format: "",
|
|
|
+ context_window: 2048,
|
|
|
+ user_id: "",
|
|
|
+ session_id: "",
|
|
|
+ language: "en",
|
|
|
+ timeout: 30,
|
|
|
+ role_name: "Admin",
|
|
|
+ role_description: "",
|
|
|
+ role_permissions: "",
|
|
|
+ custom_variables: "",
|
|
|
+ custom_prompt: "",
|
|
|
+ application_type: "",
|
|
|
+ is_default: false,
|
|
|
+ generate_new_api_key: true,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /* 取消 */
|
|
|
+ cancelApplication() {
|
|
|
+ this.$confirm("确认取消?未保存的更改将会丢失。")
|
|
|
+ .then((_) => {
|
|
|
+ this.resetForm();
|
|
|
+ this.$router.push({
|
|
|
+ path: "/knowledge/chatPage/index",
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch((_) => {});
|
|
|
+ },
|
|
|
+ /* 新增聊天应用 */
|
|
|
+ generateApplication() {
|
|
|
+ this.$refs.AIformRef.validate(async (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ try {
|
|
|
+ // 创建一个新对象来存储转换后的数据
|
|
|
+ const convertedForm = { ...this.AIform };
|
|
|
+
|
|
|
+ // 将知识库 ID 转换为名称
|
|
|
+ convertedForm.knowledge_base_names = this.safeGetNamesByIds(
|
|
|
+ this.AIform.knowledge_base_names,
|
|
|
+ this.kneList
|
|
|
+ );
|
|
|
+
|
|
|
+ // 将文档 ID 转换为名称
|
|
|
+ convertedForm.documents = this.safeGetNamesByIds(
|
|
|
+ this.AIform.documents,
|
|
|
+ this.documentList
|
|
|
+ );
|
|
|
+
|
|
|
+ // 将文档目录 ID 转换为名称,如果为空则传递 '全部'
|
|
|
+ convertedForm.document_directories = this.safeGetNamesByIds(
|
|
|
+ this.AIform.document_directories,
|
|
|
+ this.directoryList
|
|
|
+ );
|
|
|
+ if (convertedForm.document_directories.length === 0) {
|
|
|
+ convertedForm.document_directories = ["全部"];
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log("Converted form:", convertedForm);
|
|
|
+
|
|
|
+ // 使用 axios 发送 POST 请求
|
|
|
+ const response = await axios.post(
|
|
|
+ `${process.env.VUE_APP_BASE_API}/chatbot/configCreart/`,
|
|
|
+ convertedForm,
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "application/json",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ this.$message.success("应用生成成功");
|
|
|
+ this.resetForm();
|
|
|
+ /* this.fetchApplicationList(); */
|
|
|
+ this.$router.push({
|
|
|
+ path: "/knowledge/chatPage/index",
|
|
|
+ }); //跳转到聊天应用页面
|
|
|
+ // 可选:重定向到新应用或更新 UI
|
|
|
+ } else {
|
|
|
+ this.$message.error(response.data.message || "应用生成失败");
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Error generating application:", error);
|
|
|
+ this.$message.error("应用生成失败,请稍后重试");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.$message.error("请填写所有必填字段");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 数据处理方法 */
|
|
|
+ // 安全的辅助方法:通过 ID 数组获取名称数组
|
|
|
+ safeGetNamesByIds(ids, list) {
|
|
|
+ if (!Array.isArray(ids)) {
|
|
|
+ console.warn("Expected an array of ids, but received:", ids);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ return ids
|
|
|
+ .map((id) => {
|
|
|
+ if (id === "001") {
|
|
|
+ return "全部";
|
|
|
+ }
|
|
|
+ const item = list.find((item) => item.id === id);
|
|
|
+ return item ? item.name : "";
|
|
|
+ })
|
|
|
+ .filter((name) => name !== "");
|
|
|
+ },
|
|
|
+ /* 知识库选择监听联动 */
|
|
|
+ handleKnowledgeBaseChange(val) {
|
|
|
+ // 重置目录和文档选择
|
|
|
+ this.AIform.document_directories = [];
|
|
|
+ this.AIform.documents = [];
|
|
|
+ this.documentList = [];
|
|
|
+ this.bucket_id = val[0];
|
|
|
+ // 根据选中的知识库加载目录列表
|
|
|
+ // 这里需要调用后端 API 来获取目录列表
|
|
|
+ this.loadDirectoryList(val);
|
|
|
+ },
|
|
|
+ handleDirectoryChange(val) {
|
|
|
+ // 重置文档选择
|
|
|
+ this.AIform.documents = [];
|
|
|
+
|
|
|
+ // 根据选中的目录加载文档列表
|
|
|
+ // 这里需要调用后端 API 来获取文档列表
|
|
|
+ this.loadDocumentList(val);
|
|
|
+ },
|
|
|
+ async loadDirectoryList(val) {
|
|
|
+ this.directoryList = []; // 清空现有目录列表
|
|
|
+ let totalDocuments = 0;
|
|
|
+ let otherFolderCount = 0;
|
|
|
+
|
|
|
+ for (const kbId of val) {
|
|
|
+ const typeForm = {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ kb_id: kbId,
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await selectTypeList(typeForm);
|
|
|
+ // 假设 res.data 包含目录列表
|
|
|
+ if (res.data) {
|
|
|
+ // 将新的目录添加到列表中,避免重复
|
|
|
+ this.directoryList = [
|
|
|
+ ...new Set([...this.directoryList, ...res.data.dataList]),
|
|
|
+ ];
|
|
|
+ console.log(res.data.dataList);
|
|
|
+ // 计算总文档数和其他文件夹数量
|
|
|
+ res.data.dataList.forEach((folder) => {
|
|
|
+ if (folder.id === "other") {
|
|
|
+ otherFolderCount += folder.document_count || 0;
|
|
|
+ } else {
|
|
|
+ totalDocuments += folder.document_count || 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(
|
|
|
+ `Error loading directory list for kb_id ${kbId}:`,
|
|
|
+ error
|
|
|
+ );
|
|
|
+ // 可以在这里添加错误处理,比如显示一个错误提示
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 在列表开头插入"全部"选项
|
|
|
+ this.directoryList.unshift({
|
|
|
+ id: "001",
|
|
|
+ name: "全部",
|
|
|
+ /* document_count: totalDocuments + otherFolderCount, */
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 目录选择监听 */
|
|
|
+ handleDirectoryChange(val) {
|
|
|
+ // 重置文档选择
|
|
|
+ this.AIform.documents = [];
|
|
|
+
|
|
|
+ // 根据选中的目录加载文档列表
|
|
|
+ // 这里需要调用后端 API 来获取文档列表
|
|
|
+ this.loadDocumentList(val);
|
|
|
+ },
|
|
|
+ loadDocumentList(val) {
|
|
|
+ const id = this.directoryList.find((el) => el.id == val);
|
|
|
+ let queryForm = {
|
|
|
+ page: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ bucket_id: val == "001" ? this.bucket_id : id.kb_id, //this.bucket_id,
|
|
|
+ doc_type_id: val == "001" ? "" : val,
|
|
|
+ };
|
|
|
+ getBucketContents(queryForm).then((res) => {
|
|
|
+ this.documentList = res.data.documents;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 获取列表 */
|
|
|
+ init() {
|
|
|
+ /* 模型库 */
|
|
|
+ modelList({ model_type: "model" }).then((res) => {
|
|
|
+ this.modelNameList = res.data;
|
|
|
+ });
|
|
|
+ /* 知识库 */
|
|
|
+ listBuckets({ user_id: this.$store.state.user.id }).then((res) => {
|
|
|
+ this.kneList = res.data;
|
|
|
+ });
|
|
|
+ /* 应用列表 */
|
|
|
+ configList().then((res) => {
|
|
|
+ this.knowledgeBases = res.data;
|
|
|
+ console.log(res);
|
|
|
+ });
|
|
|
+ /* 获取应用类型 */
|
|
|
+ application_types().then((res) => {
|
|
|
+ if (res.status !== 200) return;
|
|
|
+ this.appTypeList = res.data.application_types;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /* 编辑查看 */
|
|
|
+ initView(){
|
|
|
+ // 根据 card 的数据填充 editForm
|
|
|
+ this.editForm = JSON.parse(JSON.stringify(this.$route.query.card)); // 深拷贝以避免直接修改原对象
|
|
|
+
|
|
|
+ // 处理 knowledge_base_names
|
|
|
+ this.editForm.knowledge_base_names = this.editForm.knowledge_base_names.map(name => {
|
|
|
+ const kb = this.kneList.find(kb => kb.name === name);
|
|
|
+ return kb ? kb.id : name; // 如果找不到对应的知识库,保留原名称
|
|
|
+ });
|
|
|
+
|
|
|
+ // 处理 role_permissions
|
|
|
+ if (this.editForm.role_permissions) {
|
|
|
+ if (typeof this.editForm.role_permissions !== 'string') {
|
|
|
+ try {
|
|
|
+ this.editForm.role_permissions = JSON.stringify(this.editForm.role_permissions);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error stringifying role_permissions:', error);
|
|
|
+ this.editForm.role_permissions = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.editForm.role_permissions = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理 custom_variables
|
|
|
+ if (this.editForm.custom_variables) {
|
|
|
+ this.editForm.custom_variables = JSON.stringify(this.editForm.custom_variables);
|
|
|
+ } else {
|
|
|
+ this.editForm.custom_variables = "";
|
|
|
+ }
|
|
|
+ console.log(this.editForm);
|
|
|
+
|
|
|
+ // 加载知识库对应的目录列表
|
|
|
+ this.loadEditDirectoryList(this.editForm.knowledge_base_names);
|
|
|
+ }
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang='scss' scoped>
|
|
|
+.header {
|
|
|
+ margin-left: 20px;
|
|
|
+ h2 {
|
|
|
+ margin-bottom: 0px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.center {
|
|
|
+ margin: 10px;
|
|
|
+ ::v-deep .el-form--inline .el-form-item {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+ ::v-deep .el-input-number--medium {
|
|
|
+ width: 150px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin: 0 50px 20px;
|
|
|
+}
|
|
|
+</style>
|