import { z } from "zod"; import { openAI } from "./clients/open-ai"; import { openAICompletionsCallWithFallback } from "./completions-call-with-fallback"; type Functions = Exclude< Parameters<typeof openAI.chat.completions.create>[0]["functions"], undefined >; type FunctionCall = Functions[number]; const productInquiryFunctionName = "productInquiry"; const productRecommendationFunctionName = "productRecommendation"; const postPurchaseSupportFunctionName = "postPurchaseSupport"; type AvailableFunctions = | typeof productInquiryFunctionName | typeof productRecommendationFunctionName | typeof postPurchaseSupportFunctionName; const ProductInquiryResponseSchema = z.object({ name: z.string(), description: z.string(), price: z.number(), }); type ProductInquiryResponse = z.infer<typeof ProductInquiryResponseSchema>; const productInquiryFunctionCall: FunctionCall = { name: productInquiryFunctionName, description: "Retrieve information about a product", parameters: { type: "object", required: ["name", "description", "price"], properties: { name: { type: "string", description: "Name of the product", }, description: { type: "string", description: "Description of the product", }, price: { type: "number", description: "Price of the product", }, }, }, }; const handleProductInquiry = async (inquiry: ProductInquiryResponse) => { // do something here }; const ProductRecommendationResponseSchema = z.object({ products: z.array( z.object({ name: z.string(), description: z.string(), productLink: z.string().url(), }) ), }); type ProductRecommendationResponse = z.infer< typeof ProductRecommendationResponseSchema >; const productRecommendationFunctionCall: FunctionCall = { name: productRecommendationFunctionName, description: "Retrieve recommended products", parameters: { type: "object", required: ["products"], properties: { products: { type: "array", description: "Recommended products", items: { type: "object", properties: { name: { type: "string", description: "Name of the product", }, description: { type: "string", description: "Short description of the product", }, productLink: { type: "string", description: "Link to the product", }, }, }, }, }, }, }; const handleProductRecommendation = async ( recommendation: ProductRecommendationResponse ) => { //typesafe recommendation.products.forEach((product) => { console.log(`${product.name} - ${product.description}`); }); }; const PostPurchaseSupportResponseSchema = z.object({ description: z.string(), troubleshootingSteps: z.array(z.string()), contactInfo: z.string().optional(), }); type PostPurchaseSupportResponse = z.infer< typeof PostPurchaseSupportResponseSchema >; const postPurchaseSupportFunctionCall: FunctionCall = { name: postPurchaseSupportFunctionName, description: "Retrieve support information for a product", parameters: { type: "object", required: ["description", "troubleshootingSteps"], properties: { description: { type: "string", description: "Description of the issue", }, troubleshootingSteps: { type: "array", description: "Steps to troubleshoot the issue", items: { type: "string", description: "Step to troubleshoot the issue", }, }, contactInfo: { type: "string", description: "Contact info for support", }, }, }, }; const handlePostPurchaseSupport = async ( support: PostPurchaseSupportResponse ) => { // typesafe support.contactInfo; support.description; support.troubleshootingSteps; }; interface MapParserToFunction<T = any> { function: (parsedResponse: T) => Promise<void>; parse: (data: unknown) => T; } const parserAndFunction: Record<AvailableFunctions, MapParserToFunction> = { [productInquiryFunctionName]: { parse: ProductInquiryResponseSchema.parse, function: handleProductInquiry, }, [productRecommendationFunctionName]: { parse: ProductRecommendationResponseSchema.parse, function: handleProductRecommendation, }, [postPurchaseSupportFunctionName]: { parse: PostPurchaseSupportResponseSchema.parse, function: handlePostPurchaseSupport, }, } as const; const functions: Functions = [ productInquiryFunctionCall, productRecommendationFunctionCall, postPurchaseSupportFunctionCall, ]; const prompt = (userQuestion: string) => ` Help this user with their question about our product: User Question: ${userQuestion} `; const answerUserQuestionAboutOurEcommerceProduct = async (question: string) => { const competitorData = await openAICompletionsCallWithFallback({ messages: [ { role: "system", content: "You are an assistant helping us answer questions from users on our ecommerce website", }, { role: "user", content: prompt(question) }, ], function_call: "auto", functions, }); await Promise.all( competitorData.choices.map((res) => { const functionName = res.message.function_call?.name as | AvailableFunctions | undefined; if (functionName) { const { parse, function: functionToExecute } = parserAndFunction[functionName]; const functionResponse = res.message.function_call?.arguments; if (functionResponse) { const parsedResponse = parse(JSON.parse(functionResponse)); return functionToExecute(parsedResponse); } } }) ); };