index.html 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. <html>
  2. <head>
  3. <title>冰蓝达者</title>
  4. <meta charset="utf-8" />
  5. <meta name="apple-mobile-web-app-capable" content="yes" />
  6. <meta name="mobile-web-app-capable" content="yes" />
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  8. <meta name="viewport" content="width=400, user-scalable=no" />
  9. <link rel="shortcut icon" href="wd.png" />
  10. <!-- <link href="https://npm.elemecdn.com/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
  11. <link href="https://npm.elemecdn.com/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  12. <script src="https://npm.elemecdn.com/vue@2.x/dist/vue.min.js"></script>
  13. <script src="https://npm.elemecdn.com/vuetify@2.x/dist/vuetify.min.js"></script> -->
  14. <link rel="manifest" href="manifest.json" />
  15. <link href="static/mdi_font/css/materialdesignicons.min.css"
  16. rel="stylesheet" />
  17. <link href="static/vuetify.min.css" rel="stylesheet" />
  18. <script src="static/vue.js"></script>
  19. <script src="static/vue-i18n.js"></script>
  20. <script src="static/vuetify.min.js"></script>
  21. <script src="static/markdown-it.min.js"></script>
  22. <script src="static/strings.js"></script>
  23. <style>
  24. .v-navigation-drawer__content::-webkit-scrollbar {
  25. width: 5px;
  26. }
  27. .v-navigation-drawer__content::-webkit-scrollbar-track {
  28. background-color: #333;
  29. }
  30. /* 滚动条的滑轨背景颜色 */
  31. .v-navigation-drawer__content::-webkit-scrollbar-thumb {
  32. background-color: #aaa;
  33. }
  34. /* 滑块颜色 */
  35. .v-navigation-drawer__content::-webkit-scrollbar-button {
  36. display: none;
  37. }
  38. /* 滑轨两头的监听按钮颜色 */
  39. .answer th,
  40. .answer td {
  41. outline: 1px solid;
  42. padding: 2px;
  43. }
  44. div[aria-haspopup] {
  45. display: inline-block;
  46. margin: 10px;
  47. }
  48. .answer table {
  49. outline: 1px solid;
  50. }
  51. .ask,
  52. .answer {
  53. padding: 1em;
  54. background: #fff;
  55. border-radius: 16px;
  56. max-width: 80%;
  57. margin: 0 10px;
  58. word-break: break-word;
  59. line-height: 1.2;
  60. }
  61. pre:has(code) {
  62. background-color: rgb(0 0 0);
  63. color: white;
  64. padding: 3px;
  65. border-radius: 5px;
  66. }
  67. code::before {
  68. content: attr(class);
  69. color: yellow;
  70. float: right;
  71. }
  72. .ask {
  73. margin-left: auto;
  74. white-space: break-spaces;
  75. }
  76. .头像 {
  77. margin-bottom: auto;
  78. color: #fff !important;
  79. position: unset;
  80. filter: brightness(150%) grayscale(50%);
  81. }
  82. .answer img {
  83. max-width: 100%;
  84. }
  85. #app,
  86. .v-application--wrap {
  87. background: transparent;
  88. min-height: 100%;
  89. /* background: #cecece0a; */
  90. }
  91. .float {
  92. position: absolute !important;
  93. }
  94. .float button {
  95. display: block !important;
  96. }
  97. header {
  98. position: fixed !important;
  99. top: 0;
  100. z-index: 1;
  101. width: 100%;
  102. }
  103. footer {
  104. padding-bottom: 54px !important;
  105. }
  106. .v-tabs {
  107. position: fixed !important;
  108. bottom: 0;
  109. z-index: 3;
  110. width: 100%;
  111. }
  112. html,
  113. .v-window__container {
  114. background-color: #0001;
  115. }
  116. [v-cloak] {
  117. display: none;
  118. }
  119. .v-tabs-items {
  120. height: 100%;
  121. }
  122. .v-window-item {
  123. min-height: 100%;
  124. margin: 3%;
  125. }
  126. .v-window-item:has(iframe) {
  127. margin: 0;
  128. }
  129. .v-tab.v-tab {
  130. color: inherit;
  131. font-weight: bold;
  132. font-size: large;
  133. }
  134. .user-avatar {
  135. background-color: #0000a0bb;
  136. }
  137. .bot-avatar {
  138. background-color: #7a0099bb;
  139. }
  140. .v-application p {
  141. margin-bottom: 10px;
  142. white-space: break-spaces;
  143. }
  144. .v-application a {
  145. line-height: 1.3;
  146. }
  147. .v-input.v-textarea.v-text-field {
  148. padding-top: 0;
  149. margin-top: 0;
  150. }
  151. .v-application--is-ltr .v-input--selection-controls__input {
  152. /* 紧凑右上角开关 */
  153. margin-right: 0px;
  154. }
  155. .v-slide-group:not(.v-slide-group--has-affixes)>.v-slide-group__prev {
  156. /* 隐藏手机选项卡左侧空白 */
  157. display: none !important;
  158. }
  159. .v-sheet.v-card {
  160. margin: 20px;
  161. padding: 10px;
  162. }
  163. .chat_toolbar {
  164. width: 100%;
  165. color: #d0d0d0;
  166. }
  167. .chat_toolbar .v-input,
  168. .chat_toolbar div[aria-haspopup] {
  169. margin: 0;
  170. }
  171. </style>
  172. </head>
  173. <body>
  174. <div id="app" v-cloak>
  175. <v-app>
  176. <v-navigation-drawer width="350" v-model="drawer"
  177. style="height: 100%; position: fixed" temporary>
  178. <v-list>
  179. <v-list-item>
  180. <v-list-item-content>
  181. <v-list-item-title class="text-h6"> Auto </v-list-item-title>
  182. <br />
  183. <v-list-item-subtitle>
  184. {{$t("feature_selection") }}
  185. </v-list-item-subtitle>
  186. </v-list-item-content>
  187. </v-list-item>
  188. <v-divider></v-divider>
  189. <v-list-item v-for="func in func_menu" @click.stop="load_func(func)"
  190. v-if="func.name!=''">
  191. <v-list-item-title v-text="func.name"> </v-list-item-title>
  192. </v-list-item>
  193. </v-list>
  194. </v-navigation-drawer>
  195. <v-toolbar v-if="show_header" style="opacity: 80%">
  196. <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
  197. <v-toolbar-title><a
  198. :style="'filter: brightness(50%);font-size: larger;text-decoration: none; color: '+color"
  199. href="https://github.com/l15y/wenda">{{$t("wenda") }}</a></v-toolbar-title>
  200. </v-toolbar>
  201. <v-tabs-items v-model="tab" style="margin-bottom: 48px"
  202. :style="{marginTop: show_header ? '64px' : '0px'}">
  203. <v-tab-item key="chat">
  204. <div v-for="(current_conversation, index) in chat" :key="index"
  205. :class="['d-flex flex-row align-center my-2', current_conversation.role == 'user' ? 'justify-end': null]">
  206. <span v-if="current_conversation.role == 'user'" class="ask"
  207. v-text="current_conversation.keyword||current_conversation.content"></span>
  208. <v-hover v-slot="{ hover }">
  209. <div style="margin-bottom: auto;">
  210. <v-avatar
  211. :class="current_conversation.role == 'user' ? 'user-avatar': 'bot-avatar'"
  212. size="36"
  213. class="头像">
  214. <span>{{ {user:"问",AI:'答'}[current_conversation.role]
  215. }}</span>
  216. </v-avatar>
  217. <br />
  218. <v-expand-transition>
  219. <div v-if="hover" class="float">
  220. <v-icon v-if="!loading" large text :color="color"
  221. @click="delete_current_conversation(current_conversation)">
  222. mdi-delete
  223. </v-icon>
  224. <v-icon large text :color="color"
  225. @click="copy(current_conversation.content)">
  226. mdi-content-copy
  227. </v-icon>
  228. <v-icon large text :color="color"
  229. @click="edit(current_conversation)">
  230. mdi-pencil
  231. </v-icon>
  232. </div>
  233. </v-expand-transition>
  234. </div>
  235. </v-hover>
  236. <span v-if="current_conversation.role != 'user'" class="answer">
  237. <div v-html="md2html(current_conversation.content)"></div>
  238. <template style="margin-top: 10px">
  239. <v-tooltip bottom
  240. v-for="source in current_conversation.sources"
  241. :max-width="500">
  242. <template v-slot:activator="{ on, attrs }">
  243. <div v-bind="attrs" v-on="on" style="margin: 3px">
  244. <v-btn outlined style="
  245. margin-top: 0px;
  246. max-width: -webkit-fill-available;
  247. overflow-x: hidden;
  248. " :color="color" rounded="lg"
  249. @click='source.url?window.open(source.url,"_blank"):source.click()'
  250. x-small>
  251. <span class="d-inline-block text-truncate"
  252. style="max-width: 150px">
  253. {{source.title}}
  254. </span>
  255. </v-btn>
  256. </div>
  257. </template>
  258. <span v-html="source.content"></span>
  259. </v-tooltip>
  260. </template>
  261. </span>
  262. </div>
  263. <br /><br /><br /><br /><br /><br /><br /><br /><br />
  264. <v-footer fixed class="pr-0">
  265. <div class="chat_toolbar">
  266. <v-tooltip bottom v-for="button in buttons">
  267. <template v-slot:activator="{ on, attrs }">
  268. <div v-bind="attrs" v-on="on">
  269. <v-icon :color="button.color()" text @click="button.click">
  270. mdi-{{button.icon}}
  271. </v-icon>
  272. </div>
  273. </template>
  274. <span>{{button.description}}</span>
  275. </v-tooltip>
  276. {{buttons.length>0?"|":""}}
  277. <v-tooltip bottom>
  278. <template v-slot:activator="{ on, attrs }">
  279. <div v-bind="attrs" v-on="on">
  280. <v-icon :color="current_func=='知识库'?color:''" text
  281. @click="current_func=='知识库'?current_func='':current_func='知识库'">
  282. mdi-book-open-variant
  283. </v-icon>
  284. </div>
  285. </template>
  286. <span>{{$t("zhishiku")}}</span>
  287. </v-tooltip>
  288. <v-tooltip bottom>
  289. <template v-slot:activator="{ on, attrs }">
  290. <div v-bind="attrs" v-on="on">
  291. <v-icon :color="current_func=='快速知识库'?color:''" text
  292. @click="current_func=='快速知识库'?current_func='':current_func='快速知识库'">
  293. mdi-lightning-bolt
  294. </v-icon>
  295. </div>
  296. </template>
  297. <span>{{$t("zhishiku")}}快速模式</span>
  298. </v-tooltip>
  299. |
  300. <v-tooltip bottom>
  301. <template v-slot:activator="{ on, attrs }">
  302. <div v-bind="attrs" v-on="on">
  303. <v-icon :color="sst_started?color:''" text
  304. @click="sst_started?stop_listen():listen()">
  305. mdi-microphone
  306. </v-icon>
  307. </div>
  308. </template>
  309. <span>语音输入</span>
  310. </v-tooltip>
  311. <v-tooltip bottom>
  312. <template v-slot:activator="{ on, attrs }">
  313. <div v-bind="attrs" v-on="on">
  314. <v-icon :color="tts_on?color:''" text
  315. @click="tts_on=!tts_on">
  316. mdi-headset
  317. </v-icon>
  318. </div>
  319. </template>
  320. <span>阅读文本</span>
  321. </v-tooltip>
  322. |
  323. <v-tooltip bottom>
  324. <template v-slot:activator="{ on, attrs }">
  325. <div v-bind="attrs" v-on="on">
  326. <v-icon :color="history_on?color:''" text
  327. @click="history_on=!history_on">
  328. mdi-history
  329. </v-icon>
  330. </div>
  331. </template>
  332. <span>{{$t("history")}}</span>
  333. </v-tooltip>
  334. <v-tooltip bottom v-if="(!loading)&&(chat.length>0)">
  335. <template v-slot:activator="{ on, attrs }">
  336. <v-icon :color="color" v-bind="attrs" v-on="on"
  337. @click="re_generate()">
  338. mdi-refresh
  339. </v-icon>
  340. </template>
  341. <span>{{$t("re_generate")}}</span>
  342. </v-tooltip>
  343. <v-tooltip bottom>
  344. <template v-slot:activator="{ on, attrs }">
  345. <div v-bind="attrs" v-on="on">
  346. <v-icon :color="color" text
  347. @click="chat=[];save_history()">
  348. mdi-delete-clock-outline
  349. </v-icon>
  350. </div>
  351. </template>
  352. <span>{{$t("clear_history")}}</span>
  353. </v-tooltip>
  354. <b>TPS:{{TPS.toFixed(2)}}</b>
  355. </div>
  356. <v-row no-gutters>
  357. <v-col cols="8" md="11">
  358. <v-textarea hide-details no-resize rows="3" :loading="loading"
  359. :placeholder="$t(func_mode.description)"
  360. @keypress.enter="submit" v-model="question">
  361. </v-textarea>
  362. </v-col>
  363. <v-col cols="4" md="1">
  364. <div style="text-align: center">
  365. <v-btn style="margin-bottom: 10px" v-if="loading"
  366. :color="color" rounded="lg" dark size="x-large"
  367. @click="abort_chatting()">
  368. {{$t("abord")}}
  369. </v-btn>
  370. <v-btn style="margin-bottom: 10px" :color="color"
  371. rounded="lg" dark size="x-large" @click="submit()"
  372. v-if="!loading">
  373. {{$t("send")}}
  374. </v-btn>
  375. <br />
  376. <v-expand-transition>
  377. <v-chip v-if="func_mode.name!=''" :color="color" outlined
  378. close @click:close="current_func=''">
  379. <span class="d-inline-block text-truncate"
  380. style="max-width: 120px">
  381. {{func_mode.name}}
  382. </span>
  383. </v-chip>
  384. </v-expand-transition>
  385. </div>
  386. </v-col>
  387. </v-row>
  388. </v-footer>
  389. </v-tab-item>
  390. <v-tab-item key="apps">
  391. <v-text-field v-model="autos_search" append-icon="mdi-magnify"
  392. label="搜索应用" single-line
  393. hide-details></v-text-field>
  394. <v-divider></v-divider>
  395. <v-data-table item-key="name" show-expand :search="autos_search"
  396. :headers="[{ text: '名称', value: 'name' },{ text: '描述', value: 'description' },{ text: '禁用', value: 'disabled' }, { text: '', value: 'data-table-expand' },]"
  397. :items="autos">
  398. <template v-slot:item.disabled="{ item }">
  399. <v-switch v-model="item.disabled" :color="color"></v-switch>
  400. </template>
  401. <template v-slot:expanded-item="{ headers, item }">
  402. <v-icon v-if="find_auto(item.name)>-1" large text
  403. @click="del_auto(item.name)">
  404. mdi-delete
  405. </v-icon>
  406. <pre style="width: 200px" :colspan="headers.length">{{ item.content }}</pre>
  407. </template>
  408. </v-data-table>
  409. <v-btn :color="color" dark size="x-large"
  410. @click="disabled_auto={};app.autos.forEach(auto=>disabled_auto[auto.name]=auto.disabled);localStorage['wenda_disabled_auto'] =JSON.stringify(disabled_auto)">
  411. 保存(刷新后起效)
  412. </v-btn>
  413. </v-tab-item>
  414. <v-tab-item key="setting">
  415. <h1>参数管理</h1>
  416. <v-card elevation="2">
  417. <v-card-title>模型运行参数</v-card-title>
  418. <v-divider></v-divider>
  419. <v-card-text><br />
  420. <v-form>
  421. <v-tooltip bottom>
  422. <template v-slot:activator="{ on, attrs }">
  423. <div v-bind="attrs" v-on="on" style="width: 100%;">
  424. <v-slider :min="2000" :max="8192" :step="100"
  425. v-model="max_length" :thumb-color="color"
  426. label="max_length" thumb-label><template v-slot:append>
  427. <v-text-field v-model="max_length" class="mt-0 pt-0"
  428. type="number" style="width: 60px">
  429. </v-text-field>
  430. </template>
  431. </v-slider>
  432. </div>
  433. </template>
  434. <span>最大生成token数(注意,不同模型对此参数实现不同。部分会将输入token计入)</span>
  435. </v-tooltip>
  436. <v-tooltip bottom>
  437. <template v-slot:activator="{ on, attrs }">
  438. <div v-bind="attrs" v-on="on" style="width: 100%;">
  439. <v-slider :min="0" :max="2" :step="0.1"
  440. v-model="temperature" :thumb-color="color"
  441. label="temperature" thumb-label><template
  442. v-slot:append>
  443. <v-text-field v-model="temperature"
  444. class="mt-0 pt-0" type="number"
  445. style="width: 60px">
  446. </v-text-field>
  447. </template>
  448. </v-slider>
  449. </div>
  450. </template>
  451. <span>温度(随机性)</span>
  452. </v-tooltip>
  453. <v-tooltip bottom>
  454. <template v-slot:activator="{ on, attrs }">
  455. <div v-bind="attrs" v-on="on" style="width: 100%;">
  456. <v-slider :min="0" :max="1" :step="0.1" v-model="top_p"
  457. :thumb-color="color" label="top_p"
  458. thumb-label>
  459. <template v-slot:append>
  460. <v-text-field v-model="top_p" class="mt-0 pt-0"
  461. type="number" style="width: 60px">
  462. </v-text-field>
  463. </template>
  464. </v-slider>
  465. </div>
  466. </template>
  467. <span>选取的前p个输出数量</span>
  468. </v-tooltip>
  469. <v-tooltip bottom>
  470. <template v-slot:activator="{ on, attrs }">
  471. <div v-bind="attrs" v-on="on" style="width: 100%;">
  472. <v-slider :min="0.2" :max="1.8" :step="0.1"
  473. v-model="cfg_factor" :thumb-color="color"
  474. label="cfg_factor" thumb-label>
  475. <template v-slot:append>
  476. <v-text-field v-model="cfg_factor" class="mt-0 pt-0"
  477. type="number" style="width: 60px">
  478. </v-text-field>
  479. </template>
  480. </v-slider>
  481. </div>
  482. </template>
  483. <span>classifier free guidence 因子,当前仅支持rwkv</span>
  484. </v-tooltip>
  485. <v-row>
  486. <v-col cols="12" sm="2">
  487. <v-switch hide-details
  488. :label="$t('simplify_historical_information')"
  489. :color="color" inset
  490. v-model="simplify_historical_information">
  491. </v-switch>
  492. </v-col>
  493. <v-col cols="12" sm="10">
  494. <v-slider :min="0" :max="10" :step="1"
  495. v-model="history_limit" :thumb-color="color"
  496. label="对话轮数限制"
  497. thumb-label>
  498. <template v-slot:append>
  499. <v-text-field v-model="history_limit"
  500. class="mt-0 pt-0" type="number" style="width: 60px">
  501. </v-text-field>
  502. </template>
  503. </v-slider>
  504. </v-col>
  505. </v-row>
  506. </v-form>
  507. </v-card-text>
  508. </v-card>
  509. <v-card elevation="2">
  510. <v-card-title>动态调整知识库</v-card-title>
  511. <v-divider></v-divider>
  512. <v-card-text>网页刷新后恢复<br />
  513. <v-row>
  514. <v-col cols="8">
  515. <v-combobox v-model="zsk_libraryStategy"
  516. :items="['rtst:3:default agents:0','sogowx:3 bing:3']"
  517. label="库参数"></v-combobox>
  518. </v-col>
  519. <v-col cols="4">
  520. <v-text-field v-model="zsk_maxItmes" outlined label="最大数量"></v-text-field>
  521. </v-col>
  522. </v-row>
  523. </v-card-text>
  524. <v-card-actions>
  525. <v-btn :color="color" dark size="x-large"
  526. @click="find=(s, step = 1)=>find_dynamic(s, step, {libraryStategy:zsk_libraryStategy,maxItmes:zsk_maxItmes})">
  527. 设置
  528. </v-btn>
  529. </v-card-actions>
  530. </v-card>
  531. <v-card elevation="2">
  532. <v-card-title>知识提取提示词</v-card-title>
  533. <v-divider></v-divider>
  534. <v-card-text><br />
  535. <v-combobox v-model="zsk_summarize_prompt"
  536. :items="['总结以下文段中与问题相关的信息。']" label="总结提示词"></v-combobox>
  537. <v-combobox v-model="zsk_answer_prompt"
  538. :items="['学习以下文段,用中文回答问题。如果无法从中得到答案,忽略文段内容并用中文回答问题。']"
  539. label="回答提示词"></v-combobox>
  540. </v-card-text>
  541. </v-card>
  542. <v-card elevation="2">
  543. <v-card-title>查询知识库</v-card-title>
  544. <v-divider></v-divider><br />
  545. <v-card-text>
  546. <v-row>
  547. <v-col cols="12" sm="6">
  548. <v-text-field v-model="test_zsk_prompt" outlined label="关键词"
  549. clearable></v-text-field>
  550. </v-col>
  551. <v-col cols="12" sm="6">
  552. <v-text-field v-model="zsk_step" outlined label="上下文数量"
  553. clearable></v-text-field>
  554. </v-col>
  555. </v-row>
  556. <v-data-table
  557. :headers="[{ text: '来源', value: 'title' },{ text: '分数', value: 'score' },{ text: '内容', value: 'content' }]"
  558. :items="zhishiku" hide-default-header hide-default-footer>
  559. <template v-slot:item.title="{ item }">
  560. <p v-html="md2html(item.title)"></p>
  561. </template>
  562. </v-data-table>
  563. <v-card-actions>
  564. <v-btn :color="color" dark size="x-large"
  565. @click="find(test_zsk_prompt,zsk_step)">
  566. 测试
  567. </v-btn>
  568. </v-card-actions>
  569. </v-card-text>
  570. </v-card>
  571. </v-tab-item>
  572. <v-tab-item v-for="plugin in plugins" :key="plugin.name">
  573. <iframe frameborder="0"
  574. :src="plugin.url+'?time='+new Date().getTime()"
  575. style="width: 100%; height: 100%"></iframe>
  576. </v-tab-item>
  577. </v-tabs-items>
  578. <v-tabs v-model="tab" :color="color" style="background: #fff">
  579. <v-tabs-slider :color="color"></v-tabs-slider>
  580. <v-tab key="chat">
  581. <v-icon text> mdi-tooltip-edit-outline </v-icon>
  582. </v-tab>
  583. <v-tab key="apps">
  584. <v-icon text> mdi-application-parentheses </v-icon>
  585. </v-tab>
  586. <v-tab key="setting">
  587. <v-icon text> mdi-cog </v-icon>
  588. </v-tab>
  589. <v-tab v-for="plugin in plugins" :key="plugin.name">
  590. <v-icon text v-if="plugin.icon"> {{"mdi-"+plugin.icon}} </v-icon>{{plugin.name}}
  591. </v-tab>
  592. </v-tabs>
  593. <v-snackbar v-model="snackbar" :timeout="3000"
  594. style="white-space: pre-line">{{snackbar_text}}</v-snackbar>
  595. <v-dialog v-model="show_dialog" persistent max-width="600px">
  596. <v-card class="ma-0 pa0">
  597. <v-card-title>
  598. <span class="text-h5">{{dialog_title}}</span>
  599. </v-card-title>
  600. <v-card-text>
  601. <v-container>
  602. <v-textarea autofocus v-model="dialog_input" rows="5"
  603. hide-details="auto"
  604. @keypress.enter="show_dialog = false;window.dialog_input_resolver()"></v-textarea>
  605. </v-container>
  606. </v-card-text>
  607. <v-card-actions>
  608. <v-spacer></v-spacer>
  609. <v-btn color="blue darken-1" text
  610. @click="show_dialog = false;dialog_input='';window.dialog_input_resolver()">
  611. 取消
  612. </v-btn>
  613. <v-btn color="blue darken-1" text
  614. @click="show_dialog = false;window.dialog_input_resolver()">
  615. 确认
  616. </v-btn>
  617. </v-card-actions>
  618. </v-card>
  619. </v-dialog>
  620. </v-app>
  621. </div>
  622. <script>
  623. func = [
  624. {
  625. name: "",
  626. description: "input_question",
  627. question: "",
  628. },
  629. ];
  630. app = new Vue({
  631. el: "#app",
  632. vuetify: new Vuetify(),
  633. i18n: i18n,
  634. watch: {
  635. current_func: (current_func) => {
  636. current_func = app.func_menu.find((i) => i.name == current_func);
  637. if (current_func) {
  638. app.func_mode = current_func
  639. document.location.hash = current_func.name
  640. } else {
  641. app.current_func = ''
  642. }
  643. },
  644. tab: (val) => {
  645. if (val > 2) {
  646. if (app.plugins[val - 3].hide_title == true) {
  647. app.show_header = false;
  648. return;
  649. }
  650. }
  651. app.show_header = true;
  652. },
  653. },
  654. data() {
  655. return {
  656. // 用户输入的问题
  657. question: "",
  658. // 聊天记录
  659. chat: JSON.parse(localStorage["wenda_chat_history"] || "[]"),
  660. // 是否开启历史记录
  661. history_on: false,
  662. // 历史记录的数量限制
  663. history_limit: 5,
  664. // 功能菜单
  665. func_menu: func,
  666. // 当前选中的功能
  667. current_func: "",
  668. // 知识库检索策略
  669. zsk_libraryStategy: "",
  670. // 知识库检索结果数量
  671. zsk_maxItmes: 5,
  672. // 知识库总结问题的提示
  673. zsk_summarize_prompt: "总结以下文段中与问题相关的信息。",
  674. // 知识库回答问题的提示
  675. zsk_answer_prompt:
  676. "总结以下文段中与问题相关的信息。",
  677. // 载入的auto
  678. func_mode: func[0],
  679. // 插件列表
  680. plugins: [],
  681. // 按钮列表
  682. buttons: [],
  683. // auto列表
  684. autos: [],
  685. // 是否简化历史记录
  686. simplify_historical_information: false,
  687. // 是否开启语音合成
  688. tts_on: false,
  689. // 是否开启语音识别
  690. sst_started: false,
  691. // 测试知识库的提示
  692. test_zsk_prompt: "",
  693. // 测试知识库的步骤
  694. zsk_step: 2,
  695. // 知识库
  696. zhishiku: [],
  697. // 设置
  698. setting: {},
  699. // 当前选中的tab
  700. tab: 0,
  701. // 生成回答的温度
  702. temperature: 0.8,
  703. // 生成回答的最大长度
  704. max_length: 4096,
  705. // 生成回答的top_p
  706. top_p: 0.8,
  707. //classifier free guidence 因子,当前仅支持rwkv
  708. cfg_factor: 1,
  709. // 语言模型类型
  710. llm_type: "",
  711. // tab列表
  712. tabs: ["chat", "zhishiku", "setting"],
  713. // 是否显示snackbar
  714. snackbar: false,
  715. // snackbar的文本
  716. snackbar_text: "",
  717. // 是否正在加载
  718. loading: false,
  719. // 是否显示左侧菜单
  720. drawer: false,
  721. //是否显示顶部菜单栏,逻辑中根据插件的hide_title属性来判断是否需要隐藏
  722. show_header: true,
  723. //主题色
  724. color: "purple",
  725. //管理界面中,搜索auto的关键词
  726. autos_search: "",
  727. TPS: 0,
  728. //显示对话框
  729. show_dialog: false,
  730. //对话框标题
  731. dialog_title: "",
  732. //对话框用户输入
  733. dialog_input: ""
  734. };
  735. },
  736. methods: {
  737. md2html: (conent) => {
  738. // return conent
  739. conent = String(conent);
  740. let md = new markdownit();
  741. // md.disable(['link', 'image'])
  742. return md.render(conent).replace(/<a /g, '<a target="_blank"').replace(/[\r\n]+!$/g, "<br>")
  743. },
  744. },
  745. });
  746. //获取用户输入
  747. input = async (title = '请输入', input = '') => {
  748. app.dialog_title = title
  749. app.dialog_input = input
  750. app.show_dialog = true
  751. await new Promise(resolve => {
  752. window.dialog_input_resolver = resolve
  753. })
  754. return app.dialog_input
  755. }
  756. //编辑会话内容
  757. edit = async (current_conversation) => {
  758. let s修改后的内容 = await input('请输入修改后的内容', current_conversation.content)
  759. if (s修改后的内容) {
  760. current_conversation.content = s修改后的内容
  761. if(current_conversation.keyword)current_conversation.keyword=s修改后的内容
  762. alert('修改成功')
  763. } else
  764. alert('取消修改')
  765. }
  766. // 加载指定功能
  767. load_func = (func) => {
  768. app.current_func = func.name;
  769. app.drawer = false;
  770. };
  771. // 从 app 的 chat 数组中删除当前的对话项并保存更新后的历史记录
  772. delete_current_conversation = (item) => {
  773. app.chat.splice(Math.floor(app.chat.indexOf(item) / 2) * 2, 2);
  774. save_history();
  775. };
  776. // 将 is_abord 标志设置为 true 并关闭 WebSocket 连接
  777. abort_chatting = () => {
  778. is_abord = true;
  779. ws.close();
  780. };
  781. // 处理表单提交事件并将用户的输入发送到服务器
  782. submit = async (e) => {
  783. if (e && e.shiftKey) {
  784. return
  785. }
  786. e && e.preventDefault()
  787. if (typeof app.func_mode.question == "function") {
  788. await app.func_mode.question(app.question);
  789. app.question = ''
  790. } else {
  791. let Q = app.question
  792. if (app.history_on)
  793. await send(app.func_mode.question + Q, Q, show = true, sources = [],
  794. addition_args = { cfg_factor: app.cfg_factor, cfg_ctx: Q });
  795. else
  796. await send(app.func_mode.question + Q, Q);
  797. }
  798. };
  799. // 重新生成用户的最后一条消息并将其发送到服务器
  800. re_generate = () => {
  801. let last_send = app.chat[app.chat.length - 2];
  802. app.chat.splice(app.chat.length - 2, 2);
  803. if (last_send.keyword) app.question = last_send.keyword;
  804. else app.question = last_send.content;
  805. submit();
  806. };
  807. // 总结聊天历史记录并提示用户提取关键信息
  808. summerize_history = async () => {
  809. zsk(false);
  810. lsdh(false);
  811. let prompt =
  812. "提取以下对话中的关键信息。\n" +
  813. app.chat
  814. .map(
  815. (i) =>
  816. (i.role == "user" ? "Alice: " : "Bob: ") +
  817. i.content.replace(/\n/g, "")
  818. )
  819. .join("\n");
  820. await send(prompt);
  821. lsdh(true);
  822. app.loading = true;
  823. app.chat = app.chat.slice(app.chat.length - 2);
  824. app.chat[0].content = "提供本次对话中的关键信息。";
  825. };
  826. // 将用户的输入发送到服务器并更新 app 的 chat 数组
  827. // 参数s为用户输入的内容,keyword为用户输入的关键词,show为是否显示用户输入的内容,sources为知识库的来源
  828. let last_cost = []
  829. send = async (s, keyword = "", show = true, sources = [], addition_args = {}) => {
  830. app.question = ''
  831. if (keyword == "") keyword = s;
  832. is_abord = false;
  833. app.loading = true;
  834. let QA_history;
  835. // 如果历史记录开启,则将历史记录保存到 QA_history 中
  836. if (app.history_on) {
  837. if (
  838. app.history_limit != 0 &&
  839. app.chat.length + 1 >= app.history_limit * 2
  840. ) {
  841. if (app.simplify_historical_information) {
  842. alert(`历史信息过长,自动进行总结`);
  843. await summerize_history();
  844. QA_history = app.chat
  845. } else {
  846. alert(
  847. `历史信息过长,将仅保留最后${app.history_limit}次chat记忆。setting为0不限制`
  848. );
  849. QA_history = app.chat.slice(
  850. app.chat.length - app.history_limit * 2
  851. );
  852. }
  853. } else {
  854. QA_history = app.chat
  855. }
  856. QA_history = QA_history.filter(i => !i.no_history)
  857. } else {
  858. QA_history = [];
  859. }
  860. let current_session = { role: "AI", content: "……", sources: sources };
  861. if (show) {
  862. app.chat.push({ role: "user", content: s, keyword: keyword });
  863. app.chat.push(current_session);
  864. }
  865. setTimeout(() => window.scrollTo(0, document.body.scrollHeight), 0);
  866. // 调用websocket聊天,具体函数在wd_sdk.js中
  867. let last_token_time = Date.now()
  868. await send_raw(s.replace(/\r\n/g, "\n"), keyword, QA_history, (message) => {
  869. current_session.content = message;
  870. let now = Date.now()
  871. let cost = now - last_token_time
  872. last_cost.push(cost)
  873. if (last_cost.length > 10) last_cost.shift()
  874. let avg_cost = 0
  875. last_cost.forEach(v => avg_cost += v)
  876. avg_cost /= last_cost.length
  877. app.TPS = 1000 / avg_cost
  878. last_token_time = now
  879. }, addition_args);
  880. if (app.tts_on) {
  881. speak(current_session.content);
  882. }
  883. app.loading = false;
  884. save_history();
  885. if (is_abord) throw new MyException("已中断");
  886. return current_session.content;
  887. };
  888. // 覆盖 console.warn 函数以不执行任何操作
  889. console.warn = function () { };
  890. </script>
  891. <script src="wd_sdk.js"></script>
  892. <script src="api/llm"></script>
  893. <script>
  894. // 从插件内容中提取描述信息
  895. get_auto_description = (plugin) => {
  896. try {
  897. return plugin.content.match(/@description (.+)\n/)[1].trim();
  898. } catch { }
  899. return "";
  900. };
  901. // 判断是否禁用了某个插件
  902. get_auto_disabled = (plugin, name) => {
  903. if (disabled_auto[name] == undefined)
  904. return !!plugin.content.match(/wenda_auto_default_disabled/);
  905. return disabled_auto[name];
  906. };
  907. // 从插件内容中提取名称信息
  908. get_auto_name = (plugin) => {
  909. try {
  910. return (
  911. plugin.content.match(/@name (.+)\n/)[1].trim() + `(${plugin.name})`
  912. );
  913. } catch { }
  914. return plugin.name;
  915. };
  916. // 将启用的插件内容添加到 app.autos 数组中,显示在菜单中
  917. add_auto = (content) => {
  918. let plugin = { content: content, name: "用户添加" };
  919. let name = get_auto_name(plugin);
  920. let auto = {
  921. name: name,
  922. description: get_auto_description(plugin),
  923. content: plugin.content,
  924. disabled: false,
  925. };
  926. saved_auto.push(auto);
  927. app.autos.push(auto);
  928. try {
  929. if (!auto.disabled) eval(plugin.content);
  930. } catch {
  931. }
  932. localStorage["wenda_saved_auto"] = JSON.stringify(saved_auto);
  933. };
  934. // 查找指定名称的插件在 saved_auto 数组中的索引
  935. find_auto = (name) => saved_auto.findIndex((a) => a.name == name);
  936. // 删除指定名称的插件
  937. del_auto = (name) => {
  938. let auto_index = find_auto(name);
  939. saved_auto.splice(auto_index, 1);
  940. localStorage["wenda_saved_auto"] = JSON.stringify(saved_auto);
  941. location.reload();
  942. };
  943. // 加载所有插件并将其添加到 app.autos 数组中
  944. load_plugins = async () => {
  945. disabled_auto = JSON.parse(localStorage["wenda_disabled_auto"] || "{}");
  946. server_auto = await fetch("/api/plugins");
  947. server_auto = await server_auto.json();
  948. server_auto.forEach((plugin) => {
  949. let name = get_auto_name(plugin);
  950. let auto = {
  951. name: name,
  952. description: get_auto_description(plugin),
  953. content: plugin.content,
  954. disabled: get_auto_disabled(plugin, name),
  955. };
  956. app.autos.push(auto);
  957. setTimeout(() => {
  958. if (!auto.disabled) eval(plugin.content);
  959. }, 0)
  960. });
  961. saved_auto = JSON.parse(localStorage["wenda_saved_auto"] || "[]");
  962. saved_auto.forEach((auto) => {
  963. app.autos.push(auto);
  964. setTimeout(() => {
  965. if (!auto.disabled) eval(auto.content);
  966. }, 0)
  967. });
  968. setTimeout(() => app.current_func = decodeURI(document.location.hash.replace('#', '')), 10)
  969. };
  970. load_plugins();
  971. alert = (text) => {
  972. app.snackbar_text = text; //.replace(/\n/g,"<br>")
  973. app.snackbar = true;
  974. }
  975. </script>
  976. </body>
  977. </html>
粤ICP备19079148号