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: {
@@ -113,7 +121,9 @@ const props = defineProps({
113121 type: Function
114122 },
115123 status: { type: String },
116- chatMode: { type: String },
124+ messageContentResolver: {
125+ type: Function as PropType <MessageContentResolver >
126+ },
117127 allowFiles: {
118128 type: Boolean ,
119129 default: false
@@ -139,7 +149,7 @@ const selectedAttachments = ref([])
139149const robotVisible = defineModel <boolean >(' show' , { required: true })
140150const fullscreen = defineModel <boolean >(' fullscreen' )
141151const inputMessage = defineModel <string >(' input' , { required: true })
142- const messages = defineModel <ChatMessage []>(' messages' , { required: true })
152+ const messages = defineModel <RobotMessage []>(' messages' , { required: true })
143153const senderRef = ref <InstanceType <typeof TrSender > | null >(null )
144154
145155watch (
@@ -159,63 +169,29 @@ const contentRendererMatches = computed<BubbleContentRendererMatch[]>(() => [
159169 },
160170 ... Object .entries (props .bubbleRenderers ).map (([type , renderer ]) => ({
161171 priority: BubbleRendererMatchPriority .NORMAL ,
162- find : (_message : any , content : any ) => content ?.type === type ,
172+ find : (_message : RobotMessage , content : RobotRenderContentItem ) => content ?.type === type ,
163173 renderer
164174 })),
165175 {
166176 priority: BubbleRendererMatchPriority .NORMAL ,
167- 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 ),
168179 renderer: BubbleRenderers .Tools
169180 },
170181 {
171182 priority: BubbleRendererMatchPriority .NORMAL ,
172- find : (message : any , content : any ) =>
173- ! message . loading && message . content && ( ! 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 ),
174185 renderer: MarkdownRenderer
175186 },
176187 {
177188 priority: BubbleRendererMatchPriority .NORMAL ,
178- find : (message : any ) => message ?.content ?.[0 ]?.type === ' img' || message ?.content ?.[0 ]?.type === ' image' ,
189+ find : (_message : RobotMessage , content : RobotRenderContentItem ) =>
190+ [RobotMessageContentType .Img , RobotMessageContentType .Image ].includes (content ?.type as any ),
179191 renderer: ImgRenderer
180192 }
181193])
182194
183- const isAgentMessage = (message : any ) => {
184- const hasAgentContent = message .renderContent ?.some ((item : any ) => {
185- return item .type === ' agent-content' || item .type === ' agent-loading'
186- })
187- return message .metadata ?.chatMode === ' agent' || hasAgentContent
188- }
189-
190- const resolveAgentRenderContent = (message : any ) => {
191- if (! isAgentMessage (message ) || message .role !== ' assistant' ) {
192- return message .renderContent
193- }
194-
195- const isLastMessage = messages .value .at (- 1 ) === message
196- const isGenerating = Boolean (message .loading ) || (isLastMessage && GeneratingStatus .includes (props .status as any ))
197- const renderContent = isGenerating
198- ? message .renderContent
199- : message .renderContent .filter ((item : any ) => item .type !== ' agent-loading' )
200- const agentContents = renderContent .filter ((item : any ) => item .type === ' agent-content' )
201- const finalStatus = agentContents .findLast ((item : any ) => [' success' , ' failed' , ' fix' ].includes (item .status ))?.status
202-
203- return renderContent .map ((item : any ) => {
204- if (item .type !== ' agent-content' || isGenerating ) {
205- return item
206- }
207-
208- if (! item .status || item .status === ' loading' ) {
209- return {
210- ... item ,
211- status: finalStatus || message .metadata ?.agentStatus || ' failed'
212- }
213- }
214-
215- return item
216- })
217- }
218-
219195// 处理文件选择事件
220196const handleSingleFilesSelected = (files : File [] | null , retry = false ) => {
221197 if (! files ?.length ) return
@@ -271,36 +247,56 @@ const getSvgIcon = (name: string, style?: CSSProperties) => {
271247const aiAvatar = getSvgIcon (' AI' )
272248const welcomeIcon = getSvgIcon (' AI' , { fontSize: ' 44px' })
273249
274- const resolveMessageContent = (message : any ) => {
275- if (Array .isArray (message .renderContent ) && message .renderContent .length > 0 ) {
276- return resolveAgentRenderContent (message )
250+ const resolveMessageContent = (message : RobotMessage ) => {
251+ if (props .messageContentResolver ) {
252+ return props .messageContentResolver (message , {
253+ messages: messages .value ,
254+ status: props .status
255+ })
277256 }
278257
279- if (isAgentMessage (message ) && message .role === ' assistant' && message .content ) {
280- const agentStatus = [' success' , ' failed' , ' fix' ].includes (message .metadata ?.agentStatus )
281- ? message .metadata .agentStatus
282- : ' failed'
283- return [
284- {
285- type: ' agent-content' ,
286- status: agentStatus ,
287- content: message .content
258+ if (Array .isArray (message .renderContent ) && message .renderContent .length > 0 ) {
259+ return message .renderContent .map ((item ) => {
260+ if (item ?.type === RobotMessageContentType .Img || item ?.type === RobotMessageContentType .Image ) {
261+ return {
262+ type: RobotMessageContentType .Img ,
263+ content: item .content || item .url || item .image_url ?.url || ' '
264+ }
288265 }
289- ]
266+ if (item ?.type === RobotMessageContentType .Text ) {
267+ return {
268+ type: RobotMessageContentType .Text ,
269+ content: item .content ?? item .text ?? ' '
270+ }
271+ }
272+ return item
273+ })
274+ }
275+
276+ if (Array .isArray (message .content ) && message .content .length > 0 ) {
277+ const textContent = extractMessageText (message .content )
278+ if (textContent ) {
279+ return textContent
280+ }
281+ }
282+
283+ const textContent = extractMessageText (message .content )
284+ if (textContent ) {
285+ return textContent
290286 }
291287
292288 return message .content
293289}
294290
295291const roleConfigs: Record <string , BubbleRoleConfig > = {
296- assistant : {
292+ [ RobotMessageRole . Assistant ] : {
297293 placement: ' start' ,
298294 avatar: aiAvatar
299295 },
300- user : {
296+ [ RobotMessageRole . User ] : {
301297 placement: ' end'
302298 },
303- system : {
299+ [ RobotMessageRole . System ] : {
304300 hidden: true
305301 }
306302}
@@ -320,37 +316,38 @@ const handleSendMessage = async (content: string) => {
320316 return
321317 }
322318
323- const userMessage: ChatMessage = {
324- role: ' user ' ,
319+ const userMessage: RobotMessage = {
320+ role: RobotMessageRole . User ,
325321 content: messageContent
326322 }
327323 const files = selectedAttachments .value .filter ((item ) => item .status === ' success' )
328324 if (files .length > 0 ) {
329- const fileMessages: ChatMessage [] = files .map ((file ) => ({
330- role: ' user' ,
331- content: ' ' ,
332- renderContent: [
333- {
334- type: ' img' ,
335- content: file .url
336- }
337- ]
338- }))
339- messages .value .push (... fileMessages )
340- userMessage .content = files
341- .map ((item ) => ({
342- type: ' image_url' ,
325+ userMessage .content = [
326+ {
327+ type: RobotMessageContentType .Text ,
328+ text: messageContent
329+ },
330+ ... files .map ((item ) => ({
331+ type: RobotMessageContentType .ImageUrl ,
343332 image_url: {
344333 url: item .url
345334 }
346335 }))
347- .concat ({
348- type: ' text' ,
349- text: messageContent
350- })
336+ ] as RobotInputContentPart []
337+ userMessage .renderContent = [
338+ {
339+ type: RobotMessageContentType .Text ,
340+ content: messageContent
341+ },
342+ ... files .map ((item ) => ({
343+ type: RobotMessageContentType .Img ,
344+ content: item .url
345+ }))
346+ ]
347+ } else {
351348 userMessage .renderContent = [
352349 {
353- type: ' text ' ,
350+ type: RobotMessageContentType . Text ,
354351 content: messageContent
355352 }
356353 ]
0 commit comments