@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
335 lines (298 loc) • 9 kB
text/typescript
import { describe, expect, it } from 'vitest';
import {
PlaceholderVariablesProcessor,
parsePlaceholderVariables,
parsePlaceholderVariablesMessages,
} from '../PlaceholderVariables';
describe('PlaceholderVariablesProcessor', () => {
const mockVariableGenerators = {
date: () => '2023-12-25',
time: () => '14:30:45',
username: () => 'TestUser',
random: () => '12345',
nested: () => 'Value with {{date}} inside',
};
describe('parsePlaceholderVariables', () => {
it('should replace simple placeholder variables', () => {
const text = 'Today is {{date}} and the time is {{time}}';
const result = parsePlaceholderVariables(text, mockVariableGenerators);
expect(result).toBe('Today is 2023-12-25 and the time is 14:30:45');
});
it('should handle missing variables gracefully', () => {
const text = 'Hello {{username}}, missing: {{missing}}';
const result = parsePlaceholderVariables(text, mockVariableGenerators);
expect(result).toBe('Hello TestUser, missing: {{missing}}');
});
it('should handle nested variables with recursion', () => {
const text = 'Nested: {{nested}}';
const result = parsePlaceholderVariables(text, mockVariableGenerators);
expect(result).toBe('Nested: Value with 2023-12-25 inside');
});
it('should respect depth limit', () => {
const text = 'Nested: {{nested}}';
const result = parsePlaceholderVariables(text, mockVariableGenerators, 1);
expect(result).toBe('Nested: Value with {{date}} inside');
});
it('should handle empty text', () => {
const text = '';
const result = parsePlaceholderVariables(text, mockVariableGenerators);
expect(result).toBe('');
});
it('should handle text without placeholders', () => {
const text = 'No placeholders here';
const result = parsePlaceholderVariables(text, mockVariableGenerators);
expect(result).toBe('No placeholders here');
});
});
describe('parsePlaceholderVariablesMessages', () => {
it('should process string content messages', () => {
const messages = [
{
id: '1',
role: 'user',
content: 'Hello {{username}}, today is {{date}}',
},
{
id: '2',
role: 'assistant',
content: 'Hi there! The time is {{time}}',
},
];
const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
expect(result).toEqual([
{
id: '1',
role: 'user',
content: 'Hello TestUser, today is 2023-12-25',
},
{
id: '2',
role: 'assistant',
content: 'Hi there! The time is 14:30:45',
},
]);
});
it('should process array content messages with text parts', () => {
const messages = [
{
id: '1',
role: 'user',
content: [
{
type: 'text',
text: 'Hello {{username}}, today is {{date}}',
},
{
type: 'image_url',
image_url: { url: 'data:image/png;base64,abc123' },
},
],
},
];
const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
expect(result).toEqual([
{
id: '1',
role: 'user',
content: [
{
type: 'text',
text: 'Hello TestUser, today is 2023-12-25',
},
{
type: 'image_url',
image_url: { url: 'data:image/png;base64,abc123' },
},
],
},
]);
});
it('should skip messages without content', () => {
const messages = [
{
id: '1',
role: 'user',
},
{
id: '2',
role: 'assistant',
content: null,
},
];
const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
expect(result).toEqual(messages);
});
it('should handle mixed content types', () => {
const messages = [
{
id: '1',
role: 'user',
content: 'Simple {{username}} message',
},
{
id: '2',
role: 'user',
content: [{ type: 'text', text: 'Complex {{date}} message' }],
},
{
id: '3',
role: 'assistant',
content: { type: 'object', data: 'not processed' },
},
];
const result = parsePlaceholderVariablesMessages(messages, mockVariableGenerators);
expect(result).toEqual([
{
id: '1',
role: 'user',
content: 'Simple TestUser message',
},
{
id: '2',
role: 'user',
content: [{ type: 'text', text: 'Complex 2023-12-25 message' }],
},
{
id: '3',
role: 'assistant',
content: { type: 'object', data: 'not processed' },
},
]);
});
});
describe('PlaceholderVariablesProcessor', () => {
it('should process messages through the processor', async () => {
const processor = new PlaceholderVariablesProcessor({
variableGenerators: mockVariableGenerators,
});
const context = {
initialState: {
messages: [],
model: 'gpt-4',
provider: 'openai',
systemRole: '',
tools: [],
},
messages: [
{
id: '1',
role: 'user',
content: 'Hello {{username}}, today is {{date}}',
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
metadata: {
model: 'gpt-4',
maxTokens: 4096,
},
isAborted: false,
executedProcessors: [],
};
const result = await processor.process(context);
expect(result.messages[0].content).toBe('Hello TestUser, today is 2023-12-25');
expect(result.metadata.placeholderVariablesProcessed).toBe(1);
});
it('should handle processing errors gracefully', async () => {
const faultyGenerators = {
error: () => {
throw new Error('Generator error');
},
working: () => 'works',
};
const processor = new PlaceholderVariablesProcessor({
variableGenerators: faultyGenerators,
});
const context = {
initialState: {
messages: [],
model: 'gpt-4',
provider: 'openai',
systemRole: '',
tools: [],
},
messages: [
{
id: '1',
role: 'user',
content: 'This {{working}} but this {{error}} fails',
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
metadata: {
model: 'gpt-4',
maxTokens: 4096,
},
isAborted: false,
executedProcessors: [],
};
// Should not throw, but continue processing
const result = await processor.process(context);
expect(result.messages).toHaveLength(1);
});
it('should use custom depth setting', async () => {
const processor = new PlaceholderVariablesProcessor({
variableGenerators: mockVariableGenerators,
depth: 1,
});
const context = {
initialState: {
messages: [],
model: 'gpt-4',
provider: 'openai',
systemRole: '',
tools: [],
},
messages: [
{
id: '1',
role: 'user',
content: 'Nested: {{nested}}',
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
metadata: {
model: 'gpt-4',
maxTokens: 4096,
},
isAborted: false,
executedProcessors: [],
};
const result = await processor.process(context);
expect(result.messages[0].content).toBe('Nested: Value with {{date}} inside');
});
it('should not modify messages that do not need processing', async () => {
const processor = new PlaceholderVariablesProcessor({
variableGenerators: mockVariableGenerators,
});
const context = {
initialState: {
messages: [],
model: 'gpt-4',
provider: 'openai',
systemRole: '',
tools: [],
},
messages: [
{
id: '1',
role: 'user',
content: 'No variables here',
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
metadata: {
model: 'gpt-4',
maxTokens: 4096,
},
isAborted: false,
executedProcessors: [],
};
const result = await processor.process(context);
expect(result.metadata.placeholderVariablesProcessed).toBe(0);
});
});
});