@pipedream/openai 0.7.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/actions/analyze-image-content/analyze-image-content.mjs +1 -1
  2. package/actions/cancel-run/cancel-run.mjs +1 -1
  3. package/actions/chat/chat.mjs +6 -1
  4. package/actions/chat-using-file-search/chat-using-file-search.mjs +229 -0
  5. package/actions/chat-using-functions/chat-using-functions.mjs +255 -0
  6. package/actions/chat-using-web-search/chat-using-web-search.mjs +200 -0
  7. package/actions/chat-with-assistant/chat-with-assistant.mjs +6 -1
  8. package/actions/classify-items-into-categories/classify-items-into-categories.mjs +1 -1
  9. package/actions/convert-text-to-speech/convert-text-to-speech.mjs +1 -1
  10. package/actions/create-assistant/create-assistant.mjs +1 -1
  11. package/actions/create-batch/create-batch.mjs +1 -1
  12. package/actions/create-embeddings/create-embeddings.mjs +1 -1
  13. package/actions/create-fine-tuning-job/create-fine-tuning-job.mjs +1 -1
  14. package/actions/create-image/create-image.mjs +1 -1
  15. package/actions/create-moderation/create-moderation.mjs +1 -1
  16. package/actions/create-thread/create-thread.mjs +1 -1
  17. package/actions/create-vector-store/create-vector-store.mjs +1 -1
  18. package/actions/create-vector-store-file/create-vector-store-file.mjs +1 -1
  19. package/actions/delete-file/delete-file.mjs +1 -1
  20. package/actions/delete-vector-store/delete-vector-store.mjs +1 -1
  21. package/actions/delete-vector-store-file/delete-vector-store-file.mjs +1 -1
  22. package/actions/list-files/list-files.mjs +1 -1
  23. package/actions/list-messages/list-messages.mjs +1 -1
  24. package/actions/list-run-steps/list-run-steps.mjs +1 -1
  25. package/actions/list-runs/list-runs.mjs +1 -1
  26. package/actions/list-vector-store-files/list-vector-store-files.mjs +1 -1
  27. package/actions/list-vector-stores/list-vector-stores.mjs +1 -1
  28. package/actions/modify-assistant/modify-assistant.mjs +1 -1
  29. package/actions/retrieve-file/retrieve-file.mjs +1 -1
  30. package/actions/retrieve-file-content/retrieve-file-content.mjs +1 -1
  31. package/actions/retrieve-run/retrieve-run.mjs +1 -1
  32. package/actions/retrieve-run-step/retrieve-run-step.mjs +1 -1
  33. package/actions/retrieve-vector-store/retrieve-vector-store.mjs +1 -1
  34. package/actions/retrieve-vector-store-file/retrieve-vector-store-file.mjs +1 -1
  35. package/actions/send-prompt/send-prompt.mjs +1 -1
  36. package/actions/submit-tool-outputs-to-run/submit-tool-outputs-to-run.mjs +1 -1
  37. package/actions/summarize/summarize.mjs +1 -1
  38. package/actions/translate-text/translate-text.mjs +1 -1
  39. package/actions/upload-file/upload-file.mjs +1 -1
  40. package/openai.app.mjs +10 -0
  41. package/package.json +1 -2
  42. package/sources/new-batch-completed/new-batch-completed.mjs +1 -1
  43. package/sources/new-file-created/new-file-created.mjs +1 -1
  44. package/sources/new-fine-tuning-job-created/new-fine-tuning-job-created.mjs +1 -1
  45. package/sources/new-run-state-changed/new-run-state-changed.mjs +1 -1
  46. package/actions/create-transcription/create-transcription.mjs +0 -264
