8282
8383<script lang="ts">
8484import { ref , onMounted , watchEffect , type CSSProperties , h , resolveComponent } from ' vue'
85- import { Notify , Loading , TinyPopover , TinyDialogBox , TinyButton } from ' @opentiny/vue'
85+ import { Notify , Loading , TinyPopover , TinyDialogBox } from ' @opentiny/vue'
8686import { useCanvas , usePage , useModal , getMetaApi , META_SERVICE } from ' @opentiny/tiny-engine-meta-register'
87- import {
88- TrContainer ,
89- TrWelcome ,
90- TrPrompts ,
91- TrBubbleList ,
92- TrSender ,
93- TrFeedback ,
94- TrAttachments
95- } from ' @opentiny/tiny-robot'
87+ import { TrContainer , TrWelcome , TrBubbleList , TrSender , TrFeedback , TrAttachments } from ' @opentiny/tiny-robot'
9688import { IconNewSession } from ' @opentiny/tiny-robot-svgs'
9789import SchemaRenderer from ' @opentiny/tiny-schema-renderer'
9890import RobotSettingPopover from ' ./RobotSettingPopover.vue'
@@ -105,19 +97,17 @@ import {
10597} from ' ./js/robotSetting'
10698import { PROMPTS } from ' ./js/prompts'
10799import * as jsonpatch from ' fast-json-patch'
100+ import { chatStream } from ' ./js/utils'
108101
109102export default {
110103 components: {
111- TinyPopover ,
112- TinyDialogBox ,
113- TinyButton ,
104+ TinyPopover: TinyPopover as unknown ,
105+ TinyDialogBox: TinyDialogBox as unknown ,
114106 RobotSettingPopover ,
115107 TrContainer ,
116108 TrWelcome ,
117- TrPrompts ,
118109 TrBubbleList ,
119110 TrSender ,
120- TrFeedback ,
121111 TrAttachments ,
122112 IconNewSession ,
123113 SchemaRenderer
@@ -215,7 +205,7 @@ export default {
215205 showPreview .value = false
216206 }
217207
218- const fixedJson = (op ) => {
208+ const _fixedJson = (op ) => {
219209 // 修正 path:替换 ; 和 : 为 /
220210 if (op .path ) {
221211 op .path = op .path .replace (/ [;:] / g , ' /' ).replace (/ \/ + / g , ' /' )
@@ -235,55 +225,114 @@ export default {
235225 op .value = { [key .trim ()]: val .trim () }
236226 }
237227 } catch (e ) {
228+ // eslint-disable-next-line no-console
238229 console .warn (' Failed to parse style:' , op .value )
239230 }
240231 }
241232
242233 return op
243234 }
244235
245- const sendRequest = () => {
246- getMetaApi (META_SERVICE .Http )
247- .post (' /app-center/api/ai/chat' , getSendSeesionProcess (), { timeout: 600000 })
248- .then ((res : any ) => {
249- const { choices } = res
250- const chatMessage = choices [0 ]?.message
251- try {
252- const regex = / ```json([\s\S ] *? )```/
253- const match = chatMessage ?.content .match (regex )
254-
255- if (match && match [1 ] && JSON .parse (match [1 ]) && isValidFastJsonPatch (JSON .parse (match [1 ]))) {
256- const newValue = JSON .parse (match [1 ])
257- // 使用 applyPatch 修改 Schema
258- const result = newValue .reduce (jsonpatch .applyReducer , pageState .pageSchema )
259-
260- sessionProcess .messages .push (
261- getAiRespMessage (JSON .stringify (pageState .pageSchema , null , 2 ), chatMessage .role )
262- )
263- sessionProcess .displayMessages .push (getAiDisplayMessage (MESSAGE_TIP , chatMessage .role , result , res .id ))
264- messages .value [messages .value .length - 1 ].content = MESSAGE_TIP
265- messages .value [messages .value .length - 1 ].schema = result
266- messages .value [messages .value .length - 1 ].id = res .id
267- } else {
268- sessionProcess .messages .push (getAiRespMessage (chatMessage ?.content ))
269- sessionProcess .displayMessages .push (getAiRespMessage (chatMessage ?.content ))
270- messages .value [messages .value .length - 1 ].content = chatMessage ?.content
236+ // 处理响应
237+ const handleResponse = (res : { id: string ; chatMessage: any }) => {
238+ try {
239+ const regex = / ```json([\s\S ] *? )```/
240+ const match = chatMessage ?.content .match (regex )
241+
242+ if (match && match [1 ] && JSON .parse (match [1 ]) && isValidFastJsonPatch (JSON .parse (match [1 ]))) {
243+ const newValue = JSON .parse (match [1 ])
244+ // 使用 applyPatch 修改 Schema
245+ const result = newValue .reduce (jsonpatch .applyReducer , pageState .pageSchema )
246+
247+ sessionProcess .messages .push (
248+ getAiRespMessage (JSON .stringify (pageState .pageSchema , null , 2 ), chatMessage .role )
249+ )
250+ sessionProcess .displayMessages .push (getAiDisplayMessage (MESSAGE_TIP , chatMessage .role , result , res .id ))
251+ messages .value [messages .value .length - 1 ].content = MESSAGE_TIP
252+ messages .value [messages .value .length - 1 ].schema = result
253+ messages .value [messages .value .length - 1 ].id = res .id
254+ } else {
255+ sessionProcess .messages .push (getAiRespMessage (chatMessage ?.content ))
256+ sessionProcess .displayMessages .push (getAiRespMessage (chatMessage ?.content ))
257+ messages .value [messages .value .length - 1 ].content = chatMessage ?.content
258+ }
259+ setContextSession ()
260+ inProcesing .value = false
261+ connectedFailed .value = false
262+ } catch (e ) {
263+ messages .value [messages .value .length - 1 ].content = ' 处理响应时出错'
264+ inProcesing .value = false
265+ connectedFailed .value = false
266+ }
267+ }
268+
269+ // 发送流式请求
270+ const _sendStreamRequest = async () => {
271+ const requestData = getSendSeesionProcess ()
272+ if (requestData .foundationModel ) {
273+ requestData .foundationModel .stream = true
274+ }
275+
276+ let streamContent = ' '
277+ const chatId = Date .now ().toString ()
278+ await chatStream (
279+ {
280+ requestUrl: ' /app-center/api/ai/chat' ,
281+ requestData
282+ },
283+ {
284+ onData : (data ) => {
285+ const choice = data .choices ?.[0 ]
286+ if (choice && choice .delta .content ) {
287+ if (messages .value .length === 0 || messages .value [messages .value .length - 1 ].role !== ' assistant' ) {
288+ messages .value .push (getAiDisplayMessage (' ' , ' assistant' , {}, chatId ))
289+ }
290+ if (streamContent !== messages .value [messages .value .length - 1 ].content ) {
291+ messages .value [messages .value .length - 1 ].content = ' '
292+ }
293+ streamContent += choice .delta .content
294+ messages .value [messages .value .length - 1 ].content += choice .delta .content
271295 }
272- setContextSession ()
296+ },
297+ onError : (error ) => {
298+ messages .value [messages .value .length - 1 ].content = ' 连接失败'
299+ localStorage .removeItem (' aiChat' )
273300 inProcesing .value = false
274301 connectedFailed .value = false
275- } catch (e ) {
276- messages .value [messages .value .length - 1 ].content = ' 处理响应时出错'
302+ // eslint-disable-next-line no-console
303+ console .error (' Stream error:' , error )
304+ },
305+ onDone : () => {
306+ // handleResponse({
307+ // id: chatId,
308+ // chatMessage: {
309+ // role: 'assistant',
310+ // content: streamContent || '没有返回内容',
311+ // name: 'AI'
312+ // }
313+ // })
314+ sessionProcess .messages .push (getAiRespMessage (streamContent ))
315+ sessionProcess .displayMessages .push (getAiRespMessage (streamContent ))
316+ setContextSession ()
277317 inProcesing .value = false
278318 connectedFailed .value = false
279319 }
320+ }
321+ )
322+ }
323+
324+ const sendRequest = async () => {
325+ try {
326+ const res: any = await getMetaApi (META_SERVICE .Http ).post (' /app-center/api/ai/chat' , getSendSeesionProcess (), {
327+ timeout: 600000
280328 })
281- .catch (() => {
282- messages .value [messages .value .length - 1 ].content = ' 连接失败'
283- localStorage .removeItem (' aiChat' )
284- inProcesing .value = false
285- connectedFailed .value = false
286- })
329+ handleResponse ({ id: res .id , chatMessage: res .choices [0 ].message })
330+ } catch (error ) {
331+ messages .value [messages .value .length - 1 ].content = ' 连接失败'
332+ localStorage .removeItem (' aiChat' )
333+ inProcesing .value = false
334+ connectedFailed .value = false
335+ }
287336 }
288337
289338 const scrollContent = async () => {
@@ -387,7 +436,7 @@ export default {
387436 await sleep (1000 )
388437 messages .value .push (getAiDisplayMessage (' 好的,正在执行相关操作,请稍等片刻...' ))
389438 await scrollContent ()
390- sendRequest ()
439+ await sendRequest ()
391440 }
392441 }
393442
@@ -505,6 +554,10 @@ export default {
505554 placement: ' start' ,
506555 avatar: aiAvatar ,
507556 maxWidth: ' 80%' ,
557+ type: ' markdown' ,
558+ mdConfig: {
559+ breaks: true
560+ },
508561 slots: {
509562 footer : ({ bubbleProps }) => {
510563 return h (TrFeedback , {
@@ -528,7 +581,15 @@ export default {
528581 }
529582 }
530583 },
531- user: { placement: ' end' , avatar: userAvatar , maxWidth: ' 80%' }
584+ user: {
585+ placement: ' end' ,
586+ avatar: userAvatar ,
587+ maxWidth: ' 80%' ,
588+ type: ' markdown' ,
589+ mdConfig: {
590+ breaks: true
591+ }
592+ }
532593 })
533594
534595 // 处理文件选择事件
@@ -588,6 +649,7 @@ export default {
588649 }
589650 })
590651 } catch (error ) {
652+ // eslint-disable-next-line no-console
591653 console .error (' 上传失败' , error )
592654 }
593655 }
0 commit comments