Commit 7fd27d15 authored by nick zheng's avatar nick zheng

feat: 应用增加AI自动配置

parent 69e0b200
...@@ -78,31 +78,54 @@ export function fetchCreateContinueQuestions<T>(payload: { input: string }) { ...@@ -78,31 +78,54 @@ export function fetchCreateContinueQuestions<T>(payload: { input: string }) {
} }
/** /**
* * @param { agentTitle: 标题 agentDesc: 描述 } * * @param { payload: { agentTitle: 标题 agentDesc: 描述 }, controller: 控制取消请求 }
* @returns AI生成应用头像 * @returns AI生成应用头像
*/ */
export function fetchCreateAgentAvatar<T>(payload: { agentTitle: string; agentDesc: string }) { export function fetchCreateAgentAvatar<T>(
payload: { agentTitle: string; agentDesc: string },
controller: AbortController,
) {
return request.post<T>('/agentApplicationInfoRest/createAgentApplicationAvatar.json', payload, { return request.post<T>('/agentApplicationInfoRest/createAgentApplicationAvatar.json', payload, {
timeout: 120000, timeout: 120000,
signal: controller.signal,
}) })
} }
/** /**
* * @param { agentTitle: 标题 agentDesc: 描述 agentSystem: 指令 } * * @param { payload: { agentTitle: 标题, agentDesc: 描述, agentSystem: 指令 }, controller: 控制取消请求 }
* @returns AI生成开场白 * @returns AI生成开场白
*/ */
export function fetchCreatePreamble<T>(payload: { agentTitle: string; agentDesc: string; agentSystem: string }) { export function fetchCreatePreamble<T>(
payload: { agentTitle: string; agentDesc: string; agentSystem: string },
controller: AbortController,
) {
return request.post<T>('/agentApplicationInfoRest/createPreamble.json', payload, { return request.post<T>('/agentApplicationInfoRest/createPreamble.json', payload, {
timeout: 120000, timeout: 120000,
signal: controller.signal,
}) })
} }
/** /**
* * @param { agentTitle: 标题 agentDesc: 描述 } * * @param { payload: { agentTitle: 标题 agentDesc: 描述 }, controller: 控制取消请求 }
* @returns AI生成推荐问 * @returns AI生成推荐问
*/ */
export function fetchCreateFeaturedQuestions<T>(payload: { agentTitle: string; agentDesc: string }) { export function fetchCreateFeaturedQuestions<T>(
payload: { agentTitle: string; agentDesc: string },
controller: AbortController,
) {
return request.post<T>('/agentApplicationInfoRest/createFeaturedQuestions.json', payload, { return request.post<T>('/agentApplicationInfoRest/createFeaturedQuestions.json', payload, {
timeout: 120000, timeout: 120000,
signal: controller.signal,
})
}
/**
* * @param { payload: { input: 自动配置信息 }, controller: 控制取消请求 }
* @returns 生成应用标题和描述
*/
export function fetchCreateAgentTitleAndDesc<T>(payload: { input: string }, controller: AbortController) {
return request.post<T>('/agentApplicationInfoRest/createAgentTitleAndDesc.json', payload, {
timeout: 120000,
signal: controller.signal,
}) })
} }
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, useSlots } from 'vue'
import { modalHeaderStyle, modalContentStyle, modalFooterStyle } from './modal-style' import { modalHeaderStyle, modalContentStyle, modalFooterStyle } from './modal-style'
interface Props { interface Props {
...@@ -36,9 +36,11 @@ const props = withDefaults(defineProps<Props>(), { ...@@ -36,9 +36,11 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()
const slots = useSlots()
const modalBasicStyle = { const modalBasicStyle = {
width: props.width + 'px', width: props.width + 'px',
height: props.height + 'px', minHeight: props.height + 'px',
borderRadius: props.borderRadius + 'px', borderRadius: props.borderRadius + 'px',
} }
...@@ -85,7 +87,9 @@ function handleDetele() { ...@@ -85,7 +87,9 @@ function handleDetele() {
</div> </div>
<template #footer> <template #footer>
<div class="flex w-full items-center justify-end"> <slot v-if="slots.footer" name="footer" />
<div v-else class="flex w-full items-center justify-end">
<NButton class="h-[32px]! rounded-md! px-6!" @click="handleCloseModal"> {{ cancelBtnText }} </NButton> <NButton class="h-[32px]! rounded-md! px-6!" @click="handleCloseModal"> {{ cancelBtnText }} </NButton>
<NButton <NButton
:loading="btnLoading" :loading="btnLoading"
......
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue' import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { FormInst, SelectOption, UploadFileInfo } from 'naive-ui' import { FormInst, InputInst, SelectOption, UploadFileInfo } from 'naive-ui'
import { useThrottleFn } from '@vueuse/core' import { useThrottleFn } from '@vueuse/core'
import CustomIcon from '@/components/custom-icon/custom-icon.vue' import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import UploadPhoto from '@/components/upload-photo/upload-photo.vue' import UploadPhoto from '@/components/upload-photo/upload-photo.vue'
import AutoConfigModal from './auto-config-modal.vue'
import OptimizeSystemModal from './optimize-system-modal.vue' import OptimizeSystemModal from './optimize-system-modal.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config' import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import { PersonalAppConfigState } from '@/store/types/personal-app-config' import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { import {
fetchCreateAgentAvatar, fetchCreateAgentAvatar,
fetchCreateAgentTitleAndDesc,
fetchCreateFeaturedQuestions, fetchCreateFeaturedQuestions,
fetchCreatePreamble, fetchCreatePreamble,
fetchGetDebugApplicationInfo, fetchGetDebugApplicationInfo,
fetchGetLargeModelList, fetchGetLargeModelList,
fetchSaveAgentApplication, fetchSaveAgentApplication,
} from '@/apis/agent-application' } from '@/apis/agent-application'
import { fetchCustomEventSource } from '@/composables/useEventSource'
const router = useRouter() const router = useRouter()
...@@ -52,6 +55,10 @@ const commConfigExpandedNames = ref<string[]>(['continuousQuestion']) ...@@ -52,6 +55,10 @@ const commConfigExpandedNames = ref<string[]>(['continuousQuestion'])
const isInitGetAgentAppDetail = ref(false) const isInitGetAgentAppDetail = ref(false)
const isFullScreenLoading = ref(false) // 是否全面加载中
const isShowAutoConfigModal = ref(false) // 是否显示自动配置对话框
const autoConfigBtnLoading = ref(false) // 自动配置按钮加载状态
const isShowOptimizeAgentSystemModal = ref(false) // 是否显示优化角色指令对话框 const isShowOptimizeAgentSystemModal = ref(false) // 是否显示优化角色指令对话框
const generateAgentAvatarLoading = ref(false) // 是否正在生成图片 const generateAgentAvatarLoading = ref(false) // 是否正在生成图片
...@@ -59,6 +66,7 @@ const generatePreambleLoading = ref(false) // 是否正在生成开场白 ...@@ -59,6 +66,7 @@ const generatePreambleLoading = ref(false) // 是否正在生成开场白
const generateFeaturedQuestionsLoading = ref(false) // 是否正在生成推荐词 const generateFeaturedQuestionsLoading = ref(false) // 是否正在生成推荐词
const personalAppFormRef = ref<FormInst | null>(null) const personalAppFormRef = ref<FormInst | null>(null)
const agentSystemInputRef = ref<InputInst | null>(null)
const personalAppRules = { const personalAppRules = {
baseInfo: { baseInfo: {
...@@ -66,6 +74,12 @@ const personalAppRules = { ...@@ -66,6 +74,12 @@ const personalAppRules = {
}, },
} }
let generateAgentTitleAndDescController: AbortController | null = null
let generateAgentAvatarController: AbortController | null = null
let generatePreambleController: AbortController | null = null
let generateFeaturedQuestionsController: AbortController | null = null
let generateAgentSystemController: AbortController | null = null
const personalAppConfig = computed(() => { const personalAppConfig = computed(() => {
return personalAppConfigStore.$state return personalAppConfigStore.$state
}) })
...@@ -74,10 +88,14 @@ const continuousQuestionStatusText = computed(() => { ...@@ -74,10 +88,14 @@ const continuousQuestionStatusText = computed(() => {
return personalAppConfig.value.commConfig.continuousQuestionStatus === 'default' ? '默认' : '关闭' return personalAppConfig.value.commConfig.continuousQuestionStatus === 'default' ? '默认' : '关闭'
}) })
const isUpdatePersonalAgentConfig = computed(() => {
return !isInitGetAgentAppDetail.value
})
watch( watch(
() => personalAppConfig.value, () => personalAppConfig.value,
() => { () => {
!isInitGetAgentAppDetail.value && handleUpdatePersonalAppId() isUpdatePersonalAgentConfig.value && handleUpdatePersonalAppId()
}, },
{ deep: true, once: true }, { deep: true, once: true },
) )
...@@ -85,7 +103,7 @@ watch( ...@@ -85,7 +103,7 @@ watch(
watch( watch(
() => personalAppConfig.value, () => personalAppConfig.value,
() => { () => {
!isInitGetAgentAppDetail.value && handleSavePersonalAppConfig() isUpdatePersonalAgentConfig.value && handleSavePersonalAppConfig()
}, },
{ deep: true }, { deep: true },
) )
...@@ -99,11 +117,15 @@ onMounted(async () => { ...@@ -99,11 +117,15 @@ onMounted(async () => {
await handleGetLargeModelList() await handleGetLargeModelList()
}) })
onUnmounted(() => {
handleStopGenerate()
})
const handleSavePersonalAppConfig = useThrottleFn( const handleSavePersonalAppConfig = useThrottleFn(
async () => { async () => {
personalAppConfig.value.baseInfo.agentId && (await handleSaveAgentApplication()) personalAppConfig.value.baseInfo.agentId && (await handleSaveAgentApplication())
}, },
2000, () => (isFullScreenLoading.value ? 6000 : 2000),
true, true,
) )
...@@ -155,6 +177,11 @@ async function handleUpdatePersonalAppId() { ...@@ -155,6 +177,11 @@ async function handleUpdatePersonalAppId() {
} }
} }
function handleShowAutoConfigModal() {
isShowAutoConfigModal.value = true
autoConfigBtnLoading.value = false
}
function handleShowOptimizeAgentSystemModal() { function handleShowOptimizeAgentSystemModal() {
if (!personalAppConfig.value.baseInfo.agentSystem) return if (!personalAppConfig.value.baseInfo.agentSystem) return
...@@ -179,11 +206,15 @@ function handleUpdateCommConfigExpandedNames(expandedNames: string[]) { ...@@ -179,11 +206,15 @@ function handleUpdateCommConfigExpandedNames(expandedNames: string[]) {
async function handleAIGenerateAgentAvatar() { async function handleAIGenerateAgentAvatar() {
generateAgentAvatarLoading.value = true generateAgentAvatarLoading.value = true
generateAgentAvatarController = new AbortController()
const res = await fetchCreateAgentAvatar({ const res = await fetchCreateAgentAvatar(
{
agentTitle: personalAppConfig.value.baseInfo.agentTitle, agentTitle: personalAppConfig.value.baseInfo.agentTitle,
agentDesc: personalAppConfig.value.baseInfo.agentDesc, agentDesc: personalAppConfig.value.baseInfo.agentDesc,
}).finally(() => (generateAgentAvatarLoading.value = false)) },
generateAgentAvatarController,
).finally(() => (generateAgentAvatarLoading.value = false))
if (res.code === 0) { if (res.code === 0) {
personalAppConfig.value.baseInfo.agentAvatar = res.data as string personalAppConfig.value.baseInfo.agentAvatar = res.data as string
...@@ -195,11 +226,16 @@ async function handleAIGeneratePreamble() { ...@@ -195,11 +226,16 @@ async function handleAIGeneratePreamble() {
generatePreambleLoading.value = true generatePreambleLoading.value = true
const res = await fetchCreatePreamble<string>({ generatePreambleController = new AbortController()
const res = await fetchCreatePreamble<string>(
{
agentTitle: personalAppConfig.value.baseInfo.agentTitle, agentTitle: personalAppConfig.value.baseInfo.agentTitle,
agentDesc: personalAppConfig.value.baseInfo.agentDesc, agentDesc: personalAppConfig.value.baseInfo.agentDesc,
agentSystem: personalAppConfig.value.baseInfo.agentSystem, agentSystem: personalAppConfig.value.baseInfo.agentSystem,
}).finally(() => (generatePreambleLoading.value = false)) },
generatePreambleController,
).finally(() => (generatePreambleLoading.value = false))
if (res.code === 0) { if (res.code === 0) {
try { try {
...@@ -217,21 +253,119 @@ async function handleAIGenerateFeaturedQuestions() { ...@@ -217,21 +253,119 @@ async function handleAIGenerateFeaturedQuestions() {
commConfigExpandedNames.value.push('featuredQuestions') commConfigExpandedNames.value.push('featuredQuestions')
generateFeaturedQuestionsLoading.value = true generateFeaturedQuestionsLoading.value = true
generateFeaturedQuestionsController = new AbortController()
const res = await fetchCreateFeaturedQuestions<string[]>({ const res = await fetchCreateFeaturedQuestions<string[]>(
{
agentTitle: personalAppConfig.value.baseInfo.agentTitle, agentTitle: personalAppConfig.value.baseInfo.agentTitle,
agentDesc: personalAppConfig.value.baseInfo.agentDesc, agentDesc: personalAppConfig.value.baseInfo.agentDesc,
}).finally(() => (generateFeaturedQuestionsLoading.value = false)) },
generateFeaturedQuestionsController,
).finally(() => (generateFeaturedQuestionsLoading.value = false))
if (res.code === 0) { if (res.code === 0) {
personalAppConfig.value.commConfig.featuredQuestions = res.data personalAppConfig.value.commConfig.featuredQuestions = res.data
} }
} }
async function handleSettingAgent(autoConfigInputValue: string) {
autoConfigBtnLoading.value = true
isShowAutoConfigModal.value = false
isFullScreenLoading.value = true
await handleCreateAgentTitleAndDesc(autoConfigInputValue)
.then(() => {
Promise.all([
handleAIGenerateAgentAvatar(),
handleAIGeneratePreamble(),
handleAIGenerateFeaturedQuestions(),
handleAIGenerateAgentSystem(),
]).finally(() => {
isFullScreenLoading.value = false
})
})
.catch(() => {
isFullScreenLoading.value = false
})
}
async function handleCreateAgentTitleAndDesc(input: string) {
generateAgentTitleAndDescController = new AbortController()
const res = await fetchCreateAgentTitleAndDesc<{ agentTitle: string; agentDesc: string }>(
{ input },
generateAgentTitleAndDescController,
)
if (res.code === 0) {
personalAppConfig.value.baseInfo.agentTitle = res.data.agentTitle
personalAppConfig.value.baseInfo.agentDesc = res.data.agentDesc
}
}
function handleSettingAgentSystem(agentSystem: string) { function handleSettingAgentSystem(agentSystem: string) {
personalAppConfig.value.baseInfo.agentSystem = agentSystem personalAppConfig.value.baseInfo.agentSystem = agentSystem
isShowOptimizeAgentSystemModal.value = false isShowOptimizeAgentSystemModal.value = false
} }
function handleAIGenerateAgentSystem() {
return new Promise((resolve, reject) => {
personalAppConfig.value.baseInfo.agentSystem = ''
generateAgentSystemController = new AbortController()
fetchCustomEventSource({
path: '/api/rest/agentApplicationInfoRest/createAgentSystem.json',
payload: {
input: personalAppConfigStore.baseInfo.agentSystem,
},
controller: generateAgentSystemController,
onMessage: (data: any) => {
if (data === '[DONE]') {
blockAnswerResponse()
resolve(data)
return
}
if (data) {
useThrottleFn(
() => {
nextTick(() => {
personalAppConfig.value.baseInfo.agentSystem += data
agentSystemInputRef.value!.textareaElRef!.scrollTop =
agentSystemInputRef.value?.textareaElRef?.scrollHeight || 0
})
},
1000,
true,
)()
}
},
onRequestError: () => {
reject()
blockAnswerResponse()
},
onError: () => {
reject()
blockAnswerResponse()
},
onFinally: () => {
generateAgentSystemController = null
},
})
})
}
function blockAnswerResponse() {
generateAgentSystemController?.abort()
}
function handleStopGenerate() {
generateAgentTitleAndDescController?.abort()
generateAgentAvatarController?.abort()
generatePreambleController?.abort()
generateFeaturedQuestionsController?.abort()
generateAgentSystemController?.abort()
isFullScreenLoading.value = false
}
</script> </script>
<template> <template>
...@@ -239,8 +373,18 @@ function handleSettingAgentSystem(agentSystem: string) { ...@@ -239,8 +373,18 @@ function handleSettingAgentSystem(agentSystem: string) {
<div <div
class="flex h-[56px] w-full items-center justify-between border-r border-[#e8e9eb] px-5 text-[#333] shadow-[inset_0_-1px_#e8e9eb]" class="flex h-[56px] w-full items-center justify-between border-r border-[#e8e9eb] px-5 text-[#333] shadow-[inset_0_-1px_#e8e9eb]"
> >
<div class="flex items-center">
<span class="font-600 mr-4 text-base">应用配置</span> <span class="font-600 mr-4 text-base">应用配置</span>
<div
class="text-theme-color flex cursor-pointer items-center rounded-md border border-[#d4e5ff] bg-[#eef3fe] px-[11px] py-[7px] text-xs hover:opacity-80"
@click="handleShowAutoConfigModal"
>
<div class="mr-2 mt-[-4px] h-[16px] w-[16px] bg-[url(@/assets/svgs/star.svg)] bg-[length:100%_100%]" />
<span>AI自动配置</span>
</div>
</div>
<div> <div>
<NPopover placement="bottom" trigger="click" style="width: 420px"> <NPopover placement="bottom" trigger="click" style="width: 420px">
<template #trigger> <template #trigger>
...@@ -474,6 +618,7 @@ function handleSettingAgentSystem(agentSystem: string) { ...@@ -474,6 +618,7 @@ function handleSettingAgentSystem(agentSystem: string) {
<div class="flex flex-1 p-1 pl-6"> <div class="flex flex-1 p-1 pl-6">
<NInput <NInput
ref="agentSystemInputRef"
v-model:value="personalAppConfig.baseInfo.agentSystem" v-model:value="personalAppConfig.baseInfo.agentSystem"
type="textarea" type="textarea"
class="prompt-input not-resize text-xs! flex-1" class="prompt-input not-resize text-xs! flex-1"
...@@ -626,6 +771,26 @@ function handleSettingAgentSystem(agentSystem: string) { ...@@ -626,6 +771,26 @@ function handleSettingAgentSystem(agentSystem: string) {
</div> </div>
</div> </div>
<div
v-show="isFullScreenLoading"
class="z-999 fixed bottom-0 left-0 right-0 top-0 flex items-center justify-center bg-[#151b2680]"
>
<div class="flex h-14 w-[300px] items-center justify-between rounded-xl bg-white px-5">
<div class="flex items-center">
<div class="mr-2 h-4 w-4 bg-[url(@/assets/images/loading.gif)] bg-[length:100%_100%]" />
<span class="text-theme-color">配置信息生成中...</span>
</div>
<div class="cursor-pointer text-[#5c5f66] hover:opacity-80" @click="handleStopGenerate">停止生成</div>
</div>
</div>
<AutoConfigModal
v-model:is-show-modal="isShowAutoConfigModal"
:btn-loading="autoConfigBtnLoading"
modal-title="AI生成配置信息"
@comfirm="handleSettingAgent"
/>
<OptimizeSystemModal <OptimizeSystemModal
v-model:is-show-modal="isShowOptimizeAgentSystemModal" v-model:is-show-modal="isShowOptimizeAgentSystemModal"
:btn-loading="false" :btn-loading="false"
......
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
interface Props {
isShowModal: boolean
btnLoading: boolean
modalTitle: string
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'comfirm', value: string): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const autoConfigInputValue = ref('')
const autoConfigInputType = ref<'random' | 'input'>('random')
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
const isDisabledBtn = computed(() => {
return !autoConfigInputValue.value
})
const isRandomBtnLoading = computed(() => {
return props.btnLoading && autoConfigInputType.value === 'random'
})
const isInputBtnLoading = computed(() => {
return props.btnLoading && autoConfigInputType.value === 'input'
})
watch(
() => showModal.value,
() => {
showModal.value && (autoConfigInputValue.value = '')
},
)
function handleCloseModal() {
emit('update:isShowModal', false)
}
function handleComfirm(inputType: 'random' | 'input') {
autoConfigInputType.value = inputType
emit('comfirm', inputType === 'random' ? '' : autoConfigInputValue.value)
}
</script>
<template>
<CustomModal v-model:is-show="showModal" :title="modalTitle" :width="600">
<template #content>
<div class="mb-3 flex h-8 w-full items-center rounded bg-[#FFF4E6] px-4">
<CustomIcon icon="ep:warning-filled" class="mr-2 h-4 w-4 text-[#FFA500]" />
<span>生成结果将覆盖当前的配置内容,请确认是否继续生成</span>
</div>
<NInput
v-model:value="autoConfigInputValue"
type="textarea"
:rows="10"
:disabled="false"
placeholder="请告诉我你想创建一个什么样的应用,大模型将为你自动生成"
class="rounded-lg!"
/>
</template>
<template #footer>
<div class="flex w-full items-center justify-end">
<NButton class="h-[32px]! rounded-md! px-6!" @click="handleCloseModal"> 取 消 </NButton>
<NButton
:loading="isRandomBtnLoading"
class="h-[32px]! rounded-md! px-6! ml-4!"
@click="handleComfirm('random')"
>
随机生成
</NButton>
<NButton
:loading="isInputBtnLoading"
type="primary"
:disabled="isDisabledBtn"
class="h-[32px]! px-6! rounded-md! ml-4!"
@click="handleComfirm('input')"
>
AI生成
</NButton>
</div>
</template>
</CustomModal>
</template>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment