mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 02:52:27 +08:00
add anthropic options as well
This commit is contained in:
parent
4a4eac6f25
commit
c40c702761
@ -60,6 +60,20 @@ interface OpenAIModelResponse {
|
||||
}>;
|
||||
}
|
||||
|
||||
interface AnthropicModelResponse {
|
||||
success: boolean;
|
||||
chatModels: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}>;
|
||||
embeddingModels: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default class AiSettingsWidget extends OptionsWidget {
|
||||
private statsRefreshInterval: NodeJS.Timeout | null = null;
|
||||
private indexRebuildRefreshInterval: NodeJS.Timeout | null = null;
|
||||
@ -221,6 +235,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
||||
<option value="claude-3-haiku-20240307">Claude 3 Haiku</option>
|
||||
</select>
|
||||
<div class="form-text">${t("ai_llm.anthropic_model_description")}</div>
|
||||
<button class="btn btn-sm btn-outline-secondary refresh-anthropic-models">${t("ai_llm.refresh_models")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -623,6 +638,63 @@ export default class AiSettingsWidget extends OptionsWidget {
|
||||
}
|
||||
});
|
||||
|
||||
// Anthropic models refresh button
|
||||
const $refreshAnthropicModels = this.$widget.find('.refresh-anthropic-models');
|
||||
$refreshAnthropicModels.on('click', async () => {
|
||||
$refreshAnthropicModels.prop('disabled', true);
|
||||
$refreshAnthropicModels.html(`<i class="spinner-border spinner-border-sm"></i>`);
|
||||
|
||||
try {
|
||||
const anthropicBaseUrl = this.$widget.find('.anthropic-base-url').val() as string;
|
||||
const response = await server.post<AnthropicModelResponse>('anthropic/list-models', { baseUrl: anthropicBaseUrl });
|
||||
|
||||
if (response && response.success) {
|
||||
// Update the chat models dropdown
|
||||
if (response.chatModels?.length > 0) {
|
||||
const $chatModelSelect = this.$widget.find('.anthropic-default-model');
|
||||
const currentChatValue = $chatModelSelect.val();
|
||||
|
||||
// Clear existing options
|
||||
$chatModelSelect.empty();
|
||||
|
||||
// Sort models by name
|
||||
const sortedChatModels = [...response.chatModels].sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
// Add models to the dropdown
|
||||
sortedChatModels.forEach(model => {
|
||||
$chatModelSelect.append(`<option value="${model.id}">${model.name}</option>`);
|
||||
});
|
||||
|
||||
// Try to restore the previously selected value
|
||||
if (currentChatValue) {
|
||||
$chatModelSelect.val(currentChatValue);
|
||||
// If the value doesn't exist anymore, select the first option
|
||||
if (!$chatModelSelect.val()) {
|
||||
$chatModelSelect.prop('selectedIndex', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle embedding models if they exist
|
||||
if (response.embeddingModels?.length > 0) {
|
||||
toastService.showMessage(`Found ${response.embeddingModels.length} Anthropic embedding models.`);
|
||||
}
|
||||
|
||||
// Show success message
|
||||
const totalModels = (response.chatModels?.length || 0) + (response.embeddingModels?.length || 0);
|
||||
toastService.showMessage(`${totalModels} Anthropic models found.`);
|
||||
} else {
|
||||
toastService.showError(`No Anthropic models found. Please check your API key and settings.`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error fetching Anthropic models:`, e);
|
||||
toastService.showError(`Error fetching Anthropic models: ${e}`);
|
||||
} finally {
|
||||
$refreshAnthropicModels.prop('disabled', false);
|
||||
$refreshAnthropicModels.html(`<span class="bx bx-refresh"></span>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Embedding options event handlers
|
||||
const $embeddingAutoUpdateEnabled = this.$widget.find('.embedding-auto-update-enabled');
|
||||
$embeddingAutoUpdateEnabled.on('change', async () => {
|
||||
|
@ -1149,6 +1149,7 @@
|
||||
"openai_url_description": "Default: https://api.openai.com/v1",
|
||||
"anthropic_configuration": "Anthropic Configuration",
|
||||
"anthropic_model_description": "Examples: claude-3-opus-20240229, claude-3-sonnet-20240229",
|
||||
"anthropic_embedding_model_description": "Anthropic embedding model (not available yet)",
|
||||
"anthropic_url_description": "Default: https://api.anthropic.com/v1",
|
||||
"ollama_configuration": "Ollama Configuration",
|
||||
"enable_ollama": "Enable Ollama",
|
||||
|
74
src/routes/api/anthropic.ts
Normal file
74
src/routes/api/anthropic.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import axios from 'axios';
|
||||
import options from "../../services/options.js";
|
||||
import log from "../../services/log.js";
|
||||
import type { Request, Response } from "express";
|
||||
|
||||
/**
|
||||
* List available models from Anthropic
|
||||
*/
|
||||
async function listModels(req: Request, res: Response) {
|
||||
try {
|
||||
const { baseUrl } = req.body;
|
||||
|
||||
// Use provided base URL or default from options
|
||||
const anthropicBaseUrl = baseUrl || await options.getOption('anthropicBaseUrl') || 'https://api.anthropic.com/v1';
|
||||
const apiKey = await options.getOption('anthropicApiKey');
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('Anthropic API key is not configured');
|
||||
}
|
||||
|
||||
// Call Anthropic API to get models
|
||||
const response = await axios.get(`${anthropicBaseUrl}/models`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': apiKey,
|
||||
'anthropic-version': '2023-06-01'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// Process the models
|
||||
const allModels = response.data.models || [];
|
||||
|
||||
// Separate models into chat models and embedding models
|
||||
const chatModels = allModels
|
||||
.filter((model: any) =>
|
||||
// Claude models are for chat
|
||||
model.id.includes('claude')
|
||||
)
|
||||
.map((model: any) => ({
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
type: 'chat'
|
||||
}));
|
||||
|
||||
// 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
|
||||
};
|
@ -87,6 +87,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
"openaiBaseUrl",
|
||||
"anthropicApiKey",
|
||||
"anthropicDefaultModel",
|
||||
"anthropicEmbeddingModel",
|
||||
"anthropicBaseUrl",
|
||||
"ollamaEnabled",
|
||||
"ollamaBaseUrl",
|
||||
|
@ -63,6 +63,7 @@ import shareRoutes from "../share/routes.js";
|
||||
import embeddingsRoute from "./api/embeddings.js";
|
||||
import ollamaRoute from "./api/ollama.js";
|
||||
import openaiRoute from "./api/openai.js";
|
||||
import anthropicRoute from "./api/anthropic.js";
|
||||
import llmRoute from "./api/llm.js";
|
||||
|
||||
import etapiAuthRoutes from "../etapi/auth.js";
|
||||
@ -412,6 +413,9 @@ function register(app: express.Application) {
|
||||
// OpenAI API endpoints
|
||||
route(PST, "/api/openai/list-models", [auth.checkApiAuth, csrfMiddleware], openaiRoute.listModels, apiResultHandler);
|
||||
|
||||
// Anthropic API endpoints
|
||||
route(PST, "/api/anthropic/list-models", [auth.checkApiAuth, csrfMiddleware], anthropicRoute.listModels, apiResultHandler);
|
||||
|
||||
// API Documentation
|
||||
apiDocsRoute.register(app);
|
||||
|
||||
|
@ -176,6 +176,7 @@ const defaultOptions: DefaultOption[] = [
|
||||
{ name: "openaiBaseUrl", value: "https://api.openai.com/v1", isSynced: true },
|
||||
{ name: "anthropicApiKey", value: "", isSynced: false },
|
||||
{ name: "anthropicDefaultModel", value: "claude-3-opus-20240229", isSynced: true },
|
||||
{ name: "anthropicEmbeddingModel", value: "", isSynced: true },
|
||||
{ name: "anthropicBaseUrl", value: "https://api.anthropic.com/v1", isSynced: true },
|
||||
{ name: "ollamaEnabled", value: "false", isSynced: true },
|
||||
{ name: "ollamaDefaultModel", value: "llama3", isSynced: true },
|
||||
|
@ -57,6 +57,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
||||
openaiBaseUrl: string;
|
||||
anthropicApiKey: string;
|
||||
anthropicDefaultModel: string;
|
||||
anthropicEmbeddingModel: string;
|
||||
anthropicBaseUrl: string;
|
||||
ollamaEnabled: boolean;
|
||||
ollamaBaseUrl: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user