UNPKG

whatsapp-claude-gpt

Version:

WhatsApp-Claude-GPT is an advanced chatbot for WhatsApp, integrating AI language models for text conversations, image generation, and voice messages.

255 lines (211 loc) 9.92 kB
import logger from '../logger'; import { OpenAI, toFile } from 'openai'; import { ChatCompletionMessageParam } from 'openai/resources'; import { AIConfig } from '../config'; import { ChatCompletion } from 'openai/src/resources/chat/completions'; import { ResponseInput, Tool } from "openai/resources/responses/responses"; export class OpenaiService { constructor() { } async sendChatWithTools( messageList: ResponseInput, responseType: any = 'text', tools?: Array<Tool> ): Promise<string> { const openAICLient = new OpenAI({ baseURL: AIConfig.ChatConfig.baseURL, apiKey: AIConfig.ChatConfig.apiKey, }); const model = AIConfig.ChatConfig.model; logger.info(`[OpenAI] Sending ${messageList.length} messages`); logger.debug(`[OpenAI] Sending Msg: ${JSON.stringify(messageList[messageList.length - 1])}`); const responseResult = await openAICLient.responses.create({ model: model, input: messageList, text: { format: { type: responseType } }, reasoning: {}, tools: tools, temperature: 1, max_output_tokens: 2048, top_p: 1, store: true }); logger.debug('[OpenAI] Completion Response:' + JSON.stringify(responseResult.output_text)); const messageResult = responseResult.output_text; const functionCalls = responseResult.output.filter(toolCall => toolCall.type === "function_call"); if (functionCalls.length === 0) return messageResult; let updatedMessages: ResponseInput = [...messageList as any]; for (const toolCall of responseResult.output) { if (toolCall.type !== "function_call") { continue; } updatedMessages.push(toolCall); // TODO: I have not implemented functions yet, so they will not be processed. //const name = toolCall.name; //const args = JSON.parse(toolCall.arguments); // // try { // // // const result = await executeFunctions(name, args, inputData); // updatedMessages.push({ // type: "function_call_output", // call_id: toolCall.call_id, // output: result.toString() // }); // }catch (error) { // logger.error(`Error executing function ${name}:`, error); // updatedMessages.push({ // type: "function_call_output", // call_id: toolCall.call_id, // output: `Error executing function ${name}.` // }); // } } // Recursive call with updated messages return this.sendChatWithTools(updatedMessages, responseType, tools); } /** * Sends a series of messages to the OpenAI Chat Completion API and retrieves a generated completion. * This function is designed to interact with the OpenAI API, sending it a context composed of several messages. * It then receives a response that is generated based on this context, aiming to provide a coherent and contextually appropriate continuation or reply. * * Parameters: * - messageList: An array of ChatCompletionMessageParam objects, which include the messages that form the context for the API request. * * Returns: * - A promise that resolves to the generated completion string, which is the API's response based on the provided context. */ async sendCompletion(messageList: ChatCompletionMessageParam[]): Promise<string> { const openAICLient = new OpenAI({ baseURL: AIConfig.ChatConfig.baseURL, apiKey: AIConfig.ChatConfig.apiKey, }); const model = AIConfig.ChatConfig.model; const MAX_RETRIES = 1; let currentTry = 0; let lastError: any; const params: any = { model: model, messages: messageList, response_format: { type: "json_object" }, store: true } while (currentTry <= MAX_RETRIES) { try { logger.debug(`[${AIConfig.ChatConfig.provider}->sendCompletion] Attempt ${currentTry + 1}/${MAX_RETRIES + 1}: Sending ${messageList.length} messages.`); const reponse: ChatCompletion = await openAICLient.chat.completions.create(params); let fullResponse = reponse.choices[0]?.message?.content; if (!fullResponse || fullResponse == '') throw new Error(`An error occurred while communicating with ${AIConfig.ChatConfig.provider}. It returned an empty response.`); logger.debug(`[${AIConfig.ChatConfig.provider}->sendCompletion] Completion Response:`); logger.debug(fullResponse); return fullResponse; } catch (e: any) { lastError = e; currentTry++; if (currentTry <= MAX_RETRIES) { logger.warn(`[${AIConfig.ChatConfig.provider}->sendCompletion] Attempt ${currentTry}/${MAX_RETRIES + 1} failed: ${e.message ?? e}`); await new Promise(resolve => setTimeout(resolve, 1000 * currentTry)); } else { logger.error(`[${AIConfig.ChatConfig.provider}->sendCompletion] All ${MAX_RETRIES + 1} attempts failed. Last error: ${e.message ?? e}`); throw new Error(`Failed after ${MAX_RETRIES + 1} attempts. Last error: ${e.message ?? e}. Please try again later or consider using an alternative AI. If the issue persists, contact support for further assistance.`); } } } throw lastError; } /** * Requests the generation of an image based on a textual description, by interacting with OpenAI's image generation API. * This function takes a prompt in the form of text and sends a request to generate an image that corresponds with the text description provided. * It aims to utilize OpenAI's capabilities to create visually representative images based on textual inputs. * * Parameters: * - message: A string containing the text description that serves as the prompt for image generation. * * Returns: * - A promise that resolves to the URL of the generated image. This URL points to the image created by OpenAI's API based on the input prompt. */ async createImage(message) { logger.debug(`[${AIConfig.ImageConfig.provider}->createImage] Creating message for: "${message}"`); const params: any = { model: AIConfig.ImageConfig.model, prompt: message, quality: 'standard', n: 1, size: "1024x1024", } const openAICLient = new OpenAI({ baseURL: AIConfig.ImageConfig.baseURL, apiKey: AIConfig.ImageConfig.apiKey, }); const response = await openAICLient.images.generate(params); return response.data; } /** * Generates speech audio from provided text by utilizing OpenAI's Text-to-Speech (TTS) API. * This function translates text into spoken words in an audio format. It offers a way to convert written messages into audio, providing an audible version of the text content. * If a specific voice model is specified in the configuration, the generated speech will use that voice. * * Parameters: * - message: A string containing the text to be converted into speech. This text serves as the input for the TTS engine. * * Returns: * - A promise that resolves to a buffer containing the audio data in MP3 format. This buffer can be played back or sent as an audio message. */ async speech(message, responseFormat?) { logger.debug(`[${AIConfig.SpeechConfig.provider}->speech] Creating speech audio for: "${message}"`); const openAICLient = new OpenAI({ baseURL: AIConfig.SpeechConfig.baseURL, apiKey: AIConfig.SpeechConfig.apiKey, }); const response: any = await openAICLient.audio.speech.create({ model: AIConfig.SpeechConfig.model, voice: AIConfig.SpeechConfig.voice, input: message, response_format: responseFormat || 'mp3' }); logger.debug(`[${AIConfig.SpeechConfig.provider}->speech] Audio Creation OK`); return Buffer.from(await response.arrayBuffer()); } /** * Transcribes audio content into text using OpenAI's transcription capabilities. * This function takes an audio file and sends a request to OpenAI's API to generate a textual representation of the spoken words. * It leverages the Whisper model for high-quality transcription, converting audio inputs into readable text output. * * Parameters: * - message: A string indicating the audio file path or description for logging purposes. Currently, it is not used in the function's implementation but can be helpful for future extensions or logging clarity. * * Returns: * - A promise that resolves to a string containing the transcribed text. This string is the result of processing the provided audio through OpenAI's transcription model. * * Throws: * - Any errors encountered during the process of reading the audio file or interacting with OpenAI's API will be thrown and should be handled by the caller function. */ async transcription(stream: any) { logger.debug(`[${AIConfig.TranscriptionConfig.provider}->transcription] Creating transcription text for audio"`); const openAIClient = new OpenAI({ baseURL: AIConfig.TranscriptionConfig.baseURL, apiKey: AIConfig.TranscriptionConfig.apiKey, }); try { // Convertir ReadStream a File o Blob const file = await toFile(stream, 'audio.ogg', {type: 'audio/ogg'}); // Enviar el archivo convertido a la API de transcripción const response = await openAIClient.audio.transcriptions.create({ file: file, model: AIConfig.TranscriptionConfig.model, language: AIConfig.TranscriptionConfig.language }); return response.text; } catch (e: any) { logger.error(e.message); throw e; } } }