初始化
This commit is contained in:
159
easyflow-ui-admin/app/src/views/ai/bots/pages/Run.vue
Normal file
159
easyflow-ui-admin/app/src/views/ai/bots/pages/Run.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<script setup lang="ts">
|
||||
import type { BotInfo, Session } from '@easyflow/types';
|
||||
|
||||
import { onMounted, ref, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { IconifyIcon } from '@easyflow/icons';
|
||||
import { preferences } from '@easyflow/preferences';
|
||||
import { uuid } from '@easyflow/utils';
|
||||
|
||||
import {
|
||||
ElAside,
|
||||
ElButton,
|
||||
ElContainer,
|
||||
ElEmpty,
|
||||
ElMain,
|
||||
ElSpace,
|
||||
} from 'element-plus';
|
||||
import { tryit } from 'radash';
|
||||
|
||||
import { getBotDetails, getSessionList } from '#/api';
|
||||
import BotAvatar from '#/components/botAvatar/botAvatar.vue';
|
||||
import Chat from '#/components/chat/chat.vue';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const bot = ref<BotInfo>();
|
||||
const sessionList = ref<Session[]>([]);
|
||||
const sessionId = ref<string>(route.params.sessionId as string);
|
||||
|
||||
watchEffect(() => {
|
||||
sessionId.value = route.params.sessionId as string;
|
||||
});
|
||||
|
||||
// 内置菜单点击方法
|
||||
// function handleMenuCommand(
|
||||
// command: ConversationMenuCommand,
|
||||
// item: ConversationItem,
|
||||
// ) {
|
||||
// console.warn('内置菜单点击事件:', command, item);
|
||||
// // 直接修改 item 是否生效
|
||||
// if (command === 'delete') {
|
||||
// const index = menuTestItems.value.findIndex(
|
||||
// (itemSlef) => itemSlef.key === item.key,
|
||||
// );
|
||||
|
||||
// if (index !== -1) {
|
||||
// menuTestItems.value.splice(index, 1);
|
||||
// console.warn('删除成功');
|
||||
// ElMessage.success('删除成功');
|
||||
// }
|
||||
// }
|
||||
// if (command === 'rename') {
|
||||
// item.label = '已修改';
|
||||
// console.warn('重命名成功');
|
||||
// ElMessage.success('重命名成功');
|
||||
// }
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.botId) {
|
||||
fetchBotDetail(route.params.botId as string);
|
||||
fetchSessionList(route.params.botId as string);
|
||||
}
|
||||
});
|
||||
|
||||
const fetchBotDetail = async (id: string) => {
|
||||
const [, res] = await tryit(getBotDetails)(id);
|
||||
|
||||
if (res?.errorCode === 0) {
|
||||
bot.value = res.data;
|
||||
}
|
||||
};
|
||||
const fetchSessionList = async (id: string) => {
|
||||
const [, res] = await tryit(getSessionList)({
|
||||
botId: id,
|
||||
tempUserId: uuid().toString() + id,
|
||||
});
|
||||
|
||||
if (res?.errorCode === 0) {
|
||||
sessionList.value = res.data.cons;
|
||||
}
|
||||
};
|
||||
|
||||
const updateActive = (_sessionId?: number | string) => {
|
||||
sessionId.value = `${_sessionId ?? ''}`;
|
||||
router.push(
|
||||
`/ai/bots/run/${bot.value?.id}${_sessionId ? `/${_sessionId}` : ''}`,
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElContainer class="h-screen" v-if="bot">
|
||||
<ElAside width="240px" class="flex flex-col items-center bg-[#f5f5f580]">
|
||||
<ElSpace class="py-7">
|
||||
<BotAvatar :src="bot.icon" :size="40" />
|
||||
<span class="text-base font-medium text-black/85">{{ bot.title }}</span>
|
||||
</ElSpace>
|
||||
<ElButton
|
||||
type="primary"
|
||||
class="!h-10 w-full max-w-[208px]"
|
||||
plain
|
||||
@click="updateActive()"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="mdi:chat-outline" />
|
||||
</template>
|
||||
{{ $t('button.newConversation') }}
|
||||
</ElButton>
|
||||
<span class="self-start p-6 pb-2 text-sm text-[#969799]">{{
|
||||
$t('common.history')
|
||||
}}</span>
|
||||
<div class="w-full max-w-[208px] flex-1 overflow-hidden">
|
||||
<ElConversations
|
||||
v-show="sessionList.length > 0"
|
||||
class="!w-full !shadow-none"
|
||||
v-model:active="sessionId"
|
||||
:items="sessionList"
|
||||
:label-max-width="120"
|
||||
:show-tooltip="true"
|
||||
row-key="sessionId"
|
||||
label-key="title"
|
||||
tooltip-placement="right"
|
||||
:tooltip-offset="35"
|
||||
show-to-top-btn
|
||||
show-built-in-menu
|
||||
show-built-in-menu-type="hover"
|
||||
@update:active="updateActive"
|
||||
/>
|
||||
<ElEmpty
|
||||
:image="`/empty${preferences.theme.mode === 'dark' ? '-dark' : ''}.png`"
|
||||
v-show="sessionList.length === 0"
|
||||
/>
|
||||
</div>
|
||||
</ElAside>
|
||||
<ElMain>
|
||||
<Chat :session-id="sessionId" :bot="bot" />
|
||||
</ElMain>
|
||||
</ElContainer>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.conversations-container :deep(.conversations-list) {
|
||||
width: 100% !important;
|
||||
padding: 0 !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.conversations-container :deep(.conversation-item) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.conversations-container :deep(.conversation-label) {
|
||||
color: #1a1a1a;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { $t } from '@easyflow/locales';
|
||||
|
||||
import { ElButton, ElDialog, ElForm, ElFormItem, ElInput } from 'element-plus';
|
||||
|
||||
import { sseClient } from '#/api/request';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const formRef = ref<InstanceType<typeof ElForm>>();
|
||||
const formData = ref();
|
||||
const rules = {
|
||||
title: [{ required: true, message: $t('message.required'), trigger: 'blur' }],
|
||||
};
|
||||
const loading = ref(false);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
loading.value = true;
|
||||
const data = {
|
||||
botId: formData.value.botId,
|
||||
prompt: formData.value.prompt,
|
||||
};
|
||||
formData.value.prompt = '';
|
||||
sseClient.post('/api/v1/bot/prompt/chore/chat', data, {
|
||||
onMessage(message) {
|
||||
const event = message.event;
|
||||
// done
|
||||
if (event === 'done') {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
if (!message.data) {
|
||||
return;
|
||||
}
|
||||
const sseData = JSON.parse(message.data);
|
||||
const delta = sseData.payload?.delta;
|
||||
formData.value.prompt += delta;
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleReplace = () => {
|
||||
emit('success', formData.value.prompt);
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
defineExpose({
|
||||
open(botId: string, systemPrompt: string) {
|
||||
formData.value = {
|
||||
botId,
|
||||
prompt: systemPrompt,
|
||||
};
|
||||
dialogVisible.value = true;
|
||||
handleSubmit();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="$t('bot.aiOptimizedPrompts')"
|
||||
draggable
|
||||
align-center
|
||||
width="550px"
|
||||
>
|
||||
<ElForm ref="formRef" :model="formData" :rules="rules">
|
||||
<ElFormItem prop="prompt">
|
||||
<ElInput type="textarea" :rows="20" v-model="formData.prompt" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">
|
||||
{{ $t('button.cancel') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" @click="handleReplace" v-if="!loading">
|
||||
{{ $t('button.replace') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="loading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ loading ? $t('button.optimizing') : $t('button.regenerate') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
1056
easyflow-ui-admin/app/src/views/ai/bots/pages/setting/config.vue
Normal file
1056
easyflow-ui-admin/app/src/views/ai/bots/pages/setting/config.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import type { BotInfo } from '@easyflow/types';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { tryit } from 'radash';
|
||||
|
||||
import { getBotDetails } from '#/api';
|
||||
import { hasPermission } from '#/api/common/hasPermission';
|
||||
|
||||
import Config from './config.vue';
|
||||
import Preview from './preview.vue';
|
||||
import Prompt from './prompt.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const hasSavePermission = computed(() =>
|
||||
hasPermission(['/api/v1/bot/save', '/api/v1/bot/updateLlmId']),
|
||||
);
|
||||
const bot = ref<BotInfo>();
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.id) {
|
||||
fetchBotDetail(route.params.id as string);
|
||||
}
|
||||
});
|
||||
|
||||
const fetchBotDetail = async (id: string) => {
|
||||
const [, res] = await tryit(getBotDetails)(id);
|
||||
|
||||
if (res?.errorCode === 0) {
|
||||
bot.value = res.data;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="settings-container">
|
||||
<div class="row-container">
|
||||
<div class="row-item">
|
||||
<Prompt :bot="bot" :has-save-permission="hasSavePermission" />
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<Config :bot="bot" :has-save-permission="hasSavePermission" />
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<Preview :bot="bot" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.settings-container {
|
||||
height: calc(100vh - 90px);
|
||||
padding: 20px;
|
||||
}
|
||||
.row-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.row-item {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Brush } from '@element-plus/icons-vue';
|
||||
import { ElButton, ElIcon } from 'element-plus';
|
||||
|
||||
import Chat from '#/components/chat/chat.vue';
|
||||
|
||||
const chatRef = ref();
|
||||
|
||||
const handleClear = () => {
|
||||
chatRef.value.clear();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-background dark:border-border flex h-full flex-col gap-3 rounded-lg p-3 dark:border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-base font-medium">
|
||||
{{ $t('button.preview') }}
|
||||
</h1>
|
||||
<ElButton text @click="handleClear">
|
||||
<ElIcon class="rotate-180"><Brush /></ElIcon>
|
||||
</ElButton>
|
||||
</div>
|
||||
<div class="relative flex-1">
|
||||
<Chat
|
||||
ref="chatRef"
|
||||
class="absolute inset-0"
|
||||
:ishow-chat-conversations="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
102
easyflow-ui-admin/app/src/views/ai/bots/pages/setting/prompt.vue
Normal file
102
easyflow-ui-admin/app/src/views/ai/bots/pages/setting/prompt.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import type { BotInfo } from '@easyflow/types';
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { ElIcon, ElInput } from 'element-plus';
|
||||
|
||||
import { updateLlmOptions } from '#/api';
|
||||
import MagicStaffIcon from '#/components/icons/MagicStaffIcon.vue';
|
||||
import { $t } from '#/locales';
|
||||
import PromptChoreChatModal from '#/views/ai/bots/pages/setting/PromptChoreChatModal.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
bot?: BotInfo;
|
||||
hasSavePermission?: boolean;
|
||||
}>();
|
||||
const systemPrompt = ref($t('bot.placeholder.prompt'));
|
||||
const promptChoreChatModalRef = ref();
|
||||
watch(
|
||||
() => props.bot?.modelOptions.systemPrompt,
|
||||
(newPrompt) => {
|
||||
if (newPrompt) {
|
||||
systemPrompt.value = newPrompt;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const handleInput = useDebounceFn((value: string) => {
|
||||
updateLlmOptions({
|
||||
id: props.bot?.id || '',
|
||||
llmOptions: {
|
||||
systemPrompt: value,
|
||||
},
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
const handelReplacePrompt = (value: string) => {
|
||||
systemPrompt.value = value;
|
||||
handleInput(value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-background dark:border-border flex h-full flex-col gap-2 rounded-lg p-3 dark:border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h1 class="text-base font-medium">
|
||||
{{ $t('bot.systemPrompt') }}
|
||||
</h1>
|
||||
<button
|
||||
@click="promptChoreChatModalRef.open(props.bot?.id, systemPrompt)"
|
||||
type="button"
|
||||
class="flex items-center gap-0.5 rounded-lg bg-[#f7f7f7] px-3 py-1"
|
||||
>
|
||||
<ElIcon size="16"><MagicStaffIcon /></ElIcon>
|
||||
<span
|
||||
class="bg-[linear-gradient(106.75666073298856deg,#F17E47,#D85ABF,#717AFF)] bg-clip-text text-sm text-transparent"
|
||||
>
|
||||
{{ $t('bot.aiOptimization') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<ElInput
|
||||
class="flex-1"
|
||||
type="textarea"
|
||||
resize="none"
|
||||
v-model="systemPrompt"
|
||||
:title="!hasSavePermission ? $t('bot.placeholder.permission') : ''"
|
||||
:disabled="!hasSavePermission"
|
||||
@input="handleInput"
|
||||
/>
|
||||
|
||||
<!--系统提示词优化模态框-->
|
||||
<PromptChoreChatModal
|
||||
ref="promptChoreChatModalRef"
|
||||
@success="handelReplacePrompt"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.el-textarea :deep(.el-textarea__inner) {
|
||||
--el-input-bg-color: #f7f7f7;
|
||||
|
||||
height: 100%;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
border-radius: 8px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dark .el-textarea :deep(.el-textarea__inner) {
|
||||
--el-input-bg-color: hsl(var(--background-deep));
|
||||
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user