@saltcorn/large-language-model 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/constants.js +17 -2
- package/generate.js +74 -30
- package/index.js +47 -8
- package/package.json +2 -1
- package/tests/configs.js +10 -0
- package/tests/llm.test.js +14 -5
package/constants.js
CHANGED
|
@@ -7,9 +7,10 @@ const OPENAI_MODELS = [
|
|
|
7
7
|
"gpt-5",
|
|
8
8
|
"gpt-5-mini",
|
|
9
9
|
"gpt-5-nano",
|
|
10
|
-
"gpt-5.1",
|
|
10
|
+
"gpt-5.1",
|
|
11
11
|
"gpt-5.2",
|
|
12
12
|
"gpt-5.2-pro",
|
|
13
|
+
"gpt-5.4",
|
|
13
14
|
"o3",
|
|
14
15
|
"o3-mini",
|
|
15
16
|
"o3-pro",
|
|
@@ -21,6 +22,20 @@ const OPENAI_MODELS = [
|
|
|
21
22
|
"gpt-5.1-codex-max",
|
|
22
23
|
];
|
|
23
24
|
|
|
25
|
+
const NO_TEMP_MODELS = [
|
|
26
|
+
"o1",
|
|
27
|
+
"o3",
|
|
28
|
+
"o3-mini",
|
|
29
|
+
"o4-mini",
|
|
30
|
+
"gpt-5",
|
|
31
|
+
"gpt-5-nano",
|
|
32
|
+
"gpt-5-mini",
|
|
33
|
+
"gpt-5.1",
|
|
34
|
+
"gpt-5.1-codex",
|
|
35
|
+
"gpt-5.2",
|
|
36
|
+
"gpt-5.4",
|
|
37
|
+
];
|
|
38
|
+
|
|
24
39
|
// https://github.com/ollama/ollama/blob/main/docs/faq.md#where-are-models-stored
|
|
25
40
|
const OLLAMA_MODELS_PATH = {
|
|
26
41
|
Darwin: `${process.env.HOME}/.ollama/models`,
|
|
@@ -28,4 +43,4 @@ const OLLAMA_MODELS_PATH = {
|
|
|
28
43
|
Windows_NT: "C:\\Users\\%username%\\.ollama\\models.",
|
|
29
44
|
};
|
|
30
45
|
|
|
31
|
-
module.exports = { OPENAI_MODELS, OLLAMA_MODELS_PATH };
|
|
46
|
+
module.exports = { OPENAI_MODELS, OLLAMA_MODELS_PATH, NO_TEMP_MODELS };
|
package/generate.js
CHANGED
|
@@ -23,8 +23,10 @@ const {
|
|
|
23
23
|
experimental_transcribe,
|
|
24
24
|
} = require("ai");
|
|
25
25
|
const { createOpenAI } = require("@ai-sdk/openai");
|
|
26
|
+
const { createAnthropic } = require("@ai-sdk/anthropic");
|
|
26
27
|
const OpenAI = require("openai");
|
|
27
28
|
const { ElevenLabsClient } = require("@elevenlabs/elevenlabs-js");
|
|
29
|
+
const { NO_TEMP_MODELS } = require("./constants");
|
|
28
30
|
|
|
29
31
|
let ollamaMod;
|
|
30
32
|
if (features.esm_plugins) ollamaMod = require("ollama");
|
|
@@ -291,11 +293,64 @@ const toolResponse = async (
|
|
|
291
293
|
}
|
|
292
294
|
};
|
|
293
295
|
|
|
296
|
+
const addImageMesssage = async (
|
|
297
|
+
{ backend, apiKey, api_key, provider, ai_sdk_provider, responses_api },
|
|
298
|
+
opts,
|
|
299
|
+
) => {
|
|
300
|
+
let chat = opts.chat;
|
|
301
|
+
let result = opts.prompt;
|
|
302
|
+
//console.log("chat", JSON.stringify(chat, null, 2));
|
|
303
|
+
let imageurl = opts.prompt;
|
|
304
|
+
switch (opts.backend || backend) {
|
|
305
|
+
case "OpenAI":
|
|
306
|
+
{
|
|
307
|
+
const new_chat_item = responses_api
|
|
308
|
+
? {
|
|
309
|
+
role: "user",
|
|
310
|
+
content: [
|
|
311
|
+
{
|
|
312
|
+
type: "input_image",
|
|
313
|
+
image_url: imageurl,
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
}
|
|
317
|
+
: {
|
|
318
|
+
role: "user",
|
|
319
|
+
content: [
|
|
320
|
+
{
|
|
321
|
+
type: "image_url",
|
|
322
|
+
image_url: {
|
|
323
|
+
url: imageurl,
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
chat.push(new_chat_item);
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
case "AI SDK":
|
|
333
|
+
chat.push({
|
|
334
|
+
role: "user",
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
type: "image",
|
|
338
|
+
image: imageurl,
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
294
348
|
const getCompletion = async (config, opts) => {
|
|
295
349
|
switch (config.backend) {
|
|
296
350
|
case "AI SDK":
|
|
297
351
|
return await getCompletionAISDK(
|
|
298
352
|
{
|
|
353
|
+
...config,
|
|
299
354
|
provider: config.ai_sdk_provider,
|
|
300
355
|
apiKey: config.api_key,
|
|
301
356
|
model: opts?.model || config.model,
|
|
@@ -368,18 +423,28 @@ const getCompletion = async (config, opts) => {
|
|
|
368
423
|
}
|
|
369
424
|
};
|
|
370
425
|
|
|
371
|
-
const getAiSdkModel = ({
|
|
426
|
+
const getAiSdkModel = ({
|
|
427
|
+
provider,
|
|
428
|
+
api_key,
|
|
429
|
+
model_name,
|
|
430
|
+
anthropic_api_key,
|
|
431
|
+
}) => {
|
|
372
432
|
switch (provider) {
|
|
373
433
|
case "OpenAI":
|
|
374
434
|
const openai = createOpenAI({ apiKey: api_key });
|
|
375
435
|
return openai(model_name);
|
|
436
|
+
case "Anthropic":
|
|
437
|
+
const anthropic = createAnthropic({
|
|
438
|
+
apiKey: anthropic_api_key,
|
|
439
|
+
});
|
|
440
|
+
return anthropic(model_name);
|
|
376
441
|
default:
|
|
377
442
|
throw new Error("Provider not found: " + provider);
|
|
378
443
|
}
|
|
379
444
|
};
|
|
380
445
|
|
|
381
446
|
const getCompletionAISDK = async (
|
|
382
|
-
{ apiKey, model, provider, temperature },
|
|
447
|
+
{ apiKey, model, provider, temperature, anthropic_api_key },
|
|
383
448
|
{
|
|
384
449
|
systemPrompt,
|
|
385
450
|
prompt,
|
|
@@ -397,6 +462,7 @@ const getCompletionAISDK = async (
|
|
|
397
462
|
model_name: use_model_name,
|
|
398
463
|
api_key: api_key || apiKey,
|
|
399
464
|
provider,
|
|
465
|
+
anthropic_api_key,
|
|
400
466
|
});
|
|
401
467
|
const modifyChat = (chat) => {
|
|
402
468
|
const f = (c) => {
|
|
@@ -429,27 +495,15 @@ const getCompletionAISDK = async (
|
|
|
429
495
|
if (appendToChat && chat && prompt) {
|
|
430
496
|
chat.push({ role: "user", content: prompt });
|
|
431
497
|
}
|
|
432
|
-
if (
|
|
498
|
+
if (NO_TEMP_MODELS.includes(use_model_name)) {
|
|
499
|
+
delete body.temperature;
|
|
500
|
+
} else if (rest.temperature || temperature) {
|
|
433
501
|
const str_or_num = rest.temperature || temperature;
|
|
434
502
|
body.temperature = +str_or_num;
|
|
435
503
|
} else if (rest.temperature === null) {
|
|
436
504
|
delete body.temperature;
|
|
437
505
|
} else if (typeof temperature === "undefined") {
|
|
438
|
-
if (
|
|
439
|
-
![
|
|
440
|
-
"o1",
|
|
441
|
-
"o3",
|
|
442
|
-
"o3-mini",
|
|
443
|
-
"o4-mini",
|
|
444
|
-
"gpt-5",
|
|
445
|
-
"gpt-5-nano",
|
|
446
|
-
"gpt-5-mini",
|
|
447
|
-
"gpt-5.1",
|
|
448
|
-
"gpt-5.1-codex",
|
|
449
|
-
"gpt-5.2",
|
|
450
|
-
].includes(use_model_name)
|
|
451
|
-
)
|
|
452
|
-
body.temperature = 0.7;
|
|
506
|
+
if (!NO_TEMP_MODELS.includes(use_model_name)) body.temperature = 0.7;
|
|
453
507
|
}
|
|
454
508
|
if (body.tools) {
|
|
455
509
|
const prevTools = [...body.tools];
|
|
@@ -546,18 +600,7 @@ const getCompletionOpenAICompatible = async (
|
|
|
546
600
|
} else if (rest.temperature === null) {
|
|
547
601
|
delete body.temperature;
|
|
548
602
|
} else if (typeof temperature === "undefined") {
|
|
549
|
-
if (
|
|
550
|
-
![
|
|
551
|
-
"o1",
|
|
552
|
-
"o3",
|
|
553
|
-
"o3-mini",
|
|
554
|
-
"o4-mini",
|
|
555
|
-
"gpt-5",
|
|
556
|
-
"gpt-5-nano",
|
|
557
|
-
"gpt-5-mini",
|
|
558
|
-
].includes(use_model)
|
|
559
|
-
)
|
|
560
|
-
body.temperature = 0.7;
|
|
603
|
+
if (!NO_TEMP_MODELS.includes(use_model)) body.temperature = 0.7;
|
|
561
604
|
}
|
|
562
605
|
if (rest.streamCallback && global.fetch) {
|
|
563
606
|
body.stream = true;
|
|
@@ -1110,4 +1153,5 @@ module.exports = {
|
|
|
1110
1153
|
getImageGeneration,
|
|
1111
1154
|
getAudioTranscription,
|
|
1112
1155
|
toolResponse,
|
|
1156
|
+
addImageMesssage,
|
|
1113
1157
|
};
|
package/index.js
CHANGED
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
getEmbedding,
|
|
12
12
|
getImageGeneration,
|
|
13
13
|
getAudioTranscription,
|
|
14
|
-
toolResponse
|
|
14
|
+
toolResponse,
|
|
15
|
+
addImageMesssage,
|
|
15
16
|
} = require("./generate");
|
|
16
17
|
const { OPENAI_MODELS } = require("./constants.js");
|
|
17
18
|
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
@@ -81,7 +82,7 @@ ${domReady(`
|
|
|
81
82
|
required: true,
|
|
82
83
|
showIf: { backend: "AI SDK" },
|
|
83
84
|
attributes: {
|
|
84
|
-
options: ["OpenAI"],
|
|
85
|
+
options: ["OpenAI", "Anthropic"],
|
|
85
86
|
},
|
|
86
87
|
},
|
|
87
88
|
{
|
|
@@ -92,6 +93,14 @@ ${domReady(`
|
|
|
92
93
|
fieldview: "password",
|
|
93
94
|
showIf: { backend: "AI SDK", ai_sdk_provider: "OpenAI" },
|
|
94
95
|
},
|
|
96
|
+
{
|
|
97
|
+
name: "anthropic_api_key",
|
|
98
|
+
label: "API key",
|
|
99
|
+
type: "String",
|
|
100
|
+
required: true,
|
|
101
|
+
fieldview: "password",
|
|
102
|
+
showIf: { backend: "AI SDK", ai_sdk_provider: "Anthropic" },
|
|
103
|
+
},
|
|
95
104
|
{
|
|
96
105
|
name: "model",
|
|
97
106
|
label: "Model", //gpt-3.5-turbo
|
|
@@ -99,7 +108,17 @@ ${domReady(`
|
|
|
99
108
|
required: true,
|
|
100
109
|
showIf: { backend: "AI SDK" },
|
|
101
110
|
attributes: {
|
|
102
|
-
calcOptions: [
|
|
111
|
+
calcOptions: [
|
|
112
|
+
"ai_sdk_provider",
|
|
113
|
+
{
|
|
114
|
+
OpenAI: OPENAI_MODELS,
|
|
115
|
+
Anthropic: [
|
|
116
|
+
"claude-opus-4-6",
|
|
117
|
+
"claude-sonnet-4-6",
|
|
118
|
+
"claude-haiku-4-5",
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
],
|
|
103
122
|
},
|
|
104
123
|
},
|
|
105
124
|
{
|
|
@@ -117,6 +136,14 @@ ${domReady(`
|
|
|
117
136
|
"text-embedding-3-large",
|
|
118
137
|
"text-embedding-ada-002",
|
|
119
138
|
],
|
|
139
|
+
Anthropic: [
|
|
140
|
+
"voyage-3-large",
|
|
141
|
+
"voyage-3",
|
|
142
|
+
"voyage-3-lite",
|
|
143
|
+
"voyage-code-3",
|
|
144
|
+
"voyage-finance-2",
|
|
145
|
+
"voyage-law-2",
|
|
146
|
+
],
|
|
120
147
|
},
|
|
121
148
|
],
|
|
122
149
|
},
|
|
@@ -422,14 +449,26 @@ const functions = (config) => {
|
|
|
422
449
|
{ name: "options", type: "JSON", tstype: "any", required: true },
|
|
423
450
|
],
|
|
424
451
|
},
|
|
425
|
-
|
|
426
|
-
run: async (prompt, opts) => {
|
|
427
|
-
|
|
428
|
-
|
|
452
|
+
llm_add_message: {
|
|
453
|
+
run: async (what, prompt, opts) => {
|
|
454
|
+
switch (what) {
|
|
455
|
+
case "tool_response":
|
|
456
|
+
return await toolResponse(config, { prompt, ...opts });
|
|
457
|
+
case "image":
|
|
458
|
+
return await addImageMesssage(config, { prompt, ...opts });
|
|
459
|
+
default:
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
429
462
|
},
|
|
430
463
|
isAsync: true,
|
|
431
|
-
description: "Insert
|
|
464
|
+
description: "Insert a tool response or an image in a chat",
|
|
432
465
|
arguments: [
|
|
466
|
+
{
|
|
467
|
+
name: "what",
|
|
468
|
+
type: "String",
|
|
469
|
+
tstype: '"tool_response"|"image"',
|
|
470
|
+
required: true,
|
|
471
|
+
},
|
|
433
472
|
{ name: "prompt", type: "String", required: true },
|
|
434
473
|
{ name: "options", type: "JSON", tstype: "any" },
|
|
435
474
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/large-language-model",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Large language models and functionality for Saltcorn",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"googleapis": "^144.0.0",
|
|
14
14
|
"ai": "5.0.44",
|
|
15
15
|
"@ai-sdk/openai": "2.0.30",
|
|
16
|
+
"@ai-sdk/anthropic": "2.0.70",
|
|
16
17
|
"openai": "6.16.0",
|
|
17
18
|
"@elevenlabs/elevenlabs-js": "2.31.0"
|
|
18
19
|
},
|
package/tests/configs.js
CHANGED
|
@@ -31,4 +31,14 @@ module.exports = [
|
|
|
31
31
|
temperature: 0.7,
|
|
32
32
|
ai_sdk_provider: "OpenAI",
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
name: "AI SDK Anthropic",
|
|
36
|
+
model: "claude-sonnet-4-6",
|
|
37
|
+
api_key: process.env.ANTHROPIC_API_KEY,
|
|
38
|
+
backend: "AI SDK",
|
|
39
|
+
embed_model: "text-embedding-3-small",
|
|
40
|
+
image_model: "gpt-image-1",
|
|
41
|
+
temperature: 0.7,
|
|
42
|
+
ai_sdk_provider: "Anthropic",
|
|
43
|
+
},
|
|
34
44
|
];
|
package/tests/llm.test.js
CHANGED
|
@@ -133,7 +133,12 @@ for (const nameconfig of require("./configs")) {
|
|
|
133
133
|
const chat = [];
|
|
134
134
|
const answer = await getState().functions.llm_generate.run(
|
|
135
135
|
"Generate a list of EU capitals in a structured format using the provided tool",
|
|
136
|
-
{
|
|
136
|
+
{
|
|
137
|
+
chat,
|
|
138
|
+
appendToChat: true,
|
|
139
|
+
...cities_tool,
|
|
140
|
+
//streamCallback() {}
|
|
141
|
+
},
|
|
137
142
|
);
|
|
138
143
|
expect(typeof answer).toBe("object");
|
|
139
144
|
|
|
@@ -142,10 +147,14 @@ for (const nameconfig of require("./configs")) {
|
|
|
142
147
|
const cities = tc.input.cities;
|
|
143
148
|
expect(cities.length).toBe(27);
|
|
144
149
|
|
|
145
|
-
await getState().functions.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
await getState().functions.llm_add_message.run(
|
|
151
|
+
"tool_response",
|
|
152
|
+
"List received",
|
|
153
|
+
{
|
|
154
|
+
chat,
|
|
155
|
+
tool_call: tc,
|
|
156
|
+
},
|
|
157
|
+
);
|
|
149
158
|
|
|
150
159
|
const answer1 = await getState().functions.llm_generate.run(
|
|
151
160
|
"Make the same list in a structured format using the provided tool but for the original 12 member countries of the EU",
|