@kumologica/sdk 4.0.0-beta8 → 4.0.0-beta9

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/LICENSE ADDED
@@ -0,0 +1,170 @@
1
+ Kumologica END USER LICENSE AGREEMENT
2
+
3
+ THIS AGREEMENT made BETWEEN
4
+ Kumologica ("Licensor")
5
+ - and -
6
+ You ("Licensee")
7
+ (the "Parties")
8
+
9
+ 1. Definitions and interpretation
10
+
11
+ 1.1. Definitions
12
+ In this Agreement (including the recitals) unless the context otherwise requires:
13
+ Agreement means this agreement and its schedule;
14
+ Product means Kumologica Designer as detailed in Schedule 1;
15
+ Terms means the terms and conditions of this Agreement.
16
+
17
+ 1.2. Interpretation
18
+ In this Agreement unless the context otherwise requires:
19
+ (a) headings are for convenience only and do not affect its interpretation and
20
+ construction;
21
+ (b) the singular includes the plural and vice versa;
22
+ (c) words importing a gender include other genders;
23
+ (d) where any word or phrase is defined, any other part of speech or other grammatical form of that word or phrase has a cognate meaning;
24
+ (e) a reference to any statute, proclamation, rule, code, regulation or ordinance
25
+ includes any amendment, consolidation, modification, re-enactment or reprint of it or any statute, proclamation, rule, code, regulation or ordinance replacing it;
26
+ (f) "includes" is not a word of limitation;
27
+ (g) a reference to any thing is a reference to the whole and each part of it;
28
+ (h) a reference to a group of persons is a reference to all of them collectively and to
29
+ each of them individually; and
30
+ (i) a reference to a document includes all amendments or supplements to, or
31
+ replacements or novation of, that document.
32
+
33
+ 2. Acceptance
34
+
35
+ 2.1. This Agreement is between you and the Licensor, and governs the Products made available to you.
36
+
37
+ 2.2. Upon:
38
+ (a) selecting the 'Accept' option;
39
+ (b) upon downloading, installing or using the Product (whichever comes first),
40
+
41
+ you are granted a revocable, non-transferable, non-exclusive and limited licence
42
+ ("Licence") strictly in accordance with the Terms of this Agreement.
43
+
44
+ 2.3. If you do not agree to the Terms of this Agreement, you must not install, use, or copy the Product.
45
+
46
+ 3. License grant
47
+
48
+ 3.1. This Agreement entitles you to
49
+ (a) install and use the Product on a single computer; or
50
+ (b) install and make an archival copy of the Product on a storage medium other than a hard drive, and may only be used for the reinstallation of the Product.
51
+
52
+ 3.2. This Agreement does not permit the installation of the Product
53
+ (a) on a system that allows shared use of applications;
54
+ (b) on a multi-user network; or
55
+ (c) on any configuration or system of computers that allow multiple users
56
+
57
+ unless you have a license for each separate computer on which the product is installed and run.
58
+
59
+ 3.3 The Product may be provided with certain Open Source Software ("OSS") as listed here: https://www.kumologica.com/legal/oss-dependencies.html. Your Right to use such OSS shall be governed by the applicable OSS license agreement instead of the terms of this agreement.
60
+
61
+ 4. Limitations
62
+
63
+ 4.1. Limitations on transfer
64
+ You may not assign your rights and obligations under this Agreement, or redistribute, encumber, sell, rent, lease, sublicense, or otherwise transfer your rights to the Products.
65
+
66
+ 4.2. Limitations on use
67
+
68
+ You may not:
69
+ unless you hold multiple, validly, licensed copies.
70
+
71
+ You may not:
72
+ (a) share the Licence, or contents of the Product, with others;
73
+ (b) copy, install or use the Product on any system with more than one computer; or
74
+ (c) permit the use, copying or installation of the Product by more than one user or on more than one computer
75
+
76
+ unless you hold multiple, validly, licensed copies.
77
+
78
+ You may not:
79
+ (d) decompile, "reverse engineer", disassemble, or otherwise attempt to derive the source code for the Product;
80
+ (e) re-sell, grant any rights under this EULA to any third party or lease, time-share, lend or rent the Product.
81
+ (f) post the Product or part of the Product on any website; or
82
+
83
+ 4.3. Limitations on derived works
84
+ You may not modify the Product, create derivative works based upon the Product, or use the Product to develop any product having the same primary function as the Product.
85
+
86
+ 4.4. Limitations on alteration
87
+
88
+ You may not:
89
+
90
+ (a) modify the Product or create any derivative work of the Product or its accompanying documentation. Derivative works include but are not limited to translations; or
91
+ (b) alter any files or libraries in any portion of the Product.
92
+
93
+ 4.5. Limitations on copying
94
+ You may not copy any part of the product except to the extent that the licensed use inherently demands the creation of a temporary copy stored in the computer memory and not permanently affixed on storage medium.
95
+
96
+ 4.6 Age restriction
97
+ You may not use the Product if you are under 18 years of age.
98
+
99
+ 5. Ownership
100
+ Kumologica or its subsidiaries, affiliates, and suppliers retain all rights, title and interest, including all copyright and intellectual property rights, in and to the Product and all copies thereof.
101
+
102
+ 6. Warranties and exclusions
103
+
104
+ 6.1. Provisions of the Competition and Consumer Act 2010 and other laws in force from time to time in Australia may imply guarantees, warranties, conditions, and impose obligations on Kumologica and its subsidiaries, affiliates, and suppliers ("Implied Terms"). If these Implied Terms apply, Kumologica's liability will be limited at its option to resupply, repair or replacement of the Product or the cost of such resupply, repair or replacement, to the
105
+ extent permitted by law.
106
+
107
+ 6.2. Unless otherwise explicitly agreed to in writing by Kumologica, subject to the Implied Terms, all representations, guarantees, conditions and warranties of any nature are expressly excluded.
108
+
109
+ 6.3. Nothing in this clause excludes, restricts or modifies your rights under an Implied Term.
110
+
111
+ 6.4. No user support or maintenance is provided as part of this Agreement.
112
+
113
+ 6.5. Third Party Services.
114
+
115
+ There may be third party software and/or services made available to Licensee by Licensor or third parties on or in connection with the Product, Kumologica marketplace or otherwise in connection with Licensee's use of the Services ("Third Party Service(s)").
116
+
117
+ Licensor provides no warranty, does not necessarily support and has not necessarily confirmed the validity, functionality or screened the content of such Third Party Services and any use is at Licensee's own risk.
118
+
119
+ The terms that apply to any Content Licensee provides to the Third Party Service are solely between Licensee and the Third Party Service and Licensee's use of the Third Party Services is subject to the Third Party Service's policies.
120
+
121
+ Any Third Party Service Licensee receives is governed by the terms provided by such third party and Licensee agrees to abide by those terms and conditions. Licensor has no obligations and/or liability with respect to such third party or the Third Party Services.
122
+
123
+ If Licensee have agreed to receive the Third Party Services, Licensee authorize Licensor to grant the provider of such Third Party Services with access to Licensee Content and/or Licensee Account to the extent required to provide the Third Party Services or for interoperability with the Third Party Services.
124
+
125
+ Third Party Services may be removed from or no longer available through the Product at any time.
126
+
127
+ 7. Exclusion of damages
128
+
129
+ The Product is provided by the Licensor and accepted by Licensee "as is". Subject to any Implied Term, Kumologica, its directors, officers, employees, or agents will not be liable to you or any other party for indirect, consequential, special, incidental, punitive or exemplary damages of any kind (including loss of revenues or profits or loss of business) arising in connection with these Terms, the Product, any software for the Product or any support services for the Product, whether based on contract, tort, statute, or any other legal theory.
130
+
131
+ The Licensor makes no warranty expressed and implied regarding the fitness of the Product for a particular purpose or that the Product will be suitable or appropriate for the specific requirements of the Licensee.
132
+
133
+ The Licensor does not warrant that use of the Product will be secure, uninterrupted, comply with regulatory requirements or error-free. The Licensee accepts that software in general is prone to bugs and flaws within an acceptable level as determined in the industry.
134
+
135
+ Licensee is solely responsible for (and that Licensor has no responsibility to you or to any third party for) the Application or any Content that Licensee create, transmit or display while using the Product and for the consequences of actions by doing so.
136
+
137
+ 8. Limitation of liability and remedies
138
+
139
+ To the extent that the applicable jurisdiction limits Kumologica's ability to disclaim any implied warranties, this disclaimer shall be effective to the maximum extent permitted.
140
+
141
+ 9. Licensee indemnity
142
+
143
+ You will indemnify Kumologica, its directors, officers, employees, agents and contractors in full against any liability, loss, damages, costs and expenses as a result of or in connection with your use of the Product, including but not limited to, any modification by you of the Product which causes the Product to infringe the intellectual property rights of a third party.
144
+
145
+ 10. Variation of Terms
146
+
147
+ Kumologica reserves the right to amend these Terms from time to time without notice to you and you will be subject to the Terms in force at the time you purchase the Licence for the Product or download the Product whichever is applicable.
148
+
149
+ 11. Termination
150
+
151
+ Without prejudice to any other rights, Kumologica may terminate this Agreement immediately and without further notice if you fail to comply with the Terms of this Agreement. In such event, you must destroy all copies of the Product.
152
+
153
+ 12. General provisions
154
+
155
+ 12.1. Any provision of, or the application of any provision of this Agreement, which is prohibited in any jurisdiction is, in that jurisdiction, ineffective only to the extent of that prohibition.
156
+
157
+ 12.2. Any provision of, or the application of any provision of this Agreement which is void, illegal or unenforceable in any jurisdiction does not affect the validity, legality or enforceability of that provision in any other jurisdiction or of the remaining provisions in that or any other jurisdiction
158
+
159
+ 12.3. The failure, delay, relaxation or indulgence on the part of a part in exercising, in part or whole, any power, right or remedy conferred upon that party by these Terms shall not operate as a waiver of that power, right, or remedy.
160
+
161
+ 12.4. This Agreement contains the entire Agreement between the Parties and supersedes any previous understandings, commitments or agreements, oral or written.
162
+
163
+ 12.5. If a clause is void, illegal or unenforceable, it may be severed without affecting the enforceability of the other provisions in this Agreement.
164
+
165
+ 12.6. This Agreement shall be governed by and construed in accordance with the laws of the State of New South Wales, Australia.
166
+
167
+ SCHEDULE 1 PRODUCT
168
+ Name of Product: Kumologica Designer and all containing components such as Kumologica Runtime and Kumologica Nodes.
169
+
170
+ This document is an adaptation of the End User License Agreement provided by Lawpath (lawpath.com.au). The original work has been modified.
@@ -349,7 +349,7 @@ function validate(argv) {
349
349
  }
