mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	feat(llm): really try to coax ollama to run tools
This commit is contained in:
		@@ -213,7 +213,23 @@ Be concise and informative in your responses.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
${context}
 | 
					${context}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Based on this information, please answer: <query>${query}</query>`
 | 
					Based on this information, please answer: <query>${query}</query>`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Tool instructions for Ollama
 | 
				
			||||||
 | 
					        TOOL_INSTRUCTIONS: `
 | 
				
			||||||
 | 
					CRITICAL INSTRUCTIONS FOR TOOL USAGE:
 | 
				
			||||||
 | 
					1. YOU MUST TRY MULTIPLE TOOLS AND SEARCH VARIATIONS before concluding information isn't available
 | 
				
			||||||
 | 
					2. ALWAYS PERFORM AT LEAST 3 DIFFERENT SEARCHES with different parameters before giving up on finding information
 | 
				
			||||||
 | 
					3. If a search returns no results, IMMEDIATELY TRY ANOTHER SEARCH with different parameters:
 | 
				
			||||||
 | 
					   - Use broader terms: If "Kubernetes deployment" fails, try just "Kubernetes" or "container orchestration"
 | 
				
			||||||
 | 
					   - Try synonyms: If "meeting notes" fails, try "conference", "discussion", or "conversation"
 | 
				
			||||||
 | 
					   - Remove specific qualifiers: If "quarterly financial report 2024" fails, try just "financial report"
 | 
				
			||||||
 | 
					   - Try semantic variations: If keyword_search fails, use vector_search which finds conceptually related content
 | 
				
			||||||
 | 
					4. CHAIN TOOLS TOGETHER: Use the results of one tool to inform parameters for the next tool
 | 
				
			||||||
 | 
					5. NEVER respond with "there are no notes about X" until you've tried at least 3 different search variations
 | 
				
			||||||
 | 
					6. DO NOT ask the user what to do next when searches fail - AUTOMATICALLY try different approaches
 | 
				
			||||||
 | 
					7. ALWAYS EXPLAIN what you're doing: "I didn't find results for X, so I'm now searching for Y instead"
 | 
				
			||||||
 | 
					8. If all reasonable search variations fail (minimum 3 attempts), THEN you may inform the user that the information might not be in their notes`
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Common prompts across providers
 | 
					    // Common prompts across providers
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import type { Message } from '../ai_interface.js';
 | 
					import type { Message } from '../ai_interface.js';
 | 
				
			||||||
import { BaseMessageFormatter } from './base_formatter.js';
 | 
					import { BaseMessageFormatter } from './base_formatter.js';
 | 
				
			||||||
import sanitizeHtml from 'sanitize-html';
 | 
					import sanitizeHtml from 'sanitize-html';
 | 
				
			||||||
import { PROVIDER_PROMPTS, FORMATTING_PROMPTS } from '../constants/llm_prompt_constants.js';
 | 
					import { PROVIDER_PROMPTS } from '../constants/llm_prompt_constants.js';
 | 
				
			||||||
import { LLM_CONSTANTS } from '../constants/provider_constants.js';
 | 
					import { LLM_CONSTANTS } from '../constants/provider_constants.js';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    HTML_ALLOWED_TAGS,
 | 
					    HTML_ALLOWED_TAGS,
 | 
				
			||||||
