@saltcorn/agents 0.3.0 → 0.3.1

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/agent-view.js CHANGED
@@ -39,6 +39,7 @@ const {
39
39
  wrapCard,
40
40
  wrapSegment,
41
41
  process_interaction,
42
+ find_image_tool,
42
43
  } = require("./common");
43
44
  const MarkdownIt = require("markdown-it"),
44
45
  md = new MarkdownIt();
@@ -157,7 +158,6 @@ const run = async (
157
158
  for (const interact of run.context.interactions) {
158
159
  switch (interact.role) {
159
160
  case "user":
160
- console.log(interact.content);
161
161
  if (interact.content?.[0]?.type === "image_url") {
162
162
  const image_url = interact.content[0].image_url.url;
163
163
  if (image_url.startsWith("data"))
@@ -187,58 +187,74 @@ const run = async (
187
187
  break;
188
188
  case "assistant":
189
189
  case "system":
190
- if (interact.tool_calls) {
191
- if (interact.content) {
192
- interactMarkups.push(
193
- div(
194
- { class: "interaction-segment" },
195
- span({ class: "badge bg-secondary" }, action.name),
196
- typeof interact.content === "string"
197
- ? md.render(interact.content)
198
- : interact.content
199
- )
200
- );
190
+
191
+ for (const tool_call of interact.tool_calls || []) {
192
+ const toolSkill = find_tool(
193
+ tool_call.function.name,
194
+ action.configuration
195
+ );
196
+ if (toolSkill) {
197
+ const row = JSON.parse(tool_call.function.arguments);
198
+ if (toolSkill.tool.renderToolCall) {
199
+ const rendered = await toolSkill.tool.renderToolCall(row, {
200
+ req,
201
+ });
202
+ if (rendered)
203
+ interactMarkups.push(
204
+ wrapSegment(
205
+ wrapCard(
206
+ toolSkill.skill.skill_label ||
207
+ toolSkill.skill.constructor.skill_name,
208
+ rendered
209
+ ),
210
+ action.name
211
+ )
212
+ );
213
+ }
201
214
  }
202
- for (const tool_call of interact.tool_calls) {
203
- const toolSkill = find_tool(
204
- tool_call.function.name,
205
- action.configuration
206
- );
207
- if (toolSkill) {
208
- const row = JSON.parse(tool_call.function.arguments);
209
- if (toolSkill.tool.renderToolCall) {
210
- const rendered = await toolSkill.tool.renderToolCall(row, {
215
+ }
216
+ for (const image_call of interact.content?.image_calls || []) {
217
+
218
+ const toolSkill = find_image_tool(action.configuration);
219
+ if (toolSkill) {
220
+ if (toolSkill.tool.renderToolResponse) {
221
+ const rendered = await toolSkill.tool.renderToolResponse(
222
+ image_call,
223
+ {
211
224
  req,
212
- });
213
- if (rendered)
214
- interactMarkups.push(
215
- wrapSegment(
216
- wrapCard(
217
- toolSkill.skill.skill_label ||
218
- toolSkill.skill.constructor.skill_name,
219
- rendered
220
- ),
221
- action.name
222
- )
223
- );
224
- }
225
+ }
226
+ );
227
+
228
+ if (rendered)
229
+ interactMarkups.push(
230
+ wrapSegment(
231
+ wrapCard(
232
+ toolSkill.skill.skill_label ||
233
+ toolSkill.skill.constructor.skill_name,
234
+ rendered
235
+ ),
236
+ action.name
237
+ )
238
+ );
225
239
  }
226
240
  }
227
- } else
228
- interactMarkups.push(
229
- div(
230
- { class: "interaction-segment" },
231
- span({ class: "badge bg-secondary" }, action.name),
232
- typeof interact.content === "string"
233
- ? md.render(interact.content)
234
- : interact.content
235
- )
236
- );
241
+ }
242
+
243
+ interactMarkups.push(
244
+ div(
245
+ { class: "interaction-segment" },
246
+ span({ class: "badge bg-secondary" }, action.name),
247
+ typeof interact.content === "string"
248
+ ? md.render(interact.content)
249
+ : typeof interact.content?.content === "string"
250
+ ? md.render(interact.content.content)
251
+ : interact.content
252
+ )
253
+ );
237
254
  break;
238
255
  case "tool":
239
256
  if (interact.content !== "Action run") {
240
257
  let markupContent;
241
- console.log("interact", interact);
242
258
  const toolSkill = find_tool(interact.name, action.configuration);
243
259
  try {
244
260
  if (toolSkill?.tool?.renderToolResponse)
package/common.js CHANGED
@@ -12,6 +12,7 @@ const get_skills = () => {
12
12
  require("./skills/Trigger"),
13
13
  require("./skills/Table"),
14
14
  require("./skills/PreloadData"),
15
+ require("./skills/GenerateImage"),
15
16
  //require("./skills/AdaptiveFeedback"),
16
17
  ];
17
18
  };
@@ -34,7 +35,7 @@ const get_skill_instances = (config) => {
34
35
  const find_tool = (name, config) => {
35
36
  const skills = get_skill_instances(config);
36
37
  for (const skill of skills) {
37
- const skillTools = skill.provideTools();
38
+ const skillTools = skill.provideTools?.();
38
39
  const tools = !skillTools
39
40
  ? []
40
41
  : Array.isArray(skillTools)
@@ -45,6 +46,20 @@ const find_tool = (name, config) => {
45
46
  }
46
47
  };
47
48
 
49
+ const find_image_tool = (config) => {
50
+ const skills = get_skill_instances(config);
51
+ for (const skill of skills) {
52
+ const skillTools = skill.provideTools?.();
53
+ const tools = !skillTools
54
+ ? []
55
+ : Array.isArray(skillTools)
56
+ ? skillTools
57
+ : [skillTools];
58
+ const found = tools.find((t) => t?.type === "image_generation");
59
+ if (found) return { tool: found, skill };
60
+ }
61
+ };
62
+
48
63
  const getCompletionArguments = async (config, user) => {
49
64
  let tools = [];
50
65
 
@@ -52,7 +67,7 @@ const getCompletionArguments = async (config, user) => {
52
67
 
53
68
  const skills = get_skill_instances(config);
54
69
  for (const skill of skills) {
55
- const sysPr = await skill.systemPrompt({ user });
70
+ const sysPr = await skill.systemPrompt?.({ user });
56
71
  if (sysPr) sysPrompts.push(sysPr);
57
72
  const skillTools = skill.provideTools?.();
58
73
  if (skillTools && Array.isArray(skillTools)) tools.push(...skillTools);
@@ -133,10 +148,43 @@ const process_interaction = async (
133
148
  const complArgs = await getCompletionArguments(config, req.user);
134
149
  complArgs.chat = run.context.interactions;
135
150
  //complArgs.debugResult = true;
136
- console.log("complArgs", JSON.stringify(complArgs, null, 2));
151
+ //console.log("complArgs", JSON.stringify(complArgs, null, 2));
137
152
 
138
153
  const answer = await getState().functions.llm_generate.run("", complArgs);
139
- console.log("answer", answer);
154
+ //console.log("answer", answer);
155
+
156
+ const responses = [];
157
+ if (typeof answer === "object" && answer.image_calls) {
158
+ for (const image_call of answer.image_calls) {
159
+ const tool = find_image_tool(config);
160
+ let prcRes;
161
+ if (tool?.tool.process) {
162
+ prcRes = await tool.tool.process(image_call, { req });
163
+ if (prcRes?.result === null) delete image_call.result;
164
+ if (prcRes?.filename) image_call.filename = prcRes?.filename;
165
+ }
166
+ if (tool?.tool.renderToolResponse) {
167
+ const rendered = await tool.tool.renderToolResponse(
168
+ { ...image_call, ...(prcRes || {}) },
169
+ {
170
+ req,
171
+ }
172
+ );
173
+ if (rendered)
174
+ responses.push(
175
+ wrapSegment(
176
+ wrapCard(
177
+ tool.skill.skill_label || tool.skill.constructor.skill_name,
178
+ rendered
179
+ ),
180
+ agent_label
181
+ )
182
+ );
183
+ }
184
+ }
185
+ if (answer.content && !answer.tool_calls)
186
+ responses.push(wrapSegment(md.render(answer.content), agent_label));
187
+ }
140
188
  await addToContext(run, {
141
189
  interactions:
142
190
  typeof answer === "object" && answer.tool_calls
@@ -149,8 +197,6 @@ const process_interaction = async (
149
197
  ]
150
198
  : [{ role: "assistant", content: answer }],
151
199
  });
152
- const responses = [];
153
-
154
200
  if (typeof answer === "object" && answer.tool_calls) {
155
201
  if (answer.content)
156
202
  responses.push(wrapSegment(md.render(answer.content), agent_label));
@@ -213,6 +259,7 @@ const process_interaction = async (
213
259
  {
214
260
  role: "tool",
215
261
  tool_call_id: tool_call.id,
262
+ call_id: tool_call.call_id,
216
263
  name: tool_call.function.name,
217
264
  content:
218
265
  result && typeof result !== "string"
@@ -228,7 +275,8 @@ const process_interaction = async (
228
275
  ...prevResponses,
229
276
  ...responses,
230
277
  ]);
231
- } else responses.push(wrapSegment(md.render(answer), agent_label));
278
+ } else if (typeof answer === "string")
279
+ responses.push(wrapSegment(md.render(answer), agent_label));
232
280
 
233
281
  return {
234
282
  json: {
@@ -251,4 +299,5 @@ module.exports = {
251
299
  wrapCard,
252
300
  wrapSegment,
253
301
  process_interaction,
302
+ find_image_tool,
254
303
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/agents",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "AI agents for Saltcorn",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -0,0 +1,98 @@
1
+ const { div, pre } = require("@saltcorn/markup/tags");
2
+ const Workflow = require("@saltcorn/data/models/workflow");
3
+ const Form = require("@saltcorn/data/models/form");
4
+ const Table = require("@saltcorn/data/models/table");
5
+ const File = require("@saltcorn/data/models/file");
6
+ const View = require("@saltcorn/data/models/view");
7
+ const { getState } = require("@saltcorn/data/db/state");
8
+ const db = require("@saltcorn/data/db");
9
+ const { eval_expression } = require("@saltcorn/data/models/expression");
10
+ const { interpolate } = require("@saltcorn/data/utils");
11
+
12
+ class GenerateImage {
13
+ static skill_name = "Image generation";
14
+
15
+ get skill_label() {
16
+ return `Image generation`;
17
+ }
18
+
19
+ constructor(cfg) {
20
+ Object.assign(this, cfg);
21
+ }
22
+
23
+ static async configFields() {
24
+ return [
25
+ {
26
+ name: "quality",
27
+ label: "Quality",
28
+ type: "String",
29
+ required: true,
30
+ attributes: { options: ["auto", "low", "medium", "high"] },
31
+ },
32
+ {
33
+ name: "size",
34
+ label: "Size",
35
+ type: "String",
36
+ required: true,
37
+ attributes: {
38
+ options: ["auto", "1024x1024", "1536x1024", "1024x1536"],
39
+ },
40
+ },
41
+ {
42
+ name: "format",
43
+ label: "Format",
44
+ type: "String",
45
+ required: true,
46
+ attributes: { options: ["png", "jpeg", "webp"] },
47
+ },
48
+ {
49
+ name: "transparent",
50
+ label: "Transparent",
51
+ type: "Bool",
52
+ },
53
+ {
54
+ name: "save_file",
55
+ label: "Save file",
56
+ type: "String",
57
+ required: true,
58
+ attributes: { options: ["Always", "Never"] }, //, "Button"] },
59
+ },
60
+ ];
61
+ }
62
+
63
+ provideTools() {
64
+ const tool = {
65
+ type: "image_generation",
66
+ size: this.size,
67
+ quality: this.quality,
68
+ process: async (v, { req }) => {
69
+ if (this.save_file === "Always") {
70
+ const buf = Buffer.from(v.result, "base64");
71
+ const file = await File.from_contents(
72
+ `genimg.${v.output_format}`,
73
+ `image/${v.output_format}`,
74
+ buf,
75
+ req?.user?.id,
76
+ 100
77
+ );
78
+ return { filename: file.path_to_serve, result: null };
79
+ }
80
+ },
81
+ renderToolResponse: (v) => {
82
+ const [ws, hs] = v.size.split("x");
83
+ if (v.filename)
84
+ return `<img height="${+hs / 4}" width="${
85
+ +ws / 4
86
+ }" src="/files/serve/${v.filename}" />`;
87
+ else
88
+ return `<img height="${+hs / 4}" width="${+ws / 4}" src="data:image/${
89
+ v.output_format
90
+ };base64, ${v.result}" />`;
91
+ },
92
+ };
93
+ if (this.transparent) tool.background = "transparent";
94
+ return tool;
95
+ }
96
+ }
97
+
98
+ module.exports = GenerateImage;
@@ -31,7 +31,6 @@ class PreloadData {
31
31
  );
32
32
 
33
33
  const rows = await table.getRows(q);
34
- console.log("preload data rows", rows);
35
34
  if (this.contents_expr) {
36
35
  for (const row of rows)
37
36
  prompts.push(interpolate(this.contents_expr, row, user));