Update anthropic.ts
This commit is contained in:
parent
364f85bda8
commit
2898775c6f
1 changed files with 105 additions and 22 deletions
|
@ -1,41 +1,80 @@
|
|||
import { ChatAnthropic } from '@langchain/anthropic';
|
||||
import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
|
||||
import { getAnthropicApiKey } from '../../config';
|
||||
import logger from '../../utils/logger';
|
||||
import axios from 'axios';
|
||||
|
||||
class RetryingChatAnthropic extends ChatAnthropic {
|
||||
constructor(config) {
|
||||
maxRetries: number;
|
||||
baseDelay: number;
|
||||
|
||||
constructor(config: ConstructorParameters<typeof ChatAnthropic>[0] & { maxRetries?: number; baseDelay?: number }) {
|
||||
super(config);
|
||||
this.maxRetries = config.maxRetries || 3;
|
||||
this.baseDelay = config.baseDelay || 1000; // 1 second
|
||||
this.baseDelay = config.baseDelay || 1000;
|
||||
}
|
||||
|
||||
async _call(messages, options, runManager) {
|
||||
let lastError;
|
||||
async call(messages: BaseMessage[], options?: this['ParsedCallOptions']): Promise<BaseMessage> {
|
||||
let lastError: unknown;
|
||||
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
||||
try {
|
||||
return await super._call(messages, options, runManager);
|
||||
logger.info(`Attempt ${attempt + 1}: Making API call to Anthropic...`);
|
||||
const result = await super.call(messages, options);
|
||||
logger.info(`Attempt ${attempt + 1}: API call successful.`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error.name !== 'APIConnectionError' || attempt === this.maxRetries) {
|
||||
throw error; // Rethrow if it's not an APIConnectionError or if we're out of retries
|
||||
}
|
||||
lastError = error;
|
||||
logger.error(`Full error object: ${JSON.stringify(error, Object.getOwnPropertyNames(error))}`);
|
||||
|
||||
if (this.isRetryableError(error) && attempt < this.maxRetries) {
|
||||
const delay = this.calculateDelay(attempt);
|
||||
logger.warn(`APIConnectionError in ${this.model}: ${error.message}. Retrying in ${delay}ms. Attempt ${attempt + 1} of ${this.maxRetries + 1}`);
|
||||
logger.warn(`Attempt ${attempt + 1}: Retryable error occurred. Retrying in ${delay}ms.`);
|
||||
await this.wait(delay);
|
||||
} else {
|
||||
logger.error(`Attempt ${attempt + 1}: Non-retryable error or max retries reached.`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
throw lastError; // This line should never be reached, but it's here for completeness
|
||||
}
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
calculateDelay(attempt) {
|
||||
const jitter = Math.random() * 0.1 * this.baseDelay; // 10% jitter
|
||||
return Math.min(
|
||||
(Math.pow(2, attempt) * this.baseDelay) + jitter,
|
||||
30000 // Cap at 30 seconds
|
||||
);
|
||||
private isRetryableError(error: unknown): boolean {
|
||||
if (error instanceof Error) {
|
||||
// Check for network-related errors
|
||||
if (error.message.includes('APIConnectionError') ||
|
||||
error.message.includes('ETIMEDOUT') ||
|
||||
error.message.includes('ENOTFOUND') ||
|
||||
error.message.includes('EBADF') ||
|
||||
error.message.includes('Connection error')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
wait(ms) {
|
||||
// Check for specific API errors that might be retryable
|
||||
if (error.message.includes('rate limit') ||
|
||||
error.message.includes('timeout') ||
|
||||
error.message.includes('temporary failure')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Anthropic-specific error types
|
||||
const anthropicError = error as any;
|
||||
if (anthropicError.type === 'not_found_error' ||
|
||||
anthropicError.type === 'service_unavailable' ||
|
||||
anthropicError.type === 'timeout_error') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private calculateDelay(attempt: number): number {
|
||||
const jitter = Math.random() * 0.1 * this.baseDelay;
|
||||
return Math.min((Math.pow(2, attempt) * this.baseDelay) + jitter, 30000);
|
||||
}
|
||||
|
||||
private wait(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
@ -54,22 +93,66 @@ export const loadAnthropicChatModels = async () => {
|
|||
{ name: 'Claude 3 Haiku', model: 'claude-3-haiku-20240307' }
|
||||
];
|
||||
|
||||
const chatModels = {};
|
||||
const chatModels: Record<string, RetryingChatAnthropic> = {};
|
||||
|
||||
for (const config of modelConfigs) {
|
||||
try {
|
||||
chatModels[config.name] = new RetryingChatAnthropic({
|
||||
temperature: 0.5,
|
||||
anthropicApiKey,
|
||||
model: config.model,
|
||||
modelName: config.model,
|
||||
maxRetries: 3,
|
||||
baseDelay: 1000,
|
||||
});
|
||||
logger.info(`Successfully initialized ${config.name} with retry logic`);
|
||||
} catch (err) {
|
||||
logger.error(`Error initializing ${config.name}: ${err.message}`);
|
||||
logger.error(`Error initializing ${config.name}: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return chatModels;
|
||||
};
|
||||
|
||||
async function getPublicIP(): Promise<string> {
|
||||
try {
|
||||
const response = await axios.get('https://api.ipify.org?format=json');
|
||||
return response.data.ip;
|
||||
} catch (error) {
|
||||
logger.error('Error fetching public IP:', error);
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage (can be commented out or removed if not needed)
|
||||
async function testRetryLogic(): Promise<void> {
|
||||
try {
|
||||
const chatModels = await loadAnthropicChatModels();
|
||||
const claude = chatModels['Claude 3.5 Sonnet'];
|
||||
if (!claude) {
|
||||
throw new Error('Claude 3.5 Sonnet model not initialized');
|
||||
}
|
||||
|
||||
logger.info("Model initialized successfully. Attempting API call...");
|
||||
|
||||
const result = await claude.call([
|
||||
new HumanMessage("Hello, Claude! Please provide a brief summary of your capabilities.")
|
||||
]);
|
||||
|
||||
logger.info("\nAPI call succeeded. Response:");
|
||||
logger.info(result.content);
|
||||
} catch (error) {
|
||||
logger.error("\nError occurred:");
|
||||
if (error instanceof Error) {
|
||||
logger.error("Error name:", error.name);
|
||||
logger.error("Error message:", error.message);
|
||||
logger.error("Error stack:", error.stack);
|
||||
} else if (typeof error === 'object' && error !== null) {
|
||||
logger.error("Anthropic API Error:", JSON.stringify(error, null, 2));
|
||||
} else {
|
||||
logger.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment the following line to run the test
|
||||
// testRetryLogic();
|
||||
|
|
Loading…
Add table
Reference in a new issue