mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 19:12:27 +08:00
refactor(llm): implement new configuration methods for provider order and validation, enhancing error handling and deprecating legacy functions
This commit is contained in:
parent
5a5a69ebb8
commit
3a55735cd5
@ -26,7 +26,8 @@ import {
|
|||||||
parseModelIdentifier,
|
parseModelIdentifier,
|
||||||
isAIEnabled,
|
isAIEnabled,
|
||||||
getDefaultModelForProvider,
|
getDefaultModelForProvider,
|
||||||
clearConfigurationCache
|
clearConfigurationCache,
|
||||||
|
validateConfiguration
|
||||||
} from './config/configuration_helpers.js';
|
} from './config/configuration_helpers.js';
|
||||||
import type { ProviderType } from './interfaces/configuration_interfaces.js';
|
import type { ProviderType } from './interfaces/configuration_interfaces.js';
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
ollama: new OllamaService()
|
ollama: new OllamaService()
|
||||||
};
|
};
|
||||||
|
|
||||||
private providerOrder: ServiceProviders[] = ['openai', 'anthropic', 'ollama']; // Default order
|
private providerOrder: ServiceProviders[] = []; // Will be populated from configuration
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -84,6 +85,23 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the provider precedence order using the new configuration system
|
* Update the provider precedence order using the new configuration system
|
||||||
|
*/
|
||||||
|
async updateProviderOrderAsync(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const providers = await getProviderPrecedence();
|
||||||
|
this.providerOrder = providers as ServiceProviders[];
|
||||||
|
this.initialized = true;
|
||||||
|
log.info(`Updated provider order: ${providers.join(', ')}`);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Failed to get provider precedence: ${error}`);
|
||||||
|
// Keep empty order, will be handled gracefully by other methods
|
||||||
|
this.providerOrder = [];
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the provider precedence order (legacy sync version)
|
||||||
* Returns true if successful, false if options not available yet
|
* Returns true if successful, false if options not available yet
|
||||||
*/
|
*/
|
||||||
updateProviderOrder(): boolean {
|
updateProviderOrder(): boolean {
|
||||||
@ -91,89 +109,57 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Use async version but don't wait
|
||||||
// Use async helper but handle it synchronously for now
|
this.updateProviderOrderAsync().catch(error => {
|
||||||
// In a real refactor, this method should become async
|
log.error(`Error in async provider order update: ${error}`);
|
||||||
getProviderPrecedence().then(providers => {
|
});
|
||||||
this.providerOrder = providers as ServiceProviders[];
|
|
||||||
log.info(`Updated provider order: ${providers.join(', ')}`);
|
|
||||||
}).catch(error => {
|
|
||||||
log.error(`Failed to get provider precedence: ${error}`);
|
|
||||||
// Keep default order
|
|
||||||
});
|
|
||||||
|
|
||||||
this.initialized = true;
|
return true;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate AI configuration using the new configuration system
|
||||||
|
*/
|
||||||
|
async validateConfiguration(): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const result = await validateConfiguration();
|
||||||
|
|
||||||
|
if (!result.isValid) {
|
||||||
|
let message = 'There are issues with your AI configuration:';
|
||||||
|
for (const error of result.errors) {
|
||||||
|
message += `\n• ${error}`;
|
||||||
|
}
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
message += '\n\nWarnings:';
|
||||||
|
for (const warning of result.warnings) {
|
||||||
|
message += `\n• ${warning}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message += '\n\nPlease check your AI settings.';
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.warnings.length > 0) {
|
||||||
|
let message = 'AI configuration warnings:';
|
||||||
|
for (const warning of result.warnings) {
|
||||||
|
message += `\n• ${warning}`;
|
||||||
|
}
|
||||||
|
log.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If options table doesn't exist yet, use defaults
|
log.error(`Error validating AI configuration: ${error}`);
|
||||||
// This happens during initial database creation
|
return `Configuration validation failed: ${error}`;
|
||||||
this.providerOrder = ['openai', 'anthropic', 'ollama'];
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate embedding providers configuration using the new configuration system
|
* @deprecated Use validateConfiguration() instead
|
||||||
*/
|
*/
|
||||||
async validateEmbeddingProviders(): Promise<string | null> {
|
async validateEmbeddingProviders(): Promise<string | null> {
|
||||||
try {
|
log.info('validateEmbeddingProviders is deprecated, use validateConfiguration instead');
|
||||||
// Check if AI is enabled using the new helper
|
return this.validateConfiguration();
|
||||||
const aiEnabled = await isAIEnabled();
|
|
||||||
if (!aiEnabled) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get precedence list using the new helper (no string parsing!)
|
|
||||||
const precedenceList = await getEmbeddingProviderPrecedence();
|
|
||||||
|
|
||||||
// Check for configuration issues with providers in the precedence list
|
|
||||||
const configIssues: string[] = [];
|
|
||||||
|
|
||||||
// Check each provider in the precedence list for proper configuration
|
|
||||||
for (const provider of precedenceList) {
|
|
||||||
if (provider === 'openai') {
|
|
||||||
// Check OpenAI configuration
|
|
||||||
const apiKey = await options.getOption('openaiApiKey');
|
|
||||||
if (!apiKey) {
|
|
||||||
configIssues.push(`OpenAI API key is missing`);
|
|
||||||
}
|
|
||||||
} else if (provider === 'anthropic') {
|
|
||||||
// Check Anthropic configuration
|
|
||||||
const apiKey = await options.getOption('anthropicApiKey');
|
|
||||||
if (!apiKey) {
|
|
||||||
configIssues.push(`Anthropic API key is missing`);
|
|
||||||
}
|
|
||||||
} else if (provider === 'ollama') {
|
|
||||||
// Check Ollama configuration
|
|
||||||
const baseUrl = await options.getOption('ollamaBaseUrl');
|
|
||||||
if (!baseUrl) {
|
|
||||||
configIssues.push(`Ollama Base URL is missing`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add checks for other providers as needed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return warning message if there are configuration issues
|
|
||||||
if (configIssues.length > 0) {
|
|
||||||
let message = 'There are issues with your AI provider configuration:';
|
|
||||||
|
|
||||||
for (const issue of configIssues) {
|
|
||||||
message += `\n• ${issue}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
message += '\n\nPlease check your AI settings.';
|
|
||||||
|
|
||||||
// Log warning to console
|
|
||||||
log.error('AI Provider Configuration Warning: ' + message);
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error validating embedding providers: ${error}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -348,6 +334,13 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
/**
|
/**
|
||||||
* Get whether AI features are enabled using the new configuration system
|
* Get whether AI features are enabled using the new configuration system
|
||||||
*/
|
*/
|
||||||
|
async getAIEnabledAsync(): Promise<boolean> {
|
||||||
|
return isAIEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether AI features are enabled (sync version for compatibility)
|
||||||
|
*/
|
||||||
getAIEnabled(): boolean {
|
getAIEnabled(): boolean {
|
||||||
// For synchronous compatibility, use the old method
|
// For synchronous compatibility, use the old method
|
||||||
// In a full refactor, this should be async
|
// In a full refactor, this should be async
|
||||||
@ -355,11 +348,12 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up embeddings provider for AI features
|
* Set up embeddings provider using the new configuration system
|
||||||
*/
|
*/
|
||||||
async setupEmbeddingsProvider(): Promise<void> {
|
async setupEmbeddingsProvider(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!this.getAIEnabled()) {
|
const aiEnabled = await isAIEnabled();
|
||||||
|
if (!aiEnabled) {
|
||||||
log.info('AI features are disabled');
|
log.info('AI features are disabled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -381,20 +375,23 @@ export class AIServiceManager implements IAIServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the AI Service
|
* Initialize the AI Service using the new configuration system
|
||||||
*/
|
*/
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
log.info("Initializing AI service...");
|
log.info("Initializing AI service...");
|
||||||
|
|
||||||
// Check if AI is enabled using the new helper
|
// Check if AI is enabled using the new helper
|
||||||
const isAIEnabled_value = await isAIEnabled();
|
const aiEnabled = await isAIEnabled();
|
||||||
|
|
||||||
if (!isAIEnabled_value) {
|
if (!aiEnabled) {
|
||||||
log.info("AI features are disabled in options");
|
log.info("AI features are disabled in options");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update provider order from configuration
|
||||||
|
await this.updateProviderOrderAsync();
|
||||||
|
|
||||||
// Set up embeddings provider if AI is enabled
|
// Set up embeddings provider if AI is enabled
|
||||||
await this.setupEmbeddingsProvider();
|
await this.setupEmbeddingsProvider();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import sql from "../../sql.js";
|
import sql from '../../sql.js'
|
||||||
import { randomString } from "../../../services/utils.js";
|
import { randomString } from "../../../services/utils.js";
|
||||||
import dateUtils from "../../../services/date_utils.js";
|
import dateUtils from "../../../services/date_utils.js";
|
||||||
import log from "../../log.js";
|
import log from "../../log.js";
|
||||||
@ -11,6 +11,7 @@ import { SEARCH_CONSTANTS } from '../constants/search_constants.js';
|
|||||||
import type { NoteEmbeddingContext } from "./embeddings_interface.js";
|
import type { NoteEmbeddingContext } from "./embeddings_interface.js";
|
||||||
import becca from "../../../becca/becca.js";
|
import becca from "../../../becca/becca.js";
|
||||||
import { isNoteExcludedFromAIById } from "../utils/ai_exclusion_utils.js";
|
import { isNoteExcludedFromAIById } from "../utils/ai_exclusion_utils.js";
|
||||||
|
import { getEmbeddingProviderPrecedence } from '../config/configuration_helpers.js';
|
||||||
|
|
||||||
interface Similarity {
|
interface Similarity {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
@ -271,44 +272,28 @@ export async function findSimilarNotes(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use dedicated embedding provider precedence from options for other strategies
|
// Try providers using the new configuration system
|
||||||
let preferredProviders: string[] = [];
|
if (useFallback) {
|
||||||
const embeddingPrecedence = await options.getOption('embeddingProviderPrecedence');
|
log.info('No embeddings found for specified provider, trying fallback providers...');
|
||||||
|
|
||||||
if (embeddingPrecedence) {
|
// Use the new configuration system - no string parsing!
|
||||||
// For "comma,separated,values"
|
const preferredProviders = await getEmbeddingProviderPrecedence();
|
||||||
if (embeddingPrecedence.includes(',')) {
|
|
||||||
preferredProviders = embeddingPrecedence.split(',').map(p => p.trim());
|
log.info(`Using provider precedence: ${preferredProviders.join(', ')}`);
|
||||||
}
|
|
||||||
// For JSON array ["value1", "value2"]
|
// Try providers in precedence order
|
||||||
else if (embeddingPrecedence.startsWith('[') && embeddingPrecedence.endsWith(']')) {
|
for (const provider of preferredProviders) {
|
||||||
try {
|
const providerEmbeddings = availableEmbeddings.filter(e => e.providerId === provider);
|
||||||
preferredProviders = JSON.parse(embeddingPrecedence);
|
|
||||||
} catch (e) {
|
if (providerEmbeddings.length > 0) {
|
||||||
log.error(`Error parsing embedding precedence: ${e}`);
|
// Choose the model with the most embeddings
|
||||||
preferredProviders = [embeddingPrecedence]; // Fallback to using as single value
|
const bestModel = providerEmbeddings.sort((a, b) => b.count - a.count)[0];
|
||||||
|
log.info(`Found fallback provider: ${provider}, model: ${bestModel.modelId}, dimension: ${bestModel.dimension}`);
|
||||||
|
|
||||||
|
// The 'regenerate' strategy would go here if needed
|
||||||
|
// We're no longer supporting the 'adapt' strategy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For a single value
|
|
||||||
else {
|
|
||||||
preferredProviders = [embeddingPrecedence];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(`Using provider precedence: ${preferredProviders.join(', ')}`);
|
|
||||||
|
|
||||||
// Try providers in precedence order
|
|
||||||
for (const provider of preferredProviders) {
|
|
||||||
const providerEmbeddings = availableEmbeddings.filter(e => e.providerId === provider);
|
|
||||||
|
|
||||||
if (providerEmbeddings.length > 0) {
|
|
||||||
// Choose the model with the most embeddings
|
|
||||||
const bestModel = providerEmbeddings.sort((a, b) => b.count - a.count)[0];
|
|
||||||
log.info(`Found fallback provider: ${provider}, model: ${bestModel.modelId}, dimension: ${bestModel.dimension}`);
|
|
||||||
|
|
||||||
// The 'regenerate' strategy would go here if needed
|
|
||||||
// We're no longer supporting the 'adapt' strategy
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user