mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
- Remove unused `path` property from Conversation class and its test - Initialize `items` and `conversations` arrays in ConversationHistoryModal - Use `Exception.messageFrom()` for safer error serialization in StreamingMarkdownService - Fix redundant type cast in obsidian mock's Events.on() - Bump @anthropic-ai/sdk, @google/genai, openai, esbuild, svelte, typescript, vitest, and other minor deps
625 lines
21 KiB
TypeScript
625 lines
21 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { Conversation } from '../../Conversations/Conversation';
|
|
import { ConversationContent } from '../../Conversations/ConversationContent';
|
|
import { Role } from '../../Enums/Role';
|
|
import { AIToolResponse } from '../../AIClasses/ToolDefinitions/AIToolResponse';
|
|
import { AITool } from '../../Enums/AITool';
|
|
import { MimeType } from '../../Enums/MimeType';
|
|
import { StringTools } from '../../Helpers/StringTools';
|
|
|
|
describe('Conversation', () => {
|
|
describe('constructor', () => {
|
|
it('should create a new conversation with current date', () => {
|
|
const before = new Date();
|
|
const conversation = new Conversation();
|
|
const after = new Date();
|
|
|
|
expect(conversation.created.getTime()).toBeGreaterThanOrEqual(before.getTime());
|
|
expect(conversation.created.getTime()).toBeLessThanOrEqual(after.getTime());
|
|
});
|
|
|
|
it('should initialize updated date to match created date', () => {
|
|
const conversation = new Conversation();
|
|
|
|
expect(conversation.updated.getTime()).toBeCloseTo(conversation.created.getTime(), -1);
|
|
});
|
|
|
|
it('should generate title from created date', () => {
|
|
const conversation = new Conversation();
|
|
|
|
// Title should be a formatted date string
|
|
expect(conversation.title).toBeDefined();
|
|
expect(conversation.title).toMatch(/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}$/);
|
|
});
|
|
|
|
it('should initialize contents as empty array', () => {
|
|
const conversation = new Conversation();
|
|
|
|
expect(conversation.contents).toEqual([]);
|
|
expect(Array.isArray(conversation.contents)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('isConversationData', () => {
|
|
it('should return true for valid conversation data', () => {
|
|
const validData = {
|
|
title: 'Test Conversation',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(validData)).toBe(true);
|
|
});
|
|
|
|
it('should return true for valid conversation data with contents', () => {
|
|
const validData = {
|
|
title: 'Test Conversation',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: [
|
|
{
|
|
role: 'user',
|
|
content: 'Hello',
|
|
promptContent: '',
|
|
toolCall: '',
|
|
timestamp: '2024-01-01T00:00:00.000Z',
|
|
isToolCall: false,
|
|
isToolCallResponse: false,
|
|
isProviderSpecificContent: false
|
|
}
|
|
]
|
|
};
|
|
|
|
expect(Conversation.isConversationData(validData)).toBe(true);
|
|
});
|
|
|
|
it('should return false when data is null', () => {
|
|
expect(Conversation.isConversationData(null)).toBe(false);
|
|
});
|
|
|
|
it('should return false when data is not an object', () => {
|
|
expect(Conversation.isConversationData('string')).toBe(false);
|
|
expect(Conversation.isConversationData(123)).toBe(false);
|
|
expect(Conversation.isConversationData(true)).toBe(false);
|
|
expect(Conversation.isConversationData([])).toBe(false);
|
|
});
|
|
|
|
it('should return false when title is missing', () => {
|
|
const invalidData = {
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when title is not a string', () => {
|
|
const invalidData = {
|
|
title: 123,
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when created is missing', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when created is not a string', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: new Date(),
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when updated is missing', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when updated is not a string', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: 123,
|
|
contents: []
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when contents is missing', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z'
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when contents is not an array', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: 'not an array'
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when contents contains invalid data', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: [
|
|
{ invalid: 'data' }
|
|
]
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
|
|
it('should return false when some contents are valid and some are invalid', () => {
|
|
const invalidData = {
|
|
title: 'Test',
|
|
created: '2024-01-01T00:00:00.000Z',
|
|
updated: '2024-01-01T00:00:00.000Z',
|
|
contents: [
|
|
{
|
|
role: 'user',
|
|
content: 'Hello',
|
|
promptContent: '',
|
|
toolCall: '',
|
|
timestamp: '2024-01-01T00:00:00.000Z',
|
|
isToolCall: false,
|
|
isToolCallResponse: false,
|
|
isProviderSpecificContent: false
|
|
},
|
|
{ invalid: 'data' }
|
|
]
|
|
};
|
|
|
|
expect(Conversation.isConversationData(invalidData)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('content manipulation', () => {
|
|
it('should update content of most recent conversation content', () => {
|
|
const conversation = new Conversation();
|
|
const content = new ConversationContent({ role: Role.User, content: 'initial' });
|
|
conversation.contents.push(content);
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.content = 'updated';
|
|
|
|
expect(conversation.contents[0].content).toBe('updated');
|
|
});
|
|
|
|
it('should only update the last content when multiple contents exist', () => {
|
|
const conversation = new Conversation();
|
|
conversation.contents.push(new ConversationContent({ role: Role.User, content: 'first' }));
|
|
conversation.contents.push(new ConversationContent({ role: Role.Assistant, content: 'second' }));
|
|
conversation.contents.push(new ConversationContent({ role: Role.User, content: 'third' }));
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.content = 'modified';
|
|
|
|
expect(conversation.contents[0].content).toBe('first');
|
|
expect(conversation.contents[1].content).toBe('second');
|
|
expect(conversation.contents[2].content).toBe('modified');
|
|
});
|
|
|
|
it('should do nothing when contents array is empty', () => {
|
|
const conversation = new Conversation();
|
|
|
|
// Should not throw error
|
|
expect(() => {
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
if (mostRecent) {
|
|
mostRecent.content = 'test';
|
|
}
|
|
}).not.toThrow();
|
|
expect(conversation.contents).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle empty string as new content', () => {
|
|
const conversation = new Conversation();
|
|
conversation.contents.push(new ConversationContent({ role: Role.User, content: 'initial' }));
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.content = '';
|
|
|
|
expect(conversation.contents[0].content).toBe('');
|
|
});
|
|
|
|
it('should handle multiline content', () => {
|
|
const conversation = new Conversation();
|
|
conversation.contents.push(new ConversationContent({ role: Role.User, content: 'initial' }));
|
|
|
|
const multilineContent = 'Line 1\nLine 2\nLine 3';
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.content = multilineContent;
|
|
|
|
expect(conversation.contents[0].content).toBe(multilineContent);
|
|
});
|
|
});
|
|
|
|
describe('function call manipulation', () => {
|
|
it('should set function call on most recent content', () => {
|
|
const conversation = new Conversation();
|
|
const content = new ConversationContent({ role: Role.Assistant });
|
|
conversation.contents.push(content);
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.toolCall = 'readFile';
|
|
|
|
expect(conversation.contents[0].toolCall).toBe('readFile');
|
|
});
|
|
|
|
it('should mark most recent content as function call', () => {
|
|
const conversation = new Conversation();
|
|
const content = new ConversationContent({ role: Role.Assistant });
|
|
conversation.contents.push(content);
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.toolCall = 'readFile';
|
|
|
|
expect(conversation.contents[0].toolCall).toBe('readFile');
|
|
});
|
|
|
|
it('should only update the last content when multiple contents exist', () => {
|
|
const conversation = new Conversation();
|
|
conversation.contents.push(new ConversationContent({ role: Role.User }));
|
|
conversation.contents.push(new ConversationContent({ role: Role.Assistant }));
|
|
conversation.contents.push(new ConversationContent({ role: Role.Assistant }));
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.toolCall = 'searchFiles';
|
|
|
|
expect(conversation.contents[0].toolCall).toBeUndefined();
|
|
expect(conversation.contents[1].toolCall).toBeUndefined();
|
|
expect(conversation.contents[2].toolCall).toBe('searchFiles');
|
|
});
|
|
|
|
it('should do nothing when contents array is empty', () => {
|
|
const conversation = new Conversation();
|
|
|
|
// Should not throw error
|
|
expect(() => {
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
if (mostRecent) {
|
|
mostRecent.toolCall = 'test';
|
|
}
|
|
}).not.toThrow();
|
|
expect(conversation.contents).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle empty string as function call', () => {
|
|
const conversation = new Conversation();
|
|
conversation.contents.push(new ConversationContent({ role: Role.Assistant }));
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.toolCall = '';
|
|
|
|
expect(conversation.contents[0].toolCall).toBe('');
|
|
});
|
|
|
|
it('should overwrite existing function call', () => {
|
|
const conversation = new Conversation();
|
|
const content = new ConversationContent({ role: Role.Assistant, toolCall: 'oldFunction' });
|
|
conversation.contents.push(content);
|
|
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.toolCall = 'newFunction';
|
|
|
|
expect(conversation.contents[0].toolCall).toBe('newFunction');
|
|
});
|
|
});
|
|
|
|
describe('integration', () => {
|
|
it('should handle complete conversation workflow', () => {
|
|
const conversation = new Conversation();
|
|
|
|
// Add user message
|
|
const userMessage = new ConversationContent({ role: Role.User, content: 'Hello' });
|
|
conversation.contents.push(userMessage);
|
|
|
|
// Add assistant response
|
|
const assistantMessage = new ConversationContent({ role: Role.Assistant });
|
|
conversation.contents.push(assistantMessage);
|
|
|
|
// Stream in assistant response
|
|
const mostRecent = conversation.contents[conversation.contents.length - 1];
|
|
mostRecent.content = 'Hi there';
|
|
mostRecent.content = 'Hi there, how can I help you?';
|
|
|
|
// Assistant makes a function call
|
|
mostRecent.toolCall = 'readFile';
|
|
|
|
expect(conversation.contents).toHaveLength(2);
|
|
expect(conversation.contents[0].role).toBe(Role.User);
|
|
expect(conversation.contents[0].content).toBe('Hello');
|
|
expect(conversation.contents[1].role).toBe(Role.Assistant);
|
|
expect(conversation.contents[1].content).toBe('Hi there, how can I help you?');
|
|
expect(conversation.contents[1].toolCall).toBe('readFile');
|
|
});
|
|
|
|
it('should maintain conversation metadata', () => {
|
|
const conversation = new Conversation();
|
|
|
|
expect(conversation.title).toBeDefined();
|
|
expect(conversation.created).toBeInstanceOf(Date);
|
|
expect(conversation.updated).toBeInstanceOf(Date);
|
|
expect(conversation.contents).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('addFunctionResponse', () => {
|
|
describe('read_vault_files with errors', () => {
|
|
it('should handle file not found errors without creating attachments', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'nonexistent.md', error: 'File does not exist: nonexistent.md' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have one content item (the function response with error)
|
|
expect(conversation.contents).toHaveLength(1);
|
|
expect(conversation.contents[0].role).toBe(Role.User);
|
|
expect(conversation.contents[0].shouldDisplayContent).toBe(false);
|
|
|
|
// Should not have attachments
|
|
expect(conversation.contents[0].attachments).toHaveLength(0);
|
|
|
|
// Parse the function response to verify error is included
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
expect(parsedResponse.functionResponse.response.results).toHaveLength(1);
|
|
expect(parsedResponse.functionResponse.response.results[0].error).toBe('File does not exist: nonexistent.md');
|
|
});
|
|
|
|
it('should handle mix of successful reads and errors', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'existing.md', type: 'md', contents: '# Hello' },
|
|
{ path: 'nonexistent.md', error: 'File does not exist: nonexistent.md' },
|
|
{ path: 'another.txt', type: 'txt', contents: 'World' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have one content item with all results
|
|
expect(conversation.contents).toHaveLength(1);
|
|
expect(conversation.contents[0].role).toBe(Role.User);
|
|
|
|
// Parse the function response
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
const results = parsedResponse.functionResponse.response.results;
|
|
|
|
// Should have all 3 results (2 successful + 1 error)
|
|
expect(results).toHaveLength(3);
|
|
|
|
// Check that we have the expected content (order may vary)
|
|
const textResults = results.filter((r: { contents?: string }) => r.contents);
|
|
const errorResult = results.filter((r: { error?: string }) => r.error);
|
|
|
|
expect(textResults).toHaveLength(2);
|
|
expect(textResults.some((r: { contents: string }) => r.contents === '# Hello')).toBe(true);
|
|
expect(textResults.some((r: { contents: string }) => r.contents === 'World')).toBe(true);
|
|
expect(errorResult).toHaveLength(1);
|
|
expect(errorResult[0].error).toBe('File does not exist: nonexistent.md');
|
|
});
|
|
|
|
it('should not create attachments for error results with binary file extensions', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'image.png', error: 'File does not exist: image.png' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have one content item (function response only)
|
|
expect(conversation.contents).toHaveLength(1);
|
|
|
|
// Should not have attachments (error result should not be treated as binary file)
|
|
expect(conversation.contents[0].attachments).toHaveLength(0);
|
|
|
|
// Verify error is in the function response
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
expect(parsedResponse.functionResponse.response.results[0].error).toBeDefined();
|
|
});
|
|
|
|
it('should handle binary files with errors not creating invalid attachments', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'image.png', error: 'File does not exist: image.png' },
|
|
{ path: 'valid.png', type: 'png', contents: 'base64encodeddata...' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have 2 content items: function response + attachments
|
|
expect(conversation.contents).toHaveLength(2);
|
|
|
|
// First item should have the error and success message
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
expect(parsedResponse.functionResponse.response.results).toHaveLength(1);
|
|
expect(parsedResponse.functionResponse.response.results[0].error).toBe('File does not exist: image.png');
|
|
|
|
// Second item should only have the valid attachment
|
|
expect(conversation.contents[1].attachments).toHaveLength(1);
|
|
expect(conversation.contents[1].attachments[0].fileName).toBe('valid.png');
|
|
});
|
|
});
|
|
|
|
describe('read_vault_files without errors', () => {
|
|
it('should handle successful text file reads', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'file.md', type: 'md', contents: '# Title' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
expect(conversation.contents).toHaveLength(1);
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
expect(parsedResponse.functionResponse.response.results[0].contents).toBe('# Title');
|
|
});
|
|
|
|
it('should create attachments for binary files', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'image.png', type: 'png', contents: 'base64data...' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have 2 items: function response + attachments
|
|
expect(conversation.contents).toHaveLength(2);
|
|
expect(conversation.contents[1].attachments).toHaveLength(1);
|
|
expect(conversation.contents[1].attachments[0].fileName).toBe('image.png');
|
|
});
|
|
});
|
|
|
|
describe('read_vault_files with document files', () => {
|
|
it('should base64-encode document file contents and set mime type to text/plain', () => {
|
|
const conversation = new Conversation();
|
|
const documentText = 'This is the extracted text from a document file.';
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'report.odt', type: 'odt', contents: documentText }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have 2 items: function response + attachments
|
|
expect(conversation.contents).toHaveLength(2);
|
|
expect(conversation.contents[1].attachments).toHaveLength(1);
|
|
|
|
const attachment = conversation.contents[1].attachments[0];
|
|
expect(attachment.fileName).toBe('report.odt');
|
|
expect(attachment.mimeType).toBe(MimeType.TEXT_PLAIN);
|
|
// Content should be base64-encoded, not plain text
|
|
expect(attachment.base64).toBe(StringTools.toBase64(documentText));
|
|
expect(attachment.base64).not.toBe(documentText);
|
|
});
|
|
|
|
it('should handle document files alongside text and binary files', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.ReadVaultFiles,
|
|
{
|
|
results: [
|
|
{ path: 'notes.md', type: 'md', contents: '# Notes' },
|
|
{ path: 'spreadsheet.xlsx', type: 'xlsx', contents: 'Extracted spreadsheet text' },
|
|
{ path: 'photo.png', type: 'png', contents: 'base64imagedata...' }
|
|
]
|
|
},
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
// Should have 2 items: function response (text) + attachments (document + binary)
|
|
expect(conversation.contents).toHaveLength(2);
|
|
|
|
// Text file in function response
|
|
const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
|
|
expect(parsedResponse.functionResponse.response.results).toHaveLength(1);
|
|
expect(parsedResponse.functionResponse.response.results[0].path).toBe('notes.md');
|
|
|
|
// Document and binary as attachments
|
|
const attachments = conversation.contents[1].attachments;
|
|
expect(attachments).toHaveLength(2);
|
|
|
|
const docAttachment = attachments.find(a => a.fileName === 'spreadsheet.xlsx')!;
|
|
expect(docAttachment.mimeType).toBe(MimeType.TEXT_PLAIN);
|
|
expect(docAttachment.base64).toBe(StringTools.toBase64('Extracted spreadsheet text'));
|
|
|
|
const imgAttachment = attachments.find(a => a.fileName === 'photo.png')!;
|
|
expect(imgAttachment.base64).toBe('base64imagedata...');
|
|
});
|
|
});
|
|
|
|
describe('non-read_vault_files functions', () => {
|
|
it('should handle other functions normally', () => {
|
|
const conversation = new Conversation();
|
|
const functionResponse = new AIToolResponse(
|
|
AITool.WriteVaultFile,
|
|
{ success: true },
|
|
'tool-123'
|
|
);
|
|
|
|
conversation.addFunctionResponse(functionResponse);
|
|
|
|
expect(conversation.contents).toHaveLength(1);
|
|
expect(conversation.contents[0].role).toBe(Role.User);
|
|
expect(conversation.contents[0].attachments).toHaveLength(0);
|
|
});
|
|
});
|
|
});
|
|
});
|