@@ -29,7 +29,7 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
 | 
				
			|||||||
     * @param context Optional context to include
 | 
					     * @param context Optional context to include
 | 
				
			||||||
     * @param preserveSystemPrompt When true, preserves existing system messages rather than replacing them
 | 
					     * @param preserveSystemPrompt When true, preserves existing system messages rather than replacing them
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    formatMessages(messages: Message[], systemPrompt?: string, context?: string, preserveSystemPrompt?: boolean): Message[] {
 | 
					    formatMessages(messages: Message[], systemPrompt?: string, context?: string, preserveSystemPrompt?: boolean, useTools?: boolean): Message[] {
 | 
				
			||||||
        const formattedMessages: Message[] = [];
 | 
					        const formattedMessages: Message[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Log the input messages with all their properties
 | 
					        // Log the input messages with all their properties
 | 
				
			||||||
@@ -61,7 +61,19 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
 | 
				
			|||||||
            log.info(`Preserving existing system message: ${systemMessages[0].content.substring(0, 50)}...`);
 | 
					            log.info(`Preserving existing system message: ${systemMessages[0].content.substring(0, 50)}...`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Use provided systemPrompt or default
 | 
					            // Use provided systemPrompt or default
 | 
				
			||||||
            const basePrompt = systemPrompt || PROVIDER_PROMPTS.COMMON.DEFAULT_ASSISTANT_INTRO;
 | 
					            let basePrompt = systemPrompt || PROVIDER_PROMPTS.COMMON.DEFAULT_ASSISTANT_INTRO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check if any message has tool_calls or if useTools flag is set, indicating this is a tool-using conversation
 | 
				
			||||||
 | 
					            const hasPreviousToolCalls = messages.some(msg => msg.tool_calls && msg.tool_calls.length > 0);
 | 
				
			||||||
 | 
					            const hasToolResults = messages.some(msg => msg.role === 'tool');
 | 
				
			||||||
 | 
					            const isToolUsingConversation = useTools || hasPreviousToolCalls || hasToolResults;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add tool instructions for Ollama when tools are being used
 | 
				
			||||||
 | 
					            if (isToolUsingConversation && PROVIDER_PROMPTS.OLLAMA.TOOL_INSTRUCTIONS) {
 | 
				
			||||||
 | 
					                log.info('Adding tool instructions to system prompt for Ollama');
 | 
				
			||||||
 | 
					                basePrompt = `${basePrompt}\n\n${PROVIDER_PROMPTS.OLLAMA.TOOL_INSTRUCTIONS}`;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            formattedMessages.push({
 | 
					            formattedMessages.push({
 | 
				
			||||||
                role: 'system',
 | 
					                role: 'system',
 | 
				
			||||||
                content: basePrompt
 | 
					                content: basePrompt
 | 
				
			||||||
@@ -151,13 +163,11 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
 | 
				
			|||||||
        if (!content) return '';
 | 
					        if (!content) return '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Store our XML tags so we can restore them after cleaning
 | 
					            // Define regexes for identifying and preserving tagged content
 | 
				
			||||||
            const noteTagsRegex = /<\/?note>/g;
 | 
					 | 
				
			||||||
            const notesTagsRegex = /<\/?notes>/g;
 | 
					            const notesTagsRegex = /<\/?notes>/g;
 | 
				
			||||||
            const queryTagsRegex = /<\/?query>[^<]*<\/query>/g;
 | 
					            // const queryTagsRegex = /<\/?query>/g; // Commenting out unused variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Capture tags to restore later
 | 
					            // Capture tags to restore later
 | 
				
			||||||
            const noteTags = content.match(noteTagsRegex) || [];
 | 
					 | 
				
			||||||
            const noteTagPositions: number[] = [];
 | 
					            const noteTagPositions: number[] = [];
 | 
				
			||||||
            let match;
 | 
					            let match;
 | 
				
			||||||
            const regex = /<\/?note>/g;
 | 
					            const regex = /<\/?note>/g;
 | 
				
			||||||
@@ -166,17 +176,15 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Remember the notes tags
 | 
					            // Remember the notes tags
 | 
				
			||||||
            const notesTagsMatch = content.match(notesTagsRegex) || [];
 | 
					 | 
				
			||||||
            const notesTagPositions: number[] = [];
 | 
					            const notesTagPositions: number[] = [];
 | 
				
			||||||
            while ((match = notesTagsRegex.exec(content)) !== null) {
 | 
					            while ((match = notesTagsRegex.exec(content)) !== null) {
 | 
				
			||||||
                notesTagPositions.push(match.index);
 | 
					                notesTagPositions.push(match.index);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Remember the query tags
 | 
					            // Remember the query tag
 | 
				
			||||||
            const queryTagsMatch = content.match(queryTagsRegex) || [];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Temporarily replace XML tags with markers that won't be affected by sanitization
 | 
					            // Temporarily replace XML tags with markers that won't be affected by sanitization
 | 
				
			||||||
            let modified = content
 | 
					            const modified = content
 | 
				
			||||||
                .replace(/<note>/g, '[NOTE_START]')
 | 
					                .replace(/<note>/g, '[NOTE_START]')
 | 
				
			||||||
                .replace(/<\/note>/g, '[NOTE_END]')
 | 
					                .replace(/<\/note>/g, '[NOTE_END]')
 | 
				
			||||||
                .replace(/<notes>/g, '[NOTES_START]')
 | 
					                .replace(/<notes>/g, '[NOTES_START]')
 | 
				
			||||||
@@ -184,7 +192,7 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
 | 
				
			|||||||
                .replace(/<query>(.*?)<\/query>/g, '[QUERY]$1[/QUERY]');
 | 
					                .replace(/<query>(.*?)<\/query>/g, '[QUERY]$1[/QUERY]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // First use the parent class to do standard cleaning
 | 
					            // First use the parent class to do standard cleaning
 | 
				
			||||||
            let sanitized = super.cleanContextContent(modified);
 | 
					            const sanitized = super.cleanContextContent(modified);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Then apply Ollama-specific aggressive cleaning
 | 
					            // Then apply Ollama-specific aggressive cleaning
 | 
				
			||||||
            // Remove any remaining HTML using sanitizeHtml while keeping our markers
 | 
					            // Remove any remaining HTML using sanitizeHtml while keeping our markers
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,14 +144,19 @@ export class OllamaService extends BaseAIService {
 | 
				
			|||||||
                messagesToSend = [...messages];
 | 
					                messagesToSend = [...messages];
 | 
				
			||||||
                log.info(`Bypassing formatter for Ollama request with ${messages.length} messages`);
 | 
					                log.info(`Bypassing formatter for Ollama request with ${messages.length} messages`);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Determine if tools will be used in this request
 | 
				
			||||||
 | 
					                const willUseTools = providerOptions.enableTools !== false;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                // Use the formatter to prepare messages
 | 
					                // Use the formatter to prepare messages
 | 
				
			||||||
                messagesToSend = this.formatter.formatMessages(
 | 
					                messagesToSend = this.formatter.formatMessages(
 | 
				
			||||||
                    messages,
 | 
					                    messages,
 | 
				
			||||||
                    systemPrompt,
 | 
					                    systemPrompt,
 | 
				
			||||||
                    undefined, // context
 | 
					                    undefined, // context
 | 
				
			||||||
                    providerOptions.preserveSystemPrompt
 | 
					                    providerOptions.preserveSystemPrompt,
 | 
				
			||||||
 | 
					                    willUseTools // Pass flag indicating if tools will be used
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                log.info(`Sending to Ollama with formatted messages: ${messagesToSend.length}`);
 | 
					                
 | 
				
			||||||
 | 
					                log.info(`Sending to Ollama with formatted messages: ${messagesToSend.length}${willUseTools ? ' (with tool instructions)' : ''}`);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Get tools if enabled
 | 
					            // Get tools if enabled
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user