1111 </template >
1212
1313 <div class =" robot-chat-container-content" ref =" chatContainerRef" >
14- <div v-if =" messages.filter((item) => item.role !== 'system' ).length === 0" >
14+ <div v-if =" messages.filter((item) => item.role !== RobotMessageRole.System ).length === 0" >
1515 <tr-welcome title =" AI助手" description =" 您好,我是您的开发小助手" :icon =" welcomeIcon" class =" robot-welcome" >
1616 </tr-welcome >
1717 <tr-prompts
@@ -99,10 +99,18 @@ import {
9999 type RawFileAttachment ,
100100 type BubbleContentRendererMatch
101101} from ' @opentiny/tiny-robot'
102- import { type ChatMessage } from ' @opentiny/tiny-robot-kit'
103102import { GeneratingStatus } from ' ../../constants/status'
104103import { LoadingRenderer , MarkdownRenderer , ImgRenderer } from ' ../renderers'
105104import { useNotify } from ' @opentiny/tiny-engine-meta-register'
105+ import {
106+ RobotMessageContentType ,
107+ RobotMessageRole ,
108+ type MessageContentResolver ,
109+ type RobotInputContentPart ,
110+ type RobotMessage ,
111+ type RobotRenderContentItem
112+ } from ' ../../types'
113+ import { extractMessageText } from ' ../../utils'
106114
107115const props = defineProps ({
108116 promptItems: {
@@ -114,7 +122,7 @@ const props = defineProps({
114122 },
115123 status: { type: String },
116124 messageContentResolver: {
117- type: Function
125+ type: Function as PropType < MessageContentResolver >
118126 },
119127 allowFiles: {
120128 type: Boolean ,
@@ -141,7 +149,7 @@ const selectedAttachments = ref([])
141149const robotVisible = defineModel <boolean >(' show' , { required: true })
142150const fullscreen = defineModel <boolean >(' fullscreen' )
143151const inputMessage = defineModel <string >(' input' , { required: true })
144- const messages = defineModel <ChatMessage []>(' messages' , { required: true })
152+ const messages = defineModel <RobotMessage []>(' messages' , { required: true })
145153const senderRef = ref <InstanceType <typeof TrSender > | null >(null )
146154
147155watch (
@@ -161,47 +169,29 @@ const contentRendererMatches = computed<BubbleContentRendererMatch[]>(() => [
161169 },
162170 ... Object .entries (props .bubbleRenderers ).map (([type , renderer ]) => ({
163171 priority: BubbleRendererMatchPriority .NORMAL ,
164- find : (_message : any , content : any ) => content ?.type === type ,
172+ find : (_message : RobotMessage , content : RobotRenderContentItem ) => content ?.type === type ,
165173 renderer
166174 })),
167175 {
168176 priority: BubbleRendererMatchPriority .NORMAL ,
169- find : (message : any , content : any ) => content ?.type === ' tool' && message .tool_calls ?.length ,
177+ find : (message : RobotMessage , content : RobotRenderContentItem ) =>
178+ content ?.type === RobotMessageContentType .Tool && Boolean (message .tool_calls ?.length ),
170179 renderer: BubbleRenderers .Tools
171180 },
172181 {
173182 priority: BubbleRendererMatchPriority .NORMAL ,
174- find : (_message : any , content : any ) => ! content ?.type || [' markdown' , ' text' ].includes (content .type ),
183+ find : (_message : RobotMessage , content : RobotRenderContentItem ) =>
184+ ! content ?.type || [RobotMessageContentType .Markdown , RobotMessageContentType .Text ].includes (content .type as any ),
175185 renderer: MarkdownRenderer
176186 },
177187 {
178188 priority: BubbleRendererMatchPriority .NORMAL ,
179- find : (_message : any , content : any ) => [' img' , ' image' ].includes (content ?.type ),
189+ find : (_message : RobotMessage , content : RobotRenderContentItem ) =>
190+ [RobotMessageContentType .Img , RobotMessageContentType .Image ].includes (content ?.type as any ),
180191 renderer: ImgRenderer
181192 }
182193])
183194
184- const getTextContent = (content : any ) => {
185- if (typeof content === ' string' ) {
186- return content
187- }
188- if (Array .isArray (content )) {
189- return content
190- .map ((item ) => {
191- if (typeof item === ' string' ) {
192- return item
193- }
194- if (item ?.type === ' text' ) {
195- return item .text ?? item .content ?? ' '
196- }
197- return ' '
198- })
199- .filter (Boolean )
200- .join (' \n ' )
201- }
202- return ' '
203- }
204-
205195// 处理文件选择事件
206196const handleSingleFilesSelected = (files : File [] | null , retry = false ) => {
207197 if (! files ?.length ) return
@@ -257,7 +247,7 @@ const getSvgIcon = (name: string, style?: CSSProperties) => {
257247const aiAvatar = getSvgIcon (' AI' )
258248const welcomeIcon = getSvgIcon (' AI' , { fontSize: ' 44px' })
259249
260- const resolveMessageContent = (message : any ) => {
250+ const resolveMessageContent = (message : RobotMessage ) => {
261251 if (props .messageContentResolver ) {
262252 return props .messageContentResolver (message , {
263253 messages: messages .value ,
@@ -266,16 +256,16 @@ const resolveMessageContent = (message: any) => {
266256 }
267257
268258 if (Array .isArray (message .renderContent ) && message .renderContent .length > 0 ) {
269- return message .renderContent .map ((item : any ) => {
270- if (item ?.type === ' img ' || item ?.type === ' image ' ) {
259+ return message .renderContent .map ((item ) => {
260+ if (item ?.type === RobotMessageContentType . Img || item ?.type === RobotMessageContentType . Image ) {
271261 return {
272- type: ' img ' ,
262+ type: RobotMessageContentType . Img ,
273263 content: item .content || item .url || item .image_url ?.url || ' '
274264 }
275265 }
276- if (item ?.type === ' text ' ) {
266+ if (item ?.type === RobotMessageContentType . Text ) {
277267 return {
278- type: ' text ' ,
268+ type: RobotMessageContentType . Text ,
279269 content: item .content ?? item .text ?? ' '
280270 }
281271 }
@@ -284,15 +274,13 @@ const resolveMessageContent = (message: any) => {
284274 }
285275
286276 if (Array .isArray (message .content ) && message .content .length > 0 ) {
287- const textContent = getTextContent (
288- message .content .map ((item : any ) => item ?.text ?? item ?.content ?? item ?.image_url ?.url ?? ' ' )
289- )
277+ const textContent = extractMessageText (message .content )
290278 if (textContent ) {
291279 return textContent
292280 }
293281 }
294282
295- const textContent = getTextContent (message .content )
283+ const textContent = extractMessageText (message .content )
296284 if (textContent ) {
297285 return textContent
298286 }
@@ -301,14 +289,14 @@ const resolveMessageContent = (message: any) => {
301289}
302290
303291const roleConfigs: Record <string , BubbleRoleConfig > = {
304- assistant : {
292+ [ RobotMessageRole . Assistant ] : {
305293 placement: ' start' ,
306294 avatar: aiAvatar
307295 },
308- user : {
296+ [ RobotMessageRole . User ] : {
309297 placement: ' end'
310298 },
311- system : {
299+ [ RobotMessageRole . System ] : {
312300 hidden: true
313301 }
314302}
@@ -328,38 +316,38 @@ const handleSendMessage = async (content: string) => {
328316 return
329317 }
330318
331- const userMessage: ChatMessage = {
332- role: ' user ' ,
319+ const userMessage: RobotMessage = {
320+ role: RobotMessageRole . User ,
333321 content: messageContent
334322 }
335323 const files = selectedAttachments .value .filter ((item ) => item .status === ' success' )
336324 if (files .length > 0 ) {
337325 userMessage .content = [
338326 {
339- type: ' text ' ,
327+ type: RobotMessageContentType . Text ,
340328 text: messageContent
341329 },
342330 ... files .map ((item ) => ({
343- type: ' image_url ' ,
331+ type: RobotMessageContentType . ImageUrl ,
344332 image_url: {
345333 url: item .url
346334 }
347335 }))
348- ] as any
336+ ] as RobotInputContentPart []
349337 userMessage .renderContent = [
350338 {
351- type: ' text ' ,
339+ type: RobotMessageContentType . Text ,
352340 content: messageContent
353341 },
354342 ... files .map ((item ) => ({
355- type: ' img ' ,
343+ type: RobotMessageContentType . Img ,
356344 content: item .url
357345 }))
358346 ]
359347 } else {
360348 userMessage .renderContent = [
361349 {
362- type: ' text ' ,
350+ type: RobotMessageContentType . Text ,
363351 content: messageContent
364352 }
365353 ]
0 commit comments