@aj-archipelago/cortex 1.0.0 → 1.0.2

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.
package/.eslintignore ADDED
@@ -0,0 +1,30 @@
1
+ # Ignore build artifacts
2
+ /dist
3
+ /build
4
+
5
+ # Ignore node_modules
6
+ /node_modules
7
+
8
+ # Ignore log files
9
+ *.log
10
+
11
+ # Ignore any config files
12
+ .env
13
+ .env.*
14
+
15
+ # Ignore coverage reports
16
+ /coverage
17
+
18
+ # Ignore documentation
19
+ /docs
20
+
21
+ # Ignore any generated or bundled files
22
+ *.min.js
23
+ *.bundle.js
24
+
25
+ # Ignore any files generated by your IDE or text editor
26
+ .idea/
27
+ .vscode/
28
+ *.sublime-*
29
+ *.iml
30
+ *.swp
package/.eslintrc ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true,
5
+ "node": true
6
+ },
7
+ "extends": [
8
+ "eslint:recommended"
9
+ ],
10
+ "parserOptions": {
11
+ "ecmaVersion": 12,
12
+ "sourceType": "module"
13
+ },
14
+ "plugins": [
15
+ "import"
16
+ ],
17
+ "rules": {
18
+ "import/no-unresolved": "error",
19
+ "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
20
+ "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
21
+ },
22
+ "settings": {
23
+ "import/resolver": {
24
+ "node": {
25
+ "extensions": [".js"],
26
+ "moduleDirectory": ["node_modules", "src"]
27
+ }
28
+ },
29
+ "import/core-modules": ["ava"]
30
+ }
31
+ }
package/LICENSE CHANGED
@@ -1,10 +1,6 @@
1
1
  MIT License
2
2
 
3
- <<<<<<< HEAD
4
- Copyright (c) 2022 Al Jazeera Media Network
5
- =======
6
- Copyright (c) 2023 aj-archipelago
7
- >>>>>>> b78a6667176ebfd09d2c7ed1549d8dc18691c344
3
+ Copyright (c) 2023 Al Jazeera Media Network
8
4
 
9
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
10
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,6 +2,16 @@
2
2
  Cortex simplifies and accelerates the process of creating applications that harness the power of modern AI models like chatGPT and GPT-4 by providing a structured interface (GraphQL or REST) to a powerful prompt execution environment. This enables complex augmented prompting and abstracts away most of the complexity of managing model connections like chunking input, rate limiting, formatting output, caching, and handling errors.
3
3
  ## Why build Cortex?
4
4
  Modern AI models are transformational, but a number of complexities emerge when developers start using them to deliver application-ready functions. Most models require precisely formatted, carefully engineered and sequenced prompts to produce consistent results, and the responses are typically largely unstructured text without validation or formatting. Additionally, these models are evolving rapidly, are typically costly and slow to query and implement hard request size and rate restrictions that need to be carefully navigated for optimum throughput. Cortex offers a solution to these problems and provides a simple and extensible package for interacting with NL AI models.
5
+
6
+ ## Okay, but what can I really do with this thing?
7
+ Yikes. Everything! It's kind of an LLM swiss army knife. Here are some ideas:
8
+ * Create custom chat agents with memory and personalization and then expose it to a bunch of different UIs (custom chat portals, Slack, teams, etc. - anything that can speak to a REST or GraphQL endpoint)
9
+ * Create custom coding assistants (code generation, code reviews, test writing, AI pair programming) and easily integrate them with your existing editing tools.
10
+ * Create powerful AI editing tools (copy editing, paraphrasing, summarization, etc.) tools for your company and then integrate them with your existing workflow tools without having to build all the LLM-handling logic into those tools.
11
+ * Make LLM chains and agents from LangChain.js available via scalable REST or GraphQL endpoints.
12
+ * Put a REST or GraphQL front end on your locally-run models (e.g. llama.cpp) and use them in concert with other tools.
13
+ * The sky is the limit!
14
+
5
15
  ## Features
6
16
 
7
17
  * Simple architecture to build custom functional endpoints (called `pathways`), that implement common NL AI tasks. Default pathways include chat, summarization, translation, paraphrasing, completion, spelling and grammar correction, entity extraction, sentiment analysis, and bias analysis.
