index.vue 90 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635
  1. <template>
  2. <div>
  3. <div class="header">
  4. <h2>
  5. <span>{{
  6. type == "add" ? "新增" : type == "edit" ? "编辑" : "查看"
  7. }}</span
  8. >应用
  9. </h2>
  10. </div>
  11. <div class="center" v-if="type == 'add'">
  12. <el-form
  13. :inline="true"
  14. :model="AIform"
  15. :rules="rules"
  16. ref="AIformRef"
  17. class="demo-form-inline"
  18. style="margin-top: 15px; margin-left: 50px"
  19. label-position="top"
  20. label-width="90px"
  21. >
  22. <el-row :gutter="24">
  23. <el-col :span="12">
  24. <el-form-item label="应用名称:" prop="chat_name">
  25. <el-input
  26. style="width: 55%"
  27. v-model="AIform.chat_name"
  28. placeholder="请输入应用名称"
  29. ></el-input>
  30. </el-form-item>
  31. </el-col>
  32. <el-col :span="12">
  33. <el-form-item label="应用类型:" prop="application_type">
  34. <el-select
  35. v-model="AIform.application_type"
  36. placeholder="应用类型"
  37. style="width: 55%"
  38. clearable
  39. >
  40. <el-option
  41. v-for="(item, index) in appTypeList"
  42. :label="item.label"
  43. :value="item.value"
  44. :key="index"
  45. ></el-option>
  46. </el-select>
  47. </el-form-item>
  48. </el-col>
  49. </el-row>
  50. <el-row :gutter="24">
  51. <el-col :span="12">
  52. <el-form-item label="模型名称:" prop="model_name">
  53. <el-select
  54. v-model="AIform.model_name"
  55. placeholder="请输入选择"
  56. style="width: 55%"
  57. clearable
  58. >
  59. <el-option
  60. v-for="(item, index) in modelNameList"
  61. :label="item.name"
  62. :value="item.name"
  63. :key="index"
  64. ></el-option>
  65. </el-select>
  66. </el-form-item>
  67. </el-col>
  68. <el-col :span="12">
  69. <el-form-item label="知识库:" prop="knowledge_base_names">
  70. <el-select
  71. v-model="AIform.knowledge_base_names"
  72. multiple
  73. placeholder="请选择知识库"
  74. style="width: 55%"
  75. @change="handleKnowledgeBaseChange"
  76. clearable
  77. filterable
  78. >
  79. <el-option
  80. v-for="(item, index) in kneList"
  81. :label="item.name"
  82. :value="item.id"
  83. :key="index"
  84. ></el-option>
  85. </el-select> </el-form-item
  86. ></el-col>
  87. </el-row>
  88. <el-row :gutter="24">
  89. <el-col :span="12">
  90. <el-form-item label="文档/目录:" prop="">
  91. <el-radio v-model="radio" label="1">目录</el-radio>
  92. <el-radio v-model="radio" label="2">文档</el-radio>
  93. </el-form-item>
  94. <!-- --></el-col
  95. >
  96. <el-col :span="12" v-if="radio == '1'">
  97. <el-form-item label="文档目录:" prop="document_directories">
  98. <el-select
  99. v-model="AIform.document_directories"
  100. placeholder="请选择文档目录"
  101. style="width: 55%"
  102. :disabled="!AIform.knowledge_base_names.length"
  103. @change="handleDirectoryChange"
  104. clearable
  105. filterable
  106. >
  107. <el-option
  108. v-for="(dir, index) in directoryList"
  109. :label="dir.name"
  110. :value="dir.id"
  111. :key="index"
  112. ></el-option>
  113. </el-select>
  114. </el-form-item>
  115. </el-col>
  116. <!-- Show document selection for both cases -->
  117. <el-col :span="12" v-else>
  118. <el-form-item label="文 档:" prop="documents">
  119. <el-select
  120. v-model="AIform.documents"
  121. multiple
  122. placeholder="请选择文档"
  123. style="width: 55%"
  124. :disabled="isDocumentSelectDisabled"
  125. clearable
  126. filterable
  127. remote
  128. :remote-method="remoteSearch"
  129. :loading="loading"
  130. @change="handleDocumentChange"
  131. >
  132. <el-option label="全部" value="all"></el-option>
  133. <el-option
  134. v-for="(doc) in documentList"
  135. :label="doc.name"
  136. :value="doc.id"
  137. :key="`doc-${doc.id}`"
  138. ></el-option>
  139. </el-select>
  140. <div class="hint">请输入文档名称搜索</div>
  141. </el-form-item>
  142. </el-col>
  143. </el-row>
  144. <el-row :gutter="24">
  145. <el-col :span="12">
  146. <el-form-item label="应用图标:">
  147. <!-- aiagent_icon -->
  148. <el-upload
  149. class="avatar-uploader"
  150. :action="uploadUrl"
  151. method="post"
  152. :data="params"
  153. :show-file-list="false"
  154. :on-success="onUploadAddSuccess"
  155. >
  156. <img
  157. v-if="
  158. AIform.aiagent_icon != null && AIform.aiagent_icon != ''
  159. "
  160. :src="AIform.aiagent_icon"
  161. class="avatar"
  162. />
  163. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  164. <!-- <svg-icon
  165. icon-class="header"
  166. style="height: 70px; width: 70px"
  167. v-else
  168. /> -->
  169. </el-upload>
  170. </el-form-item></el-col
  171. >
  172. <el-col :span="12">
  173. <el-form-item label="描 述:">
  174. <el-input
  175. style="width: 55%"
  176. v-model="AIform.role_description"
  177. placeholder="请输入描述"
  178. type="textarea"
  179. ></el-input>
  180. </el-form-item>
  181. </el-col>
  182. </el-row>
  183. <el-row :gutter="24">
  184. <el-col :span="12">
  185. <el-form-item label="温度:" prop="temperature">
  186. <div style="display: flex; justify-content: space-around">
  187. <el-slider
  188. style="width: 60%"
  189. v-model="AIform.temperature"
  190. :min="0"
  191. :max="1"
  192. :step="0.1"
  193. ></el-slider>
  194. <el-input-number
  195. v-model="AIform.temperature"
  196. :min="0"
  197. :max="1"
  198. ></el-input-number>
  199. </div>
  200. <div class="hint">控制生成文本的随机性,0为最保守,1为最创新</div>
  201. </el-form-item>
  202. </el-col>
  203. <el-col :span="12">
  204. <el-form-item label="最大token数:" prop="max_tokens">
  205. <div style="display: flex; justify-content: space-around">
  206. <el-slider
  207. style="width: 60%"
  208. v-model="AIform.max_tokens"
  209. :min="1"
  210. :max="4096"
  211. :step="1"
  212. ></el-slider>
  213. <el-input-number
  214. v-model="AIform.max_tokens"
  215. :min="0"
  216. :max="4096"
  217. :step="1"
  218. ></el-input-number>
  219. </div>
  220. <div class="hint">限制生成文本的最大长度</div>
  221. </el-form-item>
  222. </el-col>
  223. </el-row>
  224. <el-row :gutter="24">
  225. <el-col :span="12">
  226. <el-form-item label="核采样参数:" prop="top_p">
  227. <div style="display: flex; justify-content: space-around">
  228. <el-slider
  229. style="width: 60%"
  230. v-model="AIform.top_p"
  231. :min="0"
  232. :max="1"
  233. :step="0.1"
  234. ></el-slider>
  235. <el-input-number
  236. v-model="AIform.top_p"
  237. :min="0"
  238. :max="1"
  239. :step="0.1"
  240. ></el-input-number>
  241. </div>
  242. <div class="hint">控制词汇多样性,1为考虑所有可能性</div>
  243. </el-form-item>
  244. </el-col>
  245. <el-col :span="12">
  246. <el-form-item label="重复内容惩罚:" prop="frequency_penalty">
  247. <div style="display: flex; justify-content: space-around">
  248. <el-slider
  249. style="width: 60%"
  250. v-model="AIform.frequency_penalty"
  251. :min="0"
  252. :max="2"
  253. :step="0.1"
  254. ></el-slider>
  255. <el-input-number
  256. v-model="AIform.frequency_penalty"
  257. :min="0"
  258. :max="2"
  259. :step="0.1"
  260. ></el-input-number>
  261. </div>
  262. <div class="hint">防止重复,正值减少重复,负值增加重复</div>
  263. </el-form-item>
  264. </el-col>
  265. </el-row>
  266. <el-row :gutter="24">
  267. <el-col :span="12">
  268. <el-form-item label="新话题偏好:" prop="presence_penalty">
  269. <div style="display: flex; justify-content: space-around">
  270. <el-slider
  271. style="width: 60%"
  272. v-model="AIform.presence_penalty"
  273. :min="0"
  274. :max="1"
  275. :step="0.1"
  276. ></el-slider>
  277. <el-input-number
  278. v-model="AIform.presence_penalty"
  279. :min="0"
  280. :max="1"
  281. :step="0.1"
  282. ></el-input-number>
  283. </div>
  284. <div class="hint">正值鼓励新话题,负值保持话题一致性</div>
  285. </el-form-item>
  286. </el-col>
  287. <el-col :span="12">
  288. <el-form-item label="响应格式:" prop="response_format">
  289. <el-input
  290. style="width: 55%"
  291. v-model="AIform.response_format"
  292. placeholder="text"
  293. ></el-input>
  294. <div class="hint">指定AI响应的格式,如text, json等</div>
  295. </el-form-item>
  296. </el-col>
  297. </el-row>
  298. <el-row :gutter="24">
  299. <el-col :span="12">
  300. <el-form-item label="上下文窗口:" prop="context_window">
  301. <div style="display: flex; justify-content: space-around">
  302. <el-slider
  303. style="width: 60%"
  304. v-model="AIform.context_window"
  305. :min="1"
  306. :max="10000"
  307. ></el-slider>
  308. <el-input-number
  309. v-model="AIform.context_window"
  310. :min="1"
  311. :max="10000"
  312. :step="1"
  313. ></el-input-number>
  314. </div>
  315. <div class="hint">AI考虑的上下文token数量</div>
  316. </el-form-item>
  317. </el-col>
  318. <!-- <el-col :span="12">
  319. <el-form-item label="会话ID:" prop="session_id">
  320. <el-input
  321. style="width: 55%"
  322. v-model="AIform.session_id"
  323. placeholder="可选"
  324. ></el-input>
  325. <div class="hint">特定会话的唯一标识符,如果有的话</div>
  326. </el-form-item>
  327. </el-col> -->
  328. </el-row>
  329. <el-row :gutter="24">
  330. <el-col :span="12">
  331. <el-form-item label="语言:" prop="language">
  332. <el-select v-model="AIform.language" style="width: 55%">
  333. <el-option label="中文" value="zh"></el-option>
  334. <el-option label="English" value="en"></el-option>
  335. <el-option label="日本語" value="ja"></el-option>
  336. <el-option label="한국어" value="ko"></el-option>
  337. <el-option label="Français" value="fr"></el-option>
  338. <el-option label="Deutsch" value="de"></el-option>
  339. </el-select>
  340. <div class="hint">选择AI响应的主要语言</div>
  341. </el-form-item>
  342. </el-col>
  343. <el-col :span="12">
  344. <el-form-item label="请求超时:" prop="timeout">
  345. <el-input-number
  346. v-model="AIform.timeout"
  347. :min="1"
  348. :max="60"
  349. ></el-input-number>
  350. <div class="hint">设置请求的最大等待时间(秒)</div>
  351. </el-form-item>
  352. </el-col>
  353. </el-row>
  354. <el-row :gutter="24">
  355. <el-col :span="12">
  356. <el-form-item label="角色名称:" prop="role_name">
  357. <el-input
  358. style="width: 55%"
  359. v-model="AIform.role_name"
  360. placeholder="例如: 客服专员"
  361. ></el-input>
  362. <div class="hint">为AI助手设定一个角色名称</div>
  363. </el-form-item>
  364. </el-col>
  365. <!-- <el-col :span="12">
  366. <el-form-item label="角色描述:" prop="role_description">
  367. <el-input
  368. style="width: 55%"
  369. v-model="AIform.role_description"
  370. type="textarea"
  371. :rows="3"
  372. placeholder="例如: 你是一位专业的客服专员,负责解答客户关于我们产品和服务的问题。"
  373. ></el-input>
  374. <div class="hint">详细描述AI助手的角色和职责</div>
  375. </el-form-item>
  376. </el-col> -->
  377. <el-col :span="12">
  378. <el-form-item label="角色权限:" prop="role_permissions">
  379. <el-input
  380. style="width: 55%"
  381. v-model="AIform.role_permissions"
  382. placeholder='["回答产品问题", "处理退换货请求", "升级复杂问题"]'
  383. ></el-input>
  384. <div class="hint">输入JSON数组,列出AI助手的具体权限</div>
  385. </el-form-item>
  386. </el-col>
  387. </el-row>
  388. <el-row :gutter="24">
  389. <el-col :span="12">
  390. <el-form-item label="自定义变量:" prop="custom_variables">
  391. <el-input
  392. style="width: 55%"
  393. v-model="AIform.custom_variables"
  394. placeholder='{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}'
  395. ></el-input>
  396. <div class="hint">
  397. 输入JSON对象,定义可在提示模板中使用的自定义变量
  398. </div>
  399. </el-form-item>
  400. </el-col>
  401. </el-row>
  402. <el-row :gutter="24">
  403. <el-col :span="24">
  404. <el-form-item label="自定义提示模板:" prop="custom_prompt">
  405. <el-input
  406. style="width: 100%"
  407. v-model="AIform.custom_prompt"
  408. type="textarea"
  409. :rows="12"
  410. placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
  411. 上下文: {context}
  412. 人类: {question}
  413. AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}"
  414. ></el-input>
  415. <div class="hint">
  416. 自定义AI助手的回答模板,可使用变量如{context}, {question}等
  417. </div>
  418. </el-form-item>
  419. </el-col>
  420. </el-row>
  421. <el-row :gutter="24">
  422. <el-col :span="12">
  423. <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
  424. <el-switch
  425. v-model="AIform.generate_new_api_key"
  426. active-color="#13ce66"
  427. inactive-color="#ff4949"
  428. >
  429. </el-switch>
  430. </el-form-item>
  431. </el-col>
  432. <el-col :span="12">
  433. <el-form-item label="是否设为默认:" prop="is_default">
  434. <el-switch
  435. v-model="AIform.is_default"
  436. active-color="#13ce66"
  437. inactive-color="#ff4949"
  438. >
  439. </el-switch>
  440. </el-form-item>
  441. </el-col>
  442. </el-row>
  443. </el-form>
  444. </div>
  445. <div class="center" v-else-if="type == 'edit'">
  446. <el-form
  447. :model="editForm"
  448. :rules="rules"
  449. ref="editFormRef"
  450. label-width="120px"
  451. style="margin: 20px 50px"
  452. label-position="top"
  453. >
  454. <el-row :gutter="24">
  455. <el-col :span="12">
  456. <el-form-item label="应用名称:" prop="chat_name">
  457. <el-input
  458. style="width: 55%"
  459. v-model="editForm.chat_name"
  460. placeholder="请输入应用名称"
  461. ></el-input>
  462. </el-form-item>
  463. <!-- <el-form-item label="模型库:" prop="modelLibrary">
  464. <el-select
  465. v-model="editForm.modelLibrary"
  466. placeholder="ollama"
  467. style="width: 100%"
  468. >
  469. <el-option label="ollama" value="ollama"></el-option>
  470. </el-select>
  471. </el-form-item> -->
  472. </el-col>
  473. <el-col :span="12">
  474. <el-form-item label="应用类型:" prop="application_type">
  475. <el-select
  476. v-model="editForm.application_type"
  477. placeholder="应用类型"
  478. style="width: 55%"
  479. clearable
  480. >
  481. <el-option
  482. v-for="(item, index) in appTypeList"
  483. :label="item.label"
  484. :value="item.value"
  485. :key="index"
  486. ></el-option>
  487. </el-select>
  488. </el-form-item>
  489. </el-col>
  490. </el-row>
  491. <el-row :gutter="24">
  492. <el-col :span="12">
  493. <el-form-item label="模型名称:" prop="model_name">
  494. <el-select
  495. v-model="editForm.model_name"
  496. placeholder="请输入选择"
  497. style="width: 55%"
  498. clearable
  499. >
  500. <el-option
  501. v-for="(item, index) in modelNameList"
  502. :label="item.name"
  503. :value="item.name"
  504. :key="index"
  505. ></el-option>
  506. </el-select>
  507. </el-form-item>
  508. <!-- <el-form-item label="模型类型:" prop="model_type">
  509. <el-select
  510. v-model="editForm.model_type"
  511. placeholder="请输入选择"
  512. style="width: 100%"
  513. >
  514. <el-option label="chat" value="chat"></el-option>
  515. </el-select>
  516. </el-form-item> -->
  517. </el-col>
  518. <el-col :span="12">
  519. <el-form-item label="知识库:" prop="knowledge_base_names">
  520. <el-select
  521. v-model="editForm.knowledge_base_names"
  522. multiple
  523. placeholder="请选择知识库"
  524. style="width: 55%"
  525. @change="handleEditKnowledgeBaseChange"
  526. clearable
  527. filterable
  528. >
  529. <el-option
  530. v-for="(item, index) in kneList"
  531. :label="item.name"
  532. :value="item.id"
  533. :key="index"
  534. ></el-option>
  535. </el-select>
  536. </el-form-item>
  537. </el-col>
  538. </el-row>
  539. <el-row :gutter="24">
  540. <el-col :span="12">
  541. <el-form-item label="文档/目录:" prop="">
  542. <el-radio v-model="editForm.radio" label="1">目录</el-radio>
  543. <el-radio v-model="editForm.radio" label="2">文档</el-radio>
  544. </el-form-item>
  545. </el-col>
  546. <el-col :span="12" v-if="editForm.radio == '1'">
  547. <el-form-item label="文档目录:" prop="document_directories">
  548. <el-select
  549. v-model="editForm.document_directories"
  550. placeholder="请选择文档目录"
  551. style="width: 55%"
  552. :disabled="!editForm.knowledge_base_names.length"
  553. @change="handleEditDirectoryChange"
  554. clearable
  555. filterable
  556. >
  557. <el-option
  558. v-for="(dir, index) in editDirectoryList"
  559. :label="dir.name"
  560. :value="dir.id"
  561. :key="index"
  562. ></el-option>
  563. </el-select>
  564. </el-form-item>
  565. </el-col>
  566. <el-col :span="12" v-else>
  567. <el-form-item label="文 档:" prop="documents">
  568. <el-select
  569. v-model="editForm.documents"
  570. multiple
  571. placeholder="请选择文档"
  572. style="width: 55%"
  573. :disabled="!editForm.knowledge_base_names.length"
  574. clearable
  575. filterable
  576. remote
  577. :remote-method="remoteEditSearch"
  578. :loading="editLoading"
  579. @change="handleEditDocumentChange"
  580. >
  581. <el-option label="全部" value="all"></el-option>
  582. <el-option
  583. v-for="(doc) in editDocumentList"
  584. :label="doc.name"
  585. :value="doc.id"
  586. :key="`edit-doc-${doc.id}`"
  587. ></el-option>
  588. </el-select>
  589. <div class="hint">请输入文档名称搜索</div>
  590. </el-form-item>
  591. </el-col>
  592. </el-row>
  593. <el-row :gutter="24">
  594. <el-col :span="12">
  595. <el-form-item label="应用图标:">
  596. <!-- aiagent_icon -->
  597. <el-upload
  598. class="avatar-uploader"
  599. :action="uploadUrl"
  600. method="post"
  601. :data="params"
  602. :show-file-list="false"
  603. :on-success="onUploadEditSuccess"
  604. >
  605. <img
  606. v-if="
  607. editForm.aiagent_icon != null && editForm.aiagent_icon != ''
  608. "
  609. :src="editForm.aiagent_icon"
  610. class="avatar"
  611. />
  612. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  613. <!-- <svg-icon
  614. icon-class="header"
  615. style="height: 70px; width: 70px"
  616. v-else
  617. /> -->
  618. </el-upload>
  619. </el-form-item></el-col
  620. >
  621. <el-col :span="12">
  622. <el-form-item label="描 述:">
  623. <el-input
  624. style="width: 55%"
  625. v-model="editForm.role_description"
  626. placeholder="请输入描述"
  627. type="textarea"
  628. ></el-input>
  629. </el-form-item>
  630. </el-col>
  631. </el-row>
  632. <el-row :gutter="24">
  633. <el-col :span="12">
  634. <el-form-item label="温度:" prop="temperature">
  635. <div style="display: flex; justify-content: space-around">
  636. <el-slider
  637. style="width: 60%"
  638. v-model="editForm.temperature"
  639. :min="0"
  640. :max="1"
  641. :step="0.1"
  642. ></el-slider>
  643. <el-input-number
  644. v-model="editForm.temperature"
  645. :min="0"
  646. :max="1"
  647. ></el-input-number>
  648. </div>
  649. <div class="hint">控制生成文本的随机性,0为最保守,1为最创新</div>
  650. </el-form-item>
  651. </el-col>
  652. <el-col :span="12">
  653. <el-form-item label="最大token数:" prop="max_tokens">
  654. <div style="display: flex; justify-content: space-around">
  655. <el-slider
  656. style="width: 60%"
  657. v-model="editForm.max_tokens"
  658. :min="1"
  659. :max="4096"
  660. :step="1"
  661. ></el-slider>
  662. <el-input-number
  663. v-model="editForm.max_tokens"
  664. :min="0"
  665. :max="4096"
  666. :step="1"
  667. ></el-input-number>
  668. </div>
  669. <div class="hint">限制生成文本的最大长度</div>
  670. </el-form-item>
  671. </el-col>
  672. </el-row>
  673. <el-row :gutter="24">
  674. <el-col :span="12">
  675. <el-form-item label="核采样参数:" prop="top_p">
  676. <div style="display: flex; justify-content: space-around">
  677. <el-slider
  678. style="width: 60%"
  679. v-model="editForm.top_p"
  680. :min="0"
  681. :max="1"
  682. :step="0.1"
  683. ></el-slider>
  684. <el-input-number
  685. v-model="editForm.top_p"
  686. :min="0"
  687. :max="1"
  688. :step="0.1"
  689. ></el-input-number>
  690. </div>
  691. <div class="hint">控制词汇多样性,1为考虑所有可能性</div>
  692. </el-form-item>
  693. </el-col>
  694. <el-col :span="12">
  695. <el-form-item label="重复内容惩罚:" prop="frequency_penalty">
  696. <div style="display: flex; justify-content: space-around">
  697. <el-slider
  698. style="width: 60%"
  699. v-model="editForm.frequency_penalty"
  700. :min="0"
  701. :max="2"
  702. :step="0.1"
  703. ></el-slider>
  704. <el-input-number
  705. v-model="editForm.frequency_penalty"
  706. :min="0"
  707. :max="2"
  708. :step="0.1"
  709. ></el-input-number>
  710. </div>
  711. <div class="hint">防止重复,正值减少重复,负值增加重复</div>
  712. </el-form-item>
  713. </el-col>
  714. </el-row>
  715. <el-row :gutter="24">
  716. <el-col :span="12">
  717. <el-form-item label="新话题偏好:" prop="presence_penalty">
  718. <div style="display: flex; justify-content: space-around">
  719. <el-slider
  720. style="width: 60%"
  721. v-model="editForm.presence_penalty"
  722. :min="0"
  723. :max="1"
  724. :step="0.1"
  725. ></el-slider>
  726. <el-input-number
  727. v-model="editForm.presence_penalty"
  728. :min="0"
  729. :max="1"
  730. :step="0.1"
  731. ></el-input-number>
  732. </div>
  733. <div class="hint">正值鼓励新话题,负值保持话题一致性</div>
  734. </el-form-item>
  735. </el-col>
  736. <el-col :span="12">
  737. <el-form-item label="响应格式:" prop="response_format">
  738. <el-input
  739. style="width: 55%"
  740. v-model="editForm.response_format"
  741. placeholder="text"
  742. ></el-input>
  743. <div class="hint">指定AI响应的格式,如text, json等</div>
  744. </el-form-item>
  745. </el-col>
  746. </el-row>
  747. <el-row :gutter="24">
  748. <el-col :span="12">
  749. <el-form-item label="上下文窗口:" prop="context_window">
  750. <div style="display: flex; justify-content: space-around">
  751. <el-slider
  752. style="width: 60%"
  753. v-model="editForm.context_window"
  754. :min="1"
  755. :max="10000"
  756. ></el-slider>
  757. <el-input-number
  758. v-model="editForm.context_window"
  759. :min="1"
  760. :max="10000"
  761. :step="1"
  762. ></el-input-number>
  763. </div>
  764. <div class="hint">AI考虑的上下文token数量</div>
  765. </el-form-item>
  766. </el-col>
  767. <!-- <el-col :span="12">
  768. <el-form-item label="会话ID:" prop="session_id">
  769. <el-input
  770. style="width: 55%"
  771. v-model="editForm.session_id"
  772. placeholder="可选"
  773. ></el-input>
  774. <div class="hint">特定会话的唯一标识符,如果有的话</div>
  775. </el-form-item>
  776. </el-col> -->
  777. </el-row>
  778. <el-row :gutter="24">
  779. <el-col :span="12">
  780. <el-form-item label="语言:" prop="language">
  781. <el-select v-model="editForm.language" style="width: 55%">
  782. <el-option label="中文" value="zh"></el-option>
  783. <el-option label="English" value="en"></el-option>
  784. <el-option label="日本語" value="ja"></el-option>
  785. <el-option label="한국어" value="ko"></el-option>
  786. <el-option label="Français" value="fr"></el-option>
  787. <el-option label="Deutsch" value="de"></el-option>
  788. </el-select>
  789. <div class="hint">选择AI响应的主要语言</div>
  790. </el-form-item>
  791. </el-col>
  792. <el-col :span="12">
  793. <el-form-item label="请求超时:" prop="timeout">
  794. <el-input-number
  795. v-model="editForm.timeout"
  796. :min="1"
  797. :max="60"
  798. ></el-input-number>
  799. <div class="hint">设置请求的最大等待时间(秒)</div>
  800. </el-form-item>
  801. </el-col>
  802. </el-row>
  803. <el-row :gutter="24">
  804. <el-col :span="12">
  805. <el-form-item label="角色名称:" prop="role_name">
  806. <el-input
  807. style="width: 55%"
  808. v-model="editForm.role_name"
  809. placeholder="例如: 客服专员"
  810. ></el-input>
  811. <div class="hint">为AI助手设定一个角色名称</div>
  812. </el-form-item>
  813. </el-col>
  814. <el-col :span="12">
  815. <el-form-item label="角色权限:" prop="role_permissions">
  816. <el-input
  817. style="width: 55%"
  818. v-model="editForm.role_permissions"
  819. placeholder='["回答产品问题", "处理退换货请求", "升级复杂问题"]'
  820. ></el-input>
  821. <div class="hint">输入JSON数组,列出AI助手的具体权限</div>
  822. </el-form-item>
  823. </el-col>
  824. <!-- <el-col :span="12">
  825. <el-form-item label="角色描述:" prop="role_description">
  826. <el-input
  827. style="width: 55%"
  828. v-model="editForm.role_description"
  829. type="textarea"
  830. :rows="3"
  831. placeholder="例如: 你是一位专业的客服专员,负责解答客户关于我们产品和服务的问题。"
  832. ></el-input>
  833. <div class="hint">详细描述AI助手的角色和职责</div>
  834. </el-form-item>
  835. </el-col> -->
  836. </el-row>
  837. <el-row :gutter="24">
  838. <el-col :span="12">
  839. <el-form-item label="自定义变量:" prop="custom_variables">
  840. <el-input
  841. style="width: 55%"
  842. v-model="editForm.custom_variables"
  843. placeholder='{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}'
  844. ></el-input>
  845. <div class="hint">
  846. 输入JSON对象,定义可在提示模板中使用的自定义变量
  847. </div>
  848. </el-form-item>
  849. </el-col>
  850. </el-row>
  851. <el-row :gutter="24">
  852. <el-col :span="24">
  853. <el-form-item label="自定义提示模板:" prop="custom_prompt">
  854. <el-input
  855. style="width: 100%"
  856. v-model="editForm.custom_prompt"
  857. type="textarea"
  858. :rows="12"
  859. placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
  860. 上下文: {context}
  861. 人类: {question}
  862. AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}"
  863. ></el-input>
  864. <div class="hint">
  865. 自定义AI助手的回答模板,可使用变量如{context}, {question}等
  866. </div>
  867. </el-form-item>
  868. </el-col>
  869. </el-row>
  870. <el-row :gutter="24">
  871. <el-col :span="12">
  872. <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
  873. <el-switch
  874. v-model="editForm.generate_new_api_key"
  875. active-color="#13ce66"
  876. inactive-color="#ff4949"
  877. >
  878. </el-switch>
  879. </el-form-item>
  880. </el-col>
  881. <el-col :span="12">
  882. <el-form-item label="是否设为默认:" prop="is_default">
  883. <el-switch
  884. v-model="editForm.is_default"
  885. active-color="#13ce66"
  886. inactive-color="#ff4949"
  887. >
  888. </el-switch>
  889. </el-form-item>
  890. </el-col>
  891. </el-row>
  892. </el-form>
  893. </div>
  894. <div class="center" v-else>
  895. <el-form
  896. :model="viewForm"
  897. :rules="rules"
  898. ref="viewFormRef"
  899. label-width="120px"
  900. style="margin: 20px 50px"
  901. label-position="top"
  902. >
  903. <el-row :gutter="24">
  904. <el-col :span="12">
  905. <el-form-item label="应用名称:" prop="chat_name">
  906. <el-input
  907. disabled
  908. style="width: 55%"
  909. v-model="viewForm.chat_name"
  910. placeholder="请输入应用名称"
  911. ></el-input>
  912. </el-form-item>
  913. <!-- <el-form-item label="模型库:" prop="modelLibrary">
  914. <el-select
  915. v-model="viewForm.modelLibrary"
  916. placeholder="ollama"
  917. style="width: 100%"
  918. >
  919. <el-option label="ollama" value="ollama"></el-option>
  920. </el-select>
  921. </el-form-item> -->
  922. </el-col>
  923. <el-col :span="12">
  924. <el-form-item label="应用类型:" prop="application_type">
  925. <el-select
  926. disabled
  927. v-model="viewForm.application_type"
  928. placeholder="应用类型"
  929. style="width: 55%"
  930. clearable
  931. >
  932. <el-option
  933. v-for="(item, index) in appTypeList"
  934. :label="item.label"
  935. :value="item.value"
  936. :key="index"
  937. ></el-option>
  938. </el-select>
  939. </el-form-item>
  940. </el-col>
  941. </el-row>
  942. <el-row :gutter="24">
  943. <el-col :span="12">
  944. <el-form-item label="模型名称:" prop="model_name">
  945. <el-select
  946. disabled
  947. v-model="viewForm.model_name"
  948. placeholder="请输入选择"
  949. style="width: 55%"
  950. clearable
  951. >
  952. <el-option
  953. v-for="(item, index) in modelNameList"
  954. :label="item.name"
  955. :value="item.name"
  956. :key="index"
  957. ></el-option>
  958. </el-select>
  959. </el-form-item>
  960. <!-- <el-form-item label="模型类型:" prop="model_type">
  961. <el-select
  962. v-model="viewForm.model_type"
  963. placeholder="请输入选择"
  964. style="width: 100%"
  965. >
  966. <el-option label="chat" value="chat"></el-option>
  967. </el-select>
  968. </el-form-item> -->
  969. </el-col>
  970. <el-col :span="12">
  971. <el-form-item label="知识库:" prop="knowledge_base_names">
  972. <el-select
  973. disabled
  974. v-model="viewForm.knowledge_base_names"
  975. multiple
  976. placeholder="请选择知识库"
  977. style="width: 55%"
  978. @change="handleEditKnowledgeBaseChange"
  979. clearable
  980. filterable
  981. >
  982. <el-option
  983. v-for="(item, index) in kneList"
  984. :label="item.name"
  985. :value="item.id"
  986. :key="index"
  987. ></el-option>
  988. </el-select>
  989. </el-form-item>
  990. </el-col>
  991. </el-row>
  992. <el-row :gutter="24">
  993. <el-col :span="12">
  994. <el-form-item label="文档/目录:" prop="">
  995. <el-radio v-model="viewForm.radio" label="1" disabled
  996. >目录</el-radio
  997. >
  998. <el-radio v-model="viewForm.radio" label="2" disabled
  999. >文档</el-radio
  1000. >
  1001. </el-form-item>
  1002. </el-col>
  1003. <el-col :span="12" v-if="viewForm.radio == '1'">
  1004. <el-form-item label="文档目录:" prop="document_directories">
  1005. <el-select
  1006. v-model="viewForm.document_directories"
  1007. placeholder="请选择文档目录"
  1008. style="width: 55%"
  1009. disabled
  1010. @change="handleEditDirectoryChange"
  1011. clearable
  1012. filterable
  1013. >
  1014. <el-option
  1015. v-for="(dir, index) in editDirectoryList"
  1016. :label="dir.name"
  1017. :value="dir.id"
  1018. :key="index"
  1019. ></el-option>
  1020. </el-select>
  1021. </el-form-item>
  1022. </el-col>
  1023. <el-col :span="12" v-else>
  1024. <el-form-item label="文 档:" prop="documents">
  1025. <el-select
  1026. v-model="viewForm.documents"
  1027. multiple
  1028. placeholder="请选择文档"
  1029. style="width: 55%"
  1030. disabled
  1031. clearable
  1032. filterable
  1033. >
  1034. <el-option
  1035. v-for="(doc, index) in editDocumentList"
  1036. :label="doc.name"
  1037. :value="doc.id"
  1038. :key="index"
  1039. ></el-option>
  1040. </el-select>
  1041. </el-form-item>
  1042. </el-col>
  1043. </el-row>
  1044. <el-row :gutter="24">
  1045. <el-col :span="12">
  1046. <el-form-item label="应用图标:">
  1047. <!-- aiagent_icon -->
  1048. <el-upload
  1049. disabled
  1050. class="avatar-uploader"
  1051. :action="uploadUrl"
  1052. method="post"
  1053. :data="params"
  1054. :show-file-list="false"
  1055. :on-success="onUploadEditSuccess"
  1056. >
  1057. <img
  1058. v-if="
  1059. viewForm.aiagent_icon != null && viewForm.aiagent_icon != ''
  1060. "
  1061. :src="viewForm.aiagent_icon"
  1062. class="avatar"
  1063. />
  1064. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  1065. <!-- <svg-icon
  1066. icon-class="header"
  1067. style="height: 70px; width: 70px"
  1068. v-else
  1069. /> -->
  1070. </el-upload>
  1071. </el-form-item></el-col
  1072. >
  1073. <el-col :span="12">
  1074. <el-form-item label="描 述:">
  1075. <el-input
  1076. disabled
  1077. style="width: 55%"
  1078. v-model="viewForm.role_description"
  1079. placeholder="请输入描述"
  1080. type="textarea"
  1081. ></el-input>
  1082. </el-form-item>
  1083. </el-col>
  1084. </el-row>
  1085. <el-row :gutter="24">
  1086. <el-col :span="12">
  1087. <el-form-item label="温度:" prop="temperature">
  1088. <div style="display: flex; justify-content: space-around">
  1089. <el-slider
  1090. disabled
  1091. style="width: 60%"
  1092. v-model="viewForm.temperature"
  1093. :min="0"
  1094. :max="1"
  1095. :step="0.1"
  1096. ></el-slider>
  1097. <el-input-number
  1098. disabled
  1099. v-model="viewForm.temperature"
  1100. :min="0"
  1101. :max="1"
  1102. ></el-input-number>
  1103. </div>
  1104. <div class="hint">控制生成文本的随机性,0为最保守,1为最创新</div>
  1105. </el-form-item>
  1106. </el-col>
  1107. <el-col :span="12">
  1108. <el-form-item label="最大token数:" prop="max_tokens">
  1109. <div style="display: flex; justify-content: space-around">
  1110. <el-slider
  1111. disabled
  1112. style="width: 60%"
  1113. v-model="viewForm.max_tokens"
  1114. :min="1"
  1115. :max="4096"
  1116. :step="1"
  1117. ></el-slider>
  1118. <el-input-number
  1119. disabled
  1120. v-model="viewForm.max_tokens"
  1121. :min="0"
  1122. :max="4096"
  1123. :step="1"
  1124. ></el-input-number>
  1125. </div>
  1126. <div class="hint">限制生成文本的最大长度</div>
  1127. </el-form-item>
  1128. </el-col>
  1129. </el-row>
  1130. <el-row :gutter="24">
  1131. <el-col :span="12">
  1132. <el-form-item label="核采样参数:" prop="top_p">
  1133. <div style="display: flex; justify-content: space-around">
  1134. <el-slider
  1135. disabled
  1136. style="width: 60%"
  1137. v-model="viewForm.top_p"
  1138. :min="0"
  1139. :max="1"
  1140. :step="0.1"
  1141. ></el-slider>
  1142. <el-input-number
  1143. disabled
  1144. v-model="viewForm.top_p"
  1145. :min="0"
  1146. :max="1"
  1147. :step="0.1"
  1148. ></el-input-number>
  1149. </div>
  1150. <div class="hint">控制词汇多样性,1为考虑所有可能性</div>
  1151. </el-form-item>
  1152. </el-col>
  1153. <el-col :span="12">
  1154. <el-form-item label="重复内容惩罚:" prop="frequency_penalty">
  1155. <div style="display: flex; justify-content: space-around">
  1156. <el-slider
  1157. disabled
  1158. style="width: 60%"
  1159. v-model="viewForm.frequency_penalty"
  1160. :min="0"
  1161. :max="2"
  1162. :step="0.1"
  1163. ></el-slider>
  1164. <el-input-number
  1165. disabled
  1166. v-model="viewForm.frequency_penalty"
  1167. :min="0"
  1168. :max="2"
  1169. :step="0.1"
  1170. ></el-input-number>
  1171. </div>
  1172. <div class="hint">防止重复,正值减少重复,负值增加重复</div>
  1173. </el-form-item>
  1174. </el-col>
  1175. </el-row>
  1176. <el-row :gutter="24">
  1177. <el-col :span="12">
  1178. <el-form-item label="新话题偏好:" prop="presence_penalty">
  1179. <div style="display: flex; justify-content: space-around">
  1180. <el-slider
  1181. disabled
  1182. style="width: 60%"
  1183. v-model="viewForm.presence_penalty"
  1184. :min="0"
  1185. :max="1"
  1186. :step="0.1"
  1187. ></el-slider>
  1188. <el-input-number
  1189. disabled
  1190. v-model="viewForm.presence_penalty"
  1191. :min="0"
  1192. :max="1"
  1193. :step="0.1"
  1194. ></el-input-number>
  1195. </div>
  1196. <div class="hint">正值鼓励新话题,负值保持话题一致性</div>
  1197. </el-form-item>
  1198. </el-col>
  1199. <el-col :span="12">
  1200. <el-form-item label="响应格式:" prop="response_format">
  1201. <el-input
  1202. disabled
  1203. style="width: 55%"
  1204. v-model="viewForm.response_format"
  1205. placeholder="text"
  1206. ></el-input>
  1207. <div class="hint">指定AI响应的格式,如text, json等</div>
  1208. </el-form-item>
  1209. </el-col>
  1210. </el-row>
  1211. <el-row :gutter="24">
  1212. <el-col :span="12">
  1213. <el-form-item label="上下文窗口:" prop="context_window">
  1214. <div style="display: flex; justify-content: space-around">
  1215. <el-slider
  1216. disabled
  1217. style="width: 60%"
  1218. v-model="viewForm.context_window"
  1219. :min="1"
  1220. :max="10000"
  1221. ></el-slider>
  1222. <el-input-number
  1223. disabled
  1224. v-model="viewForm.context_window"
  1225. :min="1"
  1226. :max="10000"
  1227. :step="1"
  1228. ></el-input-number>
  1229. </div>
  1230. <div class="hint">AI考虑的上下文token数量</div>
  1231. </el-form-item>
  1232. </el-col>
  1233. <!-- <el-col :span="12">
  1234. <el-form-item label="会话ID:" prop="session_id">
  1235. <el-input
  1236. disabled
  1237. style="width: 55%"
  1238. v-model="viewForm.session_id"
  1239. placeholder="可选"
  1240. ></el-input>
  1241. <div class="hint">特定会话的唯一标识符,如果有的话</div>
  1242. </el-form-item>
  1243. </el-col> -->
  1244. </el-row>
  1245. <el-row :gutter="24">
  1246. <el-col :span="12">
  1247. <el-form-item label="语言:" prop="language">
  1248. <el-select
  1249. disabled
  1250. v-model="viewForm.language"
  1251. style="width: 55%"
  1252. >
  1253. <el-option label="中文" value="zh"></el-option>
  1254. <el-option label="English" value="en"></el-option>
  1255. <el-option label="日本語" value="ja"></el-option>
  1256. <el-option label="한국어" value="ko"></el-option>
  1257. <el-option label="Français" value="fr"></el-option>
  1258. <el-option label="Deutsch" value="de"></el-option>
  1259. </el-select>
  1260. <div class="hint">选择AI响应的主要语言</div>
  1261. </el-form-item>
  1262. </el-col>
  1263. <el-col :span="12">
  1264. <el-form-item label="请求超时:" prop="timeout">
  1265. <el-input-number
  1266. disabled
  1267. v-model="viewForm.timeout"
  1268. :min="1"
  1269. :max="60"
  1270. ></el-input-number>
  1271. <div class="hint">设置请求的最大等待时间(秒)</div>
  1272. </el-form-item>
  1273. </el-col>
  1274. </el-row>
  1275. <el-row :gutter="24">
  1276. <el-col :span="12">
  1277. <el-form-item label="角色名称:" prop="role_name">
  1278. <el-input
  1279. disabled
  1280. style="width: 55%"
  1281. v-model="viewForm.role_name"
  1282. placeholder="例如: 客服专员"
  1283. ></el-input>
  1284. <div class="hint">为AI助手设定一个角色名称</div>
  1285. </el-form-item>
  1286. </el-col>
  1287. <el-col :span="12">
  1288. <el-form-item label="角色权限:" prop="role_permissions">
  1289. <el-input
  1290. disabled
  1291. style="width: 55%"
  1292. v-model="viewForm.role_permissions"
  1293. placeholder='["回答产品问题", "处理退换货请求", "升级复杂问题"]'
  1294. ></el-input>
  1295. <div class="hint">输入JSON数组,列出AI助手的具体权限</div>
  1296. </el-form-item>
  1297. </el-col>
  1298. <!-- <el-col :span="12">
  1299. <el-form-item label="角色描述:" prop="role_description">
  1300. <el-input
  1301. style="width: 55%"
  1302. v-model="viewForm.role_description"
  1303. type="textarea"
  1304. :rows="3"
  1305. placeholder="例如: 你是一位专业的客服专员,负责解答客户关于我们产品和服务的问题。"
  1306. ></el-input>
  1307. <div class="hint">详细描述AI助手的角色和职责</div>
  1308. </el-form-item>
  1309. </el-col> -->
  1310. </el-row>
  1311. <el-row :gutter="24">
  1312. <el-col :span="12">
  1313. <el-form-item label="自定义变量:" prop="custom_variables">
  1314. <el-input
  1315. disabled
  1316. style="width: 55%"
  1317. v-model="viewForm.custom_variables"
  1318. placeholder='{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}'
  1319. ></el-input>
  1320. <div class="hint">
  1321. 输入JSON对象,定义可在提示模板中使用的自定义变量
  1322. </div>
  1323. </el-form-item>
  1324. </el-col>
  1325. </el-row>
  1326. <el-row :gutter="24">
  1327. <el-col :span="24">
  1328. <el-form-item label="自定义提示模板:" prop="custom_prompt">
  1329. <el-input
  1330. disabled
  1331. style="width: 100%"
  1332. v-model="viewForm.custom_prompt"
  1333. type="textarea"
  1334. :rows="12"
  1335. placeholder="使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。
  1336. 上下文: {context}
  1337. 人类: {question}
  1338. AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}"
  1339. ></el-input>
  1340. <div class="hint">
  1341. 自定义AI助手的回答模板,可使用变量如{context}, {question}等
  1342. </div>
  1343. </el-form-item>
  1344. </el-col>
  1345. </el-row>
  1346. <el-row :gutter="24">
  1347. <el-col :span="12">
  1348. <el-form-item label="是否启用API_key:" prop="generate_new_api_key">
  1349. <el-switch
  1350. disabled
  1351. v-model="viewForm.generate_new_api_key"
  1352. active-color="#13ce66"
  1353. inactive-color="#ff4949"
  1354. >
  1355. </el-switch>
  1356. </el-form-item>
  1357. </el-col>
  1358. <el-col :span="12">
  1359. <el-form-item label="是否设为默认:" prop="is_default">
  1360. <el-switch
  1361. disabled
  1362. v-model="viewForm.is_default"
  1363. active-color="#13ce66"
  1364. inactive-color="#ff4949"
  1365. >
  1366. </el-switch>
  1367. </el-form-item>
  1368. </el-col>
  1369. </el-row>
  1370. </el-form>
  1371. </div>
  1372. <div class="footer">
  1373. <el-button @click="cancelApplication">取 消</el-button>
  1374. <el-button
  1375. type="primary"
  1376. v-if="type == 'add'"
  1377. @click="generateApplication"
  1378. >生成应用</el-button
  1379. >
  1380. <el-button type="primary" v-else-if="type == 'edit'" @click="submitEdit"
  1381. >确认修改</el-button
  1382. >
  1383. </div>
  1384. </div>
  1385. </template>
  1386. <script>
  1387. import axios from "axios";
  1388. import {
  1389. modelList,
  1390. listBuckets,
  1391. selectTypeList,
  1392. getBucketContents,
  1393. configSave,
  1394. configList,
  1395. configDelete,
  1396. application_types,
  1397. set_default,
  1398. configurationInfo,
  1399. vague_search,
  1400. } from "@/api/knowledge";
  1401. export default {
  1402. /*组件参数 接收来自父组件的数据*/
  1403. props: {},
  1404. /*局部注册的组件*/
  1405. components: {},
  1406. /*组件状态值*/
  1407. data() {
  1408. return {
  1409. AIform: {
  1410. chat_name: "",
  1411. modelLibrary: "ollama",
  1412. model_type: "chat",
  1413. model_name: "",
  1414. knowledge_base_names: [],
  1415. document_directories: [],
  1416. documents: [],
  1417. temperature: 0.7,
  1418. max_tokens: 150,
  1419. top_p: 1.0,
  1420. frequency_penalty: 0.0,
  1421. presence_penalty: 0.0,
  1422. response_format: "text",
  1423. context_window: 2048,
  1424. user_id: "user123",
  1425. session_id: "session456",
  1426. language: "en",
  1427. timeout: 30,
  1428. role_name: "Admin",
  1429. role_description: "",
  1430. role_permissions: '["回答产品问题", "处理退换货请求", "升级复杂问题"]',
  1431. custom_variables:
  1432. '{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}',
  1433. custom_prompt:
  1434. "使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。上下文: {context} 人类: {question}AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}",
  1435. application_type: "",
  1436. is_default: false,
  1437. generate_new_api_key: true,
  1438. aiagent_icon: "",
  1439. },
  1440. /* 模型库 */
  1441. modelList: [],
  1442. /* 模型类型 */
  1443. modelTypeList: [],
  1444. /* 模型名称 */
  1445. modelNameList: [],
  1446. /* 知识库 */
  1447. kneList: [],
  1448. /* 文档目录 */
  1449. directoryList: [],
  1450. /* 文档 */
  1451. documentList: [],
  1452. bucket_id: "",
  1453. rules: {
  1454. chat_name: [
  1455. { required: true, message: "请填写应名称", trigger: "blur" },
  1456. ],
  1457. model_name: [
  1458. { required: true, message: "请选择模型名称", trigger: "change" },
  1459. ],
  1460. knowledge_base_names: [
  1461. {
  1462. required: true,
  1463. message: "请选择至少一个知识库",
  1464. trigger: "change",
  1465. },
  1466. ],
  1467. /* document_directories: [
  1468. { required: true, message: '请选择文档目录', trigger: 'change' }
  1469. ], */
  1470. documents: [
  1471. { required: true, message: "请选择至少一个文档", trigger: "change" },
  1472. ],
  1473. role_permissions: [
  1474. { required: true, message: "请输入角色权限", trigger: "blur" },
  1475. {
  1476. validator: (rule, value, callback) => {
  1477. try {
  1478. JSON.parse(value);
  1479. callback();
  1480. } catch (error) {
  1481. callback(new Error("请输入有效的 JSON 数组"));
  1482. }
  1483. },
  1484. trigger: "blur",
  1485. },
  1486. ],
  1487. custom_variables: [
  1488. { required: true, message: "请输入自定义变量", trigger: "blur" },
  1489. {
  1490. validator: (rule, value, callback) => {
  1491. try {
  1492. JSON.parse(value);
  1493. callback();
  1494. } catch (error) {
  1495. callback(new Error("请输入有效的 JSON 对象"));
  1496. }
  1497. },
  1498. trigger: "blur",
  1499. },
  1500. ],
  1501. },
  1502. /* 配置类型列表 */
  1503. appTypeList: [],
  1504. id: "",
  1505. editForm: {
  1506. // 复制 AIform 的结构,但初始值为空
  1507. chat_name: "",
  1508. modelLibrary: "ollama",
  1509. model_type: "chat",
  1510. model_name: "",
  1511. knowledge_base_names: [],
  1512. document_directories: [],
  1513. documents: [],
  1514. temperature: 0.7,
  1515. max_tokens: 150,
  1516. top_p: 1.0,
  1517. frequency_penalty: 0.0,
  1518. presence_penalty: 0.0,
  1519. response_format: "text",
  1520. context_window: 2048,
  1521. user_id: "user123",
  1522. session_id: "session456",
  1523. language: "en",
  1524. timeout: 30,
  1525. role_name: "Admin",
  1526. role_description: "",
  1527. role_permissions: '["回答产品问题", "处理退换货请求", "升级复杂问题"]',
  1528. custom_variables:
  1529. '{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}',
  1530. custom_prompt:
  1531. "使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。上下文: {context} 人类: {question}AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}",
  1532. application_type: "",
  1533. is_default: false,
  1534. generate_new_api_key: true,
  1535. radio: "1",
  1536. tempDocuments: [], // 用于临时存储文档数据
  1537. tempDirectories: [], // 用于临时存储目录数据
  1538. },
  1539. editDirectoryList: [],
  1540. editDocumentList: [],
  1541. type: "",
  1542. viewForm: {
  1543. // 复制 AIform 的结构,但初始值为空
  1544. chat_name: "",
  1545. modelLibrary: "ollama",
  1546. model_type: "chat",
  1547. model_name: "",
  1548. knowledge_base_names: [],
  1549. document_directories: [],
  1550. documents: [],
  1551. temperature: 0.7,
  1552. max_tokens: 150,
  1553. top_p: 1.0,
  1554. frequency_penalty: 0.0,
  1555. presence_penalty: 0.0,
  1556. response_format: "text",
  1557. context_window: 2048,
  1558. user_id: "user123",
  1559. session_id: "session456",
  1560. language: "en",
  1561. timeout: 30,
  1562. role_name: "Admin",
  1563. role_description: "",
  1564. role_permissions: "",
  1565. custom_variables: "",
  1566. custom_prompt:
  1567. "使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。上下文: {context} 人类: {question}AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}",
  1568. application_type: "",
  1569. is_default: false,
  1570. generate_new_api_key: true,
  1571. aiagent_icon: "",
  1572. radio: "1",
  1573. },
  1574. params: {
  1575. token: "",
  1576. },
  1577. uploadUrl: "",
  1578. radio: "1",
  1579. loading: false, // 添加loading状态
  1580. editLoading: false,
  1581. };
  1582. },
  1583. /*计算属性*/
  1584. computed: {
  1585. getKnowledgeBaseNames() {
  1586. const names = this.AIform.knowledge_base_names.map(
  1587. (id) => this.kneList.find((item) => item.id === id)?.name || id
  1588. );
  1589. if (names.length <= 2) {
  1590. return names.join(", ");
  1591. } else {
  1592. return `${names[0]}, ${names[1]} 等${names.length}个`;
  1593. }
  1594. },
  1595. getDocumentNames() {
  1596. const names = this.AIform.documents.map(
  1597. (id) => this.documentList.find((item) => item.id === id)?.name || id
  1598. );
  1599. if (names.length <= 2) {
  1600. return names.join(", ");
  1601. } else {
  1602. return `${names[0]}, ${names[1]} 等${names.length}个`;
  1603. }
  1604. },
  1605. isDocumentSelectDisabled() {
  1606. // Modify the disabled logic based on radio selection
  1607. if (this.radio === "1") {
  1608. return (
  1609. !this.AIform.knowledge_base_names.length ||
  1610. this.AIform.document_directories == ""
  1611. );
  1612. } else {
  1613. return !this.AIform.knowledge_base_names.length;
  1614. }
  1615. },
  1616. },
  1617. /*侦听器*/
  1618. watch: {
  1619. "editForm.radio": {
  1620. handler(newVal, oldVal) {
  1621. if (newVal === "1") {
  1622. // 切换到目录模式
  1623. // 保存当前文档选择
  1624. this.editForm.tempDocuments = [...(this.editForm.documents || [])];
  1625. // 恢复之前的目录选择
  1626. if (this.editForm.tempDirectories.length > 0) {
  1627. this.editForm.document_directories = [
  1628. ...this.editForm.tempDirectories,
  1629. ];
  1630. }
  1631. } else {
  1632. // 切换到文档模式
  1633. // 保存当前目录选择
  1634. this.editForm.tempDirectories = [
  1635. ...(this.editForm.document_directories || []),
  1636. ];
  1637. // 恢复之前的文档选择
  1638. if (this.editForm.tempDocuments.length > 0) {
  1639. this.editForm.documents = [...this.editForm.tempDocuments];
  1640. }
  1641. }
  1642. },
  1643. },
  1644. },
  1645. /*
  1646. * el 被新创建的 vm.$ el 替换,并挂载到实例上去之后调用该钩子。
  1647. * 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$ el 也在文档内。
  1648. */
  1649. created() {
  1650. let _this = this;
  1651. _this.uploadUrl = process.env.VUE_APP_BASE_API + "/upload/file";
  1652. _this.params.token = _this.$store.getters.token;
  1653. },
  1654. mounted() {
  1655. /* 初始化列表 */
  1656. this.init();
  1657. this.type = this.$route.query.type;
  1658. this.id = this.$route.query.id;
  1659. /* if (this.type == "edit") {
  1660. this.initEdit();
  1661. } else */ if (this.type == "view") {
  1662. this.initView();
  1663. }
  1664. },
  1665. /*组件方法*/
  1666. methods: {
  1667. // 编辑模式的远程搜索方法
  1668. remoteEditSearch(query) {
  1669. if (query !== "") {
  1670. this.editLoading = true;
  1671. // 构建搜索参数
  1672. const searchParams = {
  1673. kb_id: this.editForm.knowledge_base_names, // 当前选中的知识库ID
  1674. keyword: query, // 搜索关键词
  1675. limit: 10, // 限制返回数量
  1676. };
  1677. // 如果是目录模式且选择了目录,添加目录ID
  1678. if (this.editForm.radio === "1" && this.editForm.document_directories) {
  1679. searchParams.doc_type_id = this.editForm.document_directories;
  1680. }
  1681. // 调用模糊搜索接口
  1682. vague_search(searchParams)
  1683. .then((response) => {
  1684. this.editDocumentList = response.data.items || [];
  1685. })
  1686. .catch((error) => {
  1687. console.error("搜索文档失败:", error);
  1688. this.$message.error("搜索文档失败");
  1689. })
  1690. .finally(() => {
  1691. this.editLoading = false;
  1692. });
  1693. } else {
  1694. // 当搜索词为空时,恢复原始文档列表
  1695. if (this.editForm.radio === "1") {
  1696. // 目录模式:加载选中目录的文档
  1697. if (this.editForm.document_directories) {
  1698. this.loadEditDocumentList(this.editForm.document_directories);
  1699. }
  1700. } else {
  1701. // 文档模式:加载所有文档
  1702. if (this.editForm.knowledge_base_names.length > 0) {
  1703. //this.loadAllEditDocuments(this.editForm.knowledge_base_names[0]);
  1704. }
  1705. }
  1706. }
  1707. },
  1708. // 添加加载所有文档的方法
  1709. loadAllEditDocuments(kbId) {
  1710. this.editLoading = true;
  1711. let queryForm = {
  1712. page: 1,
  1713. pageSize: 9999,
  1714. bucket_id: kbId,
  1715. doc_type_id: "",
  1716. };
  1717. getBucketContents(queryForm)
  1718. .then((res) => {
  1719. this.editDocumentList = res.data.documents;
  1720. })
  1721. .finally(() => {
  1722. this.editLoading = false;
  1723. });
  1724. },
  1725. // 远程搜索方法
  1726. remoteSearch(query) {
  1727. if (query !== "") {
  1728. this.loading = true;
  1729. // 构建搜索参数
  1730. const searchParams = {
  1731. kb_id: this.AIform.knowledge_base_names, // 当前选中的知识库ID
  1732. keyword: query, // 搜索关键词
  1733. limit: 10, // 限制返回数量
  1734. };
  1735. // 如果是目录模式且选择了目录,添加目录ID
  1736. if (this.radio === "1" && this.AIform.document_directories) {
  1737. searchParams.doc_type_id = this.AIform.document_directories;
  1738. }
  1739. // 调用模糊搜索接口
  1740. vague_search(searchParams)
  1741. .then((response) => {
  1742. this.documentList = response.data.items || [];
  1743. })
  1744. .catch((error) => {
  1745. console.error("搜索文档失败:", error);
  1746. this.$message.error("搜索文档失败");
  1747. })
  1748. .finally(() => {
  1749. this.loading = false;
  1750. });
  1751. } else {
  1752. // 当搜索词为空时,恢复原始文档列表
  1753. if (this.radio === "1") {
  1754. // 目录模式:加载选中目录的文档
  1755. if (this.AIform.document_directories) {
  1756. this.loadDocumentList(this.AIform.document_directories);
  1757. }
  1758. } else {
  1759. // 文档模式:加载所有文档
  1760. if (this.AIform.knowledge_base_names.length > 0) {
  1761. //this.loadAllDocuments(this.AIform.knowledge_base_names[0]);
  1762. }
  1763. }
  1764. }
  1765. },
  1766. // 修改加载所有文档的方法,添加loading状态
  1767. loadAllDocuments(kbId) {
  1768. this.loading = true;
  1769. let queryForm = {
  1770. page: 1,
  1771. pageSize: 9999,
  1772. bucket_id: kbId,
  1773. doc_type_id: "",
  1774. };
  1775. getBucketContents(queryForm)
  1776. .then((res) => {
  1777. this.documentList = res.data.documents;
  1778. })
  1779. .finally(() => {
  1780. this.loading = false;
  1781. });
  1782. },
  1783. /* 编辑 */
  1784. /* 上传图标 */
  1785. onUploadEditSuccess(e) {
  1786. this.editForm.aiagent_icon = e.data.fileUrl;
  1787. },
  1788. // 修改文档选择处理方法
  1789. handleEditDocumentChange(selectedValues) {
  1790. if (selectedValues.includes("all")) {
  1791. // 如果选择了"全部",则清空其他选项
  1792. this.editForm.documents = ["all"];
  1793. } else {
  1794. // 直接使用新的选择值,不再保留原有文档
  1795. if (selectedValues.length > 10) {
  1796. this.editForm.documents = selectedValues.slice(0, 10);
  1797. this.$message.warning("最多只能选择10个文档");
  1798. } else {
  1799. this.editForm.documents = selectedValues;
  1800. }
  1801. }
  1802. },
  1803. /* 编辑确认修改 */
  1804. submitEdit() {
  1805. this.$refs.editFormRef.validate(async (valid) => {
  1806. if (valid) {
  1807. try {
  1808. // 深拷贝表单数据
  1809. const convertedForm = { ...this.editForm };
  1810. // 处理 role_permissions 和 custom_variables
  1811. const parsedPermissions = this.validateAndParseJSON(
  1812. "role_permissions",
  1813. "角色权限格式无效"
  1814. );
  1815. if (!parsedPermissions) return;
  1816. convertedForm.role_permissions = parsedPermissions;
  1817. const parsedVariables = this.validateAndParseJSON(
  1818. "custom_variables",
  1819. "自定义变量格式无效"
  1820. );
  1821. if (!parsedVariables) return;
  1822. convertedForm.custom_variables = parsedVariables;
  1823. // 根据 radio 值处理文档和目录数据
  1824. if (convertedForm.radio === "1") {
  1825. // 目录模式:只提交目录数据,清空文档数据
  1826. convertedForm.documents = [];
  1827. // 如果选择了目录,则使用新选择的目录
  1828. if (this.editForm.document_directories) {
  1829. convertedForm.document_directories = this.safeGetNamesByIds(
  1830. [this.editForm.document_directories],
  1831. this.editDirectoryList
  1832. );
  1833. } else {
  1834. // 如果没有选择新目录,保留原有目录
  1835. convertedForm.document_directories =
  1836. this.editForm.tempDirectories;
  1837. }
  1838. } else {
  1839. // 文档模式:只提交文档数据,清空目录数据
  1840. convertedForm.document_directories = [];
  1841. // 处理文档选择
  1842. if (convertedForm.documents.includes("all")) {
  1843. convertedForm.documents = ["全部"]; // 全选时传 "全部"
  1844. } else {
  1845. // 获取当前选中的文档ID列表
  1846. const currentSelectedIds = new Set(convertedForm.documents);
  1847. // 获取原有未删除的文档
  1848. const originalDocs = this.editForm.tempDocuments.filter(
  1849. (doc) =>
  1850. !doc.startsWith("temp_") && !currentSelectedIds.has(doc)
  1851. );
  1852. // 合并原有文档和新选择的文档
  1853. const allSelectedDocs = new Set([
  1854. ...originalDocs,
  1855. ...convertedForm.documents,
  1856. ]);
  1857. // 转换文档 ID 为名称
  1858. convertedForm.documents = Array.from(allSelectedDocs)
  1859. .map((docId) => {
  1860. // 如果是原有文档名称,直接使用
  1861. if (typeof docId === "string" && !docId.match(/^[0-9]+$/)) {
  1862. return docId;
  1863. }
  1864. // 否则查找文档名称
  1865. const doc = this.editDocumentList.find(
  1866. (d) => d.id === docId
  1867. );
  1868. return doc ? doc.name : null;
  1869. })
  1870. .filter((name) => name !== null); // 过滤掉未找到的文档
  1871. // 如果文档数量超过限制,保留前10个
  1872. if (convertedForm.documents.length > 10) {
  1873. convertedForm.documents = convertedForm.documents.slice(
  1874. 0,
  1875. 10
  1876. );
  1877. this.$message.warning(
  1878. "已超出最大文档数量限制,仅保留前10个文档"
  1879. );
  1880. }
  1881. }
  1882. }
  1883. // 删除不需要提交的临时数据和radio值
  1884. delete convertedForm.tempDocuments;
  1885. delete convertedForm.tempDirectories;
  1886. delete convertedForm.radio;
  1887. console.log("Converted form for edit:", convertedForm);
  1888. // 发送请求
  1889. const response = await axios.post(
  1890. `${process.env.VUE_APP_BASE_API}/chatbot/configuration/update/`,
  1891. convertedForm,
  1892. {
  1893. headers: {
  1894. "Content-Type": "application/json",
  1895. },
  1896. }
  1897. );
  1898. if (response.status === 200) {
  1899. this.$message.success("应用更新成功");
  1900. this.$router.push({
  1901. path: "/knowledge/chatPage/index",
  1902. });
  1903. } else {
  1904. this.$message.error(response.data.message || "应用更新失败");
  1905. }
  1906. } catch (error) {
  1907. console.error("Error updating application:", error);
  1908. this.$message.error("应用更新失败,请稍后重试");
  1909. }
  1910. } else {
  1911. this.$message.error("请填写所有必填字段");
  1912. return false;
  1913. }
  1914. });
  1915. },
  1916. handleEditKnowledgeBaseChange(val) {
  1917. if (val && val.length > 0) {
  1918. // 只在有选择知识库时重置
  1919. if (this.editForm.radio === "1") {
  1920. this.editForm.document_directories = [];
  1921. this.editForm.documents = [];
  1922. } else {
  1923. this.editForm.documents = [];
  1924. // 加载初始文档列表
  1925. this.loadAllEditDocuments(val[0]);
  1926. }
  1927. this.editDocumentList = [];
  1928. this.loadEditDirectoryList(val);
  1929. }
  1930. },
  1931. handleEditDirectoryChange(val) {
  1932. if (this.editForm.radio === "1") {
  1933. // 只在目录模式下清空文档选择
  1934. this.editForm.documents = [];
  1935. this.loadEditDocumentList(val);
  1936. }
  1937. },
  1938. /* 知识库选择监听 */
  1939. async loadEditDirectoryList(val) {
  1940. this.editDirectoryList = []; // 清空现有目录列表
  1941. let totalDocuments = 0;
  1942. let otherFolderCount = 0;
  1943. const promises = val.map(async (kbName) => {
  1944. const typeForm = {
  1945. page: 1,
  1946. pageSize: 9999,
  1947. kb_id: kbName,
  1948. };
  1949. try {
  1950. const res = await selectTypeList(typeForm);
  1951. if (res.data) {
  1952. this.editDirectoryList = [
  1953. ...new Set([...this.editDirectoryList, ...res.data.dataList]),
  1954. ];
  1955. res.data.dataList.forEach((folder) => {
  1956. if (folder.id === "other") {
  1957. otherFolderCount += folder.document_count || 0;
  1958. } else {
  1959. totalDocuments += folder.document_count || 0;
  1960. }
  1961. });
  1962. }
  1963. } catch (error) {
  1964. console.error(
  1965. `Error loading directory list for kb_id ${kbName}:`,
  1966. error
  1967. );
  1968. }
  1969. });
  1970. await Promise.all(promises);
  1971. this.editDirectoryList.unshift({
  1972. id: "001",
  1973. name: "全部",
  1974. document_count: totalDocuments + otherFolderCount,
  1975. });
  1976. return this.editDirectoryList;
  1977. },
  1978. /* 监听目录选择 */
  1979. loadEditDocumentList(val) {
  1980. // 找到选中目录的名称
  1981. const selectedDirectoryName = val;
  1982. const id = this.directoryList.find((el) => el.id == val);
  1983. // 从 kneList 中找到对应的知识库
  1984. const selectedKnowledgeBase = this.kneList.find(
  1985. (kb) => kb.id === this.editForm.knowledge_base_names[0]
  1986. );
  1987. /* if (!selectedKnowledgeBase) {
  1988. console.error('No matching knowledge base found');
  1989. return;
  1990. } */
  1991. let queryForm = {
  1992. page: 1,
  1993. pageSize: 9999,
  1994. bucket_id: selectedKnowledgeBase.id,
  1995. doc_type_id:
  1996. selectedDirectoryName === "001"
  1997. ? ""
  1998. : this.getDirectoryIdByName(selectedDirectoryName),
  1999. };
  2000. return getBucketContents(queryForm).then((res) => {
  2001. this.editDocumentList = res.data.documents;
  2002. return this.editDocumentList;
  2003. });
  2004. },
  2005. // 添加一个辅助方法来根据目录名称获取目录ID
  2006. getDirectoryIdByName(directoryName) {
  2007. const directory = this.editDirectoryList.find(
  2008. (dir) => dir.name === directoryName
  2009. );
  2010. return directory ? directory.id : null;
  2011. },
  2012. /* 新增 */
  2013. /* 上传图片 */
  2014. onUploadAddSuccess(e) {
  2015. this.AIform.aiagent_icon = e.data.fileUrl;
  2016. },
  2017. /* 重置表单 */
  2018. resetForm() {
  2019. if (this.$refs.AIformRef) {
  2020. this.$refs.AIformRef.resetFields();
  2021. }
  2022. this.AIform = {
  2023. chat_name: "",
  2024. modelLibrary: "ollama",
  2025. model_type: "chat",
  2026. model_name: "",
  2027. knowledge_base_names: [],
  2028. document_directories: [],
  2029. documents: [],
  2030. temperature: 0.7,
  2031. max_tokens: 150,
  2032. top_p: 1.0,
  2033. frequency_penalty: 0.0,
  2034. presence_penalty: 0.0,
  2035. response_format: "",
  2036. context_window: 2048,
  2037. user_id: "",
  2038. session_id: "",
  2039. language: "en",
  2040. timeout: 30,
  2041. role_name: "Admin",
  2042. role_description: "",
  2043. role_permissions: '["回答产品问题", "处理退换货请求", "升级复杂问题"]',
  2044. custom_variables:
  2045. '{"custom_greeting": "很高兴为您服务!", "company_name": "ABC公司"}',
  2046. custom_prompt:
  2047. "使用以下上下文来回答问题。如果你不知道答案,就说你不知道,不要试图编造答案。上下文: {context} 人类: {question}AI助手: 让我根据提供的上下文来回答你的问题。{custom_greeting}",
  2048. application_type: "",
  2049. is_default: false,
  2050. generate_new_api_key: true,
  2051. aiagent_icon: "",
  2052. };
  2053. },
  2054. /* 取消 */
  2055. cancelApplication() {
  2056. if (this.type == "view") {
  2057. this.resetForm();
  2058. this.$router.push({
  2059. path: "/knowledge/chatPage/index",
  2060. });
  2061. } else {
  2062. this.$confirm("确认取消?未保存的更改将会丢失。")
  2063. .then((_) => {
  2064. this.resetForm();
  2065. this.$router.push({
  2066. path: "/knowledge/chatPage/index",
  2067. });
  2068. })
  2069. .catch((_) => {});
  2070. }
  2071. },
  2072. handleDocumentChange(selectedValues) {
  2073. if (selectedValues.includes("all")) {
  2074. // 如果选择了"全部",则清空其他选项
  2075. this.AIform.documents = ["all"];
  2076. } else {
  2077. // 如果没有选择"全部",则限制最多选择10个文档
  2078. if (selectedValues.length > 10) {
  2079. this.AIform.documents = selectedValues.slice(0, 10);
  2080. this.$message.warning("最多只能选择10个文档");
  2081. }
  2082. }
  2083. },
  2084. validateAndParseJSON(field, errorMessage) {
  2085. try {
  2086. return JSON.parse(this.AIform[field]);
  2087. } catch (error) {
  2088. this.$message.error(`${errorMessage}: ${error.message}`);
  2089. return null;
  2090. }
  2091. },
  2092. /* 新增聊天应用 */
  2093. generateApplication() {
  2094. this.$refs.AIformRef.validate(async (valid) => {
  2095. if (valid) {
  2096. try {
  2097. // 创建一个新对象来存储转换后的数据
  2098. const convertedForm = { ...this.AIform };
  2099. // 处理 role_permissions
  2100. const parsedPermissions = this.validateAndParseJSON(
  2101. "role_permissions",
  2102. "角色权限格式无效"
  2103. );
  2104. if (!parsedPermissions) return;
  2105. convertedForm.role_permissions = parsedPermissions;
  2106. // 处理 custom_variables
  2107. const parsedVariables = this.validateAndParseJSON(
  2108. "custom_variables",
  2109. "自定义变量格式无效"
  2110. );
  2111. if (!parsedVariables) return;
  2112. convertedForm.custom_variables = parsedVariables;
  2113. // 将知识库 ID 转换为名称
  2114. /* convertedForm.knowledge_base_names = this.safeGetNamesByIds(
  2115. this.AIform.knowledge_base_names,
  2116. this.kneList
  2117. ); */
  2118. /* // 将文档 ID 转换为名称
  2119. convertedForm.documents = this.safeGetNamesByIds(
  2120. this.AIform.documents,
  2121. this.documentList
  2122. ); */
  2123. // 处理文档选择
  2124. if (
  2125. convertedForm.documents.includes("all") ||
  2126. convertedForm.documents.length === 0
  2127. ) {
  2128. convertedForm.documents = []; // 全选或未选择时传空数组给后台
  2129. } else {
  2130. convertedForm.documents = this.safeGetNamesByIds(
  2131. convertedForm.documents,
  2132. this.documentList
  2133. );
  2134. }
  2135. // 将文档目录 ID 转换为名称,如果为空则传递 '全部'
  2136. convertedForm.document_directories = this.safeGetNamesByIds(
  2137. this.AIform.document_directories,
  2138. this.directoryList
  2139. );
  2140. /* if (convertedForm.document_directories.length === 0) {
  2141. convertedForm.document_directories = ["全部"];
  2142. } */
  2143. console.log("Converted form:", convertedForm);
  2144. // 使用 axios 发送 POST 请求
  2145. const response = await axios.post(
  2146. `${process.env.VUE_APP_BASE_API}/chatbot/configCreart/`,
  2147. convertedForm,
  2148. {
  2149. headers: {
  2150. "Content-Type": "application/json",
  2151. },
  2152. }
  2153. );
  2154. if (response.status === 200) {
  2155. this.$message.success("应用生成成功");
  2156. this.resetForm();
  2157. /* this.fetchApplicationList(); */
  2158. this.$router.push({
  2159. path: "/knowledge/chatPage/index",
  2160. }); //跳转到聊天应用页面
  2161. // 可选:重定向到新应用或更新 UI
  2162. } else {
  2163. this.$message.error(response.data.message || "应用生成失败");
  2164. }
  2165. } catch (error) {
  2166. console.error("Error generating application:", error);
  2167. this.$message.error("应用生成失败,请稍后重试");
  2168. }
  2169. } else {
  2170. this.$message.error("请填写所有必填字段");
  2171. return false;
  2172. }
  2173. });
  2174. },
  2175. /* 数据处理方法 */
  2176. // 安全的辅助方法:通过 ID 数组获取名称数组
  2177. safeGetNamesByIds(ids, list) {
  2178. if (!Array.isArray(ids)) {
  2179. console.warn("Expected an array of ids, but received:", ids);
  2180. return [];
  2181. }
  2182. return ids
  2183. .map((id) => {
  2184. if (id === "001") {
  2185. return "全部";
  2186. }
  2187. const item = list.find((item) => item.id === id);
  2188. return item ? item.name : "";
  2189. })
  2190. .filter((name) => name !== "");
  2191. },
  2192. /* 知识库选择监听联动 */
  2193. handleKnowledgeBaseChange(val) {
  2194. // 重置目录和文档选择
  2195. this.AIform.document_directories = [];
  2196. this.AIform.documents = [];
  2197. this.documentList = [];
  2198. this.bucket_id = val[0];
  2199. // 根据选中的知识库加载目录列表
  2200. // 这里需要调用后端 API 来获取目录列表
  2201. this.loadDirectoryList(val);
  2202. },
  2203. handleDirectoryChange(val) {
  2204. // 重置文档选择
  2205. this.AIform.documents = [];
  2206. // 根据选中的目录加载文档列表
  2207. // 这里需要调用后端 API 来获取文档列表
  2208. this.loadDocumentList(val);
  2209. },
  2210. async loadDirectoryList(val) {
  2211. this.directoryList = []; // 清空现有目录列表
  2212. let totalDocuments = 0;
  2213. let otherFolderCount = 0;
  2214. for (const kbId of val) {
  2215. const typeForm = {
  2216. page: 1,
  2217. pageSize: 9999,
  2218. kb_id: kbId,
  2219. };
  2220. try {
  2221. const res = await selectTypeList(typeForm);
  2222. // 假设 res.data 包含目录列表
  2223. if (res.data) {
  2224. // 将新的目录添加到列表中,避免重复
  2225. this.directoryList = [
  2226. ...new Set([...this.directoryList, ...res.data.dataList]),
  2227. ];
  2228. console.log(res.data.dataList);
  2229. // 计算总文档数和其他文件夹数量
  2230. res.data.dataList.forEach((folder) => {
  2231. if (folder.id === "other") {
  2232. otherFolderCount += folder.document_count || 0;
  2233. } else {
  2234. totalDocuments += folder.document_count || 0;
  2235. }
  2236. });
  2237. }
  2238. } catch (error) {
  2239. console.error(
  2240. `Error loading directory list for kb_id ${kbId}:`,
  2241. error
  2242. );
  2243. // 可以在这里添加错误处理,比如显示一个错误提示
  2244. }
  2245. }
  2246. // 在列表开头插入"全部"选项
  2247. this.directoryList.unshift({
  2248. id: "001",
  2249. name: "全部",
  2250. /* document_count: totalDocuments + otherFolderCount, */
  2251. });
  2252. },
  2253. /* 目录选择监听 */
  2254. handleDirectoryChange(val) {
  2255. // 重置文档选择
  2256. this.AIform.documents = [];
  2257. // 根据选中的目录加载文档列表
  2258. // 这里需要调用后端 API 来获取文档列表
  2259. this.loadDocumentList(val);
  2260. },
  2261. // 修改原有的加载文档列表方法,添加loading状态
  2262. loadDocumentList(val) {
  2263. this.loading = true;
  2264. let queryForm = {
  2265. page: 1,
  2266. pageSize: 9999,
  2267. bucket_id: this.bucket_id,
  2268. doc_type_id: val === "001" ? "" : val,
  2269. };
  2270. getBucketContents(queryForm)
  2271. .then((res) => {
  2272. this.documentList = res.data.documents;
  2273. })
  2274. .finally(() => {
  2275. this.loading = false;
  2276. });
  2277. },
  2278. /* 获取列表 */
  2279. init() {
  2280. /* 模型库 { model_type: "model" }*/
  2281. modelList().then((res) => {
  2282. this.modelNameList = res.data.models;
  2283. });
  2284. /* 知识库 */
  2285. listBuckets({ user_id: this.$store.state.user.id }).then((res) => {
  2286. this.kneList = res.data;
  2287. if (this.$route.query.type == "edit") {
  2288. this.initEdit();
  2289. }
  2290. });
  2291. /* 应用列表 */
  2292. configList().then((res) => {
  2293. this.knowledgeBases = res.data;
  2294. console.log(res);
  2295. });
  2296. /* 获取应用类型 */
  2297. application_types().then((res) => {
  2298. if (res.status !== 200) return;
  2299. this.appTypeList = res.data.application_types;
  2300. });
  2301. },
  2302. /* 编辑 */
  2303. // 修改 initEdit 方法
  2304. async initEdit() {
  2305. try {
  2306. // 深拷贝以避免直接修改原对象
  2307. this.editForm = JSON.parse(JSON.stringify(this.$route.query.card));
  2308. // 确保必要的属性都被初始化
  2309. this.editForm = {
  2310. ...this.editForm,
  2311. document_directories: this.editForm.document_directories || [],
  2312. documents: this.editForm.documents || [],
  2313. radio: "1", // 默认值
  2314. };
  2315. // 处理 role_permissions 和 custom_variables
  2316. this.handleJsonFields();
  2317. // 设置 radio 的值
  2318. if (Array.isArray(this.editForm.document_directories)) {
  2319. if (this.editForm.document_directories.length === 0) {
  2320. this.editForm.radio = "2";
  2321. }
  2322. } else {
  2323. this.editForm.radio = "2";
  2324. }
  2325. if (Array.isArray(this.editForm.documents)) {
  2326. if (
  2327. this.editForm.documents.length === 0 &&
  2328. this.editForm.radio !== "2"
  2329. ) {
  2330. this.editForm.radio = "1";
  2331. }
  2332. }
  2333. // 初始化临时存储,保存原始数据
  2334. this.editForm.tempDocuments = Array.isArray(this.editForm.documents)
  2335. ? [...this.editForm.documents]
  2336. : [];
  2337. this.editForm.tempDirectories = Array.isArray(
  2338. this.editForm.document_directories
  2339. )
  2340. ? [...this.editForm.document_directories]
  2341. : [];
  2342. // 并行加载知识库目录列表
  2343. if (
  2344. this.editForm.knowledge_base_names &&
  2345. this.editForm.knowledge_base_names.length > 0
  2346. ) {
  2347. const directoryListPromise = this.loadEditDirectoryList(
  2348. this.editForm.knowledge_base_names
  2349. );
  2350. await directoryListPromise;
  2351. }
  2352. // 处理目录和文档选择
  2353. await this.handleDirectoryAndDocumentSelection();
  2354. } catch (error) {
  2355. console.error("Error in initEdit:", error);
  2356. this.$message.error("初始化编辑表单时出错");
  2357. }
  2358. },
  2359. async handleDirectoryAndDocumentSelection() {
  2360. // 检查是否为["全部"]的情况
  2361. if (
  2362. this.editForm.document_directories.length === 1 &&
  2363. this.editForm.document_directories[0] === "全部"
  2364. ) {
  2365. // 直接设置为"001"(全部)的ID,不需要查询接口
  2366. this.editForm.document_directories = "001";
  2367. // 如果文档为空或包含"全部",设置为all
  2368. if (
  2369. !this.editForm.documents ||
  2370. this.editForm.documents.length === 0 ||
  2371. this.editForm.documents.includes("全部")
  2372. ) {
  2373. this.editForm.documents = ["all"];
  2374. }
  2375. return;
  2376. }
  2377. // 其他情况保持原有逻辑
  2378. const dirName = this.editForm.document_directories[0];
  2379. const dir = this.editDirectoryList.find((dir) => dir.name === dirName);
  2380. if (!dir) {
  2381. return;
  2382. }
  2383. this.editForm.document_directories = dir.id;
  2384. // 延迟加载文档列表
  2385. await this.$nextTick();
  2386. await this.loadEditDocumentList(dir.id);
  2387. // 处理文档选择
  2388. this.handleDocumentSelection();
  2389. },
  2390. handleDocumentSelection() {
  2391. if (!this.editForm.documents || this.editForm.documents.length === 0) {
  2392. this.editForm.documents = ["all"];
  2393. } else if (this.editForm.documents.includes("全部")) {
  2394. this.editForm.documents = ["all"];
  2395. } else {
  2396. this.editForm.documents = this.editDocumentList
  2397. .filter((doc) => this.editForm.documents.includes(doc.name))
  2398. .map((doc) => doc.id);
  2399. }
  2400. },
  2401. handleJsonFields() {
  2402. // 处理 role_permissions
  2403. if (this.editForm.role_permissions) {
  2404. if (typeof this.editForm.role_permissions !== "string") {
  2405. try {
  2406. this.editForm.role_permissions = JSON.stringify(
  2407. this.editForm.role_permissions
  2408. );
  2409. } catch (error) {
  2410. console.error("Error stringifying role_permissions:", error);
  2411. this.editForm.role_permissions = "";
  2412. }
  2413. }
  2414. } else {
  2415. this.editForm.role_permissions = "";
  2416. }
  2417. // 处理 custom_variables
  2418. if (this.editForm.custom_variables) {
  2419. this.editForm.custom_variables = JSON.stringify(
  2420. this.editForm.custom_variables
  2421. );
  2422. } else {
  2423. this.editForm.custom_variables = "";
  2424. }
  2425. },
  2426. // 同样修改 initView 方法
  2427. initView() {
  2428. try {
  2429. // 根据 card 的数据填充 viewForm
  2430. this.viewForm = JSON.parse(JSON.stringify(this.$route.query.card));
  2431. // 确保必要的属性都被初始化
  2432. this.viewForm = {
  2433. ...this.viewForm,
  2434. document_directories: this.viewForm.document_directories || [],
  2435. documents: this.viewForm.documents || [],
  2436. radio: "1", // 默认值
  2437. };
  2438. // 设置 radio 的值
  2439. if (Array.isArray(this.viewForm.document_directories)) {
  2440. if (this.viewForm.document_directories.length === 0) {
  2441. this.viewForm.radio = "2";
  2442. }
  2443. } else {
  2444. this.viewForm.radio = "2";
  2445. }
  2446. if (Array.isArray(this.viewForm.documents)) {
  2447. if (
  2448. this.viewForm.documents.length === 0 &&
  2449. this.viewForm.radio !== "2"
  2450. ) {
  2451. this.viewForm.radio = "1";
  2452. }
  2453. }
  2454. // 处理 knowledge_base_names
  2455. if (
  2456. this.viewForm.knowledge_base_names &&
  2457. Array.isArray(this.viewForm.knowledge_base_names)
  2458. ) {
  2459. this.viewForm.knowledge_base_names =
  2460. this.viewForm.knowledge_base_names.map((name) => {
  2461. const kb = this.kneList.find((kb) => kb.name === name);
  2462. return kb ? kb.id : name;
  2463. });
  2464. } else {
  2465. this.viewForm.knowledge_base_names = [];
  2466. }
  2467. // 处理 role_permissions 和 custom_variables
  2468. if (this.viewForm.role_permissions) {
  2469. if (typeof this.viewForm.role_permissions !== "string") {
  2470. try {
  2471. this.viewForm.role_permissions = JSON.stringify(
  2472. this.viewForm.role_permissions
  2473. );
  2474. } catch (error) {
  2475. console.error("Error stringifying role_permissions:", error);
  2476. this.viewForm.role_permissions = "";
  2477. }
  2478. }
  2479. } else {
  2480. this.viewForm.role_permissions = "";
  2481. }
  2482. if (this.viewForm.custom_variables) {
  2483. this.viewForm.custom_variables = JSON.stringify(
  2484. this.viewForm.custom_variables
  2485. );
  2486. } else {
  2487. this.viewForm.custom_variables = "";
  2488. }
  2489. // 处理文档选择
  2490. if (
  2491. !this.viewForm.documents ||
  2492. !Array.isArray(this.viewForm.documents) ||
  2493. this.viewForm.documents.length === 0 ||
  2494. this.viewForm.documents.includes("全部")
  2495. ) {
  2496. this.viewForm.documents = ["全部"];
  2497. }
  2498. // 加载知识库对应的目录列表
  2499. if (
  2500. this.viewForm.knowledge_base_names &&
  2501. this.viewForm.knowledge_base_names.length > 0
  2502. ) {
  2503. this.loadEditDirectoryList(this.viewForm.knowledge_base_names);
  2504. }
  2505. } catch (error) {
  2506. console.error("Error in initView:", error);
  2507. this.$message.error("初始化查看表单时出错");
  2508. }
  2509. },
  2510. },
  2511. };
  2512. </script>
  2513. <style lang='scss' scoped>
  2514. .header {
  2515. margin-left: 20px;
  2516. h2 {
  2517. margin-bottom: 0px;
  2518. }
  2519. }
  2520. .center {
  2521. margin: 10px;
  2522. ::v-deep .el-form--inline .el-form-item {
  2523. display: block;
  2524. }
  2525. ::v-deep .el-input-number--medium {
  2526. width: 150px;
  2527. }
  2528. }
  2529. .footer {
  2530. display: flex;
  2531. justify-content: flex-end;
  2532. margin: 0 100px 20px;
  2533. }
  2534. ::v-deep .avatar-uploader .el-upload {
  2535. border: 1px dashed #d9d9d9;
  2536. border-radius: 6px;
  2537. cursor: pointer;
  2538. position: relative;
  2539. overflow: hidden;
  2540. }
  2541. .avatar-uploader .el-upload:hover {
  2542. border-color: #409eff;
  2543. }
  2544. .avatar-uploader-icon {
  2545. font-size: 28px;
  2546. color: #8c939d;
  2547. width: 100px;
  2548. height: 100px;
  2549. line-height: 100px;
  2550. text-align: center;
  2551. }
  2552. .avatar {
  2553. width: 100px;
  2554. height: 100px;
  2555. display: block;
  2556. }
  2557. .hint {
  2558. font-size: 12px;
  2559. color: #c0c4cc;
  2560. }
  2561. </style>