Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.
Open
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
42 changes: 20 additions & 22 deletions packages/promptable/src/prompts/Prompt.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { injectVariables } from "@utils/inject-variables";
import { NoopParser, Parser } from "@prompts/Parser";
import { ExtractFormatObject, ExtractVariableNames } from "@utils/type-utils";
import { extractVariableNames } from "@utils/extract-variable-names";
import { ExtractFormatObject } from "@utils/type-utils";

export type PromptVariables<T extends string> = {
[K in keyof ExtractFormatObject<T>]: Required<ExtractFormatObject<T>[K]>;
export type PromptVariables<TPromptText extends string> = {
[K in keyof ExtractFormatObject<TPromptText>]: Required<ExtractFormatObject<TPromptText>[K]>;
};

export interface PromptConfiguration {
Expand All @@ -19,15 +17,15 @@ export const DEFAULT_PROMPT_CONFIGURATION: PromptConfiguration = {
max_tokens: 128,
};

export class Prompt<T extends string, V extends PromptVariables<T>> {
readonly template: PromptTemplate<T, V>;
export class Prompt<TPromptText extends string, TPromptVariables extends PromptVariables<TPromptText>> {
readonly template: PromptTemplate<TPromptText, TPromptVariables>;
constructor(
initTemplate: PromptTemplate<T, V> | T,
readonly variables: V,
initTemplate: PromptTemplate<TPromptText, TPromptVariables> | TPromptText,
readonly variables: TPromptVariables,
readonly configuration: PromptConfiguration = DEFAULT_PROMPT_CONFIGURATION
) {
if (typeof initTemplate === "string") {
this.template = new PromptTemplate(initTemplate);
this.template = new PromptTemplate(initTemplate, configuration);
} else {
this.template = initTemplate;
}
Expand All @@ -37,12 +35,12 @@ export class Prompt<T extends string, V extends PromptVariables<T>> {
}

clone({
variables,
configuration,
variables = {},
configuration = {},
}: {
variables?: Partial<V> | V;
variables?: Partial<TPromptVariables> | TPromptVariables;
configuration?: Partial<PromptConfiguration> | PromptConfiguration;
}): Prompt<T, V> {
} = {}): Prompt<TPromptText, TPromptVariables> {
return new Prompt(
this.template,
{
Expand Down Expand Up @@ -70,12 +68,12 @@ export class Prompt<T extends string, V extends PromptVariables<T>> {
}
}

export class PromptTemplate<T extends string, V extends PromptVariables<T>> {
readonly text: T;
export class PromptTemplate<TPromptText extends string, TPromptVariables extends PromptVariables<TPromptText>> {
readonly text: TPromptText;
readonly configuration: PromptConfiguration;

constructor(
text: T,
text: TPromptText,
configuration: PromptConfiguration = DEFAULT_PROMPT_CONFIGURATION
) {
this.text = text;
Expand All @@ -88,8 +86,8 @@ export class PromptTemplate<T extends string, V extends PromptVariables<T>> {
* @param variables
* @returns Prompt
*/
build(variables: V, configuration?: PromptConfiguration) {
return new Prompt<T, V>(this, variables, configuration);
build(variables: TPromptVariables, configuration?: PromptConfiguration) {
return new Prompt<TPromptText, TPromptVariables>(this, variables, configuration);
}

toJSON() {
Expand All @@ -100,9 +98,9 @@ export class PromptTemplate<T extends string, V extends PromptVariables<T>> {
}
}

export const prompt = <T extends string>(
template: T,
variables: PromptVariables<T>,
export const prompt = <TPromptText extends string>(
template: TPromptText,
variables: PromptVariables<TPromptText>,
configuration?: PromptConfiguration
) => {
return new PromptTemplate(template, configuration).build(variables);
Expand Down
48 changes: 48 additions & 0 deletions packages/promptable/src/prompts/__tests__/Parser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { NoopParser, JSONParser, CSVParser, ListParser } from "@prompts/Parser"

test("NoopParser", () => {
expect(new NoopParser().parse("test")).toEqual("test");
})

describe("JSONParser", () => {
test("should throw error for invalid json", () => {
const parser = new JSONParser();
expect(() => parser.parse("")).toThrow("");
});

test("should parse valid json", () => {
const parser = new JSONParser();
expect(parser.parse('{ "hello": "world" }')).toEqual({ hello: "world" });
});
});

describe("CSVParser", () => {
test("should throw error for invalid csv", () => {
const parser = new CSVParser();
// @ts-expect-error
expect(() => parser.parse(1)).toThrow("")
});

test("should parse valid csv", () => {
const parser = new CSVParser();
expect(parser.parse("a,b,c\n1,2,3")).toEqual([{
a: "1",
b: "2",
c: "3"
}])
});
});


describe("ListParser", () => {
test("should throw error for invalid json", () => {
const parser = new ListParser();
// @ts-expect-error
expect(() => parser.parse(1)).toThrow("")
});

test("should parse valid json", () => {
const parser = new ListParser();
expect(parser.parse("a, b, c")).toEqual(["a", "b", "c"])
});
});
64 changes: 34 additions & 30 deletions packages/promptable/src/prompts/__tests__/Prompt.test.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import { Prompt } from "@prompts/Prompt";
import { prompt } from "@prompts/Prompt";

const PROMPT_TEXT = "this is a {{test}}";

describe("prompt", () => {
test("should throw type errors if prompt requires variables but does not provide them", () => {
// @ts-expect-error
prompt(PROMPT_TEXT, {});

// @ts-expect-error
expect(prompt(PROMPT_TEXT).text).toEqual(PROMPT_TEXT);
});

test("Can serialize prompt to JSON", () => {
const variables = { test: "test" };
const p = prompt(PROMPT_TEXT, variables)
const configuration = { max_tokens: 128, stop: null, temperature: 0.7 };
expect(p.toJSON()).toEqual({
configuration,
variables,
text: "this is a test",
template: {
configuration,
text: PROMPT_TEXT,
}
});
});

test("Can clone prompt", () => {
const variables = { test: "test" };
const p = prompt(PROMPT_TEXT, variables);
const pClone = p.clone({ variables: { test: "test2" } })
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cfortuner was this how you intended the p.clone api to be or did you mean for it to be p.clone({ test: "test2" })

expect(pClone.text).toEqual("this is a test2")
});

test("Prompt Class", () => {
const p = new Prompt("this is a {{test}}", { test: "123" });

// saves the template
expect(p.template).toEqual("this is a {{test}}");

// Valid, returns a new prompt
const formattedPrompt = p.format({ test: "123" });
expect(formattedPrompt.text).toEqual("this is a 123");
expect(formattedPrompt).not.toBe(p);

// Invalid
// @ts-expect-error
expect(p.format({ invalid: "123" }).text).toEqual("this is a {{test}}");
});

test("Prompt without variables", () => {
const p = new Prompt("this is a test", {});

// saves the template
expect(p.template).toEqual("this is a test");

// Valid, returns a new prompt
const formattedPrompt = p.format({ test: "123" });
expect(formattedPrompt.text).toEqual("this is a test");
expect(formattedPrompt).not.toBe(p);

// Invalid
// @ts-expect-error
expect(p.format({ invalid: "123" }).text).toEqual("this is a test");
});
45 changes: 45 additions & 0 deletions packages/promptable/src/prompts/__tests__/prompt-templates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
QA,
ExtractText,
Summarize,
Chatbot,
ExtractJSON,
ExtractCSV,
FixMarkup,
} from "@prompts/prompt-templates";
import { Expect, Equal } from "@utils/__tests__/type-utils.test";
import { ExtractVariableNames } from "@utils/type-utils";

test("ExtractVariableNames", () => {
// These tests will cause a compile time error if the types don't match
type tests = [
Expect<Equal<
ExtractVariableNames<typeof QA.text>,
Array<"document" | "question">
>>,
Expect<Equal<
ExtractVariableNames<typeof ExtractText.text>,
Array<"document" | "question">
>>,
Expect<Equal<
ExtractVariableNames<typeof Summarize.text>,
Array<"document">
>>,
Expect<Equal<
ExtractVariableNames<typeof Chatbot.text>,
Array<"memory" | "userInput">
>>,
Expect<Equal<
ExtractVariableNames<typeof ExtractJSON.text>,
Array<"data" | "type">
>>,
Expect<Equal<
ExtractVariableNames<typeof ExtractCSV.text>,
Array<"data" | "headers">
>>,
Expect<Equal<
ExtractVariableNames<typeof FixMarkup.text>,
Array<"markupLanguage" | "documentType" | "markup">
>>,
];
});
8 changes: 4 additions & 4 deletions packages/promptable/src/utils/type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ type Split<S extends string, Delimiter extends string> = string extends S
? [Head, ...Split<Tail, Delimiter>]
: [S];

export type ExtractFormatObject<TPrompt extends string> = {
[K in Split<TPrompt, "{{">[number] as K extends `${infer TName}}}${string}`
export type ExtractFormatObject<TPromptText extends string> = {
[K in Split<TPromptText, "{{">[number] as K extends `${infer TName}}}${string}`
? TName
: never]: string;
};
export type ExtractVariableNames<TPrompt extends string> = Array<
keyof ExtractFormatObject<TPrompt>
export type ExtractVariableNames<TPromptText extends string> = Array<
keyof ExtractFormatObject<TPromptText>
>;