@saltcorn/large-language-model 1.0.8 → 1.0.10
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 +44 -8
- package/index.js +1 -0
- package/package.json +1 -1
- package/tests/enjoy.png +0 -0
- package/tests/llm.test.js +25 -6
package/generate.js
CHANGED
|
@@ -331,12 +331,17 @@ const addImageMesssage = async (
|
|
|
331
331
|
}
|
|
332
332
|
break;
|
|
333
333
|
case "AI SDK":
|
|
334
|
+
let aisdk_image;
|
|
335
|
+
if (imageurl.startsWith("data:") && imageurl.includes("base64,")) {
|
|
336
|
+
const [_pre, b64] = imageurl.split("base64,");
|
|
337
|
+
aisdk_image = b64;
|
|
338
|
+
} else aisdk_image = imageurl;
|
|
334
339
|
chat.push({
|
|
335
340
|
role: "user",
|
|
336
341
|
content: [
|
|
337
342
|
{
|
|
338
343
|
type: "image",
|
|
339
|
-
image:
|
|
344
|
+
image: aisdk_image,
|
|
340
345
|
},
|
|
341
346
|
],
|
|
342
347
|
});
|
|
@@ -548,7 +553,12 @@ const getCompletionAISDK = async (
|
|
|
548
553
|
const debugRequest = { ...body, model: use_model_name };
|
|
549
554
|
if (debugResult)
|
|
550
555
|
console.log("AI SDK request", JSON.stringify(debugRequest, null, 2));
|
|
551
|
-
getState().log(
|
|
556
|
+
getState().log(
|
|
557
|
+
6,
|
|
558
|
+
`AI SDK request`,
|
|
559
|
+
{ tools: Object.keys(debugRequest.tools || {}), model: debugRequest.model },
|
|
560
|
+
JSON.stringify(debugRequest.messages, null, 2),
|
|
561
|
+
);
|
|
552
562
|
if (debugCollector) debugCollector.request = debugRequest;
|
|
553
563
|
const reqTimeStart = Date.now();
|
|
554
564
|
|
|
@@ -570,15 +580,18 @@ const getCompletionAISDK = async (
|
|
|
570
580
|
chat.push(...results.response.messages);
|
|
571
581
|
}
|
|
572
582
|
|
|
573
|
-
if (debugResult)
|
|
574
|
-
console.log("AI SDK response", JSON.stringify(results, null, 2));
|
|
575
|
-
else getState().log(6, `AI SDK response ${JSON.stringify(results)}`);
|
|
576
583
|
if (debugCollector) {
|
|
577
584
|
debugCollector.response = results;
|
|
578
585
|
debugCollector.response_time_ms = Date.now() - reqTimeStart;
|
|
579
586
|
}
|
|
580
587
|
const allToolCalls = (await results.steps).flatMap((step) => step.toolCalls);
|
|
581
|
-
|
|
588
|
+
if (debugResult)
|
|
589
|
+
console.log("AI SDK response", JSON.stringify(results, null, 2));
|
|
590
|
+
else
|
|
591
|
+
getState().log(6, `AI SDK response`, {
|
|
592
|
+
text: results.text,
|
|
593
|
+
tool_calls: allToolCalls,
|
|
594
|
+
});
|
|
582
595
|
if (allToolCalls.length) {
|
|
583
596
|
return {
|
|
584
597
|
tool_calls: allToolCalls,
|
|
@@ -833,10 +846,29 @@ const getCompletionOpenAICompatible = async (
|
|
|
833
846
|
if (streamToolCalls) debugCollector.response = streamToolCalls;
|
|
834
847
|
debugCollector.response_time_ms = Date.now() - reqTimeStart;
|
|
835
848
|
}
|
|
849
|
+
if (appendToChat && chat && streamToolCalls) {
|
|
850
|
+
chat.push({
|
|
851
|
+
role: "assistant",
|
|
852
|
+
content: streamParts.join("") || null,
|
|
853
|
+
tool_calls: streamToolCalls.tool_calls,
|
|
854
|
+
});
|
|
855
|
+
} else if (appendToChat && chat && streamParts.length > 0) {
|
|
856
|
+
chat.push({ role: "assistant", content: streamParts.join("") });
|
|
857
|
+
}
|
|
836
858
|
return streamToolCalls
|
|
837
859
|
? {
|
|
838
860
|
content: streamParts.join(""),
|
|
839
861
|
tool_calls: streamToolCalls.tool_calls,
|
|
862
|
+
hasToolCalls: streamToolCalls.tool_calls?.length,
|
|
863
|
+
getToolCalls() {
|
|
864
|
+
return streamToolCalls.tool_calls.map((tc) => ({
|
|
865
|
+
tool_name: tc.function.name,
|
|
866
|
+
input: tc.function.arguments
|
|
867
|
+
? JSON.parse(tc.function.arguments)
|
|
868
|
+
: {},
|
|
869
|
+
tool_call_id: tc.id,
|
|
870
|
+
}));
|
|
871
|
+
},
|
|
840
872
|
}
|
|
841
873
|
: streamParts.join("");
|
|
842
874
|
}
|
|
@@ -890,7 +922,9 @@ const getCompletionOpenAICompatible = async (
|
|
|
890
922
|
getToolCalls() {
|
|
891
923
|
return tool_calls.map((tc) => ({
|
|
892
924
|
tool_name: tc.function.name,
|
|
893
|
-
input:
|
|
925
|
+
input: tc.function.arguments
|
|
926
|
+
? JSON.parse(tc.function.arguments)
|
|
927
|
+
: {},
|
|
894
928
|
tool_call_id: tc.call_id,
|
|
895
929
|
}));
|
|
896
930
|
},
|
|
@@ -905,7 +939,9 @@ const getCompletionOpenAICompatible = async (
|
|
|
905
939
|
getToolCalls() {
|
|
906
940
|
return results?.choices?.[0]?.message?.tool_calls.map((tc) => ({
|
|
907
941
|
tool_name: tc.function.name,
|
|
908
|
-
input:
|
|
942
|
+
input: tc.function.arguments
|
|
943
|
+
? JSON.parse(tc.function.arguments)
|
|
944
|
+
: {},
|
|
909
945
|
tool_call_id: tc.id,
|
|
910
946
|
}));
|
|
911
947
|
},
|
package/index.js
CHANGED
package/package.json
CHANGED
package/tests/enjoy.png
ADDED
|
Binary file
|
package/tests/llm.test.js
CHANGED
|
@@ -2,7 +2,8 @@ const { getState } = require("@saltcorn/data/db/state");
|
|
|
2
2
|
const View = require("@saltcorn/data/models/view");
|
|
3
3
|
const Table = require("@saltcorn/data/models/table");
|
|
4
4
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
5
|
-
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
6
7
|
const { mockReqRes } = require("@saltcorn/data/tests/mocks");
|
|
7
8
|
const { afterAll, beforeAll, describe, it, expect } = require("@jest/globals");
|
|
8
9
|
|
|
@@ -97,14 +98,14 @@ for (const nameconfig of require("./configs")) {
|
|
|
97
98
|
});
|
|
98
99
|
it("uses tools", async () => {
|
|
99
100
|
const answer = await getState().functions.llm_generate.run(
|
|
100
|
-
"Generate a list of all the EU capitals in a structured format using the
|
|
101
|
+
"Generate a list of all the EU capitals in a structured format using the cities tool",
|
|
101
102
|
cities_tool,
|
|
102
103
|
);
|
|
103
104
|
expect(typeof answer).toBe("object");
|
|
104
|
-
const cities = answer.
|
|
105
|
-
? answer.tool_calls[0].input?.cities
|
|
106
|
-
: JSON.parse(answer.tool_calls[0].function.arguments).cities;
|
|
105
|
+
const cities = answer.getToolCalls()[0].input?.cities;
|
|
107
106
|
expect(cities.length).toBe(27);
|
|
107
|
+
expect(typeof cities[0].country_name).toBe("string");
|
|
108
|
+
expect(typeof cities[0].city_name).toBe("string");
|
|
108
109
|
});
|
|
109
110
|
it("appends to chat history", async () => {
|
|
110
111
|
const chat = [];
|
|
@@ -131,7 +132,7 @@ for (const nameconfig of require("./configs")) {
|
|
|
131
132
|
it("tool use sequence", async () => {
|
|
132
133
|
const chat = [];
|
|
133
134
|
const answer = await getState().functions.llm_generate.run(
|
|
134
|
-
"Generate a list of all the EU capitals in a structured format using the
|
|
135
|
+
"Generate a list of all the EU capitals in a structured format using the cities tool",
|
|
135
136
|
{
|
|
136
137
|
chat,
|
|
137
138
|
appendToChat: true,
|
|
@@ -185,6 +186,24 @@ for (const nameconfig of require("./configs")) {
|
|
|
185
186
|
expect(!!json_answer.cities[0].city_name).toBe(true);
|
|
186
187
|
expect(!!json_answer.cities[0].country_name).toBe(true);
|
|
187
188
|
});
|
|
189
|
+
it("reads images", async () => {
|
|
190
|
+
const chat = [];
|
|
191
|
+
const b64 = fs
|
|
192
|
+
.readFileSync(path.join(__dirname, "enjoy.png"))
|
|
193
|
+
.toString("base64"),
|
|
194
|
+
imageurl = `data:image/png;base64,${b64}`;
|
|
195
|
+
await getState().functions.llm_add_message.run("image", imageurl, {
|
|
196
|
+
chat,
|
|
197
|
+
});
|
|
198
|
+
const answer = await getState().functions.llm_generate.run(
|
|
199
|
+
"What is written in this picture?",
|
|
200
|
+
{
|
|
201
|
+
chat,
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
expect(typeof answer).toBe("string");
|
|
205
|
+
expect(answer.toLowerCase()).toContain("coffee");
|
|
206
|
+
});
|
|
188
207
|
if (name !== "AI SDK Anthropic")
|
|
189
208
|
it("gets embedding", async () => {
|
|
190
209
|
const v = await getState().functions.llm_embedding.run(
|