Skip to content

Commit 01104b9

Browse files
committed
fix:调整RobotChat逻辑和修复图片上传问题
1 parent 7223055 commit 01104b9

4 files changed

Lines changed: 162 additions & 73 deletions

File tree

packages/plugins/robot/src/Main.vue

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
v-model:show="robotVisible"
2020
v-model:input="inputMessage"
2121
:status="mappedStatus"
22-
:chat-mode="robotSettingState.chatMode"
2322
:prompt-items="promptItems"
2423
:bubble-renderers="bubbleRenderers"
2524
:allowFiles="isVisualModel && robotSettingState.chatMode === ChatMode.Agent"
2625
:show-aborted="robotSettingState.chatMode !== ChatMode.Agent"
26+
:message-content-resolver="resolveChatMessageContent"
2727
:beforeSubmit="checkApiKey"
2828
:promptClickHandler="promptClickHandler"
2929
@fileSelected="handleFileSelected"
@@ -106,6 +106,7 @@ const { robotSettingState, getModelCapabilities, updateThinkingState, getSelecte
106106
107107
const robotVisible = ref(false)
108108
const fullscreen = ref(false)
109+
const inputMessage = ref('')
109110
110111
watch(robotVisible, (visible) => {
111112
useLayout().layoutState.toolbars.render = visible ? META_APP.Robot : ''
@@ -150,7 +151,6 @@ const showSetting = ref(false)
150151
151152
const {
152153
mappedStatus,
153-
inputMessage,
154154
messages,
155155
changeChatMode,
156156
abortRequest,
@@ -248,6 +248,55 @@ const openAIRobot = () => {
248248
// 当前Robot的bubbleRenderers无法做到响应式更新,因此Agent模式的type要与Chat模式不同
249249
const bubbleRenderers = { 'agent-content': AgentRenderer, 'agent-loading': AgentRenderer }
250250
251+
const resolveChatMessageContent = (message: any, context: { messages: any[]; status: string }) => {
252+
const hasAgentContent = message.renderContent?.some((item: any) => {
253+
return item.type === 'agent-content' || item.type === 'agent-loading'
254+
})
255+
const isAgentMessage = message.metadata?.chatMode === 'agent' || hasAgentContent
256+
257+
if (!isAgentMessage || message.role !== 'assistant') {
258+
return Array.isArray(message.renderContent) && message.renderContent.length > 0
259+
? message.renderContent
260+
: message.content
261+
}
262+
263+
const isLastMessage = context.messages.at(-1) === message
264+
const isGenerating = Boolean(message.loading) || (isLastMessage && context.status !== 'finished')
265+
const renderContent = isGenerating
266+
? message.renderContent || []
267+
: (message.renderContent || []).filter((item: any) => item.type !== 'agent-loading')
268+
const agentContents = renderContent.filter((item: any) => item.type === 'agent-content')
269+
const finalStatus = agentContents.findLast((item: any) => ['success', 'failed', 'fix'].includes(item.status))?.status
270+
271+
if (!Array.isArray(message.renderContent) || message.renderContent.length === 0) {
272+
const agentStatus = ['success', 'failed', 'fix'].includes(message.metadata?.agentStatus)
273+
? message.metadata.agentStatus
274+
: 'failed'
275+
return [
276+
{
277+
type: 'agent-content',
278+
status: agentStatus,
279+
content: message.content
280+
}
281+
]
282+
}
283+
284+
return renderContent.map((item: any) => {
285+
if (item.type !== 'agent-content' || isGenerating) {
286+
return item
287+
}
288+
289+
if (!item.status || item.status === 'loading') {
290+
return {
291+
...item,
292+
status: finalStatus || message.metadata?.agentStatus || 'failed'
293+
}
294+
}
295+
296+
return item
297+
})
298+
}
299+
251300
const handleFileSelected = async (formData: FormData, updateAttachment: (resourceUrl: string) => void) => {
252301
try {
253302
const appId = getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id

packages/plugins/robot/src/components/chat/RobotChat.vue

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ const props = defineProps({
113113
type: Function
114114
},
115115
status: { type: String },
116-
chatMode: { type: String },
116+
messageContentResolver: {
117+
type: Function
118+
},
117119
allowFiles: {
118120
type: Boolean,
119121
default: false
@@ -169,51 +171,35 @@ const contentRendererMatches = computed<BubbleContentRendererMatch[]>(() => [
169171
},
170172
{
171173
priority: BubbleRendererMatchPriority.NORMAL,
172-
find: (message: any, content: any) =>
173-
!message.loading && message.content && (!content?.type || ['markdown', 'text'].includes(content.type)),
174+
find: (_message: any, content: any) => !content?.type || ['markdown', 'text'].includes(content.type),
174175
renderer: MarkdownRenderer
175176
},
176177
{
177178
priority: BubbleRendererMatchPriority.NORMAL,
178-
find: (message: any) => message?.content?.[0]?.type === 'img' || message?.content?.[0]?.type === 'image',
179+
find: (_message: any, content: any) => ['img', 'image'].includes(content?.type),
179180
renderer: ImgRenderer
180181
}
181182
])
182183
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
184+
const getTextContent = (content: any) => {
185+
if (typeof content === 'string') {
186+
return content
193187
}
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-
})
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 ''
217203
}
218204
219205
// 处理文件选择事件
@@ -272,21 +258,43 @@ const aiAvatar = getSvgIcon('AI')
272258
const welcomeIcon = getSvgIcon('AI', { fontSize: '44px' })
273259
274260
const resolveMessageContent = (message: any) => {
275-
if (Array.isArray(message.renderContent) && message.renderContent.length > 0) {
276-
return resolveAgentRenderContent(message)
261+
if (props.messageContentResolver) {
262+
return props.messageContentResolver(message, {
263+
messages: messages.value,
264+
status: props.status
265+
})
277266
}
278267
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
268+
if (Array.isArray(message.renderContent) && message.renderContent.length > 0) {
269+
return message.renderContent.map((item: any) => {
270+
if (item?.type === 'img' || item?.type === 'image') {
271+
return {
272+
type: 'img',
273+
content: item.content || item.url || item.image_url?.url || ''
274+
}
288275
}
289-
]
276+
if (item?.type === 'text') {
277+
return {
278+
type: 'text',
279+
content: item.content ?? item.text ?? ''
280+
}
281+
}
282+
return item
283+
})
284+
}
285+
286+
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+
)
290+
if (textContent) {
291+
return textContent
292+
}
293+
}
294+
295+
const textContent = getTextContent(message.content)
296+
if (textContent) {
297+
return textContent
290298
}
291299
292300
return message.content
@@ -326,28 +334,29 @@ const handleSendMessage = async (content: string) => {
326334
}
327335
const files = selectedAttachments.value.filter((item) => item.status === 'success')
328336
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) => ({
337+
userMessage.content = [
338+
{
339+
type: 'text',
340+
text: messageContent
341+
},
342+
...files.map((item) => ({
342343
type: 'image_url',
343344
image_url: {
344345
url: item.url
345346
}
346347
}))
347-
.concat({
348+
] as any
349+
userMessage.renderContent = [
350+
{
348351
type: 'text',
349-
text: messageContent
350-
})
352+
content: messageContent
353+
},
354+
...files.map((item) => ({
355+
type: 'img',
356+
content: item.url
357+
}))
358+
]
359+
} else {
351360
userMessage.renderContent = [
352361
{
353362
type: 'text',

packages/plugins/robot/src/components/renderers/MarkdownRenderer.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ hljs.registerLanguage('xml', xml)
2929
hljs.registerLanguage('shell', shell)
3030
3131
interface MarkdownMessage {
32-
content: string
32+
content: string | string[] | Record<string, unknown>[]
3333
}
3434
3535
const props = defineProps({
@@ -71,7 +71,15 @@ const markdownIt = new MarkdownIt({
7171
})
7272
7373
const renderContent = computed(() => {
74-
return DOMPurify.sanitize(markdownIt.render(props.message.content))
74+
const content = Array.isArray(props.message.content)
75+
? props.message.content
76+
.map((item: any) => item?.text ?? item?.content ?? '')
77+
.filter(Boolean)
78+
.join('\n')
79+
: typeof props.message.content === 'string'
80+
? props.message.content
81+
: ''
82+
return DOMPurify.sanitize(markdownIt.render(content))
7583
})
7684
</script>
7785

packages/plugins/robot/src/composables/core/useConversation.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ export interface ConversationMetadata {
3333

3434
let currentConversationMetadata: ConversationMetadata = {}
3535

36+
const extractMessageText = (content: unknown): string => {
37+
if (typeof content === 'string') {
38+
return content
39+
}
40+
41+
if (Array.isArray(content)) {
42+
return content
43+
.map((item: any) => {
44+
if (typeof item === 'string') {
45+
return item
46+
}
47+
if (item?.type === 'text') {
48+
return item.text ?? item.content ?? ''
49+
}
50+
return ''
51+
})
52+
.filter(Boolean)
53+
.join('\n')
54+
}
55+
56+
return ''
57+
}
58+
3659
const createResponseProvider = (
3760
provider: Pick<OpenAICompatibleProvider, 'chatStream'>
3861
): UseMessageOptions['responseProvider'] => {
@@ -180,7 +203,7 @@ export function useConversationAdapter(options: ConversationAdapterOptions) {
180203

181204
const saveConversations = () => {
182205
conversations.value.forEach((conversation) => {
183-
void saveConversation(conversation)
206+
saveConversation(conversation)
184207
})
185208
}
186209

@@ -198,7 +221,7 @@ export function useConversationAdapter(options: ConversationAdapterOptions) {
198221
currentConversationMetadata = conversation.metadata
199222
}
200223
conversation.updatedAt = Date.now()
201-
void saveConversation(conversation)
224+
saveConversation(conversation)
202225
}
203226

204227
const updateTitle = (conversationId: string, title?: string) => {
@@ -258,7 +281,7 @@ export function useConversationAdapter(options: ConversationAdapterOptions) {
258281
}
259282
}
260283
currentConversationMetadata = conversation.metadata || {}
261-
void saveConversation(conversation)
284+
saveConversation(conversation)
262285
return currentId
263286
}
264287
}
@@ -328,7 +351,7 @@ export function useConversationAdapter(options: ConversationAdapterOptions) {
328351
const currentTitle = currentConversation.title
329352
if (currentTitle === defaultTitle && currentId) {
330353
const messageContent = getActiveEngine()?.messages.value.find((item) => item.role === 'user')?.content
331-
const contentStr = typeof messageContent === 'string' ? messageContent : JSON.stringify(messageContent)
354+
const contentStr = extractMessageText(messageContent) || JSON.stringify(messageContent)
332355
updateTitle(currentId, contentStr.substring(0, 20))
333356
}
334357
}

0 commit comments

Comments
 (0)