@aj-archipelago/cortex 0.0.6 → 0.0.7

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.
@@ -1,19 +1,26 @@
1
1
  // AzureTranslatePlugin.js
2
2
  const ModelPlugin = require('./modelPlugin');
3
3
  const handlebars = require("handlebars");
4
+ const { encode } = require("gpt-3-encoder");
4
5
 
5
6
  class AzureTranslatePlugin extends ModelPlugin {
6
- constructor(config, modelName, pathway) {
7
- super(config, modelName, pathway);
7
+ constructor(config, pathway) {
8
+ super(config, pathway);
8
9
  }
9
10
 
10
- // Set up parameters specific to the Azure Translate API
11
- requestParameters(text, parameters, prompt) {
11
+ getCompiledPrompt(text, parameters, prompt) {
12
12
  const combinedParameters = { ...this.promptParameters, ...parameters };
13
13
  const modelPrompt = this.getModelPrompt(prompt, parameters);
14
14
  const modelPromptText = modelPrompt.prompt ? handlebars.compile(modelPrompt.prompt)({ ...combinedParameters, text }) : '';
15
-
16
- return {
15
+
16
+ return { modelPromptText, tokenLength: encode(modelPromptText).length };
17
+ }
18
+
19
+ // Set up parameters specific to the Azure Translate API
20
+ getRequestParameters(text, parameters, prompt) {
21
+ const combinedParameters = { ...this.promptParameters, ...parameters };
22
+ const { modelPromptText } = this.getCompiledPrompt(text, parameters, prompt);
23
+ const requestParameters = {
17
24
  data: [
18
25
  {
19
26
  Text: modelPromptText,
@@ -23,11 +30,12 @@ class AzureTranslatePlugin extends ModelPlugin {
23
30
  to: combinedParameters.to
24
31
  }
25
32
  };
33
+ return requestParameters;
26
34
  }
27
35
 
28
36
  // Execute the request to the Azure Translate API
29
37
  async execute(text, parameters, prompt) {
30
- const requestParameters = this.requestParameters(text, parameters, prompt);
38
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
31
39
 
32
40
  const url = this.requestUrl(text);
33
41
 
@@ -35,7 +43,7 @@ class AzureTranslatePlugin extends ModelPlugin {
35
43
  const params = requestParameters.params;
36
44
  const headers = this.model.headers || {};
37
45
 
38
- return this.executeRequest(url, data, params, headers);
46
+ return this.executeRequest(url, data, params, headers, prompt);
39
47
  }
40
48
  }
41
49
 
@@ -38,6 +38,41 @@ class ModelPlugin {
38
38
  this.shouldCache = config.get('enableCache') && (pathway.enableCache || pathway.temperature == 0);
39
39
  }
40
40
 
41
+ // Function to remove non-system messages until token length is less than target
42
+ removeMessagesUntilTarget = (messages, targetTokenLength) => {
43
+ let chatML = this.messagesToChatML(messages);
44
+ let tokenLength = encode(chatML).length;
45
+
46
+ while (tokenLength > targetTokenLength) {
47
+ for (let i = 0; i < messages.length; i++) {
48
+ if (messages[i].role !== 'system') {
49
+ messages.splice(i, 1);
50
+ chatML = this.messagesToChatML(messages);
51
+ tokenLength = encode(chatML).length;
52
+ break;
53
+ }
54
+ }
55
+ if (messages.every(message => message.role === 'system')) {
56
+ break; // All remaining messages are 'system', stop removing messages
57
+ }
58
+ }
59
+ return messages;
60
+ }
61
+
62
+ //convert a messages array to a simple chatML format
63
+ messagesToChatML = (messages) => {
64
+ let output = "";
65
+ if (messages && messages.length) {
66
+ for (let message of messages) {
67
+ output += (message.role && message.content) ? `<|im_start|>${message.role}\n${message.content}\n<|im_end|>\n` : `${message}\n`;
68
+ }
69
+ // you always want the assistant to respond next so add a
70
+ // directive for that
71
+ output += "<|im_start|>assistant\n";
72
+ }
73
+ return output;
74
+ }
75
+
41
76
  getModelMaxTokenLength() {
42
77
  return (this.promptParameters.maxTokenLength ?? this.model.maxTokenLength ?? DEFAULT_MAX_TOKENS);
43
78
  }
@@ -120,40 +155,37 @@ class ModelPlugin {
120
155
  return messageResult ?? textResult ?? null;
121
156
  }
122
157
 
123
- logMessagePreview(messages) {
124
- messages.forEach((message, index) => {
125
- const words = message.content.split(" ");
126
- const tokenCount = encode(message.content).length;
127
- let preview;
128
-
129
- if (index === 0) {
130
- preview = message.content;
131
- } else {
132
- preview = words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" ");
133
- }
158
+ logRequestData(data, responseData, prompt) {
159
+ const separator = `\n=== ${this.pathwayName}.${this.requestCount++} ===\n`;
160
+ console.log(separator);
134
161
 
135
- console.log(`Message ${index + 1}: Role: ${message.role}, Tokens: ${tokenCount}, Content: "${preview}"`);
136
- });
137
- }
162
+ const modelInput = data.prompt || (data.messages && data.messages[0].content) || (data.length > 0 && data[0].Text) || null;
138
163
 
139
- async executeRequest(url, data, params, headers) {
140
- const responseData = await request({ url, data, params, headers, cache: this.shouldCache }, this.modelName);
141
- const modelInput = data.prompt || (data.messages && data.messages[0].content) || data[0].Text || null;
142
-
143
- console.log(`=== ${this.pathwayName}.${this.requestCount++} ===`);
144
-
145
164
  if (data.messages && data.messages.length > 1) {
146
- this.logMessagePreview(data.messages);
165
+ data.messages.forEach((message, index) => {
166
+ const words = message.content.split(" ");
167
+ const tokenCount = encode(message.content).length;
168
+ const preview = words.length < 41 ? message.content : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" ");
169
+
170
+ console.log(`\x1b[36mMessage ${index + 1}: Role: ${message.role}, Tokens: ${tokenCount}, Content: "${preview}"\x1b[0m`);
171
+ });
147
172
  } else {
148
173
  console.log(`\x1b[36m${modelInput}\x1b[0m`);
149
174
  }
150
-
175
+
151
176
  console.log(`\x1b[34m> ${this.parseResponse(responseData)}\x1b[0m`);
152
177
 
178
+ prompt.debugInfo += `${separator}${JSON.stringify(data)}`;
179
+ }
180
+
181
+ async executeRequest(url, data, params, headers, prompt) {
182
+ const responseData = await request({ url, data, params, headers, cache: this.shouldCache }, this.modelName);
183
+
153
184
  if (responseData.error) {
154
185
  throw new Exception(`An error was returned from the server: ${JSON.stringify(responseData.error)}`);
155
186
  }
156
187
 
188
+ this.logRequestData(data, responseData, prompt);
157
189
  return this.parseResponse(responseData);
158
190
  }
159
191
 
@@ -1,37 +1,61 @@
1
1
  // OpenAIChatPlugin.js
2
2
  const ModelPlugin = require('./modelPlugin');
3
3
  const handlebars = require("handlebars");
4
+ const { encode } = require("gpt-3-encoder");
4
5
 
5
6
  class OpenAIChatPlugin extends ModelPlugin {
6
7
  constructor(config, pathway) {
7
8
  super(config, pathway);
8
9
  }
9
10
 
10
- // Set up parameters specific to the OpenAI Chat API
11
- requestParameters(text, parameters, prompt) {
11
+ getCompiledPrompt(text, parameters, prompt) {
12
12
  const combinedParameters = { ...this.promptParameters, ...parameters };
13
13
  const modelPrompt = this.getModelPrompt(prompt, parameters);
14
14
  const modelPromptText = modelPrompt.prompt ? handlebars.compile(modelPrompt.prompt)({ ...combinedParameters, text }) : '';
15
15
  const modelPromptMessages = this.getModelPromptMessages(modelPrompt, combinedParameters, text);
16
+ const modelPromptMessagesML = this.messagesToChatML(modelPromptMessages);
16
17
 
17
- const { stream } = parameters;
18
+ if (modelPromptMessagesML) {
19
+ return { modelPromptMessages, tokenLength: encode(modelPromptMessagesML).length };
20
+ } else {
21
+ return { modelPromptText, tokenLength: encode(modelPromptText).length };
22
+ }
23
+ }
18
24
 
19
- return {
20
- messages: modelPromptMessages || [{ "role": "user", "content": modelPromptText }],
21
- temperature: this.temperature ?? 0.7,
22
- stream
25
+ // Set up parameters specific to the OpenAI Chat API
26
+ getRequestParameters(text, parameters, prompt) {
27
+ const { modelPromptText, modelPromptMessages, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
28
+ const { stream } = parameters;
29
+
30
+ // Define the model's max token length
31
+ const modelMaxTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
32
+
33
+ let requestMessages = modelPromptMessages || [{ "role": "user", "content": modelPromptText }];
34
+
35
+ // Check if the token length exceeds the model's max token length
36
+ if (tokenLength > modelMaxTokenLength) {
37
+ // Remove older messages until the token length is within the model's limit
38
+ requestMessages = this.removeMessagesUntilTarget(requestMessages, modelMaxTokenLength);
39
+ }
40
+
41
+ const requestParameters = {
42
+ messages: requestMessages,
43
+ temperature: this.temperature ?? 0.7,
44
+ stream
23
45
  };
46
+
47
+ return requestParameters;
24
48
  }
25
49
 
26
50
  // Execute the request to the OpenAI Chat API
27
51
  async execute(text, parameters, prompt) {
28
52
  const url = this.requestUrl(text);
29
- const requestParameters = this.requestParameters(text, parameters, prompt);
53
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
30
54
 
31
55
  const data = { ...(this.model.params || {}), ...requestParameters };
32
56
  const params = {};
33
57
  const headers = this.model.headers || {};
34
- return this.executeRequest(url, data, params, headers);
58
+ return this.executeRequest(url, data, params, headers, prompt);
35
59
  }
36
60
  }
37
61
 
@@ -3,65 +3,81 @@ const ModelPlugin = require('./modelPlugin');
3
3
  const handlebars = require("handlebars");
4
4
  const { encode } = require("gpt-3-encoder");
5
5
 
6
- //convert a messages array to a simple chatML format
7
- const messagesToChatML = (messages) => {
8
- let output = "";
9
- if (messages && messages.length) {
10
- for (let message of messages) {
11
- output += (message.role && message.content) ? `<|im_start|>${message.role}\n${message.content}\n<|im_end|>\n` : `${message}\n`;
12
- }
13
- // you always want the assistant to respond next so add a
14
- // directive for that
15
- output += "<|im_start|>assistant\n";
16
- }
17
- return output;
18
- }
19
-
20
6
  class OpenAICompletionPlugin extends ModelPlugin {
21
7
  constructor(config, pathway) {
22
8
  super(config, pathway);
23
9
  }
24
10
 
25
- // Set up parameters specific to the OpenAI Completion API
26
- requestParameters(text, parameters, prompt) {
11
+ getCompiledPrompt(text, parameters, prompt) {
27
12
  const combinedParameters = { ...this.promptParameters, ...parameters };
28
13
  const modelPrompt = this.getModelPrompt(prompt, parameters);
29
14
  const modelPromptText = modelPrompt.prompt ? handlebars.compile(modelPrompt.prompt)({ ...combinedParameters, text }) : '';
30
15
  const modelPromptMessages = this.getModelPromptMessages(modelPrompt, combinedParameters, text);
31
- const modelPromptMessagesML = messagesToChatML(modelPromptMessages);
32
-
33
- const { stream } = parameters;
16
+ const modelPromptMessagesML = this.messagesToChatML(modelPromptMessages);
34
17
 
35
18
  if (modelPromptMessagesML) {
36
- return {
37
- prompt: modelPromptMessagesML,
38
- max_tokens: this.getModelMaxTokenLength() - encode(modelPromptMessagesML).length - 1,
39
- temperature: this.temperature ?? 0.7,
40
- top_p: 0.95,
41
- frequency_penalty: 0,
42
- presence_penalty: 0,
43
- stop: ["<|im_end|>"],
44
- stream
45
- };
19
+ return { modelPromptMessages, tokenLength: encode(modelPromptMessagesML).length };
46
20
  } else {
47
- return {
48
- prompt: modelPromptText,
49
- max_tokens: this.getModelMaxTokenLength() - encode(modelPromptText).length - 1,
50
- temperature: this.temperature ?? 0.7,
51
- stream
52
- };
21
+ return { modelPromptText, tokenLength: encode(modelPromptText).length };
53
22
  }
54
23
  }
55
24
 
25
+ // Set up parameters specific to the OpenAI Completion API
26
+ getRequestParameters(text, parameters, prompt) {
27
+ let { modelPromptMessages, modelPromptText, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
28
+ const { stream } = parameters;
29
+ let modelPromptMessagesML = '';
30
+ const modelMaxTokenLength = this.getModelMaxTokenLength();
31
+ let requestParameters = {};
32
+
33
+ if (modelPromptMessages) {
34
+ const requestMessages = this.removeMessagesUntilTarget(modelPromptMessages, modelMaxTokenLength - 1);
35
+ modelPromptMessagesML = this.messagesToChatML(requestMessages);
36
+ tokenLength = encode(modelPromptMessagesML).length;
37
+
38
+ if (tokenLength >= modelMaxTokenLength) {
39
+ throw new Error(`The maximum number of tokens for this model is ${modelMaxTokenLength}. Please reduce the number of messages in the prompt.`);
40
+ }
41
+
42
+ const max_tokens = modelMaxTokenLength - tokenLength - 1;
43
+
44
+ requestParameters = {
45
+ prompt: modelPromptMessagesML,
46
+ max_tokens: max_tokens,
47
+ temperature: this.temperature ?? 0.7,
48
+ top_p: 0.95,
49
+ frequency_penalty: 0,
50
+ presence_penalty: 0,
51
+ stop: ["<|im_end|>"],
52
+ stream
53
+ };
54
+ } else {
55
+ if (tokenLength >= modelMaxTokenLength) {
56
+ throw new Error(`The maximum number of tokens for this model is ${modelMaxTokenLength}. Please reduce the length of the prompt.`);
57
+ }
58
+
59
+ const max_tokens = modelMaxTokenLength - tokenLength - 1;
60
+
61
+ requestParameters = {
62
+ prompt: modelPromptText,
63
+ max_tokens: max_tokens,
64
+ temperature: this.temperature ?? 0.7,
65
+ stream
66
+ };
67
+ }
68
+
69
+ return requestParameters;
70
+ }
71
+
56
72
  // Execute the request to the OpenAI Completion API
57
73
  async execute(text, parameters, prompt) {
58
74
  const url = this.requestUrl(text);
59
- const requestParameters = this.requestParameters(text, parameters, prompt);
75
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
60
76
 
61
77
  const data = { ...(this.model.params || {}), ...requestParameters };
62
78
  const params = {};
63
79
  const headers = this.model.headers || {};
64
- return this.executeRequest(url, data, params, headers);
80
+ return this.executeRequest(url, data, params, headers, prompt);
65
81
  }
66
82
  }
67
83
 
@@ -0,0 +1,79 @@
1
+ // OpenAICompletionPlugin.js
2
+ const ModelPlugin = require('./modelPlugin');
3
+ const handlebars = require("handlebars");
4
+ const { encode } = require("gpt-3-encoder");
5
+ const FormData = require('form-data');
6
+ const fs = require('fs');
7
+ const { splitMediaFile, isValidYoutubeUrl, processYoutubeUrl, deleteTempPath } = require('../../lib/fileChunker');
8
+ const pubsub = require('../pubsub');
9
+
10
+ class OpenAIWhisperPlugin extends ModelPlugin {
11
+ constructor(config, pathway) {
12
+ super(config, pathway);
13
+ }
14
+
15
+ getCompiledPrompt(text, parameters, prompt) {
16
+ const combinedParameters = { ...this.promptParameters, ...parameters };
17
+ const modelPrompt = this.getModelPrompt(prompt, parameters);
18
+ const modelPromptText = modelPrompt.prompt ? handlebars.compile(modelPrompt.prompt)({ ...combinedParameters, text }) : '';
19
+
20
+ return { modelPromptText, tokenLength: encode(modelPromptText).length };
21
+ }
22
+
23
+ // Execute the request to the OpenAI Whisper API
24
+ async execute(text, parameters, prompt, pathwayResolver) {
25
+ const url = this.requestUrl(text);
26
+ const params = {};
27
+ const { modelPromptText } = this.getCompiledPrompt(text, parameters, prompt);
28
+
29
+ const processChunk = async (chunk) => {
30
+ try {
31
+ const formData = new FormData();
32
+ formData.append('file', fs.createReadStream(chunk));
33
+ formData.append('model', this.model.params.model);
34
+ formData.append('response_format', 'text');
35
+ // formData.append('language', 'tr');
36
+ modelPromptText && formData.append('prompt', modelPromptText);
37
+
38
+ return this.executeRequest(url, formData, params, { ...this.model.headers, ...formData.getHeaders() });
39
+ } catch (err) {
40
+ console.log(err);
41
+ }
42
+ }
43
+
44
+ let result;
45
+ let { file } = parameters;
46
+ let folder;
47
+ const isYoutubeUrl = isValidYoutubeUrl(file);
48
+
49
+ try {
50
+ if (isYoutubeUrl) {
51
+ file = await processYoutubeUrl(file);
52
+ }
53
+
54
+ const mediaSplit = await splitMediaFile(file);
55
+
56
+ const { requestId } = pathwayResolver;
57
+ pubsub.publish('REQUEST_PROGRESS', {
58
+ requestProgress: {
59
+ requestId,
60
+ progress: 0.5,
61
+ data: null,
62
+ }
63
+ });
64
+
65
+ folder = mediaSplit.folder;
66
+ result = await Promise.all(mediaSplit.chunks.map(processChunk));
67
+
68
+ } catch (error) {
69
+ console.error("An error occurred:", error);
70
+ } finally {
71
+ isYoutubeUrl && (await deleteTempPath(file));
72
+ folder && (await deleteTempPath(folder));
73
+ }
74
+ return result.join('');
75
+ }
76
+ }
77
+
78
+ module.exports = OpenAIWhisperPlugin;
79
+
package/graphql/prompt.js CHANGED
@@ -12,6 +12,7 @@ class Prompt {
12
12
 
13
13
  this.usesTextInput = promptContains('text', this.prompt ? this.prompt : this.messages);
14
14
  this.usesPreviousResult = promptContains('previousResult', this.prompt ? this.prompt : this.messages);
15
+ this.debugInfo = '';
15
16
  }
16
17
  }
17
18
 
@@ -12,16 +12,16 @@ const rootResolver = async (parent, args, contextValue, info) => {
12
12
  info.cacheControl.setCacheHint({ maxAge: 60 * 60 * 24, scope: 'PUBLIC' });
13
13
  }
14
14
 
15
- const pathwayResolver = new PathwayResolver({ config, pathway, requestState });
15
+ const pathwayResolver = new PathwayResolver({ config, pathway, args, requestState });
16
16
  contextValue.pathwayResolver = pathwayResolver;
17
17
 
18
- // Add request parameters back as debug
19
- const requestParameters = pathwayResolver.prompts.map((prompt) => pathwayResolver.pathwayPrompter.plugin.requestParameters(args.text, args, prompt));
20
- const debug = JSON.stringify(requestParameters);
21
-
22
18
  // Execute the request with timeout
23
19
  const result = await fulfillWithTimeout(pathway.resolver(parent, args, contextValue, info), pathway.timeout);
24
20
  const { warnings, previousResult, savedContextId } = pathwayResolver;
21
+
22
+ // Add request parameters back as debug
23
+ const debug = pathwayResolver.prompts.map(prompt => prompt.debugInfo || '').join('\n').trim();
24
+
25
25
  return { debug, result, warnings, previousResult, contextId: savedContextId }
26
26
  }
27
27
 
@@ -2,51 +2,60 @@ const GRAPHQL_TYPE_MAP = {
2
2
  boolean: 'Boolean',
3
3
  string: 'String',
4
4
  number: 'Int',
5
- }
6
-
7
-
8
- const typeDef = (pathway) => {
5
+ };
6
+
7
+ const typeDef = (pathway) => {
9
8
  const { name, objName, defaultInputParameters, inputParameters, format } = pathway;
10
-
9
+
11
10
  const fields = format ? format.match(/\b(\w+)\b/g) : null;
12
- const fieldsStr = !fields ? `` : fields.map(f => `${f}: String`).join('\n ');
13
-
11
+ const fieldsStr = !fields ? `` : fields.map((f) => `${f}: String`).join('\n ');
12
+
14
13
  const typeName = fields ? `${objName}Result` : `String`;
15
14
  const messageType = `input Message { role: String, content: String }`;
16
-
15
+
17
16
  const type = fields ? `type ${typeName} {
18
- ${fieldsStr}
19
- }` : ``;
20
-
17
+ ${fieldsStr}
18
+ }` : ``;
19
+
21
20
  const resultStr = pathway.list ? `[${typeName}]` : typeName;
22
-
21
+
23
22
  const responseType = `type ${objName} {
24
- debug: String
25
- result: ${resultStr}
26
- previousResult: String
27
- warnings: [String]
28
- contextId: String
29
- }`;
30
-
31
-
23
+ debug: String
24
+ result: ${resultStr}
25
+ previousResult: String
26
+ warnings: [String]
27
+ contextId: String
28
+ }`;
29
+
32
30
  const params = { ...defaultInputParameters, ...inputParameters };
33
-
34
- const paramsStr = Object.entries(params).map(
35
- ([key, value]) => {
36
- if (typeof value === 'object' && Array.isArray(value)) {
37
- return `${key}: [Message] = []`;
38
- } else {
39
- return `${key}: ${GRAPHQL_TYPE_MAP[typeof (value)]} = ${typeof (value) === 'string' ? `"${value}"` : value}`;
40
- }
31
+
32
+ const paramsStr = Object.entries(params)
33
+ .map(([key, value]) => {
34
+ if (typeof value === 'object' && Array.isArray(value)) {
35
+ return `${key}: [Message] = []`;
36
+ } else {
37
+ return `${key}: ${GRAPHQL_TYPE_MAP[typeof value]} = ${
38
+ typeof value === 'string' ? `"${value}"` : value
39
+ }`;
41
40
  }
42
- ).join('\n');
43
-
44
-
45
- const definition = `${messageType}\n\n${type}\n\n${responseType}\n\nextend type Query {${name}(${paramsStr}): ${objName}}`;
46
- //console.log(definition);
47
- return definition;
48
- }
49
-
50
- module.exports = {
41
+ })
42
+ .join('\n');
43
+
44
+ const restDefinition = Object.entries(params).map(([key, value]) => {
45
+ return {
46
+ name: key,
47
+ type: `${GRAPHQL_TYPE_MAP[typeof value]}${typeof value === 'object' && Array.isArray(value) ? '[]' : ''}`,
48
+ };
49
+ });
50
+
51
+ const gqlDefinition = `${messageType}\n\n${type}\n\n${responseType}\n\nextend type Query {${name}(${paramsStr}): ${objName}}`;
52
+
53
+ return {
54
+ gqlDefinition,
55
+ restDefinition,
56
+ };
57
+ };
58
+
59
+ module.exports = {
51
60
  typeDef,
52
- }
61
+ };