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:
parent
e61618f1b4
commit
632b0c17aa
@ -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))
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user