@saltcorn/agents 0.2.3 → 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 +61 -45
- package/common.js +60 -10
- package/package.json +1 -1
- package/skills/GenerateImage.js +98 -0
- package/skills/PreloadData.js +91 -0
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (toolSkill) {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
@@ -11,6 +11,8 @@ const get_skills = () => {
|
|
|
11
11
|
require("./skills/EmbeddingRetrieval"),
|
|
12
12
|
require("./skills/Trigger"),
|
|
13
13
|
require("./skills/Table"),
|
|
14
|
+
require("./skills/PreloadData"),
|
|
15
|
+
require("./skills/GenerateImage"),
|
|
14
16
|
//require("./skills/AdaptiveFeedback"),
|
|
15
17
|
];
|
|
16
18
|
};
|
|
@@ -33,7 +35,7 @@ const get_skill_instances = (config) => {
|
|
|
33
35
|
const find_tool = (name, config) => {
|
|
34
36
|
const skills = get_skill_instances(config);
|
|
35
37
|
for (const skill of skills) {
|
|
36
|
-
const skillTools = skill.provideTools();
|
|
38
|
+
const skillTools = skill.provideTools?.();
|
|
37
39
|
const tools = !skillTools
|
|
38
40
|
? []
|
|
39
41
|
: Array.isArray(skillTools)
|
|
@@ -44,16 +46,30 @@ const find_tool = (name, config) => {
|
|
|
44
46
|
}
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
const
|
|
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
|
+
|
|
63
|
+
const getCompletionArguments = async (config, user) => {
|
|
48
64
|
let tools = [];
|
|
49
65
|
|
|
50
66
|
let sysPrompts = [config.sys_prompt];
|
|
51
67
|
|
|
52
68
|
const skills = get_skill_instances(config);
|
|
53
69
|
for (const skill of skills) {
|
|
54
|
-
const sysPr = skill.systemPrompt();
|
|
70
|
+
const sysPr = await skill.systemPrompt?.({ user });
|
|
55
71
|
if (sysPr) sysPrompts.push(sysPr);
|
|
56
|
-
const skillTools = skill.provideTools();
|
|
72
|
+
const skillTools = skill.provideTools?.();
|
|
57
73
|
if (skillTools && Array.isArray(skillTools)) tools.push(...skillTools);
|
|
58
74
|
else if (skillTools) tools.push(skillTools);
|
|
59
75
|
}
|
|
@@ -129,13 +145,46 @@ const process_interaction = async (
|
|
|
129
145
|
agent_label = "Copilot",
|
|
130
146
|
prevResponses = []
|
|
131
147
|
) => {
|
|
132
|
-
const complArgs = await getCompletionArguments(config);
|
|
148
|
+
const complArgs = await getCompletionArguments(config, req.user);
|
|
133
149
|
complArgs.chat = run.context.interactions;
|
|
134
150
|
//complArgs.debugResult = true;
|
|
135
|
-
console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
151
|
+
//console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
136
152
|
|
|
137
153
|
const answer = await getState().functions.llm_generate.run("", complArgs);
|
|
138
|
-
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
|
+
}
|
|
139
188
|
await addToContext(run, {
|
|
140
189
|
interactions:
|
|
141
190
|
typeof answer === "object" && answer.tool_calls
|
|
@@ -148,8 +197,6 @@ const process_interaction = async (
|
|
|
148
197
|
]
|
|
149
198
|
: [{ role: "assistant", content: answer }],
|
|
150
199
|
});
|
|
151
|
-
const responses = [];
|
|
152
|
-
|
|
153
200
|
if (typeof answer === "object" && answer.tool_calls) {
|
|
154
201
|
if (answer.content)
|
|
155
202
|
responses.push(wrapSegment(md.render(answer.content), agent_label));
|
|
@@ -212,6 +259,7 @@ const process_interaction = async (
|
|
|
212
259
|
{
|
|
213
260
|
role: "tool",
|
|
214
261
|
tool_call_id: tool_call.id,
|
|
262
|
+
call_id: tool_call.call_id,
|
|
215
263
|
name: tool_call.function.name,
|
|
216
264
|
content:
|
|
217
265
|
result && typeof result !== "string"
|
|
@@ -227,7 +275,8 @@ const process_interaction = async (
|
|
|
227
275
|
...prevResponses,
|
|
228
276
|
...responses,
|
|
229
277
|
]);
|
|
230
|
-
} else
|
|
278
|
+
} else if (typeof answer === "string")
|
|
279
|
+
responses.push(wrapSegment(md.render(answer), agent_label));
|
|
231
280
|
|
|
232
281
|
return {
|
|
233
282
|
json: {
|
|
@@ -250,4 +299,5 @@ module.exports = {
|
|
|
250
299
|
wrapCard,
|
|
251
300
|
wrapSegment,
|
|
252
301
|
process_interaction,
|
|
302
|
+
find_image_tool,
|
|
253
303
|
};
|
package/package.json
CHANGED
|
@@ -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;
|
|
@@ -0,0 +1,91 @@
|
|
|
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 View = require("@saltcorn/data/models/view");
|
|
6
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
7
|
+
const db = require("@saltcorn/data/db");
|
|
8
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
9
|
+
const { interpolate } = require("@saltcorn/data/utils");
|
|
10
|
+
|
|
11
|
+
class PreloadData {
|
|
12
|
+
static skill_name = "Preload Data";
|
|
13
|
+
|
|
14
|
+
get skill_label() {
|
|
15
|
+
return `Preload Data`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(cfg) {
|
|
19
|
+
Object.assign(this, cfg);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async systemPrompt({ user }) {
|
|
23
|
+
const prompts = [];
|
|
24
|
+
if (this.add_sys_prompt) prompts.push(this.add_sys_prompt);
|
|
25
|
+
const table = Table.findOne(this.table_name);
|
|
26
|
+
const q = eval_expression(
|
|
27
|
+
this.preload_query,
|
|
28
|
+
{},
|
|
29
|
+
user,
|
|
30
|
+
"PreloadData query"
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const rows = await table.getRows(q);
|
|
34
|
+
if (this.contents_expr) {
|
|
35
|
+
for (const row of rows)
|
|
36
|
+
prompts.push(interpolate(this.contents_expr, row, user));
|
|
37
|
+
} else {
|
|
38
|
+
const hidden_fields = this.hidden_fields.split(",").map((s) => s.trim());
|
|
39
|
+
for (const row of rows) {
|
|
40
|
+
hidden_fields.forEach((k) => {
|
|
41
|
+
delete row[k];
|
|
42
|
+
});
|
|
43
|
+
prompts.push(JSON.stringify(row));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return prompts.join("\n");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static async configFields() {
|
|
50
|
+
const allTables = await Table.find();
|
|
51
|
+
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
name: "table_name",
|
|
55
|
+
label: "Table",
|
|
56
|
+
sublabel: "Which table to search",
|
|
57
|
+
type: "String",
|
|
58
|
+
required: true,
|
|
59
|
+
attributes: { options: allTables.map((t) => t.name) },
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "preload_query",
|
|
63
|
+
label: "Query",
|
|
64
|
+
type: "String",
|
|
65
|
+
class: "validate-expression",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "add_sys_prompt",
|
|
69
|
+
label: "Additional prompt",
|
|
70
|
+
type: "String",
|
|
71
|
+
fieldview: "textarea",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "contents_expr",
|
|
75
|
+
label: "Contents string",
|
|
76
|
+
type: "String",
|
|
77
|
+
sublabel:
|
|
78
|
+
"Use handlebars (<code>{{ }}</code>) to access fields in each retrieved row",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "hidden_fields",
|
|
82
|
+
label: "Hide fields",
|
|
83
|
+
type: "String",
|
|
84
|
+
sublabel:
|
|
85
|
+
"Comma-separated list of fields to hide from the prompt, if not using the contents string",
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = PreloadData;
|