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

Added Promptrix to TypeChat #88

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions examples/music/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"chalk": "^2.3.1",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"promptrix": "^0.4.2",
"open": "^7.0.4"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion examples/music/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from "fs";
import path from "path";
import { UserMessage } from "promptrix";
import { Authzor } from "./authz";
import chalk from "chalk";
import dotenv from "dotenv";
Expand Down Expand Up @@ -514,7 +515,7 @@ async function handleCall(
}
case "nonMusicQuestion": {
const text = args[0] as string;
const ret = await model.complete(text);
const ret = await model.complete(new UserMessage(text));
if (ret.success) {
console.log(ret.data);
}
Expand Down
3 changes: 2 additions & 1 deletion examples/music/src/trackFilter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UserMessage } from "promptrix";
import { TypeChatLanguageModel } from "../../../dist";
import { getArtist } from "./endpoints";
import { IClientContext } from "./main";
Expand Down Expand Up @@ -419,7 +420,7 @@ async function llmFilter(
trackNumbers: number[];
};\n`;
prompt += `Here is a JSON object of type Matches containing the track numbers of the tracks that match ${description}:\n`;
const ret = await model.complete(prompt);
const ret = await model.complete(new UserMessage(prompt));
return ret;
}

Expand Down
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
],
"dependencies": {
"axios": "^1.4.0",
"promptrix": "^0.4.2",
"typescript": "^5.1.3"
},
"devDependencies": {
Expand Down
20 changes: 16 additions & 4 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from "axios";
import { PromptSection, PromptMemory, PromptFunctions, VolatileMemory, FunctionRegistry, GPT3Tokenizer } from "promptrix";
import { Result, success, error } from "./result";

/**
Expand All @@ -8,6 +9,10 @@ import { Result, success, error } from "./result";
* and `createAzureOpenAILanguageModel` functions create instances of this interface.
*/
export interface TypeChatLanguageModel {
/**
* Optional maximum number of tokens to send to the language model (the default is 3000).
*/
maxInputTokens?: number;
/**
* Optional property that specifies the maximum number of retry attempts (the default is 3).
*/
Expand All @@ -18,9 +23,11 @@ export interface TypeChatLanguageModel {
retryPauseMs?: number;
/**
* Obtains a completion from the language model for the given prompt.
* @param prompt The prompt string.
* @param prompt The prompt template to complete.
* @param memory Optional memory object to pass to the language model.
* @param functions Optional registry of prompt template functions.
*/
complete(prompt: string): Promise<Result<string>>;
complete(prompt: PromptSection, memory?: PromptMemory, functions?: PromptFunctions): Promise<Result<string>>;
}

/**
Expand Down Expand Up @@ -93,14 +100,19 @@ function createAxiosLanguageModel(url: string, config: object, defaultParams: Re
};
return model;

async function complete(prompt: string) {
async function complete(prompt: PromptSection, memory?: PromptMemory, functions?: PromptFunctions) {
const rendered = await prompt.renderAsMessages(memory ?? new VolatileMemory(), functions ?? new FunctionRegistry(), new GPT3Tokenizer(), model.maxInputTokens ?? 3000);
if (rendered.tooLong) {
return error("Prompt is too long");
}

let retryCount = 0;
const retryMaxAttempts = model.retryMaxAttempts ?? 3;
const retryPauseMs = model.retryPauseMs ?? 1000;
while (true) {
const params = {
...defaultParams,
messages: [{ role: "user", content: prompt }],
messages: rendered.output,
temperature: 0,
n: 1
};
Expand Down
33 changes: 21 additions & 12 deletions src/program.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Prompt, UserMessage } from "promptrix";
import { Result, error, success } from "./result";
import { TypeChatLanguageModel } from "./model";
import { TypeChatJsonTranslator, createJsonTranslator } from "./typechat";
Expand Down Expand Up @@ -70,14 +71,14 @@ export type ResultReference = {
/**
* Transforms a JSON program object into an equivalent TypeScript module suitable for type checking.
* The generated module takes the form:
*
*
* import { API } from "./schema";
* function program(api: API) {
* const step1 = api.someFunction1(...);
* const step2 = api.someFunction2(...);
* return api.someFunction3(...);
* }
*
*
* @param jsonObject A JSON program object.
* @returns A `Success<string>` with the module source code or an `Error` explaining why the JSON object
* couldn't be transformed.
Expand Down Expand Up @@ -200,18 +201,26 @@ export function createProgramTranslator(model: TypeChatLanguageModel, schema: st
return translator;

function createRequestPrompt(request: string) {
return `You are a service that translates user requests into programs represented as JSON using the following TypeScript definitions:\n` +
`\`\`\`\n${programSchemaText}\`\`\`\n` +
`The programs can call functions from the API defined in the following TypeScript definitions:\n` +
`\`\`\`\n${translator.validator.schema}\`\`\`\n` +
`The following is a user request:\n` +
`"""\n${request}\n"""\n` +
`The following is the user request translated into a JSON program object with 2 spaces of indentation and no properties with the value undefined:\n`;
return new Prompt([
new UserMessage(
`You are a service that translates user requests into programs represented as JSON using the following TypeScript definitions:\n` +
`\`\`\`\n${programSchemaText}\`\`\`\n` +
`The programs can call functions from the API defined in the following TypeScript definitions:\n` +
`\`\`\`\n${translator.validator.schema}\`\`\`\n` +
`The following is a user request:\n` +
`"""\n${request}\n"""\n` +
`The following is the user request translated into a JSON program object with 2 spaces of indentation and no properties with the value undefined:\n`
)
]);
}

