mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-08-01 04:32:26 +08:00
get anthropic sdk to send tools
nice close what is even going on lol anthropic tools mostly work
This commit is contained in:
parent
2bc2aa857f
commit
3bddb60ecc
@ -70,8 +70,8 @@ async function listModels(req: Request, res: Response) {
|
|||||||
const { baseUrl } = req.body;
|
const { baseUrl } = req.body;
|
||||||
|
|
||||||
// Use provided base URL or default from options
|
// Use provided base URL or default from options
|
||||||
const anthropicBaseUrl = baseUrl ||
|
const anthropicBaseUrl = baseUrl ||
|
||||||
await options.getOption('anthropicBaseUrl') ||
|
await options.getOption('anthropicBaseUrl') ||
|
||||||
PROVIDER_CONSTANTS.ANTHROPIC.BASE_URL;
|
PROVIDER_CONSTANTS.ANTHROPIC.BASE_URL;
|
||||||
|
|
||||||
const apiKey = await options.getOption('anthropicApiKey');
|
const apiKey = await options.getOption('anthropicApiKey');
|
||||||
@ -80,71 +80,18 @@ async function listModels(req: Request, res: Response) {
|
|||||||
throw new Error('Anthropic API key is not configured');
|
throw new Error('Anthropic API key is not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Listing models from Anthropic API using the SDK`);
|
log.info(`Using predefined Anthropic models list (avoiding direct API call)`);
|
||||||
|
|
||||||
// Initialize the Anthropic client with the SDK
|
// Instead of using the SDK's built-in models listing which might not work,
|
||||||
const client = new Anthropic({
|
// directly use the predefined available models
|
||||||
apiKey,
|
const chatModels = PROVIDER_CONSTANTS.ANTHROPIC.AVAILABLE_MODELS.map(model => ({
|
||||||
baseURL: anthropicBaseUrl,
|
id: model.id,
|
||||||
defaultHeaders: {
|
name: model.name,
|
||||||
'anthropic-version': PROVIDER_CONSTANTS.ANTHROPIC.API_VERSION,
|
type: 'chat'
|
||||||
'anthropic-beta': PROVIDER_CONSTANTS.ANTHROPIC.BETA_VERSION
|
}));
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Use the SDK's built-in models listing
|
// Anthropic doesn't currently have embedding models
|
||||||
const response = await client.models.list();
|
const embeddingModels: AnthropicModel[] = [];
|
||||||
|
|
||||||
// Process the models
|
|
||||||
const allModels = response.data || [];
|
|
||||||
|
|
||||||
// Log available models
|
|
||||||
log.info(`Found ${allModels.length} models from Anthropic: ${allModels.map(m => m.id).join(', ')}`);
|
|
||||||
|
|
||||||
// Separate models into chat models and embedding models
|
|
||||||
const chatModels = allModels
|
|
||||||
.filter(model =>
|
|
||||||
// Claude models are for chat
|
|
||||||
model.id.includes('claude')
|
|
||||||
)
|
|
||||||
.map(model => {
|
|
||||||
// Get a simplified name for display purposes
|
|
||||||
let displayName = model.id;
|
|
||||||
// Try to simplify the model name by removing version suffixes
|
|
||||||
if (model.id.match(/claude-\d+-\w+-\d+/)) {
|
|
||||||
displayName = model.id.replace(/-\d+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: model.id, // Keep full ID for API calls
|
|
||||||
name: displayName, // Use simplified name for display
|
|
||||||
type: 'chat'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also include known models that might not be returned by the API
|
|
||||||
for (const model of PROVIDER_CONSTANTS.ANTHROPIC.AVAILABLE_MODELS) {
|
|
||||||
// Check if this model is already in our list
|
|
||||||
if (!chatModels.some((m: AnthropicModel) => m.id === model.id)) {
|
|
||||||
chatModels.push({
|
|
||||||
id: model.id,
|
|
||||||
name: model.name,
|
|
||||||
type: 'chat'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Anthropic might not have embedding models yet, but we'll include this for future compatibility
|
|
||||||
const embeddingModels = allModels
|
|
||||||
.filter(model =>
|
|
||||||
// If Anthropic releases embedding models, they'd likely include 'embed' in the name
|
|
||||||
model.id.includes('embed')
|
|
||||||
)
|
|
||||||
.map(model => ({
|
|
||||||
id: model.id,
|
|
||||||
name: model.id,
|
|
||||||
type: 'embedding'
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Return the models list
|
// Return the models list
|
||||||
return {
|
return {
|
||||||
@ -162,4 +109,4 @@ async function listModels(req: Request, res: Response) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
listModels
|
listModels
|
||||||
};
|
};
|
||||||
|
@ -48,6 +48,16 @@ export interface StreamChunk {
|
|||||||
* These may be accumulated over multiple chunks during streaming
|
* These may be accumulated over multiple chunks during streaming
|
||||||
*/
|
*/
|
||||||
tool_calls?: ToolCall[] | any[];
|
tool_calls?: ToolCall[] | any[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tool execution information during streaming
|
||||||
|
* Includes tool name, args, and execution status
|
||||||
|
*/
|
||||||
|
toolExecution?: {
|
||||||
|
type: 'start' | 'update' | 'complete' | 'error';
|
||||||
|
tool: any;
|
||||||
|
result?: any;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,6 +112,7 @@ export interface ChatCompletionOptions {
|
|||||||
|
|
||||||
enableTools?: boolean; // Whether to enable tool calling
|
enableTools?: boolean; // Whether to enable tool calling
|
||||||
tools?: any[]; // Tools to provide to the LLM
|
tools?: any[]; // Tools to provide to the LLM
|
||||||
|
tool_choice?: any; // Tool choice parameter for the LLM
|
||||||
useAdvancedContext?: boolean; // Whether to use advanced context enrichment
|
useAdvancedContext?: boolean; // Whether to use advanced context enrichment
|
||||||
toolExecutionStatus?: any[]; // Status information about executed tools for feedback
|
toolExecutionStatus?: any[]; // Status information about executed tools for feedback
|
||||||
providerMetadata?: ModelMetadata; // Metadata about the provider and model capabilities
|
providerMetadata?: ModelMetadata; // Metadata about the provider and model capabilities
|
||||||
|
@ -6,8 +6,9 @@ export const PROVIDER_CONSTANTS = {
|
|||||||
DEFAULT_MODEL: 'claude-3-haiku-20240307',
|
DEFAULT_MODEL: 'claude-3-haiku-20240307',
|
||||||
// Model mapping for simplified model names to their full versions
|
// Model mapping for simplified model names to their full versions
|
||||||
MODEL_MAPPING: {
|
MODEL_MAPPING: {
|
||||||
'claude-3.7-sonnet': 'claude-3-7-sonnet-20240620',
|
'claude-3.7-sonnet': 'claude-3-7-sonnet-20250219',
|
||||||
'claude-3.5-haiku': 'claude-3-5-haiku-20240307',
|
'claude-3.5-sonnet': 'claude-3-5-sonnet-20241022',
|
||||||
|
'claude-3.5-haiku': 'claude-3-5-haiku-20241022',
|
||||||
'claude-3-opus': 'claude-3-opus-20240229',
|
'claude-3-opus': 'claude-3-opus-20240229',
|
||||||
'claude-3-sonnet': 'claude-3-sonnet-20240229',
|
'claude-3-sonnet': 'claude-3-sonnet-20240229',
|
||||||
'claude-3-haiku': 'claude-3-haiku-20240307',
|
'claude-3-haiku': 'claude-3-haiku-20240307',
|
||||||
@ -16,15 +17,21 @@ export const PROVIDER_CONSTANTS = {
|
|||||||
// These are the currently available models from Anthropic
|
// These are the currently available models from Anthropic
|
||||||
AVAILABLE_MODELS: [
|
AVAILABLE_MODELS: [
|
||||||
{
|
{
|
||||||
id: 'claude-3-7-sonnet-20240620',
|
id: 'claude-3-7-sonnet-20250219',
|
||||||
name: 'Claude 3.7 Sonnet',
|
name: 'Claude 3.7 Sonnet',
|
||||||
description: 'Most intelligent model with hybrid reasoning capabilities',
|
description: 'Most intelligent model with hybrid reasoning capabilities',
|
||||||
maxTokens: 8192
|
maxTokens: 8192
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'claude-3-5-haiku-20240307',
|
id: 'claude-3-5-sonnet-20241022',
|
||||||
|
name: 'Claude 3.5 Sonnet',
|
||||||
|
description: 'High level of intelligence and capability',
|
||||||
|
maxTokens: 8192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'claude-3-5-haiku-20241022',
|
||||||
name: 'Claude 3.5 Haiku',
|
name: 'Claude 3.5 Haiku',
|
||||||
description: 'Improved version of Haiku with better performance',
|
description: 'Fastest model with high intelligence',
|
||||||
maxTokens: 8192
|
maxTokens: 8192
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -66,10 +66,15 @@ export class AnthropicService extends BaseAIService {
|
|||||||
providerOptions.betaVersion
|
providerOptions.betaVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Log API key format (without revealing the actual key)
|
||||||
|
const apiKeyPrefix = providerOptions.apiKey?.substring(0, 7) || 'undefined';
|
||||||
|
const apiKeyLength = providerOptions.apiKey?.length || 0;
|
||||||
|
log.info(`[DEBUG] Using Anthropic API key with prefix '${apiKeyPrefix}...' and length ${apiKeyLength}`);
|
||||||
|
|
||||||
log.info(`Using Anthropic API with model: ${providerOptions.model}`);
|
log.info(`Using Anthropic API with model: ${providerOptions.model}`);
|
||||||
|
|
||||||
// Configure request parameters
|
// Configure request parameters
|
||||||
const requestParams = {
|
const requestParams: any = {
|
||||||
model: providerOptions.model,
|
model: providerOptions.model,
|
||||||
messages: anthropicMessages,
|
messages: anthropicMessages,
|
||||||
system: systemPrompt,
|
system: systemPrompt,
|
||||||
@ -79,6 +84,32 @@ export class AnthropicService extends BaseAIService {
|
|||||||
stream: !!providerOptions.stream
|
stream: !!providerOptions.stream
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add tools support if provided
|
||||||
|
if (opts.tools && opts.tools.length > 0) {
|
||||||
|
log.info(`Adding ${opts.tools.length} tools to Anthropic request`);
|
||||||
|
|
||||||
|
// Convert OpenAI-style function tools to Anthropic format
|
||||||
|
const anthropicTools = this.convertToolsToAnthropicFormat(opts.tools);
|
||||||
|
requestParams.tools = anthropicTools;
|
||||||
|
|
||||||
|
// Add tool_choice parameter if specified
|
||||||
|
if (opts.tool_choice) {
|
||||||
|
if (opts.tool_choice === 'auto') {
|
||||||
|
requestParams.tool_choice = 'auto';
|
||||||
|
} else if (opts.tool_choice === 'none') {
|
||||||
|
requestParams.tool_choice = 'none';
|
||||||
|
} else if (typeof opts.tool_choice === 'object' && opts.tool_choice.function) {
|
||||||
|
// Map from OpenAI format to Anthropic format
|
||||||
|
requestParams.tool_choice = opts.tool_choice.function.name;
|
||||||
|
} else {
|
||||||
|
requestParams.tool_choice = opts.tool_choice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log request summary
|
||||||
|
log.info(`Making ${providerOptions.stream ? 'streaming' : 'non-streaming'} request to Anthropic API with model: ${providerOptions.model}`);
|
||||||
|
|
||||||
// Handle streaming responses
|
// Handle streaming responses
|
||||||
if (providerOptions.stream) {
|
if (providerOptions.stream) {
|
||||||
return this.handleStreamingResponse(client, requestParams, opts, providerOptions);
|
return this.handleStreamingResponse(client, requestParams, opts, providerOptions);
|
||||||
@ -92,10 +123,43 @@ export class AnthropicService extends BaseAIService {
|
|||||||
.map((block: any) => block.text)
|
.map((block: any) => block.text)
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
|
// Process tool calls if any are present in the response
|
||||||
|
let toolCalls = null;
|
||||||
|
if (response.content) {
|
||||||
|
const toolBlocks = response.content.filter((block: any) =>
|
||||||
|
block.type === 'tool_use' ||
|
||||||
|
(block.type === 'tool_result' && block.tool_use_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (toolBlocks.length > 0) {
|
||||||
|
log.info(`[DEBUG] Found ${toolBlocks.length} tool-related blocks in response`);
|
||||||
|
|
||||||
|
toolCalls = toolBlocks.map((block: any) => {
|
||||||
|
if (block.type === 'tool_use') {
|
||||||
|
log.info(`[DEBUG] Processing tool_use block: ${JSON.stringify(block, null, 2)}`);
|
||||||
|
|
||||||
|
// Convert Anthropic tool_use format to standard format expected by our app
|
||||||
|
return {
|
||||||
|
id: block.id,
|
||||||
|
type: 'function', // Convert back to function type for internal use
|
||||||
|
function: {
|
||||||
|
name: block.name,
|
||||||
|
arguments: block.input || '{}'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
log.info(`Extracted ${toolCalls.length} tool calls from Anthropic response`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: textContent,
|
text: textContent,
|
||||||
model: response.model,
|
model: response.model,
|
||||||
provider: this.getName(),
|
provider: this.getName(),
|
||||||
|
tool_calls: toolCalls,
|
||||||
usage: {
|
usage: {
|
||||||
// Anthropic provides token counts in the response
|
// Anthropic provides token counts in the response
|
||||||
promptTokens: response.usage?.input_tokens,
|
promptTokens: response.usage?.input_tokens,
|
||||||
@ -112,7 +176,7 @@ export class AnthropicService extends BaseAIService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle streaming response from Anthropic
|
* Handle streaming response from Anthropic
|
||||||
*
|
*
|
||||||
* Simplified implementation that leverages the Anthropic SDK's streaming capabilities
|
* Simplified implementation that leverages the Anthropic SDK's streaming capabilities
|
||||||
*/
|
*/
|
||||||
private async handleStreamingResponse(
|
private async handleStreamingResponse(
|
||||||
@ -124,9 +188,13 @@ export class AnthropicService extends BaseAIService {
|
|||||||
// Create a stream handler function that processes the SDK's stream
|
// Create a stream handler function that processes the SDK's stream
|
||||||
const streamHandler = async (callback: (chunk: StreamChunk) => Promise<void> | void): Promise<string> => {
|
const streamHandler = async (callback: (chunk: StreamChunk) => Promise<void> | void): Promise<string> => {
|
||||||
let completeText = '';
|
let completeText = '';
|
||||||
|
const toolCalls: any[] = [];
|
||||||
|
let currentToolCall: any = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Request a streaming response from Anthropic
|
// Request a streaming response from Anthropic
|
||||||
|
log.info(`Starting Anthropic streaming request to: ${providerOptions.baseUrl}/v1/messages`);
|
||||||
|
|
||||||
const streamResponse = await client.messages.create({
|
const streamResponse = await client.messages.create({
|
||||||
...params,
|
...params,
|
||||||
stream: true
|
stream: true
|
||||||
@ -134,11 +202,10 @@ export class AnthropicService extends BaseAIService {
|
|||||||
|
|
||||||
// Process each chunk in the stream
|
// Process each chunk in the stream
|
||||||
for await (const chunk of streamResponse) {
|
for await (const chunk of streamResponse) {
|
||||||
// Only process text content deltas
|
|
||||||
if (chunk.type === 'content_block_delta' && chunk.delta?.type === 'text_delta') {
|
if (chunk.type === 'content_block_delta' && chunk.delta?.type === 'text_delta') {
|
||||||
const text = chunk.delta.text || '';
|
const text = chunk.delta.text || '';
|
||||||
completeText += text;
|
completeText += text;
|
||||||
|
|
||||||
// Send the chunk to the caller
|
// Send the chunk to the caller
|
||||||
await callback({
|
await callback({
|
||||||
text,
|
text,
|
||||||
@ -146,17 +213,99 @@ export class AnthropicService extends BaseAIService {
|
|||||||
raw: chunk // Include the raw chunk for advanced processing
|
raw: chunk // Include the raw chunk for advanced processing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Process tool use events - different format in Anthropic API
|
||||||
|
else if (chunk.type === 'content_block_start' && chunk.content_block?.type === 'tool_use') {
|
||||||
|
// Start collecting a new tool call - convert to our internal format (OpenAI-like)
|
||||||
|
currentToolCall = {
|
||||||
|
id: chunk.content_block.id,
|
||||||
|
type: 'function', // Convert to function type for internal consistency
|
||||||
|
function: {
|
||||||
|
name: chunk.content_block.name,
|
||||||
|
arguments: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log the tool use event
|
||||||
|
log.info(`Streaming: Tool use started: ${chunk.content_block.name}`);
|
||||||
|
|
||||||
|
// Send the tool call event
|
||||||
|
await callback({
|
||||||
|
text: '',
|
||||||
|
done: false,
|
||||||
|
toolExecution: {
|
||||||
|
type: 'start',
|
||||||
|
tool: currentToolCall
|
||||||
|
},
|
||||||
|
raw: chunk
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Process tool input deltas
|
||||||
|
else if (chunk.type === 'content_block_delta' && chunk.delta?.type === 'tool_use_delta' && currentToolCall) {
|
||||||
|
// Accumulate tool input
|
||||||
|
if (chunk.delta.input) {
|
||||||
|
currentToolCall.function.arguments += chunk.delta.input;
|
||||||
|
|
||||||
|
// Send the tool input update
|
||||||
|
await callback({
|
||||||
|
text: '',
|
||||||
|
done: false,
|
||||||
|
toolExecution: {
|
||||||
|
type: 'update',
|
||||||
|
tool: currentToolCall
|
||||||
|
},
|
||||||
|
raw: chunk
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process tool use completion
|
||||||
|
else if (chunk.type === 'content_block_stop' && currentToolCall) {
|
||||||
|
// Add the completed tool call to our list
|
||||||
|
toolCalls.push(currentToolCall);
|
||||||
|
|
||||||
|
// Log the tool completion
|
||||||
|
log.info(`Streaming: Tool use completed: ${currentToolCall.function.name}`);
|
||||||
|
|
||||||
|
// Send the tool completion event
|
||||||
|
await callback({
|
||||||
|
text: '',
|
||||||
|
done: false,
|
||||||
|
toolExecution: {
|
||||||
|
type: 'complete',
|
||||||
|
tool: currentToolCall
|
||||||
|
},
|
||||||
|
raw: chunk
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset current tool call
|
||||||
|
currentToolCall = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal completion
|
// Signal completion
|
||||||
await callback({
|
await callback({
|
||||||
text: '',
|
text: '',
|
||||||
done: true
|
done: true,
|
||||||
|
tool_calls: toolCalls.length > 0 ? toolCalls : undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
return completeText;
|
return completeText;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Error in Anthropic streaming: ${error}`);
|
log.error(`Error in Anthropic streaming: ${error}`);
|
||||||
|
|
||||||
|
// More detailed error logging
|
||||||
|
if (error instanceof Error) {
|
||||||
|
log.error(`[DEBUG] Error name: ${error.name}`);
|
||||||
|
log.error(`[DEBUG] Error message: ${error.message}`);
|
||||||
|
log.error(`[DEBUG] Error stack: ${error.stack}`);
|
||||||
|
|
||||||
|
// If there's response data in the error, log that too
|
||||||
|
const anyError = error as any;
|
||||||
|
if (anyError.response) {
|
||||||
|
log.error(`Error response status: ${anyError.response.status}`);
|
||||||
|
log.error(`Error response data: ${JSON.stringify(anyError.response.data)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -198,4 +347,70 @@ export class AnthropicService extends BaseAIService {
|
|||||||
|
|
||||||
return anthropicMessages;
|
return anthropicMessages;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Convert OpenAI-style function tools to Anthropic format
|
||||||
|
* OpenAI uses: { type: "function", function: { name, description, parameters } }
|
||||||
|
* Anthropic uses: { name, description, input_schema }
|
||||||
|
*/
|
||||||
|
private convertToolsToAnthropicFormat(tools: any[]): any[] {
|
||||||
|
if (!tools || tools.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out invalid tools
|
||||||
|
const validTools = tools.filter(tool => {
|
||||||
|
if (!tool || typeof tool !== 'object') {
|
||||||
|
log.error(`Invalid tool format (not an object)`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For function tools, validate required fields
|
||||||
|
if (tool.type === 'function') {
|
||||||
|
if (!tool.function || !tool.function.name) {
|
||||||
|
log.error(`Function tool missing required fields`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (validTools.length < tools.length) {
|
||||||
|
log.info(`Filtered out ${tools.length - validTools.length} invalid tools`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tools to Anthropic format
|
||||||
|
return validTools.map((tool: any) => {
|
||||||
|
// Convert from OpenAI format to Anthropic format
|
||||||
|
if (tool.type === 'function' && tool.function) {
|
||||||
|
return {
|
||||||
|
name: tool.function.name,
|
||||||
|
description: tool.function.description || '',
|
||||||
|
input_schema: tool.function.parameters || {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle already converted Anthropic format (from our temporary fix)
|
||||||
|
if (tool.type === 'custom' && tool.custom) {
|
||||||
|
return {
|
||||||
|
name: tool.custom.name,
|
||||||
|
description: tool.custom.description || '',
|
||||||
|
input_schema: tool.custom.parameters || {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the tool is already in the correct Anthropic format
|
||||||
|
if (tool.name && (tool.input_schema || tool.parameters)) {
|
||||||
|
return {
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description || '',
|
||||||
|
input_schema: tool.input_schema || tool.parameters
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error(`Unhandled tool format encountered`);
|
||||||
|
return null;
|
||||||
|
}).filter(Boolean); // Filter out any null values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user