anthropic works

This commit is contained in:
perf3ct 2025-03-26 04:13:04 +00:00
parent 654ed4706e
commit 44b6734034
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
2 changed files with 82 additions and 15 deletions

View File

@ -3,6 +3,21 @@ import options from "../../services/options.js";
import log from "../../services/log.js"; import log from "../../services/log.js";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
// 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;
}
/** /**
* List available models from Anthropic * List available models from Anthropic
*/ */
@ -10,20 +25,26 @@ async function listModels(req: Request, res: Response) {
try { try {
const { baseUrl } = req.body; const { baseUrl } = req.body;
// Use provided base URL or default from options // Use provided base URL or default from options, and ensure correct formatting
const anthropicBaseUrl = baseUrl || await options.getOption('anthropicBaseUrl') || 'https://api.anthropic.com/v1'; let anthropicBaseUrl = baseUrl || await options.getOption('anthropicBaseUrl') || 'https://api.anthropic.com';
// Ensure base URL doesn't already include '/v1' and is properly formatted
anthropicBaseUrl = anthropicBaseUrl.replace(/\/+$/, '').replace(/\/v1$/, '');
const apiKey = await options.getOption('anthropicApiKey'); const apiKey = await options.getOption('anthropicApiKey');
if (!apiKey) { if (!apiKey) {
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 at: ${anthropicBaseUrl}/v1/models`);
// Call Anthropic API to get models // Call Anthropic API to get models
const response = await axios.get(`${anthropicBaseUrl}/models`, { const response = await axios.get(`${anthropicBaseUrl}/v1/models`, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'x-api-key': apiKey, 'X-Api-Key': apiKey,
'anthropic-version': '2023-06-01' 'anthropic-version': '2023-06-01',
'anthropic-beta': 'messages-2023-12-15'
}, },
timeout: 10000 timeout: 10000
}); });
@ -31,17 +52,41 @@ async function listModels(req: Request, res: Response) {
// Process the models // Process the models
const allModels = response.data.models || []; const allModels = response.data.models || [];
// Log available models
log.info(`Found ${allModels.length} models from Anthropic: ${allModels.map((m: any) => m.id).join(', ')}`);
// Separate models into chat models and embedding models // Separate models into chat models and embedding models
const chatModels = allModels const chatModels = allModels
.filter((model: any) => .filter((model: any) =>
// Claude models are for chat // Claude models are for chat
model.id.includes('claude') model.id.includes('claude')
) )
.map((model: any) => ({ .map((model: any) => {
id: model.id, // Get a simplified name for display purposes
name: model.id, let displayName = model.id;
type: 'chat' // 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 [simpleName, fullName] of Object.entries(MODEL_MAPPING)) {
// Check if this model is already in our list
if (!chatModels.some((m: AnthropicModel) => m.id === fullName)) {
chatModels.push({
id: fullName,
name: simpleName,
type: 'chat'
});
}
}
// Note: Anthropic might not have embedding models yet, but we'll include this for future compatibility // Note: Anthropic might not have embedding models yet, but we'll include this for future compatibility
const embeddingModels = allModels const embeddingModels = allModels

View File

@ -3,6 +3,14 @@ import { BaseAIService } from '../base_ai_service.js';
import type { ChatCompletionOptions, ChatResponse, Message } from '../ai_interface.js'; import type { ChatCompletionOptions, ChatResponse, Message } from '../ai_interface.js';
export class AnthropicService extends BaseAIService { export class AnthropicService extends BaseAIService {
// Map of simplified model names to full model names with versions
private static 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'
};
constructor() { constructor() {
super('Anthropic'); super('Anthropic');
} }
@ -18,7 +26,14 @@ export class AnthropicService extends BaseAIService {
const apiKey = options.getOption('anthropicApiKey'); const apiKey = options.getOption('anthropicApiKey');
const baseUrl = options.getOption('anthropicBaseUrl') || 'https://api.anthropic.com'; const baseUrl = options.getOption('anthropicBaseUrl') || 'https://api.anthropic.com';
const model = opts.model || options.getOption('anthropicDefaultModel') || 'claude-3-haiku-20240307'; let model = opts.model || options.getOption('anthropicDefaultModel') || 'claude-3-haiku-20240307';
// Apply model name mapping if needed
if (AnthropicService.MODEL_MAPPING[model]) {
model = AnthropicService.MODEL_MAPPING[model];
console.log(`Mapped model name to: ${model}`);
}
const temperature = opts.temperature !== undefined const temperature = opts.temperature !== undefined
? opts.temperature ? opts.temperature
: parseFloat(options.getOption('aiTemperature') || '0.7'); : parseFloat(options.getOption('aiTemperature') || '0.7');
@ -29,14 +44,20 @@ export class AnthropicService extends BaseAIService {
const formattedMessages = this.formatMessages(messages, systemPrompt); const formattedMessages = this.formatMessages(messages, systemPrompt);
try { try {
const endpoint = `${baseUrl.replace(/\/+$/, '')}/v1/messages`; // Ensure base URL doesn't already include '/v1' and build the complete endpoint
const cleanBaseUrl = baseUrl.replace(/\/+$/, '').replace(/\/v1$/, '');
const endpoint = `${cleanBaseUrl}/v1/messages`;
console.log(`Anthropic API endpoint: ${endpoint}`);
console.log(`Using model: ${model}`);
const response = await fetch(endpoint, { const response = await fetch(endpoint, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'x-api-key': apiKey, 'X-Api-Key': apiKey,
'anthropic-version': '2023-06-01' 'anthropic-version': '2023-06-01',
'anthropic-beta': 'messages-2023-12-15'
}, },
body: JSON.stringify({ body: JSON.stringify({
model, model,
@ -49,6 +70,7 @@ export class AnthropicService extends BaseAIService {
if (!response.ok) { if (!response.ok) {
const errorBody = await response.text(); const errorBody = await response.text();
console.error(`Anthropic API error (${response.status}): ${errorBody}`);
throw new Error(`Anthropic API error: ${response.status} ${response.statusText} - ${errorBody}`); throw new Error(`Anthropic API error: ${response.status} ${response.statusText} - ${errorBody}`);
} }
@ -82,7 +104,7 @@ export class AnthropicService extends BaseAIService {
// Format remaining messages for Anthropic's API // Format remaining messages for Anthropic's API
const formattedMessages = nonSystemMessages.map(m => ({ const formattedMessages = nonSystemMessages.map(m => ({
role: m.role, role: m.role === 'user' ? 'user' : 'assistant',
content: m.content content: m.content
})); }));