350
350
  if (argv.runtime) {
351
351
  if (!argv.runtime.startsWith("nodejs")) {
352
- throw new Error (`Validation error: parameter 'runtime' must be one of supported nodejs values: nodejs|nodejs4.3|nodejs6.10|nodejs8.10|nodejs10.x|nodejs12.x|...`);
352
+ throw new Error (`Validation error: parameter 'runtime' must be one of supported nodejs values: nodejs20.x|nodejs22.x|...`);
353
353
  }
354
354
  }
355
355
  if (argv.architectures ) {
@@ -25,8 +25,10 @@
25
25
  const path = require("path");
26
26
  const fs = require("fs");
27
27
  const { codegen } = require("@kumologica/builder");
28
- const { DesignerServer } = require("../../src/server/DesignerServer");
28
+ const { TaskServer} = require("../../src/server/TaskServer");
29
+ //const { DesignerServer } = require("../../src/server/DesignerServer");
29
30
  const { logError, logNotice, logInfo, logFatal } = require("../utils/logger");
31
+ const { log } = require("console");
30
32
 
31
33
  exports.command = "run [project_directory]";
32
34
  exports.desc = `Start a local HTTP server to execute task and then stops HTTP server and exit.`;
@@ -53,6 +55,13 @@ exports.builder = (yargs) => {
53
55
  alias: "t",
54
56
  nargs: 1,
55
57
  });
