@saltcorn/agents 0.3.6 → 0.4.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 +129 -28
- package/common.js +50 -9
- package/index.js +10 -2
- package/locales/de.json +1 -0
- package/package.json +1 -1
- package/skills/FTSRetrieval.js +69 -26
- package/skills/PreloadData.js +97 -19
package/agent-view.js
CHANGED
|
@@ -42,6 +42,7 @@ const {
|
|
|
42
42
|
process_interaction,
|
|
43
43
|
find_image_tool,
|
|
44
44
|
is_debug_mode,
|
|
45
|
+
get_initial_interactions,
|
|
45
46
|
} = require("./common");
|
|
46
47
|
const MarkdownIt = require("markdown-it"),
|
|
47
48
|
md = new MarkdownIt();
|
|
@@ -81,6 +82,11 @@ const configuration_workflow = (req) =>
|
|
|
81
82
|
label: "Show previous runs",
|
|
82
83
|
type: "Bool",
|
|
83
84
|
},
|
|
85
|
+
{
|
|
86
|
+
name: "prev_runs_closed",
|
|
87
|
+
label: "Initially closed",
|
|
88
|
+
type: "Bool",
|
|
89
|
+
},
|
|
84
90
|
{
|
|
85
91
|
name: "placeholder",
|
|
86
92
|
label: "Placeholder",
|
|
@@ -114,7 +120,16 @@ const configuration_workflow = (req) =>
|
|
|
114
120
|
],
|
|
115
121
|
});
|
|
116
122
|
|
|
117
|
-
const get_state_fields = () =>
|
|
123
|
+
const get_state_fields = async (table_id) =>
|
|
124
|
+
table_id
|
|
125
|
+
? [
|
|
126
|
+
{
|
|
127
|
+
name: "id",
|
|
128
|
+
type: "Integer",
|
|
129
|
+
primary_key: true,
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
: [];
|
|
118
133
|
|
|
119
134
|
const uploadForm = (viewname, req) =>
|
|
120
135
|
span(
|
|
@@ -139,7 +154,14 @@ const uploadForm = (viewname, req) =>
|
|
|
139
154
|
const run = async (
|
|
140
155
|
table_id,
|
|
141
156
|
viewname,
|
|
142
|
-
{
|
|
157
|
+
{
|
|
158
|
+
action_id,
|
|
159
|
+
show_prev_runs,
|
|
160
|
+
prev_runs_closed,
|
|
161
|
+
placeholder,
|
|
162
|
+
explainer,
|
|
163
|
+
image_upload,
|
|
164
|
+
},
|
|
143
165
|
state,
|
|
144
166
|
{ res, req }
|
|
145
167
|
) => {
|
|
@@ -154,6 +176,15 @@ const run = async (
|
|
|
154
176
|
const cfgMsg = incompleteCfgMsg();
|
|
155
177
|
if (cfgMsg) return cfgMsg;
|
|
156
178
|
let runInteractions = "";
|
|
179
|
+
let triggering_row_id;
|
|
180
|
+
if (table_id) {
|
|
181
|
+
const table = Table.findOne(table_id);
|
|
182
|
+
const pk = table?.pk_name;
|
|
183
|
+
if (table && state[pk])
|
|
184
|
+
//triggering_row = await table.getRow({ [pk]: state[pk] });
|
|
185
|
+
triggering_row_id = state[pk];
|
|
186
|
+
}
|
|
187
|
+
const initial_q = state.run_id ? undefined : state._q;
|
|
157
188
|
if (state.run_id) {
|
|
158
189
|
const run = prevRuns.find((r) => r.id == state.run_id);
|
|
159
190
|
const interactMarkups = [];
|
|
@@ -303,17 +334,26 @@ const run = async (
|
|
|
303
334
|
name: "run_id",
|
|
304
335
|
value: state.run_id ? +state.run_id : undefined,
|
|
305
336
|
}),
|
|
337
|
+
input({
|
|
338
|
+
type: "hidden",
|
|
339
|
+
class: "form-control ",
|
|
340
|
+
name: "triggering_row_id",
|
|
341
|
+
value: triggering_row_id || "",
|
|
342
|
+
}),
|
|
306
343
|
div(
|
|
307
344
|
{ class: "copilot-entry" },
|
|
308
|
-
textarea(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
345
|
+
textarea(
|
|
346
|
+
{
|
|
347
|
+
class: "form-control",
|
|
348
|
+
name: "userinput",
|
|
349
|
+
"data-fieldname": "userinput",
|
|
350
|
+
placeholder: placeholder || "How can I help you?",
|
|
351
|
+
id: "inputuserinput",
|
|
352
|
+
rows: "3",
|
|
353
|
+
autofocus: true,
|
|
354
|
+
},
|
|
355
|
+
initial_q
|
|
356
|
+
),
|
|
317
357
|
span(
|
|
318
358
|
{ class: "submit-button p-2", onclick: "$('form.copilot').submit()" },
|
|
319
359
|
i({ id: "sendbuttonicon", class: "far fa-paper-plane" })
|
|
@@ -333,8 +373,14 @@ const run = async (
|
|
|
333
373
|
{
|
|
334
374
|
class: "d-flex justify-content-between align-middle mb-2",
|
|
335
375
|
},
|
|
336
|
-
|
|
337
|
-
|
|
376
|
+
div(
|
|
377
|
+
{ class: "d-flex" },
|
|
378
|
+
i({
|
|
379
|
+
class: "fas fa-caret-down me-1 session-open-sessions",
|
|
380
|
+
onclick: "close_session_list()",
|
|
381
|
+
}),
|
|
382
|
+
h5(req.__("Sessions"))
|
|
383
|
+
),
|
|
338
384
|
button(
|
|
339
385
|
{
|
|
340
386
|
type: "button",
|
|
@@ -369,6 +415,17 @@ const run = async (
|
|
|
369
415
|
{ class: "card" },
|
|
370
416
|
div(
|
|
371
417
|
{ class: "card-body" },
|
|
418
|
+
div(
|
|
419
|
+
{
|
|
420
|
+
class: "open-prev-runs",
|
|
421
|
+
style: prev_runs_closed ? {} : { display: "none" },
|
|
422
|
+
onclick: "open_session_list()",
|
|
423
|
+
},
|
|
424
|
+
i({
|
|
425
|
+
class: "fas fa-caret-right me-1",
|
|
426
|
+
}),
|
|
427
|
+
req.__("Sessions")
|
|
428
|
+
),
|
|
372
429
|
div({ id: "copilotinteractions" }, runInteractions),
|
|
373
430
|
input_form,
|
|
374
431
|
style(
|
|
@@ -393,6 +450,9 @@ const run = async (
|
|
|
393
450
|
left: 0.1rem;
|
|
394
451
|
cursor: pointer;
|
|
395
452
|
}
|
|
453
|
+
.session-open-sessions, .open-prev-runs {
|
|
454
|
+
cursor: pointer;
|
|
455
|
+
}
|
|
396
456
|
.copilot-entry span.attach_agent_image_wrap {
|
|
397
457
|
position: relative;
|
|
398
458
|
top: -1.8rem;
|
|
@@ -403,6 +463,9 @@ const run = async (
|
|
|
403
463
|
top: -1.2rem;
|
|
404
464
|
display: block;
|
|
405
465
|
}
|
|
466
|
+
.col-0 {
|
|
467
|
+
width: 0%
|
|
468
|
+
}
|
|
406
469
|
.copilot-entry {margin-bottom: -1.25rem; margin-top: 1rem;}
|
|
407
470
|
p.prevrun_content {
|
|
408
471
|
white-space: nowrap;
|
|
@@ -411,7 +474,17 @@ const run = async (
|
|
|
411
474
|
display: block;
|
|
412
475
|
text-overflow: ellipsis;}`
|
|
413
476
|
),
|
|
414
|
-
script(
|
|
477
|
+
script(
|
|
478
|
+
`
|
|
479
|
+
function close_session_list() {
|
|
480
|
+
$("div.prev-runs-list").hide().parents(".col-3").removeClass("col-3").addClass("was-col-3").parent().children(".col-9").removeClass("col-9").addClass("col-12")
|
|
481
|
+
$("div.open-prev-runs").show()
|
|
482
|
+
}
|
|
483
|
+
function open_session_list() {
|
|
484
|
+
$("div.prev-runs-list").show().parents(".was-col-3").removeClass(["was-col-3","col-0","d-none"]).addClass("col-3").parent().children(".col-12").removeClass("col-12").addClass("col-9")
|
|
485
|
+
$("div.open-prev-runs").hide()
|
|
486
|
+
}
|
|
487
|
+
function processCopilotResponse(res) {
|
|
415
488
|
const hadFile = $("input#attach_agent_image").val();
|
|
416
489
|
$("span.filename-label").text("");
|
|
417
490
|
$("input#attach_agent_image").val(null);
|
|
@@ -441,10 +514,9 @@ const run = async (
|
|
|
441
514
|
const $runidin= $("input[name=run_id")
|
|
442
515
|
const runid = $runidin.val()
|
|
443
516
|
if(runid)
|
|
444
|
-
view_post('${viewname}', 'debug_info', {run_id:runid}, show_agent_debug_info)
|
|
517
|
+
view_post('${viewname}', 'debug_info', {run_id:runid, triggering_row_id:$("input[name=triggering_row_id").val()}, show_agent_debug_info)
|
|
445
518
|
}
|
|
446
519
|
function show_agent_debug_info(info) {
|
|
447
|
-
console.log(info)
|
|
448
520
|
ensure_modal_exists_and_closed();
|
|
449
521
|
$("#scmodal .modal-body").html(info.debug_html);
|
|
450
522
|
$("#scmodal .modal-title").html(decodeURIComponent("Agent session information"));
|
|
@@ -484,17 +556,20 @@ const run = async (
|
|
|
484
556
|
document.getElementById("inputuserinput").addEventListener("keydown", submitOnEnter);
|
|
485
557
|
function spin_send_button() {
|
|
486
558
|
$("#sendbuttonicon").attr("class","fas fa-spinner fa-spin");
|
|
487
|
-
}
|
|
488
|
-
|
|
559
|
+
};`,
|
|
560
|
+
initial_q && domReady("$('form.copilot').submit()")
|
|
561
|
+
)
|
|
489
562
|
)
|
|
490
563
|
);
|
|
491
564
|
return show_prev_runs
|
|
492
565
|
? {
|
|
493
|
-
widths: [3, 9],
|
|
566
|
+
widths: prev_runs_closed ? [0, 12] : [3, 9],
|
|
567
|
+
colClasses: prev_runs_closed ? ["was-col-3 d-none"] : undefined,
|
|
494
568
|
gx: 3,
|
|
495
569
|
besides: [
|
|
496
570
|
{
|
|
497
571
|
type: "container",
|
|
572
|
+
customClass: "prev-runs-list",
|
|
498
573
|
contents: prev_runs_side_bar,
|
|
499
574
|
},
|
|
500
575
|
{
|
|
@@ -507,20 +582,34 @@ const run = async (
|
|
|
507
582
|
};
|
|
508
583
|
|
|
509
584
|
const interact = async (table_id, viewname, config, body, { req, res }) => {
|
|
510
|
-
const { userinput, run_id } = body;
|
|
585
|
+
const { userinput, run_id, triggering_row_id } = body;
|
|
586
|
+
const action = await Trigger.findOne({ id: config.action_id });
|
|
587
|
+
|
|
511
588
|
let run;
|
|
512
|
-
|
|
589
|
+
let triggering_row;
|
|
590
|
+
if (table_id && triggering_row_id) {
|
|
591
|
+
const table = Table.findOne(table_id);
|
|
592
|
+
const pk = table?.pk_name;
|
|
593
|
+
if (table) triggering_row = await table.getRow({ [pk]: triggering_row_id });
|
|
594
|
+
}
|
|
595
|
+
if (!run_id || run_id === "undefined") {
|
|
596
|
+
const ini_interacts = await get_initial_interactions(
|
|
597
|
+
action.configuration,
|
|
598
|
+
req.user,
|
|
599
|
+
triggering_row
|
|
600
|
+
);
|
|
513
601
|
run = await WorkflowRun.create({
|
|
514
602
|
status: "Running",
|
|
515
603
|
started_by: req.user?.id,
|
|
516
604
|
trigger_id: config.action_id,
|
|
517
605
|
context: {
|
|
518
606
|
implemented_fcall_ids: [],
|
|
519
|
-
interactions: [{ role: "user", content: userinput }],
|
|
607
|
+
interactions: [...ini_interacts, { role: "user", content: userinput }],
|
|
520
608
|
funcalls: {},
|
|
609
|
+
triggering_row_id,
|
|
521
610
|
},
|
|
522
611
|
});
|
|
523
|
-
else {
|
|
612
|
+
} else {
|
|
524
613
|
run = await WorkflowRun.findOne({ id: +run_id });
|
|
525
614
|
await addToContext(run, {
|
|
526
615
|
interactions: [{ role: "user", content: userinput }],
|
|
@@ -561,9 +650,15 @@ const interact = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
561
650
|
],
|
|
562
651
|
});
|
|
563
652
|
}
|
|
564
|
-
const action = await Trigger.findOne({ id: config.action_id });
|
|
565
653
|
|
|
566
|
-
return await process_interaction(
|
|
654
|
+
return await process_interaction(
|
|
655
|
+
run,
|
|
656
|
+
action.configuration,
|
|
657
|
+
req,
|
|
658
|
+
action.name,
|
|
659
|
+
[],
|
|
660
|
+
triggering_row
|
|
661
|
+
);
|
|
567
662
|
};
|
|
568
663
|
|
|
569
664
|
const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
|
|
@@ -578,13 +673,19 @@ const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
578
673
|
};
|
|
579
674
|
|
|
580
675
|
const debug_info = async (table_id, viewname, config, body, { req, res }) => {
|
|
581
|
-
const { run_id } = body;
|
|
676
|
+
const { run_id, triggering_row_id } = body;
|
|
582
677
|
const action = await Trigger.findOne({ id: config.action_id });
|
|
583
|
-
|
|
678
|
+
let triggering_row;
|
|
679
|
+
if (table_id && triggering_row_id) {
|
|
680
|
+
const table = Table.findOne(table_id);
|
|
681
|
+
const pk = table?.pk_name;
|
|
682
|
+
if (table) triggering_row = await table.getRow({ [pk]: triggering_row_id });
|
|
683
|
+
}
|
|
584
684
|
const run = await WorkflowRun.findOne({ id: +run_id });
|
|
585
685
|
const complArgs = await getCompletionArguments(
|
|
586
686
|
action.configuration,
|
|
587
|
-
req.user
|
|
687
|
+
req.user,
|
|
688
|
+
triggering_row
|
|
588
689
|
);
|
|
589
690
|
const debug_html = div(
|
|
590
691
|
div(h4("System prompt"), pre(complArgs.systemPrompt)),
|
package/common.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
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
|
+
const { interpolate } = require("@saltcorn/data/utils");
|
|
4
5
|
|
|
5
6
|
const MarkdownIt = require("markdown-it"),
|
|
6
7
|
md = new MarkdownIt();
|
|
7
8
|
|
|
9
|
+
const nubBy = (f, xs) => {
|
|
10
|
+
const vs = new Set();
|
|
11
|
+
return xs.filter((x) => {
|
|
12
|
+
const y = f(x);
|
|
13
|
+
if (vs.has(y)) return false;
|
|
14
|
+
vs.add(y);
|
|
15
|
+
return true;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
8
19
|
const get_skills = () => {
|
|
20
|
+
const state = getState();
|
|
21
|
+
const exchange_skills = nubBy(
|
|
22
|
+
(c) => c.constructor.name,
|
|
23
|
+
state.exchange?.agent_skills || []
|
|
24
|
+
);
|
|
25
|
+
|
|
9
26
|
return [
|
|
10
27
|
require("./skills/FTSRetrieval"),
|
|
11
28
|
require("./skills/EmbeddingRetrieval"),
|
|
@@ -15,6 +32,7 @@ const get_skills = () => {
|
|
|
15
32
|
require("./skills/GenerateImage"),
|
|
16
33
|
require("./skills/ModelContextProtocol"),
|
|
17
34
|
//require("./skills/AdaptiveFeedback"),
|
|
35
|
+
...exchange_skills,
|
|
18
36
|
];
|
|
19
37
|
};
|
|
20
38
|
|
|
@@ -61,14 +79,26 @@ const find_image_tool = (config) => {
|
|
|
61
79
|
}
|
|
62
80
|
};
|
|
63
81
|
|
|
64
|
-
const
|
|
82
|
+
const get_initial_interactions = async (config, user, triggering_row) => {
|
|
83
|
+
const interacts = [];
|
|
84
|
+
const skills = get_skill_instances(config);
|
|
85
|
+
for (const skill of skills) {
|
|
86
|
+
const its = await skill.initialInteractions?.({ user, triggering_row });
|
|
87
|
+
if (its) interacts.push(...its);
|
|
88
|
+
}
|
|
89
|
+
return interacts;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getCompletionArguments = async (config, user, triggering_row) => {
|
|
65
93
|
let tools = [];
|
|
66
94
|
|
|
67
|
-
let sysPrompts = [
|
|
95
|
+
let sysPrompts = [
|
|
96
|
+
interpolate(config.sys_prompt, triggering_row || {}, user, "System prompt"),
|
|
97
|
+
];
|
|
68
98
|
|
|
69
99
|
const skills = get_skill_instances(config);
|
|
70
100
|
for (const skill of skills) {
|
|
71
|
-
const sysPr = await skill.systemPrompt?.({ user });
|
|
101
|
+
const sysPr = await skill.systemPrompt?.({ user, triggering_row });
|
|
72
102
|
if (sysPr) sysPrompts.push(sysPr);
|
|
73
103
|
const skillTools = skill.provideTools?.();
|
|
74
104
|
if (skillTools && Array.isArray(skillTools)) tools.push(...skillTools);
|
|
@@ -163,9 +193,14 @@ const process_interaction = async (
|
|
|
163
193
|
config,
|
|
164
194
|
req,
|
|
165
195
|
agent_label = "Copilot",
|
|
166
|
-
prevResponses = []
|
|
196
|
+
prevResponses = [],
|
|
197
|
+
triggering_row = {}
|
|
167
198
|
) => {
|
|
168
|
-
const complArgs = await getCompletionArguments(
|
|
199
|
+
const complArgs = await getCompletionArguments(
|
|
200
|
+
config,
|
|
201
|
+
req.user,
|
|
202
|
+
triggering_row
|
|
203
|
+
);
|
|
169
204
|
complArgs.chat = run.context.interactions.map(only_response_text_if_present);
|
|
170
205
|
//complArgs.debugResult = true;
|
|
171
206
|
//console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
@@ -300,10 +335,14 @@ const process_interaction = async (
|
|
|
300
335
|
}
|
|
301
336
|
}
|
|
302
337
|
if (hasResult)
|
|
303
|
-
return await process_interaction(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
338
|
+
return await process_interaction(
|
|
339
|
+
run,
|
|
340
|
+
config,
|
|
341
|
+
req,
|
|
342
|
+
agent_label,
|
|
343
|
+
[...prevResponses, ...responses],
|
|
344
|
+
triggering_row
|
|
345
|
+
);
|
|
307
346
|
} else if (typeof answer === "string")
|
|
308
347
|
responses.push(wrapSegment(md.render(answer), agent_label));
|
|
309
348
|
|
|
@@ -330,4 +369,6 @@ module.exports = {
|
|
|
330
369
|
process_interaction,
|
|
331
370
|
find_image_tool,
|
|
332
371
|
is_debug_mode,
|
|
372
|
+
get_initial_interactions,
|
|
373
|
+
nubBy,
|
|
333
374
|
};
|
package/index.js
CHANGED
|
@@ -51,7 +51,8 @@ module.exports = {
|
|
|
51
51
|
{
|
|
52
52
|
name: "sys_prompt",
|
|
53
53
|
label: "System prompt",
|
|
54
|
-
sublabel:
|
|
54
|
+
sublabel:
|
|
55
|
+
"Additional information for the system prompt. Use interpolations <code>{{ }}</code> to access triggering row variables or user",
|
|
55
56
|
type: "String",
|
|
56
57
|
fieldview: "textarea",
|
|
57
58
|
},
|
|
@@ -84,7 +85,14 @@ module.exports = {
|
|
|
84
85
|
funcalls: {},
|
|
85
86
|
},
|
|
86
87
|
});
|
|
87
|
-
return await process_interaction(
|
|
88
|
+
return await process_interaction(
|
|
89
|
+
run,
|
|
90
|
+
configuration,
|
|
91
|
+
req,
|
|
92
|
+
undefined,
|
|
93
|
+
[],
|
|
94
|
+
row
|
|
95
|
+
);
|
|
88
96
|
},
|
|
89
97
|
},
|
|
90
98
|
},
|
package/locales/de.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "Sessions": "Sitzungen" }
|
package/package.json
CHANGED
package/skills/FTSRetrieval.js
CHANGED
|
@@ -6,6 +6,7 @@ const View = require("@saltcorn/data/models/view");
|
|
|
6
6
|
const { getState } = require("@saltcorn/data/db/state");
|
|
7
7
|
const db = require("@saltcorn/data/db");
|
|
8
8
|
const { interpolate } = require("@saltcorn/data/utils");
|
|
9
|
+
const { nubBy } = require("../common");
|
|
9
10
|
|
|
10
11
|
class RetrievalByFullTextSearch {
|
|
11
12
|
static skill_name = "Retrieval by full-text search";
|
|
@@ -105,30 +106,45 @@ class RetrievalByFullTextSearch {
|
|
|
105
106
|
const table = Table.findOne(this.table_name);
|
|
106
107
|
return {
|
|
107
108
|
type: "function",
|
|
108
|
-
process: async (
|
|
109
|
+
process: async (arg, { req }) => {
|
|
109
110
|
const scState = getState();
|
|
110
111
|
const language = scState.pg_ts_config;
|
|
111
112
|
const use_websearch = scState.getConfig("search_use_websearch", false);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
let rows = [];
|
|
114
|
+
const phrases =
|
|
115
|
+
arg.query?.phrases ||
|
|
116
|
+
arg.phrases ||
|
|
117
|
+
(arg.phrase
|
|
118
|
+
? [arg.phrase]
|
|
119
|
+
: arg.query?.phrase
|
|
120
|
+
? [arg.query?.phrase]
|
|
121
|
+
: []);
|
|
122
|
+
for (const phrase of phrases) {
|
|
123
|
+
const my_rows = await table.getRows({
|
|
124
|
+
_fts: {
|
|
125
|
+
fields: table.fields,
|
|
126
|
+
searchTerm: phrase,
|
|
127
|
+
language,
|
|
128
|
+
use_websearch,
|
|
129
|
+
table: table.name,
|
|
130
|
+
schema: db.isSQLite ? undefined : db.getTenantSchema(),
|
|
131
|
+
},
|
|
130
132
|
});
|
|
133
|
+
if (this.hidden_fields) {
|
|
134
|
+
const hidden_fields = this.hidden_fields
|
|
135
|
+
.split(",")
|
|
136
|
+
.map((s) => s.trim());
|
|
137
|
+
my_rows.forEach((r) => {
|
|
138
|
+
hidden_fields.forEach((k) => {
|
|
139
|
+
delete r[k];
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
rows.push(...my_rows);
|
|
131
144
|
}
|
|
145
|
+
const pk = table.pk_name;
|
|
146
|
+
rows = nubBy((r) => r[pk], rows);
|
|
147
|
+
|
|
132
148
|
if (rows.length)
|
|
133
149
|
if (!this.doc_format) return { rows };
|
|
134
150
|
else {
|
|
@@ -139,7 +155,8 @@ class RetrievalByFullTextSearch {
|
|
|
139
155
|
}
|
|
140
156
|
else
|
|
141
157
|
return {
|
|
142
|
-
responseText:
|
|
158
|
+
responseText:
|
|
159
|
+
"There are no rows related to: " + phrases.join(" or "),
|
|
143
160
|
};
|
|
144
161
|
},
|
|
145
162
|
/*renderToolCall({ phrase }, { req }) {
|
|
@@ -163,15 +180,41 @@ class RetrievalByFullTextSearch {
|
|
|
163
180
|
name: this.toolName,
|
|
164
181
|
description: `Search the ${this.table_name} database table${
|
|
165
182
|
table.description ? ` (${table.description})` : ""
|
|
166
|
-
} by
|
|
183
|
+
} by one or several search phrase or multiple search phrases matched against all fields in the table with full text search. The retrieved rows will be returned. If you want to search for documents matching any of several phrases, call this tool once with the phrases argument and give all the phrases you want to search for in one tool call.`,
|
|
167
184
|
parameters: {
|
|
168
185
|
type: "object",
|
|
169
|
-
required: ["
|
|
186
|
+
required: ["query"],
|
|
170
187
|
properties: {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
188
|
+
query: {
|
|
189
|
+
anyOf: [
|
|
190
|
+
{
|
|
191
|
+
type: "object",
|
|
192
|
+
required: ["phrase"],
|
|
193
|
+
properties: {
|
|
194
|
+
phrase: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description:
|
|
197
|
+
"The phrase to search the table with. The search phrase is the synatx used by web search engines: use double quotes for exact match, unquoted text for words in any order, dash (minus sign) to exclude a word. Do not use SQL or any other formal query language.",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: "object",
|
|
203
|
+
required: ["phrases"],
|
|
204
|
+
description:
|
|
205
|
+
"Search the table by any of a number of phrases. This will return any document that matches one or the other of the phrases",
|
|
206
|
+
properties: {
|
|
207
|
+
phrases: {
|
|
208
|
+
type: "array",
|
|
209
|
+
description:
|
|
210
|
+
"A phrase to search the table with. The search phrase is the synatx used by web search engines: use double quotes for exact match, unquoted text for words in any order, dash (minus sign) to exclude a word. Do not use SQL or any other formal query language.",
|
|
211
|
+
items: {
|
|
212
|
+
type: "string",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
],
|
|
175
218
|
},
|
|
176
219
|
},
|
|
177
220
|
},
|
package/skills/PreloadData.js
CHANGED
|
@@ -2,11 +2,14 @@ const { div, pre } = require("@saltcorn/markup/tags");
|
|
|
2
2
|
const Workflow = require("@saltcorn/data/models/workflow");
|
|
3
3
|
const Form = require("@saltcorn/data/models/form");
|
|
4
4
|
const Table = require("@saltcorn/data/models/table");
|
|
5
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
6
|
+
const User = require("@saltcorn/data/models/user");
|
|
5
7
|
const View = require("@saltcorn/data/models/view");
|
|
6
8
|
const { getState } = require("@saltcorn/data/db/state");
|
|
7
9
|
const db = require("@saltcorn/data/db");
|
|
8
10
|
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
9
11
|
const { interpolate } = require("@saltcorn/data/utils");
|
|
12
|
+
const vm = require("vm");
|
|
10
13
|
|
|
11
14
|
class PreloadData {
|
|
12
15
|
static skill_name = "Preload Data";
|
|
@@ -19,28 +22,69 @@ class PreloadData {
|
|
|
19
22
|
Object.assign(this, cfg);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
async
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{},
|
|
25
|
+
async run_the_code({ user, triggering_row }) {
|
|
26
|
+
const sysState = getState();
|
|
27
|
+
const f = vm.runInNewContext(`async () => {${this.code}\n}`, {
|
|
28
|
+
Table,
|
|
29
|
+
row: triggering_row,
|
|
30
|
+
context: triggering_row,
|
|
29
31
|
user,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
User,
|
|
33
|
+
File,
|
|
34
|
+
Buffer,
|
|
35
|
+
Trigger,
|
|
36
|
+
setTimeout,
|
|
37
|
+
interpolate,
|
|
38
|
+
require,
|
|
39
|
+
getConfig: (k) =>
|
|
40
|
+
sysState.isFixedConfig(k) ? undefined : sysState.getConfig(k),
|
|
41
|
+
...(triggering_row || {}),
|
|
42
|
+
...sysState.eval_context,
|
|
43
|
+
});
|
|
44
|
+
return await f();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async initialInteractions({ user, triggering_row }) {
|
|
48
|
+
if (this._stashed_initial_interactions)
|
|
49
|
+
return this._stashed_initial_interactions;
|
|
50
|
+
if (this.data_source === "Code") {
|
|
51
|
+
const result = await this.run_the_code({ user, triggering_row });
|
|
52
|
+
return result?.interactions;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
async systemPrompt({ user, triggering_row }) {
|
|
57
|
+
const prompts = [];
|
|
58
|
+
if (this.data_source === "Code") {
|
|
59
|
+
const result = await this.run_the_code({ user, triggering_row });
|
|
60
|
+
if (typeof result === "string") return result;
|
|
61
|
+
if (result?.interactions)
|
|
62
|
+
this._stashed_initial_interactions = result?.interactions;
|
|
63
|
+
if (result?.systemPrompt) return result.systemPrompt;
|
|
37
64
|
} else {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
65
|
+
if (this.add_sys_prompt) prompts.push(this.add_sys_prompt);
|
|
66
|
+
const table = Table.findOne(this.table_name);
|
|
67
|
+
const q = eval_expression(
|
|
68
|
+
this.preload_query,
|
|
69
|
+
{},
|
|
70
|
+
user,
|
|
71
|
+
"PreloadData query"
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const rows = await table.getRows(q);
|
|
75
|
+
if (this.contents_expr) {
|
|
76
|
+
for (const row of rows)
|
|
77
|
+
prompts.push(interpolate(this.contents_expr, row, user));
|
|
78
|
+
} else {
|
|
79
|
+
const hidden_fields = this.hidden_fields
|
|
80
|
+
.split(",")
|
|
81
|
+
.map((s) => s.trim());
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
hidden_fields.forEach((k) => {
|
|
84
|
+
delete row[k];
|
|
85
|
+
});
|
|
86
|
+
prompts.push(JSON.stringify(row));
|
|
87
|
+
}
|
|
44
88
|
}
|
|
45
89
|
}
|
|
46
90
|
return prompts.join("\n");
|
|
@@ -50,6 +94,34 @@ class PreloadData {
|
|
|
50
94
|
const allTables = await Table.find();
|
|
51
95
|
|
|
52
96
|
return [
|
|
97
|
+
{
|
|
98
|
+
name: "data_source",
|
|
99
|
+
label: "Data source",
|
|
100
|
+
type: "String",
|
|
101
|
+
required: true,
|
|
102
|
+
attributes: { options: ["Table", "Code"] },
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "code",
|
|
106
|
+
label: "Code",
|
|
107
|
+
input_type: "code",
|
|
108
|
+
attributes: { mode: "application/javascript" },
|
|
109
|
+
showIf: { data_source: "Code" },
|
|
110
|
+
class: "validate-statements",
|
|
111
|
+
sublabel:
|
|
112
|
+
"Return string or object <code>{systemPrompt: string, interactions: Interaction[]}</code>",
|
|
113
|
+
validator(s) {
|
|
114
|
+
try {
|
|
115
|
+
let AsyncFunction = Object.getPrototypeOf(
|
|
116
|
+
async function () {}
|
|
117
|
+
).constructor;
|
|
118
|
+
AsyncFunction(s);
|
|
119
|
+
return true;
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return e.message;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
},
|
|
53
125
|
{
|
|
54
126
|
name: "table_name",
|
|
55
127
|
label: "Table",
|
|
@@ -57,23 +129,28 @@ class PreloadData {
|
|
|
57
129
|
type: "String",
|
|
58
130
|
required: true,
|
|
59
131
|
attributes: { options: allTables.map((t) => t.name) },
|
|
132
|
+
showIf: { data_source: "Table" },
|
|
60
133
|
},
|
|
61
134
|
{
|
|
62
135
|
name: "preload_query",
|
|
63
136
|
label: "Query",
|
|
64
137
|
type: "String",
|
|
65
138
|
class: "validate-expression",
|
|
139
|
+
showIf: { data_source: "Table" },
|
|
66
140
|
},
|
|
67
141
|
{
|
|
68
142
|
name: "add_sys_prompt",
|
|
69
143
|
label: "Additional prompt",
|
|
70
144
|
type: "String",
|
|
145
|
+
showIf: { data_source: "Table" },
|
|
71
146
|
fieldview: "textarea",
|
|
72
147
|
},
|
|
73
148
|
{
|
|
74
149
|
name: "contents_expr",
|
|
75
150
|
label: "Contents string",
|
|
76
151
|
type: "String",
|
|
152
|
+
fieldview: "textarea",
|
|
153
|
+
showIf: { data_source: "Table" },
|
|
77
154
|
sublabel:
|
|
78
155
|
"Use handlebars (<code>{{ }}</code>) to access fields in each retrieved row",
|
|
79
156
|
},
|
|
@@ -81,6 +158,7 @@ class PreloadData {
|
|
|
81
158
|
name: "hidden_fields",
|
|
82
159
|
label: "Hide fields",
|
|
83
160
|
type: "String",
|
|
161
|
+
showIf: { data_source: "Table" },
|
|
84
162
|
sublabel:
|
|
85
163
|
"Comma-separated list of fields to hide from the prompt, if not using the contents string",
|
|
86
164
|
},
|