import { describe, expect, it } from 'vitest';
import { addToolPattern, getToolPatterns, matchToolCalling } from '../responsePatternUtility';
describe('matchToolCalling', () => {
describe('XML-style patterns', () => {
it('should match tool_use pattern with JSON parameters', () => {
const responseText = `
I'll search for documentation about TiddlyWiki plugins.
{
"workspaceName": "documentation",
"filter": "[tag[plugin]]",
}
Let me find the relevant information for you.
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('wiki-search');
expect(result.parameters).toEqual({
workspaceName: 'documentation',
filter: '[tag[plugin]]',
});
expect(result.originalText).toContain('');
});
describe('Parameter parsing', () => {
it('should parse JSON parameters correctly', () => {
const responseText = `
{
"stringValue": "hello",
"numberValue": 42,
"booleanValue": true,
"arrayValue": [1, 2, 3],
"objectValue": {"nested": "value"}
}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.parameters).toEqual({
stringValue: 'hello',
numberValue: 42,
booleanValue: true,
arrayValue: [1, 2, 3],
objectValue: { nested: 'value' },
});
});
it('should handle empty parameters', () => {
const responseText = `
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('no-params');
expect(result.parameters).toEqual({});
});
it('should handle malformed parameters gracefully', () => {
const responseText = `
This is not valid JSON or key-value pairs
Just some random text
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('malformed-tool');
expect(result.parameters).toEqual({
input: 'This is not valid JSON or key-value pairs\nJust some random text',
});
});
});
describe('Pattern matching priority', () => {
it('should match the first valid pattern found', () => {
const responseText = `
{"param": "value1"}
{"param": "value2"}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('first-tool');
expect(result.parameters).toEqual({ param: 'value1' });
});
});
describe('No match scenarios', () => {
it('should return not found for text without tool patterns', () => {
const responseText = 'This is just regular text without any tool calling patterns.';
const result = matchToolCalling(responseText);
expect(result.found).toBe(false);
expect(result.toolId).toBeUndefined();
expect(result.parameters).toBeUndefined();
expect(result.originalText).toBeUndefined();
});
it('should return not found for malformed tool patterns', () => {
const responseText = 'no closing tag';
const result = matchToolCalling(responseText);
expect(result.found).toBe(false);
});
});
describe('Edge cases', () => {
it('should handle case-insensitive pattern matching', () => {
const responseText = `
{"test": "value"}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('uppercase-tool');
});
it('should handle extra attributes in XML tags', () => {
const responseText = `
{"query": "test"}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('test-tool');
expect(result.parameters).toEqual({ query: 'test' });
});
it('should handle multiline parameters correctly', () => {
const responseText = `
{
"longText": "This is a very long text that spans multiple lines and contains various characters like quotes ' and special symbols @#$%",
"array": [
"item1",
"item2",
"item3"
]
}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.parameters?.longText).toContain('spans multiple lines');
expect(result.parameters?.array).toEqual(['item1', 'item2', 'item3']);
});
});
});
describe('getToolPatterns', () => {
it('should return patterns with required properties', () => {
const patterns = getToolPatterns();
patterns.forEach(pattern => {
expect(pattern).toHaveProperty('name');
expect(pattern).toHaveProperty('pattern');
expect(pattern).toHaveProperty('extractToolId');
expect(pattern).toHaveProperty('extractParams');
expect(pattern).toHaveProperty('extractOriginalText');
expect(typeof pattern.extractToolId).toBe('function');
expect(typeof pattern.extractParams).toBe('function');
expect(typeof pattern.extractOriginalText).toBe('function');
});
});
});
describe('addToolPattern', () => {
it('should add a new tool pattern', () => {
const originalLength = getToolPatterns().length;
const customPattern = {
name: 'custom_pattern',
pattern: /]*>(.*?)<\/custom>/gis,
extractToolId: (match: RegExpExecArray) => match[1],
extractParams: (match: RegExpExecArray) => match[2],
extractOriginalText: (match: RegExpExecArray) => match[0],
};
addToolPattern(customPattern);
const patterns = getToolPatterns();
expect(patterns).toHaveLength(originalLength + 1);
expect(patterns[patterns.length - 1].name).toBe('custom_pattern');
});
it('should make new patterns available for matching', () => {
const customPattern = {
name: 'test_custom',
pattern: /]*>(.*?)<\/test_custom>/gis,
extractToolId: (match: RegExpExecArray) => match[1],
extractParams: (match: RegExpExecArray) => match[2],
extractOriginalText: (match: RegExpExecArray) => match[0],
};
addToolPattern(customPattern);
const responseText = `
{"param": "value"}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('my-tool');
expect(result.parameters).toEqual({ param: 'value' });
});
});
describe('Error handling', () => {
it('should handle large input text efficiently', () => {
// Create a large text with tool pattern at the end
const largePrefix = 'x'.repeat(10000);
const responseText = `${largePrefix}
{"test": "value"}
`;
const result = matchToolCalling(responseText);
expect(result.found).toBe(true);
expect(result.toolId).toBe('test-tool');
});
});
});