mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-08-11 19:22:31 +08:00
anthropic works
This commit is contained in:
parent
654ed4706e
commit
44b6734034
@ -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
|
||||||
|
@ -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
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user