diff --git a/README.md b/README.md index 4394027..0dfa55c 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,9 @@ import { Configuration, OpenAIApi } from "openai"; import { AIPersona, ModelConfiguration, ConversationPromptService } from "@wisegpt/gpt-conversation-prompt"; const aiPersona: AIPersona = { - name: "wiseGPT", - instructions: `You are a software engineer. -When providing code examples, use triple backticks.`, - exampleConversations: [], + name: "WiseGPT", + instructions: `When providing code examples, use triple backticks.`, + personality: `You are a software engineer.`, }; const modelConfiguration: ModelConfiguration = { @@ -24,7 +23,7 @@ const openAIApi = new OpenAIApi(new Configuration({ apiKey: "<>" }) const conversationPromptService = new ConversationPromptService(openAIApi); async function exampleUsage() { - const { text, usage } = await conversationPromptService.conversationCompletion({ + const { text, usage } = await conversationPromptService.completion({ prompt: { conversation: { messages: [ @@ -49,4 +48,102 @@ async function exampleUsage() { console.log(JSON.stringify({ text, usage })); } +``` + +## Detailed Example Usage +Below is an example usage which includes summary and re-using the summarized conversation to keeping the conversation going and summarizing again. + +```typescript +import { Configuration, OpenAIApi } from "openai"; +import { AIPersona, ModelConfiguration, ConversationPromptService, Author, Conversation } from "@wisegpt/gpt-conversation-prompt"; + +const aiPersona: AIPersona = { + name: "WiseGPT", + instructions: `When providing code examples, use triple backticks.`, + personality: `You are a software engineer.`, +}; + +const modelConfiguration: ModelConfiguration = { + model: "text-davinci-003", + max_tokens: 1000, +}; + +const openAIApi = new OpenAIApi(new Configuration({ apiKey: "<>" })); +const conversationPromptService = new ConversationPromptService(openAIApi); + +async function exampleUsage() { + const authors: Record = { + user1: { type: "USER", id: "EU01" }, + user2: { type: "USER", id: "EU02" }, + }; + + const conversation1: Conversation = { + messages: [ + { + text: "My name is Yigitcan.", + author: authors.user1, + }, + { + text: "My name is Tolga.", + author: authors.user2, + }, + ], + }; + + const botResponse1 = await conversationPromptService.completion({ + prompt: { conversation: conversation1, aiPersona }, + modelConfiguration, + }); + + console.log(JSON.stringify({ botResponse1 })); + + // add bots response to the conversation + conversation1.messages.push({ + text: botResponse1.text, + author: { type: "BOT" }, + }); + + const summaryResponse1 = await conversationPromptService.summary({ + prompt: { conversation: conversation1, aiPersona }, + modelConfiguration, + }); + + console.log(JSON.stringify({ summaryResponse1 })); + + // create a whole new conversation with the summary and a new message + const conversation2 = { + summary: summaryResponse1.summary, + messages: [ + { + text: "what is my name? and what is the capital of Turkey?", + author: authors.user1, + }, + ], + }; + + const botResponse2 = await conversationPromptService.completion({ + prompt: { conversation: conversation2, aiPersona }, + modelConfiguration, + }); + + console.log(JSON.stringify({ botResponse2 })); + + // add bots response to the conversation + conversation2.messages.push({ + text: botResponse1.text, + author: { type: "BOT" }, + }); + + const summaryResponse2 = await conversationPromptService.summary({ + prompt: { conversation: conversation2, aiPersona }, + modelConfiguration, + }); + + console.log(JSON.stringify({ summaryResponse2 })); +} + +exampleUsage().catch((err) => { + console.error(err); + process.exit(1); +}); ``` \ No newline at end of file diff --git a/src/conversation-prompt/prompt.dto.ts b/src/conversation-prompt/conversation-prompt-service.dto.ts similarity index 61% rename from src/conversation-prompt/prompt.dto.ts rename to src/conversation-prompt/conversation-prompt-service.dto.ts index b34ee48..a995ddc 100644 --- a/src/conversation-prompt/prompt.dto.ts +++ b/src/conversation-prompt/conversation-prompt-service.dto.ts @@ -17,6 +17,7 @@ export type ConversationCompleteInput = { prompt: { aiPersona: AIPersona; conversation: Conversation; + exampleConversations?: Conversation[]; }; modelConfiguration: ModelConfiguration; }; @@ -29,3 +30,21 @@ export type ConversationCompleteOutput = { totalTokens: number; }; }; + +export type ConversationSummaryInput = { + // prompt generation related details + prompt: { + aiPersona: AIPersona; + conversation: Conversation; + }; + modelConfiguration: ModelConfiguration; +}; + +export type ConversationSummaryOutput = { + summary: string; + usage: { + promptTokens: number; + completionTokens: number; + totalTokens: number; + }; +}; diff --git a/src/conversation-prompt/conversation-prompt.service.ts b/src/conversation-prompt/conversation-prompt.service.ts index 63cd655..5e012c5 100644 --- a/src/conversation-prompt/conversation-prompt.service.ts +++ b/src/conversation-prompt/conversation-prompt.service.ts @@ -1,17 +1,18 @@ import { OpenAIApi } from "openai"; -import { - STATEMENT_SEPARATOR_TOKEN, - createConversationCompletionPrompt, -} from "./create-conversation-completion-prompt"; import { ConversationCompleteInput, ConversationCompleteOutput, -} from "./prompt.dto"; + ConversationSummaryInput, + ConversationSummaryOutput, +} from "./conversation-prompt-service.dto"; +import { createConversationCompletionPrompt } from "./prompts/create-conversation-completion-prompt"; +import { createConversationSummaryPrompt } from "./prompts/create-conversation-summary-prompt"; +import { STATEMENT_SEPARATOR_TOKEN } from "./prompts/prompts.constants"; export class ConversationPromptService { constructor(private readonly openAIApi: OpenAIApi) {} - async conversationCompletion( + async completion( input: ConversationCompleteInput ): Promise { const prompt = createConversationCompletionPrompt(input.prompt); @@ -26,7 +27,8 @@ export class ConversationPromptService { const text = data.choices?.[0] .text!.trim() - .replace(STATEMENT_SEPARATOR_TOKEN, ""); + .replace(STATEMENT_SEPARATOR_TOKEN, "") + .trim(); const { prompt_tokens: promptTokens, @@ -36,4 +38,31 @@ export class ConversationPromptService { return { text, usage: { promptTokens, completionTokens, totalTokens } }; } + + async summary( + input: ConversationSummaryInput + ): Promise { + const prompt = createConversationSummaryPrompt(input.prompt); + + const { data } = await this.openAIApi.createCompletion({ + ...input.modelConfiguration, + best_of: 1, + n: 1, + echo: false, + prompt, + }); + + const summary = data.choices?.[0] + .text!.trim() + .replace(STATEMENT_SEPARATOR_TOKEN, "") + .trim(); + + const { + prompt_tokens: promptTokens, + completion_tokens: completionTokens, + total_tokens: totalTokens, + } = data.usage!; + + return { summary, usage: { promptTokens, completionTokens, totalTokens } }; + } } diff --git a/src/conversation-prompt/create-conversation-completion-prompt.ts b/src/conversation-prompt/create-conversation-completion-prompt.ts deleted file mode 100644 index 02ac7d4..0000000 --- a/src/conversation-prompt/create-conversation-completion-prompt.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { AIPersona, Conversation } from "../types"; -import { BOT_MENTION, buildMention } from "./mention"; - -export const STATEMENT_SEPARATOR_TOKEN = "<|endofstatement|>"; -const CONVERSATION_FORMAT_PROMPT = `The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "${STATEMENT_SEPARATOR_TOKEN}" is used to separate chat entries and make it easier for you to understand the context:`; -const CURRENT_CONVERSATION_PROMPT = `Continue the conversation, paying very close attention to things Human told you, such as their name, and personal details. Never say "${STATEMENT_SEPARATOR_TOKEN}". Current conversation:`; -const BASIC_EXAMPLE_CONVERSATIONS: Conversation[] = [ - { - messages: [ - { - text: "[MESSAGE 1]", - author: { type: "USER", id: "U01" }, - }, - { - text: "[RESPONSE TO MESSAGE 1]", - author: { type: "BOT" }, - }, - ], - }, - { - messages: [ - { - text: `hello ${BOT_MENTION}`, - author: { type: "USER", id: "U02" }, - }, - { - text: `hello ${buildMention({ - type: "USER", - id: "U02", - })}! how are you?`, - author: { type: "BOT" }, - }, - ], - }, -]; - -export type CreateConversationCompletionPromptInput = { - aiPersona: AIPersona; - conversation: Conversation; -}; - -export function createConversationCompletionPrompt({ - aiPersona, - conversation, -}: CreateConversationCompletionPromptInput): string { - const exampleConversations = - aiPersona.exampleConversations.length > 0 - ? aiPersona.exampleConversations - : BASIC_EXAMPLE_CONVERSATIONS; - - const renderConversation = ({ messages }: Conversation) => - messages - .map( - (message) => - `${buildMention(message.author)}: ${ - message.text - } ${STATEMENT_SEPARATOR_TOKEN}\n` - ) - .join(""); - - return ( - `Instructions for ${BOT_MENTION}:\n` + - `Your name is "${aiPersona.name}". You are referenced in conversations as "${BOT_MENTION}".\n` + - aiPersona.instructions + - `\n\n${CONVERSATION_FORMAT_PROMPT}\n\n${exampleConversations - .map(renderConversation) - .join("\n")}...` + - `\n\n${CURRENT_CONVERSATION_PROMPT}\n\n` + - (renderConversation(conversation) + `${BOT_MENTION}: `) - ); -} diff --git a/src/conversation-prompt/index.ts b/src/conversation-prompt/index.ts index cc36ee0..140dd58 100644 --- a/src/conversation-prompt/index.ts +++ b/src/conversation-prompt/index.ts @@ -1,3 +1,3 @@ -export * from "./prompt.dto"; +export * from "./conversation-prompt-service.dto"; export * from "./mention"; export { ConversationPromptService } from "./conversation-prompt.service"; diff --git a/src/conversation-prompt/prompts/create-conversation-completion-prompt.ts b/src/conversation-prompt/prompts/create-conversation-completion-prompt.ts new file mode 100644 index 0000000..c048ec5 --- /dev/null +++ b/src/conversation-prompt/prompts/create-conversation-completion-prompt.ts @@ -0,0 +1,32 @@ +import { AIPersona, Conversation } from "../../types"; +import { BOT_MENTION } from "../mention"; +import { STATEMENT_SEPARATOR_TOKEN } from "./prompts.constants"; +import { renderAIPersona } from "./render-ai-persona"; +import { renderConversation } from "./render-conversation"; +import { renderFormatAndExamples } from "./render-format-and-examples"; + +const CURRENT_CONVERSATION_PROMPT = `Continue the conversation, paying very close attention to things entities told you; such as their name, and personal details. Never say "${STATEMENT_SEPARATOR_TOKEN}". Current conversation:`; + +export type CreateConversationCompletionPromptInput = { + aiPersona: AIPersona; + exampleConversations?: Conversation[]; + conversation: Conversation; +}; + +export function createConversationCompletionPrompt({ + aiPersona, + conversation, + exampleConversations, +}: CreateConversationCompletionPromptInput): string { + const hasSummary = !!conversation.summary; + + return ( + renderAIPersona(aiPersona) + + `\n${renderFormatAndExamples({ + hasSummary, + exampleConversations, + })}` + + `\n\n${CURRENT_CONVERSATION_PROMPT}\n\n` + + (renderConversation(conversation) + `${BOT_MENTION}:`) + ); +} diff --git a/src/conversation-prompt/prompts/create-conversation-summary-prompt.ts b/src/conversation-prompt/prompts/create-conversation-summary-prompt.ts new file mode 100644 index 0000000..f0ac9d2 --- /dev/null +++ b/src/conversation-prompt/prompts/create-conversation-summary-prompt.ts @@ -0,0 +1,62 @@ +import { AIPersona, Conversation } from "../../types"; +import { STATEMENT_SEPARATOR_TOKEN } from "./prompts.constants"; +import { renderAIPersona } from "./render-ai-persona"; +import { renderConversation } from "./render-conversation"; +import { renderFormatAndExamples } from "./render-format-and-examples"; + +export type CreateConversationSummaryPromptInput = { + aiPersona: AIPersona; + conversation: Conversation; +}; + +const buildPrompt = ({ + hasSummary, + hasMultipleEntities, +}: { + hasSummary: boolean; + hasMultipleEntities: boolean; +}): string => { + let prompt = ""; + + if (hasSummary) { + prompt += + "Summarize the conversation below. Make a detailed summary which only consists of the previous summary and later messages. "; + } else { + prompt += + "Summarize the conversation below. Make a detailed summary of the existing messages. "; + } + + prompt += `Do not summarize the instructions or examples. Do not add anything extra or something that was not discussed. Do not repeat details. Pay close attention to the things that entities told you; especially their personal details and code details. `; + + if (hasMultipleEntities) { + prompt += `You must reference entities in the conversation with the "<@id>" format in the summary to differentiate their personal details and messages. `; + } + + prompt += `Omit small talk and conversation status. Never say "${STATEMENT_SEPARATOR_TOKEN}":`; + + return prompt; +}; + +export const createConversationSummaryPrompt = ({ + aiPersona, + conversation, +}: CreateConversationSummaryPromptInput) => { + const hasSummary = !!conversation.summary; + const participantCount = conversation.messages.reduce( + (set, { author }) => (author.type !== "BOT" ? set.add(author.id) : set), + new Set() + ).size; + const prompt = buildPrompt({ + hasSummary, + hasMultipleEntities: participantCount > 1, + }); + + return ( + renderAIPersona(aiPersona) + + `\n${renderFormatAndExamples({ + hasSummary, + })}` + + `\n\n${prompt}\n\n` + + (renderConversation(conversation) + `...\nSummary:`) + ); +}; diff --git a/src/conversation-prompt/prompts/prompts.constants.ts b/src/conversation-prompt/prompts/prompts.constants.ts new file mode 100644 index 0000000..307cd08 --- /dev/null +++ b/src/conversation-prompt/prompts/prompts.constants.ts @@ -0,0 +1 @@ +export const STATEMENT_SEPARATOR_TOKEN = "<|endofstatement|>"; diff --git a/src/conversation-prompt/prompts/render-ai-persona.ts b/src/conversation-prompt/prompts/render-ai-persona.ts new file mode 100644 index 0000000..80ddb18 --- /dev/null +++ b/src/conversation-prompt/prompts/render-ai-persona.ts @@ -0,0 +1,10 @@ +import { AIPersona } from "../../types"; +import { BOT_MENTION } from "../mention"; + +export const renderAIPersona = (aiPersona: AIPersona): string => + `Instructions for ${BOT_MENTION}, this is how you should behave in a conversation, but this is not your personality:\n` + + `Your name is "${aiPersona.name}". You are referenced in conversations as "${BOT_MENTION}".\n` + + aiPersona.instructions + + `\n\nThis is your personality:\n` + + aiPersona.personality + + "\n"; diff --git a/src/conversation-prompt/prompts/render-conversation.ts b/src/conversation-prompt/prompts/render-conversation.ts new file mode 100644 index 0000000..d6ff376 --- /dev/null +++ b/src/conversation-prompt/prompts/render-conversation.ts @@ -0,0 +1,21 @@ +import { Conversation } from "../../types"; +import { buildMention } from "../mention"; +import { STATEMENT_SEPARATOR_TOKEN } from "./prompts.constants"; + +export const renderConversation = ({ summary, messages }: Conversation) => { + const summaryText = summary + ? `Summary: ${summary} ${STATEMENT_SEPARATOR_TOKEN}\n` + : ""; + + return ( + summaryText + + messages + .map( + (message) => + `${buildMention(message.author)}: ${ + message.text + } ${STATEMENT_SEPARATOR_TOKEN}\n` + ) + .join("") + ); +}; diff --git a/src/conversation-prompt/prompts/render-format-and-examples.ts b/src/conversation-prompt/prompts/render-format-and-examples.ts new file mode 100644 index 0000000..225640e --- /dev/null +++ b/src/conversation-prompt/prompts/render-format-and-examples.ts @@ -0,0 +1,66 @@ +import { Conversation } from "../../types"; +import { BOT_MENTION, buildMention } from "../mention"; +import { STATEMENT_SEPARATOR_TOKEN } from "./prompts.constants"; +import { renderConversation } from "./render-conversation"; + +const CONVERSATION_FORMAT = `The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "${STATEMENT_SEPARATOR_TOKEN}" is used to separate chat entries and make it easier for you to understand the context:`; +const CONVERSATION_FORMAT_WITH_EXAMPLE = `The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "${STATEMENT_SEPARATOR_TOKEN}" is used to separate chat entries and make it easier for you to understand the context. The conversations start with a "Summary:" which includes a detailed summary of messages in the same conversation. Summary ends with "${STATEMENT_SEPARATOR_TOKEN}":`; + +const BASIC_EXAMPLE_CONVERSATIONS: Conversation[] = [ + { + messages: [ + { + text: "[MESSAGE 1]", + author: { type: "USER", id: "U01" }, + }, + { + text: "[RESPONSE TO MESSAGE 1]", + author: { type: "BOT" }, + }, + ], + }, + { + messages: [ + { + text: `hello ${BOT_MENTION}`, + author: { type: "USER", id: "U02" }, + }, + { + text: `hello ${buildMention({ + type: "USER", + id: "U02", + })}! how are you?`, + author: { type: "BOT" }, + }, + ], + }, +]; +const BASIC_EXAMPLE_CONVERSATIONS_WITH_SUMMARY: Conversation[] = [ + { ...BASIC_EXAMPLE_CONVERSATIONS[0], summary: "[SUMMARY]" }, + { + ...BASIC_EXAMPLE_CONVERSATIONS[1], + summary: `${buildMention({ + type: "USER", + id: "U02", + })} is a Software Engineer named Yigitcan.`, + }, +]; + +export const renderFormatAndExamples = ({ + hasSummary, + exampleConversations, +}: { + hasSummary: boolean; + exampleConversations?: Conversation[]; +}) => { + const conversations = + Array.isArray(exampleConversations) && exampleConversations.length > 0 + ? exampleConversations + : hasSummary + ? BASIC_EXAMPLE_CONVERSATIONS_WITH_SUMMARY + : BASIC_EXAMPLE_CONVERSATIONS; + + return `${ + hasSummary ? CONVERSATION_FORMAT_WITH_EXAMPLE : CONVERSATION_FORMAT + }\n\n${conversations.map(renderConversation).join("\n")}...`; +}; diff --git a/src/types/ai-persona.ts b/src/types/ai-persona.ts index e4c8dab..721f9a7 100644 --- a/src/types/ai-persona.ts +++ b/src/types/ai-persona.ts @@ -1,10 +1,8 @@ -import { Conversation } from "./conversation"; - export type AIPersona = { // name of the bot name: string; // base prompt that defines the personality and output type of the bot instructions: string; - // example conversation for the AI Persona - exampleConversations: Conversation[]; + // personality of the bot + personality: string; }; diff --git a/src/types/conversation.ts b/src/types/conversation.ts index 4aa26b4..64dc21f 100644 --- a/src/types/conversation.ts +++ b/src/types/conversation.ts @@ -2,7 +2,11 @@ import { Message } from "./message"; export type Conversation = { /** - * `messages` starting from oldest + * `summary` of prior conversation + */ + summary?: string; + /** + * `messages` which were send after the last summary. ordered from the oldest */ messages: Message[]; }; diff --git a/test/conversation-prompt/create-conversation-completion-prompt.test.ts b/test/conversation-prompt/create-conversation-completion-prompt.test.ts deleted file mode 100644 index 50977e3..0000000 --- a/test/conversation-prompt/create-conversation-completion-prompt.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { AIPersona, Author, buildMention } from "../../src"; -import { createConversationCompletionPrompt } from "../../src/conversation-prompt/create-conversation-completion-prompt"; - -describe("createConversationCompletionPrompt", () => { - const authors: Record = { - bot: { type: "BOT" }, - exampleUser1: { type: "USER", id: "EU01" }, - exampleUser2: { type: "USER", id: "EU02" }, - user1: { type: "USER", id: "U01" }, - }; - - it("should work with example conversations", () => { - const aiPersona: AIPersona = { - name: "Lenard", - instructions: "You are helpful.", - exampleConversations: [ - { - messages: [ - { text: "hello", author: authors.exampleUser1 }, - { - text: `hello ${buildMention( - authors.exampleUser1 - )}. how may I help you?`, - author: authors.bot, - }, - { - text: "how is it going?", - author: authors.exampleUser1, - }, - { - text: "it's fine. thanks for asking. how about you?", - author: authors.bot, - }, - ], - }, - { - messages: [ - { - text: "hello, friend.", - author: authors.exampleUser2, - }, - { text: "hey there! hello to you too.", author: authors.bot }, - ], - }, - ], - }; - - expect( - createConversationCompletionPrompt({ - aiPersona, - conversation: { - messages: [ - { - author: authors.user1, - text: "hello!", - }, - { - author: authors.bot, - text: `hello ${buildMention(authors.user1)}! how can I help you?`, - }, - { - author: authors.user1, - text: "can you write me fibonacci function in Typescript?", - }, - ], - }, - }) - ).toMatchInlineSnapshot(` - "Instructions for <@bot>: - Your name is "Lenard". You are referenced in conversations as "<@bot>". - You are helpful. - - The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: - - <@EU01>: hello <|endofstatement|> - <@bot>: hello <@EU01>. how may I help you? <|endofstatement|> - <@EU01>: how is it going? <|endofstatement|> - <@bot>: it's fine. thanks for asking. how about you? <|endofstatement|> - - <@EU02>: hello, friend. <|endofstatement|> - <@bot>: hey there! hello to you too. <|endofstatement|> - ... - - Continue the conversation, paying very close attention to things Human told you, such as their name, and personal details. Never say "<|endofstatement|>". Current conversation: - - <@U01>: hello! <|endofstatement|> - <@bot>: hello <@U01>! how can I help you? <|endofstatement|> - <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> - <@bot>: " - `); - }); - - it("should work without example conversations", () => { - const aiPersona: AIPersona = { - name: "Lenard", - instructions: "You are helpful.", - exampleConversations: [], - }; - - expect( - createConversationCompletionPrompt({ - aiPersona, - conversation: { - messages: [ - { - author: authors.user1, - text: "hello!", - }, - { - author: authors.bot, - text: "hello! how can I help you?", - }, - { - author: authors.user1, - text: "can you write me fibonacci function in Typescript?", - }, - ], - }, - }) - ).toMatchInlineSnapshot(` - "Instructions for <@bot>: - Your name is "Lenard". You are referenced in conversations as "<@bot>". - You are helpful. - - The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: - - <@U01>: [MESSAGE 1] <|endofstatement|> - <@bot>: [RESPONSE TO MESSAGE 1] <|endofstatement|> - - <@U02>: hello <@bot> <|endofstatement|> - <@bot>: hello <@U02>! how are you? <|endofstatement|> - ... - - Continue the conversation, paying very close attention to things Human told you, such as their name, and personal details. Never say "<|endofstatement|>". Current conversation: - - <@U01>: hello! <|endofstatement|> - <@bot>: hello! how can I help you? <|endofstatement|> - <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> - <@bot>: " - `); - }); -}); diff --git a/test/conversation-prompt/prompts/create-conversation-completion-prompt.test.ts b/test/conversation-prompt/prompts/create-conversation-completion-prompt.test.ts new file mode 100644 index 0000000..fc30e47 --- /dev/null +++ b/test/conversation-prompt/prompts/create-conversation-completion-prompt.test.ts @@ -0,0 +1,202 @@ +import { AIPersona, Author, BOT_MENTION, buildMention } from "../../../src"; +import { createConversationCompletionPrompt } from "../../../src/conversation-prompt/prompts/create-conversation-completion-prompt"; + +describe("createConversationCompletionPrompt", () => { + const aiPersona: AIPersona = { + name: "Lenard", + instructions: `When providing code examples, use triple backticks.`, + personality: `You are a software engineer.`, + }; + const authors: Record = { + bot: { type: "BOT" }, + exampleUser1: { type: "USER", id: "EU01" }, + exampleUser2: { type: "USER", id: "EU02" }, + user1: { type: "USER", id: "U01" }, + }; + + describe("with example conversations", () => { + it("should work without summary", () => { + expect( + createConversationCompletionPrompt({ + aiPersona, + exampleConversations: [ + { + messages: [ + { text: "hello", author: authors.exampleUser1 }, + { + text: `hello ${buildMention( + authors.exampleUser1 + )}. how may I help you?`, + author: authors.bot, + }, + { + text: "how is it going?", + author: authors.exampleUser1, + }, + { + text: "it's fine. thanks for asking. how about you?", + author: authors.bot, + }, + ], + }, + { + messages: [ + { + text: "hello, friend.", + author: authors.exampleUser2, + }, + { text: "hey there! hello to you too.", author: authors.bot }, + ], + }, + ], + + conversation: { + messages: [ + { + author: authors.user1, + text: "hello!", + }, + { + author: authors.bot, + text: `hello ${buildMention( + authors.user1 + )}! how can I help you?`, + }, + { + author: authors.user1, + text: "can you write me fibonacci function in Typescript?", + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + "Instructions for <@bot>, this is how you should behave in a conversation, but this is not your personality: + Your name is "Lenard". You are referenced in conversations as "<@bot>". + When providing code examples, use triple backticks. + + This is your personality: + You are a software engineer. + + The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: + + <@EU01>: hello <|endofstatement|> + <@bot>: hello <@EU01>. how may I help you? <|endofstatement|> + <@EU01>: how is it going? <|endofstatement|> + <@bot>: it's fine. thanks for asking. how about you? <|endofstatement|> + + <@EU02>: hello, friend. <|endofstatement|> + <@bot>: hey there! hello to you too. <|endofstatement|> + ... + + Continue the conversation, paying very close attention to things entities told you; such as their name, and personal details. Never say "<|endofstatement|>". Current conversation: + + <@U01>: hello! <|endofstatement|> + <@bot>: hello <@U01>! how can I help you? <|endofstatement|> + <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> + <@bot>:" + `); + }); + }); + + describe("without example conversations", () => { + it("should work without summary", () => { + expect( + createConversationCompletionPrompt({ + aiPersona, + conversation: { + messages: [ + { + author: authors.user1, + text: "hello!", + }, + { + author: authors.bot, + text: "hello! how can I help you?", + }, + { + author: authors.user1, + text: "can you write me fibonacci function in Typescript?", + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + "Instructions for <@bot>, this is how you should behave in a conversation, but this is not your personality: + Your name is "Lenard". You are referenced in conversations as "<@bot>". + When providing code examples, use triple backticks. + + This is your personality: + You are a software engineer. + + The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: + + <@U01>: [MESSAGE 1] <|endofstatement|> + <@bot>: [RESPONSE TO MESSAGE 1] <|endofstatement|> + + <@U02>: hello <@bot> <|endofstatement|> + <@bot>: hello <@U02>! how are you? <|endofstatement|> + ... + + Continue the conversation, paying very close attention to things entities told you; such as their name, and personal details. Never say "<|endofstatement|>". Current conversation: + + <@U01>: hello! <|endofstatement|> + <@bot>: hello! how can I help you? <|endofstatement|> + <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> + <@bot>:" + `); + }); + + it("should work with summary", () => { + expect( + createConversationCompletionPrompt({ + aiPersona, + conversation: { + summary: `${buildMention( + authors.user1 + )} asked ${BOT_MENTION} whether it knows Typescript.`, + messages: [ + { + author: authors.user1, + text: "hello!", + }, + { + author: authors.bot, + text: "hello! how can I help you?", + }, + { + author: authors.user1, + text: "can you write me fibonacci function in Typescript?", + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + "Instructions for <@bot>, this is how you should behave in a conversation, but this is not your personality: + Your name is "Lenard". You are referenced in conversations as "<@bot>". + When providing code examples, use triple backticks. + + This is your personality: + You are a software engineer. + + The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context. The conversations start with a "Summary:" which includes a detailed summary of messages in the same conversation. Summary ends with "<|endofstatement|>": + + Summary: [SUMMARY] <|endofstatement|> + <@U01>: [MESSAGE 1] <|endofstatement|> + <@bot>: [RESPONSE TO MESSAGE 1] <|endofstatement|> + + Summary: <@U02> is a Software Engineer named Yigitcan. <|endofstatement|> + <@U02>: hello <@bot> <|endofstatement|> + <@bot>: hello <@U02>! how are you? <|endofstatement|> + ... + + Continue the conversation, paying very close attention to things entities told you; such as their name, and personal details. Never say "<|endofstatement|>". Current conversation: + + Summary: <@U01> asked <@bot> whether it knows Typescript. <|endofstatement|> + <@U01>: hello! <|endofstatement|> + <@bot>: hello! how can I help you? <|endofstatement|> + <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> + <@bot>:" + `); + }); + }); +}); diff --git a/test/conversation-prompt/prompts/create-conversation-summary-prompt.test.ts b/test/conversation-prompt/prompts/create-conversation-summary-prompt.test.ts new file mode 100644 index 0000000..015089b --- /dev/null +++ b/test/conversation-prompt/prompts/create-conversation-summary-prompt.test.ts @@ -0,0 +1,116 @@ +import { AIPersona, Author, BOT_MENTION, buildMention } from "../../../src"; +import { createConversationSummaryPrompt } from "../../../src/conversation-prompt/prompts/create-conversation-summary-prompt"; + +describe("createConversationSummaryPrompt()", () => { + const aiPersona: AIPersona = { + name: "Lenard", + instructions: `When providing code examples, use triple backticks.`, + personality: `You are a software engineer.`, + }; + const authors: Record = { + bot: { type: "BOT" }, + user1: { type: "USER", id: "U01" }, + }; + + it("should summarize without already existing summary", () => { + expect( + createConversationSummaryPrompt({ + aiPersona, + conversation: { + messages: [ + { + author: authors.user1, + text: "hello!", + }, + { + author: authors.bot, + text: "hello! how can I help you?", + }, + { + author: authors.user1, + text: "can you write me fibonacci function in Typescript?", + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + "Instructions for <@bot>, this is how you should behave in a conversation, but this is not your personality: + Your name is "Lenard". You are referenced in conversations as "<@bot>". + When providing code examples, use triple backticks. + + This is your personality: + You are a software engineer. + + The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context: + + <@U01>: [MESSAGE 1] <|endofstatement|> + <@bot>: [RESPONSE TO MESSAGE 1] <|endofstatement|> + + <@U02>: hello <@bot> <|endofstatement|> + <@bot>: hello <@U02>! how are you? <|endofstatement|> + ... + + Summarize the conversation below. Make a detailed summary of the existing messages. Do not summarize the instructions or examples. Do not add anything extra or something that was not discussed. Do not repeat details. Pay close attention to the things that entities told you; especially their personal details and code details. Omit small talk and conversation status. Never say "<|endofstatement|>": + + <@U01>: hello! <|endofstatement|> + <@bot>: hello! how can I help you? <|endofstatement|> + <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> + ... + Summary:" + `); + }); + + it("should summarize with already existing summary", () => { + expect( + createConversationSummaryPrompt({ + aiPersona, + conversation: { + summary: `${buildMention( + authors.user1 + )} asked ${BOT_MENTION} whether it knows Typescript.`, + messages: [ + { + author: authors.user1, + text: "hello!", + }, + { + author: authors.bot, + text: "hello! how can I help you?", + }, + { + author: authors.user1, + text: "can you write me fibonacci function in Typescript?", + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + "Instructions for <@bot>, this is how you should behave in a conversation, but this is not your personality: + Your name is "Lenard". You are referenced in conversations as "<@bot>". + When providing code examples, use triple backticks. + + This is your personality: + You are a software engineer. + + The conversations are in this format, there can be an arbitrary amount of newlines between chat entries. "<@id>" format is used to reference entities in the conversation, where "id" is replaced with message author's unique id. The text "<|endofstatement|>" is used to separate chat entries and make it easier for you to understand the context. The conversations start with a "Summary:" which includes a detailed summary of messages in the same conversation. Summary ends with "<|endofstatement|>": + + Summary: [SUMMARY] <|endofstatement|> + <@U01>: [MESSAGE 1] <|endofstatement|> + <@bot>: [RESPONSE TO MESSAGE 1] <|endofstatement|> + + Summary: <@U02> is a Software Engineer named Yigitcan. <|endofstatement|> + <@U02>: hello <@bot> <|endofstatement|> + <@bot>: hello <@U02>! how are you? <|endofstatement|> + ... + + Summarize the conversation below. Make a detailed summary which only consists of the previous summary and later messages. Do not summarize the instructions or examples. Do not add anything extra or something that was not discussed. Do not repeat details. Pay close attention to the things that entities told you; especially their personal details and code details. Omit small talk and conversation status. Never say "<|endofstatement|>": + + Summary: <@U01> asked <@bot> whether it knows Typescript. <|endofstatement|> + <@U01>: hello! <|endofstatement|> + <@bot>: hello! how can I help you? <|endofstatement|> + <@U01>: can you write me fibonacci function in Typescript? <|endofstatement|> + ... + Summary:" + `); + }); +});