Added anthropic tool calling functionality

This commit is contained in:
Waleed Latif
2025-02-04 16:50:34 -08:00
parent 5ae95bbed9
commit a154e8f37e

View File

@@ -1,10 +1,9 @@
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest, Message } from '../types'
import { ToolConfig } from '@/tools/types'
export const anthropicProvider: ProviderConfig = {
id: 'anthropic',
name: 'Anthropic',
description: 'Anthropic\'s Claude models',
description: "Anthropic's Claude models",
version: '1.0.0',
models: ['claude-3-5-sonnet-20241022'],
defaultModel: 'claude-3-5-sonnet-20241022',
@@ -16,100 +15,160 @@ export const anthropicProvider: ProviderConfig = {
'anthropic-version': '2023-06-01'
}),
createRequest: (request: ProviderRequest, functions?: any) => ({
model: request.model || anthropicProvider.defaultModel,
messages: [
...(request.context ? [{ role: 'user', content: request.context }] : [])
],
system: request.systemPrompt,
temperature: request.temperature,
max_tokens: request.maxTokens,
...(functions && {
tools: functions
})
}),
extractResponse: (response: any) => {
const data = response.output || response
const textContent = data.content?.find((item: any) => item.type === 'text')
return {
content: textContent?.text || '',
tokens: {
prompt: data.usage?.input_tokens,
completion: data.usage?.output_tokens,
total: data.usage?.input_tokens + data.usage?.output_tokens
}
}
},
handleToolCall: (response: any) => {
const data = response.output || response
const hasToolUse = data.content?.some((item: any) => item.type === 'tool_use')
if (!hasToolUse) {
const textContent = data.content?.find((item: any) => item.type === 'text')
return {
hasFunctionCall: false,
content: textContent?.text || ''
}
}
return {
hasFunctionCall: true
}
},
createToolCallMessage: (functionCall: FunctionCallResponse, result: any): Message => ({
role: 'assistant',
content: [
{
type: 'tool_use',
name: functionCall.name,
input: functionCall.arguments
}
]
}),
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
if (!tools || tools.length === 0) {
return undefined
}
return tools.map(tool => ({
type: 'function',
name: tool.id,
description: tool.description,
parameters: {
input_schema: {
type: 'object',
properties: Object.entries(tool.params).reduce((acc, [key, config]) => {
acc[key] = {
type: config.type,
description: config.description,
...(config.default && { default: config.default })
}
return acc
}, {} as Record<string, any>),
required: Object.entries(tool.params)
.filter(([_, config]) => config.required)
.map(([key]) => key)
properties: tool.parameters.properties,
required: tool.parameters.required
}
}))
},
transformFunctionCallResponse: (response: any): FunctionCallResponse => {
const content = response.output ? response.output.content : response.content
if (!content || !Array.isArray(content)) {
throw new Error('Invalid response format: content is missing or not an array')
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
const rawResponse = response?.output || response
if (!rawResponse?.content) {
throw new Error('No content found in response')
}
const toolUse = content.find(item => item.type === 'tool_use')
const toolUse = rawResponse.content.find((item: any) => item.type === 'tool_use')
if (!toolUse) {
throw new Error('No tool use found in response')
}
const tool = tools?.find(t => t.id === toolUse.name)
if (!tool) {
throw new Error(`Tool not found: ${toolUse.name}`)
}
let input = toolUse.input
if (typeof input === 'string') {
try {
input = JSON.parse(input)
} catch (e) {
console.error('Failed to parse tool input:', e)
input = {}
}
}
return {
name: toolUse.name,
arguments: typeof toolUse.input === 'string'
? JSON.parse(toolUse.input)
: toolUse.input
arguments: {
...tool.params,
...input
}
}
},
transformRequest: (request: ProviderRequest, functions?: any) => {
// Transform messages to Anthropic format
const messages = request.messages?.map(msg => {
if (msg.role === 'function') {
return {
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: msg.name,
content: msg.content
}]
}
}
if (msg.function_call) {
return {
role: 'assistant',
content: [{
type: 'tool_use',
id: msg.function_call.name,
name: msg.function_call.name,
input: JSON.parse(msg.function_call.arguments)
}]
}
}
return {
role: msg.role === 'assistant' ? 'assistant' : 'user',
content: msg.content ? [{ type: 'text', text: msg.content }] : []
}
}) || []
// Add context if provided
if (request.context) {
messages.unshift({
role: 'user',
content: [{ type: 'text', text: request.context }]
})
}
// Build the request payload exactly as Anthropic expects
const payload = {
model: request.model || 'claude-3-5-sonnet-20241022',
messages,
system: request.systemPrompt || '',
max_tokens: parseInt(String(request.maxTokens)) || 1024,
temperature: parseFloat(String(request.temperature ?? 0.7)),
...(functions && { tools: functions })
}
return payload
},
transformResponse: (response: any) => {
try {
if (!response) {
console.warn('Received undefined response from Anthropic API')
return { content: '' }
}
// Get the actual response content
const rawResponse = response.output || response
// Extract text content from the message
let content = ''
const messageContent = rawResponse?.content || rawResponse?.message?.content
if (Array.isArray(messageContent)) {
content = messageContent
.filter(item => item.type === 'text')
.map(item => item.text)
.join('\n')
} else if (typeof messageContent === 'string') {
content = messageContent
}
const result = {
content,
model: rawResponse?.model || response?.model || 'claude-3-5-sonnet-20241022',
...(rawResponse?.usage && {
tokens: {
prompt: rawResponse.usage.input_tokens,
completion: rawResponse.usage.output_tokens,
total: rawResponse.usage.input_tokens + rawResponse.usage.output_tokens
}
})
}
return result
} catch (error) {
console.error('Error in transformResponse:', error)
return { content: '' }
}
},
hasFunctionCall: (response: any) => {
try {
if (!response) return false
const rawResponse = response.output || response
return rawResponse?.content?.some((item: any) => item.type === 'tool_use') || false
} catch (error) {
console.error('Error in hasFunctionCall:', error)
return false
}
}
}
}