'use strict'; require('dotenv/config'); var OpenAI = require('openai'); var fs$1 = require('node:fs'); var tsRetryPromise = require('ts-retry-promise'); var ffmpeg = require('fluent-ffmpeg'); var ffmpegPath = require('ffmpeg-static'); var path = require('path'); var fs = require('fs'); var node_events = require('node:events'); // Establece el path de ffmpeg ffmpeg.setFfmpegPath(ffmpegPath); // Función para convertir archivo .opus a .mp3 usando el mismo nombre base function convertOpusToMp3(inputPath) { return new Promise((resolve, reject) => { const outputName = path.basename(inputPath, path.extname(inputPath)) + '.mp3'; const outputPath = path.join(path.dirname(inputPath), outputName); ffmpeg(inputPath) .toFormat('mp3') .audioBitrate(128) .on('end', () => { resolve(outputPath); // Retorna la ruta del archivo convertido }) .on('error', (err) => { reject(err); }) .save(outputPath); }); } async function sendToOpenaiWhisper(path) { try { const client = new OpenAI.OpenAI(); const transcription = await client.audio.transcriptions.create({ model: "whisper-1", prompt: "Indica exactamente lo que dice el usuario", // Optional file: await OpenAI.toFile(fs.createReadStream(path)), response_format: "json", temperature: 0.0, // Optional }); return transcription.text; } catch (error) { throw error; } } const parser = (message) => message .replace(/\[.*?\]\((https?:\/\/[^\s)]+)\)/, (_, p1) => p1) .replace(/\[.*?\]\((.*?)\)/g, (_, p1) => p1) .replace(/\[(https?:\/\/.*?)\]\(\s*https?:\/\/.*?\s*\)|\[(.*?)\]\((https?:\/\/.*?)\)|\((https?:\/\/.*?)\)/g, (_, p1) => p1) .replace(/【\d+:\d+†[^】]+】/g, '') .replace(/\*\*(.*?)\*\*/g, '*$1*') .replace(/!\s*(https?:\/\/[^\s]+)/g, (_, p1) => p1); async function sleep(ms) { await new Promise(resolve => setTimeout(resolve, ms)); } const create_poll_run = async (assistant, run, thread_id, args) => { const delay = 1500; while (true) { // TIP: VERIFY STATUS await sleep(delay); let r = await assistant.beta.threads.runs.retrieve(thread_id, run.id); assistant.events.emit("runStatus", r); if (r.status === 'completed') { const messages = await assistant.beta.threads.messages.list(r.thread_id); const message = messages.data.reverse()?.at(-1); if (message.role === "assistant") { // @ts-ignore let output = parser(message?.content?.[0]?.text?.value); assistant.events.emit("runCompleted", { run: r, output: output }); return { output, usage: r.usage }; } } if (r.status === "requires_action") { const tool_calls = r.required_action.submit_tool_outputs.tool_calls; let tool_outputs = []; assistant.events.emit("runRequiresAction", r.required_action.submit_tool_outputs); if (!assistant.functions.length) { console.info("[TOOL CALLS]: No functions call set yet!"); } for (const tool of tool_calls) { const fn_name = tool.function.name; const fn_args = JSON.parse(tool.function.arguments); let tool_output = { tool_call_id: tool.id, output: 'function not found' }; try { const fn = assistant.functions.find(f => f.name === fn_name); if (!fn) throw new Error(`${fn_name} tool not set`); const tool_output = await fn(fn_args); tool_outputs.push({ tool_call_id: tool.id, output: JSON.stringify(tool_output) || 'An error occurred while processing the tool call' }); } catch (error) { tool_output.tool_call_id = tool.id; tool_output.output = error?.message || 'An error occurred while processing the tool call'; tool_outputs.push(tool_output); } } const run_tool_outputs = await assistant.beta.threads.runs.submitToolOutputsAndPoll(r.thread_id, r.id, { tool_outputs }); return await create_poll_run(assistant, run_tool_outputs, thread_id); } if (["cancelling", "cancelled", "failed", "incomplete", "expired"].includes(r.status)) { assistant.events.emit("runFailed", r); break; } } }; class TypedEventEmitter extends node_events.EventEmitter { // @ts-ignore emit(event, args) { return super.emit(event, args); } // @ts-ignore on(event, listener) { return super.on(event, listener); } // @ts-ignore once(event, listener) { return super.once(event, listener); } // @ts-ignore off(event, listener) { return super.off(event, listener); } } class Assistant extends OpenAI { constructor(args, functions) { super({ apiKey: args?.apiKey || process.env.OPENAI_API_KEY, ...args }); this.asst = this.beta.assistants; this.threads = this.beta.threads; this.messages = this.beta.threads.messages; this.functions = functions || []; this.events = new TypedEventEmitter(); } async create_assistant(args) { return await this.asst.create(args); } async update_assistant(assistant_id, args) { return await this.asst.update(assistant_id, args); } async create_vector_store(args) { return await this.beta.vectorStores.create(args); } async upload_file_to_vector_store(vector_store_id, path_files) { const stream = await Promise.all(path_files.map((path) => OpenAI.toFile(fs$1.createReadStream(path)))); return await this.beta.vectorStores.fileBatches.uploadAndPoll(vector_store_id, { files: stream }); } async upload_file_to_vision(path_file) { return await this.files.create({ file: await OpenAI.toFile(fs$1.createReadStream(path_file)), purpose: "vision", }); } async transcript(path_file) { const path = await convertOpusToMp3(path_file); return await sendToOpenaiWhisper(path); } async delete_file(file_id) { return await this.files.del(file_id); } async delete_file_into_vector_store(vector_store_id, file_id) { await this.beta.vectorStores.files.del(vector_store_id, file_id); return await this.delete_file(file_id); } async list() { return await this.asst.list(); } async create_thread() { return await this.threads.create(); } async create_message(thread_id, message) { try { const createdMessage = await this.messages.create(thread_id, { role: "user", content: message, }); this.events.emit("messageCreated", createdMessage); // Emitir evento return createdMessage; } catch (error) { this.events.emit("error", error); // Emitir error throw error; } } async get_runs(thread_id, run_id) { try { return await this.threads.runs.retrieve(thread_id, run_id); } catch (error) { throw error; } } async _invoke(args) { let run; try { const { assistant_id, signal, tool_choice, message, truncation_strategy, metadata, thread_id } = args; const assistant = await this.asst.retrieve(assistant_id || process.env.ASSISTANT_ID); if (message) { await this.create_message(thread_id, message); } run = await this.threads.runs.create(thread_id, { assistant_id: assistant.id, metadata, truncation_strategy, tool_choice, ...args?.extra, }); this.events.emit("runCreated", run); signal.addEventListener("abort", async (ev) => { try { await this.threads.runs.cancel(args?.thread_id, run.id); this.events.emit("canceled", { ev, run }); } catch (_) { } }, { once: true }); return await create_poll_run(this, run, thread_id); } catch (error) { this.events.emit("error", error); throw error; } } async invoke(args, retry_config) { return tsRetryPromise.retry(async () => { if (!args?.signal) { const controller = new AbortController(); args.signal = controller.signal; } return await this._invoke(args); }, retry_config); } } exports.Assistant = Assistant;