@aj-archipelago/cortex 1.0.3 → 1.0.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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/config/default.example.json +18 -0
  3. package/config.js +25 -5
  4. package/graphql/pathwayPrompter.js +8 -0
  5. package/graphql/pathwayResolver.js +9 -1
  6. package/graphql/plugins/azureTranslatePlugin.js +22 -0
  7. package/graphql/plugins/modelPlugin.js +15 -42
  8. package/graphql/plugins/openAiChatPlugin.js +85 -2
  9. package/graphql/plugins/openAiCompletionPlugin.js +32 -2
  10. package/graphql/plugins/openAiWhisperPlugin.js +49 -13
  11. package/graphql/plugins/palmChatPlugin.js +229 -0
  12. package/graphql/plugins/palmCompletionPlugin.js +134 -0
  13. package/graphql/prompt.js +11 -4
  14. package/helper_apps/MediaFileChunker/Dockerfile +20 -0
  15. package/helper_apps/MediaFileChunker/fileChunker.js +50 -6
  16. package/helper_apps/MediaFileChunker/helper.js +13 -1
  17. package/helper_apps/MediaFileChunker/index.js +2 -4
  18. package/helper_apps/MediaFileChunker/package-lock.json +73 -18
  19. package/helper_apps/MediaFileChunker/package.json +2 -1
  20. package/lib/gcpAuthTokenHelper.js +37 -0
  21. package/package.json +3 -1
  22. package/pathways/completions.js +17 -0
  23. package/pathways/index.js +4 -2
  24. package/pathways/{lc_test.mjs → test_langchain.mjs} +1 -1
  25. package/pathways/test_oai_chat.js +18 -0
  26. package/pathways/test_oai_cmpl.js +13 -0
  27. package/pathways/test_palm_chat.js +31 -0
  28. package/pathways/transcribe.js +1 -0
  29. package/pathways/translate.js +2 -1
  30. package/tests/chunking.test.js +8 -6
  31. package/tests/modelPlugin.test.js +2 -14
  32. package/tests/openAiChatPlugin.test.js +125 -0
  33. package/tests/palmChatPlugin.test.js +256 -0
  34. package/tests/palmCompletionPlugin.test.js +87 -0
