mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
MCP UI
This commit is contained in:
parent
f99589aaa6
commit
0a02342128
4 changed files with 94 additions and 64 deletions
|
|
@ -187,6 +187,7 @@ export type ThreadStreamState = {
|
|||
id: string;
|
||||
content: string;
|
||||
rawParams: RawToolParamsObj;
|
||||
mcpServerName: string | undefined;
|
||||
};
|
||||
interrupt: Promise<() => void>;
|
||||
} | {
|
||||
|
|
@ -448,7 +449,8 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// if running now but stream state doesn't indicate it (happens if restart Void), cancel that last tool
|
||||
if (lastMessage && lastMessage.role === 'tool' && lastMessage.type === 'running_now') {
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', content: lastMessage.content, id: lastMessage.id, rawParams: lastMessage.rawParams, result: null, name: lastMessage.name, params: lastMessage.params })
|
||||
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', content: lastMessage.content, id: lastMessage.id, rawParams: lastMessage.rawParams, result: null, name: lastMessage.name, params: lastMessage.params, mcpServerName: lastMessage.mcpServerName })
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -541,13 +543,17 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
else return
|
||||
|
||||
const { name, id, rawParams } = lastMsg
|
||||
const { name, id, rawParams, mcpServerName } = lastMsg
|
||||
|
||||
const errorMessage = this.toolErrMsgs.rejected
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null, id, rawParams })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null, id, rawParams, mcpServerName })
|
||||
this._setStreamState(threadId, undefined)
|
||||
}
|
||||
|
||||
private _computeMCPServerOfToolName = (toolName: string) => {
|
||||
return this._mcpService.getMCPTools()?.find(t => t.name === toolName)?.mcpServerName
|
||||
}
|
||||
|
||||
async abortRunning(threadId: string) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return // should never happen
|
||||
|
|
@ -556,13 +562,13 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (this.streamState[threadId]?.isRunning === 'LLM') {
|
||||
const { displayContentSoFar, reasoningSoFar, toolCallSoFar } = this.streamState[threadId].llmInfo
|
||||
this._addMessageToThread(threadId, { role: 'assistant', displayContent: displayContentSoFar, reasoning: reasoningSoFar, anthropicReasoning: null })
|
||||
if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name })
|
||||
if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name, mcpServerName: this._computeMCPServerOfToolName(toolCallSoFar.name) })
|
||||
}
|
||||
// add tool that's running
|
||||
else if (this.streamState[threadId]?.isRunning === 'tool') {
|
||||
const { toolName, toolParams, id, content: content_, rawParams } = this.streamState[threadId].toolInfo
|
||||
const { toolName, toolParams, id, content: content_, rawParams, mcpServerName } = this.streamState[threadId].toolInfo
|
||||
const content = content_ || this.toolErrMsgs.interrupted
|
||||
this._updateLatestTool(threadId, { role: 'tool', name: toolName, params: toolParams, id, content, rawParams, type: 'rejected', result: null })
|
||||
this._updateLatestTool(threadId, { role: 'tool', name: toolName, params: toolParams, id, content, rawParams, type: 'rejected', result: null, mcpServerName })
|
||||
}
|
||||
// reject the tool for the user if relevant
|
||||
else if (this.streamState[threadId]?.isRunning === 'awaiting_user') {
|
||||
|
|
@ -600,6 +606,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
threadId: string,
|
||||
toolName: ToolName,
|
||||
toolId: string,
|
||||
mcpServerName: string | undefined,
|
||||
opts: { preapproved: true, unvalidatedToolParams: RawToolParamsObj, validatedParams: ToolCallParams<ToolName> } | { preapproved: false, unvalidatedToolParams: RawToolParamsObj },
|
||||
): Promise<{ awaitingUserApproval?: boolean, interrupted?: boolean }> => {
|
||||
|
||||
|
|
@ -625,7 +632,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
catch (error) {
|
||||
const errorMessage = getErrorMessage(error)
|
||||
this._addMessageToThread(threadId, { role: 'tool', type: 'invalid_params', rawParams: opts.unvalidatedToolParams, result: null, name: toolName, content: errorMessage, id: toolId, })
|
||||
this._addMessageToThread(threadId, { role: 'tool', type: 'invalid_params', rawParams: opts.unvalidatedToolParams, result: null, name: toolName, content: errorMessage, id: toolId, mcpServerName })
|
||||
return {}
|
||||
}
|
||||
// once validated, add checkpoint for edit
|
||||
|
|
@ -638,7 +645,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (approvalType) {
|
||||
const autoApprove = this._settingsService.state.globalSettings.autoApprove[approvalType]
|
||||
// add a tool_request because we use it for UI if a tool is loading (this should be improved in the future)
|
||||
this._addMessageToThread(threadId, { role: 'tool', type: 'tool_request', content: '(Awaiting user permission...)', result: null, name: toolName, params: toolParams, id: toolId, rawParams: opts.unvalidatedToolParams })
|
||||
this._addMessageToThread(threadId, { role: 'tool', type: 'tool_request', content: '(Awaiting user permission...)', result: null, name: toolName, params: toolParams, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName })
|
||||
if (!autoApprove) {
|
||||
return { awaitingUserApproval: true }
|
||||
}
|
||||
|
|
@ -655,7 +662,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// 3. call the tool
|
||||
// this._setStreamState(threadId, { isRunning: 'tool' }, 'merge')
|
||||
const runningTool = { role: 'tool', type: 'running_now', name: toolName, params: toolParams, content: '(value not received yet...)', result: null, id: toolId, rawParams: opts.unvalidatedToolParams } as const
|
||||
const runningTool = { role: 'tool', type: 'running_now', name: toolName, params: toolParams, content: '(value not received yet...)', result: null, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName } as const
|
||||
this._updateLatestTool(threadId, runningTool)
|
||||
|
||||
|
||||
|
|
@ -665,7 +672,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
try {
|
||||
|
||||
// set stream state
|
||||
this._setStreamState(threadId, { isRunning: 'tool', interrupt: interruptorPromise, toolInfo: { toolName, toolParams, id: toolId, content: 'interrupted...', rawParams: opts.unvalidatedToolParams } })
|
||||
this._setStreamState(threadId, { isRunning: 'tool', interrupt: interruptorPromise, toolInfo: { toolName, toolParams, id: toolId, content: 'interrupted...', rawParams: opts.unvalidatedToolParams, mcpServerName } })
|
||||
|
||||
if (isBuiltInTool) {
|
||||
const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any)
|
||||
|
|
@ -695,7 +702,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
if (interrupted) { return { interrupted: true } } // the tool result is added where we interrupt, not here
|
||||
|
||||
const errorMessage = getErrorMessage(error)
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName })
|
||||
return {}
|
||||
}
|
||||
|
||||
|
|
@ -723,12 +730,12 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
}
|
||||
} catch (error) {
|
||||
const errorMessage = this.toolErrMsgs.errWhenStringifying(error)
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName })
|
||||
return {}
|
||||
}
|
||||
|
||||
// 5. add to history and keep going
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, id: toolId, rawParams: opts.unvalidatedToolParams })
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName })
|
||||
return {}
|
||||
};
|
||||
|
||||
|
|
@ -763,7 +770,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// before enter loop, call tool
|
||||
if (callThisToolFirst) {
|
||||
const { interrupted } = await this._runToolCall(threadId, callThisToolFirst.name, callThisToolFirst.id, { preapproved: true, unvalidatedToolParams: callThisToolFirst.rawParams, validatedParams: callThisToolFirst.params })
|
||||
const { interrupted } = await this._runToolCall(threadId, callThisToolFirst.name, callThisToolFirst.id, callThisToolFirst.mcpServerName, { preapproved: true, unvalidatedToolParams: callThisToolFirst.rawParams, validatedParams: callThisToolFirst.params })
|
||||
if (interrupted) {
|
||||
this._setStreamState(threadId, undefined)
|
||||
this._addUserCheckpoint({ threadId })
|
||||
|
|
@ -872,7 +879,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
const { error } = llmRes
|
||||
const { displayContentSoFar, reasoningSoFar, toolCallSoFar } = this.streamState[threadId].llmInfo
|
||||
this._addMessageToThread(threadId, { role: 'assistant', displayContent: displayContentSoFar, reasoning: reasoningSoFar, anthropicReasoning: null })
|
||||
if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name })
|
||||
if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name, mcpServerName: this._computeMCPServerOfToolName(toolCallSoFar.name) })
|
||||
|
||||
this._setStreamState(threadId, { isRunning: undefined, error })
|
||||
this._addUserCheckpoint({ threadId })
|
||||
|
|
@ -889,7 +896,10 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
|
||||
// call tool if there is one
|
||||
if (toolCall) {
|
||||
const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, toolCall.name, toolCall.id, { preapproved: false, unvalidatedToolParams: toolCall.rawParams })
|
||||
const mcpTools = this._mcpService.getMCPTools()
|
||||
const mcpTool = mcpTools?.find(t => t.name === toolCall.name)
|
||||
|
||||
const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, toolCall.name, toolCall.id, mcpTool?.mcpServerName, { preapproved: false, unvalidatedToolParams: toolCall.rawParams })
|
||||
if (interrupted) {
|
||||
this._setStreamState(threadId, undefined)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1414,14 +1414,36 @@ const titleOfBuiltinToolName = {
|
|||
} as const satisfies Record<BuiltinToolName, { done: any, proposed: any, running: any }>
|
||||
|
||||
|
||||
const getTitle = (toolMessage: Pick<ChatMessage & { role: 'tool' }, 'name' | 'type'>): React.ReactNode => {
|
||||
const getTitle = (toolMessage: Pick<ChatMessage & { role: 'tool' }, 'name' | 'type' | 'mcpServerName'>): React.ReactNode => {
|
||||
const t = toolMessage
|
||||
if (!builtinToolNames.includes(t.name as BuiltinToolName)) return t.name // good measure
|
||||
|
||||
const toolName = t.name as BuiltinToolName
|
||||
if (t.type === 'success') return titleOfBuiltinToolName[toolName].done
|
||||
if (t.type === 'running_now') return titleOfBuiltinToolName[toolName].running
|
||||
return titleOfBuiltinToolName[toolName].proposed
|
||||
// non-built-in title
|
||||
if (!builtinToolNames.includes(t.name as BuiltinToolName)) {
|
||||
|
||||
// descriptor of Running or Ran etc
|
||||
const descriptor =
|
||||
t.type === 'success' ? 'Ran'
|
||||
: t.type === 'running_now' ? 'Running'
|
||||
: t.type === 'tool_request' ? 'Requested'
|
||||
: t.type === 'rejected' ? 'Canceled'
|
||||
: t.type === 'invalid_params' ? 'Canceled'
|
||||
: t.type === 'tool_error' ? 'Canceled'
|
||||
: 'Ran'
|
||||
|
||||
const title = `${descriptor} ${t.name}`
|
||||
|
||||
if (t.type === 'running_now' || t.type === 'tool_request')
|
||||
return loadingTitleWrapper(title)
|
||||
return title
|
||||
}
|
||||
|
||||
// built-in title
|
||||
else {
|
||||
const toolName = t.name as BuiltinToolName
|
||||
if (t.type === 'success') return titleOfBuiltinToolName[toolName].done
|
||||
if (t.type === 'running_now') return titleOfBuiltinToolName[toolName].running
|
||||
return titleOfBuiltinToolName[toolName].proposed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1700,9 +1722,9 @@ const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr, toolName, threadId }:
|
|||
|
||||
|
||||
|
||||
const InvalidTool = ({ toolName, message }: { toolName: ToolName, message: string }) => {
|
||||
const InvalidTool = ({ toolName, message, mcpServerName }: { toolName: ToolName, message: string, mcpServerName: string | undefined }) => {
|
||||
const accessor = useAccessor()
|
||||
const title = getTitle({ name: toolName, type: 'invalid_params' })
|
||||
const title = getTitle({ name: toolName, type: 'invalid_params', mcpServerName })
|
||||
const desc1 = 'Invalid parameters'
|
||||
const icon = null
|
||||
const isError = true
|
||||
|
|
@ -1716,9 +1738,9 @@ const InvalidTool = ({ toolName, message }: { toolName: ToolName, message: strin
|
|||
return <ToolHeaderWrapper {...componentParams} />
|
||||
}
|
||||
|
||||
const CanceledTool = ({ toolName }: { toolName: ToolName }) => {
|
||||
const CanceledTool = ({ toolName, mcpServerName }: { toolName: ToolName, mcpServerName: string | undefined }) => {
|
||||
const accessor = useAccessor()
|
||||
const title = getTitle({ name: toolName, type: 'rejected' })
|
||||
const title = getTitle({ name: toolName, type: 'rejected', mcpServerName })
|
||||
const desc1 = ''
|
||||
const icon = null
|
||||
const isRejected = true
|
||||
|
|
@ -1839,7 +1861,7 @@ const MCPToolWrapper = ({ toolMessage }: WrapperProps<string>) => {
|
|||
const desc1 = toolMessage.name
|
||||
const icon = null
|
||||
|
||||
if (toolMessage.type === 'tool_request') return null // do not show past requests
|
||||
|
||||
if (toolMessage.type === 'running_now') return null // do not show running
|
||||
|
||||
const isError = false
|
||||
|
|
@ -1847,16 +1869,17 @@ const MCPToolWrapper = ({ toolMessage }: WrapperProps<string>) => {
|
|||
const { rawParams, params } = toolMessage
|
||||
const componentParams: ToolHeaderParams = { title, desc1, isError, icon, isRejected, }
|
||||
|
||||
if (toolMessage.type === 'success') {
|
||||
componentParams.info = `${toolMessage.mcpServerName} MCP server`
|
||||
|
||||
if (toolMessage.type === 'success' || toolMessage.type === 'tool_request') {
|
||||
const { result } = toolMessage
|
||||
componentParams.children = <ToolChildrenWrapper>
|
||||
<SmallProseWrapper>
|
||||
<ChatMarkdownRender
|
||||
string={`
|
||||
## Parameters
|
||||
\`\`\`\n${JSON.stringify(params, null, 2)}\n\`\`\`
|
||||
## Result
|
||||
\`\`\`\n${JSON.stringify(result, null, 2)}\n\`\`\`
|
||||
## (Parameters:)
|
||||
\`\`\`\n${JSON.stringify(params, null, 2)}\n\`\`\`
|
||||
`}
|
||||
chatMessageLocation={undefined}
|
||||
isApplyEnabled={false}
|
||||
|
|
@ -2501,7 +2524,7 @@ const _ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, me
|
|||
|
||||
if (chatMessage.type === 'invalid_params') {
|
||||
return <div className={`${isCheckpointGhost ? 'opacity-50' : ''}`}>
|
||||
<InvalidTool toolName={chatMessage.name} message={chatMessage.content} />
|
||||
<InvalidTool toolName={chatMessage.name} message={chatMessage.content} mcpServerName={chatMessage.mcpServerName} />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
@ -2529,7 +2552,7 @@ const _ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, me
|
|||
|
||||
else if (role === 'interrupted_streaming_tool') {
|
||||
return <div className={`${isCheckpointGhost ? 'opacity-50' : ''}`}>
|
||||
<CanceledTool toolName={chatMessage.name} />
|
||||
<CanceledTool toolName={chatMessage.name} mcpServerName={chatMessage.mcpServerName} />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
@ -2809,7 +2832,7 @@ const CommandBarInChat = () => {
|
|||
|
||||
const EditToolSoFar = ({ toolCallSoFar, }: { toolCallSoFar: RawToolCallObj }) => {
|
||||
|
||||
if (!isABuiltinToolName( toolCallSoFar.name)) return null
|
||||
if (!isABuiltinToolName(toolCallSoFar.name)) return null
|
||||
|
||||
const accessor = useAccessor()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export type ToolMessage<T extends ToolName> = {
|
|||
content: string; // give this result to LLM (string of value)
|
||||
id: string;
|
||||
rawParams: RawToolParamsObj;
|
||||
mcpServerName: string | undefined; // the server name at the time of the call
|
||||
} & (
|
||||
// in order of events:
|
||||
| { type: 'invalid_params', result: null, name: T, }
|
||||
|
|
@ -29,6 +30,7 @@ export type ToolMessage<T extends ToolName> = {
|
|||
export type DecorativeCanceledTool = {
|
||||
role: 'interrupted_streaming_tool';
|
||||
name: ToolName;
|
||||
mcpServerName: string | undefined; // the server name at the time of the call
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { AnthropicLLMChatMessage, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMe
|
|||
import { ChatMode, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js';
|
||||
import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings, getReservedOutputTokenSpace } from '../../common/modelCapabilities.js';
|
||||
import { extractReasoningWrapper, extractXMLToolsWrapper } from './extractGrammar.js';
|
||||
import { availableTools, InternalToolInfo, builtinTools, isABuiltinToolName } from '../../common/prompt/prompts.js';
|
||||
import { availableTools, InternalToolInfo } from '../../common/prompt/prompts.js';
|
||||
import { generateUuid } from '../../../../../base/common/uuid.js';
|
||||
import { BuiltinToolParamName } from '../../common/toolsServiceTypes.js';
|
||||
|
||||
|
|
@ -221,21 +221,26 @@ const openAITools = (chatMode: ChatMode | null, mcpTools: InternalToolInfo[] | u
|
|||
|
||||
|
||||
// convert LLM tool call to our tool format
|
||||
const rawToolCallObjOf = (name: string, toolParamsStr: string, id: string): RawToolCallObj | null => {
|
||||
if (!isABuiltinToolName(name)) return null
|
||||
const rawParams: RawToolParamsObj = {}
|
||||
const rawToolCallObjOfParamsStr = (name: string, toolParamsStr: string, id: string): RawToolCallObj | null => {
|
||||
let input: unknown
|
||||
try {
|
||||
input = JSON.parse(toolParamsStr)
|
||||
}
|
||||
catch (e) {
|
||||
return null
|
||||
}
|
||||
try { input = JSON.parse(toolParamsStr) }
|
||||
catch (e) { return null }
|
||||
|
||||
if (input === null) return null
|
||||
if (typeof input !== 'object') return null
|
||||
for (const paramName in builtinTools[name].params) {
|
||||
rawParams[paramName as BuiltinToolParamName] = (input as any)[paramName]
|
||||
}
|
||||
|
||||
const rawParams: RawToolParamsObj = input
|
||||
return { id, name, rawParams, doneParams: Object.keys(rawParams) as BuiltinToolParamName[], isDone: true }
|
||||
}
|
||||
|
||||
|
||||
const rawToolCallObjOfAnthropicParams = (toolBlock: Anthropic.Messages.ToolUseBlock): RawToolCallObj | null => {
|
||||
const { id, name, input } = toolBlock
|
||||
|
||||
if (input === null) return null
|
||||
if (typeof input !== 'object') return null
|
||||
|
||||
const rawParams: RawToolParamsObj = input
|
||||
return { id, name, rawParams, doneParams: Object.keys(rawParams) as BuiltinToolParamName[], isDone: true }
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +344,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE
|
|||
onText({
|
||||
fullText: fullTextSoFar,
|
||||
fullReasoning: fullReasoningSoFar,
|
||||
toolCall: isABuiltinToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined,
|
||||
toolCall: { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId },
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -348,7 +353,7 @@ const _sendOpenAICompatibleChat = async ({ messages, onText, onFinalMessage, onE
|
|||
onError({ message: 'Void: Response from model was empty.', fullError: null })
|
||||
}
|
||||
else {
|
||||
const toolCall = rawToolCallObjOf(toolName, toolParamsStr, toolId)
|
||||
const toolCall = rawToolCallObjOfParamsStr(toolName, toolParamsStr, toolId)
|
||||
const toolCallObj = toolCall ? { toolCall } : {}
|
||||
onFinalMessage({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, anthropicReasoning: null, ...toolCallObj });
|
||||
}
|
||||
|
|
@ -425,17 +430,7 @@ const anthropicTools = (chatMode: ChatMode | null, mcpTools: InternalToolInfo[]
|
|||
return anthropicTools
|
||||
}
|
||||
|
||||
const anthropicToolToRawToolCallObj = (toolBlock: Anthropic.Messages.ToolUseBlock): RawToolCallObj | null => {
|
||||
const { id, name, input } = toolBlock
|
||||
if (!isABuiltinToolName(name)) return null
|
||||
const rawParams: RawToolParamsObj = {}
|
||||
if (input === null) return null
|
||||
if (typeof input !== 'object') return null
|
||||
for (const paramName in builtinTools[name].params) {
|
||||
rawParams[paramName as BuiltinToolParamName] = (input as any)[paramName]
|
||||
}
|
||||
return { id, name, rawParams, doneParams: Object.keys(rawParams) as BuiltinToolParamName[], isDone: true }
|
||||
}
|
||||
|
||||
|
||||
// ------------ ANTHROPIC ------------
|
||||
const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessage, onError, settingsOfProvider, modelSelectionOptions, overridesOfModel, modelName: modelName_, _setAborter, separateSystemMessage, chatMode, mcpTools }: SendChatParams_Internal) => {
|
||||
|
|
@ -496,7 +491,7 @@ const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessag
|
|||
onText({
|
||||
fullText,
|
||||
fullReasoning,
|
||||
toolCall: isABuiltinToolName(fullToolName) ? { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined,
|
||||
toolCall: { name: fullToolName, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' },
|
||||
})
|
||||
}
|
||||
// there are no events for tool_use, it comes in at the end
|
||||
|
|
@ -546,7 +541,7 @@ const sendAnthropicChat = async ({ messages, providerName, onText, onFinalMessag
|
|||
stream.on('finalMessage', (response) => {
|
||||
const anthropicReasoning = response.content.filter(c => c.type === 'thinking' || c.type === 'redacted_thinking')
|
||||
const tools = response.content.filter(c => c.type === 'tool_use')
|
||||
const toolCall = tools[0] && anthropicToolToRawToolCallObj(tools[0])
|
||||
const toolCall = tools[0] && rawToolCallObjOfAnthropicParams(tools[0])
|
||||
const toolCallObj = toolCall ? { toolCall } : {}
|
||||
onFinalMessage({ fullText, fullReasoning, anthropicReasoning, ...toolCallObj })
|
||||
})
|
||||
|
|
@ -791,7 +786,7 @@ const sendGeminiChat = async ({
|
|||
onText({
|
||||
fullText: fullTextSoFar,
|
||||
fullReasoning: fullReasoningSoFar,
|
||||
toolCall: isABuiltinToolName(toolName) ? { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined,
|
||||
toolCall: { name: toolName, rawParams: {}, isDone: false, doneParams: [], id: toolId },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -800,7 +795,7 @@ const sendGeminiChat = async ({
|
|||
onError({ message: 'Void: Response from model was empty.', fullError: null })
|
||||
} else {
|
||||
if (!toolId) toolId = generateUuid() // ids are empty, but other providers might expect an id
|
||||
const toolCall = rawToolCallObjOf(toolName, toolParamsStr, toolId)
|
||||
const toolCall = rawToolCallObjOfParamsStr(toolName, toolParamsStr, toolId)
|
||||
const toolCallObj = toolCall ? { toolCall } : {}
|
||||
onFinalMessage({ fullText: fullTextSoFar, fullReasoning: fullReasoningSoFar, anthropicReasoning: null, ...toolCallObj });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue