Notes/src/routes/api/anthropic.ts

121 lines
4.3 KiB
TypeScript
Raw Normal View History

2025-03-17 20:17:28 +00:00
import axios from 'axios';
import options from "../../services/options.js";
import log from "../../services/log.js";
import type { Request, Response } from "express";
import { PROVIDER_CONSTANTS } from '../../services/llm/constants/provider_constants.js';
2025-03-17 20:17:28 +00:00
2025-03-26 04:13:04 +00:00
// Map of simplified model names to full model names with versions
const MODEL_MAPPING: Record<string, string> = {
'claude-3-opus': 'claude-3-opus-20240229',
'claude-3-sonnet': 'claude-3-sonnet-20240229',
'claude-3-haiku': 'claude-3-haiku-20240307',
'claude-2': 'claude-2.1'
};
// Interface for Anthropic model entries
interface AnthropicModel {
id: string;
name: string;
type: string;
}
2025-03-17 20:17:28 +00:00
/**
* List available models from Anthropic
*/
async function listModels(req: Request, res: Response) {
try {
const { baseUrl } = req.body;
2025-03-26 04:13:04 +00:00
// Use provided base URL or default from options, and ensure correct formatting
let anthropicBaseUrl = baseUrl || await options.getOption('anthropicBaseUrl') || PROVIDER_CONSTANTS.ANTHROPIC.BASE_URL;
2025-03-26 04:13:04 +00:00
// Ensure base URL doesn't already include '/v1' and is properly formatted
anthropicBaseUrl = anthropicBaseUrl.replace(/\/+$/, '').replace(/\/v1$/, '');
2025-03-17 20:17:28 +00:00
const apiKey = await options.getOption('anthropicApiKey');
if (!apiKey) {
throw new Error('Anthropic API key is not configured');
}
2025-03-26 04:13:04 +00:00
log.info(`Listing models from Anthropic API at: ${anthropicBaseUrl}/v1/models`);
2025-03-17 20:17:28 +00:00
// Call Anthropic API to get models
2025-03-26 04:13:04 +00:00
const response = await axios.get(`${anthropicBaseUrl}/v1/models`, {
2025-03-17 20:17:28 +00:00
headers: {
'Content-Type': 'application/json',
2025-03-26 04:13:04 +00:00
'X-Api-Key': apiKey,
'anthropic-version': PROVIDER_CONSTANTS.ANTHROPIC.API_VERSION,
'anthropic-beta': PROVIDER_CONSTANTS.ANTHROPIC.BETA_VERSION
2025-03-17 20:17:28 +00:00
},
timeout: 10000
});
// Process the models
const allModels = response.data.models || [];
2025-03-26 04:13:04 +00:00
// Log available models
log.info(`Found ${allModels.length} models from Anthropic: ${allModels.map((m: any) => m.id).join(', ')}`);
2025-03-17 20:17:28 +00:00
// Separate models into chat models and embedding models
const chatModels = allModels
.filter((model: any) =>
// Claude models are for chat
model.id.includes('claude')
)
2025-03-26 04:13:04 +00:00
.map((model: any) => {
// 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) {
2025-03-26 04:13:04 +00:00
// Check if this model is already in our list
if (!chatModels.some((m: AnthropicModel) => m.id === model.id)) {
2025-03-26 04:13:04 +00:00
chatModels.push({
id: model.id,
name: model.name,
2025-03-26 04:13:04 +00:00
type: 'chat'
});
}
}
2025-03-17 20:17:28 +00:00
// Note: Anthropic might not have embedding models yet, but we'll include this for future compatibility
const embeddingModels = allModels
.filter((model: any) =>
// If Anthropic releases embedding models, they'd likely include 'embed' in the name
model.id.includes('embed')
)
.map((model: any) => ({
id: model.id,
name: model.id,
type: 'embedding'
}));
// Return the models list
return {
success: true,
chatModels,
embeddingModels
};
} catch (error: any) {
log.error(`Error listing Anthropic models: ${error.message || 'Unknown error'}`);
// Properly throw the error to be handled by the global error handler
throw new Error(`Failed to list Anthropic models: ${error.message || 'Unknown error'}`);
}
}
export default {
listModels
};