@@ -305,7 +315,7 @@ export default {
305
315
 
306
316
  ### Building and Loading Pathways
307
317
 
308
- Pathways are loaded from modules in the `pathways` directory. The pathways are built and loaded to the `config` object using the `buildPathways` function. The `buildPathways` function loads the base pathway, the core pathways, and any custom pathways. It then creates a new object that contains all the pathways and adds it to the pathways property of the config object. The order of loading means that custom pathways will always override any core pathways that Cortext provides. While pathways are designed to be self-contained, you can override some pathway properties - including whether they're even available at all - in the `pathways` section of the config file.
318
+ Pathways are loaded from modules in the `pathways` directory. The pathways are built and loaded to the `config` object using the `buildPathways` function. The `buildPathways` function loads the base pathway, the core pathways, and any custom pathways. It then creates a new object that contains all the pathways and adds it to the pathways property of the config object. The order of loading means that custom pathways will always override any core pathways that Cortex provides. While pathways are designed to be self-contained, you can override some pathway properties - including whether they're even available at all - in the `pathways` section of the config file.
309
319
 
310
320
  ## Core (Default) Pathways
311
321
 
@@ -435,7 +445,7 @@ If you encounter any issues while using Cortex, there are a few things you can d
435
445
  If you would like to contribute to Cortex, there are two ways to do so. You can submit issues to the Cortex GitHub repository or submit pull requests with your proposed changes.
436
446
 
437
447
  ## License
438
- Cortex is released under the MIT License. See [LICENSE](https://github.com/ALJAZEERAPLUS/cortex/blob/main/LICENSE) for more details.
448
+ Cortex is released under the MIT License. See [LICENSE](https://github.com/aj-archipelago/cortex/blob/main/LICENSE) for more details.
439
449
 
440
450
  ## API Reference
441
451
  Detailed documentation on Cortex's API can be found in the /graphql endpoint of your project. Examples of queries and responses can also be found in the Cortex documentation, along with tips for getting the most out of Cortex.
package/SECURITY.md ADDED
@@ -0,0 +1,22 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ We take the security of our project seriously. The table below shows the versions of Cortex currently being supported with security updates.
6
+
7
+ | Version | Supported |
8
+ | ------- | ------------------ |
9
+ | 1.x.x | :white_check_mark: |
10
+
11
+ ## Reporting a Vulnerability
12
+
13
+ If you have discovered a security vulnerability in Cortex, please follow these steps to report it:
14
+
15
+ 1. **Do not** create a public GitHub issue, as this might expose the vulnerability to others.
16
+ 2. Please follow the GitHub process for [Privately Reporting a Security Vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability)
17
+
18
+ ## Disclosure Policy
19
+
20
+ Cortex follows responsible disclosure practices. Once a vulnerability is confirmed and a fix is developed, we will release a security update and publicly disclose the vulnerability. We will credit the reporter of the vulnerability in the disclosure, unless the reporter wishes to remain anonymous.
21
+
22
+ We appreciate your help in keeping Cortex secure and your responsible disclosure of any security vulnerabilities you discover.
@@ -0,0 +1,70 @@
1
+ {
2
+ "defaultModelName": "oai-td3",
3
+ "models": {
4
+ "azure-translate": {
5
+ "type": "AZURE-TRANSLATE",
6
+ "url": "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0",
7
+ "headers": {
8
+ "Ocp-Apim-Subscription-Key": "{{ARCHIPELAGO_TRANSLATE_KEY}}",
9
+ "Ocp-Apim-Subscription-Region": "eastus",
10
+ "Content-Type": "application/json"
11
+ },
12
+ "requestsPerSecond": 10,
13
+ "maxTokenLength": 2000
14
+ },
15
+ "oai-td3": {
16
+ "type": "OPENAI-COMPLETION",
17
+ "url": "https://api.openai.com/v1/completions",
18
+ "headers": {
19
+ "Authorization": "Bearer {{OPENAI_API_KEY}}",
20
+ "Content-Type": "application/json"
21
+ },
22
+ "params": {
23
+ "model": "text-davinci-003"
24
+ },
25
+ "requestsPerSecond": 10,
26
+ "maxTokenLength": 4096
27
+ },
28
+ "oai-gpturbo": {
29
+ "type": "OPENAI-CHAT",
30
+ "url": "https://api.openai.com/v1/chat/completions",
31
+ "headers": {
32
+ "Authorization": "Bearer {{OPENAI_API_KEY}}",
33
+ "Content-Type": "application/json"
34
+ },
35
+ "params": {
36
+ "model": "gpt-3.5-turbo"
37
+ },
38
+ "requestsPerSecond": 10,
39
+ "maxTokenLength": 8192
40
+ },
41
+ "oai-gpt4": {
42
+ "type": "OPENAI-CHAT",
43
+ "url": "https://api.openai.com/v1/chat/completions",
44
+ "headers": {
45
+ "Authorization": "Bearer {{OPENAI_API_KEY}}",
46
+ "Content-Type": "application/json"
47
+ },
48
+ "params": {
49
+ "model": "gpt-4"
50
+ },
51
+ "requestsPerSecond": 10,
52
+ "maxTokenLength": 8192
53
+ },
54
+ "local-llama13B": {
55
+ "type": "LOCAL-CPP-MODEL",
56
+ "executablePath": "../llm/llama.cpp/main",
57
+ "args": [
58
+ "-m", "../llm/llama.cpp/models/13B/ggml-model-q4_0.bin",
59
+ "--repeat_penalty", "1.0",
60
+ "--keep", "0",
61
+ "-t", "8",
62
+ "--mlock"
63
+ ],
64
+ "requestsPerSecond": 10,
65
+ "maxTokenLength": 1024
66
+ }
67
+ },
68
+ "enableCache": false,
69
+ "enableRestEndpoints": false
70
+ }
package/config.js CHANGED
@@ -110,12 +110,6 @@ var config = convict({
110
110
  default: null,
111
111
  env: 'CORTEX_CONFIG_FILE'
112
112
  },
113
- serpApiKey: {
114
- format: String,
115
- default: null,
116
- env: 'SERPAPI_API_KEY',
117
- sensitive: true
118
- },
119
113
  });
120
114
 
121
115
  // Read in environment variables and set up service configuration
@@ -43,7 +43,7 @@ const getSemanticChunks = (text, chunkSize) => {
43
43
  };
44
44
 
45
45
  const breakByParagraphs = (str) => breakByRegex(str, /[\r\n]+/, true);
46
- const breakBySentences = (str) => breakByRegex(str, /(?<=[.。؟!\?!\n])\s+/, true);
46
+ const breakBySentences = (str) => breakByRegex(str, /(?<=[.。؟!?!\n])\s+/, true);
47
47
  const breakByWords = (str) => breakByRegex(str, /(\s,;:.+)/);
48
48
 
49
49
  const createChunks = (tokens) => {
@@ -48,7 +48,7 @@ const buildRestEndpoints = (pathways, app, server, config) => {
48
48
 
49
49
  app.post(`/rest/${name}`, async (req, res) => {
50
50
  const variables = fieldVariableDefs.reduce((acc, variableDef) => {
51
- if (req.body.hasOwnProperty(variableDef.name)) {
51
+ if (Object.prototype.hasOwnProperty.call(req.body, variableDef.name)) {
52
52
  acc[variableDef.name] = req.body[variableDef.name];
53
53
  }
54
54
  return acc;
package/graphql/parser.js CHANGED
@@ -6,6 +6,7 @@ const regexParser = (text, regex) => {
6
6
  // parse numbered list text format into list
7
7
  // this supports most common numbered list returns like "1.", "1)", "1-"
8
8
  const parseNumberedList = (str) => {
9
+ // eslint-disable-next-line no-useless-escape
9
10
  return regexParser(str, /^\s*[\[\{\(]*\d+[\s.=\-:,;\]\)\}]/gm);
10
11
  }
11
12
 
@@ -1,8 +1,9 @@
1
1
  // PathwayPrompter.js
2
- import OpenAIChatPlugin from './plugins/openAIChatPlugin.js';
3
- import OpenAICompletionPlugin from './plugins/openAICompletionPlugin.js';
2
+ import OpenAIChatPlugin from './plugins/openAiChatPlugin.js';
3
+ import OpenAICompletionPlugin from './plugins/openAiCompletionPlugin.js';
4
4
  import AzureTranslatePlugin from './plugins/azureTranslatePlugin.js';
5
5
  import OpenAIWhisperPlugin from './plugins/openAiWhisperPlugin.js';
6
+ import LocalModelPlugin from './plugins/localModelPlugin.js';
6
7
  import handlebars from 'handlebars';
7
8
 
8
9
  // register functions that can be called directly in the prompt markdown
@@ -44,6 +45,9 @@ class PathwayPrompter {
44
45
  case 'OPENAI_WHISPER':
45
46
  plugin = new OpenAIWhisperPlugin(config, pathway);
46
47
  break;
48
+ case 'LOCAL-CPP-MODEL':
49
+ plugin = new LocalModelPlugin(config, pathway);
50
+ break;
47
51
  default:
48
52
  throw new handlebars.Exception(`Unsupported model type: ${model.type}`);
49
53
  }
@@ -8,8 +8,6 @@ import { Prompt } from './prompt.js';
8
8
  import { getv, setv } from '../lib/keyValueStorageClient.js';
9
9
  import { requestState } from './requestState.js';
10
10
 
11
- const MAX_PREVIOUS_RESULT_TOKEN_LENGTH = 1000;
12
-
13
11
  const callPathway = async (config, pathwayName, args, requestState, { text, ...parameters }) => {
14
12
  const pathwayResolver = new PathwayResolver({ config, pathway: config.get(`pathways.${pathwayName}`), args, requestState });
15
13
  return await pathwayResolver.resolve({ text, ...parameters });
@@ -151,7 +149,7 @@ class PathwayResolver {
151
149
  }
152
150
  const encoded = encode(text);
153
151
  if (!this.useInputChunking || encoded.length <= chunkTokenLength) { // no chunking, return as is
154
- if (encoded.length >= chunkTokenLength) {
152
+ if (encoded.length > 0 && encoded.length >= chunkTokenLength) {
155
153
  const warnText = `Truncating long input text. Text length: ${text.length}`;
156
154
  this.warnings.push(warnText);
157
155
  console.warn(warnText);
@@ -189,7 +187,7 @@ class PathwayResolver {
189
187
  // the token ratio is the ratio of the total prompt to the result text - both have to be included
190
188
  // in computing the max token length
191
189
  const promptRatio = this.pathwayPrompter.plugin.getPromptTokenRatio();
192
- let chunkMaxTokenLength = promptRatio * this.pathwayPrompter.plugin.getModelMaxTokenLength() - maxPromptTokenLength;
190
+ let chunkMaxTokenLength = promptRatio * this.pathwayPrompter.plugin.getModelMaxTokenLength() - maxPromptTokenLength - 1;
193
191
 
194
192
  // if we have to deal with prompts that have both text input
195
193
  // and previous result, we need to split the maxChunkToken in half
@@ -0,0 +1,72 @@
1
+ // localModelPlugin.js
2
+ import ModelPlugin from './modelPlugin.js';
3
+ import { execFileSync } from 'child_process';
4
+ import { encode } from 'gpt-3-encoder';
5
+
6
+ class LocalModelPlugin extends ModelPlugin {
7
+ constructor(config, pathway) {
8
+ super(config, pathway);
9
+ }
10
+
11
+ // if the input starts with a chatML response, just return that
12
+ filterFirstResponse(inputString) {
13
+ const regex = /^(.*?)(?=\n<\|im_end\|>|$)/;
14
+ const match = inputString.match(regex);
15
+
16
+ if (match) {
17
+ const firstAssistantResponse = match[1];
18
+ return firstAssistantResponse;
19
+ } else {
20
+ return inputString;
21
+ }
22
+ }
23
+
24
+ getRequestParameters(text, parameters, prompt) {
25
+ let { modelPromptMessages, modelPromptText, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
26
+ const modelTargetTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
27
+
28
+ if (modelPromptMessages) {
29
+ const minMsg = [{ role: "system", content: "" }];
30
+ const addAssistantTokens = encode(this.messagesToChatML(minMsg, true).replace(this.messagesToChatML(minMsg, false), '')).length;
31
+ const requestMessages = this.truncateMessagesToTargetLength(modelPromptMessages, (modelTargetTokenLength - addAssistantTokens));
32
+ modelPromptText = this.messagesToChatML(requestMessages);
33
+ tokenLength = encode(modelPromptText).length;
34
+ }
35
+
36
+ if (tokenLength > modelTargetTokenLength) {
37
+ throw new Error(`Input is too long at ${tokenLength} tokens. The target token length for this pathway is ${modelTargetTokenLength} tokens because the response is expected to take up the rest of the ${this.getModelMaxTokenLength()} tokens that the model can handle. You must reduce the size of the prompt to continue.`);
38
+ }
39
+
40
+ const max_tokens = this.getModelMaxTokenLength() - tokenLength;
41
+
42
+ return {
43
+ prompt: modelPromptText,
44
+ max_tokens: max_tokens,
45
+ temperature: this.temperature ?? 0.7,
46
+ };
47
+ }
48
+
49
+ async execute(text, parameters, prompt, _pathwayResolver) {
50
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
51
+ const { executablePath, args } = this.model;
52
+ args.push("--prompt", requestParameters.prompt);
53
+ //args.push("--max-tokens", requestParameters.max_tokens);
54
+ //args.push("--temperature", requestParameters.temperature);
55
+
56
+ try {
57
+ console.log(`\x1b[36mRunning local model:\x1b[0m`, executablePath, args);
58
+ const result = execFileSync(executablePath, args, { encoding: 'utf8' });
59
+ // Remove only the first occurrence of requestParameters.prompt from the result
60
+ // Could have used regex here but then would need to escape the prompt
61
+ const parts = result.split(requestParameters.prompt, 2);
62
+ const modifiedResult = parts[0] + parts[1];
63
+ console.log(`\x1b[36mResult:\x1b[0m`, modifiedResult);
64
+ return this.filterFirstResponse(modifiedResult);
65
+ } catch (error) {
66
+ console.error(`\x1b[31mError running local model:\x1b[0m`, error);
67
+ throw error;
68
+ }
69
+ }
70
+ }
71
+
72
+ export default LocalModelPlugin;
@@ -40,7 +40,7 @@ class ModelPlugin {
40
40
  this.shouldCache = config.get('enableCache') && (pathway.enableCache || pathway.temperature == 0);
41
41
  }
42
42
 
43
- truncateMessagesToTargetLength = (messages, targetTokenLength) => {
43
+ truncateMessagesToTargetLength(messages, targetTokenLength) {
44
44
  // Calculate the token length of each message
45
45
  const tokenLengths = messages.map((message) => ({
46
46
  message,
@@ -97,7 +97,7 @@ class ModelPlugin {
97
97
 
98
98
  // Return the modified messages array
99
99
  return tokenLengths.map(({ message }) => message);
100
- };
100
+ }
101
101
 
102
102
  //convert a messages array to a simple chatML format
103
103
  messagesToChatML(messages, addAssistant = true) {
@@ -12,14 +12,14 @@ class OpenAIChatPlugin extends ModelPlugin {
12
12
  const { stream } = parameters;
13
13
 
14
14
  // Define the model's max token length
15
- const modelMaxTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
15
+ const modelTargetTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
16
16
 
17
17
  let requestMessages = modelPromptMessages || [{ "role": "user", "content": modelPromptText }];
18
18
 
19
19
  // Check if the token length exceeds the model's max token length
20
- if (tokenLength > modelMaxTokenLength) {
20
+ if (tokenLength > modelTargetTokenLength) {
21
21
  // Remove older messages until the token length is within the model's limit
22
- requestMessages = this.truncateMessagesToTargetLength(requestMessages, modelMaxTokenLength);
22
+ requestMessages = this.truncateMessagesToTargetLength(requestMessages, modelTargetTokenLength);
23
23
  }
24
24
 
25
25
  const requestParameters = {
@@ -13,19 +13,22 @@ class OpenAICompletionPlugin extends ModelPlugin {
13
13
  let { modelPromptMessages, modelPromptText, tokenLength } = this.getCompiledPrompt(text, parameters, prompt);
14
14
  const { stream } = parameters;
15
15
  let modelPromptMessagesML = '';
16
- const modelMaxTokenLength = this.getModelMaxTokenLength();
16
+ // Define the model's max token length
17
+ const modelTargetTokenLength = this.getModelMaxTokenLength() * this.getPromptTokenRatio();
17
18
  let requestParameters = {};
18
19
 
19
20
  if (modelPromptMessages) {
20
- const requestMessages = this.truncateMessagesToTargetLength(modelPromptMessages, modelMaxTokenLength - 1);
21
+ const minMsg = [{ role: "system", content: "" }];
22
+ const addAssistantTokens = encode(this.messagesToChatML(minMsg, true).replace(this.messagesToChatML(minMsg, false), '')).length;
23
+ const requestMessages = this.truncateMessagesToTargetLength(modelPromptMessages, (modelTargetTokenLength - addAssistantTokens));
21
24
  modelPromptMessagesML = this.messagesToChatML(requestMessages);
22
25
  tokenLength = encode(modelPromptMessagesML).length;
23
26
 
24
- if (tokenLength >= modelMaxTokenLength) {
25
- throw new Error(`The maximum number of tokens for this model is ${modelMaxTokenLength}. Please reduce the number of messages in the prompt.`);
27
+ if (tokenLength > modelTargetTokenLength) {
28
+ throw new Error(`Input is too long at ${tokenLength} tokens (this target token length for this pathway is ${modelTargetTokenLength} tokens because the response is expected to take up the rest of the model's max tokens (${this.getModelMaxTokenLength()}). You must reduce the size of the prompt to continue.`);
26
29
  }
27
30
 
28
- const max_tokens = modelMaxTokenLength - tokenLength - 1;
31
+ const max_tokens = this.getModelMaxTokenLength() - tokenLength;
29
32
 
30
33
  requestParameters = {
31
34
  prompt: modelPromptMessagesML,
@@ -38,11 +41,11 @@ class OpenAICompletionPlugin extends ModelPlugin {
38
41
  stream
39
42
  };
40
43
  } else {
41
- if (tokenLength >= modelMaxTokenLength) {
42
- throw new Error(`The maximum number of tokens for this model is ${modelMaxTokenLength}. Please reduce the length of the prompt.`);
44
+ if (tokenLength > modelTargetTokenLength) {
45
+ throw new Error(`Input is too long at ${tokenLength} tokens. The target token length for this pathway is ${modelTargetTokenLength} tokens because the response is expected to take up the rest of the ${this.getModelMaxTokenLength()} tokens that the model can handle. You must reduce the size of the prompt to continue.`);
43
46
  }
44
47
 
45
- const max_tokens = modelMaxTokenLength - tokenLength - 1;
48
+ const max_tokens = this.getModelMaxTokenLength() - tokenLength;
46
49
 
47
50
  requestParameters = {
48
51
  prompt: modelPromptText,
@@ -1,4 +1,4 @@
1
- // OpenAICompletionPlugin.js
1
+ // openAiWhisperPlugin.js
2
2
  import ModelPlugin from './modelPlugin.js';
3
3
 
4
4
  import FormData from 'form-data';
package/graphql/prompt.js CHANGED
@@ -26,6 +26,7 @@ function promptContains(variable, prompt) {
26
26
  // if it's an array, it's the messages format
27
27
  if (Array.isArray(prompt)) {
28
28
  prompt.forEach(p => {
29
+ // eslint-disable-next-line no-cond-assign
29
30
  while (match = p.content && regexp.exec(p.content)) {
30
31
  matches.push(match[1]);
31
32
  }
@@ -26,12 +26,12 @@ const rootResolver = async (parent, args, contextValue, info) => {
26
26
  }
27
27
 
28
28
  // This resolver is used by the root resolver to process the request
29
- const resolver = async (parent, args, contextValue, info) => {
29
+ const resolver = async (parent, args, contextValue, _info) => {
30
30
  const { pathwayResolver } = contextValue;
31
31
  return await pathwayResolver.resolve(args);
32
32
  }
33
33
 
34
- const cancelRequestResolver = (parent, args, contextValue, info) => {
34
+ const cancelRequestResolver = (parent, args, contextValue, _info) => {
35
35
  const { requestId } = args;
36
36
  const { requestState } = contextValue;
37
37
  requestState[requestId] = { canceled: true };
@@ -10,7 +10,7 @@ import { requestState } from './requestState.js';
10
10
  const subscriptions = {
11
11
  requestProgress: {
12
12
  subscribe: withFilter(
13
- (_, args, __, info) => {
13
+ (_, args, __, _info) => {
14
14
  const { requestIds } = args;
15
15
  for (const requestId of requestIds) {
16
16
  if (!requestState[requestId]) {
@@ -42,10 +42,6 @@ const generateUniqueFolderName = () => {
42
42
  return uniqueOutputPath;
43
43
  }
44
44
 
45
- const generateUniqueTempFileName = () => {
46
- return path.join(os.tmpdir(), uuidv4());
47
- }
48
-
49
45
  async function splitMediaFile(inputPath, chunkDurationInSeconds = 600) {
50
46
  try {
51
47
  const metadata = await ffmpegProbe(inputPath);
@@ -146,15 +142,6 @@ const processYoutubeUrl = async (url) => {
146
142
  return outputFileName;
147
143
  }
148
144
 
149
- function deleteFile(filePath) {
150
- try {
151
- fs.unlinkSync(filePath);
152
- console.log(`File ${filePath} cleaned successfully.`);
153
- } catch (error) {
154
- console.error(`Error deleting file ${filePath}:`, error);
155
- }
156
- }
157
-
158
145
  export {
159
146
  splitMediaFile, deleteTempPath, processYoutubeUrl, isValidYoutubeUrl
160
147
  };
package/lib/request.js CHANGED
@@ -64,7 +64,6 @@ const postWithMonitor = async (model, url, data, axiosConfigObj) => {
64
64
 
65
65
  const MAX_RETRY = 10;
66
66
  const postRequest = async ({ url, data, params, headers, cache }, model) => {
67
- let retry = 0;
68
67
  const errors = []
69
68
  for (let i = 0; i < MAX_RETRY; i++) {
70
69
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,6 +42,7 @@
42
42
  "compromise": "^14.8.1",
43
43
  "compromise-paragraphs": "^0.1.0",
44
44
  "convict": "^6.2.3",
45
+ "express": "^4.18.2",
45
46
  "fluent-ffmpeg": "^2.1.2",
46
47
  "form-data": "^4.0.0",
47
48
  "gpt-3-encoder": "^1.1.4",
@@ -51,12 +52,15 @@
51
52
  "handlebars": "^4.7.7",
52
53
  "keyv": "^4.5.2",
53
54
  "langchain": "^0.0.47",
55
+ "uuid": "^9.0.0",
54
56
  "ws": "^8.12.0",
55
57
  "ytdl-core": "^4.11.2"
56
58
  },
57
59
  "devDependencies": {
58
60
  "ava": "^5.2.0",
59
- "dotenv": "^16.0.3"
61
+ "dotenv": "^16.0.3",
62
+ "eslint": "^8.38.0",
63
+ "eslint-plugin-import": "^2.27.5"
60
64
  },
61
65
  "publishConfig": {
62
66
  "access": "restricted"
@@ -3,20 +3,24 @@
3
3
 
4
4
  // Import required modules
5
5
  import { OpenAI } from "langchain/llms";
6
- import { PromptTemplate } from "langchain/prompts";
7
- import { LLMChain, ConversationChain } from "langchain/chains";
6
+ //import { PromptTemplate } from "langchain/prompts";
7
+ //import { LLMChain, ConversationChain } from "langchain/chains";
8
8
  import { initializeAgentExecutor } from "langchain/agents";
9
9
  import { SerpAPI, Calculator } from "langchain/tools";
10
- import { BufferMemory } from "langchain/memory";
10
+ //import { BufferMemory } from "langchain/memory";
11
11
 
12
12
  export default {
13
13
 
14
14
  // Agent test case
15
- resolver: async (parent, args, contextValue, info) => {
15
+ resolver: async (parent, args, contextValue, _info) => {
16
16
 
17
17
  const { config } = contextValue;
18
+ const env = config.getEnv();
19
+
20
+ // example of reading from a predefined config variable
18
21
  const openAIApiKey = config.get('openaiApiKey');
19
- const serpApiKey = config.get('serpApiKey');
22
+ // example of reading straight from environment
23
+ const serpApiKey = env.SERPAPI_API_KEY;
20
24
 
21
25
  const model = new OpenAI({ openAIApiKey: openAIApiKey, temperature: 0 });
22
26
  const tools = [new SerpAPI( serpApiKey ), new Calculator()];
@@ -16,7 +16,7 @@ export default {
16
16
  },
17
17
 
18
18
  // Custom resolver to generate summaries by reprompting if they are too long or too short.
19
- resolver: async (parent, args, contextValue, info) => {
19
+ resolver: async (parent, args, contextValue, _info) => {
20
20
  const { config, pathway, requestState } = contextValue;
21
21
  const originalTargetLength = args.targetLength;
22
22