@saltcorn/agents 0.6.7 → 0.6.8
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 +8 -8
- package/common.js +2 -0
- package/package.json +1 -1
- package/skills/Fetch.js +62 -0
- package/skills/GenerateAndRunJsCode.js +422 -0
package/agent-view.js
CHANGED
|
@@ -652,7 +652,7 @@ const run = async (
|
|
|
652
652
|
).join(" ");
|
|
653
653
|
}
|
|
654
654
|
$("span.filename-label").text("").removeClass("me-2");
|
|
655
|
-
_agentDT.items.clear();
|
|
655
|
+
window._agentDT.items.clear();
|
|
656
656
|
$("input#attach_agent_image").val(null);
|
|
657
657
|
if(!not_final || (!${JSON.stringify(dyn_updates)})) $("#sendbuttonicon").attr("class","far fa-paper-plane");
|
|
658
658
|
const $runidin= $("input[name=run_id")
|
|
@@ -672,20 +672,20 @@ const run = async (
|
|
|
672
672
|
window.final_agent_response = () => {
|
|
673
673
|
$("#sendbuttonicon").attr("class","far fa-paper-plane");
|
|
674
674
|
}
|
|
675
|
-
|
|
675
|
+
window._agentDT = new DataTransfer();
|
|
676
676
|
function setAgentFiles(files) {
|
|
677
|
-
for (const f of files) _agentDT.items.add(f);
|
|
678
|
-
document.getElementById('attach_agent_image').files = _agentDT.files;
|
|
677
|
+
for (const f of files) window._agentDT.items.add(f);
|
|
678
|
+
document.getElementById('attach_agent_image').files = window._agentDT.files;
|
|
679
679
|
updateFileLabel();
|
|
680
680
|
}
|
|
681
681
|
function updateFileLabel() {
|
|
682
|
-
const n = _agentDT.files.length;
|
|
682
|
+
const n = window._agentDT.files.length;
|
|
683
683
|
const $label = $(".attach_agent_image_wrap span.filename-label");
|
|
684
684
|
if (n === 0) {
|
|
685
685
|
$label.html("").removeClass("me-2");
|
|
686
686
|
} else {
|
|
687
687
|
$label.addClass("me-2");
|
|
688
|
-
const text = n === 1 ? _agentDT.files[0].name : n + " files";
|
|
688
|
+
const text = n === 1 ? window._agentDT.files[0].name : n + " files";
|
|
689
689
|
$label.html(${
|
|
690
690
|
isWeb(req)
|
|
691
691
|
? `text + ' <span class="badge text-bg-secondary" style="cursor:pointer;font-size:.65em;vertical-align:middle" onclick="clearAgentFiles()" title="Remove files">×</span>'`
|
|
@@ -694,13 +694,13 @@ const run = async (
|
|
|
694
694
|
}
|
|
695
695
|
}
|
|
696
696
|
function clearAgentFiles() {
|
|
697
|
-
_agentDT.items.clear();
|
|
697
|
+
window._agentDT.items.clear();
|
|
698
698
|
$("input#attach_agent_image").val(null);
|
|
699
699
|
updateFileLabel();
|
|
700
700
|
}
|
|
701
701
|
window.clearAgentFiles = clearAgentFiles;
|
|
702
702
|
function agent_file_attach(e) {
|
|
703
|
-
_agentDT.items.clear();
|
|
703
|
+
window._agentDT.items.clear();
|
|
704
704
|
setAgentFiles(e.target.files);
|
|
705
705
|
}
|
|
706
706
|
function restore_old_button_elem(btn) {
|
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
|
];
|
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;
|