mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01: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;
 | 
				
			||||||
 | 
					                // 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'
 | 
					                    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