@saltcorn/agents 0.6.7 → 0.6.9
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 +23 -16
- package/common.js +23 -1
- package/package.json +1 -1
- package/skills/Fetch.js +62 -0
- package/skills/GenerateAndRunJsCode.js +422 -0
package/agent-view.js
CHANGED
|
@@ -32,6 +32,7 @@ const {
|
|
|
32
32
|
a,
|
|
33
33
|
br,
|
|
34
34
|
img,
|
|
35
|
+
text,
|
|
35
36
|
} = require("@saltcorn/markup/tags");
|
|
36
37
|
const { getState } = require("@saltcorn/data/db/state");
|
|
37
38
|
const {
|
|
@@ -50,7 +51,7 @@ const {
|
|
|
50
51
|
} = require("./common");
|
|
51
52
|
const MarkdownIt = require("markdown-it"),
|
|
52
53
|
md = new MarkdownIt();
|
|
53
|
-
const { isWeb } = require("@saltcorn/data/utils");
|
|
54
|
+
const { isWeb, escapeHtml } = require("@saltcorn/data/utils");
|
|
54
55
|
const path = require("path");
|
|
55
56
|
|
|
56
57
|
const configuration_workflow = (req) =>
|
|
@@ -240,7 +241,7 @@ const run = async (
|
|
|
240
241
|
state,
|
|
241
242
|
{ res, req },
|
|
242
243
|
) => {
|
|
243
|
-
const action = agent_action || await Trigger.findOne({ id: action_id });
|
|
244
|
+
const action = agent_action || (await Trigger.findOne({ id: action_id }));
|
|
244
245
|
if (!action) throw new Error(`Action not found: ${action_id}`);
|
|
245
246
|
const prevRuns = show_prev_runs
|
|
246
247
|
? (
|
|
@@ -652,7 +653,7 @@ const run = async (
|
|
|
652
653
|
).join(" ");
|
|
653
654
|
}
|
|
654
655
|
$("span.filename-label").text("").removeClass("me-2");
|
|
655
|
-
_agentDT.items.clear();
|
|
656
|
+
window._agentDT.items.clear();
|
|
656
657
|
$("input#attach_agent_image").val(null);
|
|
657
658
|
if(!not_final || (!${JSON.stringify(dyn_updates)})) $("#sendbuttonicon").attr("class","far fa-paper-plane");
|
|
658
659
|
const $runidin= $("input[name=run_id")
|
|
@@ -672,20 +673,20 @@ const run = async (
|
|
|
672
673
|
window.final_agent_response = () => {
|
|
673
674
|
$("#sendbuttonicon").attr("class","far fa-paper-plane");
|
|
674
675
|
}
|
|
675
|
-
|
|
676
|
+
window._agentDT = new DataTransfer();
|
|
676
677
|
function setAgentFiles(files) {
|
|
677
|
-
for (const f of files) _agentDT.items.add(f);
|
|
678
|
-
document.getElementById('attach_agent_image').files = _agentDT.files;
|
|
678
|
+
for (const f of files) window._agentDT.items.add(f);
|
|
679
|
+
document.getElementById('attach_agent_image').files = window._agentDT.files;
|
|
679
680
|
updateFileLabel();
|
|
680
681
|
}
|
|
681
682
|
function updateFileLabel() {
|
|
682
|
-
const n = _agentDT.files.length;
|
|
683
|
+
const n = window._agentDT.files.length;
|
|
683
684
|
const $label = $(".attach_agent_image_wrap span.filename-label");
|
|
684
685
|
if (n === 0) {
|
|
685
686
|
$label.html("").removeClass("me-2");
|
|
686
687
|
} else {
|
|
687
688
|
$label.addClass("me-2");
|
|
688
|
-
const text = n === 1 ? _agentDT.files[0].name : n + " files";
|
|
689
|
+
const text = n === 1 ? window._agentDT.files[0].name : n + " files";
|
|
689
690
|
$label.html(${
|
|
690
691
|
isWeb(req)
|
|
691
692
|
? `text + ' <span class="badge text-bg-secondary" style="cursor:pointer;font-size:.65em;vertical-align:middle" onclick="clearAgentFiles()" title="Remove files">×</span>'`
|
|
@@ -694,13 +695,13 @@ const run = async (
|
|
|
694
695
|
}
|
|
695
696
|
}
|
|
696
697
|
function clearAgentFiles() {
|
|
697
|
-
_agentDT.items.clear();
|
|
698
|
+
window._agentDT.items.clear();
|
|
698
699
|
$("input#attach_agent_image").val(null);
|
|
699
700
|
updateFileLabel();
|
|
700
701
|
}
|
|
701
702
|
window.clearAgentFiles = clearAgentFiles;
|
|
702
703
|
function agent_file_attach(e) {
|
|
703
|
-
_agentDT.items.clear();
|
|
704
|
+
window._agentDT.items.clear();
|
|
704
705
|
setAgentFiles(e.target.files);
|
|
705
706
|
}
|
|
706
707
|
function restore_old_button_elem(btn) {
|
|
@@ -820,7 +821,8 @@ const run = async (
|
|
|
820
821
|
|
|
821
822
|
const interact = async (table_id, viewname, config, body, { req, res }) => {
|
|
822
823
|
const { userinput, run_id, triggering_row_id } = body;
|
|
823
|
-
const action =
|
|
824
|
+
const action =
|
|
825
|
+
config.agent_action || (await Trigger.findOne({ id: config.action_id }));
|
|
824
826
|
|
|
825
827
|
let run;
|
|
826
828
|
let triggering_row;
|
|
@@ -951,7 +953,8 @@ const delprevrun = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
951
953
|
|
|
952
954
|
const debug_info = async (table_id, viewname, config, body, { req, res }) => {
|
|
953
955
|
const { run_id, triggering_row_id } = body;
|
|
954
|
-
const action =
|
|
956
|
+
const action =
|
|
957
|
+
config.agent_action || (await Trigger.findOne({ id: config.action_id }));
|
|
955
958
|
let triggering_row;
|
|
956
959
|
if (table_id && triggering_row_id) {
|
|
957
960
|
const table = Table.findOne(table_id);
|
|
@@ -974,10 +977,12 @@ const debug_info = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
974
977
|
sysPrompt = complArgs.systemPrompt;
|
|
975
978
|
}
|
|
976
979
|
const debug_html = div(
|
|
977
|
-
div(h4("System prompt"), pre(sysPrompt)),
|
|
980
|
+
div(h4("System prompt"), pre(text(escapeHtml(sysPrompt)))),
|
|
978
981
|
div(
|
|
979
982
|
h4("API interactions"),
|
|
980
|
-
pre(
|
|
983
|
+
pre(
|
|
984
|
+
text(escapeHtml(JSON.stringify(run.context.api_interactions, null, 2))),
|
|
985
|
+
),
|
|
981
986
|
),
|
|
982
987
|
);
|
|
983
988
|
if (run && req.user?.role_id === 1)
|
|
@@ -993,7 +998,8 @@ const debug_info = async (table_id, viewname, config, body, { req, res }) => {
|
|
|
993
998
|
|
|
994
999
|
const skillroute = async (table_id, viewname, config, body, { req, res }) => {
|
|
995
1000
|
const { run_id, triggering_row_id, skillid } = body;
|
|
996
|
-
const action =
|
|
1001
|
+
const action =
|
|
1002
|
+
config.agent_action || (await Trigger.findOne({ id: config.action_id }));
|
|
997
1003
|
let triggering_row;
|
|
998
1004
|
if (table_id && triggering_row_id) {
|
|
999
1005
|
const table = Table.findOne(table_id);
|
|
@@ -1030,7 +1036,8 @@ const execute_user_action = async (
|
|
|
1030
1036
|
) => {
|
|
1031
1037
|
const { run_id, rndid, uaname } = body;
|
|
1032
1038
|
|
|
1033
|
-
const action =
|
|
1039
|
+
const action =
|
|
1040
|
+
config.agent_action || (await Trigger.findOne({ id: config.action_id }));
|
|
1034
1041
|
const run = await WorkflowRun.findOne({ id: +run_id });
|
|
1035
1042
|
//console.log("run uas",run.context.user_actions );
|
|
1036
1043
|
|
package/common.js
CHANGED
|
@@ -35,6 +35,8 @@ const get_skills = () => {
|
|
|
35
35
|
require("./skills/ModelContextProtocol"),
|
|
36
36
|
require("./skills/PromptPicker"),
|
|
37
37
|
require("./skills/RunJsCode"),
|
|
38
|
+
require("./skills/GenerateAndRunJsCode"),
|
|
39
|
+
require("./skills/Fetch"),
|
|
38
40
|
//require("./skills/AdaptiveFeedback"),
|
|
39
41
|
...exchange_skills,
|
|
40
42
|
];
|
|
@@ -429,7 +431,7 @@ const process_interaction = async (
|
|
|
429
431
|
{ role: "system", content: postprocres.add_system_prompt },
|
|
430
432
|
],
|
|
431
433
|
});
|
|
432
|
-
if (postprocres.add_response)
|
|
434
|
+
if (postprocres.add_response) {
|
|
433
435
|
add_response(
|
|
434
436
|
wrapSegment(
|
|
435
437
|
wrapCard(
|
|
@@ -439,6 +441,26 @@ const process_interaction = async (
|
|
|
439
441
|
agent_label,
|
|
440
442
|
),
|
|
441
443
|
);
|
|
444
|
+
//replace tool response with this
|
|
445
|
+
// run.context.interactions.forEach((ic) => {});
|
|
446
|
+
const result = postprocres.add_response;
|
|
447
|
+
await sysState.functions.llm_add_message.run(
|
|
448
|
+
"tool_response",
|
|
449
|
+
!result || typeof result === "string"
|
|
450
|
+
? {
|
|
451
|
+
type: "text",
|
|
452
|
+
value: result || "Action run",
|
|
453
|
+
}
|
|
454
|
+
: {
|
|
455
|
+
type: "json",
|
|
456
|
+
value: JSON.parse(JSON.stringify(result)),
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
chat: run.context.interactions,
|
|
460
|
+
tool_call,
|
|
461
|
+
},
|
|
462
|
+
);
|
|
463
|
+
}
|
|
442
464
|
if (postprocres.add_user_action && viewname) {
|
|
443
465
|
const user_actions = Array.isArray()
|
|
444
466
|
? postprocres.add_user_action
|
package/package.json
CHANGED
package/skills/Fetch.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const { div, pre, a } = 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 User = require("@saltcorn/data/models/user");
|
|
6
|
+
const File = require("@saltcorn/data/models/file");
|
|
7
|
+
const View = require("@saltcorn/data/models/view");
|
|
8
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
9
|
+
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
10
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
11
|
+
const db = require("@saltcorn/data/db");
|
|
12
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
13
|
+
const { interpolate, sleep } = require("@saltcorn/data/utils");
|
|
14
|
+
const { features } = require("@saltcorn/data/db/state");
|
|
15
|
+
const { button } = require("@saltcorn/markup/tags");
|
|
16
|
+
const { validID } = require("@saltcorn/markup/layout_utils");
|
|
17
|
+
|
|
18
|
+
const vm = require("vm");
|
|
19
|
+
|
|
20
|
+
//const { fieldProperties } = require("./helpers");
|
|
21
|
+
|
|
22
|
+
class FetchSkill {
|
|
23
|
+
static skill_name = "Fetch";
|
|
24
|
+
|
|
25
|
+
get skill_label() {
|
|
26
|
+
return "Fetch";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor(cfg) {
|
|
30
|
+
Object.assign(this, cfg);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static async configFields() {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
provideTools = () => {
|
|
38
|
+
return {
|
|
39
|
+
type: "function",
|
|
40
|
+
process: async (row) => {
|
|
41
|
+
const resp = await fetch(row.url);
|
|
42
|
+
return await resp.text();
|
|
43
|
+
},
|
|
44
|
+
function: {
|
|
45
|
+
name: "fetch_web_page",
|
|
46
|
+
description: "fetch a web page with HTTP(S) GET",
|
|
47
|
+
parameters: {
|
|
48
|
+
type: "object",
|
|
49
|
+
required: ["url"],
|
|
50
|
+
properties: {
|
|
51
|
+
url: {
|
|
52
|
+
description: "The URL to fetch with HTTP",
|
|
53
|
+
type: "string",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = FetchSkill;
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
const { div, pre, a } = 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 User = require("@saltcorn/data/models/user");
|
|
6
|
+
const File = require("@saltcorn/data/models/file");
|
|
7
|
+
const View = require("@saltcorn/data/models/view");
|
|
8
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
9
|
+
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
10
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
11
|
+
const db = require("@saltcorn/data/db");
|
|
12
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
13
|
+
const { interpolate, sleep } = require("@saltcorn/data/utils");
|
|
14
|
+
const { features } = require("@saltcorn/data/db/state");
|
|
15
|
+
const { button } = require("@saltcorn/markup/tags");
|
|
16
|
+
const { validID } = require("@saltcorn/markup/layout_utils");
|
|
17
|
+
|
|
18
|
+
const vm = require("vm");
|
|
19
|
+
|
|
20
|
+
//const { fieldProperties } = require("./helpers");
|
|
21
|
+
|
|
22
|
+
class GenerateAndRunJsCodeSkill {
|
|
23
|
+
static skill_name = "Generate and run JavaScript code";
|
|
24
|
+
|
|
25
|
+
get skill_label() {
|
|
26
|
+
return "Generate and run JavaScript code";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor(cfg) {
|
|
30
|
+
Object.assign(this, cfg);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async runCode(code, { user, req }) {
|
|
34
|
+
const sysState = getState();
|
|
35
|
+
|
|
36
|
+
const f = vm.runInNewContext(`async () => {${code}\n}`, {
|
|
37
|
+
...(this.allow_table
|
|
38
|
+
? {
|
|
39
|
+
Table: Table.subClass
|
|
40
|
+
? Table.subClass({ user, read_only: this.read_only })
|
|
41
|
+
: Table,
|
|
42
|
+
}
|
|
43
|
+
: {}),
|
|
44
|
+
...(this.allow_fetch ? { fetch } : {}),
|
|
45
|
+
user,
|
|
46
|
+
console,
|
|
47
|
+
sleep,
|
|
48
|
+
setTimeout,
|
|
49
|
+
});
|
|
50
|
+
return await f();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async systemPrompt({ triggering_row, user }) {
|
|
54
|
+
return this.add_sys_prompt || "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async skillRoute({ run, triggering_row, req }) {
|
|
58
|
+
return await this.runCode({ row: triggering_row, run, user: req.user });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static async configFields() {
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
name: "tool_name",
|
|
65
|
+
label: "Tool name",
|
|
66
|
+
type: "String",
|
|
67
|
+
class: "validate-identifier",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "tool_description",
|
|
71
|
+
label: "Tool description",
|
|
72
|
+
type: "String",
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
name: "code_description",
|
|
77
|
+
label: "Code description",
|
|
78
|
+
type: "String",
|
|
79
|
+
fieldview: "textarea",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "add_sys_prompt",
|
|
83
|
+
label: "Additional system prompt",
|
|
84
|
+
type: "String",
|
|
85
|
+
fieldview: "textarea",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "allow_fetch",
|
|
89
|
+
label: "Allow HTTP fetch",
|
|
90
|
+
type: "Bool",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "allow_table",
|
|
94
|
+
label: "Allow access to tables",
|
|
95
|
+
type: "Bool",
|
|
96
|
+
},
|
|
97
|
+
...(Table.subClass
|
|
98
|
+
? [
|
|
99
|
+
{
|
|
100
|
+
name: "table_read_only",
|
|
101
|
+
label: "Read only?",
|
|
102
|
+
type: "Bool",
|
|
103
|
+
showIf: { allow_table: true },
|
|
104
|
+
},
|
|
105
|
+
]
|
|
106
|
+
: []),
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
provideTools = () => {
|
|
111
|
+
return {
|
|
112
|
+
type: "function",
|
|
113
|
+
process: async (row, { req }) => {
|
|
114
|
+
return "Code generation tool activated";
|
|
115
|
+
//return await this.runCode({ row, user: req.user });
|
|
116
|
+
},
|
|
117
|
+
/*renderToolCall({ phrase }, { req }) {
|
|
118
|
+
return div({ class: "border border-primary p-2 m-2" }, phrase);
|
|
119
|
+
},*/
|
|
120
|
+
postProcess: async ({ tool_call, req, generate, ...rest }) => {
|
|
121
|
+
//console.log("postprocess args", { tool_call, ...rest });
|
|
122
|
+
|
|
123
|
+
const str = await generate(
|
|
124
|
+
`You will now be asked to write JavaScript code.
|
|
125
|
+
${this.code_description ? "\nSome more information: " + this.code_description : ""}
|
|
126
|
+
${this.allow_fetch ? "\nYou can use the standard fetch JavaScript function to make HTTP(S) requests." : ""}
|
|
127
|
+
${this.allow_table ? getTablePrompt(this.read_only) : ""}
|
|
128
|
+
|
|
129
|
+
The code you write can use await at the top level, and should return
|
|
130
|
+
(at the top level) a string (which can contain HTML tags) with the response which will be shown to the user.
|
|
131
|
+
|
|
132
|
+
Now generate the JavaScript code required by the user.`,
|
|
133
|
+
);
|
|
134
|
+
//console.log("gen answer", str);
|
|
135
|
+
|
|
136
|
+
const js_code = str.includes("```javascript")
|
|
137
|
+
? str.split("```javascript")[1].split("```")[0]
|
|
138
|
+
: str;
|
|
139
|
+
|
|
140
|
+
const res = await this.runCode(js_code, { user: req.user });
|
|
141
|
+
//console.log("code response", res);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
//stop: true,
|
|
145
|
+
add_response: res,
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
function: {
|
|
149
|
+
name: this.tool_name,
|
|
150
|
+
description: this.tool_description,
|
|
151
|
+
parameters: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const getTablePrompt = (read_only) => {
|
|
161
|
+
const state = getState();
|
|
162
|
+
const tables = state.tables;
|
|
163
|
+
const tableLines = [];
|
|
164
|
+
tables.forEach((table) => {
|
|
165
|
+
const fieldLines = table.fields.map(
|
|
166
|
+
(f) =>
|
|
167
|
+
` * ${f.name} with type: ${f.pretty_type.replace(
|
|
168
|
+
"Key to",
|
|
169
|
+
"ForeignKey referencing",
|
|
170
|
+
)}.${f.description ? ` ${f.description}` : ""}`,
|
|
171
|
+
);
|
|
172
|
+
tableLines.push(
|
|
173
|
+
`${table.name}${
|
|
174
|
+
table.description ? `: ${table.description}.` : "."
|
|
175
|
+
} Contains the following fields:\n${fieldLines.join("\n")}`,
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
return `Use the Table variable to access the Table class which gives you access to database tables
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
|
|
182
|
+
const someTable = Table.findOne({name: "Orders"})
|
|
183
|
+
await someTable.insertRow({name: "Alex", age: 43})
|
|
184
|
+
await someTable.deleteRows({id: order})
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
You can use the Table class to access database tables. Use this to create or delete tables and
|
|
188
|
+
their properties, or to query or change table rows.
|
|
189
|
+
|
|
190
|
+
To query, update, insert or delete rows in an existing table, first you should find the
|
|
191
|
+
table object with findOne.
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
|
|
195
|
+
Table.findOne({name: "Customers"}) // find the table with name "Customers"
|
|
196
|
+
Table.findOne("Customers") // find the table with name "Customers" (shortcut)
|
|
197
|
+
Table.findOne({ id: 5 }) // find the table with id=5
|
|
198
|
+
Table.findOne(5) // find the table with id=5 (shortcut)
|
|
199
|
+
|
|
200
|
+
Table.findOne is synchronous (no need to await), But the functions that query and manipulate
|
|
201
|
+
(such as insertRow, getRows, updateRow, deleteRows) rows are mostly asyncronous, so you can
|
|
202
|
+
put the await in front of the whole expression.
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
To count the number of rows in the customer table:
|
|
206
|
+
|
|
207
|
+
const nrows = await Table.findOne("Customers").countRows({})
|
|
208
|
+
|
|
209
|
+
Querying table rows
|
|
210
|
+
|
|
211
|
+
There are several methods you can use to retrieve rows in the database:
|
|
212
|
+
|
|
213
|
+
countRows: Count the number of rows in db table. The argument is a where-expression with conditions the
|
|
214
|
+
counted rows should match. countRows returns the number of matching rows wrapped in a promise.
|
|
215
|
+
|
|
216
|
+
countRows(where?): Promise<number>
|
|
217
|
+
Count amount of rows in db table
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
Optional where: Where
|
|
221
|
+
Returns Promise<number>
|
|
222
|
+
|
|
223
|
+
Example of using countRows:
|
|
224
|
+
const bookTable = Table.findOne({name: "books"})
|
|
225
|
+
|
|
226
|
+
// Count the total number of rows in the books table
|
|
227
|
+
const totalNumberOfBooks = await bookTable.countRows({})
|
|
228
|
+
|
|
229
|
+
// Count the number of books where the cover_color field has the value is "Red"
|
|
230
|
+
const numberOfRedBooks = await bookTable.countRows({cover_color: "Red"})
|
|
231
|
+
|
|
232
|
+
// Count number of books with more than 500 pages
|
|
233
|
+
const numberOfLongBooks = await bookTable.countRows({pages: {gt: 500}})
|
|
234
|
+
|
|
235
|
+
getRows: Get all matching rows from the table in the database.
|
|
236
|
+
|
|
237
|
+
The arguments are the same as for getRow. The first argument is where-expression
|
|
238
|
+
with the conditions to match, and the second argument is an optional object and
|
|
239
|
+
allows you to set ordering and limit options. Keywords that can be used in the
|
|
240
|
+
second argument are orderBy, orderDesc, limit and offset.
|
|
241
|
+
|
|
242
|
+
getRows will return an array of rows matching the where-expression in the first
|
|
243
|
+
argument, wrapped in a Promise (use await to read the array).
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
getRows(where?, selopts?): Promise<Row[]>
|
|
247
|
+
Get rows from Table in db
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
where: Where = {}
|
|
251
|
+
selopts: SelectOptions & ForUserRequest = {}
|
|
252
|
+
Returns Promise<Row[]>
|
|
253
|
+
|
|
254
|
+
Example of using getRows:
|
|
255
|
+
|
|
256
|
+
const bookTable = Table.findOne({name: "books"})
|
|
257
|
+
|
|
258
|
+
// get the rows in the book table with author = "Henrik Pontoppidan"
|
|
259
|
+
const myBooks = await bookTable.getRows({author: "Henrik Pontoppidan"})
|
|
260
|
+
|
|
261
|
+
// get the 3 most recent books written by "Henrik Pontoppidan" with more that 500 pages
|
|
262
|
+
const myBooks = await bookTable.getRows({author: "Henrik Pontoppidan", pages: {gt: 500}}, {orderBy: "published", orderDesc: true})
|
|
263
|
+
|
|
264
|
+
getRow: Get one row from the table in the database. The matching row will be returned in a promise -
|
|
265
|
+
use await to read the value. If no matching rule can be found, null will be returned. If more than one
|
|
266
|
+
row matches, the first found row will be returned.
|
|
267
|
+
|
|
268
|
+
The first argument to get row is a where-expression With the conditions the returned row should match.
|
|
269
|
+
|
|
270
|
+
The second document is optional and is an object that can modify the search. This is mainly useful in
|
|
271
|
+
case there is more than one matching row for the where-expression in the first argument and you want to
|
|
272
|
+
give an explicit order. For example, use {orderBy: "name"} as the second argument to pick the first
|
|
273
|
+
row by the name field, ordered ascending. {orderBy: "name", orderDesc: true} to order by name, descending
|
|
274
|
+
|
|
275
|
+
This is however rare and usually getRow is run with a single argument of a Where expression that uniquely
|
|
276
|
+
determines the row to return, if it exisits.
|
|
277
|
+
|
|
278
|
+
getRow(where?, selopts?): Promise<null | Row>
|
|
279
|
+
Get one row from table in db
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
where: Where = {}
|
|
283
|
+
selopts: SelectOptions & ForUserRequest = {}
|
|
284
|
+
Returns Promise<null | Row>
|
|
285
|
+
|
|
286
|
+
Example of using getRow:
|
|
287
|
+
const bookTable = Table.findOne({name: "books"})
|
|
288
|
+
|
|
289
|
+
// get the row in the book table with id = 5
|
|
290
|
+
const myBook = await bookTable.getRow({id: 5})
|
|
291
|
+
|
|
292
|
+
// get the row for the last book published by Leo Tolstoy
|
|
293
|
+
const myBook = await bookTable.getRow({author: "Leo Tolstoy"}, {orderBy: "published", orderDesc: true})
|
|
294
|
+
|
|
295
|
+
getJoinedRows: To retrieve rows together with joinfields and aggregations
|
|
296
|
+
|
|
297
|
+
getJoinedRows(opts?): Promise<Row[]>
|
|
298
|
+
Get rows along with joined and aggregated fields. The argument to getJoinedRows is an object with several different possible fields, all of which are optional
|
|
299
|
+
|
|
300
|
+
where: A Where expression indicating the criterion to match
|
|
301
|
+
joinFields: An object with the joinfields to retrieve
|
|
302
|
+
aggregations: An object with the aggregations to retrieve
|
|
303
|
+
orderBy: A string with the name of the field to order by
|
|
304
|
+
orderDesc: If true, descending order
|
|
305
|
+
limit: A number with the maximum number of rows to retrieve
|
|
306
|
+
offset: The number of rows to skip in the result before returning rows
|
|
307
|
+
Parameters
|
|
308
|
+
Optional opts: any = {}
|
|
309
|
+
Returns Promise<Row[]>
|
|
310
|
+
|
|
311
|
+
Example of using getJoinedRows:
|
|
312
|
+
|
|
313
|
+
const patients = Table.findOne({ name: "patients" });
|
|
314
|
+
const patients_rows = await patients.getJoinedRows({
|
|
315
|
+
where: { age: { gt: 65 } },
|
|
316
|
+
orderBy: "id",
|
|
317
|
+
aggregations: {
|
|
318
|
+
avg_temp: {
|
|
319
|
+
table: "readings",
|
|
320
|
+
ref: "patient_id",
|
|
321
|
+
field: "temperature",
|
|
322
|
+
aggregate: "avg",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
joinFields: {
|
|
326
|
+
pages: { ref: "favbook", target: "pages" },
|
|
327
|
+
author: { ref: "favbook", target: "author" },
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
These functions all take "Where expressions" which are JavaScript objects describing
|
|
332
|
+
the criterion to match to. Some examples:
|
|
333
|
+
|
|
334
|
+
{ name: "Jim" }: Match all rows with name="Jim"
|
|
335
|
+
{ name: { ilike: "im"} }: Match all rows where name contains "im" (case insensitive)
|
|
336
|
+
{ name: /im/ }: Match all rows with name matching regular expression "im"
|
|
337
|
+
{ age: { lt: 18 } }: Match all rows with age<18
|
|
338
|
+
{ age: { lt: 18, equal: true } }: Match all rows with age<=18
|
|
339
|
+
{ age: { gt: 18, lt: 65} }: Match all rows with 18<age<65
|
|
340
|
+
{ name: { or: ["Harry", "Sally"] } }: Match all rows with name="Harry" or "Sally"
|
|
341
|
+
{ or: [{ name: "Joe"}, { age: 37 }] }: Match all rows with name="Joe" or age=37
|
|
342
|
+
{ not: { id: 5 } }: All rows except id=5
|
|
343
|
+
{ id: { in: [1, 2, 3] } }: Rows with id 1, 2, or 3
|
|
344
|
+
|
|
345
|
+
There are two nearly identical functions for updating rows depending on how you want failures treated
|
|
346
|
+
|
|
347
|
+
${
|
|
348
|
+
!read_only
|
|
349
|
+
? `updateRow Update a row in the database table, throws an exception if update is invalid
|
|
350
|
+
|
|
351
|
+
updateRow(v_in, id, user?): Promise<string | void>
|
|
352
|
+
Update row
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
v_in: any. columns with values to update
|
|
356
|
+
|
|
357
|
+
id: number. id value, table primary key
|
|
358
|
+
|
|
359
|
+
Optional user: Row
|
|
360
|
+
|
|
361
|
+
Example of using updateRow:
|
|
362
|
+
|
|
363
|
+
const bookTable = Table.findOne({name: "books"})
|
|
364
|
+
|
|
365
|
+
// get the row in the book table for Moby Dick
|
|
366
|
+
const moby_dick = await bookTable.getRow({title: "Moby Dick"})
|
|
367
|
+
|
|
368
|
+
// Update the read field to true and the rating field to 5 in the retrieved row.
|
|
369
|
+
await bookTable.updateRow({read: true, rating: 5}, moby_dick.id)
|
|
370
|
+
|
|
371
|
+
// if you want to update more than one row, you must first retrieve all the rows and
|
|
372
|
+
// then update them individually
|
|
373
|
+
|
|
374
|
+
const allBooks = await bookTable.getRows()
|
|
375
|
+
for(const book of allBooks) {
|
|
376
|
+
await bookTable.updateRow({price: book.price*0.8}, book.id)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
tryUpdateRow Update a row, return an error message if update is invalid
|
|
380
|
+
|
|
381
|
+
There are two nearly identical functions for inserting a new row depending on how you want failures treated
|
|
382
|
+
|
|
383
|
+
insertRow insert a row, throws an exception if it is invalid
|
|
384
|
+
insertRow(v_in, user): Promise<any>
|
|
385
|
+
Insert row into the table. By passing in the user as the second argument, tt will check write rights. If a user object is not supplied, the insert goes ahead without checking write permissions.
|
|
386
|
+
|
|
387
|
+
Returns the primary key value of the inserted row.
|
|
388
|
+
|
|
389
|
+
This will throw an exception if the row does not conform to the table constraints. If you would like to insert a row with a function that can return an error message, use tryInsertRow instead.
|
|
390
|
+
|
|
391
|
+
Parameters
|
|
392
|
+
v_in: Row
|
|
393
|
+
Optional user: Row
|
|
394
|
+
Returns Promise<any>
|
|
395
|
+
|
|
396
|
+
Example of using insertRow:
|
|
397
|
+
await Table.findOne("People").insertRow({ name: "Jim", age: 35 })
|
|
398
|
+
|
|
399
|
+
tryInsertRow insert a row, return an error message if it is invalid
|
|
400
|
+
|
|
401
|
+
Use deleteRows to delete any number (zero, one or many) of rows matching a criterion. It uses the same where expression as the functions for querying rows
|
|
402
|
+
deleteRows(where, user?, noTrigger?): Promise<void>
|
|
403
|
+
Delete rows from table
|
|
404
|
+
|
|
405
|
+
Parameters
|
|
406
|
+
where: Where
|
|
407
|
+
condition
|
|
408
|
+
|
|
409
|
+
Optional user: Row
|
|
410
|
+
optional user, if null then no authorization will be checked
|
|
411
|
+
|
|
412
|
+
Optional noTrigger: boolean
|
|
413
|
+
Returns Promise<void>`
|
|
414
|
+
: ""
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
The following tables are present in the database:
|
|
418
|
+
|
|
419
|
+
${tableLines.join("\n\n")}`;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
module.exports = GenerateAndRunJsCodeSkill;
|