mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
feat(llm): transition from initializing LLM providers, to creating them on demand
This commit is contained in:
parent
c1b10d70b8
commit
bb8a374ab8
@ -43,11 +43,7 @@ interface NoteContext {
|
||||
}
|
||||
|
||||
export class AIServiceManager implements IAIServiceManager {
|
||||
private services: Record<ServiceProviders, AIService> = {
|
||||
openai: new OpenAIService(),
|
||||
anthropic: new AnthropicService(),
|
||||
ollama: new OllamaService()
|
||||
};
|
||||
private services: Partial<Record<ServiceProviders, AIService>> = {};
|
||||
|
||||
private providerOrder: ServiceProviders[] = []; // Will be populated from configuration
|
||||
private initialized = false;
|
||||
@ -183,9 +179,42 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
*/
|
||||
getAvailableProviders(): ServiceProviders[] {
|
||||
this.ensureInitialized();
|
||||
return Object.entries(this.services)
|
||||
.filter(([_, service]) => service.isAvailable())
|
||||
.map(([key, _]) => key as ServiceProviders);
|
||||
|
||||
const allProviders: ServiceProviders[] = ['openai', 'anthropic', 'ollama'];
|
||||
const availableProviders: ServiceProviders[] = [];
|
||||
|
||||
for (const providerName of allProviders) {
|
||||
// Use a sync approach - check if we can create the provider
|
||||
const service = this.services[providerName];
|
||||
if (service && service.isAvailable()) {
|
||||
availableProviders.push(providerName);
|
||||
} else {
|
||||
// For providers not yet created, check configuration to see if they would be available
|
||||
try {
|
||||
switch (providerName) {
|
||||
case 'openai':
|
||||
if (options.getOption('openaiApiKey')) {
|
||||
availableProviders.push(providerName);
|
||||
}
|
||||
break;
|
||||
case 'anthropic':
|
||||
if (options.getOption('anthropicApiKey')) {
|
||||
availableProviders.push(providerName);
|
||||
}
|
||||
break;
|
||||
case 'ollama':
|
||||
if (options.getOption('ollamaBaseUrl')) {
|
||||
availableProviders.push(providerName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore configuration errors, provider just won't be available
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,9 +253,12 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
|
||||
if (modelIdentifier.provider && availableProviders.includes(modelIdentifier.provider as ServiceProviders)) {
|
||||
try {
|
||||
const modifiedOptions = { ...options, model: modelIdentifier.modelId };
|
||||
log.info(`[AIServiceManager] Using provider ${modelIdentifier.provider} from model prefix with modifiedOptions.stream: ${modifiedOptions.stream}`);
|
||||
return await this.services[modelIdentifier.provider as ServiceProviders].generateChatCompletion(messages, modifiedOptions);
|
||||
const service = this.services[modelIdentifier.provider as ServiceProviders];
|
||||
if (service) {
|
||||
const modifiedOptions = { ...options, model: modelIdentifier.modelId };
|
||||
log.info(`[AIServiceManager] Using provider ${modelIdentifier.provider} from model prefix with modifiedOptions.stream: ${modifiedOptions.stream}`);
|
||||
return await service.generateChatCompletion(messages, modifiedOptions);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error with specified provider ${modelIdentifier.provider}: ${error}`);
|
||||
// If the specified provider fails, continue with the fallback providers
|
||||
@ -240,8 +272,11 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
|
||||
for (const provider of sortedProviders) {
|
||||
try {
|
||||
log.info(`[AIServiceManager] Trying provider ${provider} with options.stream: ${options.stream}`);
|
||||
return await this.services[provider].generateChatCompletion(messages, options);
|
||||
const service = this.services[provider];
|
||||
if (service) {
|
||||
log.info(`[AIServiceManager] Trying provider ${provider} with options.stream: ${options.stream}`);
|
||||
return await service.generateChatCompletion(messages, options);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error with provider ${provider}: ${error}`);
|
||||
lastError = error as Error;
|
||||
@ -348,30 +383,49 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up embeddings provider using the new configuration system
|
||||
* Get or create a chat provider on-demand
|
||||
*/
|
||||
async setupEmbeddingsProvider(): Promise<void> {
|
||||
try {
|
||||
const aiEnabled = await isAIEnabled();
|
||||
if (!aiEnabled) {
|
||||
log.info('AI features are disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the new configuration system - no string parsing!
|
||||
const enabledProviders = await getEnabledEmbeddingProviders();
|
||||
|
||||
if (enabledProviders.length === 0) {
|
||||
log.info('No embedding providers are enabled');
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize embedding providers
|
||||
log.info('Embedding providers initialized successfully');
|
||||
} catch (error: any) {
|
||||
log.error(`Error setting up embedding providers: ${error.message}`);
|
||||
throw error;
|
||||
private async getOrCreateChatProvider(providerName: ServiceProviders): Promise<AIService | null> {
|
||||
// Return existing provider if already created
|
||||
if (this.services[providerName]) {
|
||||
return this.services[providerName];
|
||||
}
|
||||
|
||||
// Create provider on-demand based on configuration
|
||||
try {
|
||||
switch (providerName) {
|
||||
case 'openai':
|
||||
const openaiApiKey = await options.getOption('openaiApiKey');
|
||||
if (openaiApiKey) {
|
||||
this.services.openai = new OpenAIService();
|
||||
log.info('Created OpenAI chat provider on-demand');
|
||||
return this.services.openai;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anthropic':
|
||||
const anthropicApiKey = await options.getOption('anthropicApiKey');
|
||||
if (anthropicApiKey) {
|
||||
this.services.anthropic = new AnthropicService();
|
||||
log.info('Created Anthropic chat provider on-demand');
|
||||
return this.services.anthropic;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ollama':
|
||||
const ollamaBaseUrl = await options.getOption('ollamaBaseUrl');
|
||||
if (ollamaBaseUrl) {
|
||||
this.services.ollama = new OllamaService();
|
||||
log.info('Created Ollama chat provider on-demand');
|
||||
return this.services.ollama;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error(`Error creating ${providerName} provider on-demand: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -392,9 +446,6 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
// Update provider order from configuration
|
||||
await this.updateProviderOrderAsync();
|
||||
|
||||
// Set up embeddings provider if AI is enabled
|
||||
await this.setupEmbeddingsProvider();
|
||||
|
||||
// Initialize index service
|
||||
await this.getIndexService().initialize();
|
||||
|
||||
@ -462,7 +513,7 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
try {
|
||||
// Get the default LLM service for context enhancement
|
||||
const provider = this.getPreferredProvider();
|
||||
const llmService = this.getService(provider);
|
||||
const llmService = await this.getService(provider);
|
||||
|
||||
// Find relevant notes
|
||||
contextNotes = await contextService.findRelevantNotes(
|
||||
@ -503,25 +554,27 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
/**
|
||||
* Get AI service for the given provider
|
||||
*/
|
||||
getService(provider?: string): AIService {
|
||||
async getService(provider?: string): Promise<AIService> {
|
||||
this.ensureInitialized();
|
||||
|
||||
// If provider is specified, try to use it
|
||||
if (provider && this.services[provider as ServiceProviders]?.isAvailable()) {
|
||||
return this.services[provider as ServiceProviders];
|
||||
}
|
||||
|
||||
// Otherwise, use the first available provider in the configured order
|
||||
for (const providerName of this.providerOrder) {
|
||||
const service = this.services[providerName];
|
||||
if (service.isAvailable()) {
|
||||
// If provider is specified, try to get or create it
|
||||
if (provider) {
|
||||
const service = await this.getOrCreateChatProvider(provider as ServiceProviders);
|
||||
if (service && service.isAvailable()) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
// If no provider is available, use first one anyway (it will throw an error)
|
||||
// This allows us to show a proper error message rather than "provider not found"
|
||||
return this.services[this.providerOrder[0]];
|
||||
// Otherwise, try providers in the configured order
|
||||
for (const providerName of this.providerOrder) {
|
||||
const service = await this.getOrCreateChatProvider(providerName);
|
||||
if (service && service.isAvailable()) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
// If no provider is available, throw a clear error
|
||||
throw new Error('No AI chat providers are available. Please check your AI settings.');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -550,7 +603,8 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
|
||||
// Return the first available provider in the order
|
||||
for (const providerName of this.providerOrder) {
|
||||
if (this.services[providerName].isAvailable()) {
|
||||
const service = this.services[providerName];
|
||||
if (service && service.isAvailable()) {
|
||||
return providerName;
|
||||
}
|
||||
}
|
||||
@ -634,13 +688,15 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
// Initialize embeddings through index service
|
||||
await indexService.startEmbeddingGeneration();
|
||||
} else {
|
||||
log.info('AI features disabled, stopping embeddings');
|
||||
log.info('AI features disabled, stopping embeddings and clearing providers');
|
||||
// Stop embeddings through index service
|
||||
await indexService.stopEmbeddingGeneration();
|
||||
// Clear chat providers
|
||||
this.services = {};
|
||||
}
|
||||
} else {
|
||||
// For other AI-related options, just recreate services
|
||||
this.recreateServices();
|
||||
// For other AI-related options, recreate services on-demand
|
||||
await this.recreateServices();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -656,8 +712,12 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
// Clear configuration cache first
|
||||
clearConfigurationCache();
|
||||
|
||||
// Recreate all service instances to pick up new configuration
|
||||
this.recreateServiceInstances();
|
||||
// Clear existing chat providers (they will be recreated on-demand)
|
||||
this.services = {};
|
||||
|
||||
// Clear embedding providers (they will be recreated on-demand when needed)
|
||||
const providerManager = await import('./providers/providers.js');
|
||||
providerManager.clearAllEmbeddingProviders();
|
||||
|
||||
// Update provider order with new configuration
|
||||
await this.updateProviderOrderAsync();
|
||||
@ -668,25 +728,6 @@ export class AIServiceManager implements IAIServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate service instances to pick up new configuration
|
||||
*/
|
||||
private recreateServiceInstances(): void {
|
||||
try {
|
||||
log.info('Recreating service instances');
|
||||
|
||||
// Recreate service instances
|
||||
this.services = {
|
||||
openai: new OpenAIService(),
|
||||
anthropic: new AnthropicService(),
|
||||
ollama: new OllamaService()
|
||||
};
|
||||
|
||||
log.info('Service instances recreated successfully');
|
||||
} catch (error) {
|
||||
log.error(`Error recreating service instances: ${this.handleError(error)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't create singleton immediately, use a lazy-loading pattern
|
||||
@ -759,7 +800,7 @@ export default {
|
||||
);
|
||||
},
|
||||
// New methods
|
||||
getService(provider?: string): AIService {
|
||||
async getService(provider?: string): Promise<AIService> {
|
||||
return getInstance().getService(provider);
|
||||
},
|
||||
getPreferredProvider(): string {
|
||||
|
@ -33,7 +33,7 @@ async function getSemanticContext(
|
||||
}
|
||||
|
||||
// Get an LLM service
|
||||
const llmService = aiServiceManager.getInstance().getService();
|
||||
const llmService = await aiServiceManager.getInstance().getService();
|
||||
|
||||
const result = await contextService.processQuery("", llmService, {
|
||||
maxResults: options.maxSimilarNotes || 5,
|
||||
@ -543,7 +543,7 @@ export class ContextExtractor {
|
||||
try {
|
||||
const { default: aiServiceManager } = await import('../ai_service_manager.js');
|
||||
const contextService = aiServiceManager.getInstance().getContextService();
|
||||
const llmService = aiServiceManager.getInstance().getService();
|
||||
const llmService = await aiServiceManager.getInstance().getService();
|
||||
|
||||
if (!contextService) {
|
||||
return "Context service not available.";
|
||||
|
@ -45,8 +45,7 @@ export async function initializeEmbeddings() {
|
||||
|
||||
// Start the embedding system if AI is enabled
|
||||
if (await options.getOptionBool('aiEnabled')) {
|
||||
// Initialize default embedding providers when AI is enabled
|
||||
await providerManager.initializeDefaultProviders();
|
||||
// Embedding providers will be created on-demand when needed
|
||||
await initEmbeddings();
|
||||
log.info("Embedding system initialized successfully.");
|
||||
} else {
|
||||
|
@ -851,10 +851,6 @@ export class IndexService {
|
||||
throw new Error("AI features must be enabled first");
|
||||
}
|
||||
|
||||
// Re-initialize providers first in case they weren't available when server started
|
||||
log.info("Re-initializing embedding providers");
|
||||
await providerManager.initializeDefaultProviders();
|
||||
|
||||
// Re-initialize if needed
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
@ -870,6 +866,13 @@ export class IndexService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify providers are available (this will create them on-demand if needed)
|
||||
const providers = await providerManager.getEnabledEmbeddingProviders();
|
||||
if (providers.length === 0) {
|
||||
throw new Error("No embedding providers available");
|
||||
}
|
||||
log.info(`Found ${providers.length} embedding providers: ${providers.map(p => p.name).join(', ')}`);
|
||||
|
||||
// Setup automatic indexing if enabled
|
||||
if (await options.getOptionBool('embeddingAutoUpdateEnabled')) {
|
||||
this.setupAutomaticIndexing();
|
||||
|
@ -28,7 +28,7 @@ export interface AIServiceManagerConfig {
|
||||
* Interface for managing AI service providers
|
||||
*/
|
||||
export interface IAIServiceManager {
|
||||
getService(provider?: string): AIService;
|
||||
getService(provider?: string): Promise<AIService>;
|
||||
getAvailableProviders(): string[];
|
||||
getPreferredProvider(): string;
|
||||
isProviderAvailable(provider: string): boolean;
|
||||
|
@ -43,7 +43,7 @@ export class ContextExtractionStage {
|
||||
|
||||
// Get enhanced context from the context service
|
||||
const contextService = aiServiceManager.getContextService();
|
||||
const llmService = aiServiceManager.getService();
|
||||
const llmService = await aiServiceManager.getService();
|
||||
|
||||
if (contextService) {
|
||||
// Use unified context service to get smart context
|
||||
|
@ -104,7 +104,7 @@ export class LLMCompletionStage extends BasePipelineStage<LLMCompletionInput, {
|
||||
|
||||
// Use specific provider if available
|
||||
if (selectedProvider && aiServiceManager.isProviderAvailable(selectedProvider)) {
|
||||
const service = aiServiceManager.getService(selectedProvider);
|
||||
const service = await aiServiceManager.getService(selectedProvider);
|
||||
log.info(`[LLMCompletionStage] Using specific service for ${selectedProvider}`);
|
||||
|
||||
// Generate completion and wrap with enhanced stream handling
|
||||
|
@ -292,7 +292,7 @@ export class ModelSelectionStage extends BasePipelineStage<ModelSelectionInput,
|
||||
log.info(`Getting default model for provider ${provider} using AI service manager`);
|
||||
|
||||
// Use the existing AI service manager instead of duplicating API calls
|
||||
const service = aiServiceManager.getInstance().getService(provider);
|
||||
const service = await aiServiceManager.getInstance().getService(provider);
|
||||
|
||||
if (!service || !service.isAvailable()) {
|
||||
log.info(`Provider ${provider} service is not available`);
|
||||
|
@ -123,6 +123,94 @@ export function getEmbeddingProvider(name: string): EmbeddingProvider | undefine
|
||||
return providers.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create providers on-demand based on current options
|
||||
*/
|
||||
export async function createProvidersFromCurrentOptions(): Promise<EmbeddingProvider[]> {
|
||||
const result: EmbeddingProvider[] = [];
|
||||
|
||||
try {
|
||||
// Create Ollama provider if embedding base URL is configured
|
||||
const ollamaEmbeddingBaseUrl = await options.getOption('ollamaEmbeddingBaseUrl');
|
||||
if (ollamaEmbeddingBaseUrl) {
|
||||
const embeddingModel = await options.getOption('ollamaEmbeddingModel');
|
||||
|
||||
try {
|
||||
const ollamaProvider = new OllamaEmbeddingProvider({
|
||||
model: embeddingModel,
|
||||
dimension: 768, // Initial value, will be updated during initialization
|
||||
type: 'float32',
|
||||
baseUrl: ollamaEmbeddingBaseUrl
|
||||
});
|
||||
|
||||
await ollamaProvider.initialize();
|
||||
registerEmbeddingProvider(ollamaProvider);
|
||||
result.push(ollamaProvider);
|
||||
log.info(`Created Ollama provider on-demand: ${embeddingModel} at ${ollamaEmbeddingBaseUrl}`);
|
||||
} catch (error: any) {
|
||||
log.error(`Error creating Ollama embedding provider on-demand: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Create OpenAI provider if API key is configured
|
||||
const openaiApiKey = await options.getOption('openaiApiKey');
|
||||
if (openaiApiKey) {
|
||||
const openaiModel = await options.getOption('openaiEmbeddingModel') || 'text-embedding-3-small';
|
||||
const openaiBaseUrl = await options.getOption('openaiBaseUrl') || 'https://api.openai.com/v1';
|
||||
|
||||
const openaiProvider = new OpenAIEmbeddingProvider({
|
||||
model: openaiModel,
|
||||
dimension: 1536,
|
||||
type: 'float32',
|
||||
apiKey: openaiApiKey,
|
||||
baseUrl: openaiBaseUrl
|
||||
});
|
||||
|
||||
registerEmbeddingProvider(openaiProvider);
|
||||
result.push(openaiProvider);
|
||||
log.info(`Created OpenAI provider on-demand: ${openaiModel}`);
|
||||
}
|
||||
|
||||
// Create Voyage provider if API key is configured
|
||||
const voyageApiKey = await options.getOption('voyageApiKey' as any);
|
||||
if (voyageApiKey) {
|
||||
const voyageModel = await options.getOption('voyageEmbeddingModel') || 'voyage-2';
|
||||
const voyageBaseUrl = 'https://api.voyageai.com/v1';
|
||||
|
||||
const voyageProvider = new VoyageEmbeddingProvider({
|
||||
model: voyageModel,
|
||||
dimension: 1024,
|
||||
type: 'float32',
|
||||
apiKey: voyageApiKey,
|
||||
baseUrl: voyageBaseUrl
|
||||
});
|
||||
|
||||
registerEmbeddingProvider(voyageProvider);
|
||||
result.push(voyageProvider);
|
||||
log.info(`Created Voyage provider on-demand: ${voyageModel}`);
|
||||
}
|
||||
|
||||
// Always include local provider as fallback
|
||||
if (!providers.has('local')) {
|
||||
const localProvider = new SimpleLocalEmbeddingProvider({
|
||||
model: 'local',
|
||||
dimension: 384,
|
||||
type: 'float32'
|
||||
});
|
||||
registerEmbeddingProvider(localProvider);
|
||||
result.push(localProvider);
|
||||
log.info(`Created local provider on-demand as fallback`);
|
||||
} else {
|
||||
result.push(providers.get('local')!);
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
log.error(`Error creating providers from current options: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all enabled embedding providers
|
||||
*/
|
||||
@ -131,31 +219,16 @@ export async function getEnabledEmbeddingProviders(): Promise<EmbeddingProvider[
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get providers from database ordered by priority
|
||||
const dbProviders = await sql.getRows(`
|
||||
SELECT providerId, name, config
|
||||
FROM embedding_providers
|
||||
ORDER BY priority DESC`
|
||||
);
|
||||
|
||||
const result: EmbeddingProvider[] = [];
|
||||
|
||||
for (const row of dbProviders) {
|
||||
const rowData = row as any;
|
||||
const provider = providers.get(rowData.name);
|
||||
|
||||
if (provider) {
|
||||
result.push(provider);
|
||||
} else {
|
||||
// Only log error if we haven't logged it before for this provider
|
||||
if (!loggedProviderErrors.has(rowData.name)) {
|
||||
log.error(`Enabled embedding provider ${rowData.name} not found in registered providers`);
|
||||
loggedProviderErrors.add(rowData.name);
|
||||
}
|
||||
}
|
||||
// First try to get existing registered providers
|
||||
const existingProviders = Array.from(providers.values());
|
||||
|
||||
// If no providers are registered, create them on-demand from current options
|
||||
if (existingProviders.length === 0) {
|
||||
log.info('No providers registered, creating from current options');
|
||||
return await createProvidersFromCurrentOptions();
|
||||
}
|
||||
|
||||
return result;
|
||||
return existingProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,130 +330,13 @@ export async function getEmbeddingProviderConfigs() {
|
||||
|
||||
/**
|
||||
* Initialize the default embedding providers
|
||||
* @deprecated - Use on-demand provider creation instead
|
||||
*/
|
||||
export async function initializeDefaultProviders() {
|
||||
// Register built-in providers
|
||||
try {
|
||||
// Register OpenAI provider if API key is configured
|
||||
const openaiApiKey = await options.getOption('openaiApiKey');
|
||||
if (openaiApiKey) {
|
||||
const openaiModel = await options.getOption('openaiEmbeddingModel') || 'text-embedding-3-small';
|
||||
const openaiBaseUrl = await options.getOption('openaiBaseUrl') || 'https://api.openai.com/v1';
|
||||
|
||||
registerEmbeddingProvider(new OpenAIEmbeddingProvider({
|
||||
model: openaiModel,
|
||||
dimension: 1536, // OpenAI's typical dimension
|
||||
type: 'float32',
|
||||
apiKey: openaiApiKey,
|
||||
baseUrl: openaiBaseUrl
|
||||
}));
|
||||
|
||||
// Create OpenAI provider config if it doesn't exist
|
||||
const existingOpenAI = await sql.getRow(
|
||||
"SELECT * FROM embedding_providers WHERE name = ?",
|
||||
['openai']
|
||||
);
|
||||
|
||||
if (!existingOpenAI) {
|
||||
await createEmbeddingProviderConfig('openai', {
|
||||
model: openaiModel,
|
||||
dimension: 1536,
|
||||
type: 'float32'
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Register Voyage provider if API key is configured
|
||||
const voyageApiKey = await options.getOption('voyageApiKey' as any);
|
||||
if (voyageApiKey) {
|
||||
const voyageModel = await options.getOption('voyageEmbeddingModel') || 'voyage-2';
|
||||
const voyageBaseUrl = 'https://api.voyageai.com/v1';
|
||||
|
||||
registerEmbeddingProvider(new VoyageEmbeddingProvider({
|
||||
model: voyageModel,
|
||||
dimension: 1024, // Voyage's embedding dimension
|
||||
type: 'float32',
|
||||
apiKey: voyageApiKey,
|
||||
baseUrl: voyageBaseUrl
|
||||
}));
|
||||
|
||||
// Create Voyage provider config if it doesn't exist
|
||||
const existingVoyage = await sql.getRow(
|
||||
"SELECT * FROM embedding_providers WHERE name = ?",
|
||||
['voyage']
|
||||
);
|
||||
|
||||
if (!existingVoyage) {
|
||||
await createEmbeddingProviderConfig('voyage', {
|
||||
model: voyageModel,
|
||||
dimension: 1024,
|
||||
type: 'float32'
|
||||
}, 75);
|
||||
}
|
||||
}
|
||||
|
||||
// Register Ollama embedding provider if embedding base URL is configured
|
||||
const ollamaEmbeddingBaseUrl = await options.getOption('ollamaEmbeddingBaseUrl');
|
||||
if (ollamaEmbeddingBaseUrl) {
|
||||
// Use specific embedding models if available
|
||||
const embeddingModel = await options.getOption('ollamaEmbeddingModel');
|
||||
|
||||
try {
|
||||
// Create provider with initial dimension to be updated during initialization
|
||||
const ollamaProvider = new OllamaEmbeddingProvider({
|
||||
model: embeddingModel,
|
||||
dimension: 768, // Initial value, will be updated during initialization
|
||||
type: 'float32',
|
||||
baseUrl: ollamaEmbeddingBaseUrl
|
||||
});
|
||||
|
||||
// Register the provider
|
||||
registerEmbeddingProvider(ollamaProvider);
|
||||
|
||||
// Initialize the provider to detect model capabilities
|
||||
await ollamaProvider.initialize();
|
||||
|
||||
// Create Ollama provider config if it doesn't exist
|
||||
const existingOllama = await sql.getRow(
|
||||
"SELECT * FROM embedding_providers WHERE name = ?",
|
||||
['ollama']
|
||||
);
|
||||
|
||||
if (!existingOllama) {
|
||||
await createEmbeddingProviderConfig('ollama', {
|
||||
model: embeddingModel,
|
||||
dimension: ollamaProvider.getDimension(),
|
||||
type: 'float32'
|
||||
}, 50);
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error(`Error initializing Ollama embedding provider: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Always register local provider as fallback
|
||||
registerEmbeddingProvider(new SimpleLocalEmbeddingProvider({
|
||||
model: 'local',
|
||||
dimension: 384,
|
||||
type: 'float32'
|
||||
}));
|
||||
|
||||
// Create local provider config if it doesn't exist
|
||||
const existingLocal = await sql.getRow(
|
||||
"SELECT * FROM embedding_providers WHERE name = ?",
|
||||
['local']
|
||||
);
|
||||
|
||||
if (!existingLocal) {
|
||||
await createEmbeddingProviderConfig('local', {
|
||||
model: 'local',
|
||||
dimension: 384,
|
||||
type: 'float32'
|
||||
}, 10);
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error(`Error initializing default embedding providers: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
// This function is now deprecated in favor of on-demand provider creation
|
||||
// The createProvidersFromCurrentOptions() function should be used instead
|
||||
log.info('initializeDefaultProviders called - using on-demand provider creation instead');
|
||||
return await createProvidersFromCurrentOptions();
|
||||
}
|
||||
|
||||
export default {
|
||||
@ -390,6 +346,7 @@ export default {
|
||||
getEmbeddingProviders,
|
||||
getEmbeddingProvider,
|
||||
getEnabledEmbeddingProviders,
|
||||
createProvidersFromCurrentOptions,
|
||||
createEmbeddingProviderConfig,
|
||||
updateEmbeddingProviderConfig,
|
||||
deleteEmbeddingProviderConfig,
|
||||
|
@ -102,12 +102,7 @@ export class NoteSummarizationTool implements ToolHandler {
|
||||
const cleanContent = this.cleanHtml(content);
|
||||
|
||||
// Generate the summary using the AI service
|
||||
const aiService = aiServiceManager.getService();
|
||||
|
||||
if (!aiService) {
|
||||
log.error('No AI service available for summarization');
|
||||
return `Error: No AI service is available for summarization`;
|
||||
}
|
||||
const aiService = await aiServiceManager.getService();
|
||||
|
||||
log.info(`Using ${aiService.getName()} to generate summary`);
|
||||
|
||||
|
@ -312,16 +312,7 @@ export class RelationshipTool implements ToolHandler {
|
||||
}
|
||||
|
||||
// Get the AI service for relationship suggestion
|
||||
const aiService = aiServiceManager.getService();
|
||||
|
||||
if (!aiService) {
|
||||
log.error('No AI service available for relationship suggestions');
|
||||
return {
|
||||
success: false,
|
||||
message: 'AI service not available for relationship suggestions',
|
||||
relatedNotes: relatedResult.relatedNotes
|
||||
};
|
||||
}
|
||||
const aiService = await aiServiceManager.getService();
|
||||
|
||||
log.info(`Using ${aiService.getName()} to suggest relationships for ${relatedResult.relatedNotes.length} related notes`);
|
||||
|
||||
|
@ -122,10 +122,10 @@ export class SearchNotesTool implements ToolHandler {
|
||||
// If summarization is requested
|
||||
if (summarize) {
|
||||
// Try to get an LLM service for summarization
|
||||
const llmService = aiServiceManager.getService();
|
||||
if (llmService) {
|
||||
try {
|
||||
const messages = [
|
||||
try {
|
||||
const llmService = await aiServiceManager.getService();
|
||||
|
||||
const messages = [
|
||||
{
|
||||
role: "system" as const,
|
||||
content: "Summarize the following note content concisely while preserving key information. Keep your summary to about 3-4 sentences."
|
||||
@ -147,13 +147,12 @@ export class SearchNotesTool implements ToolHandler {
|
||||
} as Record<string, boolean>))
|
||||
});
|
||||
|
||||
if (result && result.text) {
|
||||
return result.text;
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error summarizing content: ${error}`);
|
||||
// Fall through to smart truncation if summarization fails
|
||||
if (result && result.text) {
|
||||
return result.text;
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error summarizing content: ${error}`);
|
||||
// Fall through to smart truncation if summarization fails
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user