@aj-archipelago/cortex 1.3.57 → 1.3.58

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/README.md CHANGED
@@ -561,6 +561,12 @@ Each model configuration can include:
561
561
  }
562
562
  ```
563
563
 
564
+ **Rate Limiting**: The `requestsPerSecond` parameter controls the rate limiting for each model endpoint. If not specified, Cortex defaults to **100 requests per second** per endpoint. This rate limiting is implemented using the Bottleneck library with a token bucket algorithm that includes:
565
+ - Minimum time between requests (`minTime`)
566
+ - Maximum concurrent requests (`maxConcurrent`)
567
+ - Token reservoir that refreshes every second
568
+ - Optional Redis clustering support when `storageConnectionString` is configured
569
+
564
570
  ### API Compatibility
565
571
 
566
572
  Cortex provides OpenAI-compatible REST endpoints that allow you to use various models through a standardized interface. When `enableRestEndpoints` is set to `true`, Cortex exposes the following endpoints:
package/config.js CHANGED
@@ -435,6 +435,17 @@ var config = convict({
435
435
  "maxReturnTokens": 4096,
436
436
  "supportsStreaming": true
437
437
  },
438
+ "apptek-translate": {
439
+ "type": "APPTEK-TRANSLATE",
440
+ "url": "{{APPTEK_API_ENDPOINT}}",
441
+ "headers": {
442
+ "x-token": "{{APPTEK_API_KEY}}",
443
+ "Accept": "application/json",
444
+ "Content-Type": "text/plain"
445
+ },
446
+ "requestsPerSecond": 10,
447
+ "maxTokenLength": 128000
448
+ },
438
449
  },
439
450
  env: 'CORTEX_MODELS'
440
451
  },
@@ -533,6 +544,17 @@ var config = convict({
533
544
  format: String,
534
545
  default: null,
535
546
  env: 'JINA_API_KEY'
547
+ },
548
+ apptekApiKey: {
549
+ format: String,
550
+ default: null,
551
+ env: 'APPTEK_API_KEY',
552
+ sensitive: true
553
+ },
554
+ apptekApiEndpoint: {
555
+ format: String,
556
+ default: null,
557
+ env: 'APPTEK_API_ENDPOINT'
536
558
  }
537
559
  });
538
560
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.3.57",
3
+ "version": "1.3.58",
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
  "private": false,
6
6
  "repository": {
@@ -1,11 +1,44 @@
1
1
  // Description: Translate text using AppTek's translation service
2
2
 
3
+ import logger from '../lib/logger.js';
4
+ const { callPathway } = await import('../lib/pathwayTools.js');
5
+
3
6
  export default {
4
7
  inputParameters: {
5
8
  from: 'auto', // Source language, 'auto' for automatic detection
6
9
  to: 'en', // Target language
7
10
  glossaryId: 'none', // Optional glossary ID
11
+ fallbackPathway: 'translate_groq', // Fallback pathway to use if AppTek fails
8
12
  },
9
13
  model: 'apptek-translate',
10
14
  timeout: 120,
15
+
16
+ executePathway: async ({args, runAllPrompts, resolver}) => {
17
+ const pathwayResolver = resolver;
18
+
19
+ try {
20
+ // Execute the primary AppTek translation
21
+ const result = await runAllPrompts(args);
22
+ return result;
23
+ } catch (error) {
24
+ // If AppTek translation fails, use the configured fallback pathway
25
+ const fallbackPathway = args.fallbackPathway || 'translate_groq';
26
+ logger.warn(`AppTek translation failed: ${error.message}. Falling back to ${fallbackPathway}.`);
27
+
28
+ try {
29
+ // Call the fallback pathway
30
+ const fallbackResult = await callPathway(fallbackPathway, {
31
+ text: args.text,
32
+ to: args.to || pathwayResolver.pathway.inputParameters.to,
33
+ }, pathwayResolver);
34
+
35
+ logger.verbose(`Successfully used ${fallbackPathway} as fallback`);
36
+ return fallbackResult;
37
+ } catch (fallbackError) {
38
+ // If even the fallback fails, log it and rethrow the original error
39
+ logger.error(`${fallbackPathway} fallback also failed: ${fallbackError.message}`);
40
+ throw error; // Throw the original AppTek error, not the fallback error
41
+ }
42
+ }
43
+ }
11
44
  }
@@ -7,17 +7,6 @@ const { callPathway } = await import('../../lib/pathwayTools.js');
7
7
  class ApptekTranslatePlugin extends ModelPlugin {
8
8
  constructor(pathway, model) {
9
9
  super(pathway, model);
10
-
11
- // Get API configuration from environment variables through the base class
12
- const apiEndpoint = this.environmentVariables.APPTEK_API_ENDPOINT;
13
- const apiKey = this.environmentVariables.APPTEK_API_KEY;
14
-
15
- if (!apiEndpoint || !apiKey) {
16
- throw new Error('AppTek API configuration missing. Please check APPTEK_API_ENDPOINT and APPTEK_API_KEY environment variables.');
17
- }
18
-
19
- this.apiEndpoint = apiEndpoint;
20
- this.apiKey = apiKey;
21
10
  }
22
11
 
23
12
  // Set up parameters specific to the AppTek Translate API
@@ -38,107 +27,73 @@ class ApptekTranslatePlugin extends ModelPlugin {
38
27
 
39
28
  // Execute the request to the AppTek Translate API
40
29
  async execute(text, parameters, prompt, cortexRequest) {
41
- try {
42
- const requestParameters = this.getRequestParameters(text, parameters, prompt);
43
- const { from = 'auto', to } = requestParameters.params;
44
-
45
- let sourceLanguage = from;
46
-
47
- // If source language is 'auto', detect it
48
- if (from === 'auto') {
49
- // Assuming requestParameters.data contains the text for language detection (usually same as 'text')
50
- const detectedLang = await this.detectLanguage(requestParameters.data);
51
- if (detectedLang) {
52
- sourceLanguage = detectedLang;
53
- requestParameters.params.from = detectedLang; // Update for subsequent use
54
- } else {
55
- const warnMsg = `ApptekTranslatePlugin: Language detection for 'auto' did not return a language. Proceeding with 'auto' or default.`;
56
- logger.warn(warnMsg)
57
- // sourceLanguage remains 'auto'. The comparison 'auto' === to will likely be false.
58
- }
59
- }
60
- // At this point, sourceLanguage is either the initially provided 'from' language,
61
- // or the detected language if 'from' was 'auto' and detection was successful.
62
- // requestParameters.params.from is also updated if detection occurred.
63
-
64
- // Check if source and target languages are the same
65
- // Ensure 'to' is a valid language string, not empty or null.
66
- if (to && sourceLanguage && sourceLanguage !== 'auto' && sourceLanguage === to) {
67
- const logMessage = `ApptekTranslatePlugin: Source language (${sourceLanguage}) matches target language (${to}). Skipping translation.`;
68
- logger.verbose(logMessage)
69
- // Return the original text. Ensure the return format matches what `this.executeRequest`
70
- // would return for a successful translation (e.g., string or object).
71
- // Assuming it's a string based on typical translation plugin behavior.
72
- return text;
30
+ const requestParameters = this.getRequestParameters(text, parameters, prompt);
31
+ const { from = 'auto', to } = requestParameters.params;
32
+
33
+ let sourceLanguage = from;
34
+
35
+ // If source language is 'auto', detect it
36
+ if (from === 'auto') {
37
+ const detectedLang = await this.detectLanguage(requestParameters.data, cortexRequest);
38
+ if (detectedLang) {
39
+ sourceLanguage = detectedLang;
40
+ requestParameters.params.from = detectedLang;
41
+ } else {
42
+ const warnMsg = `ApptekTranslatePlugin: Language detection for 'auto' did not return a language. Proceeding with 'auto' or default.`;
43
+ logger.warn(warnMsg)
73
44
  }
45
+ }
74
46
 
75
- // Construct the URL with the (potentially detected) source language and target language
76
- const langPair = `${requestParameters.params.from}-${to}`; // requestParameters.params.from is correctly set
77
- cortexRequest.url = `${this.apiEndpoint}/api/v2/quicktranslate/${langPair}`;
78
- cortexRequest.data = requestParameters.data;
79
- cortexRequest.method = 'POST';
80
- cortexRequest.headers = {
81
- 'x-token': this.apiKey,
82
- 'Accept': 'application/json',
83
- 'Content-Type': 'text/plain'
84
- };
85
-
86
- // Add glossary_id parameter if it's provided and not 'none'
87
- if (requestParameters.params.glossaryId && requestParameters.params.glossaryId !== 'none') {
88
- const url = new URL(cortexRequest.url);
89
- url.searchParams.append('glossary_id', requestParameters.params.glossaryId);
90
- cortexRequest.url = url.toString();
91
-
92
- const glossaryLogMessage = `ApptekTranslatePlugin: Using glossary ID: ${requestParameters.params.glossaryId}`;
93
- logger.verbose(glossaryLogMessage)
94
- }
47
+ // Check if source and target languages are the same
48
+ if (to && sourceLanguage && sourceLanguage !== 'auto' && sourceLanguage === to) {
49
+ const logMessage = `ApptekTranslatePlugin: Source language (${sourceLanguage}) matches target language (${to}). Skipping translation.`;
50
+ logger.verbose(logMessage)
51
+ return text;
52
+ }
95
53
 
96
- return this.executeRequest(cortexRequest);
97
- } catch (error) {
98
- // If AppTek translation fails, use Groq as a fallback
99
- logger.warn(`AppTek translation failed: ${error.message}. Falling back to Groq translate.`);
54
+ // Transform the base URL for translation
55
+ const langPair = `${requestParameters.params.from}-${to}`;
56
+ const translateUrl = `${cortexRequest.url}/api/v2/quicktranslate/${langPair}`;
57
+
58
+ // Set up the request using the standard pattern
59
+ cortexRequest.url = translateUrl;
60
+ cortexRequest.data = requestParameters.data;
61
+ cortexRequest.method = 'POST';
62
+
63
+ // Add glossary_id parameter if it's provided and not 'none'
64
+ if (requestParameters.params.glossaryId && requestParameters.params.glossaryId !== 'none') {
65
+ const url = new URL(cortexRequest.url);
66
+ url.searchParams.append('glossary_id', requestParameters.params.glossaryId);
67
+ cortexRequest.url = url.toString();
100
68
 
101
- try {
102
- // Import the callPathway function if it hasn't been imported at the top
103
- const { callPathway } = await import('../../lib/pathwayTools.js');
104
-
105
- // Call the Groq translate pathway as a fallback
106
- const result = await callPathway('translate_groq', {
107
- text,
108
- to: parameters.to || this.promptParameters.to,
109
- });
110
-
111
- logger.verbose('Successfully used Groq translate as fallback');
112
- return result;
113
- } catch (fallbackError) {
114
- // If even the fallback fails, log it and rethrow the original error
115
- logger.error(`Groq translate fallback also failed: ${fallbackError.message}`);
116
- throw fallbackError;
117
- }
69
+ const glossaryLogMessage = `ApptekTranslatePlugin: Using glossary ID: ${requestParameters.params.glossaryId}`;
70
+ logger.verbose(glossaryLogMessage)
118
71
  }
72
+
73
+ return this.executeRequest(cortexRequest);
119
74
  }
120
75
 
121
76
  // Detect language using AppTek's language detection API
122
- async detectLanguage(text) {
77
+ async detectLanguage(text, cortexRequest) {
123
78
  try {
79
+ // Transform the base URL for language detection
80
+ const detectUrl = `${cortexRequest.url}/api/v2/quick_lid`;
81
+
124
82
  // Make language detection request
125
- const resultResponse = await fetch(`${this.apiEndpoint}/api/v2/quick_lid`, {
83
+ const resultResponse = await fetch(detectUrl, {
126
84
  method: 'POST',
127
85
  headers: {
128
- 'x-token': this.apiKey,
129
- 'Accept': 'application/json',
130
- 'Content-Type': 'text/plain'
86
+ ...cortexRequest.headers
131
87
  },
132
88
  body: text
133
89
  });
134
90
 
135
91
  let detectedLanguage = null;
136
92
 
137
-
138
93
  if (resultResponse.status === 200) {
139
94
  const result = await resultResponse.text();
140
95
  detectedLanguage = result.split('\n')[0].split(';')[0];
141
- }else {
96
+ } else {
142
97
  logger.error(`Apptek Language detection failed with status: ${resultResponse.status}`);
143
98
  logger.debug({error: resultResponse, text})
144
99
  }
@@ -50,8 +50,6 @@ test.afterEach.always((t) => {
50
50
 
51
51
  test('constructor initializes with correct configuration', (t) => {
52
52
  const plugin = t.context.plugin;
53
- t.is(plugin.apiEndpoint, 'https://api.mock-apptek.com');
54
- t.is(plugin.apiKey, 'mock-api-key');
55
53
  t.is(plugin.config, config); // Verify that it uses the imported config
56
54
  t.is(plugin.pathwayPrompt, mockPathway.prompt);
57
55
  t.is(plugin.modelName, mockModel.name);
@@ -1,33 +1,19 @@
1
1
  import test from 'ava';
2
- import ApptekTranslatePlugin from '../../server/plugins/apptekTranslatePlugin.js';
3
- import CortexRequest from '../../lib/cortexRequest.js';
4
- import { createLimiter } from '../../lib/requestExecutor.js';
5
- import RequestMonitor from '../../lib/requestMonitor.js';
6
- import { Prompt } from '../../server/prompt.js';
7
- // Skip tests if API credentials are not available
8
- const skipIfNoCredentials = (t) => {
9
- if (!process.env.APPTEK_API_ENDPOINT || !process.env.APPTEK_API_KEY) {
10
- t.skip('AppTek API credentials not available. Set APPTEK_API_ENDPOINT and APPTEK_API_KEY environment variables.');
11
- return true;
12
- }
13
- return false;
14
- };
2
+ import serverFactory from '../../index.js';
15
3
 
16
- // Mock model configuration
17
- const mockModel = {
18
- name: 'apptek-translate',
19
- type: 'APPTEK-TRANSLATE',
20
- endpoints: [{
21
- name: 'apptek-translate',
22
- type: 'APPTEK-TRANSLATE',
23
- apiEndpoint: process.env.APPTEK_API_ENDPOINT,
24
- apiKey: process.env.APPTEK_API_KEY,
25
- }],
26
- };
4
+ let testServer;
27
5
 
28
- createLimiter(mockModel.endpoints[0], 'apptek-translate', 0);
29
- mockModel.endpoints[0].monitor = new RequestMonitor();
6
+ test.before(async () => {
7
+ const { server, startServer } = await serverFactory();
8
+ startServer && await startServer();
9
+ testServer = server;
10
+ });
30
11
 
12
+ test.after.always('cleanup', async () => {
13
+ if (testServer) {
14
+ await testServer.stop();
15
+ }
16
+ });
31
17
 
32
18
  // Test data for different languages
33
19
  const testCases = [
@@ -78,79 +64,159 @@ const testCases = [
78
64
  // Direct AppTek plugin tests
79
65
  testCases.forEach((testCase) => {
80
66
  test.serial(`AppTek Plugin: ${testCase.name}`, async (t) => {
81
- if (skipIfNoCredentials(t)) return;
82
-
83
- // Create pathway configuration
84
- const pathway = {
85
- name: 'translate_apptek',
86
- model: 'apptek-translate',
87
- prompt: new Prompt('{{text}}'),
88
- timeout: 1000000
89
- };
90
-
91
- // Create plugin instance
92
- const plugin = new ApptekTranslatePlugin(pathway, mockModel);
67
+ const response = await testServer.executeOperation({
68
+ query: 'query translate_apptek($text: String!, $from: String, $to: String) { translate_apptek(text: $text, from: $from, to: $to) { result } }',
69
+ variables: {
70
+ text: testCase.text,
71
+ from: testCase.from,
72
+ to: testCase.to
73
+ }
74
+ });
93
75
 
94
- // Set up parameters
95
- const parameters = {
96
- from: testCase.from,
97
- to: testCase.to
98
- };
76
+ t.is(response.body?.singleResult?.errors, undefined);
77
+ const result = response.body?.singleResult?.data?.translate_apptek?.result;
99
78
 
100
- try {
101
-
102
- const cortexRequest = new CortexRequest({pathwayResolver: {
103
- requestId: 'test-request-id',
104
- pathway: pathway,
105
- model: mockModel
106
- }});
107
- const result = await plugin.execute(testCase.text, parameters, pathway.prompt, cortexRequest);
108
-
109
- // Verify the result is a string
110
- t.is(typeof result, 'string', 'Result should be a string');
111
-
112
- // Verify the result is not empty
113
- t.true(result.length > 0, 'Result should not be empty');
114
-
115
- // Log the translation for manual verification
116
- console.log(`\n${testCase.name}:`);
117
- console.log(`Source (${testCase.from}): ${testCase.text}`);
118
- console.log(`Target (${testCase.to}): ${result}`);
119
-
120
- } catch (error) {
121
- t.fail(`Translation failed: ${error.message}`);
122
- }
79
+ // Verify the result is a string
80
+ t.is(typeof result, 'string', 'Result should be a string');
81
+
82
+ // Verify the result is not empty
83
+ t.true(result.length > 0, 'Result should not be empty');
84
+
85
+ // Log the translation for manual verification
86
+ console.log(`\n${testCase.name}:`);
87
+ console.log(`Source (${testCase.from}): ${testCase.text}`);
88
+ console.log(`Target (${testCase.to}): ${result}`);
123
89
  });
124
90
  });
125
91
 
92
+ // Test AppTek failure with GPT-4 Omni fallback
93
+ test.serial('AppTek Plugin: Force failure and test GPT-4 Omni fallback', async (t) => {
94
+ // Store original environment variables
95
+ const originalEndpoint = process.env.APPTEK_API_ENDPOINT;
96
+ const originalApiKey = process.env.APPTEK_API_KEY;
97
+
98
+ try {
99
+ // Force AppTek to fail by setting invalid endpoint
100
+ process.env.APPTEK_API_ENDPOINT = 'https://invalid-apptek-endpoint-that-will-fail.com';
101
+ process.env.APPTEK_API_KEY = 'invalid-api-key';
102
+
103
+ const testText = 'Hello, this is a test for fallback translation.';
104
+
105
+ const response = await testServer.executeOperation({
106
+ query: `
107
+ query translate_apptek_with_fallback($text: String!, $from: String, $to: String, $fallbackPathway: String) {
108
+ translate_apptek(text: $text, from: $from, to: $to, fallbackPathway: $fallbackPathway) {
109
+ result
110
+ }
111
+ }
112
+ `,
113
+ variables: {
114
+ text: testText,
115
+ from: 'en',
116
+ to: 'es',
117
+ fallbackPathway: 'translate_gpt4_omni'
118
+ }
119
+ });
120
+
121
+ // Check for errors in the response
122
+ t.is(response.body?.singleResult?.errors, undefined, 'Should not have GraphQL errors');
123
+
124
+ const result = response.body?.singleResult?.data?.translate_apptek?.result;
125
+
126
+ // Verify the result is a string
127
+ t.is(typeof result, 'string', 'Result should be a string');
128
+
129
+ // Verify the result is not empty
130
+ t.true(result.length > 0, 'Result should not be empty');
131
+
132
+ // Verify it's not the original text (translation should have occurred)
133
+ t.not(result, testText, 'Result should be translated, not the original text');
134
+
135
+ // Log the fallback translation for manual verification
136
+ console.log('\nAppTek Failure with GPT-4 Omni Fallback:');
137
+ console.log(`Source (en): ${testText}`);
138
+ console.log(`Target (es): ${result}`);
139
+ console.log('✅ AppTek failed as expected and GPT-4 Omni fallback worked!');
140
+
141
+ } finally {
142
+ // Restore original environment variables
143
+ if (originalEndpoint) {
144
+ process.env.APPTEK_API_ENDPOINT = originalEndpoint;
145
+ } else {
146
+ delete process.env.APPTEK_API_ENDPOINT;
147
+ }
148
+
149
+ if (originalApiKey) {
150
+ process.env.APPTEK_API_KEY = originalApiKey;
151
+ } else {
152
+ delete process.env.APPTEK_API_KEY;
153
+ }
154
+ }
155
+ });
126
156
 
127
- // Test language detection
128
- test.serial('AppTek Plugin: Language Detection', async (t) => {
129
- if (skipIfNoCredentials(t)) return;
130
-
131
- const pathway = { name: 'translate_apptek', model: 'apptek-translate' };
132
- const plugin = new ApptekTranslatePlugin(pathway, mockModel);
133
-
134
- const testTexts = [
135
- { text: 'Hello world', expectedLang: 'en' },
136
- { text: 'Hola mundo', expectedLang: 'es' },
137
- { text: 'مرحبا بالعالم', expectedLang: 'ar' }
138
- ];
139
-
140
- for (const testText of testTexts) {
141
- try {
142
- const detectedLang = await plugin.detectLanguage(testText.text);
143
-
144
- t.is(typeof detectedLang, 'string', 'Detected language should be a string');
145
- t.true(detectedLang.length > 0, 'Detected language should not be empty');
146
-
147
- console.log(`\nLanguage Detection:`);
148
- console.log(`Text: ${testText.text}`);
149
- console.log(`Detected: ${detectedLang}`);
150
- console.log(`Expected: ${testText.expectedLang}`);
151
-
152
- } catch (error) {
153
- t.fail(`Language detection failed: ${error.message}`);
157
+ // Test AppTek failure with default fallback (translate_groq)
158
+ test.skip('AppTek Plugin: Force failure and test default fallback', async (t) => {
159
+ // Set a longer timeout for this test since Groq might be slower
160
+ t.timeout(180000); // 3 minutes
161
+
162
+ // Store original environment variables
163
+ const originalEndpoint = process.env.APPTEK_API_ENDPOINT;
164
+ const originalApiKey = process.env.APPTEK_API_KEY;
165
+
166
+ try {
167
+ // Force AppTek to fail by setting invalid endpoint
168
+ process.env.APPTEK_API_ENDPOINT = 'https://invalid-apptek-endpoint-that-will-fail.com';
169
+ process.env.APPTEK_API_KEY = 'invalid-api-key';
170
+
171
+ const testText = 'Hello, this is a test for default fallback translation.';
172
+
173
+ const response = await testServer.executeOperation({
174
+ query: `
175
+ query translate_apptek_default_fallback($text: String!, $from: String, $to: String) {
176
+ translate_apptek(text: $text, from: $from, to: $to) {
177
+ result
178
+ }
179
+ }
180
+ `,
181
+ variables: {
182
+ text: testText,
183
+ from: 'en',
184
+ to: 'fr'
185
+ }
186
+ });
187
+
188
+ // Check for errors in the response
189
+ t.is(response.body?.singleResult?.errors, undefined, 'Should not have GraphQL errors');
190
+
191
+ const result = response.body?.singleResult?.data?.translate_apptek?.result;
192
+
193
+ // Verify the result is a string
194
+ t.is(typeof result, 'string', 'Result should be a string');
195
+
196
+ // Verify the result is not empty
197
+ t.true(result.length > 0, 'Result should not be empty');
198
+
199
+ // Verify it's not the original text (translation should have occurred)
200
+ t.not(result, testText, 'Result should be translated, not the original text');
201
+
202
+ // Log the fallback translation for manual verification
203
+ console.log('\nAppTek Failure with Default Fallback:');
204
+ console.log(`Source (en): ${testText}`);
205
+ console.log(`Target (fr): ${result}`);
206
+ console.log('✅ AppTek failed as expected and default fallback worked!');
207
+
208
+ } finally {
209
+ // Restore original environment variables
210
+ if (originalEndpoint) {
211
+ process.env.APPTEK_API_ENDPOINT = originalEndpoint;
212
+ } else {
213
+ delete process.env.APPTEK_API_ENDPOINT;
214
+ }
215
+
216
+ if (originalApiKey) {
217
+ process.env.APPTEK_API_KEY = originalApiKey;
218
+ } else {
219
+ delete process.env.APPTEK_API_KEY;
154
220
  }
155
221
  }
156
222
  });
@@ -115,3 +115,19 @@ test('resolver uses correct model', (t) => {
115
115
  const model = resolver.model;
116
116
  t.is(model.type, 'APPTEK-TRANSLATE');
117
117
  });
118
+
119
+ test('pathway has fallback pathway parameter', async (t) => {
120
+ // Import the actual pathway to test the new parameter
121
+ const pathway = await import('../pathways/translate_apptek.js');
122
+
123
+ t.truthy(pathway.default.inputParameters.fallbackPathway);
124
+ t.is(pathway.default.inputParameters.fallbackPathway, 'translate_groq');
125
+ });
126
+
127
+ test('pathway has executePathway function', async (t) => {
128
+ // Import the actual pathway to test the executePathway function
129
+ const pathway = await import('../pathways/translate_apptek.js');
130
+
131
+ t.truthy(pathway.default.executePathway);
132
+ t.is(typeof pathway.default.executePathway, 'function');
133
+ });