reduce the use of any, part 3

This commit is contained in:
perf3ct 2025-04-16 17:29:35 +00:00
parent 4601e3bfdb
commit 80ea2c3eef
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
4 changed files with 127 additions and 83 deletions

View File

@ -18,6 +18,17 @@ import type {
} from './interfaces/ai_service_interfaces.js'; } from './interfaces/ai_service_interfaces.js';
import type { NoteSearchResult } from './interfaces/context_interfaces.js'; import type { NoteSearchResult } from './interfaces/context_interfaces.js';
/**
* Interface representing relevant note context
*/
interface NoteContext {
title: string;
content?: string;
noteId?: string;
summary?: string;
score?: number;
}
export class AIServiceManager implements IAIServiceManager { export class AIServiceManager implements IAIServiceManager {
private services: Record<ServiceProviders, AIService> = { private services: Record<ServiceProviders, AIService> = {
openai: new OpenAIService(), openai: new OpenAIService(),
@ -31,30 +42,30 @@ export class AIServiceManager implements IAIServiceManager {
constructor() { constructor() {
// Initialize provider order immediately // Initialize provider order immediately
this.updateProviderOrder(); this.updateProviderOrder();
// Initialize tools immediately // Initialize tools immediately
this.initializeTools().catch(error => { this.initializeTools().catch(error => {
log.error(`Error initializing LLM tools during AIServiceManager construction: ${error.message || String(error)}`); log.error(`Error initializing LLM tools during AIServiceManager construction: ${error.message || String(error)}`);
}); });
} }
/** /**
* Initialize all LLM tools in one place * Initialize all LLM tools in one place
*/ */
private async initializeTools(): Promise<void> { private async initializeTools(): Promise<void> {
try { try {
log.info('Initializing LLM tools during AIServiceManager construction...'); log.info('Initializing LLM tools during AIServiceManager construction...');
// Initialize agent tools // Initialize agent tools
await agentTools.initialize(true); await this.initializeAgentTools();
log.info("Agent tools initialized successfully"); log.info("Agent tools initialized successfully");
// Initialize LLM tools // Initialize LLM tools
const toolInitializer = await import('./tools/tool_initializer.js'); const toolInitializer = await import('./tools/tool_initializer.js');
await toolInitializer.default.initializeTools(); await toolInitializer.default.initializeTools();
log.info("LLM tools initialized successfully"); log.info("LLM tools initialized successfully");
} catch (error: any) { } catch (error: unknown) {
log.error(`Error initializing tools: ${error.message || String(error)}`); log.error(`Error initializing tools: ${this.handleError(error)}`);
// Don't throw, just log the error to prevent breaking construction // Don't throw, just log the error to prevent breaking construction
} }
} }
@ -463,10 +474,8 @@ export class AIServiceManager implements IAIServiceManager {
} }
/** /**
* Get agent tools context for a specific note * Get enhanced context with available agent tools
* This context enriches LLM prompts with tools that can interact with Trilium * @param noteId - The ID of the note
*
* @param noteId - The note ID
* @param query - The user's query * @param query - The user's query
* @param showThinking - Whether to show LLM's thinking process * @param showThinking - Whether to show LLM's thinking process
* @param relevantNotes - Optional notes already found to be relevant * @param relevantNotes - Optional notes already found to be relevant
@ -476,12 +485,12 @@ export class AIServiceManager implements IAIServiceManager {
noteId: string, noteId: string,
query: string, query: string,
showThinking: boolean = false, showThinking: boolean = false,
relevantNotes: Array<any> = [] relevantNotes: NoteSearchResult[] = []
): Promise<string> { ): Promise<string> {
try { try {
// Create agent tools message // Create agent tools message
const toolsMessage = await this.getAgentToolsDescription(); const toolsMessage = await this.getAgentToolsDescription();
// Agent tools are already initialized in the constructor // Agent tools are already initialized in the constructor
// No need to initialize them again // No need to initialize them again
@ -508,7 +517,7 @@ export class AIServiceManager implements IAIServiceManager {
log.info(`Found ${contextNotes.length} relevant notes for context`); log.info(`Found ${contextNotes.length} relevant notes for context`);
} catch (error) { } catch (error) {
log.error(`Failed to find relevant notes: ${error}`); log.error(`Failed to find relevant notes: ${this.handleError(error)}`);
// Continue without context notes // Continue without context notes
contextNotes = []; contextNotes = [];
} }
@ -526,7 +535,7 @@ export class AIServiceManager implements IAIServiceManager {
// Combine tool message with context // Combine tool message with context
return toolsMessage + contextStr; return toolsMessage + contextStr;
} catch (error) { } catch (error) {
log.error(`Error getting agent tools context: ${error}`); log.error(`Error getting agent tools context: ${this.handleError(error)}`);
return ""; return "";
} }
} }
@ -600,6 +609,16 @@ export class AIServiceManager implements IAIServiceManager {
defaultModel: 'default' defaultModel: 'default'
}; };
} }
/**
* Error handler that properly types the error object
*/
private handleError(error: unknown): string {
if (error instanceof Error) {
return error.message || String(error);
}
return String(error);
}
} }
// Don't create singleton immediately, use a lazy-loading pattern // Don't create singleton immediately, use a lazy-loading pattern
@ -662,7 +681,7 @@ export default {
noteId: string, noteId: string,
query: string, query: string,
showThinking: boolean = false, showThinking: boolean = false,
relevantNotes: Array<any> = [] relevantNotes: NoteSearchResult[] = []
): Promise<string> { ): Promise<string> {
return getInstance().getAgentToolsContext( return getInstance().getAgentToolsContext(
noteId, noteId,

View File

@ -6,17 +6,26 @@ import { ChatPipeline } from './pipeline/chat_pipeline.js';
import type { ChatPipelineConfig, StreamCallback } from './pipeline/interfaces.js'; import type { ChatPipelineConfig, StreamCallback } from './pipeline/interfaces.js';
import aiServiceManager from './ai_service_manager.js'; import aiServiceManager from './ai_service_manager.js';
import type { ChatPipelineInput } from './pipeline/interfaces.js'; import type { ChatPipelineInput } from './pipeline/interfaces.js';
import type { NoteSearchResult } from './interfaces/context_interfaces.js';
// Update the ChatCompletionOptions interface to include the missing properties // Update the ChatCompletionOptions interface to include the missing properties
// TODO fix
declare module './ai_interface.js' { declare module './ai_interface.js' {
interface ChatCompletionOptions { interface ChatCompletionOptions {
pipeline?: string; pipeline?: string;
noteId?: string; noteId?: string;
useAdvancedContext?: boolean; useAdvancedContext?: boolean;
showThinking?: boolean;
enableTools?: boolean;
} }
} }
// Add a type for context extraction result
interface ContextExtractionResult {
context: string;
sources?: NoteSearchResult[];
thinking?: string;
}
export interface ChatSession { export interface ChatSession {
id: string; id: string;
title: string; title: string;
@ -144,7 +153,7 @@ export class ChatService {
...(options || session.options || {}), ...(options || session.options || {}),
sessionId: session.id sessionId: session.id
}; };
// Execute the pipeline // Execute the pipeline
const response = await pipeline.execute({ const response = await pipeline.execute({
messages: session.messages, messages: session.messages,
@ -156,7 +165,8 @@ export class ChatService {
// Add assistant message // Add assistant message
const assistantMessage: Message = { const assistantMessage: Message = {
role: 'assistant', role: 'assistant',
content: response.text content: response.text,
tool_calls: response.tool_calls
}; };
session.messages.push(assistantMessage); session.messages.push(assistantMessage);
@ -168,13 +178,13 @@ export class ChatService {
provider: response.provider, provider: response.provider,
usage: response.usage usage: response.usage
}; };
// If there are tool calls, make sure they're stored in metadata // If there are tool calls, make sure they're stored in metadata
if (response.tool_calls && response.tool_calls.length > 0) { if (response.tool_calls && response.tool_calls.length > 0) {
// Let the storage service extract and save tool executions // Let the storage service extract and save tool executions
// The tool results are already in the messages // The tool results are already in the messages
} }
// Save the complete conversation with metadata // Save the complete conversation with metadata
await chatStorageService.updateChat(session.id, session.messages, undefined, metadata); await chatStorageService.updateChat(session.id, session.messages, undefined, metadata);
@ -187,9 +197,9 @@ export class ChatService {
return session; return session;
} catch (error: any) { } catch (error: unknown) {
session.isStreaming = false; session.isStreaming = false;
console.error('Error in AI chat:', error); console.error('Error in AI chat:', this.handleError(error));
// Add error message // Add error message
const errorMessage: Message = { const errorMessage: Message = {
@ -266,7 +276,8 @@ export class ChatService {
// Add assistant message // Add assistant message
const assistantMessage: Message = { const assistantMessage: Message = {
role: 'assistant', role: 'assistant',
content: response.text content: response.text,
tool_calls: response.tool_calls
}; };
session.messages.push(assistantMessage); session.messages.push(assistantMessage);
@ -279,13 +290,13 @@ export class ChatService {
usage: response.usage, usage: response.usage,
contextNoteId: noteId // Store the note ID used for context contextNoteId: noteId // Store the note ID used for context
}; };
// If there are tool calls, make sure they're stored in metadata // If there are tool calls, make sure they're stored in metadata
if (response.tool_calls && response.tool_calls.length > 0) { if (response.tool_calls && response.tool_calls.length > 0) {
// Let the storage service extract and save tool executions // Let the storage service extract and save tool executions
// The tool results are already in the messages // The tool results are already in the messages
} }
// Save the complete conversation with metadata // Save the complete conversation with metadata
await chatStorageService.updateChat(session.id, session.messages, undefined, metadata); await chatStorageService.updateChat(session.id, session.messages, undefined, metadata);
@ -298,9 +309,9 @@ export class ChatService {
return session; return session;
} catch (error: any) { } catch (error: unknown) {
session.isStreaming = false; session.isStreaming = false;
console.error('Error in context-aware chat:', error); console.error('Error in context-aware chat:', this.handleError(error));
// Add error message // Add error message
const errorMessage: Message = { const errorMessage: Message = {
@ -343,7 +354,7 @@ export class ChatService {
noteId, noteId,
query: lastUserMessage, query: lastUserMessage,
useSmartContext useSmartContext
}); }) as ContextExtractionResult;
const contextMessage: Message = { const contextMessage: Message = {
role: 'user', role: 'user',
@ -351,28 +362,27 @@ export class ChatService {
}; };
session.messages.push(contextMessage); session.messages.push(contextMessage);
// Store the context note id in metadata // Store the context note id in metadata
const metadata = { const metadata = {
contextNoteId: noteId contextNoteId: noteId
}; };
// Check if the context extraction result has sources // Check if the context extraction result has sources
// Note: We're adding a defensive check since TypeScript doesn't know about this property if (contextResult.sources && contextResult.sources.length > 0) {
const contextSources = (contextResult as any).sources || []; // Convert the sources to match expected format (handling null vs undefined)
if (contextSources && contextSources.length > 0) { const sources = contextResult.sources.map(source => ({
// Convert the sources to the format expected by recordSources
const sources = contextSources.map((source: any) => ({
noteId: source.noteId, noteId: source.noteId,
title: source.title, title: source.title,
similarity: source.similarity, similarity: source.similarity,
content: source.content // Replace null with undefined for content
content: source.content === null ? undefined : source.content
})); }));
// Store these sources in metadata // Store these sources in metadata
await chatStorageService.recordSources(session.id, sources); await chatStorageService.recordSources(session.id, sources);
} }
await chatStorageService.updateChat(session.id, session.messages, undefined, metadata); await chatStorageService.updateChat(session.id, session.messages, undefined, metadata);
return session; return session;
@ -380,11 +390,6 @@ export class ChatService {
/** /**
* Add semantically relevant context from a note based on a specific query * Add semantically relevant context from a note based on a specific query
*
* @param sessionId - The ID of the chat session
* @param noteId - The ID of the note to add context from
* @param query - The specific query to find relevant information for
* @returns The updated chat session
*/ */
async addSemanticNoteContext(sessionId: string, noteId: string, query: string): Promise<ChatSession> { async addSemanticNoteContext(sessionId: string, noteId: string, query: string): Promise<ChatSession> {
const session = await this.getOrCreateSession(sessionId); const session = await this.getOrCreateSession(sessionId);
@ -404,28 +409,27 @@ export class ChatService {
}; };
session.messages.push(contextMessage); session.messages.push(contextMessage);
// Store the context note id and query in metadata // Store the context note id and query in metadata
const metadata = { const metadata = {
contextNoteId: noteId contextNoteId: noteId
}; };
// Check if the semantic context extraction result has sources // Check if the semantic context extraction result has sources
// Note: We're adding a defensive check since TypeScript doesn't know about this property const contextSources = (contextResult as ContextExtractionResult).sources || [];
const contextSources = (contextResult as any).sources || [];
if (contextSources && contextSources.length > 0) { if (contextSources && contextSources.length > 0) {
// Convert the sources to the format expected by recordSources // Convert the sources to the format expected by recordSources
const sources = contextSources.map((source: any) => ({ const sources = contextSources.map((source) => ({
noteId: source.noteId, noteId: source.noteId,
title: source.title, title: source.title,
similarity: source.similarity, similarity: source.similarity,
content: source.content content: source.content === null ? undefined : source.content
})); }));
// Store these sources in metadata // Store these sources in metadata
await chatStorageService.recordSources(session.id, sources); await chatStorageService.recordSources(session.id, sources);
} }
await chatStorageService.updateChat(session.id, session.messages, undefined, metadata); await chatStorageService.updateChat(session.id, session.messages, undefined, metadata);
return session; return session;
@ -456,7 +460,7 @@ export class ChatService {
/** /**
* Get pipeline performance metrics * Get pipeline performance metrics
*/ */
getPipelineMetrics(pipelineType: string = 'default'): any { getPipelineMetrics(pipelineType: string = 'default'): unknown {
const pipeline = this.getPipeline(pipelineType); const pipeline = this.getPipeline(pipelineType);
return pipeline.getMetrics(); return pipeline.getMetrics();
} }
@ -542,11 +546,21 @@ export class ChatService {
// If not using advanced context, use direct service call // If not using advanced context, use direct service call
return await service.generateChatCompletion(messages, options); return await service.generateChatCompletion(messages, options);
} catch (error: any) { } catch (error: unknown) {
console.error('Error in generateChatCompletion:', error); console.error('Error in generateChatCompletion:', error);
throw error; throw error;
} }
} }
/**
* Error handler utility
*/
private handleError(error: unknown): string {
if (error instanceof Error) {
return error.message || String(error);
}
return String(error);
}
} }
// Singleton instance // Singleton instance

View File

@ -57,6 +57,16 @@ export class NoteNavigatorTool {
private maxBreadth: number = SEARCH_CONSTANTS.HIERARCHY.MAX_BREADTH; private maxBreadth: number = SEARCH_CONSTANTS.HIERARCHY.MAX_BREADTH;
private maxDepth: number = SEARCH_CONSTANTS.HIERARCHY.MAX_DEPTH; private maxDepth: number = SEARCH_CONSTANTS.HIERARCHY.MAX_DEPTH;
/**
* Error handler that properly types the error object
*/
private handleError(error: unknown): string {
if (error instanceof Error) {
return error.message || String(error);
}
return String(error);
}
/** /**
* Get detailed information about a note * Get detailed information about a note
*/ */
@ -84,8 +94,8 @@ export class NoteNavigatorTool {
attributeNames, attributeNames,
hasChildren: note.children.length > 0 hasChildren: note.children.length > 0
}; };
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note info: ${error.message}`); log.error(`Error getting note info: ${this.handleError(error)}`);
return null; return null;
} }
} }
@ -118,8 +128,8 @@ export class NoteNavigatorTool {
notePathTitles: titles notePathTitles: titles
}; };
}).sort((a, b) => a.notePath.length - b.notePath.length); // Sort by path length, shortest first }).sort((a, b) => a.notePath.length - b.notePath.length); // Sort by path length, shortest first
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note paths: ${error.message}`); log.error(`Error getting note paths: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -154,8 +164,8 @@ export class NoteNavigatorTool {
} }
return result; return result;
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note hierarchy: ${error.message}`); log.error(`Error getting note hierarchy: ${this.handleError(error)}`);
return null; return null;
} }
} }
@ -185,7 +195,8 @@ export class NoteNavigatorTool {
} }
return result; return result;
} catch (error) { } catch (error: unknown) {
log.error(`Error in _getHierarchyLevel: ${this.handleError(error)}`);
return null; return null;
} }
} }
@ -201,8 +212,8 @@ export class NoteNavigatorTool {
} }
return note.ownedAttributes; return note.ownedAttributes;
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note attributes: ${error.message}`); log.error(`Error getting note attributes: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -280,8 +291,8 @@ export class NoteNavigatorTool {
// No path found // No path found
return null; return null;
} catch (error: any) { } catch (error: unknown) {
log.error(`Error finding path between notes: ${error.message}`); log.error(`Error finding path between notes: ${this.handleError(error)}`);
return null; return null;
} }
} }
@ -312,8 +323,8 @@ export class NoteNavigatorTool {
} }
return results; return results;
} catch (error: any) { } catch (error: unknown) {
log.error(`Error searching notes by title: ${error.message}`); log.error(`Error searching notes by title: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -338,8 +349,8 @@ export class NoteNavigatorTool {
return parents return parents
.map(parent => this.getNoteInfo(parent.noteId)) .map(parent => this.getNoteInfo(parent.noteId))
.filter((info): info is NoteInfo => info !== null); .filter((info): info is NoteInfo => info !== null);
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note clones: ${error.message}`); log.error(`Error getting note clones: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -429,8 +440,8 @@ export class NoteNavigatorTool {
} }
return result; return result;
} catch (error: any) { } catch (error: unknown) {
log.error(`Error getting note context: ${error.message}`); log.error(`Error getting note context: ${this.handleError(error)}`);
return "Error generating note context description."; return "Error generating note context description.";
} }
} }
@ -503,8 +514,8 @@ export class NoteNavigatorTool {
attributes, attributes,
parentPath parentPath
}; };
} catch (error) { } catch (error: unknown) {
log.error(`Error getting note structure: ${error}`); log.error(`Error getting note structure: ${this.handleError(error)}`);
// Return a minimal structure with empty arrays to avoid null errors // Return a minimal structure with empty arrays to avoid null errors
return { return {
noteId, noteId,
@ -534,8 +545,8 @@ export class NoteNavigatorTool {
noteId: child.noteId, noteId: child.noteId,
title: child.title title: child.title
})); }));
} catch (error) { } catch (error: unknown) {
log.error(`Error getting child notes: ${error}`); log.error(`Error getting child notes: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -555,8 +566,8 @@ export class NoteNavigatorTool {
noteId: parent.noteId, noteId: parent.noteId,
title: parent.title title: parent.title
})); }));
} catch (error) { } catch (error: unknown) {
log.error(`Error getting parent notes: ${error}`); log.error(`Error getting parent notes: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -613,8 +624,8 @@ export class NoteNavigatorTool {
} }
return [...outboundLinks, ...inboundLinks.slice(0, Math.floor(limit / 2))]; return [...outboundLinks, ...inboundLinks.slice(0, Math.floor(limit / 2))];
} catch (error) { } catch (error: unknown) {
log.error(`Error getting linked notes: ${error}`); log.error(`Error getting linked notes: ${this.handleError(error)}`);
return []; return [];
} }
} }
@ -629,8 +640,8 @@ export class NoteNavigatorTool {
path.push(structure.title); path.push(structure.title);
return path.join(' > '); return path.join(' > ');
} catch (error) { } catch (error: unknown) {
log.error(`Error getting note path: ${error}`); log.error(`Error getting note path: ${this.handleError(error)}`);
return 'Unknown path'; return 'Unknown path';
} }
} }

View File

@ -42,7 +42,7 @@ export interface ToolCall {
type?: string; type?: string;
function: { function: {
name: string; name: string;
arguments: Record<string, any> | string; arguments: Record<string, unknown> | string;
}; };
} }
@ -58,5 +58,5 @@ export interface ToolHandler {
/** /**
* Execute the tool with the given arguments * Execute the tool with the given arguments
*/ */
execute(args: Record<string, any>): Promise<string | object>; execute(args: Record<string, unknown>): Promise<string | object>;
} }