fix: Prompt generate
--bug=1061828 --user=张展玮 【提示词生成】- AI对话节点中,生成的提示词点击替换不管用 https://www.tapd.cn/62980211/s/1773916v3.2
parent
b4cdd87e6e
commit
e489a635af
|
|
@ -135,6 +135,12 @@ export default {
|
||||||
label: 'Generate',
|
label: 'Generate',
|
||||||
generatePrompt: 'Generate Prompt',
|
generatePrompt: 'Generate Prompt',
|
||||||
placeholder: 'Please enter the prompt topic',
|
placeholder: 'Please enter the prompt topic',
|
||||||
|
title: 'The prompt is displayed here',
|
||||||
|
remake: 'Regenerate',
|
||||||
|
stop: 'Stop Generating',
|
||||||
|
continue: 'Continue Generating',
|
||||||
|
replace: 'Replace',
|
||||||
|
exit: 'Are you sure you want to exit and discard the AI-generated content?',
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
addKnowledge: 'Add Related Knowledge',
|
addKnowledge: 'Add Related Knowledge',
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,12 @@ export default {
|
||||||
label: '生成',
|
label: '生成',
|
||||||
generatePrompt: '生成提示词',
|
generatePrompt: '生成提示词',
|
||||||
placeholder: '请输入提示词主题',
|
placeholder: '请输入提示词主题',
|
||||||
|
title: '提示词显示在这里',
|
||||||
|
remake: '重新生成',
|
||||||
|
stop: '停止生成',
|
||||||
|
continue: '继续生成',
|
||||||
|
replace: '替换',
|
||||||
|
exit: '确认退出并舍弃 AI 生成的内容吗?',
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
addKnowledge: '添加关联知识库',
|
addKnowledge: '添加关联知识库',
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,12 @@ export default {
|
||||||
label: '生成',
|
label: '生成',
|
||||||
generatePrompt: '生成提示詞',
|
generatePrompt: '生成提示詞',
|
||||||
placeholder: '請輸入提示詞主題',
|
placeholder: '請輸入提示詞主題',
|
||||||
|
title: '提示詞顯示在這裡',
|
||||||
|
remake: '重新生成',
|
||||||
|
stop: '停止生成',
|
||||||
|
continue: '繼續生成',
|
||||||
|
replace: '替換',
|
||||||
|
exit: '確認退出並捨棄 AI 生成的內容嗎?',
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
addKnowledge: '新增關聯知識庫',
|
addKnowledge: '新增關聯知識庫',
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
style="width: 600px"
|
style="width: 600px"
|
||||||
append-to-body
|
append-to-body
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="true"
|
||||||
:close-on-press-escape="false"
|
:close-on-press-escape="true"
|
||||||
|
:before-close="handleDialogClose"
|
||||||
>
|
>
|
||||||
<div class="generate-prompt-dialog-bg border-r-8">
|
<div class="generate-prompt-dialog-bg border-r-8">
|
||||||
<div class="scrollbar-height">
|
<div class="scrollbar-height">
|
||||||
|
|
@ -28,13 +29,13 @@
|
||||||
</p>
|
</p>
|
||||||
<p v-else class="flex align-center">
|
<p v-else class="flex align-center">
|
||||||
<AppIcon iconName="app-generate-star" class="color-primary mr-4"></AppIcon>
|
<AppIcon iconName="app-generate-star" class="color-primary mr-4"></AppIcon>
|
||||||
提示词显示在这里
|
{{ $t('views.application.generateDialog.title') }}
|
||||||
</p>
|
</p>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div v-if="answer && !loading">
|
<div v-if="answer && !loading && !isStreaming && !showContinueButton">
|
||||||
<el-button type="primary" @click="() => emit('replace', answer)"> 替换 </el-button>
|
<el-button type="primary" @click="() => emit('replace', answer)"> {{ $t('views.application.generateDialog.replace') }} </el-button>
|
||||||
<el-button @click="reAnswerClick" :disabled="!answer || loading" :loading="loading">
|
<el-button @click="reAnswerClick" :disabled="!answer || loading" :loading="loading">
|
||||||
重新生成
|
{{ $t('views.application.generateDialog.remake') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -42,13 +43,18 @@
|
||||||
<!-- 文本输入框 -->
|
<!-- 文本输入框 -->
|
||||||
|
|
||||||
<div class="generate-prompt-operate p-16">
|
<div class="generate-prompt-operate p-16">
|
||||||
<div class="text-center mb-8" v-if="loading">
|
<div v-if="showStopButton" class="text-center mb-8">
|
||||||
<el-button class="border-primary video-stop-button" @click="stopChat">
|
<el-button class="border-primary video-stop-button" @click="pauseStreaming">
|
||||||
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
|
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
|
||||||
停止生成
|
{{ $t('views.application.generateDialog.stop') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="showContinueButton" class="text-center mb-8">
|
||||||
|
<el-button class="border-primary video-stop-button" @click="continueStreaming">
|
||||||
|
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
|
||||||
|
{{ $t('views.application.generateDialog.continue') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="operate-textarea">
|
<div class="operate-textarea">
|
||||||
<el-input
|
<el-input
|
||||||
ref="quickInputRef"
|
ref="quickInputRef"
|
||||||
|
|
@ -66,11 +72,11 @@
|
||||||
<el-button
|
<el-button
|
||||||
text
|
text
|
||||||
class="sent-button"
|
class="sent-button"
|
||||||
:disabled="!inputValue.trim() || loading"
|
:disabled="!inputValue.trim() || loading || isStreaming"
|
||||||
@click="handleSubmit"
|
@click="handleSubmit"
|
||||||
>
|
>
|
||||||
<img v-show="!inputValue.trim() || loading" src="@/assets/icon_send.svg" alt="" />
|
<img v-show="!inputValue.trim() || loading || isStreaming" src="@/assets/icon_send.svg" alt="" />
|
||||||
<SendIcon v-show="inputValue.trim() && !loading" />
|
<SendIcon v-show="inputValue.trim() && !loading && !isStreaming" />
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -82,8 +88,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, nextTick, watch } from 'vue'
|
import { computed, onUnmounted,reactive, ref, nextTick, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { MsgConfirm } from '@/utils/message'
|
||||||
|
import { t } from '@/locales'
|
||||||
import systemGeneratePromptAPI from '@/api/system-resource-management/application'
|
import systemGeneratePromptAPI from '@/api/system-resource-management/application'
|
||||||
import generatePromptAPI from '@/api/application/application'
|
import generatePromptAPI from '@/api/application/application'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
|
|
@ -146,6 +154,71 @@ const promptTemplates = {
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isStreaming = ref<boolean>(false) // 是否正在流式输出
|
||||||
|
const isPaused = ref<boolean>(false) // 是否暂停
|
||||||
|
const fullContent = ref<string>('') // 完整内容缓存
|
||||||
|
const currentDisplayIndex = ref<number>(0) // 当前显示到的字符位置
|
||||||
|
let streamTimer: number | null = null // 定时器引用
|
||||||
|
const isOutputComplete = ref<boolean>(false)
|
||||||
|
|
||||||
|
|
||||||
|
// 模拟流式输出的定时器函数
|
||||||
|
const startStreamingOutput = () => {
|
||||||
|
if (streamTimer) {
|
||||||
|
clearInterval(streamTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
isStreaming.value = true
|
||||||
|
isPaused.value = false
|
||||||
|
|
||||||
|
streamTimer = setInterval(() => {
|
||||||
|
if (!isPaused.value && currentDisplayIndex.value < fullContent.value.length) {
|
||||||
|
// 每次输出1-3个字符,模拟真实的流式输出
|
||||||
|
const step = Math.min(3, fullContent.value.length - currentDisplayIndex.value)
|
||||||
|
currentDisplayIndex.value += step
|
||||||
|
|
||||||
|
// 更新显示内容
|
||||||
|
const currentAnswer = chatMessages.value[chatMessages.value.length - 1]
|
||||||
|
if (currentAnswer && currentAnswer.role === 'ai') {
|
||||||
|
currentAnswer.content = fullContent.value.substring(0, currentDisplayIndex.value)
|
||||||
|
}
|
||||||
|
} else if (loading.value === false && currentDisplayIndex.value >= fullContent.value.length) {
|
||||||
|
stopStreaming()
|
||||||
|
}
|
||||||
|
}, 50) // 每50ms输出一次
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止流式输出
|
||||||
|
const stopStreaming = () => {
|
||||||
|
if (streamTimer) {
|
||||||
|
clearInterval(streamTimer)
|
||||||
|
streamTimer = null
|
||||||
|
}
|
||||||
|
isStreaming.value = false
|
||||||
|
isPaused.value = false
|
||||||
|
loading.value = false
|
||||||
|
isOutputComplete.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const showStopButton = computed(() => {
|
||||||
|
return isStreaming.value
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 暂停流式输出
|
||||||
|
const pauseStreaming = () => {
|
||||||
|
isPaused.value = true
|
||||||
|
isStreaming.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继续流式输出
|
||||||
|
const continueStreaming = () => {
|
||||||
|
if (currentDisplayIndex.value < fullContent.value.length) {
|
||||||
|
startStreamingOutput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取一个递归函数,处理流式数据
|
* 获取一个递归函数,处理流式数据
|
||||||
* @param chat 每一条对话记录
|
* @param chat 每一条对话记录
|
||||||
|
|
@ -154,8 +227,16 @@ const promptTemplates = {
|
||||||
*/
|
*/
|
||||||
const getWrite = (reader: any) => {
|
const getWrite = (reader: any) => {
|
||||||
let tempResult = ''
|
let tempResult = ''
|
||||||
const answer = reactive({ content: '', role: 'ai' })
|
const middleAnswer = reactive({ content: '', role: 'ai' })
|
||||||
chatMessages.value.push(answer)
|
chatMessages.value.push(middleAnswer )
|
||||||
|
|
||||||
|
// 初始化状态并
|
||||||
|
fullContent.value = ''
|
||||||
|
currentDisplayIndex.value = 0
|
||||||
|
isOutputComplete.value = false
|
||||||
|
|
||||||
|
let streamingStarted = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param done 是否结束
|
* @param done 是否结束
|
||||||
|
|
@ -164,8 +245,8 @@ const getWrite = (reader: any) => {
|
||||||
const write_stream = ({ done, value }: { done: boolean; value: any }) => {
|
const write_stream = ({ done, value }: { done: boolean; value: any }) => {
|
||||||
try {
|
try {
|
||||||
if (done) {
|
if (done) {
|
||||||
|
// 流数据接收完成,但定时器继续运行直到显示完所有内容
|
||||||
loading.value = false
|
loading.value = false
|
||||||
// console.log('结束')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const decoder = new TextDecoder('utf-8')
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
|
@ -185,11 +266,15 @@ const getWrite = (reader: any) => {
|
||||||
for (const index in split) {
|
for (const index in split) {
|
||||||
const chunk = JSON?.parse(split[index].replace('data:', ''))
|
const chunk = JSON?.parse(split[index].replace('data:', ''))
|
||||||
if (!chunk.is_end) {
|
if (!chunk.is_end) {
|
||||||
answer.content += chunk.content
|
// 实时将新接收的内容添加到完整内容中
|
||||||
|
fullContent.value += chunk.content
|
||||||
|
if (!streamingStarted) {
|
||||||
|
streamingStarted = true
|
||||||
|
startStreamingOutput()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chunk.is_end) {
|
if (chunk.is_end) {
|
||||||
// 流处理成功 返回成功回调
|
isApiComplete.value = true
|
||||||
loading.value = false
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,6 +282,7 @@ const getWrite = (reader: any) => {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
stopStreaming()
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
return reader.read().then(write_stream)
|
return reader.read().then(write_stream)
|
||||||
|
|
@ -204,7 +290,7 @@ const getWrite = (reader: any) => {
|
||||||
|
|
||||||
return write_stream
|
return write_stream
|
||||||
}
|
}
|
||||||
|
const isApiComplete = ref<boolean>(false)
|
||||||
const answer = computed(() => {
|
const answer = computed(() => {
|
||||||
const result = chatMessages.value[chatMessages.value.length - 1]
|
const result = chatMessages.value[chatMessages.value.length - 1]
|
||||||
|
|
||||||
|
|
@ -214,6 +300,12 @@ const answer = computed(() => {
|
||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 按钮状态计算
|
||||||
|
const showContinueButton = computed(() => {
|
||||||
|
return !isStreaming.value && isPaused.value && currentDisplayIndex.value < fullContent.value.length
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
function generatePrompt(inputValue: any) {
|
function generatePrompt(inputValue: any) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const workspaceId = user.getWorkspaceId() || 'default'
|
const workspaceId = user.getWorkspaceId() || 'default'
|
||||||
|
|
@ -268,8 +360,12 @@ const handleSubmit = (event?: any) => {
|
||||||
if (!originalUserInput.value) {
|
if (!originalUserInput.value) {
|
||||||
originalUserInput.value = inputValue.value
|
originalUserInput.value = inputValue.value
|
||||||
}
|
}
|
||||||
generatePrompt(inputValue.value)
|
if (inputValue.value) {
|
||||||
|
generatePrompt(inputValue.value)
|
||||||
inputValue.value = ''
|
inputValue.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 如果同时按下ctrl/shift/cmd/opt +enter,则会换行
|
// 如果同时按下ctrl/shift/cmd/opt +enter,则会换行
|
||||||
insertNewlineAtCursor(event)
|
insertNewlineAtCursor(event)
|
||||||
|
|
@ -290,11 +386,6 @@ const insertNewlineAtCursor = (event?: any) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopChat = () => {
|
|
||||||
loading.value = false
|
|
||||||
chatMessages.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = (modelId: string, applicationId: string) => {
|
const open = (modelId: string, applicationId: string) => {
|
||||||
modelID.value = modelId
|
modelID.value = modelId
|
||||||
applicationID.value = applicationId
|
applicationID.value = applicationId
|
||||||
|
|
@ -323,6 +414,38 @@ const handleScroll = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDialogClose = (done: () => void) => {
|
||||||
|
|
||||||
|
// 弹出 消息
|
||||||
|
MsgConfirm(
|
||||||
|
t('common.tip'),
|
||||||
|
t('views.application.generateDialog.exit'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('common.confirm'),
|
||||||
|
cancelButtonText: t('common.cancel'),
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
// 点击确认,清除状态
|
||||||
|
stopStreaming()
|
||||||
|
chatMessages.value = []
|
||||||
|
fullContent.value = ''
|
||||||
|
currentDisplayIndex.value = 0
|
||||||
|
isOutputComplete.value = false
|
||||||
|
done() // 真正关闭
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 点击取消
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载时清理定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopStreaming()
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
answer,
|
answer,
|
||||||
() => {
|
() => {
|
||||||
|
|
|
||||||
|
|
@ -443,7 +443,8 @@ const openGeneratePromptDialog = (modelId: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const replace = (v: any) => {
|
const replace = (v: any) => {
|
||||||
set(props.nodeModel.properties.node_data.model_setting, 'system', v)
|
console.log(props.nodeModel.properties.node_data.model_setting)
|
||||||
|
set(props.nodeModel.properties.node_data, 'system', v)
|
||||||
}
|
}
|
||||||
const openReasoningParamSettingDialog = () => {
|
const openReasoningParamSettingDialog = () => {
|
||||||
ReasoningParamSettingDialogRef.value?.open(chat_data.value.model_setting)
|
ReasoningParamSettingDialogRef.value?.open(chat_data.value.model_setting)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue