@saltcorn/large-language-model 1.0.3 → 1.0.4
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/generate.js +113 -0
- package/package.json +4 -4
- package/tests/llm.test.js +24 -1
package/generate.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
streamText,
|
|
19
19
|
tool,
|
|
20
20
|
jsonSchema,
|
|
21
|
+
Output,
|
|
21
22
|
embed,
|
|
22
23
|
embedMany,
|
|
23
24
|
experimental_transcribe,
|
|
@@ -518,6 +519,14 @@ const getCompletionAISDK = async (
|
|
|
518
519
|
});
|
|
519
520
|
});
|
|
520
521
|
}
|
|
522
|
+
if (body.response_format?.type === "json_schema" && !body.output) {
|
|
523
|
+
body.output = Output.object({
|
|
524
|
+
schema: jsonSchema(
|
|
525
|
+
lockDownSchema(body.response_format.json_schema.schema),
|
|
526
|
+
),
|
|
527
|
+
});
|
|
528
|
+
delete body.response_format;
|
|
529
|
+
}
|
|
521
530
|
|
|
522
531
|
const debugRequest = { ...body, model: use_model_name };
|
|
523
532
|
if (debugResult)
|
|
@@ -619,6 +628,17 @@ const getCompletionOpenAICompatible = async (
|
|
|
619
628
|
if (tool.function.required) tool.required = tool.function.required;
|
|
620
629
|
delete tool.function;
|
|
621
630
|
}
|
|
631
|
+
if (body.response_format?.type === "json_schema" && !body.text) {
|
|
632
|
+
body.text = {
|
|
633
|
+
format: {
|
|
634
|
+
type: "json_schema",
|
|
635
|
+
name: body.response_format.json_schema.name,
|
|
636
|
+
//strict: true,
|
|
637
|
+
schema: lockDownSchema(body.response_format.json_schema.schema),
|
|
638
|
+
},
|
|
639
|
+
};
|
|
640
|
+
delete body.response_format;
|
|
641
|
+
}
|
|
622
642
|
let newChat;
|
|
623
643
|
if (!appendToChat) {
|
|
624
644
|
newChat = [];
|
|
@@ -1148,6 +1168,99 @@ const getEmbeddingGoogleVertex = async (config, opts, oauth2Client) => {
|
|
|
1148
1168
|
return embeddings;
|
|
1149
1169
|
};
|
|
1150
1170
|
|
|
1171
|
+
function lockDownSchema(schema) {
|
|
1172
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
1173
|
+
|
|
1174
|
+
// Handle arrays (e.g., allOf, oneOf, anyOf, items as array)
|
|
1175
|
+
if (Array.isArray(schema)) {
|
|
1176
|
+
schema.forEach((item) => lockDownSchema(item));
|
|
1177
|
+
return schema;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// If this subschema defines properties, lock it down
|
|
1181
|
+
if (schema.properties) {
|
|
1182
|
+
schema.additionalProperties = false;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Recurse into properties
|
|
1186
|
+
if (schema.properties) {
|
|
1187
|
+
for (const key of Object.keys(schema.properties)) {
|
|
1188
|
+
lockDownSchema(schema.properties[key]);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// Recurse into additionalProperties if it's a schema (not just a boolean)
|
|
1193
|
+
if (
|
|
1194
|
+
schema.additionalProperties &&
|
|
1195
|
+
typeof schema.additionalProperties === "object"
|
|
1196
|
+
) {
|
|
1197
|
+
lockDownSchema(schema.additionalProperties);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// Recurse into patternProperties
|
|
1201
|
+
if (schema.patternProperties) {
|
|
1202
|
+
for (const key of Object.keys(schema.patternProperties)) {
|
|
1203
|
+
lockDownSchema(schema.patternProperties[key]);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// Recurse into composition keywords
|
|
1208
|
+
for (const keyword of ["allOf", "oneOf", "anyOf"]) {
|
|
1209
|
+
if (Array.isArray(schema[keyword])) {
|
|
1210
|
+
schema[keyword].forEach((sub) => lockDownSchema(sub));
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// Recurse into not
|
|
1215
|
+
if (schema.not) {
|
|
1216
|
+
lockDownSchema(schema.not);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Recurse into if/then/else
|
|
1220
|
+
for (const keyword of ["if", "then", "else"]) {
|
|
1221
|
+
if (schema[keyword]) {
|
|
1222
|
+
lockDownSchema(schema[keyword]);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Recurse into items (tuple or single schema)
|
|
1227
|
+
if (schema.items) {
|
|
1228
|
+
if (Array.isArray(schema.items)) {
|
|
1229
|
+
schema.items.forEach((item) => lockDownSchema(item));
|
|
1230
|
+
} else {
|
|
1231
|
+
lockDownSchema(schema.items);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// Recurse into prefixItems (Draft 2020-12)
|
|
1236
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
1237
|
+
schema.prefixItems.forEach((item) => lockDownSchema(item));
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Recurse into $defs / definitions
|
|
1241
|
+
for (const defsKey of ["$defs", "definitions"]) {
|
|
1242
|
+
if (schema[defsKey]) {
|
|
1243
|
+
for (const key of Object.keys(schema[defsKey])) {
|
|
1244
|
+
lockDownSchema(schema[defsKey][key]);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// Recurse into dependentSchemas
|
|
1250
|
+
if (schema.dependentSchemas) {
|
|
1251
|
+
for (const key of Object.keys(schema.dependentSchemas)) {
|
|
1252
|
+
lockDownSchema(schema.dependentSchemas[key]);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// Recurse into contains
|
|
1257
|
+
if (schema.contains) {
|
|
1258
|
+
lockDownSchema(schema.contains);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
return schema;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1151
1264
|
module.exports = {
|
|
1152
1265
|
getCompletion,
|
|
1153
1266
|
getEmbedding,
|
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.4",
|
|
4
4
|
"description": "Large language models and functionality for Saltcorn",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"@google-cloud/vertexai": "^1.9.3",
|
|
12
12
|
"@google-cloud/aiplatform": "^3.34.0",
|
|
13
13
|
"googleapis": "^144.0.0",
|
|
14
|
-
"ai": "
|
|
15
|
-
"@ai-sdk/openai": "
|
|
16
|
-
"@ai-sdk/anthropic": "
|
|
14
|
+
"ai": "6.0.116",
|
|
15
|
+
"@ai-sdk/openai": "3.0.41",
|
|
16
|
+
"@ai-sdk/anthropic": "3.0.58",
|
|
17
17
|
"openai": "6.16.0",
|
|
18
18
|
"@elevenlabs/elevenlabs-js": "2.31.0"
|
|
19
19
|
},
|
package/tests/llm.test.js
CHANGED
|
@@ -29,7 +29,6 @@ for (const nameconfig of require("./configs")) {
|
|
|
29
29
|
config,
|
|
30
30
|
);
|
|
31
31
|
});
|
|
32
|
-
|
|
33
32
|
it("generates text", async () => {
|
|
34
33
|
const answer = await getState().functions.llm_generate.run(
|
|
35
34
|
"What is the Capital of France?",
|
|
@@ -165,6 +164,28 @@ for (const nameconfig of require("./configs")) {
|
|
|
165
164
|
|
|
166
165
|
expect(cities1.length).toBe(12);
|
|
167
166
|
});
|
|
167
|
+
it("uses response_format", async () => {
|
|
168
|
+
const answer = await getState().functions.llm_generate.run(
|
|
169
|
+
"Generate a list of EU capitals in JSON format",
|
|
170
|
+
{
|
|
171
|
+
response_format: {
|
|
172
|
+
type: "json_schema",
|
|
173
|
+
json_schema: {
|
|
174
|
+
name: "cities",
|
|
175
|
+
schema: cities_tool.tools[0].function.parameters,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
expect(typeof answer).toBe("string");
|
|
181
|
+
console.log("answer", answer);
|
|
182
|
+
|
|
183
|
+
const json_answer = JSON.parse(answer);
|
|
184
|
+
|
|
185
|
+
expect(json_answer.cities.length).toBe(27);
|
|
186
|
+
expect(!!json_answer.cities[0].city_name).toBe(true);
|
|
187
|
+
expect(!!json_answer.cities[0].country_name).toBe(true);
|
|
188
|
+
});
|
|
168
189
|
if (name !== "AI SDK Anthropic")
|
|
169
190
|
it("gets embedding", async () => {
|
|
170
191
|
const v = await getState().functions.llm_embedding.run(
|
|
@@ -186,11 +207,13 @@ const cities_tool = {
|
|
|
186
207
|
description: "Provide a list of cities by country and city name",
|
|
187
208
|
parameters: {
|
|
188
209
|
type: "object",
|
|
210
|
+
required: ["cities"],
|
|
189
211
|
properties: {
|
|
190
212
|
cities: {
|
|
191
213
|
type: "array",
|
|
192
214
|
items: {
|
|
193
215
|
type: "object",
|
|
216
|
+
additionalProperties: false,
|
|
194
217
|
properties: {
|
|
195
218
|
country_name: {
|
|
196
219
|
type: "string",
|