feat(AnthropicProvider, MessagesService): Add empty message filtering and stream processing improvements

- Introduce filterEmptyMessages function to remove empty messages
- Update AnthropicProvider to use filterEmptyMessages in message preparation
- Refactor stream processing with minor improvements and return statement fixes
- Simplify tool response and message handling logic
This commit is contained in:
kangfenmao 2025-03-11 13:00:04 +08:00
parent e61618f1b4
commit 632b0c17aa
2 changed files with 35 additions and 26 deletions

View File

@ -11,7 +11,11 @@ import { getStoreSetting } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n' import i18n from '@renderer/i18n'
import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@renderer/services/AssistantService' import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@renderer/services/AssistantService'
import { EVENT_NAMES } from '@renderer/services/EventService' import { EVENT_NAMES } from '@renderer/services/EventService'
import { filterContextMessages, filterUserRoleStartMessages } from '@renderer/services/MessagesService' import {
filterContextMessages,
filterEmptyMessages,
filterUserRoleStartMessages
} from '@renderer/services/MessagesService'
import { Assistant, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types' import { Assistant, FileTypes, MCPToolResponse, Message, Model, Provider, Suggestion } from '@renderer/types'
import { removeSpecialCharactersForTopicName } from '@renderer/utils' import { removeSpecialCharactersForTopicName } from '@renderer/utils'
import { first, flatten, sum, takeRight } from 'lodash' import { first, flatten, sum, takeRight } from 'lodash'
@ -136,7 +140,10 @@ export default class AnthropicProvider extends BaseProvider {
const { contextCount, maxTokens, streamOutput } = getAssistantSettings(assistant) const { contextCount, maxTokens, streamOutput } = getAssistantSettings(assistant)
const userMessagesParams: MessageParam[] = [] const userMessagesParams: MessageParam[] = []
const _messages = filterUserRoleStartMessages(filterContextMessages(takeRight(messages, contextCount + 2)))
const _messages = filterUserRoleStartMessages(
filterContextMessages(filterEmptyMessages(takeRight(messages, contextCount + 2)))
)
onFilterMessages(_messages) onFilterMessages(_messages)
@ -200,8 +207,9 @@ export default class AnthropicProvider extends BaseProvider {
const { abortController, cleanup } = this.createAbortController(lastUserMessage?.id) const { abortController, cleanup } = this.createAbortController(lastUserMessage?.id)
const { signal } = abortController const { signal } = abortController
const toolResponses: MCPToolResponse[] = [] const toolResponses: MCPToolResponse[] = []
const processStream = async (body: MessageCreateParamsNonStreaming) => { const processStream = async (body: MessageCreateParamsNonStreaming) => {
new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const toolCalls: ToolUseBlock[] = [] const toolCalls: ToolUseBlock[] = []
let hasThinkingContent = false let hasThinkingContent = false
const stream = this.sdk.messages const stream = this.sdk.messages
@ -211,6 +219,7 @@ export default class AnthropicProvider extends BaseProvider {
stream.controller.abort() stream.controller.abort()
return resolve() return resolve()
} }
if (time_first_token_millsec == 0) { if (time_first_token_millsec == 0) {
time_first_token_millsec = new Date().getTime() - start_time_millsec time_first_token_millsec = new Date().getTime() - start_time_millsec
} }
@ -224,6 +233,7 @@ export default class AnthropicProvider extends BaseProvider {
: 0 : 0
const time_completion_millsec = new Date().getTime() - start_time_millsec const time_completion_millsec = new Date().getTime() - start_time_millsec
onChunk({ onChunk({
text, text,
metrics: { metrics: {
@ -236,11 +246,13 @@ export default class AnthropicProvider extends BaseProvider {
}) })
.on('thinking', (thinking) => { .on('thinking', (thinking) => {
hasThinkingContent = true hasThinkingContent = true
if (time_first_token_millsec == 0) { if (time_first_token_millsec == 0) {
time_first_token_millsec = new Date().getTime() - start_time_millsec time_first_token_millsec = new Date().getTime() - start_time_millsec
} }
const time_completion_millsec = new Date().getTime() - start_time_millsec const time_completion_millsec = new Date().getTime() - start_time_millsec
onChunk({ onChunk({
reasoning_content: thinking, reasoning_content: thinking,
text: '', text: '',
@ -262,30 +274,10 @@ export default class AnthropicProvider extends BaseProvider {
for (const toolCall of toolCalls) { for (const toolCall of toolCalls) {
const mcpTool = anthropicToolUseToMcpTool(mcpTools, toolCall) const mcpTool = anthropicToolUseToMcpTool(mcpTools, toolCall)
if (mcpTool) { if (mcpTool) {
upsertMCPToolResponse( upsertMCPToolResponse(toolResponses, { tool: mcpTool, status: 'invoking' }, onChunk)
toolResponses,
{
tool: mcpTool,
status: 'invoking'
},
onChunk
)
const resp = await callMCPTool(mcpTool) const resp = await callMCPTool(mcpTool)
toolCallResults.push({ toolCallResults.push({ type: 'tool_result', tool_use_id: toolCall.id, content: resp.content })
type: 'tool_result', upsertMCPToolResponse(toolResponses, { tool: mcpTool, status: 'done', response: resp }, onChunk)
tool_use_id: toolCall.id,
content: resp.content
})
upsertMCPToolResponse(
toolResponses,
{
tool: mcpTool,
status: 'done',
response: resp
},
onChunk
)
} }
} }
@ -294,6 +286,7 @@ export default class AnthropicProvider extends BaseProvider {
role: message.role, role: message.role,
content: message.content content: message.content
}) })
userMessages.push({ userMessages.push({
role: 'user', role: 'user',
content: toolCallResults content: toolCallResults
@ -301,6 +294,7 @@ export default class AnthropicProvider extends BaseProvider {
const newBody = body const newBody = body
body.messages = userMessages body.messages = userMessages
await processStream(newBody) await processStream(newBody)
} }
} }
@ -309,6 +303,7 @@ export default class AnthropicProvider extends BaseProvider {
const time_thinking_millsec = time_first_content_millsec const time_thinking_millsec = time_first_content_millsec
? time_first_content_millsec - start_time_millsec ? time_first_content_millsec - start_time_millsec
: 0 : 0
onChunk({ onChunk({
text: '', text: '',
usage: { usage: {
@ -324,6 +319,7 @@ export default class AnthropicProvider extends BaseProvider {
}, },
mcpToolResponse: toolResponses mcpToolResponse: toolResponses
}) })
resolve() resolve()
}) })
.on('error', (error) => reject(error)) .on('error', (error) => reject(error))

View File

@ -39,6 +39,19 @@ export function filterUserRoleStartMessages(messages: Message[]): Message[] {
return messages.slice(firstUserMessageIndex) return messages.slice(firstUserMessageIndex)
} }
export function filterEmptyMessages(messages: Message[]): Message[] {
return messages.filter((message) => {
const content = message.content as string | any[]
if (typeof content === 'string') {
return !isEmpty(content.trim())
}
if (Array.isArray(content)) {
return content.some((c) => !isEmpty(c.text.trim()))
}
return true
})
}
export function filterUsefulMessages(messages: Message[]): Message[] { export function filterUsefulMessages(messages: Message[]): Message[] {
const _messages = messages const _messages = messages
const groupedMessages = getGroupedMessages(messages) const groupedMessages = getGroupedMessages(messages)