@librechat/agents 3.0.31 → 3.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -67,16 +67,13 @@ async function handleToolCallChunks({ graph, stepKey, toolCallChunks, metadata,
|
|
|
67
67
|
/**
|
|
68
68
|
* Create tool_calls step as soon as we receive the first tool call chunk
|
|
69
69
|
* This ensures deltas are always associated with the correct step
|
|
70
|
+
*
|
|
71
|
+
* NOTE: We do NOT dispatch an empty text block here because:
|
|
72
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
73
|
+
* - The tool_calls themselves are sufficient for the step
|
|
74
|
+
* - Empty content with tool_call_ids gets stored in conversation history
|
|
75
|
+
* and causes "messages must have non-empty content" errors on replay
|
|
70
76
|
*/
|
|
71
|
-
await graph.dispatchMessageDelta(prevStepId, {
|
|
72
|
-
content: [
|
|
73
|
-
{
|
|
74
|
-
type: _enum.ContentTypes.TEXT,
|
|
75
|
-
text: '',
|
|
76
|
-
tool_call_ids: tool_calls?.map((tc) => tc.id ?? '') ?? [],
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
77
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
81
78
|
stepId = await graph.dispatchRunStep(stepKey, {
|
|
82
79
|
type: _enum.StepTypes.TOOL_CALLS,
|
|
@@ -115,22 +112,18 @@ const handleToolCalls = async (toolCalls, metadata, graph) => {
|
|
|
115
112
|
catch {
|
|
116
113
|
// no previous step
|
|
117
114
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
],
|
|
127
|
-
});
|
|
128
|
-
};
|
|
115
|
+
/**
|
|
116
|
+
* NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:
|
|
117
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
118
|
+
* - They get stored in conversation history and cause errors on replay:
|
|
119
|
+
* "messages must have non-empty content" (Anthropic)
|
|
120
|
+
* "The content field in the Message object is empty" (Bedrock)
|
|
121
|
+
* - The tool_calls themselves are sufficient
|
|
122
|
+
*/
|
|
129
123
|
/* If the previous step exists and is a message creation */
|
|
130
124
|
if (prevStepId &&
|
|
131
125
|
prevRunStep &&
|
|
132
126
|
prevRunStep.type === _enum.StepTypes.MESSAGE_CREATION) {
|
|
133
|
-
await dispatchToolCallIds(prevStepId);
|
|
134
127
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
135
128
|
/* If the previous step doesn't exist or is not a message creation */
|
|
136
129
|
}
|
|
@@ -143,8 +136,7 @@ const handleToolCalls = async (toolCalls, metadata, graph) => {
|
|
|
143
136
|
message_id: messageId,
|
|
144
137
|
},
|
|
145
138
|
}, metadata);
|
|
146
|
-
|
|
147
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
139
|
+
graph.messageStepHasToolCalls.set(stepId, true);
|
|
148
140
|
}
|
|
149
141
|
await graph.dispatchRunStep(stepKey, {
|
|
150
142
|
type: _enum.StepTypes.TOOL_CALLS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.cjs","sources":["../../../src/tools/handlers.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/tools/handlers.ts\nimport { nanoid } from 'nanoid';\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { AnthropicWebSearchResultBlockParam } from '@/llm/anthropic/types';\nimport type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { AgentContext } from '@/agents/AgentContext';\nimport type * as t from '@/types';\nimport {\n ToolCallTypes,\n ContentTypes,\n GraphEvents,\n StepTypes,\n Providers,\n Constants,\n} from '@/common';\nimport {\n coerceAnthropicSearchResults,\n isAnthropicWebSearchResult,\n} from '@/tools/search/anthropic';\nimport { formatResultsForLLM } from '@/tools/search/format';\nimport { getMessageId } from '@/messages';\n\nexport async function handleToolCallChunks({\n graph,\n stepKey,\n toolCallChunks,\n metadata,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n stepKey: string;\n toolCallChunks: ToolCallChunk[];\n metadata?: Record<string, unknown>;\n}): Promise<void> {\n let prevStepId: string;\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n /** Edge Case: If no previous step exists, create a new message creation step */\n const message_id = getMessageId(stepKey, graph, true) ?? '';\n prevStepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id,\n },\n },\n metadata\n );\n prevRunStep = graph.getRunStep(prevStepId);\n }\n\n const _stepId = graph.getStepIdByKey(stepKey);\n\n /** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */\n const tool_calls: ToolCall[] | undefined =\n prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION\n ? []\n : undefined;\n\n /** Edge Case: `id` and `name` fields cannot be empty strings */\n for (const toolCallChunk of toolCallChunks) {\n if (toolCallChunk.name === '') {\n toolCallChunk.name = undefined;\n }\n if (toolCallChunk.id === '') {\n toolCallChunk.id = undefined;\n } else if (\n tool_calls != null &&\n toolCallChunk.id != null &&\n toolCallChunk.name != null\n ) {\n tool_calls.push({\n args: {},\n id: toolCallChunk.id,\n name: toolCallChunk.name,\n type: ToolCallTypes.TOOL_CALL,\n });\n }\n }\n\n let stepId: string = _stepId;\n const alreadyDispatched =\n prevRunStep?.type === StepTypes.MESSAGE_CREATION &&\n graph.messageStepHasToolCalls.has(prevStepId);\n\n if (prevRunStep?.type === StepTypes.TOOL_CALLS) {\n /**\n * If previous step is already a tool_calls step, use that step ID\n * This ensures tool call deltas are dispatched to the correct step\n */\n stepId = prevStepId;\n } else if (\n !alreadyDispatched &&\n prevRunStep?.type === StepTypes.MESSAGE_CREATION\n ) {\n /**\n * Create tool_calls step as soon as we receive the first tool call chunk\n * This ensures deltas are always associated with the correct step\n */\n await graph.dispatchMessageDelta(prevStepId, {\n content: [\n {\n type: ContentTypes.TEXT,\n text: '',\n tool_call_ids: tool_calls?.map((tc) => tc.id ?? '') ?? [],\n },\n ],\n });\n graph.messageStepHasToolCalls.set(prevStepId, true);\n stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: tool_calls ?? [],\n },\n metadata\n );\n }\n await graph.dispatchRunStepDelta(stepId, {\n type: StepTypes.TOOL_CALLS,\n tool_calls: toolCallChunks,\n });\n}\n\nexport const handleToolCalls = async (\n toolCalls?: ToolCall[],\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n): Promise<void> => {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n if (!toolCalls) {\n return;\n }\n\n if (toolCalls.length === 0) {\n return;\n }\n\n const stepKey = graph.getStepKey(metadata);\n\n for (const tool_call of toolCalls) {\n const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;\n tool_call.id = toolCallId;\n if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {\n continue;\n }\n\n let prevStepId = '';\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n // no previous step\n }\n\n const dispatchToolCallIds = async (\n lastMessageStepId: string\n ): Promise<void> => {\n await graph.dispatchMessageDelta(lastMessageStepId, {\n content: [\n {\n type: 'text',\n text: '',\n tool_call_ids: [toolCallId],\n },\n ],\n });\n };\n /* If the previous step exists and is a message creation */\n if (\n prevStepId &&\n prevRunStep &&\n prevRunStep.type === StepTypes.MESSAGE_CREATION\n ) {\n await dispatchToolCallIds(prevStepId);\n graph.messageStepHasToolCalls.set(prevStepId, true);\n /* If the previous step doesn't exist or is not a message creation */\n } else if (\n !prevRunStep ||\n prevRunStep.type !== StepTypes.MESSAGE_CREATION\n ) {\n const messageId = getMessageId(stepKey, graph, true) ?? '';\n const stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id: messageId,\n },\n },\n metadata\n );\n await dispatchToolCallIds(stepId);\n graph.messageStepHasToolCalls.set(prevStepId, true);\n }\n\n await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: [tool_call],\n },\n metadata\n );\n }\n};\n\nexport const toolResultTypes = new Set([\n // 'tool_use',\n // 'server_tool_use',\n // 'input_json_delta',\n 'tool_result',\n 'web_search_result',\n 'web_search_tool_result',\n]);\n\n/**\n * Handles the result of a server tool call; in other words, a provider's built-in tool.\n * As of 2025-07-06, only Anthropic handles server tool calls with this pattern.\n */\nexport async function handleServerToolResult({\n graph,\n content,\n metadata,\n agentContext,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n content?: string | t.MessageContentComplex[];\n metadata?: Record<string, unknown>;\n agentContext?: AgentContext;\n}): Promise<boolean> {\n let skipHandling = false;\n if (agentContext?.provider !== Providers.ANTHROPIC) {\n return skipHandling;\n }\n if (\n typeof content === 'string' ||\n content == null ||\n content.length === 0 ||\n (content.length === 1 &&\n (content[0] as t.ToolResultContent).tool_use_id == null)\n ) {\n return skipHandling;\n }\n\n for (const contentPart of content) {\n const toolUseId = (contentPart as t.ToolResultContent).tool_use_id;\n if (toolUseId == null || toolUseId === '') {\n continue;\n }\n const stepId = graph.toolCallStepIds.get(toolUseId);\n if (stepId == null || stepId === '') {\n console.warn(\n `Tool use ID ${toolUseId} not found in graph, cannot dispatch tool result.`\n );\n continue;\n }\n const runStep = graph.getRunStep(stepId);\n if (!runStep) {\n console.warn(\n `Run step for ${stepId} does not exist, cannot dispatch tool result.`\n );\n continue;\n } else if (runStep.type !== StepTypes.TOOL_CALLS) {\n console.warn(\n `Run step for ${stepId} is not a tool call step, cannot dispatch tool result.`\n );\n continue;\n }\n\n const toolCall =\n runStep.stepDetails.type === StepTypes.TOOL_CALLS\n ? (runStep.stepDetails.tool_calls?.find(\n (toolCall) => toolCall.id === toolUseId\n ) as ToolCall)\n : undefined;\n\n if (!toolCall) {\n continue;\n }\n\n if (\n contentPart.type === 'web_search_result' ||\n contentPart.type === 'web_search_tool_result'\n ) {\n await handleAnthropicSearchResults({\n contentPart: contentPart as t.ToolResultContent,\n toolCall,\n metadata,\n graph,\n });\n }\n\n if (!skipHandling) {\n skipHandling = true;\n }\n }\n\n return skipHandling;\n}\n\nasync function handleAnthropicSearchResults({\n contentPart,\n toolCall,\n metadata,\n graph,\n}: {\n contentPart: t.ToolResultContent;\n toolCall: Partial<ToolCall>;\n metadata?: Record<string, unknown>;\n graph: StandardGraph | MultiAgentGraph;\n}): Promise<void> {\n if (!Array.isArray(contentPart.content)) {\n console.warn(\n `Expected content to be an array, got ${typeof contentPart.content}`\n );\n return;\n }\n\n if (!isAnthropicWebSearchResult(contentPart.content[0])) {\n console.warn(\n `Expected content to be an Anthropic web search result, got ${JSON.stringify(\n contentPart.content\n )}`\n );\n return;\n }\n\n const turn = graph.invokedToolIds?.size ?? 0;\n const searchResultData = coerceAnthropicSearchResults({\n turn,\n results: contentPart.content as AnthropicWebSearchResultBlockParam[],\n });\n\n const name = toolCall.name;\n const input = toolCall.args ?? {};\n const artifact = {\n [Constants.WEB_SEARCH]: searchResultData,\n };\n const { output: formattedOutput } = formatResultsForLLM(\n turn,\n searchResultData\n );\n const output = new ToolMessage({\n name,\n artifact,\n content: formattedOutput,\n tool_call_id: toolCall.id!,\n });\n const toolEndData: t.ToolEndData = {\n input,\n output,\n };\n await graph.handlerRegistry\n ?.getHandler(GraphEvents.TOOL_END)\n ?.handle(GraphEvents.TOOL_END, toolEndData, metadata, graph);\n\n if (graph.invokedToolIds == null) {\n graph.invokedToolIds = new Set<string>();\n }\n\n graph.invokedToolIds.add(toolCall.id!);\n}\n"],"names":["getMessageId","StepTypes","ToolCallTypes","ContentTypes","nanoid","Providers","isAnthropicWebSearchResult","coerceAnthropicSearchResults","Constants","formatResultsForLLM","ToolMessage","GraphEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handlers.cjs","sources":["../../../src/tools/handlers.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/tools/handlers.ts\nimport { nanoid } from 'nanoid';\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { AnthropicWebSearchResultBlockParam } from '@/llm/anthropic/types';\nimport type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { AgentContext } from '@/agents/AgentContext';\nimport type * as t from '@/types';\nimport {\n ToolCallTypes,\n GraphEvents,\n StepTypes,\n Providers,\n Constants,\n} from '@/common';\nimport {\n coerceAnthropicSearchResults,\n isAnthropicWebSearchResult,\n} from '@/tools/search/anthropic';\nimport { formatResultsForLLM } from '@/tools/search/format';\nimport { getMessageId } from '@/messages';\n\nexport async function handleToolCallChunks({\n graph,\n stepKey,\n toolCallChunks,\n metadata,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n stepKey: string;\n toolCallChunks: ToolCallChunk[];\n metadata?: Record<string, unknown>;\n}): Promise<void> {\n let prevStepId: string;\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n /** Edge Case: If no previous step exists, create a new message creation step */\n const message_id = getMessageId(stepKey, graph, true) ?? '';\n prevStepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id,\n },\n },\n metadata\n );\n prevRunStep = graph.getRunStep(prevStepId);\n }\n\n const _stepId = graph.getStepIdByKey(stepKey);\n\n /** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */\n const tool_calls: ToolCall[] | undefined =\n prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION\n ? []\n : undefined;\n\n /** Edge Case: `id` and `name` fields cannot be empty strings */\n for (const toolCallChunk of toolCallChunks) {\n if (toolCallChunk.name === '') {\n toolCallChunk.name = undefined;\n }\n if (toolCallChunk.id === '') {\n toolCallChunk.id = undefined;\n } else if (\n tool_calls != null &&\n toolCallChunk.id != null &&\n toolCallChunk.name != null\n ) {\n tool_calls.push({\n args: {},\n id: toolCallChunk.id,\n name: toolCallChunk.name,\n type: ToolCallTypes.TOOL_CALL,\n });\n }\n }\n\n let stepId: string = _stepId;\n const alreadyDispatched =\n prevRunStep?.type === StepTypes.MESSAGE_CREATION &&\n graph.messageStepHasToolCalls.has(prevStepId);\n\n if (prevRunStep?.type === StepTypes.TOOL_CALLS) {\n /**\n * If previous step is already a tool_calls step, use that step ID\n * This ensures tool call deltas are dispatched to the correct step\n */\n stepId = prevStepId;\n } else if (\n !alreadyDispatched &&\n prevRunStep?.type === StepTypes.MESSAGE_CREATION\n ) {\n /**\n * Create tool_calls step as soon as we receive the first tool call chunk\n * This ensures deltas are always associated with the correct step\n *\n * NOTE: We do NOT dispatch an empty text block here because:\n * - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages\n * - The tool_calls themselves are sufficient for the step\n * - Empty content with tool_call_ids gets stored in conversation history\n * and causes \"messages must have non-empty content\" errors on replay\n */\n graph.messageStepHasToolCalls.set(prevStepId, true);\n stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: tool_calls ?? [],\n },\n metadata\n );\n }\n await graph.dispatchRunStepDelta(stepId, {\n type: StepTypes.TOOL_CALLS,\n tool_calls: toolCallChunks,\n });\n}\n\nexport const handleToolCalls = async (\n toolCalls?: ToolCall[],\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n): Promise<void> => {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n if (!toolCalls) {\n return;\n }\n\n if (toolCalls.length === 0) {\n return;\n }\n\n const stepKey = graph.getStepKey(metadata);\n\n for (const tool_call of toolCalls) {\n const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;\n tool_call.id = toolCallId;\n if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {\n continue;\n }\n\n let prevStepId = '';\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n // no previous step\n }\n\n /**\n * NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:\n * - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages\n * - They get stored in conversation history and cause errors on replay:\n * \"messages must have non-empty content\" (Anthropic)\n * \"The content field in the Message object is empty\" (Bedrock)\n * - The tool_calls themselves are sufficient\n */\n\n /* If the previous step exists and is a message creation */\n if (\n prevStepId &&\n prevRunStep &&\n prevRunStep.type === StepTypes.MESSAGE_CREATION\n ) {\n graph.messageStepHasToolCalls.set(prevStepId, true);\n /* If the previous step doesn't exist or is not a message creation */\n } else if (\n !prevRunStep ||\n prevRunStep.type !== StepTypes.MESSAGE_CREATION\n ) {\n const messageId = getMessageId(stepKey, graph, true) ?? '';\n const stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id: messageId,\n },\n },\n metadata\n );\n graph.messageStepHasToolCalls.set(stepId, true);\n }\n\n await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: [tool_call],\n },\n metadata\n );\n }\n};\n\nexport const toolResultTypes = new Set([\n // 'tool_use',\n // 'server_tool_use',\n // 'input_json_delta',\n 'tool_result',\n 'web_search_result',\n 'web_search_tool_result',\n]);\n\n/**\n * Handles the result of a server tool call; in other words, a provider's built-in tool.\n * As of 2025-07-06, only Anthropic handles server tool calls with this pattern.\n */\nexport async function handleServerToolResult({\n graph,\n content,\n metadata,\n agentContext,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n content?: string | t.MessageContentComplex[];\n metadata?: Record<string, unknown>;\n agentContext?: AgentContext;\n}): Promise<boolean> {\n let skipHandling = false;\n if (agentContext?.provider !== Providers.ANTHROPIC) {\n return skipHandling;\n }\n if (\n typeof content === 'string' ||\n content == null ||\n content.length === 0 ||\n (content.length === 1 &&\n (content[0] as t.ToolResultContent).tool_use_id == null)\n ) {\n return skipHandling;\n }\n\n for (const contentPart of content) {\n const toolUseId = (contentPart as t.ToolResultContent).tool_use_id;\n if (toolUseId == null || toolUseId === '') {\n continue;\n }\n const stepId = graph.toolCallStepIds.get(toolUseId);\n if (stepId == null || stepId === '') {\n console.warn(\n `Tool use ID ${toolUseId} not found in graph, cannot dispatch tool result.`\n );\n continue;\n }\n const runStep = graph.getRunStep(stepId);\n if (!runStep) {\n console.warn(\n `Run step for ${stepId} does not exist, cannot dispatch tool result.`\n );\n continue;\n } else if (runStep.type !== StepTypes.TOOL_CALLS) {\n console.warn(\n `Run step for ${stepId} is not a tool call step, cannot dispatch tool result.`\n );\n continue;\n }\n\n const toolCall =\n runStep.stepDetails.type === StepTypes.TOOL_CALLS\n ? (runStep.stepDetails.tool_calls?.find(\n (toolCall) => toolCall.id === toolUseId\n ) as ToolCall)\n : undefined;\n\n if (!toolCall) {\n continue;\n }\n\n if (\n contentPart.type === 'web_search_result' ||\n contentPart.type === 'web_search_tool_result'\n ) {\n await handleAnthropicSearchResults({\n contentPart: contentPart as t.ToolResultContent,\n toolCall,\n metadata,\n graph,\n });\n }\n\n if (!skipHandling) {\n skipHandling = true;\n }\n }\n\n return skipHandling;\n}\n\nasync function handleAnthropicSearchResults({\n contentPart,\n toolCall,\n metadata,\n graph,\n}: {\n contentPart: t.ToolResultContent;\n toolCall: Partial<ToolCall>;\n metadata?: Record<string, unknown>;\n graph: StandardGraph | MultiAgentGraph;\n}): Promise<void> {\n if (!Array.isArray(contentPart.content)) {\n console.warn(\n `Expected content to be an array, got ${typeof contentPart.content}`\n );\n return;\n }\n\n if (!isAnthropicWebSearchResult(contentPart.content[0])) {\n console.warn(\n `Expected content to be an Anthropic web search result, got ${JSON.stringify(\n contentPart.content\n )}`\n );\n return;\n }\n\n const turn = graph.invokedToolIds?.size ?? 0;\n const searchResultData = coerceAnthropicSearchResults({\n turn,\n results: contentPart.content as AnthropicWebSearchResultBlockParam[],\n });\n\n const name = toolCall.name;\n const input = toolCall.args ?? {};\n const artifact = {\n [Constants.WEB_SEARCH]: searchResultData,\n };\n const { output: formattedOutput } = formatResultsForLLM(\n turn,\n searchResultData\n );\n const output = new ToolMessage({\n name,\n artifact,\n content: formattedOutput,\n tool_call_id: toolCall.id!,\n });\n const toolEndData: t.ToolEndData = {\n input,\n output,\n };\n await graph.handlerRegistry\n ?.getHandler(GraphEvents.TOOL_END)\n ?.handle(GraphEvents.TOOL_END, toolEndData, metadata, graph);\n\n if (graph.invokedToolIds == null) {\n graph.invokedToolIds = new Set<string>();\n }\n\n graph.invokedToolIds.add(toolCall.id!);\n}\n"],"names":["getMessageId","StepTypes","ToolCallTypes","nanoid","Providers","isAnthropicWebSearchResult","coerceAnthropicSearchResults","Constants","formatResultsForLLM","ToolMessage","GraphEvents"],"mappings":";;;;;;;;;;AAAA;AACA;AAsBO,eAAe,oBAAoB,CAAC,EACzC,KAAK,EACL,OAAO,EACP,cAAc,EACd,QAAQ,GAMT,EAAA;AACC,IAAA,IAAI,UAAkB;AACtB,IAAA,IAAI,WAAkC;AACtC,IAAA,IAAI;AACF,QAAA,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC;AAC1C,QAAA,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC;;AAC1C,IAAA,MAAM;;AAEN,QAAA,MAAM,UAAU,GAAGA,gBAAY,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;AAC3D,QAAA,UAAU,GAAG,MAAM,KAAK,CAAC,eAAe,CACtC,OAAO,EACP;YACE,IAAI,EAAEC,eAAS,CAAC,gBAAgB;AAChC,YAAA,gBAAgB,EAAE;gBAChB,UAAU;AACX,aAAA;SACF,EACD,QAAQ,CACT;AACD,QAAA,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC;;IAG5C,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC;;AAG7C,IAAA,MAAM,UAAU,GACd,UAAU,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAKA,eAAS,CAAC;AAC1D,UAAE;UACA,SAAS;;AAGf,IAAA,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE;AAC1C,QAAA,IAAI,aAAa,CAAC,IAAI,KAAK,EAAE,EAAE;AAC7B,YAAA,aAAa,CAAC,IAAI,GAAG,SAAS;;AAEhC,QAAA,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE;AAC3B,YAAA,aAAa,CAAC,EAAE,GAAG,SAAS;;aACvB,IACL,UAAU,IAAI,IAAI;YAClB,aAAa,CAAC,EAAE,IAAI,IAAI;AACxB,YAAA,aAAa,CAAC,IAAI,IAAI,IAAI,EAC1B;YACA,UAAU,CAAC,IAAI,CAAC;AACd,gBAAA,IAAI,EAAE,EAAE;gBACR,EAAE,EAAE,aAAa,CAAC,EAAE;gBACpB,IAAI,EAAE,aAAa,CAAC,IAAI;gBACxB,IAAI,EAAEC,mBAAa,CAAC,SAAS;AAC9B,aAAA,CAAC;;;IAIN,IAAI,MAAM,GAAW,OAAO;IAC5B,MAAM,iBAAiB,GACrB,WAAW,EAAE,IAAI,KAAKD,eAAS,CAAC,gBAAgB;AAChD,QAAA,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC,UAAU,CAAC;IAE/C,IAAI,WAAW,EAAE,IAAI,KAAKA,eAAS,CAAC,UAAU,EAAE;AAC9C;;;AAGG;QACH,MAAM,GAAG,UAAU;;AACd,SAAA,IACL,CAAC,iBAAiB;AAClB,QAAA,WAAW,EAAE,IAAI,KAAKA,eAAS,CAAC,gBAAgB,EAChD;AACA;;;;;;;;;AASG;QACH,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC;AACnD,QAAA,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CAClC,OAAO,EACP;YACE,IAAI,EAAEA,eAAS,CAAC,UAAU;YAC1B,UAAU,EAAE,UAAU,IAAI,EAAE;SAC7B,EACD,QAAQ,CACT;;AAEH,IAAA,MAAM,KAAK,CAAC,oBAAoB,CAAC,MAAM,EAAE;QACvC,IAAI,EAAEA,eAAS,CAAC,UAAU;AAC1B,QAAA,UAAU,EAAE,cAAc;AAC3B,KAAA,CAAC;AACJ;AAEO,MAAM,eAAe,GAAG,OAC7B,SAAsB,EACtB,QAAkC,EAClC,KAAuC,KACtB;AACjB,IAAA,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE;AACvB,QAAA,OAAO,CAAC,IAAI,CAAC,kCAAkC,KAAK,CAAA,MAAA,CAAQ,CAAC;QAC7D;;IAGF,IAAI,CAAC,SAAS,EAAE;QACd;;AAGF,IAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;QAC1B;;IAGF,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC;AAE1C,IAAA,KAAK,MAAM,SAAS,IAAI,SAAS,EAAE;QACjC,MAAM,UAAU,GAAG,SAAS,CAAC,EAAE,IAAI,CAAS,MAAA,EAAAE,aAAM,EAAE,CAAA,CAAE;AACtD,QAAA,SAAS,CAAC,EAAE,GAAG,UAAU;AACzB,QAAA,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;YACxD;;QAGF,IAAI,UAAU,GAAG,EAAE;AACnB,QAAA,IAAI,WAAkC;AACtC,QAAA,IAAI;AACF,YAAA,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC;AAC1C,YAAA,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC;;AAC1C,QAAA,MAAM;;;AAIR;;;;;;;AAOG;;AAGH,QAAA,IACE,UAAU;YACV,WAAW;AACX,YAAA,WAAW,CAAC,IAAI,KAAKF,eAAS,CAAC,gBAAgB,EAC/C;YACA,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC;;;AAE9C,aAAA,IACL,CAAC,WAAW;AACZ,YAAA,WAAW,CAAC,IAAI,KAAKA,eAAS,CAAC,gBAAgB,EAC/C;AACA,YAAA,MAAM,SAAS,GAAGD,gBAAY,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;YAC1D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CACxC,OAAO,EACP;gBACE,IAAI,EAAEC,eAAS,CAAC,gBAAgB;AAChC,gBAAA,gBAAgB,EAAE;AAChB,oBAAA,UAAU,EAAE,SAAS;AACtB,iBAAA;aACF,EACD,QAAQ,CACT;YACD,KAAK,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC;;AAGjD,QAAA,MAAM,KAAK,CAAC,eAAe,CACzB,OAAO,EACP;YACE,IAAI,EAAEA,eAAS,CAAC,UAAU;YAC1B,UAAU,EAAE,CAAC,SAAS,CAAC;SACxB,EACD,QAAQ,CACT;;AAEL;AAEa,MAAA,eAAe,GAAG,IAAI,GAAG,CAAC;;;;IAIrC,aAAa;IACb,mBAAmB;IACnB,wBAAwB;AACzB,CAAA;AAED;;;AAGG;AACI,eAAe,sBAAsB,CAAC,EAC3C,KAAK,EACL,OAAO,EACP,QAAQ,EACR,YAAY,GAMb,EAAA;IACC,IAAI,YAAY,GAAG,KAAK;IACxB,IAAI,YAAY,EAAE,QAAQ,KAAKG,eAAS,CAAC,SAAS,EAAE;AAClD,QAAA,OAAO,YAAY;;IAErB,IACE,OAAO,OAAO,KAAK,QAAQ;AAC3B,QAAA,OAAO,IAAI,IAAI;QACf,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,SAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAClB,OAAO,CAAC,CAAC,CAAyB,CAAC,WAAW,IAAI,IAAI,CAAC,EAC1D;AACA,QAAA,OAAO,YAAY;;AAGrB,IAAA,KAAK,MAAM,WAAW,IAAI,OAAO,EAAE;AACjC,QAAA,MAAM,SAAS,GAAI,WAAmC,CAAC,WAAW;QAClE,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,KAAK,EAAE,EAAE;YACzC;;QAEF,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC;QACnD,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,EAAE;AACnC,YAAA,OAAO,CAAC,IAAI,CACV,eAAe,SAAS,CAAA,iDAAA,CAAmD,CAC5E;YACD;;QAEF,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE;AACZ,YAAA,OAAO,CAAC,IAAI,CACV,gBAAgB,MAAM,CAAA,6CAAA,CAA+C,CACtE;YACD;;aACK,IAAI,OAAO,CAAC,IAAI,KAAKH,eAAS,CAAC,UAAU,EAAE;AAChD,YAAA,OAAO,CAAC,IAAI,CACV,gBAAgB,MAAM,CAAA,sDAAA,CAAwD,CAC/E;YACD;;QAGF,MAAM,QAAQ,GACZ,OAAO,CAAC,WAAW,CAAC,IAAI,KAAKA,eAAS,CAAC;AACrC,cAAG,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,IAAI,CACrC,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,KAAK,SAAS;cAEvC,SAAS;QAEf,IAAI,CAAC,QAAQ,EAAE;YACb;;AAGF,QAAA,IACE,WAAW,CAAC,IAAI,KAAK,mBAAmB;AACxC,YAAA,WAAW,CAAC,IAAI,KAAK,wBAAwB,EAC7C;AACA,YAAA,MAAM,4BAA4B,CAAC;AACjC,gBAAA,WAAW,EAAE,WAAkC;gBAC/C,QAAQ;gBACR,QAAQ;gBACR,KAAK;AACN,aAAA,CAAC;;QAGJ,IAAI,CAAC,YAAY,EAAE;YACjB,YAAY,GAAG,IAAI;;;AAIvB,IAAA,OAAO,YAAY;AACrB;AAEA,eAAe,4BAA4B,CAAC,EAC1C,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,KAAK,GAMN,EAAA;IACC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE;QACvC,OAAO,CAAC,IAAI,CACV,CAAwC,qCAAA,EAAA,OAAO,WAAW,CAAC,OAAO,CAAE,CAAA,CACrE;QACD;;IAGF,IAAI,CAACI,oCAA0B,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;AACvD,QAAA,OAAO,CAAC,IAAI,CACV,CAAA,2DAAA,EAA8D,IAAI,CAAC,SAAS,CAC1E,WAAW,CAAC,OAAO,CACpB,CAAA,CAAE,CACJ;QACD;;IAGF,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC;IAC5C,MAAM,gBAAgB,GAAGC,sCAA4B,CAAC;QACpD,IAAI;QACJ,OAAO,EAAE,WAAW,CAAC,OAA+C;AACrE,KAAA,CAAC;AAEF,IAAA,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI;AAC1B,IAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,EAAE;AACjC,IAAA,MAAM,QAAQ,GAAG;AACf,QAAA,CAACC,eAAS,CAAC,UAAU,GAAG,gBAAgB;KACzC;AACD,IAAA,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAGC,0BAAmB,CACrD,IAAI,EACJ,gBAAgB,CACjB;AACD,IAAA,MAAM,MAAM,GAAG,IAAIC,oBAAW,CAAC;QAC7B,IAAI;QACJ,QAAQ;AACR,QAAA,OAAO,EAAE,eAAe;QACxB,YAAY,EAAE,QAAQ,CAAC,EAAG;AAC3B,KAAA,CAAC;AACF,IAAA,MAAM,WAAW,GAAkB;QACjC,KAAK;QACL,MAAM;KACP;IACD,MAAM,KAAK,CAAC;AACV,UAAE,UAAU,CAACC,iBAAW,CAAC,QAAQ;AACjC,UAAE,MAAM,CAACA,iBAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC;AAE9D,IAAA,IAAI,KAAK,CAAC,cAAc,IAAI,IAAI,EAAE;AAChC,QAAA,KAAK,CAAC,cAAc,GAAG,IAAI,GAAG,EAAU;;IAG1C,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAG,CAAC;AACxC;;;;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { ToolMessage } from '@langchain/core/messages';
|
|
3
|
-
import { StepTypes, ToolCallTypes,
|
|
3
|
+
import { StepTypes, ToolCallTypes, Providers, Constants, GraphEvents } from '../common/enum.mjs';
|
|
4
4
|
import { isAnthropicWebSearchResult, coerceAnthropicSearchResults } from './search/anthropic.mjs';
|
|
5
5
|
import { formatResultsForLLM } from './search/format.mjs';
|
|
6
6
|
import '../messages/core.mjs';
|
|
@@ -65,16 +65,13 @@ async function handleToolCallChunks({ graph, stepKey, toolCallChunks, metadata,
|
|
|
65
65
|
/**
|
|
66
66
|
* Create tool_calls step as soon as we receive the first tool call chunk
|
|
67
67
|
* This ensures deltas are always associated with the correct step
|
|
68
|
+
*
|
|
69
|
+
* NOTE: We do NOT dispatch an empty text block here because:
|
|
70
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
71
|
+
* - The tool_calls themselves are sufficient for the step
|
|
72
|
+
* - Empty content with tool_call_ids gets stored in conversation history
|
|
73
|
+
* and causes "messages must have non-empty content" errors on replay
|
|
68
74
|
*/
|
|
69
|
-
await graph.dispatchMessageDelta(prevStepId, {
|
|
70
|
-
content: [
|
|
71
|
-
{
|
|
72
|
-
type: ContentTypes.TEXT,
|
|
73
|
-
text: '',
|
|
74
|
-
tool_call_ids: tool_calls?.map((tc) => tc.id ?? '') ?? [],
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
});
|
|
78
75
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
79
76
|
stepId = await graph.dispatchRunStep(stepKey, {
|
|
80
77
|
type: StepTypes.TOOL_CALLS,
|
|
@@ -113,22 +110,18 @@ const handleToolCalls = async (toolCalls, metadata, graph) => {
|
|
|
113
110
|
catch {
|
|
114
111
|
// no previous step
|
|
115
112
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
],
|
|
125
|
-
});
|
|
126
|
-
};
|
|
113
|
+
/**
|
|
114
|
+
* NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:
|
|
115
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
116
|
+
* - They get stored in conversation history and cause errors on replay:
|
|
117
|
+
* "messages must have non-empty content" (Anthropic)
|
|
118
|
+
* "The content field in the Message object is empty" (Bedrock)
|
|
119
|
+
* - The tool_calls themselves are sufficient
|
|
120
|
+
*/
|
|
127
121
|
/* If the previous step exists and is a message creation */
|
|
128
122
|
if (prevStepId &&
|
|
129
123
|
prevRunStep &&
|
|
130
124
|
prevRunStep.type === StepTypes.MESSAGE_CREATION) {
|
|
131
|
-
await dispatchToolCallIds(prevStepId);
|
|
132
125
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
133
126
|
/* If the previous step doesn't exist or is not a message creation */
|
|
134
127
|
}
|
|
@@ -141,8 +134,7 @@ const handleToolCalls = async (toolCalls, metadata, graph) => {
|
|
|
141
134
|
message_id: messageId,
|
|
142
135
|
},
|
|
143
136
|
}, metadata);
|
|
144
|
-
|
|
145
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
137
|
+
graph.messageStepHasToolCalls.set(stepId, true);
|
|
146
138
|
}
|
|
147
139
|
await graph.dispatchRunStep(stepKey, {
|
|
148
140
|
type: StepTypes.TOOL_CALLS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.mjs","sources":["../../../src/tools/handlers.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/tools/handlers.ts\nimport { nanoid } from 'nanoid';\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { AnthropicWebSearchResultBlockParam } from '@/llm/anthropic/types';\nimport type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { AgentContext } from '@/agents/AgentContext';\nimport type * as t from '@/types';\nimport {\n ToolCallTypes,\n ContentTypes,\n GraphEvents,\n StepTypes,\n Providers,\n Constants,\n} from '@/common';\nimport {\n coerceAnthropicSearchResults,\n isAnthropicWebSearchResult,\n} from '@/tools/search/anthropic';\nimport { formatResultsForLLM } from '@/tools/search/format';\nimport { getMessageId } from '@/messages';\n\nexport async function handleToolCallChunks({\n graph,\n stepKey,\n toolCallChunks,\n metadata,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n stepKey: string;\n toolCallChunks: ToolCallChunk[];\n metadata?: Record<string, unknown>;\n}): Promise<void> {\n let prevStepId: string;\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n /** Edge Case: If no previous step exists, create a new message creation step */\n const message_id = getMessageId(stepKey, graph, true) ?? '';\n prevStepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id,\n },\n },\n metadata\n );\n prevRunStep = graph.getRunStep(prevStepId);\n }\n\n const _stepId = graph.getStepIdByKey(stepKey);\n\n /** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */\n const tool_calls: ToolCall[] | undefined =\n prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION\n ? []\n : undefined;\n\n /** Edge Case: `id` and `name` fields cannot be empty strings */\n for (const toolCallChunk of toolCallChunks) {\n if (toolCallChunk.name === '') {\n toolCallChunk.name = undefined;\n }\n if (toolCallChunk.id === '') {\n toolCallChunk.id = undefined;\n } else if (\n tool_calls != null &&\n toolCallChunk.id != null &&\n toolCallChunk.name != null\n ) {\n tool_calls.push({\n args: {},\n id: toolCallChunk.id,\n name: toolCallChunk.name,\n type: ToolCallTypes.TOOL_CALL,\n });\n }\n }\n\n let stepId: string = _stepId;\n const alreadyDispatched =\n prevRunStep?.type === StepTypes.MESSAGE_CREATION &&\n graph.messageStepHasToolCalls.has(prevStepId);\n\n if (prevRunStep?.type === StepTypes.TOOL_CALLS) {\n /**\n * If previous step is already a tool_calls step, use that step ID\n * This ensures tool call deltas are dispatched to the correct step\n */\n stepId = prevStepId;\n } else if (\n !alreadyDispatched &&\n prevRunStep?.type === StepTypes.MESSAGE_CREATION\n ) {\n /**\n * Create tool_calls step as soon as we receive the first tool call chunk\n * This ensures deltas are always associated with the correct step\n */\n await graph.dispatchMessageDelta(prevStepId, {\n content: [\n {\n type: ContentTypes.TEXT,\n text: '',\n tool_call_ids: tool_calls?.map((tc) => tc.id ?? '') ?? [],\n },\n ],\n });\n graph.messageStepHasToolCalls.set(prevStepId, true);\n stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: tool_calls ?? [],\n },\n metadata\n );\n }\n await graph.dispatchRunStepDelta(stepId, {\n type: StepTypes.TOOL_CALLS,\n tool_calls: toolCallChunks,\n });\n}\n\nexport const handleToolCalls = async (\n toolCalls?: ToolCall[],\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n): Promise<void> => {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n if (!toolCalls) {\n return;\n }\n\n if (toolCalls.length === 0) {\n return;\n }\n\n const stepKey = graph.getStepKey(metadata);\n\n for (const tool_call of toolCalls) {\n const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;\n tool_call.id = toolCallId;\n if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {\n continue;\n }\n\n let prevStepId = '';\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n // no previous step\n }\n\n const dispatchToolCallIds = async (\n lastMessageStepId: string\n ): Promise<void> => {\n await graph.dispatchMessageDelta(lastMessageStepId, {\n content: [\n {\n type: 'text',\n text: '',\n tool_call_ids: [toolCallId],\n },\n ],\n });\n };\n /* If the previous step exists and is a message creation */\n if (\n prevStepId &&\n prevRunStep &&\n prevRunStep.type === StepTypes.MESSAGE_CREATION\n ) {\n await dispatchToolCallIds(prevStepId);\n graph.messageStepHasToolCalls.set(prevStepId, true);\n /* If the previous step doesn't exist or is not a message creation */\n } else if (\n !prevRunStep ||\n prevRunStep.type !== StepTypes.MESSAGE_CREATION\n ) {\n const messageId = getMessageId(stepKey, graph, true) ?? '';\n const stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id: messageId,\n },\n },\n metadata\n );\n await dispatchToolCallIds(stepId);\n graph.messageStepHasToolCalls.set(prevStepId, true);\n }\n\n await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: [tool_call],\n },\n metadata\n );\n }\n};\n\nexport const toolResultTypes = new Set([\n // 'tool_use',\n // 'server_tool_use',\n // 'input_json_delta',\n 'tool_result',\n 'web_search_result',\n 'web_search_tool_result',\n]);\n\n/**\n * Handles the result of a server tool call; in other words, a provider's built-in tool.\n * As of 2025-07-06, only Anthropic handles server tool calls with this pattern.\n */\nexport async function handleServerToolResult({\n graph,\n content,\n metadata,\n agentContext,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n content?: string | t.MessageContentComplex[];\n metadata?: Record<string, unknown>;\n agentContext?: AgentContext;\n}): Promise<boolean> {\n let skipHandling = false;\n if (agentContext?.provider !== Providers.ANTHROPIC) {\n return skipHandling;\n }\n if (\n typeof content === 'string' ||\n content == null ||\n content.length === 0 ||\n (content.length === 1 &&\n (content[0] as t.ToolResultContent).tool_use_id == null)\n ) {\n return skipHandling;\n }\n\n for (const contentPart of content) {\n const toolUseId = (contentPart as t.ToolResultContent).tool_use_id;\n if (toolUseId == null || toolUseId === '') {\n continue;\n }\n const stepId = graph.toolCallStepIds.get(toolUseId);\n if (stepId == null || stepId === '') {\n console.warn(\n `Tool use ID ${toolUseId} not found in graph, cannot dispatch tool result.`\n );\n continue;\n }\n const runStep = graph.getRunStep(stepId);\n if (!runStep) {\n console.warn(\n `Run step for ${stepId} does not exist, cannot dispatch tool result.`\n );\n continue;\n } else if (runStep.type !== StepTypes.TOOL_CALLS) {\n console.warn(\n `Run step for ${stepId} is not a tool call step, cannot dispatch tool result.`\n );\n continue;\n }\n\n const toolCall =\n runStep.stepDetails.type === StepTypes.TOOL_CALLS\n ? (runStep.stepDetails.tool_calls?.find(\n (toolCall) => toolCall.id === toolUseId\n ) as ToolCall)\n : undefined;\n\n if (!toolCall) {\n continue;\n }\n\n if (\n contentPart.type === 'web_search_result' ||\n contentPart.type === 'web_search_tool_result'\n ) {\n await handleAnthropicSearchResults({\n contentPart: contentPart as t.ToolResultContent,\n toolCall,\n metadata,\n graph,\n });\n }\n\n if (!skipHandling) {\n skipHandling = true;\n }\n }\n\n return skipHandling;\n}\n\nasync function handleAnthropicSearchResults({\n contentPart,\n toolCall,\n metadata,\n graph,\n}: {\n contentPart: t.ToolResultContent;\n toolCall: Partial<ToolCall>;\n metadata?: Record<string, unknown>;\n graph: StandardGraph | MultiAgentGraph;\n}): Promise<void> {\n if (!Array.isArray(contentPart.content)) {\n console.warn(\n `Expected content to be an array, got ${typeof contentPart.content}`\n );\n return;\n }\n\n if (!isAnthropicWebSearchResult(contentPart.content[0])) {\n console.warn(\n `Expected content to be an Anthropic web search result, got ${JSON.stringify(\n contentPart.content\n )}`\n );\n return;\n }\n\n const turn = graph.invokedToolIds?.size ?? 0;\n const searchResultData = coerceAnthropicSearchResults({\n turn,\n results: contentPart.content as AnthropicWebSearchResultBlockParam[],\n });\n\n const name = toolCall.name;\n const input = toolCall.args ?? {};\n const artifact = {\n [Constants.WEB_SEARCH]: searchResultData,\n };\n const { output: formattedOutput } = formatResultsForLLM(\n turn,\n searchResultData\n );\n const output = new ToolMessage({\n name,\n artifact,\n content: formattedOutput,\n tool_call_id: toolCall.id!,\n });\n const toolEndData: t.ToolEndData = {\n input,\n output,\n };\n await graph.handlerRegistry\n ?.getHandler(GraphEvents.TOOL_END)\n ?.handle(GraphEvents.TOOL_END, toolEndData, metadata, graph);\n\n if (graph.invokedToolIds == null) {\n graph.invokedToolIds = new Set<string>();\n }\n\n graph.invokedToolIds.add(toolCall.id!);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handlers.mjs","sources":["../../../src/tools/handlers.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/tools/handlers.ts\nimport { nanoid } from 'nanoid';\nimport { ToolMessage } from '@langchain/core/messages';\nimport type { AnthropicWebSearchResultBlockParam } from '@/llm/anthropic/types';\nimport type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { AgentContext } from '@/agents/AgentContext';\nimport type * as t from '@/types';\nimport {\n ToolCallTypes,\n GraphEvents,\n StepTypes,\n Providers,\n Constants,\n} from '@/common';\nimport {\n coerceAnthropicSearchResults,\n isAnthropicWebSearchResult,\n} from '@/tools/search/anthropic';\nimport { formatResultsForLLM } from '@/tools/search/format';\nimport { getMessageId } from '@/messages';\n\nexport async function handleToolCallChunks({\n graph,\n stepKey,\n toolCallChunks,\n metadata,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n stepKey: string;\n toolCallChunks: ToolCallChunk[];\n metadata?: Record<string, unknown>;\n}): Promise<void> {\n let prevStepId: string;\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n /** Edge Case: If no previous step exists, create a new message creation step */\n const message_id = getMessageId(stepKey, graph, true) ?? '';\n prevStepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id,\n },\n },\n metadata\n );\n prevRunStep = graph.getRunStep(prevStepId);\n }\n\n const _stepId = graph.getStepIdByKey(stepKey);\n\n /** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */\n const tool_calls: ToolCall[] | undefined =\n prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION\n ? []\n : undefined;\n\n /** Edge Case: `id` and `name` fields cannot be empty strings */\n for (const toolCallChunk of toolCallChunks) {\n if (toolCallChunk.name === '') {\n toolCallChunk.name = undefined;\n }\n if (toolCallChunk.id === '') {\n toolCallChunk.id = undefined;\n } else if (\n tool_calls != null &&\n toolCallChunk.id != null &&\n toolCallChunk.name != null\n ) {\n tool_calls.push({\n args: {},\n id: toolCallChunk.id,\n name: toolCallChunk.name,\n type: ToolCallTypes.TOOL_CALL,\n });\n }\n }\n\n let stepId: string = _stepId;\n const alreadyDispatched =\n prevRunStep?.type === StepTypes.MESSAGE_CREATION &&\n graph.messageStepHasToolCalls.has(prevStepId);\n\n if (prevRunStep?.type === StepTypes.TOOL_CALLS) {\n /**\n * If previous step is already a tool_calls step, use that step ID\n * This ensures tool call deltas are dispatched to the correct step\n */\n stepId = prevStepId;\n } else if (\n !alreadyDispatched &&\n prevRunStep?.type === StepTypes.MESSAGE_CREATION\n ) {\n /**\n * Create tool_calls step as soon as we receive the first tool call chunk\n * This ensures deltas are always associated with the correct step\n *\n * NOTE: We do NOT dispatch an empty text block here because:\n * - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages\n * - The tool_calls themselves are sufficient for the step\n * - Empty content with tool_call_ids gets stored in conversation history\n * and causes \"messages must have non-empty content\" errors on replay\n */\n graph.messageStepHasToolCalls.set(prevStepId, true);\n stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: tool_calls ?? [],\n },\n metadata\n );\n }\n await graph.dispatchRunStepDelta(stepId, {\n type: StepTypes.TOOL_CALLS,\n tool_calls: toolCallChunks,\n });\n}\n\nexport const handleToolCalls = async (\n toolCalls?: ToolCall[],\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n): Promise<void> => {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n if (!toolCalls) {\n return;\n }\n\n if (toolCalls.length === 0) {\n return;\n }\n\n const stepKey = graph.getStepKey(metadata);\n\n for (const tool_call of toolCalls) {\n const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;\n tool_call.id = toolCallId;\n if (!toolCallId || graph.toolCallStepIds.has(toolCallId)) {\n continue;\n }\n\n let prevStepId = '';\n let prevRunStep: t.RunStep | undefined;\n try {\n prevStepId = graph.getStepIdByKey(stepKey);\n prevRunStep = graph.getRunStep(prevStepId);\n } catch {\n // no previous step\n }\n\n /**\n * NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:\n * - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages\n * - They get stored in conversation history and cause errors on replay:\n * \"messages must have non-empty content\" (Anthropic)\n * \"The content field in the Message object is empty\" (Bedrock)\n * - The tool_calls themselves are sufficient\n */\n\n /* If the previous step exists and is a message creation */\n if (\n prevStepId &&\n prevRunStep &&\n prevRunStep.type === StepTypes.MESSAGE_CREATION\n ) {\n graph.messageStepHasToolCalls.set(prevStepId, true);\n /* If the previous step doesn't exist or is not a message creation */\n } else if (\n !prevRunStep ||\n prevRunStep.type !== StepTypes.MESSAGE_CREATION\n ) {\n const messageId = getMessageId(stepKey, graph, true) ?? '';\n const stepId = await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.MESSAGE_CREATION,\n message_creation: {\n message_id: messageId,\n },\n },\n metadata\n );\n graph.messageStepHasToolCalls.set(stepId, true);\n }\n\n await graph.dispatchRunStep(\n stepKey,\n {\n type: StepTypes.TOOL_CALLS,\n tool_calls: [tool_call],\n },\n metadata\n );\n }\n};\n\nexport const toolResultTypes = new Set([\n // 'tool_use',\n // 'server_tool_use',\n // 'input_json_delta',\n 'tool_result',\n 'web_search_result',\n 'web_search_tool_result',\n]);\n\n/**\n * Handles the result of a server tool call; in other words, a provider's built-in tool.\n * As of 2025-07-06, only Anthropic handles server tool calls with this pattern.\n */\nexport async function handleServerToolResult({\n graph,\n content,\n metadata,\n agentContext,\n}: {\n graph: StandardGraph | MultiAgentGraph;\n content?: string | t.MessageContentComplex[];\n metadata?: Record<string, unknown>;\n agentContext?: AgentContext;\n}): Promise<boolean> {\n let skipHandling = false;\n if (agentContext?.provider !== Providers.ANTHROPIC) {\n return skipHandling;\n }\n if (\n typeof content === 'string' ||\n content == null ||\n content.length === 0 ||\n (content.length === 1 &&\n (content[0] as t.ToolResultContent).tool_use_id == null)\n ) {\n return skipHandling;\n }\n\n for (const contentPart of content) {\n const toolUseId = (contentPart as t.ToolResultContent).tool_use_id;\n if (toolUseId == null || toolUseId === '') {\n continue;\n }\n const stepId = graph.toolCallStepIds.get(toolUseId);\n if (stepId == null || stepId === '') {\n console.warn(\n `Tool use ID ${toolUseId} not found in graph, cannot dispatch tool result.`\n );\n continue;\n }\n const runStep = graph.getRunStep(stepId);\n if (!runStep) {\n console.warn(\n `Run step for ${stepId} does not exist, cannot dispatch tool result.`\n );\n continue;\n } else if (runStep.type !== StepTypes.TOOL_CALLS) {\n console.warn(\n `Run step for ${stepId} is not a tool call step, cannot dispatch tool result.`\n );\n continue;\n }\n\n const toolCall =\n runStep.stepDetails.type === StepTypes.TOOL_CALLS\n ? (runStep.stepDetails.tool_calls?.find(\n (toolCall) => toolCall.id === toolUseId\n ) as ToolCall)\n : undefined;\n\n if (!toolCall) {\n continue;\n }\n\n if (\n contentPart.type === 'web_search_result' ||\n contentPart.type === 'web_search_tool_result'\n ) {\n await handleAnthropicSearchResults({\n contentPart: contentPart as t.ToolResultContent,\n toolCall,\n metadata,\n graph,\n });\n }\n\n if (!skipHandling) {\n skipHandling = true;\n }\n }\n\n return skipHandling;\n}\n\nasync function handleAnthropicSearchResults({\n contentPart,\n toolCall,\n metadata,\n graph,\n}: {\n contentPart: t.ToolResultContent;\n toolCall: Partial<ToolCall>;\n metadata?: Record<string, unknown>;\n graph: StandardGraph | MultiAgentGraph;\n}): Promise<void> {\n if (!Array.isArray(contentPart.content)) {\n console.warn(\n `Expected content to be an array, got ${typeof contentPart.content}`\n );\n return;\n }\n\n if (!isAnthropicWebSearchResult(contentPart.content[0])) {\n console.warn(\n `Expected content to be an Anthropic web search result, got ${JSON.stringify(\n contentPart.content\n )}`\n );\n return;\n }\n\n const turn = graph.invokedToolIds?.size ?? 0;\n const searchResultData = coerceAnthropicSearchResults({\n turn,\n results: contentPart.content as AnthropicWebSearchResultBlockParam[],\n });\n\n const name = toolCall.name;\n const input = toolCall.args ?? {};\n const artifact = {\n [Constants.WEB_SEARCH]: searchResultData,\n };\n const { output: formattedOutput } = formatResultsForLLM(\n turn,\n searchResultData\n );\n const output = new ToolMessage({\n name,\n artifact,\n content: formattedOutput,\n tool_call_id: toolCall.id!,\n });\n const toolEndData: t.ToolEndData = {\n input,\n output,\n };\n await graph.handlerRegistry\n ?.getHandler(GraphEvents.TOOL_END)\n ?.handle(GraphEvents.TOOL_END, toolEndData, metadata, graph);\n\n if (graph.invokedToolIds == null) {\n graph.invokedToolIds = new Set<string>();\n }\n\n graph.invokedToolIds.add(toolCall.id!);\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/tools/handlers.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type { AgentContext } from '@/agents/AgentContext';
|
|
|
9
9
|
import type * as t from '@/types';
|
|
10
10
|
import {
|
|
11
11
|
ToolCallTypes,
|
|
12
|
-
ContentTypes,
|
|
13
12
|
GraphEvents,
|
|
14
13
|
StepTypes,
|
|
15
14
|
Providers,
|
|
@@ -101,16 +100,13 @@ export async function handleToolCallChunks({
|
|
|
101
100
|
/**
|
|
102
101
|
* Create tool_calls step as soon as we receive the first tool call chunk
|
|
103
102
|
* This ensures deltas are always associated with the correct step
|
|
103
|
+
*
|
|
104
|
+
* NOTE: We do NOT dispatch an empty text block here because:
|
|
105
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
106
|
+
* - The tool_calls themselves are sufficient for the step
|
|
107
|
+
* - Empty content with tool_call_ids gets stored in conversation history
|
|
108
|
+
* and causes "messages must have non-empty content" errors on replay
|
|
104
109
|
*/
|
|
105
|
-
await graph.dispatchMessageDelta(prevStepId, {
|
|
106
|
-
content: [
|
|
107
|
-
{
|
|
108
|
-
type: ContentTypes.TEXT,
|
|
109
|
-
text: '',
|
|
110
|
-
tool_call_ids: tool_calls?.map((tc) => tc.id ?? '') ?? [],
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
});
|
|
114
110
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
115
111
|
stepId = await graph.dispatchRunStep(
|
|
116
112
|
stepKey,
|
|
@@ -163,26 +159,21 @@ export const handleToolCalls = async (
|
|
|
163
159
|
// no previous step
|
|
164
160
|
}
|
|
165
161
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
});
|
|
178
|
-
};
|
|
162
|
+
/**
|
|
163
|
+
* NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:
|
|
164
|
+
* - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
|
|
165
|
+
* - They get stored in conversation history and cause errors on replay:
|
|
166
|
+
* "messages must have non-empty content" (Anthropic)
|
|
167
|
+
* "The content field in the Message object is empty" (Bedrock)
|
|
168
|
+
* - The tool_calls themselves are sufficient
|
|
169
|
+
*/
|
|
170
|
+
|
|
179
171
|
/* If the previous step exists and is a message creation */
|
|
180
172
|
if (
|
|
181
173
|
prevStepId &&
|
|
182
174
|
prevRunStep &&
|
|
183
175
|
prevRunStep.type === StepTypes.MESSAGE_CREATION
|
|
184
176
|
) {
|
|
185
|
-
await dispatchToolCallIds(prevStepId);
|
|
186
177
|
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
187
178
|
/* If the previous step doesn't exist or is not a message creation */
|
|
188
179
|
} else if (
|
|
@@ -200,8 +191,7 @@ export const handleToolCalls = async (
|
|
|
200
191
|
},
|
|
201
192
|
metadata
|
|
202
193
|
);
|
|
203
|
-
|
|
204
|
-
graph.messageStepHasToolCalls.set(prevStepId, true);
|
|
194
|
+
graph.messageStepHasToolCalls.set(stepId, true);
|
|
205
195
|
}
|
|
206
196
|
|
|
207
197
|
await graph.dispatchRunStep(
|