58
+ yargs.option(`taskTimeout`, {
59
+ describe: "The timeout in seconds for task execution, default 1800 seconds (30 minutes).",
60
+ type: "number",
61
+ alias: "o",
62
+ default: 1800,
63
+ nargs: 1,
64
+ });
56
65
  yargs.option(`args`, {
57
66
  describe: "The list of arguments, either '-a one two three' or '-a one -a two -a three' ",
58
67
  type: "array",
@@ -60,19 +69,28 @@ exports.builder = (yargs) => {
60
69
  });
61
70
  };
62
71
 
63
- exports.handler = async ({ project_directory, loglevel, port, taskName, args }) => {
72
+ exports.handler = async ({ project_directory, loglevel, port, taskName, taskTimeout, args }) => {
64
73
  logNotice(`Starting HTTP Server...`);
65
74
  let projectDirectory = project_directory || process.cwd();
66
75
  // project_directory can point to a directory or a flow, so lets make sure that first the directory exists
67
76
  let absProjectDirectory;
68
77
  let exitCode = 1;
78
+ // Timeout in ms (default 10 minutes); make configurable via env or param if needed
79
+ const TIMEOUT_MS =
80
+ (typeof taskTimeout !== 'undefined' ? 1000 * taskTimeout :
81
+ process.env.TIMEOUT_MS ? Number(process.env.TIMEOUT_MS) :
82
+ 30 * 60 * 1000); // 30 minutes default in ms
83
+
84
+
85
+ logInfo(`TIMEOUT_MS: ${TIMEOUT_MS} ms`);
69
86
  try {
70
87
  absProjectDirectory = fs.realpathSync(projectDirectory);
71
88
  } catch (err) {
72
89
  logFatal(`Project not found: ${projectDirectory}`);
73
90
  process.exit(exitCode);
74
91
  }
75
-
92
+
93
+ let server;
76
94
  try {
77
95
  // Resolve the path to the flow path
78
96
  const [projectFlowDirname, projectFlowFullPath] = resolveProjectFlowPath(absProjectDirectory);
@@ -86,38 +104,59 @@ exports.handler = async ({ project_directory, loglevel, port, taskName, args })
86
104
  };
87
105
 
88
106
  // Start a server
89
- let server = new DesignerServer({
90
- flowPath: projectFlowFullPath,
91
- cliParams,
92
- });
107
+ server = new TaskServer(projectFlowFullPath, cliParams);
93
108
 
94
- await server.start();
109
+ await server.listen();
95
110
 
96
- const tName = taskName || process.env.taskName;
97
- const req = {
98
- method: 'POST',
99
- headers: {
100
- },
101
- body: JSON.stringify({args: args})
102
- };
111
+ const got = require('got');
103
112
 
104
- if (tName) {
105
- const url = `http://127.0.0.1:${port||1880}/__task__/${tName}`;
106
-
107
- const response = await fetch(url, req);
108
- const data = await response.text();
109
-
110
- logInfo("Status: " + response.status);
111
- logInfo("Message: " + data);
113
+ const tName = taskName || process.env.taskName;
114
+ const url = `http://127.0.0.1:${port || 1880}/__task__/${tName}`;
115
+
116
+ try {
117
+ const response = await got(url, {
118
+ method: 'POST',
119
+ headers: {
120
+ // Add any specific headers here if needed (e.g., 'Content-Type': 'application/json')
121
+ },
122
+ json: { args: args }, // Automatically stringifies to JSON and sets Content-Type
123
+ timeout: { request: TIMEOUT_MS } // Full request timeout (headers + body)
124
+ });
125
+
126
+ // Log success and response details
127
+ logInfo(`Request succeeded!`);
128
+ logInfo(`Status: ${response.statusCode}`);
129
+ logInfo(`Response body: ${response.body}`); // Assuming JSON response; parse if needed via response.json
130
+
131
+ exitCode = 0;
132
+
133
+ } catch (error) {
134
+ // Log failure details
135
+ logFatal(`Task Error: ${error.message}`);
136
+
137
+ // if (error.response) {
138
+ // logFatal('Status:', error.response.statusCode);
139
+ // logFatal('Response body:', error.response.body);
140
+ // }
141
+ if (error.code === 'ETIMEDOUT') {
142
+ logFatal(`Timeout exceeded (${TIMEOUT_MS / 1000}s)`);
143
+ }
144
+ }
145
+ } catch (error) {
146
+ logFatal(`Task failed!`);
147
+ logFatal(`Error: ${error.message}`);
148
+ }
112
149
 
150
+ try {
151
+ if (server) {
113
152
  await server.stop();
114
-
115
- exitCode = response.status >= 200 && response.status < 300 ? 0 : 1;
116
- process.exit(exitCode); // this is task, terminate after completion
117
- }
118
- } catch (e) {
119
- logFatal(e.message);
153
+ }
154
+ } catch (error) {
155
+ logError('Error stopping server:', error.message);
120
156
  }
157
+ logInfo('HTTP Server stopped.');
158
+
159
+ process.exit(exitCode); // Error exit
121
160
  };
