@saltcorn/agents 0.4.3 → 0.4.5
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 +44 -9
- package/common.js +59 -7
- package/package.json +1 -1
- package/skills/PreloadData.js +2 -2
- package/skills/PromptPicker.js +51 -0
package/agent-view.js
CHANGED
|
@@ -43,6 +43,7 @@ const {
|
|
|
43
43
|
find_image_tool,
|
|
44
44
|
is_debug_mode,
|
|
45
45
|
get_initial_interactions,
|
|
46
|
+
get_skill_instances,
|
|
46
47
|
} = require("./common");
|
|
47
48
|
const MarkdownIt = require("markdown-it"),
|
|
48
49
|
md = new MarkdownIt();
|
|
@@ -86,6 +87,12 @@ const configuration_workflow = (req) =>
|
|
|
86
87
|
name: "prev_runs_closed",
|
|
87
88
|
label: "Initially closed",
|
|
88
89
|
type: "Bool",
|
|
90
|
+
showIf: { show_prev_runs: true },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "stream",
|
|
94
|
+
label: "Stream response",
|
|
95
|
+
type: "Bool",
|
|
89
96
|
},
|
|
90
97
|
{
|
|
91
98
|
name: "placeholder",
|
|
@@ -161,6 +168,7 @@ const run = async (
|
|
|
161
168
|
placeholder,
|
|
162
169
|
explainer,
|
|
163
170
|
image_upload,
|
|
171
|
+
stream,
|
|
164
172
|
},
|
|
165
173
|
state,
|
|
166
174
|
{ res, req }
|
|
@@ -316,11 +324,19 @@ const run = async (
|
|
|
316
324
|
}
|
|
317
325
|
runInteractions = interactMarkups.join("");
|
|
318
326
|
}
|
|
327
|
+
const skill_form_widgets = [];
|
|
328
|
+
for (const skill of get_skill_instances(action.configuration)) {
|
|
329
|
+
if (skill.formWidget)
|
|
330
|
+
skill_form_widgets.push(
|
|
331
|
+
await skill.formWidget({ user: req.user, klass: "skill-form-widget" })
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
319
335
|
const debugMode = is_debug_mode(action.configuration, req.user);
|
|
320
336
|
const input_form = form(
|
|
321
337
|
{
|
|
322
338
|
onsubmit: `event.preventDefault();spin_send_button();view_post('${viewname}', 'interact', new FormData(this), processCopilotResponse);return false;`,
|
|
323
|
-
class: "form-namespace copilot mt-2",
|
|
339
|
+
class: ["form-namespace copilot mt-2 agent-view"],
|
|
324
340
|
method: "post",
|
|
325
341
|
},
|
|
326
342
|
input({
|
|
@@ -364,8 +380,10 @@ const run = async (
|
|
|
364
380
|
onclick: "press_agent_debug_button()",
|
|
365
381
|
class: "debugicon fas fa-bug",
|
|
366
382
|
}),
|
|
383
|
+
skill_form_widgets,
|
|
367
384
|
explainer && small({ class: "explainer" }, i(explainer))
|
|
368
|
-
)
|
|
385
|
+
),
|
|
386
|
+
stream && div({ class: "next_response_scratch" })
|
|
369
387
|
);
|
|
370
388
|
|
|
371
389
|
const prev_runs_side_bar = div(
|
|
@@ -449,6 +467,12 @@ const run = async (
|
|
|
449
467
|
top: -1.8rem;
|
|
450
468
|
left: 0.1rem;
|
|
451
469
|
cursor: pointer;
|
|
470
|
+
}
|
|
471
|
+
.copilot-entry .skill-form-widget {
|
|
472
|
+
position: relative;
|
|
473
|
+
top: -2rem;
|
|
474
|
+
left: 0.4rem;
|
|
475
|
+
display: inline;
|
|
452
476
|
}
|
|
453
477
|
.session-open-sessions, .open-prev-runs {
|
|
454
478
|
cursor: pointer;
|
|
@@ -497,6 +521,7 @@ const run = async (
|
|
|
497
521
|
if(hadFile)
|
|
498
522
|
$("#copilotinteractions").append(wrapSegment('File', "You"))
|
|
499
523
|
$("textarea[name=userinput]").val("")
|
|
524
|
+
$('form.agent-view div.next_response_scratch').html("")
|
|
500
525
|
|
|
501
526
|
if(res.response)
|
|
502
527
|
$("#copilotinteractions").append(res.response)
|
|
@@ -657,7 +682,8 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
657
682
|
req,
|
|
658
683
|
action.name,
|
|
659
684
|
[],
|
|
660
|
-
triggering_row
|
|
685
|
+
triggering_row,
|
|
686
|
+
config.stream
|
|
661
687
|
);
|
|
662
688
|
};
|
|
663
689
|
|
|
@@ -682,13 +708,22 @@ const debug_info = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
682
708
|
if (table) triggering_row = await table.getRow({ [pk]: triggering_row_id });
|
|
683
709
|
}
|
|
684
710
|
const run = await WorkflowRun.findOne({ id: +run_id });
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
711
|
+
let sysPrompt = "";
|
|
712
|
+
if (
|
|
713
|
+
run.context.api_interactions?.[0].request?.messages?.[0]?.role === "system"
|
|
714
|
+
) {
|
|
715
|
+
sysPrompt =
|
|
716
|
+
run.context.api_interactions?.[0].request?.messages?.[0].content;
|
|
717
|
+
} else {
|
|
718
|
+
const complArgs = await getCompletionArguments(
|
|
719
|
+
action.configuration,
|
|
720
|
+
req.user,
|
|
721
|
+
triggering_row
|
|
722
|
+
);
|
|
723
|
+
sysPrompt = complArgs.systemPrompt;
|
|
724
|
+
}
|
|
690
725
|
const debug_html = div(
|
|
691
|
-
div(h4("System prompt"), pre(
|
|
726
|
+
div(h4("System prompt"), pre(sysPrompt)),
|
|
692
727
|
div(
|
|
693
728
|
h4("API interactions"),
|
|
694
729
|
pre(JSON.stringify(run.context.api_interactions, null, 2))
|
package/common.js
CHANGED
|
@@ -2,6 +2,7 @@ const { getState } = require("@saltcorn/data/db/state");
|
|
|
2
2
|
const { div, span } = require("@saltcorn/markup/tags");
|
|
3
3
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
4
4
|
const { interpolate } = require("@saltcorn/data/utils");
|
|
5
|
+
const db = require("@saltcorn/data/db");
|
|
5
6
|
|
|
6
7
|
const MarkdownIt = require("markdown-it"),
|
|
7
8
|
md = new MarkdownIt();
|
|
@@ -31,6 +32,7 @@ const get_skills = () => {
|
|
|
31
32
|
require("./skills/PreloadData"),
|
|
32
33
|
require("./skills/GenerateImage"),
|
|
33
34
|
require("./skills/ModelContextProtocol"),
|
|
35
|
+
require("./skills/PromptPicker"),
|
|
34
36
|
//require("./skills/AdaptiveFeedback"),
|
|
35
37
|
...exchange_skills,
|
|
36
38
|
];
|
|
@@ -89,7 +91,12 @@ const get_initial_interactions = async (config, user, triggering_row) => {
|
|
|
89
91
|
return interacts;
|
|
90
92
|
};
|
|
91
93
|
|
|
92
|
-
const getCompletionArguments = async (
|
|
94
|
+
const getCompletionArguments = async (
|
|
95
|
+
config,
|
|
96
|
+
user,
|
|
97
|
+
triggering_row,
|
|
98
|
+
formbody
|
|
99
|
+
) => {
|
|
93
100
|
let tools = [];
|
|
94
101
|
|
|
95
102
|
let sysPrompts = [
|
|
@@ -98,7 +105,11 @@ const getCompletionArguments = async (config, user, triggering_row) => {
|
|
|
98
105
|
|
|
99
106
|
const skills = get_skill_instances(config);
|
|
100
107
|
for (const skill of skills) {
|
|
101
|
-
const sysPr = await skill.systemPrompt?.({
|
|
108
|
+
const sysPr = await skill.systemPrompt?.({
|
|
109
|
+
...(formbody || {}),
|
|
110
|
+
user,
|
|
111
|
+
triggering_row,
|
|
112
|
+
});
|
|
102
113
|
if (sysPr) sysPrompts.push(sysPr);
|
|
103
114
|
const skillTools = skill.provideTools?.();
|
|
104
115
|
if (skillTools && Array.isArray(skillTools)) tools.push(...skillTools);
|
|
@@ -190,12 +201,15 @@ const process_interaction = async (
|
|
|
190
201
|
req,
|
|
191
202
|
agent_label = "Copilot",
|
|
192
203
|
prevResponses = [],
|
|
193
|
-
triggering_row = {}
|
|
204
|
+
triggering_row = {},
|
|
205
|
+
stream = false
|
|
194
206
|
) => {
|
|
207
|
+
const sysState = getState();
|
|
195
208
|
const complArgs = await getCompletionArguments(
|
|
196
209
|
config,
|
|
197
210
|
req.user,
|
|
198
|
-
triggering_row
|
|
211
|
+
triggering_row,
|
|
212
|
+
req.body
|
|
199
213
|
);
|
|
200
214
|
complArgs.chat = run.context.interactions.map(only_response_text_if_present);
|
|
201
215
|
//complArgs.debugResult = true;
|
|
@@ -203,7 +217,26 @@ const process_interaction = async (
|
|
|
203
217
|
const debugMode = is_debug_mode(config, req.user);
|
|
204
218
|
const debugCollector = {};
|
|
205
219
|
if (debugMode) complArgs.debugCollector = debugCollector;
|
|
206
|
-
|
|
220
|
+
if (stream && sysState.getConfig("enable_dynamic_updates") && req.user) {
|
|
221
|
+
complArgs.streamCallback = (response) => {
|
|
222
|
+
const content =
|
|
223
|
+
response.choices[0].content || response.choices[0].delta?.content;
|
|
224
|
+
if (content)
|
|
225
|
+
sysState?.emitDynamicUpdate(
|
|
226
|
+
db.getTenantSchema(),
|
|
227
|
+
{
|
|
228
|
+
eval_js: `$('form.agent-view div.next_response_scratch').append(${JSON.stringify(
|
|
229
|
+
content
|
|
230
|
+
)})`,
|
|
231
|
+
},
|
|
232
|
+
[req.user.id]
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const answer = await sysState.functions.llm_generate.run("", complArgs);
|
|
237
|
+
|
|
238
|
+
//console.log({answer});
|
|
239
|
+
|
|
207
240
|
if (debugMode)
|
|
208
241
|
await addToContext(run, {
|
|
209
242
|
api_interactions: [debugCollector],
|
|
@@ -263,7 +296,7 @@ const process_interaction = async (
|
|
|
263
296
|
let hasResult = false;
|
|
264
297
|
if ((answer.mcp_calls || []).length && !answer.content) hasResult = true;
|
|
265
298
|
for (const tool_call of answer.tool_calls || []) {
|
|
266
|
-
console.log("call function", tool_call.function);
|
|
299
|
+
console.log("call function", tool_call.function?.name);
|
|
267
300
|
|
|
268
301
|
await addToContext(run, {
|
|
269
302
|
funcalls: { [tool_call.id]: tool_call.function },
|
|
@@ -272,6 +305,24 @@ const process_interaction = async (
|
|
|
272
305
|
const tool = find_tool(tool_call.function?.name, config);
|
|
273
306
|
|
|
274
307
|
if (tool) {
|
|
308
|
+
if (
|
|
309
|
+
stream &&
|
|
310
|
+
sysState.getConfig("enable_dynamic_updates") &&
|
|
311
|
+
req.user
|
|
312
|
+
) {
|
|
313
|
+
let content =
|
|
314
|
+
"Using skill: " + tool.skill.skill_label ||
|
|
315
|
+
tool.skill.constructor.skill_name;
|
|
316
|
+
sysState?.emitDynamicUpdate(
|
|
317
|
+
db.getTenantSchema(),
|
|
318
|
+
{
|
|
319
|
+
eval_js: `$('form.agent-view div.next_response_scratch').append(${JSON.stringify(
|
|
320
|
+
content
|
|
321
|
+
)})`,
|
|
322
|
+
},
|
|
323
|
+
[req.user.id]
|
|
324
|
+
);
|
|
325
|
+
}
|
|
275
326
|
if (tool.tool.renderToolCall) {
|
|
276
327
|
const row = JSON.parse(tool_call.function.arguments);
|
|
277
328
|
const rendered = await tool.tool.renderToolCall(row, {
|
|
@@ -337,7 +388,8 @@ const process_interaction = async (
|
|
|
337
388
|
req,
|
|
338
389
|
agent_label,
|
|
339
390
|
[...prevResponses, ...responses],
|
|
340
|
-
triggering_row
|
|
391
|
+
triggering_row,
|
|
392
|
+
stream
|
|
341
393
|
);
|
|
342
394
|
} else if (typeof answer === "string")
|
|
343
395
|
responses.push(wrapSegment(md.render(answer), agent_label));
|
package/package.json
CHANGED
package/skills/PreloadData.js
CHANGED
|
@@ -11,7 +11,7 @@ const db = require("@saltcorn/data/db");
|
|
|
11
11
|
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
12
12
|
const { interpolate } = require("@saltcorn/data/utils");
|
|
13
13
|
const vm = require("vm");
|
|
14
|
-
const fetch = require("node-fetch");
|
|
14
|
+
const fetch = global.fetch || require("node-fetch");
|
|
15
15
|
|
|
16
16
|
class PreloadData {
|
|
17
17
|
static skill_name = "Preload Data";
|
|
@@ -69,7 +69,7 @@ class PreloadData {
|
|
|
69
69
|
const table = Table.findOne(this.table_name);
|
|
70
70
|
const q = eval_expression(
|
|
71
71
|
this.preload_query,
|
|
72
|
-
{},
|
|
72
|
+
triggering_row || {},
|
|
73
73
|
user,
|
|
74
74
|
"PreloadData query"
|
|
75
75
|
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const Workflow = require("@saltcorn/data/models/workflow");
|
|
2
|
+
const Form = require("@saltcorn/data/models/form");
|
|
3
|
+
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
4
|
+
const { select, option } = require("@saltcorn/markup/tags");
|
|
5
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
6
|
+
const { validID } = require("@saltcorn/markup/layout_utils");
|
|
7
|
+
|
|
8
|
+
class PromptPicker {
|
|
9
|
+
static skill_name = "Prompt picker";
|
|
10
|
+
get skill_label() {
|
|
11
|
+
return `Prompt picker`;
|
|
12
|
+
}
|
|
13
|
+
constructor(cfg) {
|
|
14
|
+
Object.assign(this, cfg);
|
|
15
|
+
this.options = eval_expression(
|
|
16
|
+
this.options_obj,
|
|
17
|
+
{},
|
|
18
|
+
null,
|
|
19
|
+
"Prompt picker options"
|
|
20
|
+
);
|
|
21
|
+
this.formname = validID("pp" + Object.keys(this.options));
|
|
22
|
+
}
|
|
23
|
+
static async configFields() {
|
|
24
|
+
return [
|
|
25
|
+
{ name: "placeholder", label: "Placeholder", type: "String" },
|
|
26
|
+
{
|
|
27
|
+
name: "options_obj",
|
|
28
|
+
label: "System prompt contents",
|
|
29
|
+
sublabel: `JavaScript object where the keys are the options and values are added to system prompt. Example:<br><code>{"Pirate":"Speak like a pirate", "Pop star":"Speak like a pop star"}</code>`,
|
|
30
|
+
type: "String",
|
|
31
|
+
fieldview: "textarea",
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
async formWidget({ user, klass }) {
|
|
37
|
+
return select(
|
|
38
|
+
{
|
|
39
|
+
class: ["form-select form-select-sm w-unset", klass],
|
|
40
|
+
name: this.formname,
|
|
41
|
+
},
|
|
42
|
+
this.placeholder && option({ disabled: true }, this.placeholder),
|
|
43
|
+
Object.keys(this.options).map((o) => option(o))
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
systemPrompt(body) {
|
|
47
|
+
if (body[this.formname]) return this.options[body[this.formname]];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = PromptPicker;
|