@@ -0,0 +1,229 @@
1
+ // palmChatPlugin.js
2
+ import ModelPlugin from './modelPlugin.js';
3
+ import { encode } from 'gpt-3-encoder';
4
+ import HandleBars from '../../lib/handleBars.js';
5
+
6
+ class PalmChatPlugin extends ModelPlugin {
7
+ constructor(config, pathway) {
8
+ super(config, pathway);
9
+ }
10
+
11
+ // Convert to PaLM messages array format if necessary
12
+ convertMessagesToPalm(messages) {
13
+ let context = '';
14
+ let modifiedMessages = [];
15
+ let lastAuthor = '';
16
+
17
+ messages.forEach(message => {
18
+ const { role, author, content } = message;
19
+
20
+ // Extract system messages into the context string
21
+ if (role === 'system') {
22
+ context += (context.length > 0 ? '\n' : '') + content;
23
+ return;
24
+ }
25
+
26
+ // Aggregate consecutive author messages, appending the content
27
+ if ((role === lastAuthor || author === lastAuthor) && modifiedMessages.length > 0) {
28
+ modifiedMessages[modifiedMessages.length - 1].content += '\n' + content;
29
+ }
30
+ // Only push messages with role 'user' or 'assistant' or existing author messages
31
+ else if (role === 'user' || role === 'assistant' || author) {
32
+ modifiedMessages.push({
33
+ author: author || role,
34
+ content,
35
+ });
36
+ lastAuthor = author || role;
37
+ }
38
+ });
39
+
40
+ return {
41
+ modifiedMessages,
42
+ context,
43
+ };
44
+ }
45
+
46
+ // Handlebars compiler for context (PaLM chat specific)
47
+ getCompiledContext(text, parameters, context) {
48
+ const combinedParameters = { ...this.promptParameters, ...parameters };
49
+ return context ? HandleBars.compile(context)({ ...combinedParameters, text}) : '';
50
+ }
51
+
52
+ // Handlebars compiler for examples (PaLM chat specific)
53
+ getCompiledExamples(text, parameters, examples = []) {
54
+ const combinedParameters = { ...this.promptParameters, ...parameters };
55
+
56
+ const compileContent = (content) => {
57
+ const compile = HandleBars.compile(content);
58
+ return compile({ ...combinedParameters, text });
59
+ };
60
+
61
+ const processExample = (example, key) => {
62
+ if (example[key]?.content) {
63
+ return { ...example[key], content: compileContent(example[key].content) };
64
+ }
65
+ return { ...example[key] };
66
+ };
67
+
68
+ return examples.map((example) => ({
69
+ input: example.input ? processExample(example, 'input') : undefined,
70
+ output: example.output ? processExample(example, 'output') : undefined,
71
+ }));
72
+ }
73
+
74
+ // Set up parameters specific to the PaLM Chat API
75
+ getRequestParameters(text, parameters, prompt) {
76
+ const { modelPromptText, modelPromptMessages, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
77
+ const { stream } = parameters;
78
+
79
+ // Define the model's max token length
80
+ const modelTargetTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
81
+
82
+ const palmMessages = this.convertMessagesToPalm(modelPromptMessages || [{ "author": "user", "content": modelPromptText }]);
83
+
84
+ let requestMessages = palmMessages.modifiedMessages;
85
+
86
+ // Check if the token length exceeds the model's max token length
87
+ if (tokenLength > modelTargetTokenLength) {
88
+ // Remove older messages until the token length is within the model's limit
89
+ requestMessages = this.truncateMessagesToTargetLength(requestMessages, modelTargetTokenLength);
90
+ }
91
+
92
+ const context = this.getCompiledContext(text, parameters, prompt.context || palmMessages.context || '');
93
+ const examples = this.getCompiledExamples(text, parameters, prompt.examples || []);
94
+
95
+ // For PaLM right now, the max return tokens is 1024, regardless of the max context length
96
+ // I can't think of a time you'd want to constrain it to fewer at the moment.
97
+ const max_tokens = 1024//this.getModelMaxTokenLength() - tokenLength;
98
+
99
+ if (max_tokens < 0) {
100
+ throw new Error(`Prompt is too long to successfully call the model at ${tokenLength} tokens. The model will not be called.`);
101
+ }
102
+
103
+ // Ensure there are an even number of messages (PaLM requires an even number of messages)
104
+ if (requestMessages.length % 2 === 0) {
105
+ requestMessages = requestMessages.slice(1);
106
+ }
107
+
108
+ const requestParameters = {
109
+ instances: [{
110
+ context: context,
111
+ examples: examples,
112
+ messages: requestMessages,
113
+ }],
114
+ parameters: {
115
+ temperature: this.temperature ?? 0.7,
116
+ maxOutputTokens: max_tokens,
117
+ topP: parameters.topP ?? 0.95,
118
+ topK: parameters.topK ?? 40,
119
+ }
120
+ };
121
+
122
+ return requestParameters;
123
+ }
124
+
125
+ // Get the safetyAttributes from the PaLM Chat API response data
126
+ getSafetyAttributes(data) {
127
+ const { predictions } = data;
128
+ if (!predictions || !predictions.length) {
129
+ return null;
130
+ }
131
+
132
+ // if we got a predictions array back with more than one prediction, return the safetyAttributes of the first prediction
133
+ if (predictions.length > 1) {
134
+ return predictions[0].safetyAttributes ?? null;
135
+ }
136
+
137
+ // otherwise, return the safetyAttributes of the content of the first prediction
138
+ return predictions[0].safetyAttributes ?? null;
139
+ }
140
+
141
+ // Execute the request to the PaLM Chat API
142
+ async execute(text, parameters, prompt) {
143
+ const url = this.requestUrl(text);
144
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
145
+
146
+ const data = { ...(this.model.params || {}), ...requestParameters };
147
+ const params = {};
148
+ const headers = this.model.headers || {};
149
+ const gcpAuthTokenHelper = this.config.get('gcpAuthTokenHelper');
150
+ const authToken = await gcpAuthTokenHelper.getAccessToken();
151
+ headers.Authorization = `Bearer ${authToken}`;
152
+ return this.executeRequest(url, data, params, headers, prompt);
153
+ }
154
+
155
+ // Parse the response from the PaLM Chat API
156
+ parseResponse(data) {
157
+ const { predictions } = data;
158
+ if (!predictions || !predictions.length) {
159
+ return null;
160
+ }
161
+
162
+ // Get the candidates array from the first prediction
163
+ const { candidates } = predictions[0];
164
+
165
+ // if it was blocked, return the blocked message
166
+ if (predictions[0].safetyAttributes?.blocked) {
167
+ return 'The response is blocked because the input or response potentially violates Google policies. Try rephrasing the prompt or adjusting the parameter settings. Currently, only English is supported.';
168
+ }
169
+
170
+ if (!candidates || !candidates.length) {
171
+ return null;
172
+ }
173
+
174
+ // If we got a candidates array back with more than one candidate, return the whole array
175
+ if (candidates.length > 1) {
176
+ return candidates;
177
+ }
178
+
179
+ // Otherwise, return the content of the first candidate
180
+ const messageResult = candidates[0].content && candidates[0].content.trim();
181
+ return messageResult ?? null;
182
+ }
183
+
184
+ // Override the logging function to display the messages and responses
185
+ logRequestData(data, responseData, prompt) {
186
+ const separator = `\n=== ${this.pathwayName}.${this.requestCount++} ===\n`;
187
+ console.log(separator);
188
+
189
+ const instances = data && data.instances;
190
+ const messages = instances && instances[0] && instances[0].messages;
191
+ const { context, examples } = instances && instances [0] || {};
192
+
193
+ if (context) {
194
+ console.log(`\x1b[36mContext: ${context}\x1b[0m`);
195
+ }
196
+
197
+ if (examples && examples.length) {
198
+ examples.forEach((example, index) => {
199
+ console.log(`\x1b[36mExample ${index + 1}: Input: "${example.input.content}", Output: "${example.output.content}"\x1b[0m`);
200
+ });
201
+ }
202
+
203
+ if (messages && messages.length > 1) {
204
+ messages.forEach((message, index) => {
205
+ const words = message.content.split(" ");
206
+ const tokenCount = encode(message.content).length;
207
+ const preview = words.length < 41 ? message.content : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" ");
208
+
209
+ console.log(`\x1b[36mMessage ${index + 1}: Author: ${message.author}, Tokens: ${tokenCount}, Content: "${preview}"\x1b[0m`);
210
+ });
211
+ } else if (messages && messages.length === 1) {
212
+ console.log(`\x1b[36m${messages[0].content}\x1b[0m`);
213
+ }
214
+
215
+ const safetyAttributes = this.getSafetyAttributes(responseData);
216
+
217
+ console.log(`\x1b[34m> ${this.parseResponse(responseData)}\x1b[0m`);
218
+
219
+ if (safetyAttributes) {
220
+ console.log(`\x1b[33mSafety Attributes: ${JSON.stringify(safetyAttributes, null, 2)}\x1b[0m`);
221
+ }
222
+
223
+ if (prompt && prompt.debugInfo) {
224
+ prompt.debugInfo += `${separator}${JSON.stringify(data)}`;
225
+ }
226
+ }
227
+ }
228
+
229
+ export default PalmChatPlugin;
@@ -0,0 +1,134 @@
1
+ // palmCompletionPlugin.js
2
+
3
+ import ModelPlugin from './modelPlugin.js';
4
+
5
+ // Helper function to truncate the prompt if it is too long
6
+ const truncatePromptIfNecessary = (text, textTokenCount, modelMaxTokenCount, targetTextTokenCount, pathwayResolver) => {
7
+ const maxAllowedTokens = textTokenCount + ((modelMaxTokenCount - targetTextTokenCount) * 0.5);
8
+
9
+ if (textTokenCount > maxAllowedTokens) {
10
+ pathwayResolver.logWarning(`Prompt is too long at ${textTokenCount} tokens (this target token length for this pathway is ${targetTextTokenCount} tokens because the response is expected to take up the rest of the model's max tokens (${modelMaxTokenCount}). Prompt will be truncated.`);
11
+ return pathwayResolver.truncate(text, maxAllowedTokens);
12
+ }
13
+ return text;
14
+ }
15
+
16
+ // PalmCompletionPlugin class for handling requests and responses to the PaLM API Text Completion API
17
+ class PalmCompletionPlugin extends ModelPlugin {
18
+ constructor(config, pathway) {
19
+ super(config, pathway);
20
+ }
21
+
22
+ // Set up parameters specific to the PaLM API Text Completion API
23
+ getRequestParameters(text, parameters, prompt, pathwayResolver) {
24
+ const { modelPromptText, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
25
+ const { stream } = parameters;
26
+ // Define the model's max token length
27
+ const modelTargetTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
28
+
29
+ const truncatedPrompt = truncatePromptIfNecessary(modelPromptText, tokenLength, this.getModelMaxTokenLength(), modelTargetTokenLength, pathwayResolver);
30
+
31
+ const max_tokens = 1024//this.getModelMaxTokenLength() - tokenLength;
32
+
33
+ if (max_tokens < 0) {
34
+ throw new Error(`Prompt is too long to successfully call the model at ${tokenLength} tokens. The model will not be called.`);
35
+ }
36
+
37
+ if (!truncatedPrompt) {
38
+ throw new Error(`Prompt is empty. The model will not be called.`);
39
+ }
40
+
41
+ const requestParameters = {
42
+ instances: [
43
+ { prompt: truncatedPrompt }
44
+ ],
45
+ parameters: {
46
+ temperature: this.temperature ?? 0.7,
47
+ maxOutputTokens: max_tokens,
48
+ topP: parameters.topP ?? 0.95,
49
+ topK: parameters.topK ?? 40,
50
+ }
51
+ };
52
+
53
+ return requestParameters;
54
+ }
55
+
56
+ // Execute the request to the PaLM API Text Completion API
57
+ async execute(text, parameters, prompt, pathwayResolver) {
58
+ const url = this.requestUrl(text);
59
+ const requestParameters = this.getRequestParameters(text, parameters, prompt, pathwayResolver);
60
+
61
+ const data = { ...requestParameters };
62
+ const params = {};
63
+ const headers = this.model.headers || {};
64
+ const gcpAuthTokenHelper = this.config.get('gcpAuthTokenHelper');
65
+ const authToken = await gcpAuthTokenHelper.getAccessToken();
66
+ headers.Authorization = `Bearer ${authToken}`;
67
+ return this.executeRequest(url, data, params, headers, prompt);
68
+ }
69
+
70
+ // Parse the response from the PaLM API Text Completion API
71
+ parseResponse(data) {
72
+ const { predictions } = data;
73
+ if (!predictions || !predictions.length) {
74
+ return data;
75
+ }
76
+
77
+ // if we got a predictions array back with more than one prediction, return the whole array
78
+ if (predictions.length > 1) {
79
+ return predictions;
80
+ }
81
+
82
+ // otherwise, return the content of the first prediction
83
+ // if it was blocked, return the blocked message
84
+ if (predictions[0].safetyAttributes?.blocked) {
85
+ return 'The response is blocked because the input or response potentially violates Google policies. Try rephrasing the prompt or adjusting the parameter settings. Currently, only English is supported.';
86
+ }
87
+
88
+ const contentResult = predictions[0].content && predictions[0].content.trim();
89
+ return contentResult ?? null;
90
+ }
91
+
92
+ // Get the safetyAttributes from the PaLM API Text Completion API response data
93
+ getSafetyAttributes(data) {
94
+ const { predictions } = data;
95
+ if (!predictions || !predictions.length) {
96
+ return null;
97
+ }
98
+
99
+ // if we got a predictions array back with more than one prediction, return the safetyAttributes of the first prediction
100
+ if (predictions.length > 1) {
101
+ return predictions[0].safetyAttributes ?? null;
102
+ }
103
+
104
+ // otherwise, return the safetyAttributes of the content of the first prediction
105
+ return predictions[0].safetyAttributes ?? null;
106
+ }
107
+
108
+ // Override the logging function to log the prompt and response
109
+ logRequestData(data, responseData, prompt) {
110
+ const separator = `\n=== ${this.pathwayName}.${this.requestCount++} ===\n`;
111
+ console.log(separator);
112
+
113
+ const safetyAttributes = this.getSafetyAttributes(responseData);
114
+
115
+ const instances = data && data.instances;
116
+ const modelInput = instances && instances[0] && instances[0].prompt;
117
+
118
+ if (modelInput) {
119
+ console.log(`\x1b[36m${modelInput}\x1b[0m`);
120
+ }
121
+
122
+ console.log(`\x1b[34m> ${this.parseResponse(responseData)}\x1b[0m`);
123
+
124
+ if (safetyAttributes) {
125
+ console.log(`\x1b[33mSafety Attributes: ${JSON.stringify(safetyAttributes, null, 2)}\x1b[0m`);
126
+ }
127
+
128
+ if (prompt && prompt.debugInfo) {
129
+ prompt.debugInfo += `${separator}${JSON.stringify(data)}`;
130
+ }
131
+ }
132
+ }
133
+
134
+ export default PalmCompletionPlugin;
package/graphql/prompt.js CHANGED
@@ -3,15 +3,21 @@ class Prompt {
3
3
  if (typeof params === 'string' || params instanceof String) {
4
4
  this.prompt = params;
5
5
  } else {
6
- const { prompt, saveResultTo, messages } = params;
6
+ const { prompt, saveResultTo, messages, context, examples } = params;
7
7
  this.prompt = prompt;
8
8
  this.saveResultTo = saveResultTo;
9
9
  this.messages = messages;
10
+ this.context = context;
11
+ this.examples = examples;
10
12
  this.params = params;
11
13
  }
12
14
 
13
- this.usesTextInput = promptContains('text', this.prompt ? this.prompt : this.messages);
14
- this.usesPreviousResult = promptContains('previousResult', this.prompt ? this.prompt : this.messages);
15
+ this.usesTextInput = promptContains('text', this.prompt ? this.prompt : this.messages) ||
16
+ (this.context && promptContains('text', this.context)) ||
17
+ (this.examples && promptContains('text', this.examples));
18
+ this.usesPreviousResult = promptContains('previousResult', this.prompt ? this.prompt : this.messages) ||
19
+ (this.context && promptContains('previousResult', this.context)) ||
20
+ (this.examples && promptContains('previousResult', this.examples));
15
21
  this.debugInfo = '';
16
22
  }
17
23
  }