@@ -1,264 +0,0 @@
1
- import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
2
- import { ConfigurationError } from "@pipedream/platform";
3
- import axios from "axios";
4
- import Bottleneck from "bottleneck";
5
- import { exec } from "child_process";
6
- import FormData from "form-data";
7
- import fs from "fs";
8
- import {
9
- extname,
10
- join,
11
- } from "path";
12
- import stream from "stream";
13
- import { promisify } from "util";
14
- import openai from "../../openai.app.mjs";
15
- import common from "../common/common.mjs";
16
- import constants from "../../common/constants.mjs";
17
- import lang from "../common/lang.mjs";
18
-
19
- const COMMON_AUDIO_FORMATS_TEXT = "Your audio file must be in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm.";
20
- const CHUNK_SIZE_MB = 20;
21
-
22
- const execAsync = promisify(exec);
23
- const pipelineAsync = promisify(stream.pipeline);
24
-
25
- export default {
26
- name: "Create Transcription (Whisper)",
27
- version: "0.1.16",
28
- key: "openai-create-transcription",
29
- description: "Transcribes audio into the input language. [See the documentation](https://platform.openai.com/docs/api-reference/audio/create).",
30
- type: "action",
31
- props: {
32
- openai,
33
- uploadType: {
34
- label: "Audio Upload Type",
35
- description: "Are you uploading an audio file from [your workflow's `/tmp` directory](https://pipedream.com/docs/code/nodejs/working-with-files/#the-tmp-directory), or providing a URL to the file?",
36
- type: "string",
37
- options: [
38
- "File",
39
- "URL",
40
- ],
41
- reloadProps: true,
42
- },
43
- language: {
44
- label: "Language",
45
- description: "**Optional**. The language of the input audio. Supplying the input language will improve accuracy and latency.",
46
- type: "string",
47
- optional: true,
48
- options: lang.LANGUAGES.map((l) => ({
49
- label: l.label,
50
- value: l.value,
51
- })),
52
- },
53
- },
54
- async additionalProps() {
55
- const props = {};
56
- switch (this.uploadType) {
57
- case "File":
58
- props.path = {
59
- type: "string",
60
- label: "File Path",
61
- description: `A path to your audio file to transcribe, e.g. \`/tmp/audio.mp3\`. ${COMMON_AUDIO_FORMATS_TEXT} Add the appropriate extension (mp3, mp4, etc.) on your filename — OpenAI uses the extension to determine the file type. [See the Pipedream docs on saving files to \`/tmp\`](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp)`,
62
- };
63
- break;
64
- case "URL":
65
- props.url = {
66
- type: "string",
67
- label: "URL",
68
- description: `A public URL to the audio file to transcribe. This URL must point directly to the audio file, not a webpage that links to the audio file. ${COMMON_AUDIO_FORMATS_TEXT}`,
69
- };
70
- break;
71
- default:
72
- throw new ConfigurationError("Invalid upload type specified. Please provide 'File' or 'URL'.");
73
- }
74
- // Because we need to display the file or URL above, and not below, these optional props
75
- // TODO: Will be fixed when we render optional props correctly when used with additionalProps
76
- props.prompt = {
77
- label: "Prompt",
78
- description: "**Optional** text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting) should match the audio language.",
79
- type: "string",
80
- optional: true,
81
- };
82
- props.responseFormat = {
83
- label: "Response Format",
84
- description: "**Optional**. The format of the response. The default is `json`.",
85
- type: "string",
86
- default: "json",
87
- optional: true,
88
- options: constants.TRANSCRIPTION_FORMATS,
89
- };
90
- props.temperature = common.props.temperature;
91
-
92
- return props;
93
- },
94
- methods: {
95
- createForm({
96
- file, outputDir,
97
- }) {
98
- const form = new FormData();
99
- form.append("model", "whisper-1");
100
- if (this.prompt) form.append("prompt", this.prompt);
101
- if (this.temperature) form.append("temperature", this.temperature);
102
- if (this.language) form.append("language", this.language);
103
- if (this.responseFormat) form.append("response_format", this.responseFormat);
104
- const readStream = fs.createReadStream(join(outputDir, file));
105
- form.append("file", readStream);
106
- return form;
107
- },
108
- async splitLargeChunks(files, outputDir) {
109
- for (const file of files) {
110
- if (fs.statSync(`${outputDir}/${file}`).size / (1024 * 1024) > CHUNK_SIZE_MB) {
111
- await this.chunkFile({
112
- file: `${outputDir}/${file}`,
113
- outputDir,
114
- index: file.slice(6, 9),
115
- });
116
- await execAsync(`rm -f "${outputDir}/${file}"`);
117
- }
118
- }
119
- },
120
- async chunkFileAndTranscribe({
121
- file, $,
122
- }) {
123
- const outputDir = join("/tmp", "chunks");
124
- await execAsync(`mkdir -p "${outputDir}"`);
125
- await execAsync(`rm -f "${outputDir}/*"`);
126
-
127
- await this.chunkFile({
128
- file,
129
- outputDir,
130
- });
131
-
132
- let files = await fs.promises.readdir(outputDir);
133
- // ffmpeg will sometimes return chunks larger than the allowed size,
134
- // so we need to identify large chunks and break them down further
135
- await this.splitLargeChunks(files, outputDir);
136
- files = await fs.promises.readdir(outputDir);
137
-
138
- return this.transcribeFiles({
139
- files,
140
- outputDir,
141
- $,
142
- });
143
- },
144
- async chunkFile({
145
- file, outputDir, index,
146
- }) {
147
- const ffmpegPath = ffmpegInstaller.path;
148
- const ext = extname(file);
149
-
150
- const fileSizeInMB = fs.statSync(file).size / (1024 * 1024);
151
- // We're limited to 26MB per request. Because of how ffmpeg splits files,
152
- // we need to be conservative in the number of chunks we create
153
- const conservativeChunkSizeMB = CHUNK_SIZE_MB;
154
- const numberOfChunks = !index
155
- ? Math.ceil(fileSizeInMB / conservativeChunkSizeMB)
156
- : 2;
157
-
158
- if (numberOfChunks === 1) {
159
- await execAsync(`cp "${file}" "${outputDir}/chunk-000${ext}"`);
160
- return;
161
- }
162
-
163
- const { stdout } = await execAsync(`${ffmpegPath} -i "${file}" 2>&1 | grep "Duration"`);
164
- const duration = stdout.match(/\d{2}:\d{2}:\d{2}\.\d{2}/s)[0];
165
- const [
166
- hours,
167
- minutes,
168
- seconds,
169
- ] = duration.split(":").map(parseFloat);
170
-
171
- const totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds;
172
- const segmentTime = Math.ceil(totalSeconds / numberOfChunks);
173
-
174
- const command = `${ffmpegPath} -i "${file}" -f segment -segment_time ${segmentTime} -c copy "${outputDir}/chunk-${index
175
- ? `${index}-`
176
- : ""}%03d${ext}"`;
177
- await execAsync(command);
178
- },
179
- transcribeFiles({
180
- files, outputDir, $,
181
- }) {
182
- const limiter = new Bottleneck({
183
- maxConcurrent: 1,
184
- minTime: 1000 / 59,
185
- });
186
-
187
- return Promise.all(files.map((file) => {
188
- return limiter.schedule(() => this.transcribe({
189
- file,
190
- outputDir,
191
- $,
192
- }));
193
- }));
194
- },
195
- transcribe({
196
- file, outputDir, $,
197
- }) {
198
- const form = this.createForm({
199
- file,
200
- outputDir,
201
- });
202
- return this.openai.createTranscription({
203
- $,
204
- form,
205
- });
206
- },
207
- getFullText(transcriptions = []) {
208
- return transcriptions.map((t) => t.text || t).join(" ");
209
- },
210
- },
211
- async run({ $ }) {
212
- const {
213
- url,
214
- path,
215
- } = this;
216
-
217
- if (!url && !path) {
218
- throw new ConfigurationError("Must specify either File URL or File Path");
219
- }
220
-
221
- let file;
222
-
223
- if (path) {
224
- if (!fs.existsSync(path)) {
225
- throw new ConfigurationError(`${path} does not exist`);
226
- }
227
-
228
- file = path;
229
- } else if (url) {
230
- const ext = extname(url).split("?")[0];
231
-
232
- const response = await axios({
233
- method: "GET",
234
- url,
235
- responseType: "stream",
236
- timeout: 250000,
237
- });
238
-
239
- const bufferStream = new stream.PassThrough();
240
- response.data.pipe(bufferStream);
241
-
242
- const downloadPath = join("/tmp", `audio${ext}`);
243
- const writeStream = fs.createWriteStream(downloadPath);
244
-
245
- await pipelineAsync(bufferStream, writeStream);
246
-
247
- file = downloadPath;
248
- }
249
-
250
- const transcriptions = await this.chunkFileAndTranscribe({
251
- file,
252
- $,
253
- });
254
-
255
- if (transcriptions.length) {
256
- $.export("$summary", "Successfully created transcription");
257
- }
258
-
259
- return {
260
- transcription: this.getFullText(transcriptions),
261
- transcriptions,
262
- };
263
- },
264
- };