index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <div>
  3. <el-popover
  4. :visible="state.show"
  5. placement="bottom"
  6. trigger="click"
  7. :popper-style="{
  8. padding: 0,
  9. minWidth: 0,
  10. width: '360px',
  11. }"
  12. >
  13. <template #reference>
  14. <div
  15. @click="() => (state.show = !state.show)"
  16. class="bg __hover-bg flex h-8 w-full items-center justify-end rounded-sm px-2 text-xs"
  17. >
  18. <img
  19. v-if="modelConfig?.modelId"
  20. src="@/assets/images/model/model-default-logo.png"
  21. class="mr-2 size-4"
  22. />
  23. <span v-title class="mr-auto text-xs opacity-65">
  24. {{ modelConfig?.modelId ? modelConfig?.modelName : '请选择模型' }}
  25. </span>
  26. <el-popover
  27. :visible="state.showConfig"
  28. placement="bottom"
  29. trigger="click"
  30. :popper-style="{
  31. padding: 0,
  32. minWidth: 0,
  33. width: '360px',
  34. }"
  35. >
  36. <template #reference>
  37. <SvgIcon
  38. name="config"
  39. size="16"
  40. rotate="90"
  41. color="#33333377"
  42. class="__hover mr-2"
  43. @click.stop="() => (state.showConfig = !state.showConfig)"
  44. />
  45. </template>
  46. <div class="px-2 py-3" :model-select-config-popover="true">
  47. <div class="text-sm font-bold">参数</div>
  48. <template v-for="item in state.configOptions">
  49. <div class="mt-1 flex items-center">
  50. <a-switch
  51. v-model:checked="state.modelConfig.paramsConfig[item.isKey]"
  52. size="small"
  53. :checkedValue="1"
  54. :unCheckedValue="0"
  55. ></a-switch>
  56. <span class="ml-1 flex w-20 items-center text-sm">
  57. {{ item.label }}
  58. <el-tooltip :content="item.tips" placement="top">
  59. <SvgIcon name="czr_tip" size="14" class="ml-1" />
  60. </el-tooltip>
  61. </span>
  62. <a-row class="ml-auto flex-1" justify="end">
  63. <a-col :span="12" class="mr-2">
  64. <a-slider
  65. v-model:value="
  66. state.modelConfig.paramsConfig[item.valueKey]
  67. "
  68. :min="item.min"
  69. :max="item.max"
  70. :step="item.step"
  71. :disabled="!state.modelConfig.paramsConfig[item.isKey]"
  72. />
  73. </a-col>
  74. <a-col :span="8">
  75. <a-input-number
  76. v-model:value="
  77. state.modelConfig.paramsConfig[item.valueKey]
  78. "
  79. :min="item.min"
  80. :max="item.max"
  81. :step="item.step"
  82. :disabled="!state.modelConfig.paramsConfig[item.isKey]"
  83. style="width: 100%"
  84. />
  85. </a-col>
  86. </a-row>
  87. </div>
  88. </template>
  89. <div class="mt-1 flex justify-between">
  90. <div class="text-xs">
  91. <div class="flex items-center">
  92. 停止序列
  93. <el-tooltip
  94. content="最多四个序列,API 将停止生成更多的token。返回的文本将不包含停止序列。"
  95. placement="top"
  96. >
  97. <SvgIcon name="czr_tip" size="14" class="ml-1" />
  98. </el-tooltip>
  99. </div>
  100. <div>输入序列并按Enter键</div>
  101. </div>
  102. <div class="bg ml-8 w-full flex-1 rounded-sm p-2 text-xs">
  103. <div class="flex flex-wrap gap-1">
  104. <template
  105. v-for="(item, index) in state.modelConfig.paramsConfig
  106. .stopSequence"
  107. >
  108. <div
  109. class="flex items-center gap-1 rounded-sm border border-gray-300 p-1"
  110. >
  111. <div class="flex-1 text-justify">
  112. {{ item }}
  113. </div>
  114. <SvgIcon
  115. class="__hover"
  116. name="czr_close_1"
  117. size="10"
  118. @click="
  119. state.modelConfig.paramsConfig.stopSequence.splice(
  120. index,
  121. 1,
  122. )
  123. "
  124. />
  125. </div>
  126. </template>
  127. </div>
  128. <div class="mt-1">
  129. <a-input
  130. v-model:value="state.stopText"
  131. size="small"
  132. :maxlength="20"
  133. placeholder="输入序列并按 Enter 键"
  134. @pressEnter="
  135. (e) =>
  136. state.stopText.trim()
  137. ? (state.modelConfig.paramsConfig.stopSequence.push(
  138. state.stopText,
  139. ),
  140. (state.stopText = ''))
  141. : undefined
  142. "
  143. />
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. </el-popover>
  149. <SvgIcon name="czr_arrow" size="12" rotate="90" color="#33333377" />
  150. </div>
  151. </template>
  152. <div class="" :model-select-popover="true">
  153. <div class="filter">
  154. <el-input
  155. v-model="state.text"
  156. :prefix-icon="Search"
  157. placeholder="搜索变量"
  158. clearable
  159. />
  160. </div>
  161. <div class="flex flex-col p-1">
  162. <template v-for="item in optionsCpt">
  163. <div
  164. class="__hover-bg flex items-center px-2 py-2"
  165. @click="onModel(item)"
  166. >
  167. <img
  168. src="@/assets/images/model/model-default-logo.png"
  169. class="mr-2 size-4"
  170. />
  171. {{ item.name }}
  172. </div>
  173. </template>
  174. </div>
  175. </div>
  176. </el-popover>
  177. </div>
  178. </template>
  179. <script setup lang="ts">
  180. import { computed, onBeforeMount, onMounted, reactive, watch } from 'vue'
  181. import { domRootHasAttr } from '@/utils/czr-util'
  182. import { pluginGetInstanceList } from '@/api/modules/model'
  183. import { Search } from '@element-plus/icons-vue'
  184. import SvgIcon from '@/components/SvgIcon/index.vue'
  185. const emit = defineEmits(['update:modelConfig'])
  186. const props = defineProps({
  187. type: { required: true },
  188. modelConfig: {},
  189. })
  190. const state: any = reactive({
  191. modelConfig: props.modelConfig,
  192. show: false,
  193. showConfig: false,
  194. options: [],
  195. text: '',
  196. stopText: '',
  197. configOptions: [
  198. {
  199. label: '温度',
  200. tips: '核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。',
  201. isKey: 'isTemperature',
  202. is: 1,
  203. valueKey: 'temperature',
  204. value: 0.7,
  205. max: 2,
  206. min: 0,
  207. step: 0.1,
  208. },
  209. {
  210. label: 'Top P',
  211. tips: '生成过程中核采样方法概率阈值。取值越大,生成的随机性越高;取值越小,生成的确定性越高。',
  212. isKey: 'isTopP',
  213. valueKey: 'topP',
  214. value: 1,
  215. max: 1,
  216. min: 0,
  217. step: 0.1,
  218. },
  219. {
  220. label: '频率惩罚',
  221. tips: '用于控制模型已使用字词的重复率。 提高此项可以降低模型在输出中重复相同字词的重复度。',
  222. isKey: 'isFrequency',
  223. valueKey: 'frequency',
  224. value: 0,
  225. max: 2,
  226. min: -2,
  227. step: 0.1,
  228. },
  229. {
  230. label: '存在惩罚',
  231. tips: '用于控制模型生成时的重复度。提高此项可以降低模型生成的重复度。',
  232. isKey: 'isExist',
  233. valueKey: 'exist',
  234. value: 0,
  235. max: 2,
  236. min: -2,
  237. step: 0.1,
  238. },
  239. {
  240. label: '最大标记',
  241. tips: '模型回答的tokens的最大长度。',
  242. isKey: 'isTokens',
  243. valueKey: 'tokens',
  244. value: 512,
  245. max: 4096,
  246. min: 1,
  247. step: 1,
  248. },
  249. ],
  250. })
  251. watch(
  252. () => props.modelConfig,
  253. (n) => {
  254. reset()
  255. },
  256. )
  257. const reset = () => {
  258. if (props.modelConfig) {
  259. state.modelConfig = props.modelConfig
  260. } else {
  261. state.modelConfig = {
  262. modelId: '',
  263. modelName: '',
  264. paramsConfig: {
  265. stopSequence: [],
  266. },
  267. }
  268. state.configOptions.forEach((v) => {
  269. state.modelConfig.paramsConfig[v.isKey] = v.is
  270. state.modelConfig.paramsConfig[v.valueKey] = v.value
  271. })
  272. }
  273. }
  274. watch(
  275. () => state.modelConfig,
  276. (n) => {
  277. emit('update:modelConfig', n)
  278. },
  279. { deep: true },
  280. )
  281. const optionsCpt = computed(() => {
  282. if (!state.text) {
  283. return state.options
  284. }
  285. return state.options
  286. .map((v) => {
  287. const obj = { ...v }
  288. obj.options = obj.options.filter(
  289. (s) => s.label.includes(state.text) || s.key.includes(state.text),
  290. )
  291. return obj
  292. })
  293. .filter((v) => v.options.length > 0)
  294. })
  295. const onModel = (row) => {
  296. state.modelConfig.modelId = row.id
  297. state.modelConfig.modelName = row.name
  298. state.show = false
  299. }
  300. const onMouseDown = (e) => {
  301. if (!domRootHasAttr(e.target, 'model-select-popover')) {
  302. state.show = false
  303. }
  304. }
  305. watch(
  306. () => state.show,
  307. (n) => {
  308. if (n) {
  309. document.addEventListener('mousedown', onMouseDown)
  310. } else {
  311. document.removeEventListener('mousedown', onMouseDown)
  312. }
  313. },
  314. { immediate: true },
  315. )
  316. const onConfigMouseDown = (e) => {
  317. if (!domRootHasAttr(e.target, 'model-select-config-popover')) {
  318. state.showConfig = false
  319. }
  320. }
  321. watch(
  322. () => state.showConfig,
  323. (n) => {
  324. if (n) {
  325. document.addEventListener('mousedown', onConfigMouseDown)
  326. } else {
  327. document.removeEventListener('mousedown', onConfigMouseDown)
  328. }
  329. },
  330. { immediate: true },
  331. )
  332. onMounted(() => {
  333. if (state.options.length === 0) {
  334. pluginGetInstanceList({
  335. page: 1,
  336. size: 100000,
  337. modeType: props.type,
  338. status: 1,
  339. }).then(({ data }: any) => {
  340. state.options = data.records
  341. })
  342. }
  343. })
  344. onBeforeMount(() => {
  345. reset()
  346. })
  347. </script>
  348. <style lang="scss" scoped>
  349. @use '@/views/workflow/instance/component/style';
  350. .bg {
  351. background-color: style.$inputBg;
  352. }
  353. .vars-display {
  354. //border: style.$borderStyle;
  355. width: 100%;
  356. height: 32px;
  357. padding: 0 8px;
  358. border-radius: 4px;
  359. font-size: 12px;
  360. display: flex;
  361. align-items: center;
  362. justify-content: flex-end;
  363. position: relative;
  364. background-color: style.$inputBg;
  365. .del {
  366. position: absolute;
  367. right: 0;
  368. width: 30px;
  369. display: flex;
  370. align-items: center;
  371. justify-content: center;
  372. }
  373. }
  374. .filter {
  375. padding: 10px;
  376. border-bottom: style.$borderStyle;
  377. }
  378. </style>