@saltcorn/agents 0.7.4 → 0.7.6
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/action.js +5 -3
- package/agent-view.js +42 -7
- package/common.js +13 -5
- package/package.json +1 -1
- package/skills/GenerateAndRunJsCode.js +51 -18
- package/skills/ModelPicker.js +3 -2
- package/skills/Subagent.js +32 -3
- package/tests/action.test.js +2 -2
package/action.js
CHANGED
|
@@ -90,6 +90,8 @@ module.exports = {
|
|
|
90
90
|
run_id,
|
|
91
91
|
req,
|
|
92
92
|
is_sub_agent,
|
|
93
|
+
agent_view_config,
|
|
94
|
+
dyn_updates,
|
|
93
95
|
...rest
|
|
94
96
|
}) => {
|
|
95
97
|
const userinput = interpolate(configuration.prompt, row, user);
|
|
@@ -115,9 +117,9 @@ module.exports = {
|
|
|
115
117
|
undefined,
|
|
116
118
|
[],
|
|
117
119
|
row,
|
|
118
|
-
{ stream: false },
|
|
119
|
-
|
|
120
|
-
is_sub_agent
|
|
120
|
+
agent_view_config || { stream: false },
|
|
121
|
+
dyn_updates,
|
|
122
|
+
is_sub_agent,
|
|
121
123
|
);
|
|
122
124
|
},
|
|
123
125
|
};
|
package/agent-view.js
CHANGED
|
@@ -192,13 +192,15 @@ const realTimeCollabScript = (viewname, rndid, layout) => {
|
|
|
192
192
|
['${view.getRealTimeEventName(
|
|
193
193
|
"STREAM_CHUNK",
|
|
194
194
|
)}' + \`?page_load_tag=\${_sc_pageloadtag}\`]: async (data) => {
|
|
195
|
+
$(".agent-waiting-indicator").remove();
|
|
195
196
|
window['stream scratch ${viewname} ${rndid}'].push(data.content)
|
|
196
197
|
const rendered = md.render(window['stream scratch ${viewname} ${rndid}'].join(""));
|
|
197
|
-
$('
|
|
198
|
+
$('div.next_response_scratch').html(
|
|
198
199
|
(${JSON.stringify(layout || "")} || "").startsWith("Modern chat")
|
|
199
200
|
? '<div class="chat-message chat-assistant"><div class="chat-avatar"><i class="fas fa-robot"></i></div><div class="chat-bubble">' + rendered + '</div></div>'
|
|
200
201
|
: rendered
|
|
201
202
|
);
|
|
203
|
+
scrollAgentToBottom();
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
};
|
|
@@ -434,7 +436,7 @@ const run = async (
|
|
|
434
436
|
const rndid = Math.floor(Math.random() * 16777215).toString(16);
|
|
435
437
|
const input_form = form(
|
|
436
438
|
{
|
|
437
|
-
onsubmit: `event.preventDefault();spin_send_button();view_post('${viewname}', 'interact',
|
|
439
|
+
onsubmit: `event.preventDefault();const _fd=new FormData(this);spin_send_button();view_post('${viewname}', 'interact', _fd, ${dyn_updates ? "null" : "processCopilotResponse"});return false;`,
|
|
438
440
|
class: ["form-namespace copilot mt-2 agent-view"],
|
|
439
441
|
method: "post",
|
|
440
442
|
},
|
|
@@ -492,8 +494,7 @@ const run = async (
|
|
|
492
494
|
explainer && small({ class: "explainer" }, i(explainer)),
|
|
493
495
|
),
|
|
494
496
|
stream &&
|
|
495
|
-
realTimeCollabScript(viewname, rndid, layout)
|
|
496
|
-
div({ class: "next_response_scratch" }),
|
|
497
|
+
realTimeCollabScript(viewname, rndid, layout),
|
|
497
498
|
);
|
|
498
499
|
|
|
499
500
|
const isModernSidebar = layout && layout.startsWith("Modern chat");
|
|
@@ -612,6 +613,7 @@ const run = async (
|
|
|
612
613
|
req.__("Sessions"),
|
|
613
614
|
),
|
|
614
615
|
div({ id: "copilotinteractions" }, runInteractions),
|
|
616
|
+
stream ? div({ class: "next_response_scratch" }) : "",
|
|
615
617
|
input_form,
|
|
616
618
|
style(
|
|
617
619
|
`div.interaction-segment:not(:first-child) {border-top: 1px solid #e7e7e7; }
|
|
@@ -685,6 +687,14 @@ const run = async (
|
|
|
685
687
|
margin-bottom: 0px;
|
|
686
688
|
display: block;
|
|
687
689
|
text-overflow: ellipsis;}
|
|
690
|
+
/* Typing / Waiting Indicator */
|
|
691
|
+
.agent-waiting-indicator { display:flex; align-items:center; padding:0.75rem 1rem; }
|
|
692
|
+
.typing-dots { display:flex; gap:4px; align-items:center; }
|
|
693
|
+
.typing-dots span { width:8px; height:8px; border-radius:50%; background:#6c757d; animation:typingBounce 1.4s infinite ease-in-out both; }
|
|
694
|
+
.typing-dots span:nth-child(1) { animation-delay:-0.32s; }
|
|
695
|
+
.typing-dots span:nth-child(2) { animation-delay:-0.16s; }
|
|
696
|
+
.typing-dots span:nth-child(3) { animation-delay:0s; }
|
|
697
|
+
@keyframes typingBounce { 0%,80%,100%{transform:scale(.6);opacity:.4} 40%{transform:scale(1);opacity:1} }
|
|
688
698
|
/* Modern Chat Layout */
|
|
689
699
|
.modern-chat-layout {
|
|
690
700
|
display: flex;
|
|
@@ -881,6 +891,16 @@ const run = async (
|
|
|
881
891
|
script(domReady(`$( "#inputuserinput" ).autogrow({paddingBottom: 20});`)),
|
|
882
892
|
script(
|
|
883
893
|
`
|
|
894
|
+
function scrollAgentToBottom() {
|
|
895
|
+
const container = document.getElementById('copilotinteractions');
|
|
896
|
+
if (container) {
|
|
897
|
+
if (container.scrollHeight > container.clientHeight) {
|
|
898
|
+
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
|
|
899
|
+
}
|
|
900
|
+
const inputForm = document.querySelector('form.agent-view');
|
|
901
|
+
if (inputForm) inputForm.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
902
|
+
}
|
|
903
|
+
}
|
|
884
904
|
function close_session_list() {
|
|
885
905
|
$("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")
|
|
886
906
|
$("div.open-prev-runs").show()
|
|
@@ -916,14 +936,21 @@ const run = async (
|
|
|
916
936
|
if(user_input && (!${JSON.stringify(dyn_updates)}))
|
|
917
937
|
$("#copilotinteractions").append(wrapSegment('<p>'+user_input+'</p>'+fileBadge, "You", true))
|
|
918
938
|
$("textarea[name=userinput]").val("")
|
|
919
|
-
$('
|
|
939
|
+
$('div.next_response_scratch').html("")
|
|
920
940
|
window['stream scratch ${viewname} ${rndid}'] = []
|
|
921
|
-
if(res.response)
|
|
922
|
-
$("
|
|
941
|
+
if(res.response) {
|
|
942
|
+
$(".agent-waiting-indicator").remove();
|
|
943
|
+
$("#copilotinteractions").append(res.response);
|
|
944
|
+
scrollAgentToBottom();
|
|
945
|
+
}
|
|
923
946
|
}
|
|
924
947
|
window.processCopilotResponse = processCopilotResponse;
|
|
925
948
|
window.final_agent_response = () => {
|
|
926
949
|
$("#sendbuttonicon").attr("class","far fa-paper-plane");
|
|
950
|
+
$(".agent-waiting-indicator").remove();
|
|
951
|
+
$("textarea[name=userinput]").prop("disabled", false).attr("placeholder", ${JSON.stringify(placeholder || "How can I help you?")}).focus();
|
|
952
|
+
$(".copilot-entry .submit-button").css("pointer-events", "");
|
|
953
|
+
scrollAgentToBottom();
|
|
927
954
|
}
|
|
928
955
|
window._agentDT = new DataTransfer();
|
|
929
956
|
function setAgentFiles(files) {
|
|
@@ -1046,6 +1073,14 @@ const run = async (
|
|
|
1046
1073
|
}
|
|
1047
1074
|
function spin_send_button() {
|
|
1048
1075
|
$("#sendbuttonicon").attr("class","fas fa-spinner fa-spin");
|
|
1076
|
+
$("textarea[name=userinput]").prop("disabled", true).attr("placeholder", "Waiting for response...");
|
|
1077
|
+
$(".copilot-entry .submit-button").css("pointer-events", "none");
|
|
1078
|
+
const isModernLayout = ${JSON.stringify((layout || "").startsWith("Modern chat"))};
|
|
1079
|
+
const indicator = isModernLayout
|
|
1080
|
+
? '<div class="agent-waiting-indicator chat-message chat-assistant"><div class="chat-avatar"><i class="fas fa-robot"></i></div><div class="chat-bubble"><div class="typing-dots"><span></span><span></span><span></span></div></div></div>'
|
|
1081
|
+
: '<div class="agent-waiting-indicator"><div class="typing-dots"><span></span><span></span><span></span></div></div>';
|
|
1082
|
+
$('div.next_response_scratch').before(indicator);
|
|
1083
|
+
scrollAgentToBottom();
|
|
1049
1084
|
};`,
|
|
1050
1085
|
stream &&
|
|
1051
1086
|
domReady(
|
package/common.js
CHANGED
|
@@ -277,7 +277,9 @@ const process_interaction = async (
|
|
|
277
277
|
run.context.interactions[run.context.interactions.length - 1];
|
|
278
278
|
|
|
279
279
|
const answer = await sysState.functions.llm_generate.run(
|
|
280
|
-
lastInteract?.role === "user"
|
|
280
|
+
lastInteract?.role === "user" || lastInteract?.role === "tool"
|
|
281
|
+
? ""
|
|
282
|
+
: "Continue",
|
|
281
283
|
complArgs,
|
|
282
284
|
);
|
|
283
285
|
|
|
@@ -487,6 +489,8 @@ const process_interaction = async (
|
|
|
487
489
|
chat,
|
|
488
490
|
req,
|
|
489
491
|
run,
|
|
492
|
+
agent_view_config: agentsViewCfg,
|
|
493
|
+
dyn_updates,
|
|
490
494
|
async generate(prompt, opts = {}) {
|
|
491
495
|
generateUsed = true;
|
|
492
496
|
return await sysState.functions.llm_generate.run(prompt, {
|
|
@@ -530,9 +534,7 @@ const process_interaction = async (
|
|
|
530
534
|
for (const add_resp of postprocres.add_responses || []) {
|
|
531
535
|
raw_responses.push(add_resp);
|
|
532
536
|
const renderedAddResponse =
|
|
533
|
-
typeof add_resp === "string"
|
|
534
|
-
? md.render(add_resp)
|
|
535
|
-
: add_resp;
|
|
537
|
+
typeof add_resp === "string" ? md.render(add_resp) : add_resp;
|
|
536
538
|
add_response(
|
|
537
539
|
wrapSegment(
|
|
538
540
|
wrapCard(
|
|
@@ -635,16 +637,22 @@ const process_interaction = async (
|
|
|
635
637
|
return {
|
|
636
638
|
json: {
|
|
637
639
|
success: "ok",
|
|
638
|
-
...(is_sub_agent ? { raw_responses } : {}),
|
|
640
|
+
...(is_sub_agent && !stream ? { raw_responses } : {}),
|
|
639
641
|
response: [...prevResponses, ...responses].join(""),
|
|
640
642
|
run_id: run?.id,
|
|
641
643
|
},
|
|
642
644
|
};
|
|
643
645
|
};
|
|
644
646
|
|
|
647
|
+
const replaceUserContinue = (chat, newPrompt) => {
|
|
648
|
+
const lastChat = chat[chat.length - 1];
|
|
649
|
+
console.log("lastChat", lastChat);
|
|
650
|
+
};
|
|
651
|
+
|
|
645
652
|
module.exports = {
|
|
646
653
|
get_skills,
|
|
647
654
|
get_skill_class,
|
|
655
|
+
replaceUserContinue,
|
|
648
656
|
incompleteCfgMsg,
|
|
649
657
|
find_tool,
|
|
650
658
|
get_skill_instances,
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ const { button } = require("@saltcorn/markup/tags");
|
|
|
16
16
|
const { validID } = require("@saltcorn/markup/layout_utils");
|
|
17
17
|
|
|
18
18
|
const vm = require("vm");
|
|
19
|
+
const { replaceUserContinue } = require("../common");
|
|
19
20
|
|
|
20
21
|
//const { fieldProperties } = require("./helpers");
|
|
21
22
|
|
|
@@ -100,7 +101,8 @@ class GenerateAndRunJsCodeSkill {
|
|
|
100
101
|
},
|
|
101
102
|
{
|
|
102
103
|
name: "allow_functions",
|
|
103
|
-
label: "Allow
|
|
104
|
+
label: "Allow system functions",
|
|
105
|
+
sublabel: "Allow calls to functions from codepages and modules",
|
|
104
106
|
type: "Bool",
|
|
105
107
|
},
|
|
106
108
|
...(Table.subClass
|
|
@@ -131,12 +133,14 @@ class GenerateAndRunJsCodeSkill {
|
|
|
131
133
|
req,
|
|
132
134
|
generate,
|
|
133
135
|
emit_update,
|
|
136
|
+
chat,
|
|
134
137
|
...rest
|
|
135
138
|
}) => {
|
|
136
139
|
//console.log("postprocess args", { tool_call, ...rest });
|
|
137
140
|
emit_update("Generating code");
|
|
138
|
-
const
|
|
139
|
-
|
|
141
|
+
const gen_the_code = async (extra) => {
|
|
142
|
+
const str = await generate(
|
|
143
|
+
`You will now be asked to write JavaScript code.
|
|
140
144
|
${this.code_description ? "\nSome more information: " + this.code_description : ""}
|
|
141
145
|
${this.allow_fetch ? "\nYou can use the standard fetch JavaScript function to make HTTP(S) requests." : ""}
|
|
142
146
|
${this.allow_table ? getTablePrompt(this.read_only) : ""}
|
|
@@ -165,24 +169,53 @@ const y = await anotherAsyncFunction(x)
|
|
|
165
169
|
return { x, y }
|
|
166
170
|
\`\`\`
|
|
167
171
|
|
|
172
|
+
${extra || ""}
|
|
168
173
|
|
|
169
174
|
Now generate the JavaScript code required by the user.`,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const res = await this.runCode(js_code, { user: req.user });
|
|
180
|
-
//console.log("code response", res);
|
|
181
|
-
getState().log(6, "Code answer: " + JSON.stringify(res));
|
|
182
|
-
return {
|
|
183
|
-
stop: typeof res ==="string",
|
|
184
|
-
add_response: res,
|
|
175
|
+
);
|
|
176
|
+
getState().log(
|
|
177
|
+
6,
|
|
178
|
+
"Generated code:\n--BEGIN CODE--\n" + str + "\n--END CODE--\n",
|
|
179
|
+
);
|
|
180
|
+
const js_code = str.includes("```javascript")
|
|
181
|
+
? str.split("```javascript")[1].split("```")[0]
|
|
182
|
+
: str;
|
|
183
|
+
return js_code;
|
|
185
184
|
};
|
|
185
|
+
const js_code = await gen_the_code();
|
|
186
|
+
emit_update("Running code");
|
|
187
|
+
try {
|
|
188
|
+
const res = await this.runCode(js_code, { user: req.user });
|
|
189
|
+
//console.log("code response", res);
|
|
190
|
+
getState().log(6, "Code answer: " + JSON.stringify(res));
|
|
191
|
+
return {
|
|
192
|
+
stop: typeof res === "string",
|
|
193
|
+
add_response: res,
|
|
194
|
+
};
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error(err);
|
|
197
|
+
const retry_js_code =
|
|
198
|
+
await gen_the_code(`You were previously asked to complete this task. This was the code generated:
|
|
199
|
+
\`\`\`javascript
|
|
200
|
+
${js_code}
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
this code produced the following error:
|
|
204
|
+
|
|
205
|
+
\`\`\`
|
|
206
|
+
${err.message}
|
|
207
|
+
\`\`\`
|
|
208
|
+
|
|
209
|
+
Correct this error.
|
|
210
|
+
`);
|
|
211
|
+
const res = await this.runCode(retry_js_code, { user: req.user });
|
|
212
|
+
//console.log("code response", res);
|
|
213
|
+
getState().log(6, "Code retry answer: " + JSON.stringify(res));
|
|
214
|
+
return {
|
|
215
|
+
stop: typeof res === "string",
|
|
216
|
+
add_response: res,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
186
219
|
},
|
|
187
220
|
function: {
|
|
188
221
|
name: this.tool_name,
|
package/skills/ModelPicker.js
CHANGED
|
@@ -34,8 +34,9 @@ class ModelPicker {
|
|
|
34
34
|
class: ["form-select form-select-sm w-unset", klass],
|
|
35
35
|
name: "modelpicker",
|
|
36
36
|
},
|
|
37
|
-
this.placeholder &&
|
|
38
|
-
|
|
37
|
+
this.placeholder &&
|
|
38
|
+
option({ disabled: true }, this.placeholder || "Select model"),
|
|
39
|
+
option({ value: "" }, this.default_label || "Standard"),
|
|
39
40
|
alt_config_options.map((o) => option(o)),
|
|
40
41
|
);
|
|
41
42
|
}
|
package/skills/Subagent.js
CHANGED
|
@@ -8,6 +8,7 @@ const { getState } = require("@saltcorn/data/db/state");
|
|
|
8
8
|
const db = require("@saltcorn/data/db");
|
|
9
9
|
const { fieldProperties } = require("./helpers");
|
|
10
10
|
const agent_action = require("../action");
|
|
11
|
+
const { replaceUserContinue } = require("../common");
|
|
11
12
|
|
|
12
13
|
class SubagentToSkill {
|
|
13
14
|
static skill_name = "Subagent";
|
|
@@ -45,7 +46,12 @@ class SubagentToSkill {
|
|
|
45
46
|
required: true,
|
|
46
47
|
attributes: { options: actions.map((a) => a.name) },
|
|
47
48
|
},
|
|
48
|
-
|
|
49
|
+
{
|
|
50
|
+
name: "handover_prompt",
|
|
51
|
+
label: "Handover prompt",
|
|
52
|
+
sublabel: `Optional. The prompt initialising the subagent. Example: "Continue answering my query using the tool now at you disposal"`,
|
|
53
|
+
type: "String",
|
|
54
|
+
},
|
|
49
55
|
];
|
|
50
56
|
}
|
|
51
57
|
|
|
@@ -65,15 +71,38 @@ class SubagentToSkill {
|
|
|
65
71
|
/*renderToolCall({ phrase }, { req }) {
|
|
66
72
|
return div({ class: "border border-primary p-2 m-2" }, phrase);
|
|
67
73
|
},*/
|
|
68
|
-
postProcess: async ({
|
|
74
|
+
postProcess: async ({
|
|
75
|
+
tool_call,
|
|
76
|
+
req,
|
|
77
|
+
generate,
|
|
78
|
+
emit_update,
|
|
79
|
+
run,
|
|
80
|
+
chat,
|
|
81
|
+
agent_view_config,
|
|
82
|
+
dyn_updates,
|
|
83
|
+
}) => {
|
|
84
|
+
getState().log(6, "Running subagent", this.agent_name);
|
|
69
85
|
const subres = await agent_action.run({
|
|
70
86
|
row: {},
|
|
71
|
-
configuration: {
|
|
87
|
+
configuration: {
|
|
88
|
+
...trigger.configuration,
|
|
89
|
+
prompt:
|
|
90
|
+
this.handover_prompt ||
|
|
91
|
+
"Your instructions and tools have changed. Continue answering my query using the instructions and tools at you disposal, if any",
|
|
92
|
+
},
|
|
72
93
|
user: req.user,
|
|
73
94
|
run_id: run.id,
|
|
74
95
|
is_sub_agent: true,
|
|
96
|
+
agent_view_config,
|
|
97
|
+
dyn_updates,
|
|
75
98
|
req,
|
|
76
99
|
});
|
|
100
|
+
getState().log(
|
|
101
|
+
6,
|
|
102
|
+
"Subagent response",
|
|
103
|
+
subres?.json?.raw_responses || "No response",
|
|
104
|
+
);
|
|
105
|
+
|
|
77
106
|
if (subres.json.raw_responses)
|
|
78
107
|
return { add_responses: subres.json.raw_responses };
|
|
79
108
|
return {
|
package/tests/action.test.js
CHANGED
|
@@ -41,7 +41,7 @@ beforeAll(async () => {
|
|
|
41
41
|
//await getState().setConfig("log_level", 6);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
jest.setTimeout(
|
|
44
|
+
jest.setTimeout(40000);
|
|
45
45
|
|
|
46
46
|
const user = { id: 1, role_id: 1 };
|
|
47
47
|
const action = require("../action");
|
|
@@ -128,7 +128,7 @@ for (const nameconfig of require("./configs")) {
|
|
|
128
128
|
const result = await action.run({
|
|
129
129
|
row: {
|
|
130
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.
|
|
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. Call both the OracleAgent and the MathsAgent tools",
|
|
132
132
|
},
|
|
133
133
|
configuration,
|
|
134
134
|
user,
|