Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JS] chore: #54 Add unit tests for PromptManager.ts #1246

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The files in this folder `/test` are used for unit tests in `PromptManager.spec.ts`. The files in this folder are not used in the actual package.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"name": "SendCard",
"description": "Sends an adaptive card to the user",
"parameters": {
"type": "object",
"properties": {
"card": {
"type": "object",
"description": "The adaptive card to send"
}
},
"required": ["card"]
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You are a friendly assistant for Microsoft Teams.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"name": "SendCard",
"description": "Sends an adaptive card to the user",
"parameters": {
"type": "object",
"properties": {
"card": {
"type": "object",
"description": "The adaptive card to send"
}
},
"required": ["card"]
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"schema": 1.1,
"description": "A bot that can manage a list of work items.",
"type": "completion",
"completion": {
"model": "gpt-3.5-turbo",
"completion_type": "chat",
"include_history": true,
"include_input": true,
"max_input_tokens": 2800,
"max_tokens": 1000,
"temperature": 0.2,
"top_p": 0.0,
"presence_penalty": 0.6,
"frequency_penalty": 0.0,
"stop_sequences": []
},
"augmentation": {
"augmentation_type": "monologue"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"name": "SendCard",
"description": "Sends an adaptive card to the user",
"parameters": {
"type": "object",
"properties": {
"card": {
"type": "object",
"description": "The adaptive card to send"
}
},
"required": ["card"]
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"schema": 1.1,
"description": "A bot that can manage a list of work items.",
"type": "completion",
"completion": {
"model": "gpt-3.5-turbo",
"completion_type": "chat",
"include_history": true,
"include_input": true,
"max_input_tokens": 2800,
"max_tokens": 1000,
"temperature": 0.2,
"top_p": 0.0,
"presence_penalty": 0.6,
"frequency_penalty": 0.0,
"stop_sequences": []
},
"augmentation": {
"augmentation_type": "monologue"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You are a friendly assistant for Microsoft Teams.
184 changes: 184 additions & 0 deletions js/packages/teams-ai/src/prompts/PromptManager.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { strict as assert } from 'assert';
import path from 'path';

import { DataSource } from '../dataSources';
import { TestPromptManager } from '../internals/testing/TestPromptManager';
import { ConfiguredPromptManagerOptions, PromptManager } from './PromptManager';
import { Message } from './Message';
import { PromptTemplate } from './PromptTemplate';
import { RenderedPromptSection } from './PromptSection';

describe('PromptManager', () => {
describe('constructor', () => {
Expand All @@ -10,6 +17,89 @@ describe('PromptManager', () => {
});
});

it('should return options', () => {
const options: ConfiguredPromptManagerOptions = {
promptsFolder: 'promptFolder',
role: 'system',
max_conversation_history_tokens: -1,
max_history_messages: 10,
max_input_tokens: -1
};
const prompts = new TestPromptManager(options);
const configuredOptions = prompts.options;
assert(configuredOptions !== undefined);
assert(configuredOptions.promptsFolder === 'promptFolder');
assert(configuredOptions.role === 'system');
assert(configuredOptions.max_conversation_history_tokens === -1);
assert(configuredOptions.max_history_messages === 10);
});

describe('addDataSource', () => {
it('should add a data source', () => {
const prompts = new TestPromptManager();
const newDataSource: DataSource = {
name: 'test',
renderData: async (context, memory, tokenizer, maxTokens): Promise<RenderedPromptSection<string>> => {
return Promise.resolve({ text: 'test', output: 'test', length: 1, tokens: 1, tooLong: false });
}
};
prompts.addDataSource(newDataSource);
assert(newDataSource !== undefined);
});

it('should throw an error on adding duplicate data source', () => {
const prompts = new TestPromptManager();
const newDataSource: DataSource = {
name: 'test',
renderData: async (context, memory, tokenizer, maxTokens): Promise<RenderedPromptSection<string>> => {
return Promise.resolve({ text: 'test', output: 'test', length: 1, tokens: 1, tooLong: false });
}
};
prompts.addDataSource(newDataSource);
assert(newDataSource !== undefined);
assert.throws(() => prompts.addDataSource(newDataSource));
});
});

describe('getDataSource', () => {
it('should get a data source', () => {
const prompts = new TestPromptManager();
const newDataSource: DataSource = {
name: 'test',
renderData: async (context, memory, tokenizer, maxTokens): Promise<RenderedPromptSection<string>> => {
return Promise.resolve({ text: 'test', output: 'test', length: 1, tokens: 1, tooLong: false });
}
};
prompts.addDataSource(newDataSource);
const dataSource = prompts.getDataSource('test');
assert(dataSource !== undefined);
});

it('should throw an error on getting a data source that does not exist', () => {
const prompts = new TestPromptManager();
assert.throws(() => prompts.getDataSource('test'));
});
});

describe('hasDataSource', () => {
it('should return true when a data source exists', () => {
const prompts = new TestPromptManager();
const newDataSource: DataSource = {
name: 'test',
renderData: async (context, memory, tokenizer, maxTokens): Promise<RenderedPromptSection<string>> => {
return Promise.resolve({ text: 'test', output: 'test', length: 1, tokens: 1, tooLong: false });
}
};
prompts.addDataSource(newDataSource);
assert(prompts.hasDataSource('test'));
});

it('should return false when a data source does not exist', () => {
const prompts = new TestPromptManager();
assert(!prompts.hasDataSource('test'));
});
});

describe('addFunction', () => {
it('should add a function', () => {
const prompts = new TestPromptManager();
Expand Down Expand Up @@ -50,4 +140,98 @@ describe('PromptManager', () => {
assert.equal(prompts.hasFunction('test'), true);
});
});

describe('Prompts', () => {
const newPrompt: PromptTemplate = {
name: 'test',
prompt: {
required: true,
tokens: -1,
renderAsMessages(context, memory, functions, tokenizer, maxTokens) {
return Promise.resolve({
messages: [],
output: [] as Message<string>[],
length: 1,
tokens: 1,
tooLong: false
});
},
renderAsText(context, memory, functions, tokenizer, maxTokens) {
return Promise.resolve({ text: 'test', output: 'test', length: 1, tokens: 1, tooLong: false });
}
},
config: {
completion: {
frequency_penalty: 0,
include_history: true,
include_input: true,
include_images: false,
max_tokens: 100,
max_input_tokens: 2048,
model: 'test',
presence_penalty: 0,
temperature: 0,
top_p: 1
},
schema: 1.1,
type: 'completion'
}
};
it('addPrompt should add a prompt', async () => {
const prompts = new TestPromptManager();
prompts.addPrompt(newPrompt);
assert.deepEqual(await prompts.getPrompt('test'), newPrompt);
assert.equal(await prompts.hasPrompt('test'), true);
});

it('should throw when adding a prompt that already exists', () => {
const prompts = new TestPromptManager();
prompts.addPrompt(newPrompt);
assert.throws(() => prompts.addPrompt(newPrompt));
});

it(`should resolve the prompt template files if the prompt name doesn't exist`, async () => {
const prompts = new PromptManager({
promptsFolder: path.join(__dirname, '..', 'internals', 'testing', 'assets', 'test')
});
const prompt = await prompts.getPrompt('unknown');
assert.notEqual(prompt, null);
});

it(`should throw an error if config.json is missing when using getPrompt`, async () => {
const prompts = new PromptManager({
promptsFolder: path.join(__dirname, '..', 'internals', 'testing', 'assets', 'test')
});
assert.rejects(async () => {
await prompts.getPrompt('missingConfig');
});
});

it(`should throw an error if skprompt.txt is missing when using getPrompt`, async () => {
const prompts = new PromptManager({
promptsFolder: path.join(__dirname, '..', 'internals', 'testing', 'assets', 'test')
});
assert.rejects(async () => {
await prompts.getPrompt('missingPrompt');
});
});

it(`should assign role 'system' if role is missing`, async () => {
const prompts = new PromptManager({
role: '',
promptsFolder: path.join(__dirname, '..', 'internals', 'testing', 'assets', 'test')
});
assert.rejects(async () => {
await prompts.getPrompt('missingPrompt');
});
});

it(`should resolve the prompt template files if the prompt name doesn't exist`, async () => {
const prompts = new PromptManager({
promptsFolder: path.join(__dirname, '..', 'internals', 'testing', 'assets', 'test')
});
const result = await prompts.hasPrompt('unknown');
assert.equal(result, true);
});
});
});
Loading
Loading