import type OpenAI from "openai"; import { type PromptTemplate } from "@langchain/core/prompts"; import { type Message, type SupportOfferResult, type SupportTicketContext, type SupportRequestResult, } from "../schemas"; import { RequireSupportTemplate } from "../prompt-templates"; import { type OfferSupportStrategy } from "./offer-detection/offer-detection-strategy"; /** * The SupportTicket class is responsible for managing the process of determining whether a support ticket should be offered or generated * based on user interactions in a chat environment. It utilizes OpenAI's language model to analyze chat history and make decisions. * * Usage: * - Initialize with a support strategy, context, and OpenAI instance. * - Use `requireSupport` to check if a support ticket is requested by the user. * - Use `detectSupportRequest` to determine if a support offer should be made. * - Internally uses the `getCompletion` method to get responses from the OpenAI model based on chat history. * - Formats chat history and handles API calls to OpenAI. * * @class SupportTicket */ export class SupportTicket { temp = 0; model = "gpt-3.5-turbo"; tokenLimit = 100; examplePath = "./data/prompt/multi-attempt/examples.json"; systemInstruction: PromptTemplate; constructor( private readonly offerSupportStrategy: OfferSupportStrategy, private readonly supportTicketContext: SupportTicketContext, private readonly openAi: OpenAI ) { this.systemInstruction = new RequireSupportTemplate().getInstruction; } public get getTemp(): number { return this.temp; } public get getModel(): string { return this.model; } public get getTokenLimit(): number { return this.tokenLimit; } public async requireSupport(): Promise { const completion = await this.getCompletion(); if (!completion) { throw new Error( "Failed to get a response from OpenAI, consider retrying." ); } const parsedResponse: Record = JSON.parse( completion ) as Record; return { userRequestedSupportTicket: parsedResponse.userRequestedSupportTicket, userAcceptsOfferedSupportTicket: parsedResponse.userAcceptsOfferedSupportTicket, generateSupportTicket: parsedResponse.generateSupportTicket, }; } public async determineOfferSupport(): Promise { return this.offerSupportStrategy.shouldOfferSupport(); } async getCompletion() { const lastTwoMessages = this.supportTicketContext.chatHistory.slice(-2); // We only care about the last two messages sent from the user and the assistant to detemine if support ticket should be generated - see prompt template for details const chatHistoryString = this.convertMessagesToString(lastTwoMessages); const formattedSystemInstruction = await this.systemInstruction.format({ chatHistory: chatHistoryString, }); try { const completion = await this.openAi.chat.completions.create({ messages: [{ role: "system", content: formattedSystemInstruction }], temperature: this.getTemp, model: this.getModel, /* eslint-disable @typescript-eslint/naming-convention */ response_format: { type: "json_object" }, max_tokens: this.getTokenLimit, /* eslint-enable @typescript-eslint/naming-convention */ }); return completion?.choices[0]?.message?.content ?? null; } catch (error_) { const error = error_ instanceof Error ? new Error(`Failed to call OpenAI Client: ${error_.message}`) : new Error("Failed to call OpenAI Client with an unknown error"); throw error; } } convertMessagesToString(messages: Message[]): string { return messages .map((entry) => `${entry.role}: ${entry.content}`) .join("\n"); } }