function createRepairPrompt(validationError: string) {
return `The JSON program object is invalid for the following reason:\n` +
`"""\n${validationError}\n"""\n` +
`The following is a revised JSON program object:\n`;
return new Prompt([
new UserMessage(
`The JSON program object is invalid for the following reason:\n` +
`"""\n${validationError}\n"""\n` +
`The following is a revised JSON program object:\n`
)
]);
}
}
36 changes: 25 additions & 11 deletions src/typechat.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { PromptSection, Prompt, UserMessage, AssistantMessage } from "promptrix";
import { Result, error } from "./result";
import { TypeChatLanguageModel } from "./model";
import { TypeChatJsonValidator, createJsonValidator } from "./validate";


/**
* Represents an object that can translate natural language requests in JSON objects of the given type.
*/
Expand Down Expand Up @@ -32,7 +34,7 @@ export interface TypeChatJsonTranslator<T extends object> {
* @param request The natural language request.
* @returns A prompt that combines the request with the schema and type name of the underlying validator.
*/
createRequestPrompt(request: string): string;
createRequestPrompt(request: string): PromptSection;
/**
* Creates a repair prompt to append to an original prompt/response in order to repair a JSON object that
* failed to validate. This function is called by `completeAndValidate` when `attemptRepair` is true and the
Expand All @@ -41,7 +43,7 @@ export interface TypeChatJsonTranslator<T extends object> {
* @param validationError The error message returned by the validator.
* @returns A repair prompt constructed from the error message.
*/
createRepairPrompt(validationError: string): string;
createRepairPrompt(validationError: string): PromptSection;
/**
* Translates a natural language request into an object of type `T`. If the JSON object returned by
* the language model fails to validate and the `attemptRepair` property is `true`, a second
Expand Down Expand Up @@ -76,17 +78,25 @@ export function createJsonTranslator<T extends object>(model: TypeChatLanguageMo
return typeChat;

function createRequestPrompt(request: string) {
return `You are a service that translates user requests into JSON objects of type "${validator.typeName}" according to the following TypeScript definitions:\n` +
`\`\`\`\n${validator.schema}\`\`\`\n` +
`The following is a user request:\n` +
`"""\n${request}\n"""\n` +
`The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:\n`;
return new Prompt([
new UserMessage(
`You are a service that translates user requests into JSON objects of type "${validator.typeName}" according to the following TypeScript definitions:\n` +
`\`\`\`\n${validator.schema}\`\`\`\n` +
`The following is a user request:\n` +
`"""\n${request}\n"""\n` +
`The following is the user request translated into a JSON object with 2 spaces of indentation and no properties with the value undefined:\n`
)
]);
}

function createRepairPrompt(validationError: string) {
return `The JSON object is invalid for the following reason:\n` +
`"""\n${validationError}\n"""\n` +
`The following is a revised JSON object:\n`;
return new Prompt([
new UserMessage(
`The JSON object is invalid for the following reason:\n` +
`"""\n${validationError}\n"""\n` +
`The following is a revised JSON object:\n`
)
]);
}

async function translate(request: string) {
Expand All @@ -111,7 +121,11 @@ export function createJsonTranslator<T extends object>(model: TypeChatLanguageMo
if (!attemptRepair) {
return error(`JSON validation failed: ${validation.message}\n${jsonText}`);
}
prompt += `${responseText}\n${typeChat.createRepairPrompt(validation.message)}`;
prompt = new Prompt([
prompt,
new AssistantMessage(responseText),
typeChat.createRepairPrompt(validation.message)
]);
attemptRepair = false;
}
}
Expand Down