diff --git a/src/services/llm/context/modules/query_enhancer.ts b/src/services/llm/context/modules/query_enhancer.ts index e2f2eeefe..25764b186 100644 --- a/src/services/llm/context/modules/query_enhancer.ts +++ b/src/services/llm/context/modules/query_enhancer.ts @@ -41,7 +41,10 @@ Format your answer as a valid JSON array without markdown code blocks, like this return cached; } - const messages: Message[] = [ + const messages: Array<{ + role: 'user' | 'assistant' | 'system'; + content: string; + }> = [ { role: "system", content: this.getEnhancedPrompt() }, { role: "user", content: userQuestion } ]; diff --git a/src/services/llm/pipeline/stages/semantic_context_extraction_stage.ts b/src/services/llm/pipeline/stages/semantic_context_extraction_stage.ts index c31337012..5addf606f 100644 --- a/src/services/llm/pipeline/stages/semantic_context_extraction_stage.ts +++ b/src/services/llm/pipeline/stages/semantic_context_extraction_stage.ts @@ -34,12 +34,12 @@ export class SemanticContextExtractionStage extends BasePipelineStage { + const toolResults = await Promise.all((response.tool_calls || []).map(async (toolCall, index) => { try { - log.info(`========== TOOL CALL ${index + 1} OF ${response.tool_calls.length} ==========`); + log.info(`========== TOOL CALL ${index + 1} OF ${response.tool_calls?.length || 0} ==========`); log.info(`Tool call ${index + 1} received - Name: ${toolCall.function.name}, ID: ${toolCall.id || 'unknown'}`); // Log parameters diff --git a/src/services/llm/rest_chat_service.ts b/src/services/llm/rest_chat_service.ts index 89dfb6a82..33ee7e2fc 100644 --- a/src/services/llm/rest_chat_service.ts +++ b/src/services/llm/rest_chat_service.ts @@ -874,8 +874,8 @@ class RestChatService { if (typeof toolCall.function.arguments === 'string') { try { args = JSON.parse(toolCall.function.arguments); - } catch (e) { - log.error(`Failed to parse tool arguments: ${e.message}`); + } catch (e: unknown) { + log.error(`Failed to parse tool arguments: ${e instanceof Error ? e.message : String(e)}`); // Try cleanup and retry try { diff --git a/src/services/llm/tools/attribute_manager_tool.ts b/src/services/llm/tools/attribute_manager_tool.ts index 2d322b1d9..4739626c1 100644 --- a/src/services/llm/tools/attribute_manager_tool.ts +++ b/src/services/llm/tools/attribute_manager_tool.ts @@ -116,7 +116,7 @@ export class AttributeManagerTool implements ToolHandler { } // Create the attribute - await attributes.createAttribute(noteId, attributeName, value); + await attributes.createLabel(noteId, attributeName, value); const duration = Date.now() - startTime; log.info(`Added attribute ${attributeName}=${value || ''} in ${duration}ms`); @@ -153,7 +153,18 @@ export class AttributeManagerTool implements ToolHandler { // Remove all matching attributes for (const attr of attributesToRemove) { - await attributes.deleteAttribute(attr.attributeId); + // Delete attribute by recreating it with isDeleted flag + const attrToDelete = { + attributeId: attr.attributeId, + noteId: attr.noteId, + type: attr.type, + name: attr.name, + value: attr.value, + isDeleted: true, + position: attr.position, + utcDateModified: new Date().toISOString() + }; + await attributes.createAttribute(attrToDelete); } const duration = Date.now() - startTime; @@ -195,7 +206,18 @@ export class AttributeManagerTool implements ToolHandler { // Update all matching attributes for (const attr of attributesToUpdate) { - await attributes.updateAttributeValue(attr.attributeId, attributeValue); + // Update by recreating with the same ID but new value + const attrToUpdate = { + attributeId: attr.attributeId, + noteId: attr.noteId, + type: attr.type, + name: attr.name, + value: attributeValue, + isDeleted: false, + position: attr.position, + utcDateModified: new Date().toISOString() + }; + await attributes.createAttribute(attrToUpdate); } const duration = Date.now() - startTime; diff --git a/src/services/llm/tools/calendar_integration_tool.ts b/src/services/llm/tools/calendar_integration_tool.ts index a9db37708..d11bec39c 100644 --- a/src/services/llm/tools/calendar_integration_tool.ts +++ b/src/services/llm/tools/calendar_integration_tool.ts @@ -221,13 +221,14 @@ export class CalendarIntegrationTool implements ToolHandler { // Create the new note const createStartTime = Date.now(); - const noteId = await notes.createNewNote({ + const result = notes.createNewNote({ parentNoteId: parent.noteId, title: title, content: content, - type: 'text', + type: 'text' as const, mime: 'text/html' }); + const noteId = result.note.noteId; const createDuration = Date.now() - createStartTime; if (!noteId) { diff --git a/src/services/llm/tools/content_extraction_tool.ts b/src/services/llm/tools/content_extraction_tool.ts index 5412cfd10..121220713 100644 --- a/src/services/llm/tools/content_extraction_tool.ts +++ b/src/services/llm/tools/content_extraction_tool.ts @@ -88,22 +88,22 @@ export class ContentExtractionTool implements ToolHandler { const extractedContent: any = {}; if (extractionType === 'lists' || extractionType === 'all') { - extractedContent.lists = this.extractLists(content); + extractedContent.lists = this.extractLists(typeof content === 'string' ? content : content.toString()); log.info(`Extracted ${extractedContent.lists.length} lists`); } if (extractionType === 'tables' || extractionType === 'all') { - extractedContent.tables = this.extractTables(content); + extractedContent.tables = this.extractTables(typeof content === 'string' ? content : content.toString()); log.info(`Extracted ${extractedContent.tables.length} tables`); } if (extractionType === 'headings' || extractionType === 'all') { - extractedContent.headings = this.extractHeadings(content); + extractedContent.headings = this.extractHeadings(typeof content === 'string' ? content : content.toString()); log.info(`Extracted ${extractedContent.headings.length} headings`); } if (extractionType === 'codeBlocks' || extractionType === 'all') { - extractedContent.codeBlocks = this.extractCodeBlocks(content); + extractedContent.codeBlocks = this.extractCodeBlocks(typeof content === 'string' ? content : content.toString()); log.info(`Extracted ${extractedContent.codeBlocks.length} code blocks`); } @@ -315,46 +315,42 @@ export class ContentExtractionTool implements ToolHandler { private filterContentByQuery(content: any, query: string): void { const lowerQuery = query.toLowerCase(); - // Filter lists if (content.lists) { - content.lists = content.lists.filter(list => { - // Keep the list if any item matches the query - return list.items.some(item => item.toLowerCase().includes(lowerQuery)); + content.lists = content.lists.filter((list: { type: string; items: string[] }) => { + // Check if any item in the list contains the query + return list.items.some((item: string) => item.toLowerCase().includes(lowerQuery)); }); // Also filter individual items in each list - content.lists.forEach(list => { - list.items = list.items.filter(item => item.toLowerCase().includes(lowerQuery)); + content.lists.forEach((list: { type: string; items: string[] }) => { + list.items = list.items.filter((item: string) => item.toLowerCase().includes(lowerQuery)); }); } - // Filter headings if (content.headings) { - content.headings = content.headings.filter(heading => + content.headings = content.headings.filter((heading: { level: number; text: string }) => heading.text.toLowerCase().includes(lowerQuery) ); } - // Filter tables if (content.tables) { - content.tables = content.tables.filter(table => { - // Check headers - const headerMatch = table.headers.some(header => + content.tables = content.tables.filter((table: { headers: string[]; rows: string[][] }) => { + // Check if any header contains the query + const headerMatch = table.headers.some((header: string) => header.toLowerCase().includes(lowerQuery) ); - // Check cells - const cellMatch = table.rows.some(row => - row.some(cell => cell.toLowerCase().includes(lowerQuery)) + // Check if any cell in any row contains the query + const cellMatch = table.rows.some((row: string[]) => + row.some((cell: string) => cell.toLowerCase().includes(lowerQuery)) ); return headerMatch || cellMatch; }); } - // Filter code blocks if (content.codeBlocks) { - content.codeBlocks = content.codeBlocks.filter(block => + content.codeBlocks = content.codeBlocks.filter((block: { language?: string; code: string }) => block.code.toLowerCase().includes(lowerQuery) ); } diff --git a/src/services/llm/tools/note_creation_tool.ts b/src/services/llm/tools/note_creation_tool.ts index 326b2796e..58cd34dc4 100644 --- a/src/services/llm/tools/note_creation_tool.ts +++ b/src/services/llm/tools/note_creation_tool.ts @@ -8,6 +8,7 @@ import type { Tool, ToolHandler } from './tool_interfaces.js'; import log from '../../log.js'; import becca from '../../../becca/becca.js'; import notes from '../../notes.js'; +import attributes from '../../attributes.js'; /** * Definition of the note creation tool @@ -43,20 +44,7 @@ export const noteCreationToolDefinition: Tool = { }, attributes: { type: 'array', - description: 'Array of attributes to set on the note (e.g., [{"name":"#tag"}, {"name":"priority", "value":"high"}])', - items: { - type: 'object', - properties: { - name: { - type: 'string', - description: 'Name of the attribute' - }, - value: { - type: 'string', - description: 'Value of the attribute (if applicable)' - } - } - } + description: 'Array of attributes to set on the note (e.g., [{"name":"#tag"}, {"name":"priority", "value":"high"}])' } }, required: ['title', 'content'] @@ -87,7 +75,7 @@ export class NoteCreationTool implements ToolHandler { log.info(`Executing create_note tool - Title: "${title}", Type: ${type}, ParentNoteId: ${parentNoteId || 'root'}`); // Validate parent note exists if specified - let parent; + let parent = null; if (parentNoteId) { parent = becca.notes[parentNoteId]; if (!parent) { @@ -98,6 +86,11 @@ export class NoteCreationTool implements ToolHandler { parent = becca.getNote('root'); } + // Make sure we have a valid parent at this point + if (!parent) { + return 'Error: Failed to get a valid parent note. Root note may not be accessible.'; + } + // Determine the appropriate mime type let noteMime = mime; if (!noteMime) { @@ -122,13 +115,14 @@ export class NoteCreationTool implements ToolHandler { // Create the note const createStartTime = Date.now(); - const noteId = await notes.createNewNote({ + const result = notes.createNewNote({ parentNoteId: parent.noteId, title: title, content: content, - type: type, + type: type as any, // Cast as any since not all string values may match the exact NoteType union mime: noteMime }); + const noteId = result.note.noteId; const createDuration = Date.now() - createStartTime; if (!noteId) { @@ -145,7 +139,18 @@ export class NoteCreationTool implements ToolHandler { if (!attr.name) continue; const attrStartTime = Date.now(); - await notes.createAttribute(noteId, attr.name, attr.value || ''); + // Use createLabel for label attributes + if (attr.name.startsWith('#') || attr.name.startsWith('~')) { + await attributes.createLabel(noteId, attr.name.substring(1), attr.value || ''); + } else { + // Use createRelation for relation attributes if value looks like a note ID + if (attr.value && attr.value.match(/^[a-zA-Z0-9_]{12}$/)) { + await attributes.createRelation(noteId, attr.name, attr.value); + } else { + // Default to label for other attributes + await attributes.createLabel(noteId, attr.name, attr.value || ''); + } + } const attrDuration = Date.now() - attrStartTime; log.info(`Added attribute ${attr.name}=${attr.value || ''} in ${attrDuration}ms`); diff --git a/src/services/llm/tools/note_update_tool.ts b/src/services/llm/tools/note_update_tool.ts index 6e43bed8e..6e9c90c01 100644 --- a/src/services/llm/tools/note_update_tool.ts +++ b/src/services/llm/tools/note_update_tool.ts @@ -7,6 +7,7 @@ import type { Tool, ToolHandler } from './tool_interfaces.js'; import log from '../../log.js'; import becca from '../../../becca/becca.js'; +import notes from '../../notes.js'; /** * Definition of the note update tool @@ -79,7 +80,10 @@ export class NoteUpdateTool implements ToolHandler { const titleStartTime = Date.now(); try { - await note.setTitle(title); + // Update the note title by setting it and saving + note.title = title; + note.save(); + const titleDuration = Date.now() - titleStartTime; log.info(`Updated note title to "${title}" in ${titleDuration}ms`); titleUpdateResult = `Title updated from "${note.title}" to "${title}"`; diff --git a/src/services/llm/tools/relationship_tool.ts b/src/services/llm/tools/relationship_tool.ts index a9e010e4a..2652c464b 100644 --- a/src/services/llm/tools/relationship_tool.ts +++ b/src/services/llm/tools/relationship_tool.ts @@ -194,23 +194,27 @@ export class RelationshipTool implements ToolHandler { } // Get incoming relationships (where this note is the target) - const incomingNotes = becca.findNotesWithRelation(sourceNote.noteId); + // Since becca.findNotesWithRelation doesn't exist, use attributes to find notes with relation const incomingRelations = []; - for (const sourceOfRelation of incomingNotes) { - const incomingAttributes = sourceOfRelation.getOwnedAttributes() - .filter((attr: any) => attr.type === 'relation' && attr.value === sourceNote.noteId); + // Find all attributes of type relation that point to this note + const relationAttributes = sourceNote.getTargetRelations(); - for (const attr of incomingAttributes) { - incomingRelations.push({ - relationName: attr.name, - sourceNoteId: sourceOfRelation.noteId, - sourceTitle: sourceOfRelation.title - }); - } + for (const attr of relationAttributes) { + if (attr.type === 'relation') { + const sourceOfRelation = attr.getNote(); - if (incomingRelations.length >= limit) { - break; + if (sourceOfRelation && !sourceOfRelation.isDeleted) { + incomingRelations.push({ + relationName: attr.name, + sourceNoteId: sourceOfRelation.noteId, + sourceTitle: sourceOfRelation.title + }); + + if (incomingRelations.length >= limit) { + break; + } + } } }