297 lines
8.1 KiB
TypeScript
297 lines
8.1 KiB
TypeScript
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,
|
|
};
|