Initial commit
This commit is contained in:
296
templates/tool-use-advanced.ts
Normal file
296
templates/tool-use-advanced.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { betaZodTool } from '@anthropic-ai/sdk/helpers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY || '',
|
||||
});
|
||||
|
||||
// Example 1: Using Zod schemas with betaZodTool
|
||||
const weatherTool = betaZodTool({
|
||||
name: 'get_weather',
|
||||
inputSchema: z.object({
|
||||
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
|
||||
unit: z.enum(['celsius', 'fahrenheit']).optional().describe('Temperature unit'),
|
||||
}),
|
||||
description: 'Get the current weather in a given location',
|
||||
run: async (input) => {
|
||||
// Mock implementation - replace with actual API call
|
||||
console.log(`Fetching weather for ${input.location}...`);
|
||||
const temp = input.unit === 'celsius' ? 22 : 72;
|
||||
return `The weather in ${input.location} is sunny and ${temp}°${input.unit || 'F'}`;
|
||||
},
|
||||
});
|
||||
|
||||
const searchTool = betaZodTool({
|
||||
name: 'search_web',
|
||||
inputSchema: z.object({
|
||||
query: z.string().describe('The search query'),
|
||||
max_results: z.number().int().min(1).max(10).optional().describe('Maximum number of results'),
|
||||
}),
|
||||
description: 'Search the web for information',
|
||||
run: async (input) => {
|
||||
console.log(`Searching for: ${input.query}...`);
|
||||
// Mock implementation
|
||||
return `Found ${input.max_results || 5} results for "${input.query}":
|
||||
1. Example result 1
|
||||
2. Example result 2
|
||||
3. Example result 3`;
|
||||
},
|
||||
});
|
||||
|
||||
const calculatorTool = betaZodTool({
|
||||
name: 'calculate',
|
||||
inputSchema: z.object({
|
||||
expression: z.string().describe('Mathematical expression to evaluate'),
|
||||
}),
|
||||
description: 'Evaluate a mathematical expression',
|
||||
run: async (input) => {
|
||||
try {
|
||||
// WARNING: eval is dangerous - this is just for demonstration
|
||||
// In production, use a safe math parser like math.js
|
||||
const result = eval(input.expression);
|
||||
return `${input.expression} = ${result}`;
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid expression: ${input.expression}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Example 2: Using toolRunner for automatic execution
|
||||
async function automaticToolExecution() {
|
||||
const finalMessage = await anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1000,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What is the weather in Tokyo? Also, search for "best sushi restaurants in Tokyo"',
|
||||
},
|
||||
],
|
||||
tools: [weatherTool, searchTool],
|
||||
});
|
||||
|
||||
console.log('\nFinal response:');
|
||||
for (const block of finalMessage.content) {
|
||||
if (block.type === 'text') {
|
||||
console.log(block.text);
|
||||
}
|
||||
}
|
||||
|
||||
return finalMessage;
|
||||
}
|
||||
|
||||
// Example 3: Streaming with tools
|
||||
async function streamingWithTools() {
|
||||
const runner = anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1000,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Calculate 123 * 456, then tell me about the result',
|
||||
},
|
||||
],
|
||||
tools: [calculatorTool],
|
||||
stream: true,
|
||||
});
|
||||
|
||||
console.log('Streaming response:');
|
||||
|
||||
// Iterate through messages as they arrive
|
||||
for await (const messageStream of runner) {
|
||||
// Each message can have multiple events
|
||||
for await (const event of messageStream) {
|
||||
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
||||
process.stdout.write(event.delta.text);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n\nMessage completed');
|
||||
}
|
||||
|
||||
// Get final result
|
||||
const result = await runner;
|
||||
console.log('\nFinal result:', result);
|
||||
}
|
||||
|
||||
// Example 4: Complex tool chain
|
||||
const databaseTool = betaZodTool({
|
||||
name: 'query_database',
|
||||
inputSchema: z.object({
|
||||
query: z.string().describe('SQL query to execute'),
|
||||
}),
|
||||
description: 'Query the database',
|
||||
run: async (input) => {
|
||||
console.log(`Executing SQL: ${input.query}`);
|
||||
// Mock database response
|
||||
return JSON.stringify([
|
||||
{ id: 1, name: 'Product A', price: 29.99 },
|
||||
{ id: 2, name: 'Product B', price: 49.99 },
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
const emailTool = betaZodTool({
|
||||
name: 'send_email',
|
||||
inputSchema: z.object({
|
||||
to: z.string().email().describe('Recipient email address'),
|
||||
subject: z.string().describe('Email subject'),
|
||||
body: z.string().describe('Email body'),
|
||||
}),
|
||||
description: 'Send an email',
|
||||
run: async (input) => {
|
||||
console.log(`Sending email to ${input.to}...`);
|
||||
// Mock email sending
|
||||
return `Email sent successfully to ${input.to}`;
|
||||
},
|
||||
});
|
||||
|
||||
async function complexToolChain() {
|
||||
const finalMessage = await anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 2000,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content:
|
||||
'Query the database for all products, calculate their average price, and send me an email with the results to test@example.com',
|
||||
},
|
||||
],
|
||||
tools: [databaseTool, calculatorTool, emailTool],
|
||||
});
|
||||
|
||||
console.log('\nTool chain completed');
|
||||
for (const block of finalMessage.content) {
|
||||
if (block.type === 'text') {
|
||||
console.log(block.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example 5: Tool with max iterations limit
|
||||
async function toolsWithMaxIterations() {
|
||||
try {
|
||||
const finalMessage = await anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1000,
|
||||
max_iterations: 3, // Limit tool execution loops
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Search for "quantum computing" and then search for each result',
|
||||
},
|
||||
],
|
||||
tools: [searchTool],
|
||||
});
|
||||
|
||||
console.log('Completed with max_iterations limit');
|
||||
for (const block of finalMessage.content) {
|
||||
if (block.type === 'text') {
|
||||
console.log(block.text);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Max iterations reached or error occurred:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Example 6: Custom tool runner with manual control
|
||||
async function manualToolRunner() {
|
||||
const runner = anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1000,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'What is 15 * 27?',
|
||||
},
|
||||
],
|
||||
tools: [calculatorTool],
|
||||
});
|
||||
|
||||
// Manually iterate through messages
|
||||
for await (const message of runner) {
|
||||
console.log('\nReceived message');
|
||||
console.log('Stop reason:', message.stop_reason);
|
||||
|
||||
// Generate tool response if needed
|
||||
const toolResponse = await runner.generateToolResponse();
|
||||
if (toolResponse) {
|
||||
console.log('Tool results:', toolResponse.content);
|
||||
}
|
||||
|
||||
// Can inspect and modify the conversation here
|
||||
console.log('Current params:', runner.params);
|
||||
}
|
||||
|
||||
// Wait for completion
|
||||
const finalMessage = await runner.done();
|
||||
console.log('\nFinal message:', finalMessage);
|
||||
}
|
||||
|
||||
// Example 7: Error recovery in tools
|
||||
const unreliableTool = betaZodTool({
|
||||
name: 'unreliable_api',
|
||||
inputSchema: z.object({
|
||||
data: z.string(),
|
||||
}),
|
||||
description: 'An API that sometimes fails',
|
||||
run: async (input) => {
|
||||
// Randomly fail to demonstrate error handling
|
||||
if (Math.random() < 0.3) {
|
||||
throw new Error('API temporarily unavailable');
|
||||
}
|
||||
return `Processed: ${input.data}`;
|
||||
},
|
||||
});
|
||||
|
||||
async function toolWithErrorRecovery() {
|
||||
try {
|
||||
const finalMessage = await anthropic.beta.messages.toolRunner({
|
||||
model: 'claude-sonnet-4-5-20250929',
|
||||
max_tokens: 1000,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Process this data with the unreliable API: "test data"',
|
||||
},
|
||||
],
|
||||
tools: [unreliableTool],
|
||||
});
|
||||
|
||||
console.log('Success:', finalMessage.content);
|
||||
} catch (error) {
|
||||
console.error('Tool execution failed:', error);
|
||||
// Implement retry logic or fallback
|
||||
}
|
||||
}
|
||||
|
||||
// Run examples
|
||||
if (require.main === module) {
|
||||
console.log('=== Automatic Tool Execution ===\n');
|
||||
automaticToolExecution()
|
||||
.then(() => {
|
||||
console.log('\n\n=== Streaming with Tools ===\n');
|
||||
return streamingWithTools();
|
||||
})
|
||||
.then(() => {
|
||||
console.log('\n\n=== Complex Tool Chain ===\n');
|
||||
return complexToolChain();
|
||||
})
|
||||
.then(() => {
|
||||
console.log('\n\n=== Manual Tool Runner ===\n');
|
||||
return manualToolRunner();
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
export {
|
||||
automaticToolExecution,
|
||||
streamingWithTools,
|
||||
complexToolChain,
|
||||
toolsWithMaxIterations,
|
||||
manualToolRunner,
|
||||
toolWithErrorRecovery,
|
||||
};
|
||||
Reference in New Issue
Block a user