Skip to content

Instantly share code, notes, and snippets.

@johnkueh
Last active June 19, 2023 04:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnkueh/0340ee25da52c005f41e01735aff2e24 to your computer and use it in GitHub Desktop.
Save johnkueh/0340ee25da52c005f41e01735aff2e24 to your computer and use it in GitHub Desktop.
openai-function-calling
import { ChatOpenAI } from 'langchain/chat_models/openai';
import { HumanChatMessage } from 'langchain/schema';
import { ZodSchema, z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
import { winstonLogger } from '../winston';
const logger = winstonLogger.child({ service: 'AIService' });
const chatOpenAIModel = new ChatOpenAI({
temperature: 0,
timeout: 80000,
maxConcurrency: 10,
modelName: 'gpt-3.5-turbo-16k-0613',
openAIApiKey: process.env.OPENAI_API_KEY,
});
// Example usage
// AIService.callChatOpenAI({
// promptTemplate: AIService.extractRecipeIngredientsFromTextPrompt,
// variables: {
// contentType,
// text,
// },
// });
// Example prompt template
// export const extractShoppingListItemFromTextPrompt = {
// prompt: `For this shopping list item: "{text}"`,
// inputSchema: z.object({
// text: z.string(),
// }),
// responseSchema: z.object({
// isRecipeIngredient: z.boolean().describe('is the item a food ingredient?'),
// aisle: z
// .string()
// .describe('pick one from "Household, Pet, Personal Care, Baby, Uncategorized"'),
// quantity: z.number().describe('quantity of the item, 1 if unknown'),
// unit: z.string().nullable().describe('unit of the item, null if unknown'),
// }),
// };
export async function callChatOpenAI<I extends ZodSchema, R extends ZodSchema>({
promptTemplate: { prompt, inputSchema, responseSchema },
variables,
}: CallOpenAI<I, R>) {
const startTime = performance.now();
const content = `${interpolatePrompt(prompt, inputSchema.parse(variables))}`;
const jsonSchema = zodToJsonSchema(responseSchema, 'schema');
try {
const functionName = 'print_response';
const response = await chatOpenAIModel.call([new HumanChatMessage(content)], {
functions: [{ name: functionName, parameters: jsonSchema.definitions?.schema }],
function_call: { name: functionName },
});
const elapsedTime = `${performance.now() - startTime}ms`;
const responseJson = response.additional_kwargs?.function_call as {
arguments: string;
};
const data = JSON.parse(responseJson.arguments);
const parsedResponse = responseSchema.parse(data) as z.infer<R>;
logger.info('callOpenAI success', { prompt, parsedResponse, elapsedTime });
return data;
} catch (e: any) {
logger.error('callOpenAI', { prompt, error: e.message });
throw e;
}
}
export function interpolatePrompt(prompt: string, variables: Record<string, string>): string {
let interpolatedPrompt = prompt;
for (const variable in variables) {
const regex = new RegExp(`\\{${variable}\\}`, 'g');
interpolatedPrompt = interpolatedPrompt.replace(regex, variables[variable]);
}
return interpolatedPrompt;
}
type PromptTemplate<I extends z.ZodType<any, any>, R extends z.ZodType<any, any>> = {
prompt: string;
inputSchema: I;
responseSchema: R;
};
type CallOpenAI<I extends ZodSchema, R extends ZodSchema> = {
promptTemplate: PromptTemplate<I, R>;
variables: z.infer<I>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment