@pipedream/openai 0.1.3 → 0.1.5

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.
@@ -4,7 +4,7 @@ import common from "../common/common.mjs";
4
4
  export default {
5
5
  ...common,
6
6
  name: "Chat",
7
- version: "0.1.1",
7
+ version: "0.1.2",
8
8
  key: "openai-chat",
9
9
  description: "The Chat API, using the `gpt-3.5-turbo` or `gpt-4` model. [See docs here](https://platform.openai.com/docs/api-reference/chat)",
10
10
  type: "action",
@@ -3,7 +3,7 @@ import common from "../common/common-helper.mjs";
3
3
  export default {
4
4
  ...common,
5
5
  name: "Classify Items into Categories",
6
- version: "0.0.3",
6
+ version: "0.0.4",
7
7
  key: "openai-classify-items-into-categories",
8
8
  description: "Classify items into specific categories using the Chat API",
9
9
  type: "action",
@@ -4,7 +4,7 @@ import { ConfigurationError } from "@pipedream/platform";
4
4
 
5
5
  export default {
6
6
  name: "Create Embeddings",
7
- version: "0.0.1",
7
+ version: "0.0.2",
8
8
  key: "openai-create-embeddings",
9
9
  description: "Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. [See the docs here](https://platform.openai.com/docs/api-reference/embeddings)",
10
10
  type: "action",
@@ -3,7 +3,7 @@ import constants from "../common/constants.mjs";
3
3
 
4
4
  export default {
5
5
  name: "Create Image",
6
- version: "0.1.1",
6
+ version: "0.1.2",
7
7
  key: "openai-create-image",
8
8
  description: "Creates an image given a prompt. returns a URL to the image. [See docs here](https://platform.openai.com/docs/api-reference/images)",
9
9
  type: "action",
@@ -1,18 +1,27 @@
1
+ import axios from "axios";
1
2
  import fs from "fs";
2
- import got from "got";
3
- import { extname } from "path";
3
+ import {
4
+ join, extname,
5
+ } from "path";
4
6
  import FormData from "form-data";
5
7
  import { ConfigurationError } from "@pipedream/platform";
6
8
  import common from "../common/common.mjs";
7
9
  import constants from "../common/constants.mjs";
8
10
  import lang from "../common/lang.mjs";
9
11
  import openai from "../../app/openai.app.mjs";
12
+ import { promisify } from "util";
13
+ import stream from "stream";
14
+ import { exec } from "child_process";
15
+ import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
10
16
 
11
17
  const COMMON_AUDIO_FORMATS_TEXT = "Your audio file must be in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm.";
12
18
 
19
+ const execAsync = promisify(exec);
20
+ const pipelineAsync = promisify(stream.pipeline);
21
+
13
22
  export default {
14
23
  name: "Create Transcription",
15
- version: "0.0.1",
24
+ version: "0.0.3",
16
25
  key: "openai-create-transcription",
17
26
  description: "Transcribes audio into the input language. [See docs here](https://platform.openai.com/docs/api-reference/audio/create).",
18
27
  type: "action",
@@ -80,6 +89,95 @@ export default {
80
89
 
81
90
  return props;
82
91
  },
92
+ methods: {
93
+ createForm({
94
+ file, outputDir,
95
+ }) {
96
+ const form = new FormData();
97
+ form.append("model", "whisper-1");
98
+ if (this.prompt) form.append("prompt", this.prompt);
99
+ if (this.temperature) form.append("temperature", this.temperature);
100
+ if (this.language) form.append("language", this.language);
101
+ if (this.responseFormat) form.append("response_format", this.responseFormat);
102
+ const readStream = fs.createReadStream(join(outputDir, file));
103
+ form.append("file", readStream);
104
+ return form;
105
+ },
106
+ async chunkFileAndTranscribe({
107
+ file, $,
108
+ }) {
109
+ const outputDir = join("/tmp", "chunks");
110
+ await execAsync(`mkdir -p ${outputDir}`);
111
+ await execAsync(`rm -f ${outputDir}/*`);
112
+
113
+ await this.chunkFile({
114
+ file,
115
+ outputDir,
116
+ });
117
+
118
+ const files = await fs.promises.readdir(outputDir);
119
+ const transcription = await this.transcribeFiles({
120
+ files,
121
+ outputDir,
122
+ $,
123
+ });
124
+
125
+ return {
126
+ transcription,
127
+ };
128
+ },
129
+ async chunkFile({
130
+ file, outputDir,
131
+ }) {
132
+ const ffmpegPath = ffmpegInstaller.path;
133
+ const ext = extname(file);
134
+
135
+ const fileSizeInMB = fs.statSync(file).size / (1024 * 1024);
136
+ const numberOfChunks = Math.ceil(fileSizeInMB / 24);
137
+
138
+ if (numberOfChunks === 1) {
139
+ await execAsync(`cp ${file} ${outputDir}/chunk-000${ext}`);
140
+ return;
141
+ }
142
+
143
+ const { stdout } = await execAsync(`${ffmpegPath} -i ${file} 2>&1 | grep "Duration"`);
144
+ const duration = stdout.match(/\d{2}:\d{2}:\d{2}\.\d{2}/s)[0];
145
+ const [
146
+ hours,
147
+ minutes,
148
+ seconds,
149
+ ] = duration.split(":").map(parseFloat);
150
+
151
+ const totalSeconds = (hours * 60 * 60) + (minutes * 60) + seconds;
152
+ const segmentTime = Math.ceil(totalSeconds / numberOfChunks);
153
+
154
+ const command = `${ffmpegPath} -i ${file} -f segment -segment_time ${segmentTime} -c copy ${outputDir}/chunk-%03d${ext}`;
155
+ await execAsync(command);
156
+ },
157
+ async transcribeFiles({
158
+ files, outputDir, $,
159
+ }) {
160
+ const transcriptions = await Promise.all(files.map((file) => this.transcribe({
161
+ file,
162
+ outputDir,
163
+ $,
164
+ })));
165
+ return transcriptions.join(" ");
166
+ },
167
+ async transcribe({
168
+ file, outputDir, $,
169
+ }) {
170
+ const form = this.createForm({
171
+ file,
172
+ outputDir,
173
+ });
174
+ const response = await this.openai.createTranscription({
175
+ $,
176
+ form,
177
+ });
178
+ return response.text;
179
+ },
180
+ },
83
181
  async run({ $ }) {
84
182
  const {
85
183
  url,
@@ -90,38 +188,37 @@ export default {
90
188
  throw new Error("Must specify either File URL or File Path");
91
189
  }
92
190
 
93
- const form = new FormData();
94
- form.append("model", "whisper-1");
95
- if (this.prompt) form.append("prompt", this.prompt);
96
- if (this.temperature) form.append("temperature", this.temperature);
97
- if (this.language) form.append("language", this.language);
98
- if (this.responseFormat) form.append("response_format", this.responseFormat);
191
+ let file;
99
192
 
100
193
  if (path) {
101
194
  if (!fs.existsSync(path)) {
102
195
  throw new Error(`${path} does not exist`);
103
196
  }
104
- const readStream = fs.createReadStream(path);
105
- form.append("file", readStream);
197
+ file = path;
106
198
  } else if (url) {
107
199
  const ext = extname(url);
108
- // OpenAI only supports a few audio formats and uses the extension to determine the format
109
- const tempFilePath = `/tmp/audioFile${ext}`;
110
-
111
- const writeStream = fs.createWriteStream(tempFilePath);
112
- const responseStream = got.stream(url);
113
- responseStream.pipe(writeStream);
114
- await new Promise((resolve, reject) => {
115
- writeStream.on("finish", resolve);
116
- writeStream.on("error", reject);
117
- responseStream.on("error", reject);
200
+
201
+ const response = await axios({
202
+ method: "GET",
203
+ url,
204
+ responseType: "stream",
205
+ timeout: 250000,
118
206
  });
119
- const readStream = fs.createReadStream(tempFilePath);
120
- form.append("file", readStream);
207
+
208
+ const bufferStream = new stream.PassThrough();
209
+ response.data.pipe(bufferStream);
210
+
211
+ const downloadPath = join("/tmp", `audio${ext}`);
212
+ const writeStream = fs.createWriteStream(downloadPath);
213
+
214
+ await pipelineAsync(bufferStream, writeStream);
215
+
216
+ file = downloadPath;
121
217
  }
122
- const response = await this.openai.createTranscription({
218
+
219
+ const response = await this.chunkFileAndTranscribe({
220
+ file,
123
221
  $,
124
- form,
125
222
  });
126
223
 
127
224
  if (response) {
@@ -4,7 +4,7 @@ import common from "../common/common.mjs";
4
4
  export default {
5
5
  ...common,
6
6
  name: "Create Completion (Send Prompt)",
7
- version: "0.1.0",
7
+ version: "0.1.1",
8
8
  key: "openai-send-prompt",
9
9
  description: "OpenAI recommends using the **Chat** action for the latest `gpt-3.5-turbo` API, since it's faster and 10x cheaper. This action creates a completion for the provided prompt and parameters using the older `/completions` API. [See docs here](https://beta.openai.com/docs/api-reference/completions/create)",
10
10
  type: "action",
@@ -3,7 +3,7 @@ import common from "../common/common-helper.mjs";
3
3
  export default {
4
4
  ...common,
5
5
  name: "Summarize Text",
6
- version: "0.0.3",
6
+ version: "0.0.4",
7
7
  key: "openai-summarize",
8
8
  description: "Summarizes text using the Chat API",
9
9
  type: "action",
@@ -9,7 +9,7 @@ const langOptions = lang.LANGUAGES.map((l) => ({
9
9
  export default {
10
10
  ...common,
11
11
  name: "Translate Text",
12
- version: "0.0.3",
12
+ version: "0.0.4",
13
13
  key: "openai-translate-text",
14
14
  description: "Translate text from one language to another using the Chat API",
15
15
  type: "action",
@@ -56,6 +56,7 @@ export default {
56
56
  headers: {
57
57
  ...this._commonHeaders(),
58
58
  },
59
+ maxBodyLength: Infinity,
59
60
  ...args,
60
61
  });
61
62
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/openai",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Pipedream OpenAI Components",
5
5
  "main": "app/openai.app.mjs",
6
6
  "keywords": [
@@ -14,8 +14,10 @@
14
14
  "access": "public"
15
15
  },
16
16
  "dependencies": {
17
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
17
18
  "@pipedream/platform": "^1.2.1",
18
19
  "@pipedream/types": "^0.1.4",
20
+ "got": "^12.6.0",
19
21
  "openai": "^3.2.1"
20
22
  },
21
23
  "devDependencies": {