122
161
 
123
162
  /**
@@ -117,7 +117,7 @@ async function runTestOnNewServer(flowFilePath, testcaseSelected, iterative) {
117
117
  logFatal(`No matched testcases found`);
118
118
  } else {
119
119
  const errors = await testSuiteRunner.runAll(testCasesSelected);
120
- process.exit(errors > 0);
120
+ process.exit(errors ? 1 : 0);
121
121
  }
122
122
 
123
123
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kumologica/sdk",
3
- "version": "4.0.0-beta8",
3
+ "version": "4.0.0-beta9",
4
4
  "productName": "Kumologica Designer",
5
5
  "copyright": "Copyright 2020 Kumologica Pty Ltd, All Rights Reserved.",
6
6
  "author": "Kumologica Pty Ltd <contact@kumologica.com>",
@@ -60,11 +60,12 @@
60
60
  "grunt": "grunt",
61
61
  "postinstall": "node scripts/welcome.js",
62
62
  "license-check": "npx license-checker --summary && npx license-compatibility-checker ",
63
- "license-report": "npx license-report --output=table"
63
+ "license-report": "npx license-report --omit=dev --output=table"
64
64
  },
65
- "license": "Apache-2.0",
65
+ "license": "Proprietary see LICENSE file",
66
66
  "dependencies": {
67
67
  "@apidevtools/swagger-parser": "^10.1.1",
68
+ "@aws-crypto/sha256-js": "^5.2.0",
68
69
  "@aws-sdk/client-api-gateway": "^3.556.0",
69
70
  "@aws-sdk/client-cloudformation": "^3.556.0",
70
71
  "@aws-sdk/client-cloudwatch-events": "^3.556.0",
@@ -81,13 +82,15 @@
81
82
  "@aws-sdk/client-sqs": "^3.556.0",
82
83
  "@aws-sdk/client-ssm": "^3.549.0",
83
84
  "@aws-sdk/client-sts": "^3.549.0",
84
- "@aws-sdk/credential-providers": "^3.556.0",
85
+ "@aws-sdk/credential-providers": "^3.943.0",
85
86
  "@aws-sdk/lib-dynamodb": "^3.549.0",
87
+ "@aws-sdk/protocol-http": "^3.370.0",
88
+ "@aws-sdk/signature-v4": "^3.370.0",
89
+ "@aws-sdk/types": "^3.936.0",
86
90
  "@electron/remote": "^2.0.8",
87
- "@kumologica/builder": "4.0.0-beta8",
88
- "@kumologica/devkit": "4.0.0-beta8",
89
- "@kumologica/runtime": "4.0.0-beta8",
90
- "adm-zip": "0.4.13",
91
+ "@kumologica/builder": "4.0.0-beta9",
92
+ "@kumologica/devkit": "4.0.0-beta9",
93
+ "@kumologica/runtime": "4.0.0-beta9",
91
94
  "ajv": "8.10.0",
92
95
  "archive-type": "^4.0.0",
93
96
  "basic-auth": "2.0.1",
@@ -179,7 +182,7 @@
179
182
  "license-compatibility-checker": "^0.3.4",
180
183
  "license-report": "^3.0.0",
181
184
  "mocha": "10.2.0",
182
- "node-sass": "8.0.0",
185
+ "node-sass": "9.0.0",
183
186
  "tslint": "^5.18.0",
184
187
  "typescript": "^3.5.3"
185
188
  },
@@ -0,0 +1,179 @@
1
+ const got = require('got');
2
+ const { fromIni } = require('@aws-sdk/credential-providers');
3
+ const { SignatureV4 } = require('@aws-sdk/signature-v4');
4
+ const { Sha256 } = require('@aws-crypto/sha256-js');
5
+ const { defaultProvider } = require('@aws-sdk/credential-providers');
6
+
7
+ const PROVIDER_CONFIG = {
8
+ openai: { baseUrl: 'https://api.openai.com/v1', headers: {} },
9
+ deepseek: { baseUrl: 'https://api.deepseek.com/v1', headers: {} },
10
+ xai: { baseUrl: 'https://api.x.ai/v1', headers: {} },
11
+ anthropic: { baseUrl: 'https://api.anthropic.com/v1', headers: { 'anthropic-version': '2023-06-01' } },
12
+ gemini: { baseUrl: 'https://generativelanguage.googleapis.com/v1beta', headers: {} },
13
+ bedrock: { type: 'bedrock-http' },
14
+ };
15
+
16
+ let signerCache = null; // Cache signer per region/profile to avoid recreating
17
+
18
+ async function getSigner({ region, profile }) {
19
+ const cacheKey = `${region || 'default'}:${profile || 'default'}`;
20
+ if (signerCache && signerCache.key === cacheKey) return signerCache.signer;
21
+
22
+ const credentials = profile ? fromIni({ profile }) : defaultProvider();
23
+
24
+ const signer = new SignatureV4({
25
+ credentials,
26
+ region: region || 'us-east-1',
27
+ service: 'bedrock',
28
+ sha256: Sha256,
29
+ });
30
+
31
+ signerCache = { key: cacheKey, signer };
32
+ return signer;
33
+ }
34
+
35
+ /**
36
+ * Unified chat — Bedrock now uses 100% public AWS SDK v3 SigV4
37
+ */
38
+ async function chatAi({
39
+ provider,
40
+ model,
41
+ apiKey,
42
+ region = process.env.AWS_REGION || 'us-east-1',
43
+ profile, // ← optional AWS profile name
44
+ messages,
45
+ systemPrompt,
46
+ params = {}
47
+ }) {
48
+ const { temperature = 0.7, maxTokens = 4096, timeout = 60000 } = params;
49
+ provider = provider.toLowerCase();
50
+
51
+ let url, headers = { 'Content-Type': 'application/json' }, body;
52
+
53
+ // ========================================
54
+ // BEDROCK — pure HTTP + official SigV4
55
+ // ========================================
56
+ if (provider === 'bedrock') {
57
+ const signer = await getSigner({ region, profile });
58
+
59
+ url = `https://bedrock-runtime.${region}.amazonaws.com/model/${model}/converse`;
60
+
61
+ const messagesNoSystem = messages.filter(m => m.role !== 'system');
62
+ const converseMessages = messagesNoSystem.map(m => ({
63
+ role: m.role === 'assistant' ? 'assistant' : 'user',
64
+ content: [{ text: m.content }]
65
+ }));
66
+
67
+ body = {
68
+ messages: converseMessages,
69
+ system: systemPrompt ? [{'text': systemPrompt}] : undefined,
70
+ inferenceConfig: { maxTokens, temperature },
71
+ };
72
+
73
+ const request = {
74
+ method: 'POST',
75
+ protocol: 'https:',
76
+ hostname: `bedrock-runtime.${region}.amazonaws.com`,
77
+ path: `/model/${model}/converse`,
78
+ headers: {
79
+ 'Host': `bedrock-runtime.${region}.amazonaws.com`,
80
+ 'Accept': 'application/json',
81
+ 'Content-Type': 'application/json',
82
+ },
83
+ body: JSON.stringify(body),
84
+ };
85
+
86
+ // Sign the request
87
+ const signed = await signer.sign(request);
88
+
89
+ headers = signed.headers;
90
+ url = `https://${signed.hostname}${signed.path}`;
91
+ }
92
+ // ========================================
93
+ // ALL OTHER PROVIDERS (unchanged)
94
+ // ========================================
95
+ else {
96
+ const config = PROVIDER_CONFIG[provider];
97
+ if (!config) throw new Error(`Unsupported provider: ${provider}`);
98
+
99
+ headers = { 'Content-Type': 'application/json', ...config.headers };
100
+
101
+ if (provider === 'gemini') {
102
+ // key in URL
103
+ } else if (provider === 'anthropic') {
104
+ headers['x-api-key'] = apiKey;
105
+ } else {
106
+ headers.Authorization = `Bearer ${apiKey}`;
107
+ }
108
+
109
+ let finalMessages = messages;
110
+ if (systemPrompt) {
111
+ finalMessages = provider === 'gemini'
112
+ ? [{ role: 'user', content: systemPrompt }, ...messages]
113
+ : [{ role: 'system', content: systemPrompt }, ...messages];
114
+ }
115
+
116
+ if (provider === 'anthropic') {
117
+ url = `${config.baseUrl}/messages`;
118
+ body = {
119
+ model,
120
+ messages: finalMessages.filter(m => m.role !== 'system'),
121
+ system: systemPrompt || undefined,
122
+ max_tokens: maxTokens,
123
+ temperature,
124
+ };
125
+ } else if (provider === 'gemini') {
126
+ url = `${config.baseUrl}/models/${model}:generateContent?key=${apiKey}`;
127
+ body = {
128
+ contents: finalMessages.map(m => ({
129
+ role: m.role === 'assistant' ? 'model' : 'user',
130
+ parts: [{ text: m.content }]
131
+ })),
132
+ generationConfig: { temperature, maxOutputTokens: maxTokens },
133
+ };
134
+ } else {
135
+ url = `${config.baseUrl}/chat/completions`;
136
+ body = { model, messages: finalMessages, temperature, max_tokens: maxTokens };
137
+ }
138
+ }
139
+
140
+ // ========================================
141
+ // SEND WITH GOT
142
+ // ========================================
143
+ const response = await got.post(url, {
144
+ headers,
145
+ json: provider === 'bedrock' ? undefined : body,
146
+ body: provider === 'bedrock' ? JSON.stringify(body) : undefined,
147
+ timeout: { request: timeout },
148
+ responseType: 'json',
149
+ });
150
+
151
+ // ========================================
152
+ // PARSE RESPONSE
153
+ // ========================================
154
+ if (provider === 'bedrock') {
155
+ const data = response.body;
156
+ const text = data.output?.message?.content?.[0]?.text || '';
157
+ return {
158
+ content: text,
159
+ usage: data.usage,
160
+ };
161
+ }
162
+
163
+ if (provider === 'anthropic') {
164
+ return { content: response.body.content?.[0]?.text || '', usage: response.body.usage };
165
+ }
166
+ if (provider === 'gemini') {
167
+ const c = response.body.candidates?.[0];
168
+ if (!c?.content?.parts?.[0]?.text) throw new Error('Empty response from Gemini');
169
+ return { content: c.content.parts[0].text };
170
+ }
171
+
172
+ const choice = response.body.choices?.[0];
173
+ return {
174
+ content: choice?.message?.content || '',
175
+ usage: response.body.usage,
176
+ };
177
+ }
178
+
179
+ module.exports = { chatAi };