add anthropic built in tools

This commit is contained in:
Henry 2025-09-30 12:21:52 +01:00
parent e7ed4311ee
commit b28e18e9a2
2 changed files with 168 additions and 11 deletions

View File

@ -182,6 +182,35 @@ class Agent_Agentflow implements INode {
agentModel: 'chatGoogleGenerativeAI'
}
},
{
label: 'Anthropic Built-in Tools',
name: 'agentToolsBuiltInAnthropic',
type: 'multiOptions',
optional: true,
options: [
{
label: 'Web Search',
name: 'web_search_20250305',
description: 'Search the web for the latest information'
},
{
label: 'Web Fetch',
name: 'web_fetch_20250910',
description: 'Retrieve full content from specified web pages'
}
/*
* Not supported yet as we need to get bash_code_execution_tool_result from content:
https://docs.claude.com/en/docs/agents-and-tools/tool-use/code-execution-tool#retrieve-generated-files
{
label: 'Code Interpreter',
name: 'code_execution_20250825',
description: 'Write and run Python code in a sandboxed environment'
}*/
],
show: {
agentModel: 'chatAnthropic'
}
},
{
label: 'Tools',
name: 'agentTools',
@ -803,6 +832,43 @@ class Agent_Agentflow implements INode {
}
}
const agentToolsBuiltInAnthropic = convertMultiOptionsToStringArray(nodeData.inputs?.agentToolsBuiltInAnthropic)
if (agentToolsBuiltInAnthropic && agentToolsBuiltInAnthropic.length > 0) {
for (const tool of agentToolsBuiltInAnthropic) {
// split _ to get the tool name by removing the last part (date)
const toolName = tool.split('_').slice(0, -1).join('_')
if (tool === 'code_execution_20250825') {
;(llmNodeInstance as any).clientOptions = {
defaultHeaders: {
'anthropic-beta': ['code-execution-2025-08-25', 'files-api-2025-04-14']
}
}
}
if (tool === 'web_fetch_20250910') {
;(llmNodeInstance as any).clientOptions = {
defaultHeaders: {
'anthropic-beta': ['web-fetch-2025-09-10']
}
}
}
const builtInTool: ICommonObject = {
type: tool,
name: toolName
}
;(toolsInstance as any).push(builtInTool)
;(availableTools as any).push({
name: tool,
toolNode: {
label: tool,
name: tool
}
})
}
}
if (llmNodeInstance && toolsInstance.length > 0) {
if (llmNodeInstance.bindTools === undefined) {
throw new Error(`Agent needs to have a function calling capable models.`)
@ -1659,6 +1725,10 @@ class Agent_Agentflow implements INode {
}> {
// Track total tokens used throughout this process
let totalTokens = response.usage_metadata?.total_tokens || 0
const usedTools: IUsedTool[] = []
let sourceDocuments: Array<any> = []
let artifacts: any[] = []
let isWaitingForHumanInput: boolean | undefined
if (!response.tool_calls || response.tool_calls.length === 0) {
return { response, usedTools: [], sourceDocuments: [], artifacts: [], totalTokens }
@ -1669,6 +1739,23 @@ class Agent_Agentflow implements INode {
sseStreamer.streamCalledToolsEvent(chatId, JSON.stringify(response.tool_calls))
}
const toBeRemovedToolCalls = []
for (let i = 0; i < response.tool_calls.length; i++) {
const toolCall = response.tool_calls[i]
if (!toolCall.id) {
toBeRemovedToolCalls.push(toolCall)
usedTools.push({
tool: toolCall.name || 'tool',
toolInput: toolCall.args,
toolOutput: response.content
})
}
}
for (const toolCall of toBeRemovedToolCalls) {
response.tool_calls.splice(response.tool_calls.indexOf(toolCall), 1)
}
// Add LLM response with tool calls to messages
messages.push({
id: response.id,
@ -1678,11 +1765,6 @@ class Agent_Agentflow implements INode {
usage_metadata: response.usage_metadata
})
const usedTools: IUsedTool[] = []
let sourceDocuments: Array<any> = []
let artifacts: any[] = []
let isWaitingForHumanInput: boolean | undefined
// Process each tool call
for (let i = 0; i < response.tool_calls.length; i++) {
const toolCall = response.tool_calls[i]
@ -1826,6 +1908,17 @@ class Agent_Agentflow implements INode {
}
}
if (response.tool_calls.length === 0) {
const responseContent = typeof response.content === 'string' ? response.content : JSON.stringify(response.content, null, 2)
return {
response: new AIMessageChunk(responseContent),
usedTools,
sourceDocuments,
artifacts,
totalTokens
}
}
// Get LLM response after tool calls
let newResponse: AIMessageChunk
@ -1925,6 +2018,10 @@ class Agent_Agentflow implements INode {
isWaitingForHumanInput?: boolean
}> {
let llmNodeInstance = llmWithoutToolsBind
const usedTools: IUsedTool[] = []
let sourceDocuments: Array<any> = []
let artifacts: any[] = []
let isWaitingForHumanInput: boolean | undefined
const lastCheckpointMessages = humanInputAction?.data?.input?.messages ?? []
if (!lastCheckpointMessages.length) {
@ -1950,6 +2047,23 @@ class Agent_Agentflow implements INode {
sseStreamer.streamCalledToolsEvent(chatId, JSON.stringify(response.tool_calls))
}
const toBeRemovedToolCalls = []
for (let i = 0; i < response.tool_calls.length; i++) {
const toolCall = response.tool_calls[i]
if (!toolCall.id) {
toBeRemovedToolCalls.push(toolCall)
usedTools.push({
tool: toolCall.name || 'tool',
toolInput: toolCall.args,
toolOutput: response.content
})
}
}
for (const toolCall of toBeRemovedToolCalls) {
response.tool_calls.splice(response.tool_calls.indexOf(toolCall), 1)
}
// Add LLM response with tool calls to messages
messages.push({
id: response.id,
@ -1959,11 +2073,6 @@ class Agent_Agentflow implements INode {
usage_metadata: response.usage_metadata
})
const usedTools: IUsedTool[] = []
let sourceDocuments: Array<any> = []
let artifacts: any[] = []
let isWaitingForHumanInput: boolean | undefined
// Process each tool call
for (let i = 0; i < response.tool_calls.length; i++) {
const toolCall = response.tool_calls[i]

View File

@ -25,7 +25,8 @@ import {
IconCode,
IconWorldWww,
IconPhoto,
IconBrandGoogle
IconBrandGoogle,
IconBrowserCheck
} from '@tabler/icons-react'
import StopCircleIcon from '@mui/icons-material/StopCircle'
import CancelIcon from '@mui/icons-material/Cancel'
@ -154,6 +155,17 @@ const AgentFlowNode = ({ data }) => {
}
}
const getBuiltInAnthropicToolIcon = (toolName) => {
switch (toolName) {
case 'web_search_20250305':
return <IconWorldWww size={14} color={'white'} />
case 'web_fetch_20250910':
return <IconBrowserCheck size={14} color={'white'} />
default:
return null
}
}
useEffect(() => {
if (ref.current) {
setTimeout(() => {
@ -455,6 +467,16 @@ const AgentFlowNode = ({ data }) => {
: [],
toolProperty: 'builtInTool',
isBuiltInGemini: true
},
{
tools: data.inputs?.agentToolsBuiltInAnthropic
? (typeof data.inputs.agentToolsBuiltInAnthropic === 'string'
? JSON.parse(data.inputs.agentToolsBuiltInAnthropic)
: data.inputs.agentToolsBuiltInAnthropic
).map((tool) => ({ builtInTool: tool }))
: [],
toolProperty: 'builtInTool',
isBuiltInAnthropic: true
}
]
@ -541,6 +563,32 @@ const AgentFlowNode = ({ data }) => {
]
}
// Handle built-in Anthropic tools with icons
if (config.isBuiltInAnthropic) {
const icon = getBuiltInAnthropicToolIcon(toolName)
if (!icon) return []
return [
<Box
key={`tool-${configIndex}-${toolIndex}`}
sx={{
width: 20,
height: 20,
borderRadius: '50%',
backgroundColor: customization.isDarkMode
? darken(data.color, 0.5)
: darken(data.color, 0.2),
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: 0.2
}}
>
{icon}
</Box>
]
}
return [
<Box
key={`tool-${configIndex}-${toolIndex}`}