mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
feat(llm): remove provider "list" in favor of only 1 provider selection for embedding/chat
This commit is contained in:
parent
8b2d951ad1
commit
5a25fb51d9
@ -65,7 +65,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
|
|
||||||
// Core AI options
|
// Core AI options
|
||||||
this.setupChangeHandler('.ai-enabled', 'aiEnabled', true, true);
|
this.setupChangeHandler('.ai-enabled', 'aiEnabled', true, true);
|
||||||
this.setupChangeHandler('.ai-provider-precedence', 'aiProviderPrecedence', true);
|
this.setupChangeHandler('.ai-chat-provider', 'aiChatProvider', true);
|
||||||
this.setupChangeHandler('.ai-temperature', 'aiTemperature');
|
this.setupChangeHandler('.ai-temperature', 'aiTemperature');
|
||||||
this.setupChangeHandler('.ai-system-prompt', 'aiSystemPrompt');
|
this.setupChangeHandler('.ai-system-prompt', 'aiSystemPrompt');
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
this.setupChangeHandler('.enable-automatic-indexing', 'enableAutomaticIndexing', false, true);
|
this.setupChangeHandler('.enable-automatic-indexing', 'enableAutomaticIndexing', false, true);
|
||||||
this.setupChangeHandler('.embedding-similarity-threshold', 'embeddingSimilarityThreshold');
|
this.setupChangeHandler('.embedding-similarity-threshold', 'embeddingSimilarityThreshold');
|
||||||
this.setupChangeHandler('.max-notes-per-llm-query', 'maxNotesPerLlmQuery');
|
this.setupChangeHandler('.max-notes-per-llm-query', 'maxNotesPerLlmQuery');
|
||||||
this.setupChangeHandler('.embedding-provider-precedence', 'embeddingProviderPrecedence', true);
|
this.setupChangeHandler('.ai-embedding-provider', 'aiEmbeddingProvider', true);
|
||||||
this.setupChangeHandler('.embedding-dimension-strategy', 'embeddingDimensionStrategy');
|
this.setupChangeHandler('.embedding-dimension-strategy', 'embeddingDimensionStrategy');
|
||||||
this.setupChangeHandler('.embedding-batch-size', 'embeddingBatchSize');
|
this.setupChangeHandler('.embedding-batch-size', 'embeddingBatchSize');
|
||||||
this.setupChangeHandler('.embedding-update-interval', 'embeddingUpdateInterval');
|
this.setupChangeHandler('.embedding-update-interval', 'embeddingUpdateInterval');
|
||||||
@ -194,42 +194,26 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get provider precedence
|
// Get selected chat provider
|
||||||
const providerPrecedence = (this.$widget.find('.ai-provider-precedence').val() as string || '').split(',');
|
const selectedChatProvider = this.$widget.find('.ai-chat-provider').val() as string;
|
||||||
|
|
||||||
// Check for OpenAI configuration if it's in the precedence list
|
// Check for configuration issues with the selected provider
|
||||||
const openaiWarnings: string[] = [];
|
const chatWarnings: string[] = [];
|
||||||
if (providerPrecedence.includes('openai')) {
|
|
||||||
|
if (selectedChatProvider === 'openai') {
|
||||||
const openaiApiKey = this.$widget.find('.openai-api-key').val();
|
const openaiApiKey = this.$widget.find('.openai-api-key').val();
|
||||||
if (!openaiApiKey) {
|
if (!openaiApiKey) {
|
||||||
openaiWarnings.push(t("ai_llm.empty_key_warning.openai"));
|
chatWarnings.push(t("ai_llm.empty_key_warning.openai"));
|
||||||
}
|
}
|
||||||
}
|
} else if (selectedChatProvider === 'anthropic') {
|
||||||
|
|
||||||
// Check for Anthropic configuration if it's in the precedence list
|
|
||||||
const anthropicWarnings: string[] = [];
|
|
||||||
if (providerPrecedence.includes('anthropic')) {
|
|
||||||
const anthropicApiKey = this.$widget.find('.anthropic-api-key').val();
|
const anthropicApiKey = this.$widget.find('.anthropic-api-key').val();
|
||||||
if (!anthropicApiKey) {
|
if (!anthropicApiKey) {
|
||||||
anthropicWarnings.push(t("ai_llm.empty_key_warning.anthropic"));
|
chatWarnings.push(t("ai_llm.empty_key_warning.anthropic"));
|
||||||
}
|
}
|
||||||
}
|
} else if (selectedChatProvider === 'ollama') {
|
||||||
|
|
||||||
// Check for Voyage configuration if it's in the precedence list
|
|
||||||
const voyageWarnings: string[] = [];
|
|
||||||
if (providerPrecedence.includes('voyage')) {
|
|
||||||
const voyageApiKey = this.$widget.find('.voyage-api-key').val();
|
|
||||||
if (!voyageApiKey) {
|
|
||||||
voyageWarnings.push(t("ai_llm.empty_key_warning.voyage"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for Ollama configuration if it's in the precedence list
|
|
||||||
const ollamaWarnings: string[] = [];
|
|
||||||
if (providerPrecedence.includes('ollama')) {
|
|
||||||
const ollamaBaseUrl = this.$widget.find('.ollama-base-url').val();
|
const ollamaBaseUrl = this.$widget.find('.ollama-base-url').val();
|
||||||
if (!ollamaBaseUrl) {
|
if (!ollamaBaseUrl) {
|
||||||
ollamaWarnings.push(t("ai_llm.ollama_no_url"));
|
chatWarnings.push(t("ai_llm.ollama_no_url"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,27 +222,20 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
const embeddingsEnabled = this.$widget.find('.enable-automatic-indexing').prop('checked');
|
const embeddingsEnabled = this.$widget.find('.enable-automatic-indexing').prop('checked');
|
||||||
|
|
||||||
if (embeddingsEnabled) {
|
if (embeddingsEnabled) {
|
||||||
const embeddingProviderPrecedence = (this.$widget.find('.embedding-provider-precedence').val() as string || '').split(',');
|
const selectedEmbeddingProvider = this.$widget.find('.ai-embedding-provider').val() as string;
|
||||||
|
|
||||||
if (embeddingProviderPrecedence.includes('openai') && !this.$widget.find('.openai-api-key').val()) {
|
if (selectedEmbeddingProvider === 'openai' && !this.$widget.find('.openai-api-key').val()) {
|
||||||
embeddingWarnings.push(t("ai_llm.empty_key_warning.openai"));
|
embeddingWarnings.push(t("ai_llm.empty_key_warning.openai"));
|
||||||
}
|
} else if (selectedEmbeddingProvider === 'voyage' && !this.$widget.find('.voyage-api-key').val()) {
|
||||||
|
|
||||||
if (embeddingProviderPrecedence.includes('voyage') && !this.$widget.find('.voyage-api-key').val()) {
|
|
||||||
embeddingWarnings.push(t("ai_llm.empty_key_warning.voyage"));
|
embeddingWarnings.push(t("ai_llm.empty_key_warning.voyage"));
|
||||||
}
|
} else if (selectedEmbeddingProvider === 'ollama' && !this.$widget.find('.ollama-base-url').val()) {
|
||||||
|
|
||||||
if (embeddingProviderPrecedence.includes('ollama') && !this.$widget.find('.ollama-base-url').val()) {
|
|
||||||
embeddingWarnings.push(t("ai_llm.empty_key_warning.ollama"));
|
embeddingWarnings.push(t("ai_llm.empty_key_warning.ollama"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all warnings
|
// Combine all warnings
|
||||||
const allWarnings = [
|
const allWarnings = [
|
||||||
...openaiWarnings,
|
...chatWarnings,
|
||||||
...anthropicWarnings,
|
|
||||||
...voyageWarnings,
|
|
||||||
...ollamaWarnings,
|
|
||||||
...embeddingWarnings
|
...embeddingWarnings
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -459,7 +436,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
this.$widget.find('.ai-enabled').prop('checked', options.aiEnabled !== 'false');
|
this.$widget.find('.ai-enabled').prop('checked', options.aiEnabled !== 'false');
|
||||||
this.$widget.find('.ai-temperature').val(options.aiTemperature || '0.7');
|
this.$widget.find('.ai-temperature').val(options.aiTemperature || '0.7');
|
||||||
this.$widget.find('.ai-system-prompt').val(options.aiSystemPrompt || '');
|
this.$widget.find('.ai-system-prompt').val(options.aiSystemPrompt || '');
|
||||||
this.$widget.find('.ai-provider-precedence').val(options.aiProviderPrecedence || 'openai,anthropic,ollama');
|
this.$widget.find('.ai-chat-provider').val(options.aiChatProvider || '');
|
||||||
|
|
||||||
// OpenAI Section
|
// OpenAI Section
|
||||||
this.$widget.find('.openai-api-key').val(options.openaiApiKey || '');
|
this.$widget.find('.openai-api-key').val(options.openaiApiKey || '');
|
||||||
@ -482,7 +459,7 @@ export default class AiSettingsWidget extends OptionsWidget {
|
|||||||
this.$widget.find('.ollama-embedding-model').val(options.ollamaEmbeddingModel || 'nomic-embed-text');
|
this.$widget.find('.ollama-embedding-model').val(options.ollamaEmbeddingModel || 'nomic-embed-text');
|
||||||
|
|
||||||
// Embedding Options
|
// Embedding Options
|
||||||
this.$widget.find('.embedding-provider-precedence').val(options.embeddingProviderPrecedence || 'openai,voyage,ollama,local');
|
this.$widget.find('.ai-embedding-provider').val(options.aiEmbeddingProvider || '');
|
||||||
this.$widget.find('.embedding-auto-update-enabled').prop('checked', options.embeddingAutoUpdateEnabled !== 'false');
|
this.$widget.find('.embedding-auto-update-enabled').prop('checked', options.embeddingAutoUpdateEnabled !== 'false');
|
||||||
this.$widget.find('.enable-automatic-indexing').prop('checked', options.enableAutomaticIndexing !== 'false');
|
this.$widget.find('.enable-automatic-indexing').prop('checked', options.enableAutomaticIndexing !== 'false');
|
||||||
this.$widget.find('.embedding-similarity-threshold').val(options.embeddingSimilarityThreshold || '0.75');
|
this.$widget.find('.embedding-similarity-threshold').val(options.embeddingSimilarityThreshold || '0.75');
|
||||||
|
@ -61,9 +61,14 @@ export const TPL = `
|
|||||||
<h4>${t("ai_llm.provider_configuration")}</h4>
|
<h4>${t("ai_llm.provider_configuration")}</h4>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>${t("ai_llm.provider_precedence")}</label>
|
<label>${t("ai_llm.chat_provider")}</label>
|
||||||
<input type="text" class="ai-provider-precedence form-control" placeholder="openai,anthropic,ollama">
|
<select class="ai-chat-provider form-control">
|
||||||
<div class="form-text">${t("ai_llm.provider_precedence_description")}</div>
|
<option value="">-- Select a provider --</option>
|
||||||
|
<option value="openai">OpenAI</option>
|
||||||
|
<option value="anthropic">Anthropic</option>
|
||||||
|
<option value="ollama">Ollama</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">${t("ai_llm.chat_provider_description")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -225,9 +230,15 @@ export const TPL = `
|
|||||||
<h4>${t("ai_llm.embeddings_configuration")}</h4>
|
<h4>${t("ai_llm.embeddings_configuration")}</h4>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="embedding-provider-label">${t("ai_llm.embedding_provider_precedence")}</label>
|
<label class="embedding-provider-label">${t("ai_llm.embedding_provider")}</label>
|
||||||
<input type="text" class="embedding-provider-precedence form-control" placeholder="openai,voyage,ollama,local">
|
<select class="ai-embedding-provider form-control">
|
||||||
<div class="form-text">${t("ai_llm.embedding_provider_precedence_description")}</div>
|
<option value="">-- Select a provider --</option>
|
||||||
|
<option value="openai">OpenAI</option>
|
||||||
|
<option value="voyage">Voyage</option>
|
||||||
|
<option value="ollama">Ollama</option>
|
||||||
|
<option value="local">Local</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">${t("ai_llm.embedding_provider_description")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -297,9 +308,4 @@ export const TPL = `
|
|||||||
<div class="form-text">${t("ai_llm.rebuild_index_description")}</div>
|
<div class="form-text">${t("ai_llm.rebuild_index_description")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Note about embedding provider precedence -->
|
|
||||||
<div class="form-group mt-3">
|
|
||||||
<h5>${t("ai_llm.embedding_providers_order")}</h5>
|
|
||||||
<div class="form-text mt-2">${t("ai_llm.embedding_providers_order_description")}</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -8,6 +8,7 @@ import type { Request } from "express";
|
|||||||
import { changeLanguage, getLocales } from "../../services/i18n.js";
|
import { changeLanguage, getLocales } from "../../services/i18n.js";
|
||||||
import type { OptionNames } from "@triliumnext/commons";
|
import type { OptionNames } from "@triliumnext/commons";
|
||||||
import config from "../../services/config.js";
|
import config from "../../services/config.js";
|
||||||
|
import aiServiceManager from "../../services/llm/ai_service_manager.js";
|
||||||
|
|
||||||
interface UserTheme {
|
interface UserTheme {
|
||||||
val: string; // value of the theme, used in the URL
|
val: string; // value of the theme, used in the URL
|
||||||
@ -95,7 +96,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
|
|||||||
"aiEnabled",
|
"aiEnabled",
|
||||||
"aiTemperature",
|
"aiTemperature",
|
||||||
"aiSystemPrompt",
|
"aiSystemPrompt",
|
||||||
"aiProviderPrecedence",
|
"aiChatProvider",
|
||||||
"openaiApiKey",
|
"openaiApiKey",
|
||||||
"openaiBaseUrl",
|
"openaiBaseUrl",
|
||||||
"openaiDefaultModel",
|
"openaiDefaultModel",
|
||||||
@ -110,7 +111,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
|
|||||||
"ollamaEmbeddingModel",
|
"ollamaEmbeddingModel",
|
||||||
"embeddingAutoUpdateEnabled",
|
"embeddingAutoUpdateEnabled",
|
||||||
"embeddingDimensionStrategy",
|
"embeddingDimensionStrategy",
|
||||||
"embeddingProviderPrecedence",
|
"aiEmbeddingProvider",
|
||||||
"embeddingSimilarityThreshold",
|
"embeddingSimilarityThreshold",
|
||||||
"embeddingBatchSize",
|
"embeddingBatchSize",
|
||||||
"embeddingUpdateInterval",
|
"embeddingUpdateInterval",
|
||||||
@ -178,6 +179,14 @@ function update(name: string, value: string) {
|
|||||||
changeLanguage(value);
|
changeLanguage(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reinitialize AI service manager when provider settings change
|
||||||
|
if (name === "aiChatProvider" || name === "aiEmbeddingProvider") {
|
||||||
|
// Run asynchronously to avoid blocking the response
|
||||||
|
aiServiceManager.reinitialize().catch(error => {
|
||||||
|
log.error(`Failed to reinitialize AI service manager: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ export async function findSimilarNotes(
|
|||||||
} else {
|
} else {
|
||||||
// Use dedicated embedding provider precedence from options for other strategies
|
// Use dedicated embedding provider precedence from options for other strategies
|
||||||
let preferredProviders: string[] = [];
|
let preferredProviders: string[] = [];
|
||||||
const embeddingPrecedence = await options.getOption('embeddingProviderPrecedence');
|
const embeddingPrecedence = await options.getOption('aiEmbeddingProvider');
|
||||||
|
|
||||||
if (embeddingPrecedence) {
|
if (embeddingPrecedence) {
|
||||||
// For "comma,separated,values"
|
// For "comma,separated,values"
|
||||||
|
@ -501,7 +501,7 @@ export class IndexService {
|
|||||||
const options = (await import('../options.js')).default;
|
const options = (await import('../options.js')).default;
|
||||||
let preferredProviders: string[] = [];
|
let preferredProviders: string[] = [];
|
||||||
|
|
||||||
const embeddingPrecedence = await options.getOption('embeddingProviderPrecedence');
|
const embeddingPrecedence = await options.getOption('aiEmbeddingProvider');
|
||||||
let provider;
|
let provider;
|
||||||
|
|
||||||
if (embeddingPrecedence) {
|
if (embeddingPrecedence) {
|
||||||
|
@ -212,9 +212,9 @@ const defaultOptions: DefaultOption[] = [
|
|||||||
// Adding missing AI options
|
// Adding missing AI options
|
||||||
{ name: "aiTemperature", value: "0.7", isSynced: true },
|
{ name: "aiTemperature", value: "0.7", isSynced: true },
|
||||||
{ name: "aiSystemPrompt", value: "", isSynced: true },
|
{ name: "aiSystemPrompt", value: "", isSynced: true },
|
||||||
{ name: "aiProviderPrecedence", value: "openai,anthropic,ollama", isSynced: true },
|
{ name: "aiChatProvider", value: "openai", isSynced: true },
|
||||||
{ name: "embeddingDimensionStrategy", value: "auto", isSynced: true },
|
{ name: "embeddingDimensionStrategy", value: "auto", isSynced: true },
|
||||||
{ name: "embeddingProviderPrecedence", value: "openai,voyage,ollama,local", isSynced: true },
|
{ name: "aiEmbeddingProvider", value: "openai", isSynced: true },
|
||||||
{ name: "embeddingSimilarityThreshold", value: "0.75", isSynced: true },
|
{ name: "embeddingSimilarityThreshold", value: "0.75", isSynced: true },
|
||||||
{ name: "enableAutomaticIndexing", value: "true", isSynced: true },
|
{ name: "enableAutomaticIndexing", value: "true", isSynced: true },
|
||||||
{ name: "maxNotesPerLlmQuery", value: "3", isSynced: true },
|
{ name: "maxNotesPerLlmQuery", value: "3", isSynced: true },
|
||||||
|
@ -142,7 +142,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
|||||||
ollamaDefaultModel: string;
|
ollamaDefaultModel: string;
|
||||||
ollamaEmbeddingModel: string;
|
ollamaEmbeddingModel: string;
|
||||||
codeOpenAiModel: string;
|
codeOpenAiModel: string;
|
||||||
aiProviderPrecedence: string;
|
aiChatProvider: string;
|
||||||
|
|
||||||
// Embedding-related options
|
// Embedding-related options
|
||||||
embeddingAutoUpdateEnabled: boolean;
|
embeddingAutoUpdateEnabled: boolean;
|
||||||
@ -150,7 +150,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
|
|||||||
embeddingBatchSize: number;
|
embeddingBatchSize: number;
|
||||||
embeddingDefaultDimension: number;
|
embeddingDefaultDimension: number;
|
||||||
embeddingsDefaultProvider: string;
|
embeddingsDefaultProvider: string;
|
||||||
embeddingProviderPrecedence: string;
|
aiEmbeddingProvider: string;
|
||||||
enableAutomaticIndexing: boolean;
|
enableAutomaticIndexing: boolean;
|
||||||
embeddingGenerationLocation: string;
|
embeddingGenerationLocation: string;
|
||||||
embeddingDimensionStrategy: string;
|
embeddingDimensionStrategy: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user