@@ -23,7 +29,8 @@ function promptContains(variable, prompt) {
23
29
  let matches = [];
24
30
  let match;
25
31
 
26
- // if it's an array, it's the messages format
32
+ // if it's an array, it's either an OpenAI messages array or a PaLM messages
33
+ // array or a PaLM examples array, all of which have a content property
27
34
  if (Array.isArray(prompt)) {
28
35
  prompt.forEach(p => {
29
36
  // eslint-disable-next-line no-cond-assign
@@ -0,0 +1,20 @@
1
+ FROM node:18-alpine
2
+
3
+ WORKDIR /usr/src/app
4
+
5
+ COPY package*.json ./
6
+
7
+ RUN npm install
8
+
9
+ ## following 3 lines are for installing ffmepg
10
+ RUN apk update
11
+ RUN apk add
12
+ RUN apk add ffmpeg
13
+
14
+ COPY . .
15
+
16
+ EXPOSE 7071
17
+
18
+ # RUN npm run build
19
+
20
+ CMD [ "node", "start.js" ]
@@ -7,6 +7,8 @@ import { v4 as uuidv4 } from 'uuid';
7
7
  import os from 'os';
8
8
  import ytdl from 'ytdl-core';
9
9
  import { promisify } from 'util';
10
+ import axios from 'axios';
11
+ import { ensureEncoded } from './helper.js';
10
12
 
11
13
  ffmpeg.setFfmpegPath(ffmpegPath);
12
14
  ffmpeg.setFfprobePath(ffprobePath);
@@ -15,12 +17,15 @@ console.log(`ffprobePath: ${ffprobePath}`);
15
17
 
16
18
  const ffmpegProbe = promisify(ffmpeg.ffprobe);
17
19
 
20
+
18
21
  async function processChunk(inputPath, outputFileName, start, duration) {
19
22
  return new Promise((resolve, reject) => {
20
23
  ffmpeg(inputPath)
21
24
  .seekInput(start)
22
25
  .duration(duration)
23
- .audioCodec('libmp3lame') // Ensure output is always in MP3 format
26
+ .format('mp3')
27
+ .audioCodec('libmp3lame')
28
+ .audioBitrate(128)
24
29
  .on('start', (cmd) => {
25
30
  console.log(`Started FFmpeg with command: ${cmd}`);
26
31
  })
@@ -43,18 +48,57 @@ const generateUniqueFolderName = () => {
43
48
  return uniqueOutputPath;
44
49
  }
45
50
 
51
+ async function downloadFile(url, outputPath) {
52
+ try {
53
+ // Make an HTTP request for the file
54
+ const response = await axios.get(url, { responseType: 'stream' });
55
+
56
+ // Create a writable file stream to save the file
57
+ const fileStream = fs.createWriteStream(outputPath);
58
+
59
+ // Pipe the response data into the file stream
60
+ response.data.pipe(fileStream);
61
+
62
+ // Wait for the file stream to finish writing
63
+ await new Promise((resolve, reject) => {
64
+ fileStream.on('finish', resolve);
65
+ fileStream.on('error', reject);
66
+ });
67
+
68
+ console.log(`Downloaded file saved to: ${outputPath}`);
69
+ } catch (error) {
70
+ console.error(`Error downloading file from ${url}:`, error);
71
+ throw error;
72
+ }
73
+ }
74
+
46
75
  async function splitMediaFile(inputPath, chunkDurationInSeconds = 600) {
47
76
  try {
77
+ // Create unique folder
78
+ const uniqueOutputPath = generateUniqueFolderName();
79
+ fs.mkdirSync(uniqueOutputPath, { recursive: true });
80
+
81
+ // Download the file if it's not a local file
82
+ const isUrl = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i.test(inputPath);
83
+ if (isUrl) {
84
+ inputPath = ensureEncoded(inputPath);
85
+ // Extract the original file name from the URL
86
+ const urlObj = new URL(inputPath);
87
+ const originalFileName = path.basename(urlObj.pathname);
88
+
89
+ // Use the original file name when saving the downloaded file
90
+ const downloadPath = path.join(uniqueOutputPath, originalFileName);
91
+ await downloadFile(inputPath, downloadPath);
92
+ inputPath = downloadPath;
93
+ }
94
+
95
+
48
96
  const metadata = await ffmpegProbe(inputPath);
49
97
  const duration = metadata.format.duration;
50
98
  const numChunks = Math.ceil((duration - 1) / chunkDurationInSeconds);
51
99
 
52
100
  const chunkPromises = [];
53
101
 
54
- const uniqueOutputPath = generateUniqueFolderName();
55
-
56
- // Create unique folder
57
- fs.mkdirSync(uniqueOutputPath, { recursive: true });
58
102
 
59
103
 
60
104
  for (let i = 0; i < numChunks; i++) {
@@ -73,7 +117,7 @@ async function splitMediaFile(inputPath, chunkDurationInSeconds = 600) {
73
117
  chunkPromises.push(chunkPromise);
74
118
  }
75
119
 
76
- return { chunkPromises, uniqueOutputPath }
120
+ return { chunkPromises, uniqueOutputPath };
77
121
  } catch (err) {
78
122
  console.error('Error occurred during the splitting process:', err);
79
123
  }
@@ -28,6 +28,18 @@ async function deleteTempPath(path) {
28
28
  }
29
29
  }
30
30
 
31
+ function ensureEncoded(url) {
32
+ try {
33
+ const decodedUrl = decodeURI(url);
34
+ if (decodedUrl === url) {
35
+ return encodeURI(url);
36
+ }
37
+ return url;
38
+ } catch (e) {
39
+ return url;
40
+ }
41
+ }
42
+
31
43
  export {
32
- isValidYoutubeUrl, deleteTempPath
44
+ isValidYoutubeUrl, deleteTempPath, ensureEncoded
33
45
  }
@@ -1,7 +1,7 @@
1
1
  import { processYoutubeUrl, splitMediaFile } from './fileChunker.js';
2
2
  import { saveFileToBlob, deleteBlob, uploadBlob } from './blobHandler.js';
3
3
  import { publishRequestProgress } from './redis.js';
4
- import { deleteTempPath, isValidYoutubeUrl } from './helper.js';
4
+ import { deleteTempPath, ensureEncoded, isValidYoutubeUrl } from './helper.js';
5
5
  import { moveFileToPublicFolder, deleteFolder } from './localFileHandler.js';
6
6
 
7
7
  const useAzure = process.env.AZURE_STORAGE_CONNECTION_STRING ? true : false;
@@ -10,7 +10,6 @@ console.log(useAzure ? 'Using Azure Storage' : 'Using local file system');
10
10
 
11
11
  async function main(context, req) {
12
12
  context.log('Starting req processing..');
13
- // await publishRequestProgress({ requestId:222, progress: 0, data: null });
14
13
 
15
14
  // Clean up blob when request delete which means processing marked completed
16
15
  if (req.method.toLowerCase() === `delete`) {
@@ -48,7 +47,7 @@ async function main(context, req) {
48
47
  let completedCount = 0;
49
48
  let numberOfChunks;
50
49
 
51
- let file = uri;
50
+ let file = ensureEncoded(uri); // encode url to handle special characters
52
51
  let folder;
53
52
  const isYoutubeUrl = isValidYoutubeUrl(uri);
54
53
 
@@ -111,6 +110,5 @@ async function main(context, req) {
111
110
  };
112
111
  }
113
112
 
114
- // main(console, { query: { uri: "https://www.youtube.com/watch?v=QH2-TGUlwu4" } });
115
113
 
116
114
  export default main;