Skip to content

Commit 29cf383

Browse files
committed
fix: Prompt generate
--bug=1061828 --user=张展玮 【提示词生成】- AI对话节点中,生成的提示词点击替换不管用 https://www.tapd.cn/62980211/s/1773916
1 parent b4cdd87 commit 29cf383

File tree

5 files changed

+170
-28
lines changed

5 files changed

+170
-28
lines changed

ui/src/locales/lang/en-US/views/application.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ export default {
135135
label: 'Generate',
136136
generatePrompt: 'Generate Prompt',
137137
placeholder: 'Please enter the prompt topic',
138+
title: 'The prompt is displayed here',
139+
remake: 'Regenerate',
140+
stop: 'Stop Generating',
141+
continue: 'Continue Generating',
142+
replace: 'Replace',
143+
exit: 'Are you sure you want to exit and discard the AI-generated content?',
138144
},
139145
dialog: {
140146
addKnowledge: 'Add Related Knowledge',

ui/src/locales/lang/zh-CN/views/application.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ export default {
127127
label: '生成',
128128
generatePrompt: '生成提示词',
129129
placeholder: '请输入提示词主题',
130+
title: '提示词显示在这里',
131+
remake: '重新生成',
132+
stop: '停止生成',
133+
continue: '继续生成',
134+
replace: '替换',
135+
exit: '确认退出并舍弃 AI 生成的内容吗?',
130136
},
131137
dialog: {
132138
addKnowledge: '添加关联知识库',

ui/src/locales/lang/zh-Hant/views/application.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ export default {
126126
label: '生成',
127127
generatePrompt: '生成提示詞',
128128
placeholder: '請輸入提示詞主題',
129+
title: '提示詞顯示在這裡',
130+
remake: '重新生成',
131+
stop: '停止生成',
132+
continue: '繼續生成',
133+
replace: '替換',
134+
exit: '確認退出並捨棄 AI 生成的內容嗎?',
129135
},
130136
dialog: {
131137
addKnowledge: '新增關聯知識庫',

ui/src/views/application/component/GeneratePromptDialog.vue

Lines changed: 150 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
v-model="dialogVisible"
66
style="width: 600px"
77
append-to-body
8-
:close-on-click-modal="false"
9-
:close-on-press-escape="false"
8+
:close-on-click-modal="true"
9+
:close-on-press-escape="true"
10+
:before-close="handleDialogClose"
1011
>
1112
<div class="generate-prompt-dialog-bg border-r-8">
1213
<div class="scrollbar-height">
@@ -28,27 +29,32 @@
2829
</p>
2930
<p v-else class="flex align-center">
3031
<AppIcon iconName="app-generate-star" class="color-primary mr-4"></AppIcon>
31-
提示词显示在这里
32+
{{ $t('views.application.generateDialog.title') }}
3233
</p>
3334
</el-scrollbar>
34-
<div v-if="answer && !loading">
35-
<el-button type="primary" @click="() => emit('replace', answer)"> 替换 </el-button>
35+
<div v-if="answer && !loading && !isStreaming && !showContinueButton">
36+
<el-button type="primary" @click="() => emit('replace', answer)"> {{ $t('views.application.generateDialog.replace') }} </el-button>
3637
<el-button @click="reAnswerClick" :disabled="!answer || loading" :loading="loading">
37-
重新生成
38+
{{ $t('views.application.generateDialog.remake') }}
3839
</el-button>
3940
</div>
4041
</div>
4142

4243
<!-- 文本输入框 -->
4344

4445
<div class="generate-prompt-operate p-16">
45-
<div class="text-center mb-8" v-if="loading">
46-
<el-button class="border-primary video-stop-button" @click="stopChat">
46+
<div v-if="showStopButton" class="text-center mb-8">
47+
<el-button class="border-primary video-stop-button" @click="pauseStreaming">
4748
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
48-
停止生成
49+
{{ $t('views.application.generateDialog.stop') }}
50+
</el-button>
51+
</div>
52+
<div v-if="showContinueButton" class="text-center mb-8">
53+
<el-button class="border-primary video-stop-button" @click="continueStreaming">
54+
<app-icon iconName="app-video-stop" class="mr-8"></app-icon>
55+
{{ $t('views.application.generateDialog.continue') }}
4956
</el-button>
5057
</div>
51-
5258
<div class="operate-textarea">
5359
<el-input
5460
ref="quickInputRef"
@@ -66,11 +72,11 @@
6672
<el-button
6773
text
6874
class="sent-button"
69-
:disabled="!inputValue.trim() || loading"
75+
:disabled="!inputValue.trim() || loading || isStreaming"
7076
@click="handleSubmit"
7177
>
72-
<img v-show="!inputValue.trim() || loading" src="@/assets/icon_send.svg" alt="" />
73-
<SendIcon v-show="inputValue.trim() && !loading" />
78+
<img v-show="!inputValue.trim() || loading || isStreaming" src="@/assets/icon_send.svg" alt="" />
79+
<SendIcon v-show="inputValue.trim() && !loading && !isStreaming" />
7480
</el-button>
7581
</div>
7682
</div>
@@ -82,8 +88,10 @@
8288
</template>
8389

8490
<script setup lang="ts">
85-
import { computed, reactive, ref, nextTick, watch } from 'vue'
91+
import { computed, onUnmounted,reactive, ref, nextTick, watch } from 'vue'
8692
import { useRoute } from 'vue-router'
93+
import { MsgConfirm } from '@/utils/message'
94+
import { t } from '@/locales'
8795
import systemGeneratePromptAPI from '@/api/system-resource-management/application'
8896
import generatePromptAPI from '@/api/application/application'
8997
import useStore from '@/stores'
@@ -146,6 +154,71 @@ const promptTemplates = {
146154
`,
147155
}
148156
157+
const isStreaming = ref<boolean>(false) // 是否正在流式输出
158+
const isPaused = ref<boolean>(false) // 是否暂停
159+
const fullContent = ref<string>('') // 完整内容缓存
160+
const currentDisplayIndex = ref<number>(0) // 当前显示到的字符位置
161+
let streamTimer: number | null = null // 定时器引用
162+
const isOutputComplete = ref<boolean>(false)
163+
164+
165+
// 模拟流式输出的定时器函数
166+
const startStreamingOutput = () => {
167+
if (streamTimer) {
168+
clearInterval(streamTimer)
169+
}
170+
171+
isStreaming.value = true
172+
isPaused.value = false
173+
174+
streamTimer = setInterval(() => {
175+
if (!isPaused.value && currentDisplayIndex.value < fullContent.value.length) {
176+
// 每次输出1-3个字符,模拟真实的流式输出
177+
const step = Math.min(3, fullContent.value.length - currentDisplayIndex.value)
178+
currentDisplayIndex.value += step
179+
180+
// 更新显示内容
181+
const currentAnswer = chatMessages.value[chatMessages.value.length - 1]
182+
if (currentAnswer && currentAnswer.role === 'ai') {
183+
currentAnswer.content = fullContent.value.substring(0, currentDisplayIndex.value)
184+
}
185+
} else if (loading.value === false && currentDisplayIndex.value >= fullContent.value.length) {
186+
stopStreaming()
187+
}
188+
}, 50) // 每50ms输出一次
189+
}
190+
191+
// 停止流式输出
192+
const stopStreaming = () => {
193+
if (streamTimer) {
194+
clearInterval(streamTimer)
195+
streamTimer = null
196+
}
197+
isStreaming.value = false
198+
isPaused.value = false
199+
loading.value = false
200+
isOutputComplete.value = true
201+
}
202+
203+
const showStopButton = computed(() => {
204+
return isStreaming.value
205+
})
206+
207+
208+
// 暂停流式输出
209+
const pauseStreaming = () => {
210+
isPaused.value = true
211+
isStreaming.value = false
212+
}
213+
214+
// 继续流式输出
215+
const continueStreaming = () => {
216+
if (currentDisplayIndex.value < fullContent.value.length) {
217+
startStreamingOutput()
218+
}
219+
}
220+
221+
149222
/**
150223
* 获取一个递归函数,处理流式数据
151224
* @param chat 每一条对话记录
@@ -154,8 +227,16 @@ const promptTemplates = {
154227
*/
155228
const getWrite = (reader: any) => {
156229
let tempResult = ''
157-
const answer = reactive({ content: '', role: 'ai' })
158-
chatMessages.value.push(answer)
230+
const middleAnswer = reactive({ content: '', role: 'ai' })
231+
chatMessages.value.push(middleAnswer )
232+
233+
// 初始化状态并
234+
fullContent.value = ''
235+
currentDisplayIndex.value = 0
236+
isOutputComplete.value = false
237+
238+
let streamingStarted = false
239+
159240
/**
160241
*
161242
* @param done 是否结束
@@ -164,8 +245,8 @@ const getWrite = (reader: any) => {
164245
const write_stream = ({ done, value }: { done: boolean; value: any }) => {
165246
try {
166247
if (done) {
248+
// 流数据接收完成,但定时器继续运行直到显示完所有内容
167249
loading.value = false
168-
// console.log('结束')
169250
return
170251
}
171252
const decoder = new TextDecoder('utf-8')
@@ -185,26 +266,31 @@ const getWrite = (reader: any) => {
185266
for (const index in split) {
186267
const chunk = JSON?.parse(split[index].replace('data:', ''))
187268
if (!chunk.is_end) {
188-
answer.content += chunk.content
269+
// 实时将新接收的内容添加到完整内容中
270+
fullContent.value += chunk.content
271+
if (!streamingStarted) {
272+
streamingStarted = true
273+
startStreamingOutput()
274+
}
189275
}
190276
if (chunk.is_end) {
191-
// 流处理成功 返回成功回调
192-
loading.value = false
277+
isApiComplete.value = true
193278
return Promise.resolve()
194279
}
195280
}
196281
}
197282
}
198283
} catch (e) {
199284
loading.value = false
285+
stopStreaming()
200286
return Promise.reject(e)
201287
}
202288
return reader.read().then(write_stream)
203289
}
204290
205291
return write_stream
206292
}
207-
293+
const isApiComplete = ref<boolean>(false)
208294
const answer = computed(() => {
209295
const result = chatMessages.value[chatMessages.value.length - 1]
210296
@@ -214,6 +300,12 @@ const answer = computed(() => {
214300
return ''
215301
})
216302
303+
// 按钮状态计算
304+
const showContinueButton = computed(() => {
305+
return !isStreaming.value && isPaused.value && currentDisplayIndex.value < fullContent.value.length
306+
})
307+
308+
217309
function generatePrompt(inputValue: any) {
218310
loading.value = true
219311
const workspaceId = user.getWorkspaceId() || 'default'
@@ -268,8 +360,12 @@ const handleSubmit = (event?: any) => {
268360
if (!originalUserInput.value) {
269361
originalUserInput.value = inputValue.value
270362
}
271-
generatePrompt(inputValue.value)
363+
if (inputValue.value) {
364+
generatePrompt(inputValue.value)
272365
inputValue.value = ''
366+
}
367+
368+
273369
} else {
274370
// 如果同时按下ctrl/shift/cmd/opt +enter,则会换行
275371
insertNewlineAtCursor(event)
@@ -290,11 +386,6 @@ const insertNewlineAtCursor = (event?: any) => {
290386
})
291387
}
292388
293-
const stopChat = () => {
294-
loading.value = false
295-
chatMessages.value = []
296-
}
297-
298389
const open = (modelId: string, applicationId: string) => {
299390
modelID.value = modelId
300391
applicationID.value = applicationId
@@ -323,6 +414,38 @@ const handleScroll = () => {
323414
}
324415
}
325416
417+
const handleDialogClose = (done: () => void) => {
418+
419+
// 弹出 消息
420+
MsgConfirm(
421+
t('common.tip'),
422+
t('views.application.generateDialog.exit'),
423+
{
424+
confirmButtonText: t('common.confirm'),
425+
cancelButtonText: t('common.cancel'),
426+
distinguishCancelAndClose: true,
427+
}
428+
)
429+
.then(() => {
430+
// 点击确认,清除状态
431+
stopStreaming()
432+
chatMessages.value = []
433+
fullContent.value = ''
434+
currentDisplayIndex.value = 0
435+
isOutputComplete.value = false
436+
done() // 真正关闭
437+
})
438+
.catch(() => {
439+
// 点击取消
440+
}
441+
)
442+
}
443+
444+
// 组件卸载时清理定时器
445+
onUnmounted(() => {
446+
stopStreaming()
447+
})
448+
326449
watch(
327450
answer,
328451
() => {

ui/src/workflow/nodes/ai-chat-node/index.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,8 @@ const openGeneratePromptDialog = (modelId: string) => {
443443
}
444444
}
445445
const replace = (v: any) => {
446-
set(props.nodeModel.properties.node_data.model_setting, 'system', v)
446+
console.log(props.nodeModel.properties.node_data.model_setting)
447+
set(props.nodeModel.properties.node_data, 'system', v)
447448
}
448449
const openReasoningParamSettingDialog = () => {
449450
ReasoningParamSettingDialogRef.value?.open(chat_data.value.model_setting)

0 commit comments

Comments
 (0)