@saltcorn/agents 0.7.2 → 0.7.3
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/common.js +36 -5
- package/package.json +1 -1
- package/skills/ModelPicker.js +47 -0
- package/skills/WebSearch.js +96 -0
- package/tests/action.test.js +28 -0
- package/tests/agentcfg.js +23 -1
package/common.js
CHANGED
|
@@ -34,9 +34,11 @@ const get_skills = () => {
|
|
|
34
34
|
require("./skills/GenerateImage"),
|
|
35
35
|
require("./skills/ModelContextProtocol"),
|
|
36
36
|
require("./skills/PromptPicker"),
|
|
37
|
+
require("./skills/ModelPicker"),
|
|
37
38
|
require("./skills/RunJsCode"),
|
|
38
39
|
require("./skills/GenerateAndRunJsCode"),
|
|
39
40
|
require("./skills/Fetch"),
|
|
41
|
+
require("./skills/WebSearch"),
|
|
40
42
|
require("./skills/Subagent"),
|
|
41
43
|
//require("./skills/AdaptiveFeedback"),
|
|
42
44
|
...exchange_skills,
|
|
@@ -127,12 +129,21 @@ const getCompletionArguments = async (
|
|
|
127
129
|
];
|
|
128
130
|
|
|
129
131
|
const skills = get_skill_instances(config);
|
|
132
|
+
const overrides = {};
|
|
130
133
|
for (const skill of skills) {
|
|
131
134
|
const sysPr = await skill.systemPrompt?.({
|
|
132
135
|
...(formbody || {}),
|
|
133
136
|
user,
|
|
134
137
|
triggering_row,
|
|
135
138
|
});
|
|
139
|
+
const overide =
|
|
140
|
+
(await skill.settingsOverride?.({
|
|
141
|
+
...(formbody || {}),
|
|
142
|
+
user,
|
|
143
|
+
triggering_row,
|
|
144
|
+
})) || {};
|
|
145
|
+
Object.assign(overrides, overide);
|
|
146
|
+
|
|
136
147
|
if (sysPr) sysPrompts.push(sysPr);
|
|
137
148
|
const skillTools = skill.provideTools?.();
|
|
138
149
|
if (skillTools && Array.isArray(skillTools)) tools.push(...skillTools);
|
|
@@ -141,7 +152,9 @@ const getCompletionArguments = async (
|
|
|
141
152
|
if (tools.length === 0) tools = undefined;
|
|
142
153
|
const complArgs = { tools, systemPrompt: sysPrompts.join("\n\n") };
|
|
143
154
|
if (config.model) complArgs.model = config.model;
|
|
144
|
-
|
|
155
|
+
|
|
156
|
+
if (overrides.alt_config || config.alt_config)
|
|
157
|
+
complArgs.alt_config = overrides.alt_config || config.alt_config;
|
|
145
158
|
return complArgs;
|
|
146
159
|
};
|
|
147
160
|
|
|
@@ -237,6 +250,7 @@ const process_interaction = async (
|
|
|
237
250
|
);
|
|
238
251
|
complArgs.appendToChat = true;
|
|
239
252
|
complArgs.chat = run.context.interactions;
|
|
253
|
+
const use_alt_config = complArgs.alt_config;
|
|
240
254
|
//complArgs.debugResult = true;
|
|
241
255
|
//console.log("complArgs", JSON.stringify(complArgs, null, 2));
|
|
242
256
|
const debugMode = is_debug_mode(config, req.user);
|
|
@@ -344,6 +358,7 @@ const process_interaction = async (
|
|
|
344
358
|
//const actions = [];
|
|
345
359
|
let hasResult = false;
|
|
346
360
|
if ((answer.mcp_calls || []).length && !answer.content) hasResult = true;
|
|
361
|
+
const toolResults = {};
|
|
347
362
|
if (answer.hasToolCalls)
|
|
348
363
|
for (const tool_call of answer.getToolCalls()) {
|
|
349
364
|
getState().log(6, "call function " + tool_call.tool_name);
|
|
@@ -395,6 +410,7 @@ const process_interaction = async (
|
|
|
395
410
|
const result = await tool.tool.process(tool_call.input, {
|
|
396
411
|
req,
|
|
397
412
|
});
|
|
413
|
+
toolResults[tool_call.tool_call_id] = result;
|
|
398
414
|
if (result.stop) stop = true;
|
|
399
415
|
if (
|
|
400
416
|
(typeof result === "object" && Object.keys(result || {}).length) ||
|
|
@@ -440,7 +456,21 @@ const process_interaction = async (
|
|
|
440
456
|
await addToContext(run, {
|
|
441
457
|
interactions: run.context.interactions,
|
|
442
458
|
});
|
|
459
|
+
|
|
460
|
+
if (myHasResult && !stop) hasResult = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// postprocess - now all tool calls have responses
|
|
465
|
+
if (answer.hasToolCalls)
|
|
466
|
+
for (const tool_call of answer.getToolCalls()) {
|
|
467
|
+
const tool = find_tool(tool_call.tool_name, config);
|
|
468
|
+
if (tool) {
|
|
469
|
+
let stop = false,
|
|
470
|
+
myHasResult = false;
|
|
443
471
|
if (tool.tool.postProcess && !stop) {
|
|
472
|
+
let result = toolResults[tool_call.tool_call_id];
|
|
473
|
+
|
|
444
474
|
const chat = run.context.interactions;
|
|
445
475
|
let generateUsed = false;
|
|
446
476
|
const systemPrompt = await getSystemPrompt(
|
|
@@ -461,7 +491,7 @@ const process_interaction = async (
|
|
|
461
491
|
chat,
|
|
462
492
|
appendToChat: true,
|
|
463
493
|
systemPrompt,
|
|
464
|
-
alt_config:
|
|
494
|
+
alt_config: use_alt_config,
|
|
465
495
|
...opts,
|
|
466
496
|
});
|
|
467
497
|
},
|
|
@@ -490,9 +520,10 @@ const process_interaction = async (
|
|
|
490
520
|
],
|
|
491
521
|
});
|
|
492
522
|
if (postprocres.add_response) {
|
|
493
|
-
const renderedAddResponse =
|
|
494
|
-
|
|
495
|
-
|
|
523
|
+
const renderedAddResponse =
|
|
524
|
+
typeof postprocres.add_response === "string"
|
|
525
|
+
? md.render(postprocres.add_response)
|
|
526
|
+
: postprocres.add_response;
|
|
496
527
|
add_response(
|
|
497
528
|
wrapSegment(
|
|
498
529
|
wrapCard(
|
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
8
|
+
|
|
9
|
+
class ModelPicker {
|
|
10
|
+
static skill_name = "Model picker";
|
|
11
|
+
get skill_label() {
|
|
12
|
+
return `Model picker`;
|
|
13
|
+
}
|
|
14
|
+
constructor(cfg) {
|
|
15
|
+
Object.assign(this, cfg);
|
|
16
|
+
}
|
|
17
|
+
static async configFields() {
|
|
18
|
+
return [
|
|
19
|
+
{ name: "placeholder", label: "Placeholder", type: "String" },
|
|
20
|
+
{
|
|
21
|
+
name: "default_label",
|
|
22
|
+
label: "Default configuration label",
|
|
23
|
+
type: "String",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
async formWidget({ user, klass }) {
|
|
28
|
+
const llm_cfg_fun = getState().functions.llm_get_configuration;
|
|
29
|
+
const alt_config_options = llm_cfg_fun
|
|
30
|
+
? llm_cfg_fun.run().alt_config_names || []
|
|
31
|
+
: [];
|
|
32
|
+
return select(
|
|
33
|
+
{
|
|
34
|
+
class: ["form-select form-select-sm w-unset", klass],
|
|
35
|
+
name: "modelpicker",
|
|
36
|
+
},
|
|
37
|
+
this.placeholder && option({ disabled: true }, this.placeholder),
|
|
38
|
+
option({ value: "" }, this.default_label),
|
|
39
|
+
alt_config_options.map((o) => option(o)),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
settingsOverride(body) {
|
|
43
|
+
if (body.modelpicker) return { alt_config: body.modelpicker };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = ModelPicker;
|
|
@@ -0,0 +1,96 @@
|
|
|
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 WebSearchSkill {
|
|
23
|
+
static skill_name = "Web search";
|
|
24
|
+
|
|
25
|
+
get skill_label() {
|
|
26
|
+
return "Web search";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor(cfg) {
|
|
30
|
+
Object.assign(this, cfg);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static async configFields() {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
name: "search_provider",
|
|
37
|
+
label: "Search provider",
|
|
38
|
+
type: "String",
|
|
39
|
+
required: true,
|
|
40
|
+
attributes: { options: ["By URL template"] },
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "url_template",
|
|
44
|
+
label: "URL template",
|
|
45
|
+
sublabel: "Use <code>{{q}}</code> for the URL-encoded search phrase",
|
|
46
|
+
type: "String",
|
|
47
|
+
required: true,
|
|
48
|
+
showIf: { search_provider: "By URL template" },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "header",
|
|
52
|
+
label: "Header",
|
|
53
|
+
sublabel: "For example <code>X-Subscription-Token: YOUR_API_KEY</code>",
|
|
54
|
+
type: "String",
|
|
55
|
+
showIf: { search_provider: "By URL template" },
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
systemPrompt() {
|
|
60
|
+
return "If you need to search the web with a search phrase, use the web_search to get search engine results";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
provideTools = () => {
|
|
64
|
+
return {
|
|
65
|
+
type: "function",
|
|
66
|
+
process: async (row) => {
|
|
67
|
+
const fOpts = { method: "GET" };
|
|
68
|
+
if (this.header) {
|
|
69
|
+
const [key, val] = this.header.split(":");
|
|
70
|
+
const myHeaders = new Headers();
|
|
71
|
+
myHeaders.append(key, val.trim());
|
|
72
|
+
fOpts.headers = myHeaders;
|
|
73
|
+
}
|
|
74
|
+
const url = interpolate(this.url_template, { q: row.search_phrase });
|
|
75
|
+
const resp = await fetch(url);
|
|
76
|
+
return await resp.text();
|
|
77
|
+
},
|
|
78
|
+
function: {
|
|
79
|
+
name: "web_search",
|
|
80
|
+
description: "Search the web with a search engine by a search phrase",
|
|
81
|
+
parameters: {
|
|
82
|
+
type: "object",
|
|
83
|
+
required: ["search_phrase"],
|
|
84
|
+
properties: {
|
|
85
|
+
search_phrase: {
|
|
86
|
+
description: "The phrase to search for",
|
|
87
|
+
type: "string",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = WebSearchSkill;
|
package/tests/action.test.js
CHANGED
|
@@ -29,6 +29,13 @@ beforeAll(async () => {
|
|
|
29
29
|
when_trigger: "Never",
|
|
30
30
|
configuration: require("./agentcfg").maths_agent_cfg,
|
|
31
31
|
});
|
|
32
|
+
await Trigger.create({
|
|
33
|
+
name: "OracleAgent",
|
|
34
|
+
description: "The all-knowing oracle answers any question",
|
|
35
|
+
action: "Agent",
|
|
36
|
+
when_trigger: "Never",
|
|
37
|
+
configuration: require("./agentcfg").oracle_agent_cfg,
|
|
38
|
+
});
|
|
32
39
|
|
|
33
40
|
await getState().refresh_triggers(false);
|
|
34
41
|
//await getState().setConfig("log_level", 6);
|
|
@@ -109,6 +116,27 @@ for (const nameconfig of require("./configs")) {
|
|
|
109
116
|
});
|
|
110
117
|
expect(result.json.response).toContain("987");
|
|
111
118
|
});
|
|
119
|
+
it("run multiple subagents concurrenty", async () => {
|
|
120
|
+
const configuration = { ...require("./agentcfg").agent1 };
|
|
121
|
+
configuration.skills = [
|
|
122
|
+
...configuration.skills,
|
|
123
|
+
{
|
|
124
|
+
agent_name: "OracleAgent",
|
|
125
|
+
skill_type: "Subagent",
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
const result = await action.run({
|
|
129
|
+
row: {
|
|
130
|
+
theprompt:
|
|
131
|
+
"What is the 16th Fibonacci number (when F1=1 and F2=1)? Consult both the math agent and the oracle and see if they agree",
|
|
132
|
+
},
|
|
133
|
+
configuration,
|
|
134
|
+
user,
|
|
135
|
+
req: { user },
|
|
136
|
+
});
|
|
137
|
+
expect(result.json.response).toContain("987");
|
|
138
|
+
});
|
|
112
139
|
});
|
|
140
|
+
|
|
113
141
|
//break;
|
|
114
142
|
}
|
package/tests/agentcfg.js
CHANGED
|
@@ -65,4 +65,26 @@ const maths_agent_cfg = {
|
|
|
65
65
|
"If the user asks an arithmetic question, generate javascript code to solve it with the generate_arithmetic_code tool",
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
const oracle_agent_cfg = {
|
|
69
|
+
model: "",
|
|
70
|
+
prompt: "{{theprompt}}",
|
|
71
|
+
skills: [
|
|
72
|
+
{
|
|
73
|
+
tool_name: "ask_the_oracle",
|
|
74
|
+
tool_description: "Ask the all-knowing oracle a question",
|
|
75
|
+
skill_type: "Run JavaScript code",
|
|
76
|
+
js_code: "return '987';",
|
|
77
|
+
toolargs: [
|
|
78
|
+
{
|
|
79
|
+
name: "question",
|
|
80
|
+
description: "The question you would like to ask",
|
|
81
|
+
argtype: "string",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
sys_prompt:
|
|
87
|
+
"You can ask a question of the all-knowing oracle by calling the ask_the_oracle tool ",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
module.exports = { agent1, maths_agent_cfg, oracle_agent_cfg };
|